- Fuse deploy-caddy.yml and cloudflare.yml into deploy.yml - Add env.local.j2 template for production secrets - Vault: add all production secrets - Workflow: single deploy.yml playbook - MailerService: rewrite with S/MIME signing, email tracking, unsubscribe - ngrok-sync: run as root for .env.local write access - Fix domain references to ticket.e-cosplay.fr Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
263 lines
7.7 KiB
YAML
263 lines
7.7 KiB
YAML
---
|
|
# --- Cloudflare configuration ---
|
|
- name: Configure Cloudflare
|
|
hosts: localhost
|
|
connection: local
|
|
vars_files:
|
|
- vault.yml
|
|
|
|
vars:
|
|
zone_id: "{{ cloudflare_zone_id }}"
|
|
cloudflare_record: ticket.e-cosplay.fr
|
|
server_ip: 34.90.187.4
|
|
|
|
tasks:
|
|
# --- DNS ---
|
|
- name: Create or update DNS A record
|
|
uri:
|
|
url: "https://api.cloudflare.com/client/v4/zones/{{ zone_id }}/dns_records"
|
|
method: POST
|
|
headers:
|
|
Authorization: "Bearer {{ cloudflare_api_token }}"
|
|
Content-Type: application/json
|
|
body_format: json
|
|
body:
|
|
type: A
|
|
name: "{{ cloudflare_record }}"
|
|
content: "{{ server_ip }}"
|
|
ttl: 1
|
|
proxied: true
|
|
status_code: [200, 409]
|
|
register: dns_result
|
|
ignore_errors: true
|
|
|
|
- name: Update DNS A record if already exists
|
|
when: dns_result.status == 409 or (dns_result.json is defined and not dns_result.json.success)
|
|
block:
|
|
- name: Get existing DNS record
|
|
uri:
|
|
url: "https://api.cloudflare.com/client/v4/zones/{{ zone_id }}/dns_records?name={{ cloudflare_record }}&type=A"
|
|
method: GET
|
|
headers:
|
|
Authorization: "Bearer {{ cloudflare_api_token }}"
|
|
return_content: true
|
|
register: existing_dns
|
|
|
|
- name: Update DNS record
|
|
uri:
|
|
url: "https://api.cloudflare.com/client/v4/zones/{{ zone_id }}/dns_records/{{ existing_dns.json.result[0].id }}"
|
|
method: PUT
|
|
headers:
|
|
Authorization: "Bearer {{ cloudflare_api_token }}"
|
|
Content-Type: application/json
|
|
body_format: json
|
|
body:
|
|
type: A
|
|
name: "{{ cloudflare_record }}"
|
|
content: "{{ server_ip }}"
|
|
ttl: 1
|
|
proxied: true
|
|
|
|
# --- SSL/TLS ---
|
|
- name: Set SSL mode to Full (Strict)
|
|
uri:
|
|
url: "https://api.cloudflare.com/client/v4/zones/{{ zone_id }}/settings/ssl"
|
|
method: PATCH
|
|
headers:
|
|
Authorization: "Bearer {{ cloudflare_api_token }}"
|
|
Content-Type: application/json
|
|
body_format: json
|
|
body:
|
|
value: strict
|
|
|
|
- name: Enable Always Use HTTPS
|
|
uri:
|
|
url: "https://api.cloudflare.com/client/v4/zones/{{ zone_id }}/settings/always_use_https"
|
|
method: PATCH
|
|
headers:
|
|
Authorization: "Bearer {{ cloudflare_api_token }}"
|
|
Content-Type: application/json
|
|
body_format: json
|
|
body:
|
|
value: "on"
|
|
|
|
- name: Set minimum TLS version to 1.2
|
|
uri:
|
|
url: "https://api.cloudflare.com/client/v4/zones/{{ zone_id }}/settings/min_tls_version"
|
|
method: PATCH
|
|
headers:
|
|
Authorization: "Bearer {{ cloudflare_api_token }}"
|
|
Content-Type: application/json
|
|
body_format: json
|
|
body:
|
|
value: "1.2"
|
|
|
|
# --- Security headers ---
|
|
- name: Enable HSTS
|
|
uri:
|
|
url: "https://api.cloudflare.com/client/v4/zones/{{ zone_id }}/settings/security_header"
|
|
method: PATCH
|
|
headers:
|
|
Authorization: "Bearer {{ cloudflare_api_token }}"
|
|
Content-Type: application/json
|
|
body_format: json
|
|
body:
|
|
value:
|
|
strict_transport_security:
|
|
enabled: true
|
|
max_age: 31536000
|
|
include_subdomains: true
|
|
nosniff: true
|
|
|
|
# --- Performance ---
|
|
- name: Enable Brotli compression
|
|
uri:
|
|
url: "https://api.cloudflare.com/client/v4/zones/{{ zone_id }}/settings/brotli"
|
|
method: PATCH
|
|
headers:
|
|
Authorization: "Bearer {{ cloudflare_api_token }}"
|
|
Content-Type: application/json
|
|
body_format: json
|
|
body:
|
|
value: "on"
|
|
|
|
- name: Set browser cache TTL to 1 month
|
|
uri:
|
|
url: "https://api.cloudflare.com/client/v4/zones/{{ zone_id }}/settings/browser_cache_ttl"
|
|
method: PATCH
|
|
headers:
|
|
Authorization: "Bearer {{ cloudflare_api_token }}"
|
|
Content-Type: application/json
|
|
body_format: json
|
|
body:
|
|
value: 2592000
|
|
|
|
# --- Security ---
|
|
- name: Set security level to medium
|
|
uri:
|
|
url: "https://api.cloudflare.com/client/v4/zones/{{ zone_id }}/settings/security_level"
|
|
method: PATCH
|
|
headers:
|
|
Authorization: "Bearer {{ cloudflare_api_token }}"
|
|
Content-Type: application/json
|
|
body_format: json
|
|
body:
|
|
value: medium
|
|
|
|
- name: Enable bot fight mode
|
|
uri:
|
|
url: "https://api.cloudflare.com/client/v4/zones/{{ zone_id }}/bot_management"
|
|
method: PUT
|
|
headers:
|
|
Authorization: "Bearer {{ cloudflare_api_token }}"
|
|
Content-Type: application/json
|
|
body_format: json
|
|
body:
|
|
fight_mode: true
|
|
ignore_errors: true
|
|
|
|
# --- Allow SEO bots ---
|
|
- name: Allow SEO and social media bots
|
|
uri:
|
|
url: "https://api.cloudflare.com/client/v4/zones/{{ zone_id }}/firewall/rules"
|
|
method: POST
|
|
headers:
|
|
Authorization: "Bearer {{ cloudflare_api_token }}"
|
|
Content-Type: application/json
|
|
body_format: json
|
|
body:
|
|
- filter:
|
|
expression: '(cf.client.bot) or (http.user_agent contains "Googlebot") or (http.user_agent contains "Bingbot") or (http.user_agent contains "bingbot") or (http.user_agent contains "Yandex") or (http.user_agent contains "DuckDuckBot") or (http.user_agent contains "Baiduspider") or (http.user_agent contains "facebookexternalhit") or (http.user_agent contains "Twitterbot") or (http.user_agent contains "LinkedInBot")'
|
|
action: allow
|
|
description: "Allow SEO and social media bots"
|
|
status_code: [200, 409]
|
|
ignore_errors: true
|
|
|
|
# --- Server deployment ---
|
|
- name: Deploy e-ticket to production
|
|
hosts: production
|
|
become: true
|
|
vars_files:
|
|
- vault.yml
|
|
|
|
pre_tasks:
|
|
- name: Enable maintenance mode
|
|
command: make maintenance_on
|
|
args:
|
|
chdir: /var/www/e-ticket
|
|
|
|
tasks:
|
|
- name: Deploy .env.local
|
|
template:
|
|
src: env.local.j2
|
|
dest: /var/www/e-ticket/.env.local
|
|
owner: bot
|
|
group: bot
|
|
mode: "0600"
|
|
|
|
- name: Stop production containers
|
|
command: make stop_prod
|
|
args:
|
|
chdir: /var/www/e-ticket
|
|
|
|
- name: Install dependencies and build assets
|
|
command: make install_prod
|
|
args:
|
|
chdir: /var/www/e-ticket
|
|
|
|
- name: Start production containers
|
|
command: make start_prod
|
|
args:
|
|
chdir: /var/www/e-ticket
|
|
|
|
- name: Run migrations
|
|
command: make migrate_prod
|
|
args:
|
|
chdir: /var/www/e-ticket
|
|
|
|
- name: Clear cache
|
|
command: make clear_prod
|
|
args:
|
|
chdir: /var/www/e-ticket
|
|
|
|
- name: Ensure Caddy sites directory exists
|
|
file:
|
|
path: /etc/caddy/sites
|
|
state: directory
|
|
owner: root
|
|
group: root
|
|
mode: "0755"
|
|
|
|
- name: Deploy Caddy config
|
|
template:
|
|
src: caddy.j2
|
|
dest: /etc/caddy/sites/e-ticket.conf
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
notify: Reload Caddy
|
|
|
|
- name: Deploy Messenger supervisor config
|
|
template:
|
|
src: messenger.j2
|
|
dest: /etc/supervisor/conf.d/e-ticket.conf
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
notify: Reload Supervisor
|
|
|
|
post_tasks:
|
|
- name: Disable maintenance mode
|
|
command: make maintenance_off
|
|
args:
|
|
chdir: /var/www/e-ticket
|
|
|
|
handlers:
|
|
- name: Reload Caddy
|
|
systemd:
|
|
name: caddy
|
|
state: reloaded
|
|
|
|
- name: Reload Supervisor
|
|
command: supervisorctl reread && supervisorctl update
|