This commit is contained in:
Andreas Knuth 2026-02-23 10:00:36 -06:00
parent 98c78d8dce
commit 7920ab07b8
2 changed files with 36 additions and 177 deletions

View File

@ -6,6 +6,7 @@
# Liest Domains aus dem laufenden DMS und erstellt: # Liest Domains aus dem laufenden DMS und erstellt:
# - docker-data/dms/config/dovecot-sni.cf # - docker-data/dms/config/dovecot-sni.cf
# - docker-data/dms/config/postfix-main.cf # - docker-data/dms/config/postfix-main.cf
# - docker-data/dms/config/postfix-sni.map (NEU für Postfix SNI)
# #
# Cert-Konvention (Caddy Wildcard): # Cert-Konvention (Caddy Wildcard):
# Caddy speichert *.domain.tld unter: wildcard_.domain.tld/wildcard_.domain.tld.crt # Caddy speichert *.domain.tld unter: wildcard_.domain.tld/wildcard_.domain.tld.crt
@ -53,7 +54,6 @@ echo " Gefundene Domains:"
for d in $DOMAINS; do echo " - $d"; done for d in $DOMAINS; do echo " - $d"; done
# --- Cert-Pfad Hilfsfunktionen --- # --- Cert-Pfad Hilfsfunktionen ---
# Caddy speichert Wildcard-Certs unter: wildcard_.domain.tld/wildcard_.domain.tld.crt
wildcard_cert_path() { wildcard_cert_path() {
echo "$CERTS_BASE_PATH/wildcard_.${1}/wildcard_.${1}.crt" echo "$CERTS_BASE_PATH/wildcard_.${1}/wildcard_.${1}.crt"
} }
@ -76,13 +76,14 @@ for domain in $DOMAINS; do
DOMAINS_OK="$DOMAINS_OK $domain" DOMAINS_OK="$DOMAINS_OK $domain"
else else
echo " ⚠️ $domain → KEIN Cert unter $CERT_PATH" echo " ⚠️ $domain → KEIN Cert unter $CERT_PATH"
echo " → update-caddy-certs.sh ausführen + caddy reload!" echo " → update-caddy-certs.sh ausführen + caddy reload!"
DOMAINS_MISSING="$DOMAINS_MISSING $domain" DOMAINS_MISSING="$DOMAINS_MISSING $domain"
fi fi
done done
# Node-Hostname Cert prüfen (direktes Cert, kein Wildcard) # Node-Hostname Cert prüfen (direktes Cert, kein Wildcard)
NODE_CERT_PATH="$CERTS_BASE_PATH/$NODE_HOSTNAME/$NODE_HOSTNAME.crt" NODE_CERT_PATH="$CERTS_BASE_PATH/$NODE_HOSTNAME/$NODE_HOSTNAME.crt"
NODE_KEY_PATH="$CERTS_BASE_PATH/$NODE_HOSTNAME/$NODE_HOSTNAME.key"
if docker exec "$DMS_CONTAINER" test -f "$NODE_CERT_PATH" 2>/dev/null; then if docker exec "$DMS_CONTAINER" test -f "$NODE_CERT_PATH" 2>/dev/null; then
echo "$NODE_HOSTNAME → Cert vorhanden (Node Default)" echo "$NODE_HOSTNAME → Cert vorhanden (Node Default)"
else else
@ -113,13 +114,6 @@ cat > "$DOVECOT_CFG" << 'HEADER'
# SNI-basierte Zertifikat-Auswahl für Dovecot (IMAP/POP3). # SNI-basierte Zertifikat-Auswahl für Dovecot (IMAP/POP3).
# Dovecot liest dieses File über den Volume-Mount in /tmp/docker-mailserver/ # Dovecot liest dieses File über den Volume-Mount in /tmp/docker-mailserver/
# und wendet es automatisch an. # und wendet es automatisch an.
#
# Caddy Wildcard-Cert Pfad-Schema:
# wildcard_.domain.tld/wildcard_.domain.tld.crt|.key
#
# Volume-Mount in docker-compose.yml:
# - ./docker-data/dms/config/dovecot-sni.cf:/tmp/docker-mailserver/dovecot-sni.cf:ro
HEADER HEADER
for domain in $DOMAINS_OK; do for domain in $DOMAINS_OK; do
@ -149,56 +143,49 @@ EOF
done done
echo " ✅ Dovecot SNI: $(echo $DOMAINS_OK | wc -w) Domain(s)" echo " ✅ Dovecot SNI: $(echo $DOMAINS_OK | wc -w) Domain(s)"
echo ""
echo " --- dovecot-sni.cf Inhalt ---"
cat "$DOVECOT_CFG"
echo " --- Ende ---"
# ================================================================ # ================================================================
# POSTFIX SNI Konfiguration # POSTFIX SNI Konfiguration (Neu geschrieben für echte SNI Maps)
# ================================================================ # ================================================================
POSTFIX_CFG="$CONFIG_DIR/postfix-main.cf" POSTFIX_CFG="$CONFIG_DIR/postfix-main.cf"
POSTFIX_MAP="$CONFIG_DIR/postfix-sni.map"
echo "" echo ""
echo "📝 Generiere: $POSTFIX_CFG" echo "📝 Generiere: $POSTFIX_CFG und $POSTFIX_MAP"
if [ -f "$POSTFIX_CFG" ]; then if [ -f "$POSTFIX_CFG" ]; then
cp "$POSTFIX_CFG" "${POSTFIX_CFG}.bak.$(date +%Y%m%d%H%M%S)" cp "$POSTFIX_CFG" "${POSTFIX_CFG}.bak.$(date +%Y%m%d%H%M%S)"
echo " Backup: ${POSTFIX_CFG}.bak.*"
fi fi
# smtpd_tls_chain_files: Key + Cert Paar pro Domain # 1. postfix-main.cf erstellen
# Postfix wählt automatisch per SNI das passende Paar cat > "$POSTFIX_CFG" << POSTFIX_EOF
CHAIN_LINES="" # postfix-main.cf - Automatisch generiert von setup-dms-tls.sh
#
# 1. Fallback-Zertifikat (Wird genutzt, wenn kein SNI-Match gefunden wird)
smtpd_tls_chain_files = ${NODE_KEY_PATH}, ${NODE_CERT_PATH}
# 2. SNI-Mapping aktivieren
# Wir nutzen 'texthash', damit Postfix die Map direkt lesen kann,
# ohne dass 'postmap' ausgeführt werden muss!
tls_server_sni_maps = texthash:/tmp/docker-mailserver/postfix-sni.map
POSTFIX_EOF
# 2. postfix-sni.map erstellen
echo "# postfix-sni.map - Automatisch generiert (Format: host key_pfad cert_pfad)" > "$POSTFIX_MAP"
for domain in $DOMAINS_OK; do for domain in $DOMAINS_OK; do
KEY_PATH=$(wildcard_key_path "$domain") KEY_PATH=$(wildcard_key_path "$domain")
CERT_PATH=$(wildcard_cert_path "$domain") CERT_PATH=$(wildcard_cert_path "$domain")
if [ -z "$CHAIN_LINES" ]; then
CHAIN_LINES=" $KEY_PATH, $CERT_PATH" cat >> "$POSTFIX_MAP" << EOF
else mail.${domain} ${KEY_PATH} ${CERT_PATH}
CHAIN_LINES="$CHAIN_LINES,\n $KEY_PATH, $CERT_PATH" smtp.${domain} ${KEY_PATH} ${CERT_PATH}
fi imap.${domain} ${KEY_PATH} ${CERT_PATH}
pop.${domain} ${KEY_PATH} ${CERT_PATH}
${domain} ${KEY_PATH} ${CERT_PATH}
EOF
done done
cat > "$POSTFIX_CFG" << POSTFIX_EOF echo " ✅ Postfix SNI: $(echo $DOMAINS_OK | wc -w) Domain(s) konfiguriert"
# postfix-main.cf - Automatisch generiert von setup-dms-tls.sh
# Postfix SNI-Konfiguration: pro Kundendomain ein Key/Cert-Paar.
# Postfix wählt beim TLS-Handshake das passende Paar per SNI.
# DMS lädt dieses File automatisch beim Start.
#
# Caddy Wildcard-Cert Pfad-Schema:
# wildcard_.domain.tld/wildcard_.domain.tld.crt|.key
# TLS Chain: Key + Cert Paare (Postfix >= 3.4)
smtpd_tls_chain_files =
$(printf '%b' "$CHAIN_LINES")
POSTFIX_EOF
echo " ✅ Postfix SNI: $(echo $DOMAINS_OK | wc -w) Domain(s)"
echo ""
echo " --- postfix-main.cf Inhalt ---"
cat "$POSTFIX_CFG"
echo " --- Ende ---"
# ================================================================ # ================================================================
# Zusammenfassung # Zusammenfassung
@ -207,20 +194,14 @@ echo ""
echo "============================================================" echo "============================================================"
echo "✅ Konfigurationen generiert." echo "✅ Konfigurationen generiert."
echo "" echo ""
echo "🔄 Lade Postfix und Dovecot neu (ohne Downtime)..."
docker exec "$DMS_CONTAINER" postfix reload || echo "⚠️ Postfix Reload fehlgeschlagen"
docker exec "$DMS_CONTAINER" dovecot reload || echo "⚠️ Dovecot Reload fehlgeschlagen"
echo ""
echo "📋 Nächste Schritte:" echo "📋 Nächste Schritte:"
echo "" echo ""
echo "1. DMS neu starten:" echo "1. TLS testen (SNI):"
echo " docker compose restart mailserver"
echo ""
echo "2. TLS testen (SNI):"
for domain in $DOMAINS_OK; do for domain in $DOMAINS_OK; do
echo " openssl s_client -connect mail.$domain:993 -servername mail.$domain 2>/dev/null | grep 'subject\|issuer'" echo " openssl s_client -connect mail.$domain:993 -servername mail.$domain 2>/dev/null | grep 'subject\|issuer'"
done done
echo ""
echo "3. Bei neuen Domains:"
echo " a) Accounts anlegen: ./manage_mail_user.sh add user@newdomain.com PW"
echo " b) Im Caddy-Dir: ./update-caddy-certs.sh && docker exec caddy caddy reload ..."
echo " c) Warten bis Cert generiert (~30s)"
echo " d) Dieses Script erneut ausführen"
echo " e) docker compose restart mailserver"
echo "============================================================" echo "============================================================"

View File

@ -1,122 +0,0 @@
#!/bin/bash
# setup-dms-tls.sh
# Generiert SNI-Konfigurationen für Dovecot und Postfix anhand der DMS-Accounts.
#
# Vorbereitung:
# Stelle sicher, dass dieses Skript ausführbar ist: chmod +x setup-dms-tls.sh
set -e
DMS_CONTAINER=${DMS_CONTAINER:-"mailserver"}
CONFIG_DIR="./docker-data/dms/config"
NODE_HOSTNAME=${NODE_HOSTNAME:-"node1.email-srvr.com"}
# Zieldateien im Config-Verzeichnis (werden in den Container gemountet)
DOVECOT_SNI_FILE="$CONFIG_DIR/dovecot-sni.cf"
POSTFIX_MAIN_FILE="$CONFIG_DIR/postfix-main.cf"
POSTFIX_SNI_FILE="$CONFIG_DIR/postfix-sni.map"
echo "============================================================"
echo " 🔐 DMS SNI-Config Generator (Postfix & Dovecot)"
echo " DMS Container: $DMS_CONTAINER"
echo " Node Hostname: $NODE_HOSTNAME"
echo "============================================================"
# Sicherstellen, dass das Config-Verzeichnis existiert
mkdir -p "$CONFIG_DIR"
# --- Domains aus DMS lesen ---
echo "📋 Lese Domains aus DMS..."
DOMAINS=$(docker exec "$DMS_CONTAINER" setup email list 2>/dev/null \
| grep -oP '(?<=@)[^\s]+' \
| sort -u)
if [ -z "$DOMAINS" ]; then
echo "⚠️ Keine DMS-Accounts gefunden."
# Wir erstellen trotzdem leere/Basis-Dateien, damit Postfix nicht abstürzt
fi
# ==========================================
# 1. Postfix Main Config (postfix-main.cf)
# ==========================================
echo "📝 Erstelle $POSTFIX_MAIN_FILE ..."
cat <<EOF > "$POSTFIX_MAIN_FILE"
# postfix-main.cf - Automatisch generiert von setup-dms-tls.sh
#
# 1. Fallback-Zertifikat (Wird genutzt, wenn kein SNI-Match gefunden wird)
smtpd_tls_chain_files = /etc/mail/certs/${NODE_HOSTNAME}/${NODE_HOSTNAME}.key, /etc/mail/certs/${NODE_HOSTNAME}/${NODE_HOSTNAME}.crt
# 2. SNI-Mapping aktivieren
# Wir nutzen 'texthash', damit Postfix die Klartext-Map direkt lesen kann,
# ohne dass 'postmap' im Container ausgeführt werden muss!
tls_server_sni_maps = texthash:/tmp/docker-mailserver/postfix-sni.map
EOF
# ==========================================
# 2. Dovecot & Postfix SNI Maps initialisieren
# ==========================================
echo "📝 Initialisiere Map-Dateien..."
echo "# dovecot-sni.cf - Automatisch generiert" > "$DOVECOT_SNI_FILE"
echo "# postfix-sni.map - Automatisch generiert (Format: domain key_pfad cert_pfad)" > "$POSTFIX_SNI_FILE"
# ==========================================
# 3. Schleife über alle Kundendomains
# ==========================================
for domain in $DOMAINS; do
echo " -> Füge SNI-Regeln für $domain hinzu..."
# Pfade, wie Caddy sie für Wildcards anlegt (wildcard_.domain.tld)
CERT_DIR="wildcard_.${domain}"
CERT_KEY="/etc/mail/certs/${CERT_DIR}/${CERT_DIR}.key"
CERT_CRT="/etc/mail/certs/${CERT_DIR}/${CERT_DIR}.crt"
# --- Dovecot Block ---
cat <<EOF >> "$DOVECOT_SNI_FILE"
# --- $domain ---
local_name mail.${domain} {
ssl_cert = <${CERT_CRT}
ssl_key = <${CERT_KEY}
}
local_name imap.${domain} {
ssl_cert = <${CERT_CRT}
ssl_key = <${CERT_KEY}
}
local_name smtp.${domain} {
ssl_cert = <${CERT_CRT}
ssl_key = <${CERT_KEY}
}
local_name pop.${domain} {
ssl_cert = <${CERT_CRT}
ssl_key = <${CERT_KEY}
}
local_name ${domain} {
ssl_cert = <${CERT_CRT}
ssl_key = <${CERT_KEY}
}
EOF
# --- Postfix Map Einträge ---
# Bei Postfix SNI Maps müssen Key und Cert in einer Zeile hinter dem Hostnamen stehen
cat <<EOF >> "$POSTFIX_SNI_FILE"
mail.${domain} ${CERT_KEY} ${CERT_CRT}
smtp.${domain} ${CERT_KEY} ${CERT_CRT}
imap.${domain} ${CERT_KEY} ${CERT_CRT}
pop.${domain} ${CERT_KEY} ${CERT_CRT}
${domain} ${CERT_KEY} ${CERT_CRT}
EOF
done
echo ""
echo "✅ Alle Konfigurationsdateien erfolgreich generiert!"
# ==========================================
# 4. Dienste im Container neu laden
# ==========================================
echo "🔄 Lade Postfix und Dovecot neu (ohne Downtime)..."
docker exec "$DMS_CONTAINER" postfix reload || echo "⚠️ Postfix Reload fehlgeschlagen (Container läuft nicht?)"
docker exec "$DMS_CONTAINER" dovecot reload || echo "⚠️ Dovecot Reload fehlgeschlagen"
echo "============================================================"
echo "🎉 Fertig!"