infra/roles/postfix/tasks/main.yml
Jeremie Fraeys 3e0e97a00c
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
2026-03-06 14:25:10 -05:00

90 lines
3.6 KiB
YAML

---
- name: Configure Postfix (send-only)
block:
- 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('587', true), true) }}"
no_log: true
- name: Read Postfix relay host password
set_fact:
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"
when: (postfix_relayhost_username | length == 0) != (postfix_relayhost_password | length == 0)
- name: Read Postfix allowed sender domains
set_fact:
postfix_allowed_sender_domains: "{{ POSTFIX_ALLOWED_SENDER_DOMAINS | default(lookup('env', 'POSTFIX_ALLOWED_SENDER_DOMAINS') | default('', true), true) }}"
no_log: true
- name: Read Postfix allow empty sender domains
set_fact:
postfix_allow_empty_sender_domains: "{{ (POSTFIX_ALLOW_EMPTY_SENDER_DOMAINS | default(lookup('env', 'POSTFIX_ALLOW_EMPTY_SENDER_DOMAINS') | default('true', true), true)) | bool }}"
no_log: true
- name: Read Postfix SMTP TLS security level
set_fact:
postfix_smtp_tls_security_level: "{{ POSTFIX_SMTP_TLS_SECURITY_LEVEL | default(lookup('env', 'POSTFIX_SMTP_TLS_SECURITY_LEVEL') | default('may', true), true) }}"
no_log: true
- name: Create Postfix directory
file:
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
dest: /opt/postfix/docker-compose.yml
- name: Deploy Postfix
command: docker compose up -d
args:
chdir: /opt/postfix