diff --git a/DMS/docker-compose.yml b/DMS/docker-compose.yml index 978f24a..d7efbad 100644 --- a/DMS/docker-compose.yml +++ b/DMS/docker-compose.yml @@ -1,23 +1,26 @@ services: mailserver: - # image: docker.io/mailserver/docker-mailserver:latest # AUSKOMMENTIERT build: context: . dockerfile: Dockerfile image: dms-custom:latest container_name: mailserver - hostname: mail.email-srvr.com - domainname: email-srvr.com + + # Node-spezifischer Hostname - A-Record zeigt auf DIESEN Server. + # email-srvr.com selbst zeigt auf einen anderen Server und wird hier NICHT verwendet. + hostname: node1.email-srvr.com + ports: - - "25:25" # SMTP (parallel zu MailCow auf Port 25) - - "587:587" # SMTP Submission - - "465:465" # SMTP SSL - - "143:143" # IMAP - - "993:993" # IMAP SSL - - "110:110" # POP3 - - "995:995" # POP3 SSL - - "127.0.0.1:11334:11334" # Bindet nur an Localhost! + - "25:25" + - "587:587" + - "465:465" + - "143:143" + - "993:993" + - "110:110" + - "995:995" + - "127.0.0.1:11334:11334" + volumes: - ./docker-data/dms/mail-data/:/var/mail/ - ./docker-data/dms/mail-state/:/var/mail-state/ @@ -27,67 +30,88 @@ services: - /etc/localtime:/etc/localtime:ro - ./sync_dynamodb_to_sieve.py:/scripts/sync.py:ro - ./sieve-cron:/etc/cron.d/sieve-sync:ro - - /var/lib/docker/volumes/caddy_data/_data/caddy/certificates/acme-v02.api.letsencrypt.org-directory/mail.email-srvr.com:/etc/mail/certs:ro + + # ------------------------------------------------------- + # Caddy Zertifikate: gesamtes Cert-Verzeichnis mounten. + # + # Caddy legt Wildcard-Certs so ab: + # *.andreasknuth.de/ + # *.andreasknuth.de.crt + # *.andreasknuth.de.key + # node1.email-srvr.com/ + # node1.email-srvr.com.crt + # node1.email-srvr.com.key + # + # setup-dms-tls.sh referenziert per: + # /etc/mail/certs/*.domain/*.domain.crt|.key + # ------------------------------------------------------- + - /var/lib/docker/volumes/caddy_data/_data/caddy/certificates/acme-v02.api.letsencrypt.org-directory:/etc/mail/certs:ro + + # ------------------------------------------------------- + # Dovecot SNI Konfiguration (generiert von setup-dms-tls.sh) + # DMS lädt /tmp/docker-mailserver/dovecot-sni.cf automatisch. + # ------------------------------------------------------- + - ./docker-data/dms/config/dovecot-sni.cf:/tmp/docker-mailserver/dovecot-sni.cf:ro + environment: - # Wichtig: Rspamd und andere Services deaktivieren für ersten Test + # ------------------------------------------------------- + # SSL Default-Cert: node1.email-srvr.com + # Das ist das Fallback-Cert wenn kein SNI-Match gefunden wird + # (z.B. bei direktem IP-Connect ohne Hostname). + # Kundendomain-SNI wird über postfix-main.cf + dovecot-sni.cf gesteuert. + # ------------------------------------------------------- - SSL_TYPE=manual - # Diese Pfade beziehen sich auf das INNERE des Containers (wo wir hin mounten) - - SSL_CERT_PATH=/etc/mail/certs/mail.email-srvr.com.crt - - SSL_KEY_PATH=/etc/mail/certs/mail.email-srvr.com.key + - SSL_CERT_PATH=/etc/mail/certs/node1.email-srvr.com/node1.email-srvr.com.crt + - SSL_KEY_PATH=/etc/mail/certs/node1.email-srvr.com/node1.email-srvr.com.key + + # SPAM / Rspamd - ENABLE_OPENDKIM=1 - ENABLE_OPENDMARC=0 - ENABLE_POLICYD_SPF=0 - # #### SPAM SECTION ##### - # SPAM Rspamd aktivieren - ENABLE_RSPAMD=1 - # Greylisting AUS (vermeidet Verzögerungen) - RSPAMD_GREYLISTING=0 - # Eigene Mails NICHT scannen (vermeidet Probleme beim Senden) - RSPAMD_CHECK_AUTHENTICATED=0 - # Hostname Check AN (filtert Botnets, sehr sicher) - RSPAMD_HFILTER=1 - # Spam sortieren statt löschen (Sieve Magic) - MOVE_SPAM_TO_JUNK=1 - # Alte Dienste aus - ENABLE_AMAVIS=0 - ENABLE_SPAMASSASSIN=0 - ENABLE_POSTGREY=0 - # 2. ClamAV deaktivieren (Anti-Virus) - ENABLE_CLAMAV=0 - # HACKERSCHUTZ (Pflicht!) + + # Sicherheit - ENABLE_FAIL2BAN=1 - # DNS Resolver (verhindert Spamhaus-Probleme) - - ENABLE_UNBOUND=1 - # #### END SPAM SECTION ##### - # END SPAM SECTION + - ENABLE_UNBOUND=1 + + # Sonstige - ENABLE_MANAGESIEVE=0 - ENABLE_POP3=1 - RSPAMD_LEARN=1 - ONE_DIR=1 - ENABLE_UPDATE_CHECK=0 - PERMIT_DOCKER=network - # - PERMIT_DOCKER=empty - - SSL_TYPE=manual - - SSL_CERT_PATH=/tmp/docker-mailserver/ssl/cert.pem - - SSL_KEY_PATH=/tmp/docker-mailserver/ssl/key.pem - # Amazon SES SMTP Relay + - SPOOF_PROTECTION=0 + - ENABLE_SRS=0 + - LOG_LEVEL=info + + # Amazon SES Relay - RELAY_HOST=email-smtp.us-east-2.amazonaws.com - RELAY_PORT=587 - RELAY_USER=${SES_SMTP_USER} - RELAY_PASSWORD=${SES_SMTP_PASSWORD} - # Content Filter AWS Credentials + + # AWS Credentials - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} - - AWS_REGION=us-east-2 - # Weitere Einstellungen - - POSTFIX_OVERRIDE_HOSTNAME=email-srvr.com + - AWS_REGION=us-east-2 + + # Postfix + # POSTFIX_OVERRIDE_HOSTNAME: Was Postfix im EHLO/HELO Banner sendet. + # node1.email-srvr.com passt zum TLS-Cert und ist der echte Hostname. + - POSTFIX_OVERRIDE_HOSTNAME=node1.email-srvr.com - POSTFIX_MYNETWORKS=172.16.0.0/12 172.17.0.0/12 172.18.0.0/12 [::1]/128 [fe80::]/64 - POSTFIX_MAILBOX_SIZE_LIMIT=0 - POSTFIX_MESSAGE_SIZE_LIMIT=0 - - SPOOF_PROTECTION=0 - - ENABLE_SRS=0 - # Debug-Einstellungen - - LOG_LEVEL=info + cap_add: - NET_ADMIN - SYS_PTRACE @@ -95,7 +119,6 @@ services: networks: mail_network: aliases: - - mail.email-srvr.com - mailserver roundcube: @@ -111,16 +134,14 @@ services: - ROUNDCUBEMAIL_DB_NAME=roundcube - ROUNDCUBEMAIL_DB_USER=roundcube - ROUNDCUBEMAIL_DB_PASSWORD=${ROUNDCUBE_DB_PASSWORD} - # Einfache Konfiguration ohne SSL-Probleme (für ersten Test) - - ROUNDCUBEMAIL_DEFAULT_HOST=ssl://mail.email-srvr.com + # Roundcube verbindet intern über den Docker-Alias + - ROUNDCUBEMAIL_DEFAULT_HOST=ssl://mailserver - ROUNDCUBEMAIL_DEFAULT_PORT=993 - - ROUNDCUBEMAIL_SMTP_SERVER=tls://mail.email-srvr.com + - ROUNDCUBEMAIL_SMTP_SERVER=tls://mailserver - ROUNDCUBEMAIL_SMTP_PORT=587 - #- ROUNDCUBEMAIL_PLUGINS=password,email_config,managesieve - ROUNDCUBEMAIL_PLUGINS=password,email_config - # In docker-compose.yml bei roundcube hinzufügen: ports: - - "8888:80" # Host:Container + - "8888:80" volumes: - ./docker-data/roundcube/config:/var/roundcube/config - ./docker-data/roundcube/plugins/email_config:/var/www/html/plugins/email_config:ro @@ -145,4 +166,4 @@ services: networks: mail_network: - external: true \ No newline at end of file + external: true \ No newline at end of file diff --git a/DMS/setup-dms-tls.sh b/DMS/setup-dms-tls.sh index a3c166e..ab0ec85 100755 --- a/DMS/setup-dms-tls.sh +++ b/DMS/setup-dms-tls.sh @@ -1,37 +1,40 @@ #!/bin/bash # setup-dms-tls.sh -# Generiert Dovecot und Postfix SNI-Konfigurationen für Multi-Domain TLS. -# Liest die vorhandenen Domains aus den DMS Accounts und erstellt: -# - docker-data/dms/config/dovecot-sni.cf (Dovecot SNI pro Domain) -# - docker-data/dms/config/postfix-main.cf (Postfix SNI Map + TLS Chain) +# Gehört ins Root-Verzeichnis des DMS (neben docker-compose.yml). # -# Voraussetzung: -# - Caddy hat Wildcard-Certs gezogen (z.B. *.andreasknuth.de) -# - Cert-Verzeichnis ist gemountet unter /etc/mail/certs im Container -# - Konvention Cert-Pfad: /etc/mail/certs/DOMAIN_NAME/*.DOMAIN_NAME.crt|.key +# Generiert Dovecot- und Postfix-SNI-Konfigurationen für Multi-Domain TLS. +# Liest Domains aus dem laufenden DMS und erstellt: +# - docker-data/dms/config/dovecot-sni.cf +# - docker-data/dms/config/postfix-main.cf +# +# Cert-Konvention (Caddy Wildcard): +# /etc/mail/certs/*.domain.tld/*.domain.tld.crt +# /etc/mail/certs/*.domain.tld/*.domain.tld.key # # Usage: -# DMS_CONTAINER=mailserver ./setup-dms-tls.sh -# DMS_CONTAINER=mailserver DEFAULT_DOMAIN=email-srvr.com ./setup-dms-tls.sh +# ./setup-dms-tls.sh +# DMS_CONTAINER=mailserver NODE_HOSTNAME=node1.email-srvr.com ./setup-dms-tls.sh set -e DMS_CONTAINER=${DMS_CONTAINER:-"mailserver"} -CONFIG_DIR=${CONFIG_DIR:-"./docker-data/dms/config"} +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +CONFIG_DIR="$SCRIPT_DIR/docker-data/dms/config" CERTS_BASE_PATH=${CERTS_BASE_PATH:-"/etc/mail/certs"} -# Die Default-Domain für DMS hostname/domainname (bleibt email-srvr.com) -DEFAULT_DOMAIN=${DEFAULT_DOMAIN:-"email-srvr.com"} +# Node-Hostname: Fallback-Cert für DMS (kein Wildcard, direktes Cert) +# Muss mit dem 'hostname' in docker-compose.yml übereinstimmen. +NODE_HOSTNAME=${NODE_HOSTNAME:-"node1.email-srvr.com"} echo "============================================================" echo " 🔐 DMS TLS SNI Setup (Multi-Domain)" -echo " Container: $DMS_CONTAINER" -echo " Config Dir: $CONFIG_DIR" -echo " Certs Base: $CERTS_BASE_PATH" -echo " Default Domain: $DEFAULT_DOMAIN" +echo " DMS Container: $DMS_CONTAINER" +echo " Config Dir: $CONFIG_DIR" +echo " Certs Base: $CERTS_BASE_PATH" +echo " Node Hostname: $NODE_HOSTNAME" echo "============================================================" -# --- Alle Domains aus DMS Accounts lesen --- +# --- Domains aus DMS lesen --- echo "" echo "📋 Lese Domains aus DMS..." DOMAINS=$(docker exec "$DMS_CONTAINER" setup email list 2>/dev/null \ @@ -40,74 +43,80 @@ DOMAINS=$(docker exec "$DMS_CONTAINER" setup email list 2>/dev/null \ if [ -z "$DOMAINS" ]; then echo "❌ Keine Accounts im DMS gefunden!" - echo " Bitte zuerst Accounts anlegen: ./manage_mail_user.sh add user@domain.com PW" + echo " Bitte zuerst anlegen: ./manage_mail_user.sh add user@domain.com PW" exit 1 fi echo " Gefundene Domains:" for d in $DOMAINS; do echo " - $d"; done -# --- Cert-Verfügbarkeit prüfen --- +# --- Cert-Verfügbarkeit im Container prüfen --- echo "" -echo "🔍 Prüfe Zertifikat-Verfügbarkeit (im Container)..." -DOMAINS_WITH_CERTS="" -DOMAINS_WITHOUT_CERTS="" +echo "🔍 Prüfe Zertifikat-Verfügbarkeit..." +DOMAINS_OK="" +DOMAINS_MISSING="" for domain in $DOMAINS; do - # Caddy speichert Wildcard-Certs als: *.domain.tld/ - # Pfad im Container (über den Volume-Mount): /etc/mail/certs/*.domain.tld/ CERT_PATH="$CERTS_BASE_PATH/*.$domain/*.$domain.crt" KEY_PATH="$CERTS_BASE_PATH/*.$domain/*.$domain.key" - # Prüfe ob die Datei im Container existiert if docker exec "$DMS_CONTAINER" test -f "$CERT_PATH" 2>/dev/null; then - echo " ✅ $domain → Cert gefunden" - DOMAINS_WITH_CERTS="$DOMAINS_WITH_CERTS $domain" + echo " ✅ $domain → Cert vorhanden" + DOMAINS_OK="$DOMAINS_OK $domain" else echo " ⚠️ $domain → KEIN Cert unter $CERT_PATH" - echo " Caddy-Block '*.${domain}' eintragen und Caddy neu starten!" - DOMAINS_WITHOUT_CERTS="$DOMAINS_WITHOUT_CERTS $domain" + echo " → update-caddy-certs.sh ausführen + caddy reload!" + DOMAINS_MISSING="$DOMAINS_MISSING $domain" fi done -if [ -n "$DOMAINS_WITHOUT_CERTS" ]; then - echo "" - echo "⚠️ WARNUNG: Fehlende Certs für:$DOMAINS_WITHOUT_CERTS" - echo " Diese Domains werden NICHT in die SNI-Configs eingetragen." - echo " Bitte Certs erzeugen und Script erneut ausführen." - echo "" +# Node-Hostname Cert prüfen (direktes Cert, kein Wildcard) +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 + echo " ✅ $NODE_HOSTNAME → Cert vorhanden (Node Default)" + NODE_CERT_OK=true +else + echo " ⚠️ $NODE_HOSTNAME → KEIN Cert! Caddy-Block im Caddyfile prüfen." + NODE_CERT_OK=false fi -if [ -z "$DOMAINS_WITH_CERTS" ]; then - echo "❌ Kein einziges Zertifikat gefunden! Abbruch." +if [ -n "$DOMAINS_MISSING" ]; then + echo "" + echo " ⚠️ Fehlende Certs:$DOMAINS_MISSING" + echo " Diese Domains werden NICHT in SNI-Config eingetragen." +fi + +if [ -z "$DOMAINS_OK" ]; then + echo "❌ Kein einziges Kundendomain-Cert gefunden!" + echo " Bitte zuerst update-caddy-certs.sh ausführen + caddy reload abwarten." exit 1 fi # ================================================================ -# DOVECOT SNI Konfiguration generieren +# DOVECOT SNI Konfiguration # ================================================================ DOVECOT_CFG="$CONFIG_DIR/dovecot-sni.cf" echo "" -echo "📝 Generiere Dovecot SNI Konfiguration: $DOVECOT_CFG" +echo "📝 Generiere: $DOVECOT_CFG" cat > "$DOVECOT_CFG" << 'HEADER' # dovecot-sni.cf - Automatisch generiert von setup-dms-tls.sh -# SNI-basierte TLS-Konfiguration für mehrere Domains. -# Dovecot wählt das Zertifikat anhand des SNI-Hostnamens des Clients. -# Dieses File wird via Volume-Mount in den Container eingebunden. +# SNI-basierte Zertifikat-Auswahl für Dovecot (IMAP/POP3). +# Dovecot liest dieses File über den Volume-Mount in /tmp/docker-mailserver/ +# und wendet es automatisch an. # -# Gemounteter Pfad: /tmp/docker-mailserver/dovecot-sni.cf -# In DMS docker-compose.yml volumes Sektion: +# Volume-Mount in docker-compose.yml: # - ./docker-data/dms/config/dovecot-sni.cf:/tmp/docker-mailserver/dovecot-sni.cf:ro HEADER -for domain in $DOMAINS_WITH_CERTS; do +for domain in $DOMAINS_OK; do CERT_PATH="$CERTS_BASE_PATH/*.$domain/*.$domain.crt" KEY_PATH="$CERTS_BASE_PATH/*.$domain/*.$domain.key" cat >> "$DOVECOT_CFG" << EOF -# Domain: $domain +# $domain local_name mail.$domain { ssl_cert = <$CERT_PATH ssl_key = <$KEY_PATH @@ -128,75 +137,69 @@ local_name pop.$domain { EOF done -echo " ✅ $DOVECOT_CFG erstellt ($(echo $DOMAINS_WITH_CERTS | wc -w) Domains)" +echo " ✅ Dovecot SNI: $(echo $DOMAINS_OK | wc -w) Domain(s)" # ================================================================ -# POSTFIX SNI Konfiguration generieren +# POSTFIX SNI Konfiguration # ================================================================ POSTFIX_CFG="$CONFIG_DIR/postfix-main.cf" echo "" -echo "📝 Generiere Postfix SNI Konfiguration: $POSTFIX_CFG" +echo "📝 Generiere: $POSTFIX_CFG" -# Prüfe ob postfix-main.cf schon existiert und sichere sie +# Backup falls vorhanden if [ -f "$POSTFIX_CFG" ]; then cp "$POSTFIX_CFG" "${POSTFIX_CFG}.bak.$(date +%Y%m%d%H%M%S)" - echo " ℹ️ Backup erstellt: ${POSTFIX_CFG}.bak.*" + echo " ℹ️ Backup: ${POSTFIX_CFG}.bak.*" fi -# TLS Chain Files für Postfix aufbauen -# Postfix unterstützt smtpd_tls_chain_files mit mehreren Key/Cert Paaren -CHAIN_FILES="" -for domain in $DOMAINS_WITH_CERTS; do +# smtpd_tls_chain_files aufbauen: Key + Cert Paar pro Domain +# Postfix wählt automatisch per SNI das passende Paar +CHAIN_LINES="" +for domain in $DOMAINS_OK; do KEY_PATH="$CERTS_BASE_PATH/*.$domain/*.$domain.key" CERT_PATH="$CERTS_BASE_PATH/*.$domain/*.$domain.crt" - if [ -z "$CHAIN_FILES" ]; then - CHAIN_FILES=" $KEY_PATH, $CERT_PATH" + if [ -z "$CHAIN_LINES" ]; then + CHAIN_LINES=" $KEY_PATH, $CERT_PATH" else - CHAIN_FILES="$CHAIN_FILES,\n $KEY_PATH, $CERT_PATH" + CHAIN_LINES="$CHAIN_LINES,\n $KEY_PATH, $CERT_PATH" fi done -cat > "$POSTFIX_CFG" << POSTFIX_CONF +cat > "$POSTFIX_CFG" << POSTFIX_EOF # postfix-main.cf - Automatisch generiert von setup-dms-tls.sh -# Postfix SNI-Konfiguration für mehrere Domains. -# DMS lädt dieses File automatisch beim Start via /tmp/docker-mailserver/ +# 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. -# ------------------------------------------------------------------ -# TLS Chain Files (Key + Cert pro Domain) -# Postfix wählt das passende Paar automatisch per SNI -# ------------------------------------------------------------------ +# TLS Chain: Key + Cert Paare (Postfix >= 3.4) smtpd_tls_chain_files = -$(printf '%b' "$CHAIN_FILES") +$(printf '%b' "$CHAIN_LINES") -POSTFIX_CONF +POSTFIX_EOF -echo " ✅ $POSTFIX_CFG erstellt" +echo " ✅ Postfix SNI: $(echo $DOMAINS_OK | wc -w) Domain(s)" # ================================================================ -# Hinweise für docker-compose.yml +# Zusammenfassung # ================================================================ echo "" echo "============================================================" +echo "✅ Konfigurationen generiert." +echo "" echo "📋 Nächste Schritte:" echo "" -echo "1. Volume-Mounts in DMS docker-compose.yml hinzufügen:" -echo "" -echo " volumes:" -echo " # Bestehend (Caddy Certs - gesamtes Verzeichnis):" -echo " - /var/lib/docker/volumes/caddy_data/_data/caddy/certificates/" -echo " acme-v02.api.letsencrypt.org-directory:/etc/mail/certs:ro" -echo "" -echo " # NEU - Dovecot SNI:" -echo " - ./docker-data/dms/config/dovecot-sni.cf:/tmp/docker-mailserver/dovecot-sni.cf:ro" -echo "" -echo " # Postfix-main.cf wird von DMS automatisch geladen wenn sie liegt unter:" -echo " - ./docker-data/dms/config/postfix-main.cf:/tmp/docker-mailserver/postfix-main.cf:ro" -echo "" -echo "2. DMS neu starten:" +echo "1. DMS neu starten:" echo " docker compose restart mailserver" echo "" -echo "3. TLS testen:" -for domain in $DOMAINS_WITH_CERTS; do - echo " openssl s_client -connect mail.$domain:993 -servername mail.$domain" +echo "2. TLS testen (SNI):" +for domain in $DOMAINS_OK; do + echo " openssl s_client -connect mail.$domain:993 -servername mail.$domain 2>/dev/null | grep 'subject\|issuer'" 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 "============================================================" \ No newline at end of file diff --git a/caddy/update-caddy-certs.sh b/caddy/update-caddy-certs.sh index 70c3952..ed4ca2a 100755 --- a/caddy/update-caddy-certs.sh +++ b/caddy/update-caddy-certs.sh @@ -1,28 +1,35 @@ #!/bin/bash # update-caddy-certs.sh -# Liest alle Domains aus dem DMS und generiert die notwendigen -# Caddyfile-Blöcke für Wildcard-Zertifikate. +# Gehört ins Caddy-Verzeichnis (neben dem Caddyfile). # -# Die generierten Blöcke werden NICHT automatisch in das Caddyfile geschrieben, -# sondern in eine separate Datei (caddy_mail_certs.conf) ausgegeben, -# die per "import" in das Hauptcaddyfile eingebunden werden kann. +# Liest alle Domains aus dem DMS und generiert die Wildcard-Cert-Blöcke +# für Caddy in die Datei "mail_certs" (per "import mail_certs" im Caddyfile). +# +# Bei neuen Domains: Script erneut laufen lassen + caddy reload. # # Usage: -# DMS_CONTAINER=mailserver ./update-caddy-certs.sh -# DMS_CONTAINER=mailserver CADDY_DIR=/pfad/zu/caddy ./update-caddy-certs.sh -# DMS_CONTAINER=mailserver DRY_RUN=true ./update-caddy-certs.sh +# ./update-caddy-certs.sh +# DRY_RUN=true ./update-caddy-certs.sh +# DMS_CONTAINER=mailserver CADDY_CONTAINER=caddy ./update-caddy-certs.sh set -e DMS_CONTAINER=${DMS_CONTAINER:-"mailserver"} -CADDY_DIR=${CADDY_DIR:-"."} # Verzeichnis wo das Caddyfile liegt -OUTPUT_FILE=${OUTPUT_FILE:-"$CADDY_DIR/mail_certs"} # Ohne Extension - Caddy importiert ohne .conf +CADDY_CONTAINER=${CADDY_CONTAINER:-"caddy"} +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +OUTPUT_FILE="$SCRIPT_DIR/mail_certs" DRY_RUN=${DRY_RUN:-"false"} +# Node-Hostname des Mailservers (für Default-Cert Block) +# Wird immer mit eingetragen, auch wenn keine DMS-Accounts existieren. +NODE_HOSTNAME=${NODE_HOSTNAME:-"node1.email-srvr.com"} + echo "============================================================" echo " 📜 Caddy Wildcard-Cert Konfig Generator" -echo " DMS Container: $DMS_CONTAINER" -echo " Output: $OUTPUT_FILE" +echo " DMS Container: $DMS_CONTAINER" +echo " Caddy Container: $CADDY_CONTAINER" +echo " Output: $OUTPUT_FILE" +echo " Node Hostname: $NODE_HOSTNAME" [ "$DRY_RUN" = "true" ] && echo " ⚠️ DRY RUN - Keine Dateien werden geschrieben" echo "============================================================" @@ -34,104 +41,92 @@ DOMAINS=$(docker exec "$DMS_CONTAINER" setup email list 2>/dev/null \ | sort -u) if [ -z "$DOMAINS" ]; then - echo "❌ Keine Accounts gefunden!" - exit 1 + echo "⚠️ Keine DMS-Accounts gefunden. Nur Node-Hostname wird eingetragen." fi -echo " Gefundene Domains:" -for d in $DOMAINS; do echo " - $d"; done - -# --- email-srvr.com immer einschließen (Default-Domain des DMS) --- -EXTRA_DOMAINS="email-srvr.com" -for extra in $EXTRA_DOMAINS; do - if ! echo "$DOMAINS" | grep -q "^${extra}$"; then - DOMAINS="$DOMAINS $extra" - echo " + $extra (Default DMS Domain - immer dabei)" - fi -done +if [ -n "$DOMAINS" ]; then + echo " Gefundene Domains:" + for d in $DOMAINS; do echo " - $d"; done +fi # --- Konfig generieren --- echo "" echo "📝 Generiere Caddy-Konfiguration..." -CONTENT="" -CONTENT="${CONTENT}# mail_certs - Automatisch generiert von update-caddy-certs.sh\n" -CONTENT="${CONTENT}# Wildcard-Zertifikate für alle DMS-Domains.\n" -CONTENT="${CONTENT}# Einbinden im Hauptcaddyfile: import mail_certs\n" -CONTENT="${CONTENT}# Generiert: $(date)\n" -CONTENT="${CONTENT}\n" +OUTPUT="" +OUTPUT="${OUTPUT}# mail_certs - Automatisch generiert von update-caddy-certs.sh\n" +OUTPUT="${OUTPUT}# Wildcard-Zertifikate für DMS-Domains + Node-Hostname.\n" +OUTPUT="${OUTPUT}# Einbinden im Caddyfile: import mail_certs\n" +OUTPUT="${OUTPUT}# Generiert: $(date)\n" +OUTPUT="${OUTPUT}\n" +# Node-Hostname immer als erstes (Default-Cert des DMS) +echo " → Node-Hostname Block: $NODE_HOSTNAME" +OUTPUT="${OUTPUT}# Node-Hostname (Default-Cert für DMS Fallback)\n" +OUTPUT="${OUTPUT}${NODE_HOSTNAME} {\n" +OUTPUT="${OUTPUT} tls {\n" +OUTPUT="${OUTPUT} dns cloudflare {env.CLOUDFLARE_API_TOKEN}\n" +OUTPUT="${OUTPUT} }\n" +OUTPUT="${OUTPUT} respond \"OK\" 200\n" +OUTPUT="${OUTPUT}}\n\n" + +# Wildcard-Blocks pro Kundendomain for domain in $DOMAINS; do - echo " → Block für: $domain" - CONTENT="${CONTENT}# Wildcard-Cert für $domain\n" - CONTENT="${CONTENT}*.${domain}, ${domain} {\n" - CONTENT="${CONTENT} tls {\n" - CONTENT="${CONTENT} dns cloudflare {env.CLOUDFLARE_API_TOKEN}\n" - CONTENT="${CONTENT} }\n" - CONTENT="${CONTENT} respond \"OK\" 200\n" - CONTENT="${CONTENT}}\n" - CONTENT="${CONTENT}\n" + echo " → Wildcard Block: *.${domain}" + OUTPUT="${OUTPUT}# Wildcard-Cert für $domain\n" + OUTPUT="${OUTPUT}*.${domain}, ${domain} {\n" + OUTPUT="${OUTPUT} tls {\n" + OUTPUT="${OUTPUT} dns cloudflare {env.CLOUDFLARE_API_TOKEN}\n" + OUTPUT="${OUTPUT} }\n" + OUTPUT="${OUTPUT} respond \"OK\" 200\n" + OUTPUT="${OUTPUT}}\n\n" done # --- Ausgabe --- if [ "$DRY_RUN" = "true" ]; then echo "" - echo "--- VORSCHAU (DRY RUN) ---" - printf '%b' "$CONTENT" - echo "--- ENDE VORSCHAU ---" + echo "--- VORSCHAU ---" + printf '%b' "$OUTPUT" + echo "--- ENDE ---" else - printf '%b' "$CONTENT" > "$OUTPUT_FILE" - echo "" + printf '%b' "$OUTPUT" > "$OUTPUT_FILE" echo " ✅ Geschrieben: $OUTPUT_FILE" fi -# --- Prüfen ob Import im Caddyfile vorhanden --- -CADDYFILE="$CADDY_DIR/Caddyfile" +# --- Import im Caddyfile prüfen --- +CADDYFILE="$SCRIPT_DIR/Caddyfile" if [ -f "$CADDYFILE" ]; then if grep -q "import mail_certs" "$CADDYFILE"; then echo " ✅ 'import mail_certs' bereits im Caddyfile vorhanden." else echo "" - echo "⚠️ AKTION ERFORDERLICH:" - echo " 'import mail_certs' fehlt noch im Caddyfile!" - echo " Bitte folgende Zeile am Anfang (nach dem globalen Block) eintragen:" + echo "⚠️ AKTION: 'import mail_certs' fehlt noch im Caddyfile!" + echo " Bitte nach dem globalen {} Block eintragen:" echo "" - echo " import mail_certs" - echo "" - echo " Oder automatisch einfügen? (y/N)" - read -r answer - if [ "$answer" = "y" ] || [ "$answer" = "Y" ]; then - # Import nach der ersten Zeile mit "import " einfügen (falls schon welche da sind) - # oder nach dem globalen {} Block - if grep -q "^import " "$CADDYFILE"; then - # Schreibe nach der letzten import-Zeile - sed -i "/^import /a import mail_certs" "$CADDYFILE" - else - # Schreibe nach dem schließenden } des globalen Blocks - sed -i "/^}/a \\\nimport mail_certs" "$CADDYFILE" - fi - echo " ✅ Import eingefügt." - fi + echo " { ← globaler Block" + echo " email {env.CLOUDFLARE_EMAIL}" + echo " ..." + echo " }" + echo " import mail_certs ← hier einfügen" + echo " import email_autodiscover" + echo " ..." fi fi -# --- Caddy reload --- echo "" echo "============================================================" echo "🔄 Nächste Schritte:" echo "" -echo "1. Caddyfile prüfen - 'import mail_certs' muss vorhanden sein" +echo "1. Caddy Konfiguration validieren:" +echo " docker exec $CADDY_CONTAINER caddy validate --config /etc/caddy/Caddyfile" echo "" -echo "2. Caddy Konfiguration validieren:" -echo " docker exec caddy caddy validate --config /etc/caddy/Caddyfile" +echo "2. Caddy neu laden (kein Downtime):" +echo " docker exec $CADDY_CONTAINER caddy reload --config /etc/caddy/Caddyfile" echo "" -echo "3. Caddy neu laden (kein Downtime):" -echo " docker exec caddy caddy reload --config /etc/caddy/Caddyfile" +echo "3. Cert-Generierung verfolgen (~30s pro Domain):" +echo " docker logs -f $CADDY_CONTAINER 2>&1 | grep -i 'certificate\|acme\|tls\|error'" echo "" -echo "4. Cert-Generierung verfolgen (dauert ~30s pro Domain):" -echo " docker logs -f caddy 2>&1 | grep -i 'certificate\|acme\|tls'" -echo "" -echo "5. Cert-Pfade prüfen:" +echo "4. Cert-Pfade kontrollieren:" echo " ls /var/lib/docker/volumes/caddy_data/_data/caddy/certificates/" echo " acme-v02.api.letsencrypt.org-directory/" echo "============================================================" \ No newline at end of file