Includes: API (Express/TypeScript), Astro site, Python workers, document generators, FCC compliance tools, Canada CRTC formation, Ansible infrastructure, and deployment scripts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
447 lines
13 KiB
YAML
447 lines
13 KiB
YAML
---
|
|
- name: Install nginx
|
|
ansible.builtin.apt:
|
|
name: nginx
|
|
state: present
|
|
|
|
- name: Install certbot and nginx plugin
|
|
ansible.builtin.apt:
|
|
name:
|
|
- certbot
|
|
- python3-certbot-nginx
|
|
state: present
|
|
|
|
- name: Install fail2ban
|
|
ansible.builtin.apt:
|
|
name: fail2ban
|
|
state: present
|
|
|
|
- name: Create certbot webroot directory
|
|
ansible.builtin.file:
|
|
path: "{{ certbot_webroot }}"
|
|
state: directory
|
|
owner: www-data
|
|
group: www-data
|
|
mode: "0755"
|
|
|
|
- name: Create snippets directory
|
|
ansible.builtin.file:
|
|
path: /etc/nginx/snippets
|
|
state: directory
|
|
owner: root
|
|
group: root
|
|
mode: "0755"
|
|
|
|
- name: Deploy shared security snippet
|
|
ansible.builtin.template:
|
|
src: pw-security.conf.j2
|
|
dest: /etc/nginx/snippets/pw-security.conf
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
notify: Reload nginx
|
|
|
|
# ── Phase 1: HTTP-only configs for certbot bootstrap ─────────────────────────
|
|
|
|
- name: Deploy initial HTTP-only site config
|
|
ansible.builtin.template:
|
|
src: pw-site.conf.j2
|
|
dest: /etc/nginx/sites-available/pw-site.conf
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
|
|
- name: Enable HTTP-only site config
|
|
ansible.builtin.file:
|
|
src: /etc/nginx/sites-available/pw-site.conf
|
|
dest: /etc/nginx/sites-enabled/pw-site.conf
|
|
state: link
|
|
|
|
- name: Remove default nginx site
|
|
ansible.builtin.file:
|
|
path: /etc/nginx/sites-enabled/default
|
|
state: absent
|
|
|
|
- name: Reload nginx for HTTP configs
|
|
ansible.builtin.systemd:
|
|
name: nginx
|
|
state: reloaded
|
|
|
|
# ── Phase 2: Obtain TLS certificates ─────────────────────────────────────────
|
|
|
|
- name: Obtain certificate for performancewest.net
|
|
ansible.builtin.command:
|
|
cmd: >-
|
|
certbot certonly --webroot
|
|
-w {{ certbot_webroot }}
|
|
-d performancewest.net -d www.performancewest.net
|
|
--email {{ certbot_email }}
|
|
--agree-tos --non-interactive
|
|
creates: /etc/letsencrypt/live/performancewest.net/fullchain.pem
|
|
|
|
- name: Obtain certificate for api.performancewest.net
|
|
ansible.builtin.command:
|
|
cmd: >-
|
|
certbot certonly --webroot
|
|
-w {{ certbot_webroot }}
|
|
-d {{ api_domain }}
|
|
--email {{ certbot_email }}
|
|
--agree-tos --non-interactive
|
|
creates: /etc/letsencrypt/live/{{ api_domain }}/fullchain.pem
|
|
|
|
- name: Obtain certificate for portal.performancewest.net
|
|
ansible.builtin.command:
|
|
cmd: >-
|
|
certbot certonly --webroot
|
|
-w {{ certbot_webroot }}
|
|
-d {{ portal_domain }}
|
|
--email {{ certbot_email }}
|
|
--agree-tos --non-interactive
|
|
creates: /etc/letsencrypt/live/{{ portal_domain }}/fullchain.pem
|
|
|
|
- name: Obtain certificate for crm.performancewest.net
|
|
ansible.builtin.command:
|
|
cmd: >-
|
|
certbot certonly --webroot
|
|
-w {{ certbot_webroot }}
|
|
-d {{ crm_domain }}
|
|
--email {{ certbot_email }}
|
|
--agree-tos --non-interactive
|
|
creates: /etc/letsencrypt/live/{{ crm_domain }}/fullchain.pem
|
|
|
|
- name: Obtain certificate for lists.performancewest.net (Listmonk)
|
|
ansible.builtin.command:
|
|
cmd: >-
|
|
certbot certonly --webroot
|
|
-w {{ certbot_webroot }}
|
|
-d {{ listmonk_domain }}
|
|
--email {{ certbot_email }}
|
|
--agree-tos --non-interactive
|
|
creates: /etc/letsencrypt/live/{{ listmonk_domain }}/fullchain.pem
|
|
|
|
- name: Obtain certificate for analytics.performancewest.net
|
|
ansible.builtin.command:
|
|
cmd: >-
|
|
certbot certonly --webroot
|
|
-w {{ certbot_webroot }}
|
|
-d {{ analytics_domain }}
|
|
--email {{ certbot_email }}
|
|
--agree-tos --non-interactive
|
|
creates: /etc/letsencrypt/live/{{ analytics_domain }}/fullchain.pem
|
|
|
|
- name: Obtain certificate for dev.performancewest.net
|
|
ansible.builtin.command:
|
|
cmd: >-
|
|
certbot certonly --webroot
|
|
-w {{ certbot_webroot }}
|
|
-d {{ dev_domain }} -d www.{{ dev_domain }}
|
|
--email {{ certbot_email }}
|
|
--agree-tos --non-interactive
|
|
creates: /etc/letsencrypt/live/{{ dev_domain }}/fullchain.pem
|
|
|
|
- name: Obtain certificate for api.dev.performancewest.net
|
|
ansible.builtin.command:
|
|
cmd: >-
|
|
certbot certonly --webroot
|
|
-w {{ certbot_webroot }}
|
|
-d {{ dev_api_domain }}
|
|
--email {{ certbot_email }}
|
|
--agree-tos --non-interactive
|
|
creates: /etc/letsencrypt/live/{{ dev_api_domain }}/fullchain.pem
|
|
|
|
- name: Obtain certificate for pay.performancewest.net (SHKeeper API)
|
|
ansible.builtin.command:
|
|
cmd: >-
|
|
certbot certonly --webroot
|
|
-w {{ certbot_webroot }}
|
|
-d {{ shkeeper_domain }}
|
|
--email {{ certbot_email }}
|
|
--agree-tos --non-interactive
|
|
creates: /etc/letsencrypt/live/{{ shkeeper_domain }}/fullchain.pem
|
|
|
|
- name: Obtain certificate for crypto.performancewest.net (SHKeeper Admin)
|
|
ansible.builtin.command:
|
|
cmd: >-
|
|
certbot certonly --webroot
|
|
-w {{ certbot_webroot }}
|
|
-d {{ shkeeper_admin_domain }}
|
|
--email {{ certbot_email }}
|
|
--agree-tos --non-interactive
|
|
creates: /etc/letsencrypt/live/{{ shkeeper_admin_domain }}/fullchain.pem
|
|
|
|
- name: Obtain certificate for minio.performancewest.net
|
|
ansible.builtin.command:
|
|
cmd: >-
|
|
certbot certonly --webroot
|
|
-w {{ certbot_webroot }}
|
|
-d {{ minio_domain }}
|
|
--email {{ certbot_email }}
|
|
--agree-tos --non-interactive
|
|
creates: /etc/letsencrypt/live/{{ minio_domain }}/fullchain.pem
|
|
|
|
- name: Obtain certificate for minio-console.performancewest.net
|
|
ansible.builtin.command:
|
|
cmd: >-
|
|
certbot certonly --webroot
|
|
-w {{ certbot_webroot }}
|
|
-d {{ minio_console_domain }}
|
|
--email {{ certbot_email }}
|
|
--agree-tos --non-interactive
|
|
creates: /etc/letsencrypt/live/{{ minio_console_domain }}/fullchain.pem
|
|
|
|
# ── Phase 3: Deploy TLS configs ───────────────────────────────────────────────
|
|
|
|
- name: Deploy TLS config for performancewest.net
|
|
ansible.builtin.template:
|
|
src: pw-site-tls.conf.j2
|
|
dest: /etc/nginx/sites-available/pw-site.conf
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
notify: Reload nginx
|
|
|
|
- name: Deploy TLS config for api.performancewest.net
|
|
ansible.builtin.template:
|
|
src: pw-api-tls.conf.j2
|
|
dest: /etc/nginx/sites-available/pw-api.conf
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
notify: Reload nginx
|
|
|
|
- name: Enable API site config
|
|
ansible.builtin.file:
|
|
src: /etc/nginx/sites-available/pw-api.conf
|
|
dest: /etc/nginx/sites-enabled/pw-api.conf
|
|
state: link
|
|
|
|
- name: Deploy TLS config for portal.performancewest.net
|
|
ansible.builtin.template:
|
|
src: pw-portal-tls.conf.j2
|
|
dest: /etc/nginx/sites-available/pw-portal.conf
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
notify: Reload nginx
|
|
|
|
- name: Enable portal site config
|
|
ansible.builtin.file:
|
|
src: /etc/nginx/sites-available/pw-portal.conf
|
|
dest: /etc/nginx/sites-enabled/pw-portal.conf
|
|
state: link
|
|
|
|
- name: Deploy TLS config for crm.performancewest.net (ERPNext internal CRM)
|
|
ansible.builtin.template:
|
|
src: pw-support-tls.conf.j2
|
|
dest: /etc/nginx/sites-available/pw-crm.conf
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
notify: Reload nginx
|
|
|
|
- name: Enable CRM site config
|
|
ansible.builtin.file:
|
|
src: /etc/nginx/sites-available/pw-crm.conf
|
|
dest: /etc/nginx/sites-enabled/pw-crm.conf
|
|
state: link
|
|
|
|
- name: Remove deprecated Zammad/Mautic/support site configs
|
|
ansible.builtin.file:
|
|
path: "{{ item }}"
|
|
state: absent
|
|
loop:
|
|
- /etc/nginx/sites-available/pw-support.conf
|
|
- /etc/nginx/sites-enabled/pw-support.conf
|
|
- /etc/nginx/sites-available/pw-mautic.conf
|
|
- /etc/nginx/sites-enabled/pw-mautic.conf
|
|
notify: Reload nginx
|
|
|
|
- name: Deploy TLS config for lists.performancewest.net (Listmonk)
|
|
ansible.builtin.template:
|
|
src: pw-listmonk-tls.conf.j2
|
|
dest: /etc/nginx/sites-available/pw-listmonk.conf
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
notify: Reload nginx
|
|
|
|
- name: Enable Listmonk site config
|
|
ansible.builtin.file:
|
|
src: /etc/nginx/sites-available/pw-listmonk.conf
|
|
dest: /etc/nginx/sites-enabled/pw-listmonk.conf
|
|
state: link
|
|
|
|
- name: Deploy TLS config for analytics.performancewest.net
|
|
ansible.builtin.template:
|
|
src: pw-analytics-tls.conf.j2
|
|
dest: /etc/nginx/sites-available/pw-analytics.conf
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
notify: Reload nginx
|
|
|
|
- name: Enable analytics site config
|
|
ansible.builtin.file:
|
|
src: /etc/nginx/sites-available/pw-analytics.conf
|
|
dest: /etc/nginx/sites-enabled/pw-analytics.conf
|
|
state: link
|
|
|
|
- name: Deploy TLS config for dev.performancewest.net
|
|
ansible.builtin.template:
|
|
src: pw-dev-tls.conf.j2
|
|
dest: /etc/nginx/sites-available/pw-dev-site.conf
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
notify: Reload nginx
|
|
|
|
- name: Enable dev site config
|
|
ansible.builtin.file:
|
|
src: /etc/nginx/sites-available/pw-dev-site.conf
|
|
dest: /etc/nginx/sites-enabled/pw-dev-site.conf
|
|
state: link
|
|
|
|
- name: Deploy TLS config for api.dev.performancewest.net
|
|
ansible.builtin.template:
|
|
src: pw-dev-api-tls.conf.j2
|
|
dest: /etc/nginx/sites-available/pw-dev-api.conf
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
notify: Reload nginx
|
|
|
|
- name: Enable dev API site config
|
|
ansible.builtin.file:
|
|
src: /etc/nginx/sites-available/pw-dev-api.conf
|
|
dest: /etc/nginx/sites-enabled/pw-dev-api.conf
|
|
state: link
|
|
|
|
- name: Deploy TLS config for pay.performancewest.net (SHKeeper API)
|
|
ansible.builtin.template:
|
|
src: pw-btcpay-tls.conf.j2
|
|
dest: /etc/nginx/sites-available/pw-btcpay.conf
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
notify: Reload nginx
|
|
|
|
- name: Enable SHKeeper API site config
|
|
ansible.builtin.file:
|
|
src: /etc/nginx/sites-available/pw-btcpay.conf
|
|
dest: /etc/nginx/sites-enabled/pw-btcpay.conf
|
|
state: link
|
|
|
|
- name: Deploy TLS config for crypto.performancewest.net (SHKeeper Admin)
|
|
ansible.builtin.template:
|
|
src: pw-crypto-tls.conf.j2
|
|
dest: /etc/nginx/sites-available/pw-crypto.conf
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
notify: Reload nginx
|
|
|
|
- name: Enable SHKeeper Admin site config
|
|
ansible.builtin.file:
|
|
src: /etc/nginx/sites-available/pw-crypto.conf
|
|
dest: /etc/nginx/sites-enabled/pw-crypto.conf
|
|
state: link
|
|
|
|
- name: Deploy TLS config for minio.performancewest.net + console
|
|
ansible.builtin.template:
|
|
src: pw-minio-tls.conf.j2
|
|
dest: /etc/nginx/sites-available/pw-minio.conf
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
notify: Reload nginx
|
|
|
|
- name: Enable MinIO site config
|
|
ansible.builtin.file:
|
|
src: /etc/nginx/sites-available/pw-minio.conf
|
|
dest: /etc/nginx/sites-enabled/pw-minio.conf
|
|
state: link
|
|
|
|
# ── Phase 4: Firewall & fail2ban ─────────────────────────────────────────────
|
|
|
|
- name: Allow HTTP through UFW
|
|
community.general.ufw:
|
|
rule: allow
|
|
port: "80"
|
|
proto: tcp
|
|
comment: HTTP
|
|
|
|
- name: Allow HTTPS through UFW
|
|
community.general.ufw:
|
|
rule: allow
|
|
port: "443"
|
|
proto: tcp
|
|
comment: HTTPS
|
|
|
|
- name: Deploy fail2ban nginx filter
|
|
ansible.builtin.copy:
|
|
content: |
|
|
[Definition]
|
|
failregex = ^<HOST> .* "(GET|POST|HEAD) .*(\.php|\.asp|wp-admin|wp-login|\.env|\.git).*" (400|403|404|444)
|
|
ignoreregex =
|
|
dest: /etc/fail2ban/filter.d/nginx-badbots.conf
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
notify: Restart fail2ban
|
|
|
|
- name: Deploy fail2ban nginx jail
|
|
ansible.builtin.copy:
|
|
content: |
|
|
[nginx-badbots]
|
|
enabled = true
|
|
port = http,https
|
|
filter = nginx-badbots
|
|
logpath = /var/log/nginx/access.log
|
|
maxretry = 5
|
|
bantime = 3600
|
|
findtime = 600
|
|
dest: /etc/fail2ban/jail.d/nginx-badbots.conf
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
notify: Restart fail2ban
|
|
|
|
- name: Deploy fail2ban pw-api filter
|
|
ansible.builtin.copy:
|
|
src: "{{ playbook_dir }}/../../../fail2ban/filter.d/pw-api.conf"
|
|
dest: /etc/fail2ban/filter.d/pw-api.conf
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
notify: Restart fail2ban
|
|
|
|
- name: Deploy fail2ban pw-api jail
|
|
ansible.builtin.copy:
|
|
src: "{{ playbook_dir }}/../../../fail2ban/jail.d/pw.conf"
|
|
dest: /etc/fail2ban/jail.d/pw-api.conf
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
notify: Restart fail2ban
|
|
|
|
- name: Enable and start fail2ban
|
|
ansible.builtin.systemd:
|
|
name: fail2ban
|
|
enabled: true
|
|
state: started
|
|
|
|
- name: Enable and start nginx
|
|
ansible.builtin.systemd:
|
|
name: nginx
|
|
enabled: true
|
|
state: started
|
|
|
|
# ── Phase 5: Certbot renewal ──────────────────────────────────────────────────
|
|
|
|
- name: Set up certbot renewal cron
|
|
ansible.builtin.cron:
|
|
name: "Certbot renewal"
|
|
minute: "30"
|
|
hour: "3"
|
|
job: "certbot renew --quiet --post-hook 'systemctl reload nginx'"
|
|
user: root
|