update
This commit is contained in:
parent
ee19b5b659
commit
8808d81113
|
|
@ -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
|
||||
|
||||
# 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
|
||||
|
||||
# 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
|
||||
|
|
|
|||
|
|
@ -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 " DMS Container: $DMS_CONTAINER"
|
||||
echo " Config Dir: $CONFIG_DIR"
|
||||
echo " Certs Base: $CERTS_BASE_PATH"
|
||||
echo " Default Domain: $DEFAULT_DOMAIN"
|
||||
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 "============================================================"
|
||||
|
|
@ -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 " 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
|
||||
|
||||
if [ -n "$DOMAINS" ]; then
|
||||
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
|
||||
|
||||
# --- 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 "============================================================"
|
||||
Loading…
Reference in New Issue