Emi Matchu
a57b3629db
and with a `rails neopets:import` task you can call to do them all at once! I'm gonna do some other stuff here too to make `neopets:import` easier to call all in one go, like prompting for the Neologin cookie just once at the start. Note that this changes the cron setup, so you gotta run `bin/deploy:setup` after this deploys!
486 lines
15 KiB
YAML
486 lines
15 KiB
YAML
---
|
|
- name: Set up the environment for the impress app
|
|
hosts: webserver
|
|
become: yes
|
|
become_user: root
|
|
vars:
|
|
email_address: "emi@matchu.dev" # TODO: Extract this to personal config?
|
|
impress_hostname: impress.openneo.net
|
|
ruby_version: "3.3.5"
|
|
vars_files:
|
|
# mysql_root_password, mysql_user_password, mysql_user_password_2020,
|
|
# dev_ips
|
|
- files/setup_secrets.yml
|
|
tasks:
|
|
- name: Set hostname to impress.openneo.net
|
|
hostname:
|
|
name: impress.openneo.net
|
|
|
|
- name: Create SSH folder for logged-in user
|
|
become: no
|
|
file:
|
|
name: .ssh
|
|
mode: "700"
|
|
state: directory
|
|
|
|
- name: Copy authorized SSH keys to logged-in user
|
|
become: no
|
|
copy:
|
|
dest: ~/.ssh/authorized_keys
|
|
src: files/authorized-ssh-keys.txt
|
|
mode: "600"
|
|
|
|
- name: Disable root SSH login
|
|
lineinfile:
|
|
dest: /etc/ssh/sshd_config
|
|
regexp: ^#?PermitRootLogin
|
|
line: PermitRootLogin no
|
|
|
|
- name: Disable password-based SSH authentication
|
|
lineinfile:
|
|
dest: /etc/ssh/sshd_config
|
|
regexp: ^#?PasswordAuthentication
|
|
line: PasswordAuthentication no
|
|
|
|
- name: Enable public-key SSH authentication
|
|
lineinfile:
|
|
dest: /etc/ssh/sshd_config
|
|
regexp: ^#?PubkeyAuthentication
|
|
line: PubkeyAuthentication yes
|
|
|
|
- name: Update the apt cache
|
|
apt:
|
|
update_cache: yes
|
|
|
|
- name: Install fail2ban firewall with default settings
|
|
apt:
|
|
name: fail2ban
|
|
|
|
- name: Configure ufw firewall to allow SSH connections on port 22
|
|
community.general.ufw:
|
|
rule: allow
|
|
port: "22"
|
|
|
|
- name: Configure ufw firewall to allow HTTP connections on port 80
|
|
community.general.ufw:
|
|
rule: allow
|
|
port: "80"
|
|
|
|
- name: Configure ufw firewall to allow HTTPS connections on port 443
|
|
community.general.ufw:
|
|
rule: allow
|
|
port: "443"
|
|
|
|
- name: Configure ufw firewall to allow MySQL connections from impress-2020
|
|
community.general.ufw:
|
|
rule: allow
|
|
port: "3306"
|
|
from_ip: "{{ item }}"
|
|
loop:
|
|
- "45.56.112.222"
|
|
- "2600:3c02::f03c:92ff:fe9a:4615"
|
|
|
|
- name: Configure ufw firewall to allow MySQL connections from known devs
|
|
community.general.ufw:
|
|
rule: allow
|
|
port: "3306"
|
|
from_ip: "{{ item }}"
|
|
loop: "{{ dev_ips }}"
|
|
|
|
- name: Enable ufw firewall with all other ports closed by default
|
|
community.general.ufw:
|
|
state: enabled
|
|
policy: deny
|
|
|
|
- name: Install unattended-upgrades
|
|
apt:
|
|
name: unattended-upgrades
|
|
|
|
- name: Enable unattended-upgrades to auto-upgrade our system
|
|
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
|
|
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
|
|
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
|
|
community.general.timezone:
|
|
name: America/Los_Angeles
|
|
|
|
- name: Create "impress" user
|
|
user:
|
|
name: impress
|
|
comment: Impress App
|
|
home: /srv/impress
|
|
create_home: false
|
|
shell: /bin/bash
|
|
|
|
- name: Create "impress-deployers" group
|
|
group:
|
|
name: impress-deployers
|
|
|
|
- name: Add the current user to the "impress-deployers" group
|
|
user:
|
|
name: "{{ lookup('env', 'USER') }}"
|
|
groups:
|
|
- impress-deployers
|
|
append: yes
|
|
|
|
# We use this so the deploy playbook doesn't have to prompt for a root
|
|
# password: this user just is trusted to act as "impress" in the future.
|
|
- name: Enable the "impress-deployers" group to freely act as the "impress" user
|
|
community.general.sudoers:
|
|
name: impress-deployers-as-impress
|
|
group: impress-deployers
|
|
runas: impress
|
|
commands: ALL
|
|
nopassword: yes
|
|
|
|
# Similarly, this enables us to manage the impress service in the deploy playbook
|
|
# and in live debugging without a password.
|
|
# NOTE: In the sudoers file, you need to specify the full path to the
|
|
# command, to avoid tricks where you use PATH to get around the intent!
|
|
- name: Enable the "impress-deployers" group to freely start and stop the impress service
|
|
community.general.sudoers:
|
|
name: impress-deployers-systemctl
|
|
group: impress-deployers
|
|
commands:
|
|
- /bin/systemctl status impress
|
|
- /bin/systemctl start impress
|
|
- /bin/systemctl stop impress
|
|
- /bin/systemctl restart impress
|
|
nopassword: yes
|
|
|
|
- name: Install ACL, to enable us to run commands as the "impress" user
|
|
apt:
|
|
name: acl
|
|
|
|
- name: Install ruby-build
|
|
git:
|
|
repo: https://github.com/rbenv/ruby-build.git
|
|
dest: /opt/ruby-build
|
|
version: v20240917
|
|
|
|
- name: Check if Ruby {{ ruby_version }} is already installed
|
|
stat:
|
|
path: /opt/ruby-{{ ruby_version }}
|
|
register: ruby_dir
|
|
|
|
- name: Install Ruby {{ ruby_version }}
|
|
command: "/opt/ruby-build/bin/ruby-build {{ ruby_version }} /opt/ruby-{{ ruby_version }}"
|
|
when: not ruby_dir.stat.exists
|
|
|
|
- name: Add Ruby {{ ruby_version }} to the global PATH, for developer convenience
|
|
copy:
|
|
dest: /etc/profile.d/ruby_path.sh
|
|
content: PATH="/opt/ruby-{{ ruby_version }}/bin:$PATH"
|
|
|
|
- name: Install system dependencies for impress's Ruby gems
|
|
apt:
|
|
name:
|
|
- libmysqlclient-dev
|
|
- libyaml-dev
|
|
|
|
- name: Create the app folder
|
|
file:
|
|
path: /srv/impress
|
|
owner: impress
|
|
group: impress
|
|
mode: "755"
|
|
state: directory
|
|
|
|
- name: Add a convenient .bash_profile for when we log in as "impress"
|
|
copy:
|
|
owner: impress
|
|
group: impress
|
|
dest: /srv/impress/.bash_profile
|
|
content: |
|
|
set -a # Export all of the below
|
|
RAILS_ENV=production
|
|
EXECJS_RUNTIME=Disabled
|
|
source /srv/impress/shared/production.env
|
|
set +a
|
|
|
|
- name: Create the app's "versions" folder
|
|
become_user: impress
|
|
file:
|
|
path: /srv/impress/versions
|
|
state: directory
|
|
|
|
- name: Create the app's "shared" folder
|
|
become_user: impress
|
|
file:
|
|
path: /srv/impress/shared
|
|
state: directory
|
|
|
|
- name: Check for a current app version
|
|
stat:
|
|
path: /srv/impress/current
|
|
register: current_app_version
|
|
|
|
- name: Check whether we already have a placeholder app
|
|
stat:
|
|
path: /srv/impress/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
|
|
become_user: impress
|
|
copy:
|
|
src: files/initial-placeholder
|
|
dest: /srv/impress/versions
|
|
when: |
|
|
not current_app_version.stat.exists and
|
|
not existing_placeholder_app.stat.exists
|
|
|
|
- name: Configure the placeholder app to run in deployment mode
|
|
become_user: impress
|
|
command:
|
|
chdir: /srv/impress/versions/initial-placeholder
|
|
cmd: /opt/ruby-{{ ruby_version }}/bin/bundle config set --local deployment true
|
|
when: not current_app_version.stat.exists
|
|
|
|
- name: Install the placeholder app's dependencies
|
|
become_user: impress
|
|
command:
|
|
chdir: /srv/impress/versions/initial-placeholder
|
|
cmd: /opt/ruby-{{ ruby_version }}/bin/bundle install
|
|
when: not current_app_version.stat.exists
|
|
|
|
- name: Set the placeholder app as the current version
|
|
become_user: impress
|
|
file:
|
|
src: /srv/impress/versions/initial-placeholder
|
|
dest: /srv/impress/current
|
|
state: link
|
|
when: not current_app_version.stat.exists
|
|
|
|
# NOTE: This file is uploaded with stricter permissions, to help protect
|
|
# the secrets inside. Most of the app is world-readable for convenience
|
|
# for debugging and letting nginx serve static files, but keep this safer!
|
|
- name: Upload the production.env file
|
|
become_user: impress
|
|
copy:
|
|
dest: /srv/impress/shared/production.env
|
|
src: files/production.env
|
|
mode: "600"
|
|
notify:
|
|
- Reload systemctl
|
|
- Restart impress
|
|
|
|
- name: Create the shared public-data folder
|
|
become_user: impress
|
|
file:
|
|
dest: /srv/impress/shared/public-data
|
|
mode: "755"
|
|
state: directory
|
|
|
|
- name: Create service file for impress
|
|
template:
|
|
src: files/impress.service.j2
|
|
dest: /etc/systemd/system/impress.service
|
|
notify:
|
|
- Reload systemctl
|
|
- Restart impress
|
|
|
|
- name: Configure impress to run now, and automatically when the system starts
|
|
systemd:
|
|
name: impress
|
|
state: started
|
|
enabled: true
|
|
|
|
- name: Install nginx
|
|
apt:
|
|
name: nginx
|
|
|
|
- name: Install core snap
|
|
community.general.snap:
|
|
name: core
|
|
|
|
- name: Install certbot as a snap
|
|
community.general.snap:
|
|
name: certbot
|
|
classic: yes
|
|
|
|
- name: Set up certbot
|
|
command: "certbot certonly --nginx -n --agree-tos --email {{ email_address }} --domains {{ impress_hostname }}"
|
|
|
|
- name: Add impress config file to nginx
|
|
template:
|
|
src: files/sites-available/impress.conf
|
|
dest: /etc/nginx/sites-available/impress.conf
|
|
notify:
|
|
- Reload nginx
|
|
|
|
- name: Enable impress config file in nginx
|
|
file:
|
|
src: /etc/nginx/sites-available/impress.conf
|
|
dest: /etc/nginx/sites-enabled/impress.conf
|
|
state: link
|
|
notify:
|
|
- Reload nginx
|
|
|
|
- name: Add openneo-home config file to nginx
|
|
template:
|
|
src: files/sites-available/openneo-home.conf
|
|
dest: /etc/nginx/sites-available/openneo-home.conf
|
|
notify:
|
|
- Reload nginx
|
|
|
|
- name: Enable openneo-home config file in nginx
|
|
file:
|
|
src: /etc/nginx/sites-available/openneo-home.conf
|
|
dest: /etc/nginx/sites-enabled/openneo-home.conf
|
|
state: link
|
|
notify:
|
|
- Reload nginx
|
|
|
|
- name: Install MariaDB
|
|
apt:
|
|
name: mariadb-server
|
|
|
|
- name: Install a Python MySQL client, for Ansible to use when configuring
|
|
apt:
|
|
name: python3-mysqldb
|
|
|
|
- name: Update MariaDB root password
|
|
community.mysql.mysql_user:
|
|
name: root
|
|
host_all: true
|
|
password: "{{mysql_root_password}}"
|
|
|
|
- name: Create root's .my.cnf file
|
|
copy:
|
|
content: |
|
|
[client]
|
|
user=root
|
|
password='{{ mysql_root_password }}'
|
|
dest: /root/.my.cnf
|
|
mode: 0400
|
|
|
|
- name: Remove test database
|
|
community.mysql.mysql_db:
|
|
name: test
|
|
state: absent
|
|
login_unix_socket: "{{ login_unix_socket | default(omit) }}"
|
|
|
|
- name: Remove anonymous users
|
|
community.mysql.mysql_user:
|
|
name: ""
|
|
state: absent
|
|
host_all: true
|
|
|
|
- name: Remove remote root access
|
|
community.mysql.mysql_query:
|
|
query:
|
|
- DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1')
|
|
|
|
- name: Expose MariaDB to the internet (but ufw will block most clients)
|
|
copy:
|
|
dest: /etc/mysql/mariadb.conf.d/80-bind-address.cnf
|
|
content: |
|
|
[mysqld]
|
|
skip-networking=0
|
|
skip-bind-address
|
|
notify: Restart MariaDB
|
|
|
|
# This is the best Unicode collation available in our version of MariaDB!
|
|
# We already specify it for all the tables in `schema.rb`, but also set it
|
|
# as the default collation for new tables here, too.
|
|
- name: Set MariaDb's default collation to utf8mb4_unicode_520_ci
|
|
copy:
|
|
dest: /etc/mysql/mariadb.conf.d/80-charsets.cnf
|
|
content: |
|
|
[mysqld]
|
|
character-set-server=utf8mb4
|
|
collation-server=utf8mb4_unicode_520_ci
|
|
notify: Restart MariaDB
|
|
|
|
- name: Enable slow query logging for MariaDB
|
|
copy:
|
|
dest: /etc/mysql/mariadb.conf.d/80-logging.cnf
|
|
content: |
|
|
[mysqld]
|
|
slow-query-log
|
|
notify: Restart MariaDB
|
|
|
|
- name: Create MySQL databases
|
|
community.mysql.mysql_db:
|
|
name:
|
|
- openneo_impress
|
|
- openneo_id
|
|
|
|
- name: Create MySQL user openneo_impress
|
|
community.mysql.mysql_user:
|
|
name: openneo_impress
|
|
password: "{{ mysql_user_password }}"
|
|
priv: "openneo_impress.*:ALL,openneo_id.*:ALL"
|
|
|
|
- name: Create MySQL user impress2020
|
|
community.mysql.mysql_user:
|
|
name: impress2020
|
|
password: "{{ mysql_user_password_2020 }}"
|
|
priv: "openneo_impress.*:ALL,openneo_id.*:ALL"
|
|
|
|
- name: Create the Neopets Media Archive data directory
|
|
file:
|
|
path: /var/lib/neopets-media-archive
|
|
owner: impress
|
|
group: impress
|
|
mode: "755"
|
|
state: directory
|
|
|
|
- name: Remove 10min cron job to run `rails nc_mall:sync`
|
|
become_user: impress
|
|
cron:
|
|
state: absent
|
|
name: "Impress: sync NC Mall data"
|
|
minute: "*/10"
|
|
job: "bash -c 'source /etc/profile && source ~/.bash_profile && cd /srv/impress/current && bin/rails nc_mall:sync'"
|
|
|
|
- name: Create 10min cron job to run `rails neopets:import:nc_mall`
|
|
become_user: impress
|
|
cron:
|
|
name: "Impress: import NC Mall data"
|
|
minute: "*/10"
|
|
job: "bash -c 'source /etc/profile && source ~/.bash_profile && cd /srv/impress/current && bin/rails neopets:import:nc_mall'"
|
|
|
|
- name: Create weekly cron job to run `rails public_data:commit`
|
|
become_user: impress
|
|
cron:
|
|
name: "Impress: commit public data"
|
|
weekday: "0" # Sunday
|
|
hour: "1" # 1:15am
|
|
minute: "15" # 1:15am
|
|
job: "bash -c 'source /etc/profile && source ~/.bash_profile && cd /srv/impress/current && bin/rails public_data:commit[scheduled]'"
|
|
|
|
handlers:
|
|
- name: Reload nginx
|
|
systemd:
|
|
name: nginx
|
|
state: reloaded
|
|
|
|
- name: Restart MariaDB
|
|
systemd:
|
|
name: mariadb
|
|
state: restarted
|
|
|
|
- name: Reload systemctl
|
|
command: systemctl daemon-reload
|
|
|
|
- name: Restart impress
|
|
systemd:
|
|
name: impress
|
|
state: restarted
|