From e77a9633906d6e3c14c099b813309568f0f7e683 Mon Sep 17 00:00:00 2001 From: Ronni Baslund Date: Wed, 10 Jun 2026 21:58:35 +0200 Subject: [PATCH] feat(infra): real TLS for mail.dezky.eu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The cert-sync timer waited forever for a mail/mail-tls secret no Certificate resource ever requested — Stalwart served self-signed certs since install, so mail clients refused the IMAP handshake ('cannot verify account name or password' in Apple Mail). Adds the cert-manager Certificate (HTTP-01 via Traefik on :80) and documents the v0.16 wrinkle: TLS files aren't read from config anymore; a one-time file-backed x:Certificate object (created via management JMAP) points at the synced paths, after which cert-sync renewals keep working unchanged. Verified: :993 now serves the Let's Encrypt cert, verify rc=0. --- .../fleet/cert-manager/mail-certificate.yaml | 29 +++++++++++++++++++ .../production/host/stalwart/cert-sync.sh | 8 +++++ 2 files changed, 37 insertions(+) create mode 100644 infrastructure/production/fleet/cert-manager/mail-certificate.yaml diff --git a/infrastructure/production/fleet/cert-manager/mail-certificate.yaml b/infrastructure/production/fleet/cert-manager/mail-certificate.yaml new file mode 100644 index 0000000..fd5bf7a --- /dev/null +++ b/infrastructure/production/fleet/cert-manager/mail-certificate.yaml @@ -0,0 +1,29 @@ +# TLS for mail.dezky.eu — issued in-cluster by cert-manager, consumed on the +# HOST by Stalwart: stalwart-cert-sync.timer (host/stalwart/cert-sync.sh) +# copies the mail/mail-tls secret to /opt/stalwart/etc/tls every 12h and +# reloads Stalwart when it changes. Until this Certificate is Ready, Stalwart +# serves the self-signed bootstrap cert and mail clients refuse the TLS +# handshake ("cannot verify account name or password" in Apple Mail). +# +# HTTP-01 works because Traefik owns :80 on the node and cert-manager's +# solver ingress answers /.well-known/acme-challenge for any Host. +# +# Apply by hand with the rest of the cert-manager layer (see RUNBOOK): +# kubectl apply -f mail-certificate.yaml +apiVersion: v1 +kind: Namespace +metadata: + name: mail +--- +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: mail-dezky-eu + namespace: mail +spec: + secretName: mail-tls + dnsNames: + - mail.dezky.eu + issuerRef: + name: letsencrypt-prod + kind: ClusterIssuer diff --git a/infrastructure/production/host/stalwart/cert-sync.sh b/infrastructure/production/host/stalwart/cert-sync.sh index 8290a2a..a342c2f 100755 --- a/infrastructure/production/host/stalwart/cert-sync.sh +++ b/infrastructure/production/host/stalwart/cert-sync.sh @@ -7,6 +7,14 @@ # # Run by stalwart-cert-sync.timer (every 12h + on boot). Safe to run by hand. # +# v0.16 NOTE: Stalwart no longer reads TLS files directly from config.toml. +# A one-time x:Certificate object (management JMAP) points at these paths +# with the File variant: +# {"certificate":{"@type":"File","filePath":"/opt/stalwart/etc/tls/cert.pem"}, +# "privateKey":{"@type":"File","filePath":"/opt/stalwart/etc/tls/key.pem"}} +# Created 2026-06-10. With that in place this script's file update + reload +# keeps working for renewals exactly as designed. +# # Forward dependency: needs the fleet layer to have created the TLS secret # (default: namespace 'mail', secret 'mail-tls'). Until then this is a no-op and # Stalwart keeps using the self-signed bootstrap cert from install.sh.