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>
This commit is contained in:
parent
b6147549c9
commit
c97e42996e
2 changed files with 68 additions and 35 deletions
50
infra/ansible/roles/haproxy/files/selfsigned.pem
Normal file
50
infra/ansible/roles/haproxy/files/selfsigned.pem
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCgyerZjp1+RxU8
|
||||||
|
/bISXduo8OjR2ejl5SD034PyQvT5B9tk83yplplHoG+JL78UGqpflPlhU9fQSoT9
|
||||||
|
Walusf/MDDCEbQ75sjPui+yNuvcgWkmpN0MUdOHR8gvfiADCR6/eDQuRf7JJh5N8
|
||||||
|
YdCtLtnOYsha7Bix+bN11GO6XzPG869I/UGdg4g0v7LvDCP3tI0tpno+y4MuiDvJ
|
||||||
|
R1pQd7sl6jxPp4zvNtVw8vrSVA3qJ8G6F78nnPUUPFnrAlUFNcnMVLamxY0IA3H4
|
||||||
|
n9o7X73RnphrpcnPr6eyEYxOL0UGhsDMsQxTrhSaOErL68QDTk3hV60SxWqsVlxX
|
||||||
|
/DoKAb9VAgMBAAECggEAenTt6V3Fsxv+H+Jz0assFYHNP63/w797FyR4QHUgT93d
|
||||||
|
CQisRBjPio61A72agHxCj+NM/wQ1FIz8tluoQAdO8x/Bf8nzotZG2QI2Wkcv2bMJ
|
||||||
|
8NeGvji6mAQJaOgS8+RXG/3BdsHTjk60VAHHRW6uMZJoV18C++FZ/X6RqarCK13N
|
||||||
|
UEfHX529qNvLhw+xkjXFW/qiB3dQTTEJq+9y0U4nGrjZCXtspkXN3g6ETU6Svzhq
|
||||||
|
z4tq0udC7FjZPqdA79ChXweZlDCq89FQfxAnxRoZAiwymK91VrGz/GyMIwdBPidm
|
||||||
|
+or8Rk6nodKk8AuwsGE6ub9UhWUS+Kdpl9fNcV1jLQKBgQDRA7D786sf25tgyooF
|
||||||
|
6IMZwQfHWGmIepUPruHLz5aV6ozO8XQBgEN4XBI15mxJTu+eeXGbqOhwwuhvYR9u
|
||||||
|
G02qPE0OlftBRnBJp2AH5+gRphLyrRAvgnjVw323ucnsjOzO0TPwdehomKC0J3b9
|
||||||
|
B+hZ2tKW/nNxqX/iU1ue969lAwKBgQDE7vJnppvAZLSMo4PCtBTJm11u58AZ9LyZ
|
||||||
|
6dxvpiq6XxPw9DcC2gj91pCST2g4vIqDYQgmh5U3RzMIFsKLtKfDvHEAYbFOnEfz
|
||||||
|
UXoNFjlCEmB2jHgpn51/ZDokpPSF9MooDUFna0JPaUrduHs8Zzv7kfrsAhq2N++C
|
||||||
|
eB+jMea+xwKBgESDzEFbB85io5Vf70yugkMv9ofPIJD/ddt1PUkdHES6ZTv1BEz1
|
||||||
|
qahLriCDDx4cxQmSz73x6XgFPEI+eRoT0yqpp6zPV1R3bZmHR0BwMa+PXAi22GZq
|
||||||
|
g4e3FH/kZB+ptnq5MyhwziVzWsKTaTram7zQsVWTxW4N3QDoyFDc6l7XAoGBAI85
|
||||||
|
+bLIyZ4zn9xpT/rbXgMCrAFtK5m1FTYbj+bjw0+otqgX9aptSPzUgHDor7QT6+mB
|
||||||
|
OJxNH4kEj2jipLtWuGzzMHxGkN3La8jbCRlbgGk9VErj/sDHBZURH/hmwDBsyFo4
|
||||||
|
ycidiayXt4tqELbtngJpOUVMgoDkTZ1mIBxgvqEhAoGBAK6uX4k2xiOQorpByvjd
|
||||||
|
gT16MbuntXO/bDXnXaq1keNMr1JzQ5aS346XweiUgRG7ZJdEb2C8sXwSmh2+oeGa
|
||||||
|
G+QCLH73hwo/PWbU560dFY8s6z5E79WBjYUu5+1/a0SCBwQ4mEVB7REQVY1mQoJT
|
||||||
|
A+A8WW+EDvaPpVFujA26K3fc
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDjTCCAnWgAwIBAgIUbgZuZRFj8M8ZcdhRFikB2bJKswYwDQYJKoZIhvcNAQEL
|
||||||
|
BQAwVjELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEcMBoGA1UE
|
||||||
|
CgwTRGVmYXVsdCBDb21wYW55IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTIy
|
||||||
|
MDQwODEwMTA0OFoXDTQ5MDgyNDEwMTA0OFowVjELMAkGA1UEBhMCWFgxFTATBgNV
|
||||||
|
BAcMDERlZmF1bHQgQ2l0eTEcMBoGA1UECgwTRGVmYXVsdCBDb21wYW55IEx0ZDES
|
||||||
|
MBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
|
||||||
|
AQEAoMnq2Y6dfkcVPP2yEl3bqPDo0dno5eUg9N+D8kL0+QfbZPN8qZaZR6BviS+/
|
||||||
|
FBqqX5T5YVPX0EqE/VmpbrH/zAwwhG0O+bIz7ovsjbr3IFpJqTdDFHTh0fIL34gA
|
||||||
|
wkev3g0LkX+ySYeTfGHQrS7ZzmLIWuwYsfmzddRjul8zxvOvSP1BnYOINL+y7wwj
|
||||||
|
97SNLaZ6PsuDLog7yUdaUHe7Jeo8T6eM7zbVcPL60lQN6ifBuhe/J5z1FDxZ6wJV
|
||||||
|
BTXJzFS2psWNCANx+J/aO1+90Z6Ya6XJz6+nshGMTi9FBobAzLEMU64UmjhKy+vE
|
||||||
|
A05N4VetEsVqrFZcV/w6CgG/VQIDAQABo1MwUTAdBgNVHQ4EFgQUJZDike5gfaOV
|
||||||
|
k8uCwfCh2OrPXd0wHwYDVR0jBBgwFoAUJZDike5gfaOVk8uCwfCh2OrPXd0wDwYD
|
||||||
|
VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAQbXAIBoDHQakksvKGo3X
|
||||||
|
/bIyc+IQKFpsyWrn5GvS69wTE7XBfKLtyY3X8NygvsCaRx0r2OIdVERNjrhELkes
|
||||||
|
tWQE17D1+tDnsaEQRUNJsjBYmealNPpqqacdRlBNnkTSGM/3d3m/ihlA51A1QzyI
|
||||||
|
IOtKxRRIZ+24L/eww5Hv96ub3Wu4rVmepXP4cVIcPEnN6ntmOv4Ja/M83hLI2oXy
|
||||||
|
4XmXOVsyliYDGWiyvT2U3LcRsv9PHr09SqYO/5yW+fYC7diLGSHW0kfwht2Q8Zqg
|
||||||
|
IFMJMDmmKTbCWCmFYdoVTRm2fFl0YvgpC5JrXuSloHh3hRiLwDIUiTxlTM3JDP8q
|
||||||
|
PQ==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
@ -29,42 +29,25 @@
|
||||||
# Chicken-and-egg : haproxy.cfg.j2 references `bind *:443 ssl crt
|
# Chicken-and-egg : haproxy.cfg.j2 references `bind *:443 ssl crt
|
||||||
# {{ haproxy_tls_cert_dir }}/` ; haproxy refuses to validate the
|
# {{ haproxy_tls_cert_dir }}/` ; haproxy refuses to validate the
|
||||||
# config if that directory is empty (or missing). dehydrated creates
|
# config if that directory is empty (or missing). dehydrated creates
|
||||||
# real LE certs there LATER (in letsencrypt.yml). To break the cycle,
|
# real LE certs there LATER (in letsencrypt.yml). Break the cycle
|
||||||
# pre-create the dir with a 30-day self-signed placeholder cert.
|
# the same way the working roles in
|
||||||
# The placeholder is overwritten / shadowed once dehydrated lands ;
|
# /home/senke/Documents/TG__Talas_Group/.../roles/haproxy do : ship a
|
||||||
# SNI picks the matching real cert.
|
# checked-in `selfsigned.pem` and copy it into the cert dir.
|
||||||
- name: Ensure TLS cert dir + placeholder cert exist (gates the haproxy.cfg validate)
|
# Once dehydrated lands real certs alongside, SNI picks the matching
|
||||||
when: haproxy_letsencrypt | default(false)
|
# real cert ; selfsigned.pem only matches CN=localhost (harmless).
|
||||||
block:
|
|
||||||
- name: Ensure {{ haproxy_tls_cert_dir }} exists
|
- name: Ensure {{ haproxy_tls_cert_dir }} exists
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: "{{ haproxy_tls_cert_dir }}"
|
path: "{{ haproxy_tls_cert_dir }}"
|
||||||
state: directory
|
state: directory
|
||||||
owner: root
|
mode: "0755"
|
||||||
group: haproxy
|
tags: [haproxy, config]
|
||||||
mode: "0750"
|
|
||||||
|
|
||||||
- name: Generate self-signed placeholder cert if dir is empty
|
- name: Drop selfsigned.pem so haproxy can validate the cfg
|
||||||
ansible.builtin.shell: |
|
ansible.builtin.copy:
|
||||||
set -e
|
src: selfsigned.pem
|
||||||
if ls "{{ haproxy_tls_cert_dir }}"/*.pem >/dev/null 2>&1; then
|
dest: "{{ haproxy_tls_cert_dir }}/selfsigned.pem"
|
||||||
echo "cert already present"
|
mode: "0640"
|
||||||
exit 0
|
tags: [haproxy, config]
|
||||||
fi
|
|
||||||
openssl req -x509 -nodes -newkey rsa:2048 \
|
|
||||||
-keyout /tmp/_placeholder.key \
|
|
||||||
-out /tmp/_placeholder.crt \
|
|
||||||
-days 30 \
|
|
||||||
-subj '/CN=placeholder.veza.local' >/dev/null 2>&1
|
|
||||||
cat /tmp/_placeholder.crt /tmp/_placeholder.key \
|
|
||||||
> "{{ haproxy_tls_cert_dir }}/_placeholder.pem"
|
|
||||||
chmod 0640 "{{ haproxy_tls_cert_dir }}/_placeholder.pem"
|
|
||||||
chown root:haproxy "{{ haproxy_tls_cert_dir }}/_placeholder.pem"
|
|
||||||
rm -f /tmp/_placeholder.key /tmp/_placeholder.crt
|
|
||||||
echo "placeholder cert generated"
|
|
||||||
register: placeholder_cert
|
|
||||||
changed_when: "'placeholder cert generated' in placeholder_cert.stdout"
|
|
||||||
tags: [haproxy, config, letsencrypt]
|
|
||||||
|
|
||||||
- name: Render haproxy.cfg
|
- name: Render haproxy.cfg
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue