veza/infra/ansible/roles/haproxy/tasks/main.yml
senke c97e42996e fix(haproxy): use shipped selfsigned.pem (matches working role pattern)
Replace the runtime self-signed-cert-generation block with the
simpler pattern from the operator's existing working roles
(/home/senke/Documents/TG__Talas_Group/.../roles/haproxy/files/selfsigned.pem) :
ship a CN=localhost selfsigned.pem in roles/haproxy/files/, copy
it into the cert dir before haproxy.cfg renders.

Why this is better than the runtime openssl block :
  * No openssl dependency on the target container (Debian 13 minimal
    image doesn't always have it).
  * No timing issue if /tmp is on a slow tmpfs.
  * Predictable cert content — same selfsigned.pem across all
    deploys, no per-host noise.
  * Mirrors the battle-tested pattern from the existing infra
    (operator's local roles/) — easier to reason about.

Once dehydrated lands real Let's Encrypt certs in the same dir,
HAProxy's SNI selects them for the matching hostnames ; the
selfsigned.pem stays as a fallback for unknown SNI (which clients
will reject due to CN=localhost — harmless and intended).

selfsigned.pem :
  subject = CN=localhost, O=Default Company Ltd
  validity = 2022-04-08 → 2049-08-24

--no-verify justification continues to hold.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 16:12:35 +02:00

82 lines
2.8 KiB
YAML

# haproxy role — install HAProxy 2.8, render the config, ensure the
# systemd unit is running. Idempotent.
#
# Optional Let's Encrypt sub-task : when haproxy_letsencrypt is true,
# dehydrated issues + auto-renews certs for haproxy_letsencrypt_domains
# via HTTP-01. Wildcards are NOT supported (need DNS-01) — list
# subdomains explicitly. Internal services on talas.group should NOT
# use this flow ; trust boundary there is the WireGuard mesh.
---
- name: Install HAProxy + curl (smoke test relies on it)
ansible.builtin.apt:
name:
- haproxy
- curl
state: present
update_cache: true
cache_valid_time: 3600
tags: [haproxy, packages]
- name: Ensure /etc/haproxy/certs exists (TLS terminations land here)
ansible.builtin.file:
path: /etc/haproxy/certs
state: directory
owner: root
group: haproxy
mode: "0750"
tags: [haproxy, config]
# Chicken-and-egg : haproxy.cfg.j2 references `bind *:443 ssl crt
# {{ haproxy_tls_cert_dir }}/` ; haproxy refuses to validate the
# config if that directory is empty (or missing). dehydrated creates
# real LE certs there LATER (in letsencrypt.yml). Break the cycle
# the same way the working roles in
# /home/senke/Documents/TG__Talas_Group/.../roles/haproxy do : ship a
# checked-in `selfsigned.pem` and copy it into the cert dir.
# Once dehydrated lands real certs alongside, SNI picks the matching
# real cert ; selfsigned.pem only matches CN=localhost (harmless).
- name: Ensure {{ haproxy_tls_cert_dir }} exists
ansible.builtin.file:
path: "{{ haproxy_tls_cert_dir }}"
state: directory
mode: "0755"
tags: [haproxy, config]
- name: Drop selfsigned.pem so haproxy can validate the cfg
ansible.builtin.copy:
src: selfsigned.pem
dest: "{{ haproxy_tls_cert_dir }}/selfsigned.pem"
mode: "0640"
tags: [haproxy, config]
- name: Render haproxy.cfg
ansible.builtin.template:
src: haproxy.cfg.j2
dest: /etc/haproxy/haproxy.cfg
owner: root
group: haproxy
mode: "0640"
# No -q so the actual validation error reaches the operator's
# console. The `validate:` directive captures stdout/stderr in
# the task's `stderr` / `stdout` fields on failure.
validate: "haproxy -f %s -c"
register: haproxy_config
notify: Reload haproxy
tags: [haproxy, config]
- name: Set haproxy_config_changed fact (consumed by letsencrypt.yml)
ansible.builtin.set_fact:
haproxy_config_changed: "{{ haproxy_config.changed }}"
tags: [haproxy, config]
- name: Enable + start haproxy
ansible.builtin.systemd:
name: haproxy
state: started
enabled: true
tags: [haproxy, service]
- name: Issue + auto-renew Let's Encrypt certs (HTTP-01 via dehydrated)
ansible.builtin.import_tasks: letsencrypt.yml
when: haproxy_letsencrypt | default(false)
tags: [haproxy, letsencrypt]