f5f45e7afb
Idempotent playbooks under deploy/ansible/ prepare both production hosts: docker-ce + compose plugin, a non-sudo deploy service account holding the CI deploy key, key-only sshd, default-deny ufw, fail2ban, unattended upgrades and chrony. The main host also opens 80/443/9443 and creates the external edge network; the tg host verifies direct Bot API egress (the no-VPN assumption). The application is deployed separately by the prod-deploy workflow (later phase), running as the deploy account this playbook provisions.
168 lines
4.4 KiB
YAML
168 lines
4.4 KiB
YAML
---
|
|
# Common baseline applied to both prod hosts: Docker engine, a non-sudo deploy
|
|
# service account, SSH hardening, a default-deny firewall, fail2ban, unattended
|
|
# security upgrades and time sync. Every task is idempotent.
|
|
|
|
- name: Install base packages
|
|
ansible.builtin.apt:
|
|
name:
|
|
- ca-certificates
|
|
- curl
|
|
- gnupg
|
|
- ufw
|
|
- fail2ban
|
|
- unattended-upgrades
|
|
- chrony
|
|
state: present
|
|
update_cache: true
|
|
cache_valid_time: 3600
|
|
|
|
# --- Docker engine (official repo; trixie is published upstream) ---------------
|
|
|
|
- name: Create apt keyring directory
|
|
ansible.builtin.file:
|
|
path: /etc/apt/keyrings
|
|
state: directory
|
|
mode: "0755"
|
|
|
|
- name: Install Docker apt GPG key
|
|
ansible.builtin.get_url:
|
|
url: https://download.docker.com/linux/debian/gpg
|
|
dest: /etc/apt/keyrings/docker.asc
|
|
mode: "0644"
|
|
|
|
- name: Add Docker apt repository
|
|
ansible.builtin.apt_repository:
|
|
repo: >-
|
|
deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.asc]
|
|
https://download.docker.com/linux/debian {{ ansible_distribution_release }} stable
|
|
filename: docker
|
|
state: present
|
|
|
|
- name: Install Docker engine and the compose plugin
|
|
ansible.builtin.apt:
|
|
name:
|
|
- docker-ce
|
|
- docker-ce-cli
|
|
- containerd.io
|
|
- docker-buildx-plugin
|
|
- docker-compose-plugin
|
|
state: present
|
|
update_cache: true
|
|
|
|
- name: Configure the Docker daemon (live-restore + log rotation)
|
|
ansible.builtin.template:
|
|
src: daemon.json.j2
|
|
dest: /etc/docker/daemon.json
|
|
mode: "0644"
|
|
notify: restart docker
|
|
|
|
- name: Enable and start Docker
|
|
ansible.builtin.service:
|
|
name: docker
|
|
enabled: true
|
|
state: started
|
|
|
|
# --- Deploy service account ----------------------------------------------------
|
|
|
|
- name: Create the deploy service account
|
|
ansible.builtin.user:
|
|
name: "{{ deploy_user }}"
|
|
groups: docker
|
|
append: true
|
|
shell: /bin/bash
|
|
create_home: true
|
|
|
|
- name: Ensure the deploy .ssh directory
|
|
ansible.builtin.file:
|
|
path: "/home/{{ deploy_user }}/.ssh"
|
|
state: directory
|
|
owner: "{{ deploy_user }}"
|
|
group: "{{ deploy_user }}"
|
|
mode: "0700"
|
|
|
|
- name: Authorize the CI deploy SSH key (exclusive)
|
|
ansible.builtin.copy:
|
|
dest: "/home/{{ deploy_user }}/.ssh/authorized_keys"
|
|
content: "{{ deploy_ci_pubkey }}\n"
|
|
owner: "{{ deploy_user }}"
|
|
group: "{{ deploy_user }}"
|
|
mode: "0600"
|
|
|
|
# --- SSH hardening -------------------------------------------------------------
|
|
|
|
- name: Harden sshd (key-only auth)
|
|
ansible.builtin.template:
|
|
src: sshd-hardening.conf.j2
|
|
dest: /etc/ssh/sshd_config.d/10-scrabble-hardening.conf
|
|
mode: "0644"
|
|
validate: sshd -t -f %s
|
|
notify: reload sshd
|
|
|
|
# --- Firewall (default deny incoming) ------------------------------------------
|
|
# SSH is allowed before the policy flips so enabling ufw never locks us out.
|
|
|
|
- name: Allow SSH through the firewall
|
|
community.general.ufw:
|
|
rule: allow
|
|
name: OpenSSH
|
|
|
|
- name: Default-deny incoming, allow outgoing
|
|
community.general.ufw:
|
|
direction: "{{ item.direction }}"
|
|
policy: "{{ item.policy }}"
|
|
loop:
|
|
- { direction: incoming, policy: deny }
|
|
- { direction: outgoing, policy: allow }
|
|
|
|
- name: Enable the firewall
|
|
community.general.ufw:
|
|
state: enabled
|
|
|
|
# --- fail2ban ------------------------------------------------------------------
|
|
|
|
- name: Configure the fail2ban sshd jail
|
|
ansible.builtin.template:
|
|
src: jail.local.j2
|
|
dest: /etc/fail2ban/jail.local
|
|
mode: "0644"
|
|
notify: restart fail2ban
|
|
|
|
- name: Enable and start fail2ban
|
|
ansible.builtin.service:
|
|
name: fail2ban
|
|
enabled: true
|
|
state: started
|
|
|
|
# --- Unattended security upgrades + time sync ----------------------------------
|
|
|
|
- name: Enable unattended upgrades
|
|
ansible.builtin.copy:
|
|
dest: /etc/apt/apt.conf.d/20auto-upgrades
|
|
mode: "0644"
|
|
content: |
|
|
APT::Periodic::Update-Package-Lists "1";
|
|
APT::Periodic::Unattended-Upgrade "1";
|
|
|
|
- name: Enable and start chrony
|
|
ansible.builtin.service:
|
|
name: chrony
|
|
enabled: true
|
|
state: started
|
|
|
|
# --- Deploy directories --------------------------------------------------------
|
|
|
|
- name: Create the scrabble base directories
|
|
ansible.builtin.file:
|
|
path: "{{ scrabble_base_dir }}/{{ item }}"
|
|
state: directory
|
|
owner: "{{ deploy_user }}"
|
|
group: "{{ deploy_user }}"
|
|
mode: "0750"
|
|
loop:
|
|
- ""
|
|
- config
|
|
- certs
|
|
- dumps
|
|
- images
|