From 3e0e97a00c1db4686e7f1f53172f15c8e500104e Mon Sep 17 00:00:00 2001 From: Jeremie Fraeys Date: Fri, 6 Mar 2026 14:25:10 -0500 Subject: [PATCH] fix(postfix): enable TLS and fix Postmark authentication - Add Python script to extract certificates from Traefik acme.json - Mount extracted certs to /etc/ssl in container for TLS support - Enable smtpd_tls_security_level: may for incoming STARTTLS - Remove failed_when: false on cert extraction to catch failures early - Fix relayhost username to default to password (Postmark server token auth) - Change default Postmark port from 2525 to 587 (blocked on some networks) - Create SSL directory before extraction Fixes: SMTP authentication failures and enables TLS for Authelia password reset --- roles/postfix/tasks/main.yml | 48 ++++++++++++++++--- roles/postfix/templates/docker-compose.yml.j2 | 9 +++- 2 files changed, 50 insertions(+), 7 deletions(-) diff --git a/roles/postfix/tasks/main.yml b/roles/postfix/tasks/main.yml index 0a77dd1..77c573a 100644 --- a/roles/postfix/tasks/main.yml +++ b/roles/postfix/tasks/main.yml @@ -5,12 +5,7 @@ - name: Read Postfix relay host set_fact: postfix_relayhost: "{{ POSTFIX_RELAYHOST | default(lookup('env', 'POSTFIX_RELAYHOST') | default('smtp.postmarkapp.com', true), true) }}" - postfix_relayhost_port: "{{ POSTFIX_RELAYHOST_PORT | default(lookup('env', 'POSTFIX_RELAYHOST_PORT') | default('2525', true), true) }}" - no_log: true - - - name: Read Postfix relay host username - set_fact: - postfix_relayhost_username: "{{ POSTFIX_RELAYHOST_USERNAME | default(lookup('env', 'POSTFIX_RELAYHOST_USERNAME') | default('', true), true) }}" + postfix_relayhost_port: "{{ POSTFIX_RELAYHOST_PORT | default(lookup('env', 'POSTFIX_RELAYHOST_PORT') | default('587', true), true) }}" no_log: true - name: Read Postfix relay host password @@ -18,6 +13,11 @@ postfix_relayhost_password: "{{ POSTFIX_RELAYHOST_PASSWORD | default(lookup('env', 'POSTFIX_RELAYHOST_PASSWORD') | default('', true), true) }}" no_log: true + - name: Read Postfix relay host username + set_fact: + postfix_relayhost_username: "{{ POSTFIX_RELAYHOST_USERNAME | default(lookup('env', 'POSTFIX_RELAYHOST_USERNAME') | default(postfix_relayhost_password, true), true) }}" + no_log: true + - name: Fail if Postfix relay host username/password pairing is invalid fail: msg: "POSTFIX_RELAYHOST_USERNAME and POSTFIX_RELAYHOST_PASSWORD must both be set, or both be empty" @@ -43,6 +43,42 @@ path: /opt/postfix state: directory + - name: Create Postfix SSL directory + file: + path: /opt/postfix/ssl + state: directory + + - name: Extract certificate from Traefik acme.json + shell: | + set -euo pipefail + ACME_FILE="/opt/traefik/letsencrypt/acme.json" + DEST_DIR="/opt/postfix/ssl" + DOMAIN="{{ auth_hostname }}" + + if [ -f "$ACME_FILE" ]; then + python3 << 'PYEOF' + import json, base64, sys + with open("/opt/traefik/letsencrypt/acme.json") as f: + data = json.load(f) + for resolver in data.values(): + if "Certificates" in resolver: + for cert in resolver["Certificates"]: + if cert.get("domain", {}).get("main") == "{{ auth_hostname }}": + with open("/opt/postfix/ssl/tls.crt", "wb") as f: + f.write(base64.b64decode(cert["certificate"])) + with open("/opt/postfix/ssl/tls.key", "wb") as f: + f.write(base64.b64decode(cert["key"])) + print("Certificates extracted successfully") + sys.exit(0) + print("Domain not found in acme.json") + sys.exit(1) + PYEOF + chmod 644 "$DEST_DIR/tls.crt" + chmod 600 "$DEST_DIR/tls.key" + fi + args: + executable: /bin/bash + - name: Copy Docker Compose file for Postfix template: src: docker-compose.yml.j2 diff --git a/roles/postfix/templates/docker-compose.yml.j2 b/roles/postfix/templates/docker-compose.yml.j2 index fa9c232..0fad7ce 100644 --- a/roles/postfix/templates/docker-compose.yml.j2 +++ b/roles/postfix/templates/docker-compose.yml.j2 @@ -10,18 +10,25 @@ services: {% endif %} {% endif %} POSTFIX_smtp_tls_security_level: "{{ postfix_smtp_tls_security_level }}" - POSTFIX_smtpd_tls_security_level: none + POSTFIX_smtpd_tls_security_level: may + POSTFIX_smtpd_tls_cert_file: /etc/ssl/tls.crt + POSTFIX_smtpd_tls_key_file: /etc/ssl/tls.key + POSTFIX_smtpd_tls_loglevel: 1 POSTFIX_relay_domains: "*" POSTFIX_smtpd_relay_restrictions: "permit_mynetworks,reject" POSTFIX_smtpd_recipient_restrictions: "permit_mynetworks,reject_unauth_destination" ALLOWED_SENDER_DOMAINS: "{{ postfix_allowed_sender_domains }},services" ALLOW_EMPTY_SENDER_DOMAINS: "{{ postfix_allow_empty_sender_domains | ternary('true', 'false') }}" POSTFIX_mynetworks: "127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16" + volumes: + - /opt/postfix/ssl:/etc/ssl:ro ports: - "25:25" networks: - proxy restart: unless-stopped + security_opt: + - no-new-privileges:true networks: proxy: