From 4865efd078b81fef3c37fa8473b713042b2c7255 Mon Sep 17 00:00:00 2001 From: Sergei Poljanski Date: Fri, 13 Nov 2026 08:07:36 +0400 Subject: [PATCH] acme_forward: send services' ACME challenges to Traefik on ops The XMPP certificate covers xmpp.asxp.io and conference.asxp.io, which point at asxpio-services, so Let's Encrypt validates them there. A small nginx on port 80 passes /.well-known/acme-challenge/ over the VPC to Traefik, which holds all certificates, and answers 404 to the rest. Assisted-by: Claude:opus-5-5 --- ansible/inventory/group_vars/all/xmpp_cert.yaml | 8 ++++++++ ansible/roles/acme_forward/tasks/main.yaml | 14 ++++++++++++++ .../acme_forward/templates/docker-compose.yml.j2 | 11 +++++++++++ .../roles/acme_forward/templates/nginx.conf.j2 | 16 ++++++++++++++++ ansible/site.yaml | 1 + 5 files changed, 50 insertions(+) create mode 100644 ansible/inventory/group_vars/all/xmpp_cert.yaml create mode 100644 ansible/roles/acme_forward/tasks/main.yaml create mode 100644 ansible/roles/acme_forward/templates/docker-compose.yml.j2 create mode 100644 ansible/roles/acme_forward/templates/nginx.conf.j2 diff --git a/ansible/inventory/group_vars/all/xmpp_cert.yaml b/ansible/inventory/group_vars/all/xmpp_cert.yaml new file mode 100644 index 0000000..026c7f6 --- /dev/null +++ b/ansible/inventory/group_vars/all/xmpp_cert.yaml @@ -0,0 +1,8 @@ +--- +# The XMPP certificate is issued by Traefik on asxpio-ops (HTTP challenge; +# services forwards its challenges there, role acme_forward) and pushed to +# ejabberd on asxpio-services on every renewal (role xmpp_cert_sync). +xmpp_cert_main: xmpp.asxp.io +xmpp_cert_sans: [asxp.io, conference.asxp.io] +# renovate: datasource=docker depName=nginx +acme_forward_nginx_version: "1.31.8-alpine-slim" diff --git a/ansible/roles/acme_forward/tasks/main.yaml b/ansible/roles/acme_forward/tasks/main.yaml new file mode 100644 index 0000000..a740abc --- /dev/null +++ b/ansible/roles/acme_forward/tasks/main.yaml @@ -0,0 +1,14 @@ +--- +# Port 80 on asxpio-services exists only for Let's Encrypt: HTTP challenges +# for names pointing here (xmpp., conference.) go to Traefik on ops, which +# holds the certificates. Anything else gets a 404. +- name: Deploy the ACME challenge forwarder + ansible.builtin.include_role: + name: docker_service + vars: + service_name: acme-forward + service_base_path: "{{ services_base_path }}/acme-forward" + service_templates: + - { src: docker-compose.yml.j2, dest: docker-compose.yml } + - { src: nginx.conf.j2, dest: nginx.conf } + health_check_port: 80 diff --git a/ansible/roles/acme_forward/templates/docker-compose.yml.j2 b/ansible/roles/acme_forward/templates/docker-compose.yml.j2 new file mode 100644 index 0000000..9f1a070 --- /dev/null +++ b/ansible/roles/acme_forward/templates/docker-compose.yml.j2 @@ -0,0 +1,11 @@ +# Managed by Ansible (role acme_forward). +services: + acme-forward: + image: nginx:{{ acme_forward_nginx_version }} + container_name: acme-forward + ports: + - "80:80" + volumes: + - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro + restart: unless-stopped + mem_limit: 32m diff --git a/ansible/roles/acme_forward/templates/nginx.conf.j2 b/ansible/roles/acme_forward/templates/nginx.conf.j2 new file mode 100644 index 0000000..dfc79be --- /dev/null +++ b/ansible/roles/acme_forward/templates/nginx.conf.j2 @@ -0,0 +1,16 @@ +# Managed by Ansible (role acme_forward). +server { + listen 80 default_server; + listen [::]:80 default_server; + server_tokens off; + access_log off; + + location /.well-known/acme-challenge/ { + proxy_pass http://{{ asxpio_ops_vpc_ip }}; + proxy_set_header Host $host; + } + + location / { + return 404; + } +} diff --git a/ansible/site.yaml b/ansible/site.yaml index a50db23..c385845 100644 --- a/ansible/site.yaml +++ b/ansible/site.yaml @@ -35,6 +35,7 @@ - { role: sillytavern, tags: [sillytavern] } - { role: minecraft, tags: [minecraft] } - { role: xmpp, tags: [xmpp] } + - { role: acme_forward, tags: [acme_forward, xmpp] } - { role: xray, tags: [xray] } # Late: restarting Forgejo briefly cuts the runner off from its job log