1
0
Fork 0
forked from OpenNeo/impress
impress/deploy/setup.yml
Emi Matchu abbde80f60 Install MySQL server during deployment setup
It's finally colocated onto this box, instead of being on the old
server! I think I'm noticing substantial perf improvements, probably
both from increased colocation (tho they were in the same house
before), and also from like ten years of performance optimizations LOL!

As part of this, I created a new `setup_secrets.yml` file that's
similar to `production.env`, but is for values that the setup script
itself needs access to, whereas `production.env` is for values that the
app needs at runtime. (Though they have some things in common, like the
MySQL user password!) It's gitignored for security, as per usual!
2024-02-19 13:21:24 -08:00

407 lines
12 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
vars_files:
# mysql_root_password, mysql_user_password, mysql_user_password_2020,
# dev_ips
- files/setup_secrets.yml
tasks:
- 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: 4d4678bc1ed89aa6900c0ea0da23495445dbcf50
- name: Check if Ruby 3.1.4 is already installed
stat:
path: /opt/ruby-3.1.4
register: ruby_dir
- name: Install Ruby 3.1.4
command: "/opt/ruby-build/bin/ruby-build 3.1.4 /opt/ruby-3.1.4"
when: not ruby_dir.stat.exists
- name: Add Ruby 3.1.4 to the global PATH, for developer convenience
lineinfile:
dest: /etc/profile
line: 'PATH="/opt/ruby-3.1.4/bin:$PATH" # Added by impress deploy setup script'
- 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-3.1.4/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-3.1.4/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 service file for impress
copy:
src: files/impress.service
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: 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
- 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"
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