Matchu
07d54b9a9e
This helps me set up new devices, while still keeping passworded SSH access locked down!
275 lines
8.4 KiB
YAML
275 lines
8.4 KiB
YAML
---
|
|
- name: Set up the environment for the impress-2020 app
|
|
hosts: webserver
|
|
vars:
|
|
email_address: "emi@matchu.dev" # TODO: Extract this to personal config?
|
|
tasks:
|
|
- name: Copy authorized SSH keys
|
|
copy:
|
|
dest: ~/.ssh/authorized_keys
|
|
src: ../authorized-ssh-keys.txt
|
|
|
|
- name: Disable root SSH login
|
|
become: yes
|
|
lineinfile:
|
|
dest: /etc/ssh/sshd_config
|
|
regexp: ^#?PermitRootLogin
|
|
line: PermitRootLogin no
|
|
|
|
- name: Disable password-based SSH authentication
|
|
become: yes
|
|
lineinfile:
|
|
dest: /etc/ssh/sshd_config
|
|
regexp: ^#?PasswordAuthentication
|
|
line: PasswordAuthentication no
|
|
|
|
- name: Install fail2ban firewall with default settings
|
|
become: yes
|
|
apt:
|
|
update_cache: yes
|
|
name: fail2ban
|
|
|
|
- name: Configure ufw firewall to allow SSH connections on port 22
|
|
become: yes
|
|
community.general.ufw:
|
|
rule: allow
|
|
port: "22"
|
|
|
|
- name: Configure ufw firewall to allow HTTP connections on port 80
|
|
become: yes
|
|
community.general.ufw:
|
|
rule: allow
|
|
port: "80"
|
|
|
|
- name: Configure ufw firewall to allow HTTP connections on port 443
|
|
become: yes
|
|
community.general.ufw:
|
|
rule: allow
|
|
port: "443"
|
|
|
|
- name: Enable ufw firewall with all other ports closed by default
|
|
become: yes
|
|
community.general.ufw:
|
|
state: enabled
|
|
policy: deny
|
|
|
|
- name: Install unattended-upgrades
|
|
become: yes
|
|
apt:
|
|
update_cache: yes
|
|
name: unattended-upgrades
|
|
|
|
- name: Enable unattended-upgrades to auto-upgrade our system
|
|
become: yes
|
|
copy:
|
|
content: |
|
|
APT::Periodic::Update-Package-Lists "1";
|
|
APT::Periodic::Unattended-Upgrade "1";
|
|
dest: /etc/apt/apt.conf.d/20auto-upgrades
|
|
|
|
- name: Configure unattended-upgrades to auto-reboot our server when necessary
|
|
become: yes
|
|
lineinfile:
|
|
regex: ^(//\s*)?Unattended-Upgrade::Automatic-Reboot ".*";$
|
|
line: Unattended-Upgrade::Automatic-Reboot "true";
|
|
dest: /etc/apt/apt.conf.d/50unattended-upgrades
|
|
|
|
- name: Configure unattended-upgrades to delay necessary reboots to 3am
|
|
become: yes
|
|
lineinfile:
|
|
regex: ^(//\s*)?Unattended-Upgrade::Automatic-Reboot-Time ".*";$
|
|
line: Unattended-Upgrade::Automatic-Reboot-Time "03:00";
|
|
dest: /etc/apt/apt.conf.d/50unattended-upgrades
|
|
|
|
- name: Configure the system timezone to be US Pacific time
|
|
become: yes
|
|
community.general.timezone:
|
|
name: America/Los_Angeles
|
|
|
|
- name: Create the app versions folder
|
|
become: yes
|
|
file:
|
|
path: /srv/impress-2020/versions
|
|
owner: "{{ ansible_user_id }}"
|
|
group: "{{ ansible_user_id }}"
|
|
state: directory
|
|
|
|
- name: Add Nodesource apt key
|
|
become: yes
|
|
apt_key:
|
|
id: 9FD3B784BC1C6FC31A8A0A1C1655A0AB68576280
|
|
url: https://deb.nodesource.com/gpgkey/nodesource.gpg.key
|
|
|
|
- name: Add Node v16 apt repository
|
|
become: yes
|
|
apt_repository:
|
|
repo: deb https://deb.nodesource.com/node_16.x focal main
|
|
|
|
- name: Install Node v16
|
|
become: yes
|
|
apt:
|
|
update_cache: yes
|
|
name: nodejs
|
|
|
|
- name: Install Yarn
|
|
become: yes
|
|
npm:
|
|
name: yarn
|
|
global: yes
|
|
|
|
- name: Check for a current app version
|
|
stat:
|
|
path: /srv/impress-2020/current
|
|
register: current_app_version
|
|
|
|
- name: Check whether we already have a placeholder app
|
|
stat:
|
|
path: /srv/impress-2020/versions/initial-placeholder
|
|
register: existing_placeholder_app
|
|
when: not current_app_version.stat.exists
|
|
|
|
- name: Create a placeholder app, to run until we deploy a real version
|
|
command:
|
|
chdir: /srv/impress-2020/versions
|
|
cmd: yarn create next-app initial-placeholder
|
|
when: |
|
|
not current_app_version.stat.exists and
|
|
not existing_placeholder_app.stat.exists
|
|
|
|
- name: Build the placeholder app
|
|
command:
|
|
chdir: /srv/impress-2020/versions/initial-placeholder
|
|
cmd: yarn build
|
|
when: not current_app_version.stat.exists
|
|
|
|
- name: Set the placeholder app as the current version
|
|
file:
|
|
src: /srv/impress-2020/versions/initial-placeholder
|
|
dest: /srv/impress-2020/current
|
|
state: link
|
|
when: not current_app_version.stat.exists
|
|
|
|
- name: Install pm2
|
|
become: yes
|
|
npm:
|
|
name: pm2
|
|
global: yes
|
|
|
|
- name: Create pm2 startup script
|
|
# The current user is going to become the pm2 owner of the app server
|
|
# process. They'll be able to manage it without `sudo`, including during
|
|
# normal deploys, and run `pm2 monit` from their shell to see status.
|
|
become: yes
|
|
command: "pm2 startup systemd {{ ansible_user_id }} --hp /home/{{ ansible_user_id }}"
|
|
|
|
- name: Create pm2 ecosystem file
|
|
copy:
|
|
content: |
|
|
module.exports = {
|
|
apps: [
|
|
{
|
|
name: "impress-2020",
|
|
cwd: "/srv/impress-2020/current",
|
|
// Instead of `yarn start`, we specify the `next` binary
|
|
// directly, because it helps pm2 monitor our app correctly.
|
|
// https://github.com/vercel/next.js/discussions/10675#discussioncomment-34615
|
|
script: "./node_modules/.bin/next",
|
|
args: "start --port=3000",
|
|
instances: "max",
|
|
exec_mode: "cluster",
|
|
}
|
|
]
|
|
}
|
|
dest: "~/ecosystem.config.js"
|
|
# Create a temporary backup file, so we can use it to delete the old
|
|
# version of the services. (This is important if e.g. a service is
|
|
# removed or renamed, in which case deleting from the *new* config file
|
|
# wouldn't include it.)
|
|
backup: yes
|
|
register: pm2_ecosystem_file
|
|
|
|
- name: Delete old pm2 services if config file changed
|
|
command: "pm2 delete {{ pm2_ecosystem_file.backup_file | quote }}"
|
|
when: pm2_ecosystem_file is changed and pm2_ecosystem_file.backup_file is defined
|
|
|
|
- name: Delete old pm2 config file if it changed
|
|
file:
|
|
path: "{{ pm2_ecosystem_file.backup_file }}"
|
|
state: absent
|
|
when: pm2_ecosystem_file is changed and pm2_ecosystem_file.backup_file is defined
|
|
|
|
- name: Start pm2 services
|
|
command: "pm2 start ~/ecosystem.config.js"
|
|
|
|
- name: Save pm2 startup script
|
|
command: pm2 save
|
|
|
|
- name: Install nginx
|
|
become: yes
|
|
apt:
|
|
update_cache: yes
|
|
name: nginx
|
|
|
|
- name: Install core snap
|
|
become: yes
|
|
community.general.snap:
|
|
name: core
|
|
|
|
- name: Install certbot as a snap
|
|
become: yes
|
|
community.general.snap:
|
|
name: certbot
|
|
classic: yes
|
|
|
|
- name: Set up certbot
|
|
become: yes
|
|
command: "certbot certonly --nginx -n --agree-tos --email {{ email_address }} --domains impress-2020-box.openneo.net"
|
|
|
|
- name: Add impress-2020 config file to nginx
|
|
become: yes
|
|
copy:
|
|
content: |
|
|
server {
|
|
server_name impress-2020-box.openneo.net;
|
|
listen 80;
|
|
if ($host = impress-2020-box.openneo.net) {
|
|
return 301 https://$host$request_uri;
|
|
}
|
|
}
|
|
|
|
server {
|
|
server_name impress-2020-box.openneo.net;
|
|
listen 443 ssl;
|
|
ssl_certificate /etc/letsencrypt/live/impress-2020-box.openneo.net/fullchain.pem;
|
|
ssl_certificate_key /etc/letsencrypt/live/impress-2020-box.openneo.net/privkey.pem;
|
|
include /etc/letsencrypt/options-ssl-nginx.conf;
|
|
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
|
|
ssl_session_cache shared:SSL:10m; # https://superuser.com/q/1484466/14127
|
|
|
|
# TODO: Serve static files directly, instead of through the proxy
|
|
location / {
|
|
proxy_pass http://127.0.0.1:3000;
|
|
}
|
|
}
|
|
dest: /etc/nginx/sites-enabled/impress-2020
|
|
notify:
|
|
- Restart nginx
|
|
|
|
- name: Install dependencies for the npm module node-canvas
|
|
become: yes
|
|
apt:
|
|
update_cache: yes
|
|
name:
|
|
- build-essential
|
|
- libcairo2-dev
|
|
- libpango1.0-dev
|
|
- libjpeg-dev
|
|
- libgif-dev
|
|
- librsvg2-dev
|
|
|
|
handlers:
|
|
- name: Restart nginx
|
|
become: yes
|
|
systemd:
|
|
name: nginx
|
|
state: restarted
|