288 lines
13 KiB
Bash
Executable File
288 lines
13 KiB
Bash
Executable File
#!/bin/bash
|
|
# update-caddy-certs.sh
|
|
# Gehört ins Caddy-Verzeichnis (neben dem Caddyfile).
|
|
#
|
|
# 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).
|
|
#
|
|
# Generiert pro Domain:
|
|
# - Wildcard-Cert Block (*.domain + domain)
|
|
# - Webmail Block (reverse_proxy zu Roundcube)
|
|
# - Autodiscover/Autoconfig Block (importiert email_settings Snippet)
|
|
#
|
|
# Bei neuen Domains: Script erneut laufen lassen + caddy reload.
|
|
#
|
|
# Usage:
|
|
# ./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_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)
|
|
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 "============================================================"
|
|
|
|
# --- Domains aus DMS lesen ---
|
|
echo ""
|
|
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. Nur Node-Hostname wird eingetragen."
|
|
fi
|
|
|
|
if [ -n "$DOMAINS" ]; then
|
|
echo " Gefundene Domains:"
|
|
for d in $DOMAINS; do echo " - $d"; done
|
|
fi
|
|
|
|
# --- Konfig generieren ---
|
|
echo ""
|
|
echo "📝 Generiere Caddy-Konfiguration..."
|
|
|
|
OUTPUT=""
|
|
OUTPUT="${OUTPUT}# mail_certs - Automatisch generiert von update-caddy-certs.sh\n"
|
|
OUTPUT="${OUTPUT}# Wildcard-Zertifikate + Webmail + Autodiscover für DMS-Domains.\n"
|
|
OUTPUT="${OUTPUT}# Einbinden im Caddyfile: import mail_certs\n"
|
|
OUTPUT="${OUTPUT}# Generiert: $(date)\n"
|
|
OUTPUT="${OUTPUT}\n"
|
|
|
|
# --- Autodiscover/Autoconfig Snippet einbetten ---
|
|
OUTPUT="${OUTPUT}# ═══════════════════════════════════════════════\n"
|
|
OUTPUT="${OUTPUT}# Autodiscover/Autoconfig Snippet (dynamisch)\n"
|
|
OUTPUT="${OUTPUT}# {labels.1}.{labels.0} = Basisdomain aus Hostname\n"
|
|
OUTPUT="${OUTPUT}# ═══════════════════════════════════════════════\n"
|
|
OUTPUT="${OUTPUT}(email_settings) {\n"
|
|
OUTPUT="${OUTPUT} # Outlook Autodiscover (XML)\n"
|
|
OUTPUT="${OUTPUT} route /autodiscover/autodiscover.xml {\n"
|
|
OUTPUT="${OUTPUT} header Content-Type \"application/xml\"\n"
|
|
OUTPUT="${OUTPUT} respond \`<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
|
|
OUTPUT="${OUTPUT}<Autodiscover xmlns=\"http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006\">\n"
|
|
OUTPUT="${OUTPUT} <Response xmlns=\"http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a\">\n"
|
|
OUTPUT="${OUTPUT} <Account>\n"
|
|
OUTPUT="${OUTPUT} <AccountType>email</AccountType>\n"
|
|
OUTPUT="${OUTPUT} <Action>settings</Action>\n"
|
|
OUTPUT="${OUTPUT} <Protocol>\n"
|
|
OUTPUT="${OUTPUT} <Type>IMAP</Type>\n"
|
|
OUTPUT="${OUTPUT} <Server>imap.{labels.1}.{labels.0}</Server>\n"
|
|
OUTPUT="${OUTPUT} <Port>993</Port>\n"
|
|
OUTPUT="${OUTPUT} <DomainRequired>on</DomainRequired>\n"
|
|
OUTPUT="${OUTPUT} <LoginName>{header.X-Anchormailbox}</LoginName>\n"
|
|
OUTPUT="${OUTPUT} <SPA>off</SPA>\n"
|
|
OUTPUT="${OUTPUT} <SSL>on</SSL>\n"
|
|
OUTPUT="${OUTPUT} <AuthRequired>on</AuthRequired>\n"
|
|
OUTPUT="${OUTPUT} </Protocol>\n"
|
|
OUTPUT="${OUTPUT} <Protocol>\n"
|
|
OUTPUT="${OUTPUT} <Type>POP3</Type>\n"
|
|
OUTPUT="${OUTPUT} <Server>pop.{labels.1}.{labels.0}</Server>\n"
|
|
OUTPUT="${OUTPUT} <Port>995</Port>\n"
|
|
OUTPUT="${OUTPUT} <DomainRequired>on</DomainRequired>\n"
|
|
OUTPUT="${OUTPUT} <LoginName>{header.X-Anchormailbox}</LoginName>\n"
|
|
OUTPUT="${OUTPUT} <SPA>off</SPA>\n"
|
|
OUTPUT="${OUTPUT} <SSL>on</SSL>\n"
|
|
OUTPUT="${OUTPUT} <AuthRequired>on</AuthRequired>\n"
|
|
OUTPUT="${OUTPUT} </Protocol>\n"
|
|
OUTPUT="${OUTPUT} <Protocol>\n"
|
|
OUTPUT="${OUTPUT} <Type>SMTP</Type>\n"
|
|
OUTPUT="${OUTPUT} <Server>smtp.{labels.1}.{labels.0}</Server>\n"
|
|
OUTPUT="${OUTPUT} <Port>465</Port>\n"
|
|
OUTPUT="${OUTPUT} <DomainRequired>on</DomainRequired>\n"
|
|
OUTPUT="${OUTPUT} <LoginName>{header.X-Anchormailbox}</LoginName>\n"
|
|
OUTPUT="${OUTPUT} <SPA>off</SPA>\n"
|
|
OUTPUT="${OUTPUT} <SSL>on</SSL>\n"
|
|
OUTPUT="${OUTPUT} <AuthRequired>on</AuthRequired>\n"
|
|
OUTPUT="${OUTPUT} </Protocol>\n"
|
|
OUTPUT="${OUTPUT} </Account>\n"
|
|
OUTPUT="${OUTPUT} </Response>\n"
|
|
OUTPUT="${OUTPUT}</Autodiscover>\` 200\n"
|
|
OUTPUT="${OUTPUT} }\n"
|
|
OUTPUT="${OUTPUT}\n"
|
|
OUTPUT="${OUTPUT} # Modern Outlook (JSON)\n"
|
|
OUTPUT="${OUTPUT} route /autodiscover/autodiscover.json {\n"
|
|
OUTPUT="${OUTPUT} header Content-Type \"application/json\"\n"
|
|
OUTPUT="${OUTPUT} respond \`{\n"
|
|
OUTPUT="${OUTPUT} \"Protocol\": \"AutodiscoverV1\",\n"
|
|
OUTPUT="${OUTPUT} \"Url\": \"https://autodiscover.{labels.1}.{labels.0}/autodiscover/autodiscover.xml\"\n"
|
|
OUTPUT="${OUTPUT} }\` 200\n"
|
|
OUTPUT="${OUTPUT} }\n"
|
|
OUTPUT="${OUTPUT}\n"
|
|
OUTPUT="${OUTPUT} # Thunderbird Autoconfig\n"
|
|
OUTPUT="${OUTPUT} route /mail/config-v1.1.xml {\n"
|
|
OUTPUT="${OUTPUT} header Content-Type \"application/xml\"\n"
|
|
OUTPUT="${OUTPUT} respond \`<?xml version=\"1.0\"?>\n"
|
|
OUTPUT="${OUTPUT}<clientConfig version=\"1.1\">\n"
|
|
OUTPUT="${OUTPUT} <emailProvider id=\"{labels.1}.{labels.0}\">\n"
|
|
OUTPUT="${OUTPUT} <displayName>{labels.1}.{labels.0} Mail</displayName>\n"
|
|
OUTPUT="${OUTPUT} <domain>{labels.1}.{labels.0}</domain>\n"
|
|
OUTPUT="${OUTPUT} <incomingServer type=\"imap\">\n"
|
|
OUTPUT="${OUTPUT} <hostname>imap.{labels.1}.{labels.0}</hostname>\n"
|
|
OUTPUT="${OUTPUT} <port>993</port>\n"
|
|
OUTPUT="${OUTPUT} <socketType>SSL</socketType>\n"
|
|
OUTPUT="${OUTPUT} <authentication>password-cleartext</authentication>\n"
|
|
OUTPUT="${OUTPUT} <username>%%EMAILADDRESS%%</username>\n"
|
|
OUTPUT="${OUTPUT} </incomingServer>\n"
|
|
OUTPUT="${OUTPUT} <incomingServer type=\"pop3\">\n"
|
|
OUTPUT="${OUTPUT} <hostname>pop.{labels.1}.{labels.0}</hostname>\n"
|
|
OUTPUT="${OUTPUT} <port>995</port>\n"
|
|
OUTPUT="${OUTPUT} <socketType>SSL</socketType>\n"
|
|
OUTPUT="${OUTPUT} <authentication>password-cleartext</authentication>\n"
|
|
OUTPUT="${OUTPUT} <username>%%EMAILADDRESS%%</username>\n"
|
|
OUTPUT="${OUTPUT} </incomingServer>\n"
|
|
OUTPUT="${OUTPUT} <outgoingServer type=\"smtp\">\n"
|
|
OUTPUT="${OUTPUT} <hostname>smtp.{labels.1}.{labels.0}</hostname>\n"
|
|
OUTPUT="${OUTPUT} <port>465</port>\n"
|
|
OUTPUT="${OUTPUT} <socketType>SSL</socketType>\n"
|
|
OUTPUT="${OUTPUT} <authentication>password-cleartext</authentication>\n"
|
|
OUTPUT="${OUTPUT} <username>%%EMAILADDRESS%%</username>\n"
|
|
OUTPUT="${OUTPUT} </outgoingServer>\n"
|
|
OUTPUT="${OUTPUT} </emailProvider>\n"
|
|
OUTPUT="${OUTPUT}</clientConfig>\` 200\n"
|
|
OUTPUT="${OUTPUT} }\n"
|
|
OUTPUT="${OUTPUT}\n"
|
|
OUTPUT="${OUTPUT} # Apple MobileConfig\n"
|
|
OUTPUT="${OUTPUT} route /apple {\n"
|
|
OUTPUT="${OUTPUT} templates {\n"
|
|
OUTPUT="${OUTPUT} mime \"application/x-apple-aspen-config\"\n"
|
|
OUTPUT="${OUTPUT} }\n"
|
|
OUTPUT="${OUTPUT} header Content-Type \"application/x-apple-aspen-config; charset=utf-8\"\n"
|
|
OUTPUT="${OUTPUT} root * /etc/caddy\n"
|
|
OUTPUT="${OUTPUT} rewrite * /email.mobileconfig.tpl\n"
|
|
OUTPUT="${OUTPUT} file_server\n"
|
|
OUTPUT="${OUTPUT} }\n"
|
|
OUTPUT="${OUTPUT}}\n\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 + Webmail + Autodiscover pro Kundendomain
|
|
for domain in $DOMAINS; do
|
|
echo " → Wildcard Block: *.${domain}"
|
|
echo " → Webmail Block: webmail.${domain}"
|
|
echo " → Autodiscover Block: autodiscover.${domain}, autoconfig.${domain}"
|
|
|
|
# Wildcard-Cert Block (für Cert-Generierung + Fallback)
|
|
OUTPUT="${OUTPUT}# ═══════════════════════════════════════════════\n"
|
|
OUTPUT="${OUTPUT}# ${domain}\n"
|
|
OUTPUT="${OUTPUT}# ═══════════════════════════════════════════════\n\n"
|
|
|
|
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"
|
|
|
|
# Webmail Block (Roundcube)
|
|
OUTPUT="${OUTPUT}# Roundcube Webmail für $domain\n"
|
|
OUTPUT="${OUTPUT}webmail.${domain} {\n"
|
|
OUTPUT="${OUTPUT} reverse_proxy roundcube:80\n"
|
|
OUTPUT="${OUTPUT} encode gzip\n"
|
|
OUTPUT="${OUTPUT} log {\n"
|
|
OUTPUT="${OUTPUT} output stderr\n"
|
|
OUTPUT="${OUTPUT} format console\n"
|
|
OUTPUT="${OUTPUT} }\n"
|
|
OUTPUT="${OUTPUT}}\n\n"
|
|
|
|
# Autodiscover / Autoconfig Block
|
|
OUTPUT="${OUTPUT}# Autodiscover/Autoconfig für $domain\n"
|
|
OUTPUT="${OUTPUT}autodiscover.${domain}, autoconfig.${domain} {\n"
|
|
OUTPUT="${OUTPUT} import email_settings\n"
|
|
OUTPUT="${OUTPUT} respond \"Autodiscover Service Online\" 200\n"
|
|
OUTPUT="${OUTPUT}}\n\n"
|
|
done
|
|
|
|
# --- Ausgabe ---
|
|
if [ "$DRY_RUN" = "true" ]; then
|
|
echo ""
|
|
echo "--- VORSCHAU ---"
|
|
printf '%b' "$OUTPUT"
|
|
echo "--- ENDE ---"
|
|
else
|
|
printf '%b' "$OUTPUT" > "$OUTPUT_FILE"
|
|
echo " ✅ Geschrieben: $OUTPUT_FILE"
|
|
fi
|
|
|
|
# --- 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: 'import mail_certs' fehlt noch im Caddyfile!"
|
|
echo " Bitte nach dem globalen {} Block eintragen:"
|
|
echo ""
|
|
echo " { ← globaler Block"
|
|
echo " email {env.CLOUDFLARE_EMAIL}"
|
|
echo " ..."
|
|
echo " }"
|
|
echo " import mail_certs ← hier einfügen"
|
|
fi
|
|
|
|
# Prüfe ob alte email_autodiscover Referenz entfernt werden kann
|
|
if grep -q "import email_autodiscover" "$CADDYFILE"; then
|
|
echo ""
|
|
echo "⚠️ AUFRÄUMEN: 'import email_autodiscover' im Caddyfile gefunden!"
|
|
echo " Das Snippet (email_settings) ist jetzt in mail_certs eingebettet."
|
|
echo " Bitte 'import email_autodiscover' aus dem Caddyfile entfernen."
|
|
fi
|
|
fi
|
|
|
|
# --- Prüfe ob alte hartcodierte Autodiscover-Blöcke existieren ---
|
|
if [ -f "$CADDYFILE" ]; then
|
|
if grep -q "autodiscover\.bayarea-cc\.com\|autodiscover\.bizmatch\.net\|autodiscover\.ruehrgedoens\.de" "$CADDYFILE"; then
|
|
echo ""
|
|
echo "⚠️ AUFRÄUMEN: Alte hartcodierte Autodiscover-Blöcke im Caddyfile gefunden!"
|
|
echo " Diese werden jetzt dynamisch über mail_certs generiert."
|
|
echo " Bitte den alten 'Block A' manuell aus dem Caddyfile entfernen:"
|
|
echo " → autodiscover.bayarea-cc.com, autodiscover.bizmatch.net, ..."
|
|
fi
|
|
fi
|
|
|
|
echo ""
|
|
echo "============================================================"
|
|
echo "🔄 Nächste Schritte:"
|
|
echo ""
|
|
echo "1. Caddy Konfiguration validieren:"
|
|
echo " docker exec $CADDY_CONTAINER caddy validate --config /etc/caddy/Caddyfile"
|
|
echo ""
|
|
echo "2. Caddy neu laden (kein Downtime):"
|
|
echo " docker exec $CADDY_CONTAINER caddy reload --config /etc/caddy/Caddyfile"
|
|
echo ""
|
|
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-Pfade kontrollieren:"
|
|
echo " ls /var/lib/docker/volumes/caddy_data/_data/caddy/certificates/"
|
|
echo " acme-v02.api.letsencrypt.org-directory/"
|
|
echo ""
|
|
echo "5. Autodiscover testen:"
|
|
for domain in $DOMAINS; do
|
|
echo " curl -s https://autoconfig.${domain}/mail/config-v1.1.xml | head -5"
|
|
done
|
|
echo "============================================================" |