Add shared nginx-proxy role; deploy Hermes in Docker; rename Paperclip
nginx-proxy (new role): - Shared nginx + certbot under /opt/nginx, replacing the copies that lived inside the Paperclip compose project - Apps publish via tasks_from: add_vhost (cert marker -> issue cert -> write vhost -> nginx -t -> reload); a vhost that fails nginx -t is rolled back - Certs issued one per domain via Cloudflare DNS challenge; domains registered as marker files in cert-domains/; optional LE staging - Stock nginx.conf plus conf.d (directory mounts, not single-file mounts); catch-all server drops unknown Host/SNI - Containers renamed to nginx and certbot hermes_docker (new role): - Official Hermes image, host networking, gateway and dashboard bound to loopback and published through nginx-proxy - Dashboard login via basic-auth provider, secrets from the vault - Main model set from hermes_model (fantasmic endpoint, key from vault); config.yaml overrides are merged from a 0600 staged file that is removed afterwards, so the key never appears in a command line - Data dir owner-only (HERMES_HOME_MODE=0700) paperclip-docker: - Remove nginx, certbot and docker-compose download tasks; deploy with community.docker.docker_compose_v2 (idempotent, remove_orphans) - Stop passing the Cloudflare token and certbot email to the Paperclip container via env_file - Bind Postgres to 127.0.0.1 (host networking exposed it on all interfaces); stop chowning pgdata, which the postgres image owns - Handler recreates the paperclip service so .env changes apply - Domain m-v.wdw-labs.cloud -> cranium-command.wdw-labs.cloud Playbooks: - Add day1_deploy_nginx_proxy.yml and day1_deploy_hermes_docker.yml - deploy_paperclip_stack.yml: run nginx-proxy before paperclip-docker, CLI-based container checks (no Docker SDK dependency) - Remove deploy-hermes.yml Host vars: add nginx_proxy_certbot_email and nginx_proxy_certbot_staging (staging stays on until the switch to real certs).
This commit is contained in:
1 parent
5b48536c32
commit
881e2e24a8
37 files changed
+1683
-755
No files matched your search
@@ -0,0 +1,118 @@
|
||||
---
|
||||
# Publish an app through nginx-proxy. Usage:
|
||||
#
|
||||
# - ansible.builtin.include_role:
|
||||
# name: nginx-proxy
|
||||
# tasks_from: add_vhost
|
||||
# vars:
|
||||
# vhost_name: paperclip # required, becomes conf.d/<name>.conf
|
||||
# vhost_domain: "{{ paperclip_domain }}" # required
|
||||
# vhost_upstream_port: 3100 # required
|
||||
# vhost_upstream_host: 127.0.0.1 # optional
|
||||
# vhost_proxy_timeout: 60s # optional (use 3600s for WebSockets)
|
||||
# vhost_health_path: /health # optional, proxied without access log
|
||||
#
|
||||
# Order: marker -> certificate -> vhost file -> nginx -t -> reload.
|
||||
# The vhost is only written once the certificate exists, and a vhost that
|
||||
# fails nginx -t is rolled back, so one bad app cannot take the others down.
|
||||
- name: Validate vhost parameters
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- vhost_name is defined
|
||||
- vhost_domain is defined
|
||||
- vhost_upstream_port is defined
|
||||
fail_msg: "add_vhost needs vhost_name, vhost_domain and vhost_upstream_port"
|
||||
quiet: true
|
||||
|
||||
- name: Check that the nginx-proxy containers are running
|
||||
ansible.builtin.command: "docker inspect -f '{% raw %}{{.State.Running}}{% endraw %}' {{ item }}"
|
||||
register: nginx_proxy_running
|
||||
failed_when: nginx_proxy_running.rc != 0 or nginx_proxy_running.stdout | trim != 'true'
|
||||
changed_when: false
|
||||
loop:
|
||||
- "{{ nginx_proxy_nginx_container }}"
|
||||
- "{{ nginx_proxy_certbot_container }}"
|
||||
loop_control:
|
||||
label: "{{ item }}"
|
||||
|
||||
- name: "Register cert marker for {{ vhost_domain }}"
|
||||
ansible.builtin.file:
|
||||
path: "{{ nginx_proxy_domains_dir }}/{{ vhost_domain }}"
|
||||
state: touch
|
||||
modification_time: preserve
|
||||
access_time: preserve
|
||||
mode: "0644"
|
||||
|
||||
- name: "Check for existing certificate for {{ vhost_domain }}"
|
||||
ansible.builtin.stat:
|
||||
path: "{{ nginx_proxy_certs_dir }}/live/{{ vhost_domain }}/fullchain.pem"
|
||||
register: nginx_proxy_cert_before
|
||||
|
||||
# Targeted run: only this domain, so another app's failed issuance
|
||||
# cannot fail this play.
|
||||
- name: "Issue certificate for {{ vhost_domain }}"
|
||||
ansible.builtin.command: >-
|
||||
docker exec {{ nginx_proxy_certbot_container }}
|
||||
/scripts/certbot-init.sh {{ vhost_domain }}
|
||||
register: nginx_proxy_cert_issue
|
||||
changed_when: "'Certificate obtained' in nginx_proxy_cert_issue.stdout"
|
||||
when: not nginx_proxy_cert_before.stat.exists
|
||||
|
||||
- name: "Confirm certificate exists for {{ vhost_domain }}"
|
||||
ansible.builtin.stat:
|
||||
path: "{{ nginx_proxy_certs_dir }}/live/{{ vhost_domain }}/fullchain.pem"
|
||||
register: nginx_proxy_cert_after
|
||||
failed_when: not nginx_proxy_cert_after.stat.exists
|
||||
|
||||
- name: "Write {{ vhost_name }} vhost, test, and reload"
|
||||
block:
|
||||
- name: "Template conf.d/{{ vhost_name }}.conf"
|
||||
ansible.builtin.template:
|
||||
src: vhost.conf.j2
|
||||
dest: "{{ nginx_proxy_conf_dir }}/{{ vhost_name }}.conf"
|
||||
mode: "0644"
|
||||
backup: true
|
||||
register: nginx_proxy_vhost_file
|
||||
|
||||
- name: Test nginx config
|
||||
ansible.builtin.command: "docker exec {{ nginx_proxy_nginx_container }} nginx -t"
|
||||
changed_when: false
|
||||
|
||||
- name: Reload nginx
|
||||
ansible.builtin.command: "docker exec {{ nginx_proxy_nginx_container }} nginx -s reload"
|
||||
when: nginx_proxy_vhost_file.changed
|
||||
|
||||
- name: Remove superseded backup
|
||||
ansible.builtin.file:
|
||||
path: "{{ nginx_proxy_vhost_file.backup_file }}"
|
||||
state: absent
|
||||
when: nginx_proxy_vhost_file.backup_file is defined
|
||||
|
||||
rescue:
|
||||
- name: Restore previous vhost
|
||||
ansible.builtin.copy:
|
||||
src: "{{ nginx_proxy_vhost_file.backup_file }}"
|
||||
dest: "{{ nginx_proxy_conf_dir }}/{{ vhost_name }}.conf"
|
||||
remote_src: true
|
||||
mode: "0644"
|
||||
when: nginx_proxy_vhost_file.backup_file | default('') | length > 0
|
||||
|
||||
- name: Remove backup after restoring it
|
||||
ansible.builtin.file:
|
||||
path: "{{ nginx_proxy_vhost_file.backup_file }}"
|
||||
state: absent
|
||||
when: nginx_proxy_vhost_file.backup_file | default('') | length > 0
|
||||
|
||||
- name: Remove the new vhost (there was no previous one)
|
||||
ansible.builtin.file:
|
||||
path: "{{ nginx_proxy_conf_dir }}/{{ vhost_name }}.conf"
|
||||
state: absent
|
||||
when:
|
||||
- nginx_proxy_vhost_file is defined
|
||||
- nginx_proxy_vhost_file.backup_file | default('') | length == 0
|
||||
|
||||
- name: Fail with context
|
||||
ansible.builtin.fail:
|
||||
msg: >-
|
||||
nginx rejected the {{ vhost_name }} vhost; it was rolled back and
|
||||
the running config is unchanged.
|
||||
@@ -0,0 +1,70 @@
|
||||
---
|
||||
- name: Assert required variables
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- nginx_proxy_certbot_email | length > 0
|
||||
- nginx_proxy_cloudflare_api_token | length > 0
|
||||
fail_msg: >-
|
||||
Set nginx_proxy_certbot_email, and make sure vault_cloudflare_api_token
|
||||
(or nginx_proxy_cloudflare_api_token) is defined.
|
||||
quiet: true
|
||||
tags: [nginx-proxy, preflight]
|
||||
|
||||
- name: Create directories
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
mode: "0755"
|
||||
loop:
|
||||
- "{{ nginx_proxy_base_path }}"
|
||||
- "{{ nginx_proxy_certs_dir }}"
|
||||
- "{{ nginx_proxy_conf_dir }}"
|
||||
- "{{ nginx_proxy_domains_dir }}"
|
||||
- "{{ nginx_proxy_scripts_dir }}"
|
||||
tags: [nginx-proxy]
|
||||
|
||||
- name: Install certbot script
|
||||
ansible.builtin.copy:
|
||||
src: certbot-init.sh
|
||||
dest: "{{ nginx_proxy_scripts_dir }}/certbot-init.sh"
|
||||
mode: "0755"
|
||||
tags: [nginx-proxy]
|
||||
|
||||
- name: Template global nginx settings
|
||||
ansible.builtin.template:
|
||||
src: 00-global.conf.j2
|
||||
dest: "{{ nginx_proxy_conf_dir }}/00-global.conf"
|
||||
mode: "0644"
|
||||
notify: Reload nginx
|
||||
tags: [nginx-proxy]
|
||||
|
||||
- name: Template compose env file
|
||||
ansible.builtin.template:
|
||||
src: env.j2
|
||||
dest: "{{ nginx_proxy_base_path }}/.env"
|
||||
mode: "0600"
|
||||
no_log: true
|
||||
tags: [nginx-proxy]
|
||||
|
||||
- name: Template compose file
|
||||
ansible.builtin.template:
|
||||
src: docker-compose.yml.j2
|
||||
dest: "{{ nginx_proxy_base_path }}/docker-compose.yml"
|
||||
mode: "0644"
|
||||
tags: [nginx-proxy]
|
||||
|
||||
- name: Deploy nginx and certbot
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ nginx_proxy_base_path }}"
|
||||
pull: "{{ nginx_proxy_pull_policy }}"
|
||||
state: present
|
||||
tags: [nginx-proxy]
|
||||
|
||||
- name: Verify nginx config is valid and the container is serving
|
||||
ansible.builtin.command: "docker exec {{ nginx_proxy_nginx_container }} nginx -t"
|
||||
register: nginx_proxy_test
|
||||
until: nginx_proxy_test.rc == 0
|
||||
retries: 6
|
||||
delay: 5
|
||||
changed_when: false
|
||||
tags: [nginx-proxy, verify]
|
||||
Reference in new issue
Block a user