changes
This commit is contained in:
parent
b072083318
commit
ee19b5b659
|
|
@ -0,0 +1,202 @@
|
|||
#!/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)
|
||||
#
|
||||
# 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
|
||||
#
|
||||
# Usage:
|
||||
# DMS_CONTAINER=mailserver ./setup-dms-tls.sh
|
||||
# DMS_CONTAINER=mailserver DEFAULT_DOMAIN=email-srvr.com ./setup-dms-tls.sh
|
||||
|
||||
set -e
|
||||
|
||||
DMS_CONTAINER=${DMS_CONTAINER:-"mailserver"}
|
||||
CONFIG_DIR=${CONFIG_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"}
|
||||
|
||||
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 "============================================================"
|
||||
|
||||
# --- Alle Domains aus DMS Accounts 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 Accounts im DMS gefunden!"
|
||||
echo " Bitte zuerst Accounts 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 ---
|
||||
echo ""
|
||||
echo "🔍 Prüfe Zertifikat-Verfügbarkeit (im Container)..."
|
||||
DOMAINS_WITH_CERTS=""
|
||||
DOMAINS_WITHOUT_CERTS=""
|
||||
|
||||
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"
|
||||
else
|
||||
echo " ⚠️ $domain → KEIN Cert unter $CERT_PATH"
|
||||
echo " Caddy-Block '*.${domain}' eintragen und Caddy neu starten!"
|
||||
DOMAINS_WITHOUT_CERTS="$DOMAINS_WITHOUT_CERTS $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 ""
|
||||
fi
|
||||
|
||||
if [ -z "$DOMAINS_WITH_CERTS" ]; then
|
||||
echo "❌ Kein einziges Zertifikat gefunden! Abbruch."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ================================================================
|
||||
# DOVECOT SNI Konfiguration generieren
|
||||
# ================================================================
|
||||
DOVECOT_CFG="$CONFIG_DIR/dovecot-sni.cf"
|
||||
echo ""
|
||||
echo "📝 Generiere Dovecot SNI Konfiguration: $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.
|
||||
#
|
||||
# Gemounteter Pfad: /tmp/docker-mailserver/dovecot-sni.cf
|
||||
# In DMS docker-compose.yml volumes Sektion:
|
||||
# - ./docker-data/dms/config/dovecot-sni.cf:/tmp/docker-mailserver/dovecot-sni.cf:ro
|
||||
|
||||
HEADER
|
||||
|
||||
for domain in $DOMAINS_WITH_CERTS; do
|
||||
CERT_PATH="$CERTS_BASE_PATH/*.$domain/*.$domain.crt"
|
||||
KEY_PATH="$CERTS_BASE_PATH/*.$domain/*.$domain.key"
|
||||
|
||||
cat >> "$DOVECOT_CFG" << EOF
|
||||
# Domain: $domain
|
||||
local_name mail.$domain {
|
||||
ssl_cert = <$CERT_PATH
|
||||
ssl_key = <$KEY_PATH
|
||||
}
|
||||
local_name imap.$domain {
|
||||
ssl_cert = <$CERT_PATH
|
||||
ssl_key = <$KEY_PATH
|
||||
}
|
||||
local_name smtp.$domain {
|
||||
ssl_cert = <$CERT_PATH
|
||||
ssl_key = <$KEY_PATH
|
||||
}
|
||||
local_name pop.$domain {
|
||||
ssl_cert = <$CERT_PATH
|
||||
ssl_key = <$KEY_PATH
|
||||
}
|
||||
|
||||
EOF
|
||||
done
|
||||
|
||||
echo " ✅ $DOVECOT_CFG erstellt ($(echo $DOMAINS_WITH_CERTS | wc -w) Domains)"
|
||||
|
||||
# ================================================================
|
||||
# POSTFIX SNI Konfiguration generieren
|
||||
# ================================================================
|
||||
POSTFIX_CFG="$CONFIG_DIR/postfix-main.cf"
|
||||
echo ""
|
||||
echo "📝 Generiere Postfix SNI Konfiguration: $POSTFIX_CFG"
|
||||
|
||||
# Prüfe ob postfix-main.cf schon existiert und sichere sie
|
||||
if [ -f "$POSTFIX_CFG" ]; then
|
||||
cp "$POSTFIX_CFG" "${POSTFIX_CFG}.bak.$(date +%Y%m%d%H%M%S)"
|
||||
echo " ℹ️ Backup erstellt: ${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
|
||||
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"
|
||||
else
|
||||
CHAIN_FILES="$CHAIN_FILES,\n $KEY_PATH, $CERT_PATH"
|
||||
fi
|
||||
done
|
||||
|
||||
cat > "$POSTFIX_CFG" << POSTFIX_CONF
|
||||
# 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/
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# TLS Chain Files (Key + Cert pro Domain)
|
||||
# Postfix wählt das passende Paar automatisch per SNI
|
||||
# ------------------------------------------------------------------
|
||||
smtpd_tls_chain_files =
|
||||
$(printf '%b' "$CHAIN_FILES")
|
||||
|
||||
POSTFIX_CONF
|
||||
|
||||
echo " ✅ $POSTFIX_CFG erstellt"
|
||||
|
||||
# ================================================================
|
||||
# Hinweise für docker-compose.yml
|
||||
# ================================================================
|
||||
echo ""
|
||||
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 " 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"
|
||||
done
|
||||
echo "============================================================"
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
#!/bin/bash
|
||||
# cloudflareMigrationDns.sh
|
||||
# Setzt DNS Records für Amazon SES Migration + Cloudflare
|
||||
# Unterstützt: DKIM, SPF (Merge), DMARC, MX (Safety Check), Autodiscover
|
||||
# Unterstützt: DKIM, SPF (Merge), DMARC, MX, Autodiscover
|
||||
# NEU: Setzt mail/imap/smtp/pop Subdomains für domain-spezifischen Mailserver-Zugang
|
||||
|
||||
set -e
|
||||
|
||||
|
|
@ -9,8 +10,13 @@ set -e
|
|||
AWS_REGION=${AWS_REGION:-"us-east-2"}
|
||||
DRY_RUN=${DRY_RUN:-"false"}
|
||||
|
||||
# Ziel für Autodiscover/IMAP (wohin sollen Mail-Clients verbinden?)
|
||||
# Standard: mail.deinedomain.tld. Kann überschrieben werden.
|
||||
# IP des Mailservers - PFLICHT wenn keine CNAME-Kette gewünscht
|
||||
# export MAIL_SERVER_IP="1.2.3.4"
|
||||
MAIL_SERVER_IP=${MAIL_SERVER_IP:-""}
|
||||
|
||||
# Ziel-Server für Mailclients. Standard: mail.<kundendomain>
|
||||
# Wenn MAIL_SERVER_IP gesetzt ist, bekommt mail.<domain> einen A-Record
|
||||
# und imap/smtp/pop/webmail zeigen per CNAME auf mail.<domain>
|
||||
TARGET_MAIL_SERVER=${TARGET_MAIL_SERVER:-"mail.${DOMAIN_NAME}"}
|
||||
|
||||
# --- CHECKS ---
|
||||
|
|
@ -19,9 +25,18 @@ if [ -z "$CF_API_TOKEN" ]; then echo "❌ Fehler: CF_API_TOKEN fehlt."; exit 1;
|
|||
if ! command -v jq &> /dev/null; then echo "❌ Fehler: 'jq' fehlt."; exit 1; fi
|
||||
if ! command -v aws &> /dev/null; then echo "❌ Fehler: 'aws' CLI fehlt."; exit 1; fi
|
||||
|
||||
if [ -z "$MAIL_SERVER_IP" ] && [ "$TARGET_MAIL_SERVER" == "mail.$DOMAIN_NAME" ]; then
|
||||
echo "⚠️ WARNUNG: MAIL_SERVER_IP ist nicht gesetzt!"
|
||||
echo " mail.$DOMAIN_NAME braucht einen A-Record."
|
||||
echo " Bitte setzen: export MAIL_SERVER_IP=<IP deines Servers>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "============================================================"
|
||||
echo " 🛡️ DNS Migration Setup für: $DOMAIN_NAME"
|
||||
echo " 🌍 Region: $AWS_REGION"
|
||||
echo " 📬 Mail-Server Target: $TARGET_MAIL_SERVER"
|
||||
[ -n "$MAIL_SERVER_IP" ] && echo " 🖥️ Server IP: $MAIL_SERVER_IP"
|
||||
[ "$DRY_RUN" = "true" ] && echo " ⚠️ DRY RUN MODE - Keine Änderungen!"
|
||||
echo "============================================================"
|
||||
|
||||
|
|
@ -38,41 +53,31 @@ echo " ✅ Zone ID: $ZONE_ID"
|
|||
|
||||
# ------------------------------------------------------------------
|
||||
# FUNKTION: ensure_record
|
||||
# Prüft Existenz -> Create oder Update (je nach Typ)
|
||||
# ------------------------------------------------------------------
|
||||
ensure_record() {
|
||||
local type=$1
|
||||
local name=$2
|
||||
local content=$3
|
||||
local proxied=${4:-false}
|
||||
local priority=$5 # Optional für MX
|
||||
local priority=$5
|
||||
|
||||
echo " ⚙️ Prüfe $type $name..."
|
||||
|
||||
# Bestehenden Record suchen
|
||||
# Hinweis: Wir suchen exakt nach Name und Typ
|
||||
local search_res=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?type=$type&name=$name" \
|
||||
-H "Authorization: Bearer $CF_API_TOKEN" -H "Content-Type: application/json")
|
||||
|
||||
local rec_id=$(echo "$search_res" | jq -r '.result[0].id')
|
||||
local rec_content=$(echo "$search_res" | jq -r '.result[0].content')
|
||||
|
||||
# JSON Body bauen
|
||||
if [ "$type" == "MX" ]; then
|
||||
json_data=$(jq -n --arg t "$type" --arg n "$name" --arg c "$content" --argjson p "$proxied" --argjson prio "$priority" \
|
||||
'{type: $t, name: $n, content: $c, ttl: 3600, proxied: $p, priority: $prio}')
|
||||
elif [ "$type" == "TXT" ]; then
|
||||
# Bei TXT Quotes escapen falls nötig, aber jq macht das meist gut
|
||||
json_data=$(jq -n --arg t "$type" --arg n "$name" --arg c "$content" --argjson p "$proxied" \
|
||||
'{type: $t, name: $n, content: $c, ttl: 3600, proxied: $p}')
|
||||
else
|
||||
json_data=$(jq -n --arg t "$type" --arg n "$name" --arg c "$content" --argjson p "$proxied" \
|
||||
'{type: $t, name: $n, content: $c, ttl: 3600, proxied: $p}')
|
||||
fi
|
||||
|
||||
# LOGIK
|
||||
if [ "$rec_id" == "null" ]; then
|
||||
# --- CREATE ---
|
||||
if [ "$DRY_RUN" = "true" ]; then
|
||||
echo " [DRY] Würde ERSTELLEN: $content"
|
||||
else
|
||||
|
|
@ -81,34 +86,27 @@ ensure_record() {
|
|||
if [ "$(echo $res | jq -r .success)" == "true" ]; then
|
||||
echo " ✅ Erstellt."
|
||||
else
|
||||
echo " ❌ Fehler beim Erstellen: $(echo $res | jq -r .errors[0].message)"
|
||||
echo " ❌ Fehler: $(echo $res | jq -r .errors[0].message)"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
# --- EXISTS ---
|
||||
if [ "$rec_content" == "$content" ]; then
|
||||
echo " 🆗 Identisch vorhanden. Überspringe."
|
||||
echo " 🆗 Identisch. Überspringe."
|
||||
else
|
||||
# Inhalt anders -> Update oder Error?
|
||||
if [ "$type" == "MX" ] && [ "$name" == "$DOMAIN_NAME" ]; then
|
||||
echo " ⛔ MX Record existiert aber ist anders!"
|
||||
echo " Gefunden: $rec_content"
|
||||
echo " Erwartet: $content"
|
||||
echo " ABBRUCH: Bitte alten MX Record ID $rec_id manuell löschen."
|
||||
# Wir brechen hier nicht das ganze Script ab, aber setzen den neuen nicht.
|
||||
echo " ⛔ MX existiert aber anders! Gefunden: $rec_content / Erwartet: $content"
|
||||
echo " Bitte Record ID $rec_id manuell löschen."
|
||||
return
|
||||
fi
|
||||
|
||||
# Für TXT (SPF/DMARC) oder CNAME machen wir ein UPDATE (Overwrite)
|
||||
if [ "$DRY_RUN" = "true" ]; then
|
||||
echo " [DRY] Würde UPDATEN von '$rec_content' auf '$content'"
|
||||
echo " [DRY] Würde UPDATEN: '$rec_content' → '$content'"
|
||||
else
|
||||
res=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$rec_id" \
|
||||
-H "Authorization: Bearer $CF_API_TOKEN" -H "Content-Type: application/json" --data "$json_data")
|
||||
if [ "$(echo $res | jq -r .success)" == "true" ]; then
|
||||
echo " 🔄 Aktualisiert."
|
||||
else
|
||||
echo " ❌ Fehler beim Update: $(echo $res | jq -r .errors[0].message)"
|
||||
echo " ❌ Fehler: $(echo $res | jq -r .errors[0].message)"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
|
@ -120,37 +118,36 @@ ensure_record() {
|
|||
# ------------------------------------------------------------------
|
||||
echo ""
|
||||
echo "--- 1. MAIL FROM Domain ---"
|
||||
# Wenn von außen nicht gesetzt, versuche via AWS
|
||||
if [ -z "$MAIL_FROM_DOMAIN" ]; then
|
||||
echo " Variable MAIL_FROM_DOMAIN leer, frage AWS SES..."
|
||||
SES_JSON=$(aws sesv2 get-email-identity --email-identity $DOMAIN_NAME --region $AWS_REGION 2>/dev/null)
|
||||
MAIL_FROM_DOMAIN=$(echo "$SES_JSON" | jq -r '.MailFromAttributes.MailFromDomain')
|
||||
|
||||
if [ "$MAIL_FROM_DOMAIN" == "null" ] || [ -z "$MAIL_FROM_DOMAIN" ]; then
|
||||
MAIL_FROM_DOMAIN="mail.$DOMAIN_NAME"
|
||||
echo " ⚠️ Keine MAIL FROM in SES gefunden. Fallback auf: $MAIL_FROM_DOMAIN"
|
||||
echo " ⚠️ Kein MAIL FROM in SES. Fallback: $MAIL_FROM_DOMAIN"
|
||||
fi
|
||||
else
|
||||
echo " Nutze vorgegebene MAIL FROM: $MAIL_FROM_DOMAIN"
|
||||
echo " Nutze: $MAIL_FROM_DOMAIN"
|
||||
fi
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# SCHRITT 2: DKIM Records (CNAME)
|
||||
# SCHRITT 2: DKIM Records
|
||||
# ------------------------------------------------------------------
|
||||
echo ""
|
||||
echo "--- 2. DKIM Records ---"
|
||||
TOKENS=$(aws ses get-identity-dkim-attributes --identities $DOMAIN_NAME --region $AWS_REGION --query "DkimAttributes.\"$DOMAIN_NAME\".DkimTokens" --output text)
|
||||
TOKENS=$(aws ses get-identity-dkim-attributes --identities $DOMAIN_NAME --region $AWS_REGION \
|
||||
--query "DkimAttributes.\"$DOMAIN_NAME\".DkimTokens" --output text)
|
||||
for token in $TOKENS; do
|
||||
ensure_record "CNAME" "${token}._domainkey.$DOMAIN_NAME" "${token}.dkim.amazonses.com" false
|
||||
done
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# SCHRITT 3: SES Verification (_amazonses)
|
||||
# SCHRITT 3: SES Verification
|
||||
# ------------------------------------------------------------------
|
||||
echo ""
|
||||
echo "--- 3. SES Verification TXT ---"
|
||||
VERIF_TOKEN=$(aws ses get-identity-verification-attributes --identities $DOMAIN_NAME --region $AWS_REGION --query "VerificationAttributes.\"$DOMAIN_NAME\".VerificationToken" --output text)
|
||||
if [ "$VERIF_TOKEN" != "None" ]; then
|
||||
VERIF_TOKEN=$(aws ses get-identity-verification-attributes --identities $DOMAIN_NAME \
|
||||
--region $AWS_REGION --query "VerificationAttributes.\"$DOMAIN_NAME\".VerificationToken" --output text)
|
||||
if [ "$VERIF_TOKEN" != "None" ] && [ -n "$VERIF_TOKEN" ]; then
|
||||
ensure_record "TXT" "_amazonses.$DOMAIN_NAME" "$VERIF_TOKEN" false
|
||||
fi
|
||||
|
||||
|
|
@ -159,47 +156,27 @@ fi
|
|||
# ------------------------------------------------------------------
|
||||
echo ""
|
||||
echo "--- 4. MAIL FROM Subdomain ($MAIL_FROM_DOMAIN) ---"
|
||||
# MX für die Subdomain (feedback loop)
|
||||
ensure_record "MX" "$MAIL_FROM_DOMAIN" "feedback-smtp.$AWS_REGION.amazonses.com" false 10
|
||||
# SPF für die Subdomain (strikte SES Regel)
|
||||
ensure_record "TXT" "$MAIL_FROM_DOMAIN" "v=spf1 include:amazonses.com ~all" false
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# SCHRITT 5: Root Domain SPF (Merge Logic)
|
||||
# SCHRITT 5: Root Domain SPF
|
||||
# ------------------------------------------------------------------
|
||||
echo ""
|
||||
echo "--- 5. Root Domain SPF ---"
|
||||
if [ -n "$OLD_PROVIDER_SPF" ]; then
|
||||
# Merge: SES + Alter Provider
|
||||
FINAL_SPF="v=spf1 include:amazonses.com $OLD_PROVIDER_SPF ~all"
|
||||
echo " ℹ️ Modus: Migration (SES + Alt)"
|
||||
else
|
||||
# Nur SES
|
||||
FINAL_SPF="v=spf1 include:amazonses.com ~all"
|
||||
echo " ℹ️ Modus: SES only"
|
||||
fi
|
||||
ensure_record "TXT" "$DOMAIN_NAME" "$FINAL_SPF" false
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# SCHRITT 6: Root Domain MX (Safety First)
|
||||
# SCHRITT 6: Root Domain MX
|
||||
# ------------------------------------------------------------------
|
||||
echo ""
|
||||
echo "--- 6. Root Domain MX ---"
|
||||
# Hier wollen wir den Inbound SMTP von AWS (falls man AWS WorkMail nutzt oder DMS via AWS ingress)
|
||||
# WARTE: Du nutzt DMS. Dein DMS hat vermutlich eine eigene IP/Hostname (z.B. mail.buddelectric.net).
|
||||
# Wenn du SES NUR ZUM SENDEN nutzt, darfst du den Root MX NICHT auf Amazon ändern!
|
||||
#
|
||||
# Annahme: Du willst den MX für den Empfang setzen.
|
||||
# Da du oben "feedback-smtp" erwähnt hast, geht es wohl um den SES Return-Path.
|
||||
# Aber der "echte MX" für die Domain ($DOMAIN_NAME) zeigt auf DEINEN Mailserver (DMS).
|
||||
#
|
||||
# Falls du den MX auf deinen DMS Server zeigen lassen willst:
|
||||
TARGET_MX=${TARGET_MX:-"mail.$DOMAIN_NAME"}
|
||||
echo " ℹ️ Ziel-MX ist: $TARGET_MX"
|
||||
|
||||
# HINWEIS: MX Records brauchen oft einen Hostnamen, keine IP.
|
||||
# Wir prüfen, ob ein MX existiert.
|
||||
ensure_record "MX" "$DOMAIN_NAME" "$TARGET_MX" false 10
|
||||
ensure_record "MX" "$DOMAIN_NAME" "mail.$DOMAIN_NAME" false 10
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# SCHRITT 7: DMARC
|
||||
|
|
@ -209,34 +186,51 @@ echo "--- 7. DMARC ---"
|
|||
ensure_record "TXT" "_dmarc.$DOMAIN_NAME" "v=DMARC1; p=none; rua=mailto:postmaster@$DOMAIN_NAME" false
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# SCHRITT 8: Autodiscover / Autoconfig
|
||||
# SCHRITT 8 (NEU): Mailclient Subdomains
|
||||
# ------------------------------------------------------------------
|
||||
echo ""
|
||||
echo "--- 8. Autodiscover / Autoconfig ---"
|
||||
# Ziel ist meist der IMAP/SMTP Server
|
||||
echo " ℹ️ Ziel für Clients: $TARGET_MAIL_SERVER"
|
||||
echo "--- 8. Mailclient Subdomains (A + CNAME) ---"
|
||||
|
||||
ensure_record "CNAME" "autodiscover.$DOMAIN_NAME" "$TARGET_MAIL_SERVER" false
|
||||
ensure_record "CNAME" "autoconfig.$DOMAIN_NAME" "$TARGET_MAIL_SERVER" false
|
||||
if [ -n "$MAIL_SERVER_IP" ]; then
|
||||
# A-Record für mail.<domain> direkt auf Server-IP
|
||||
ensure_record "A" "mail.$DOMAIN_NAME" "$MAIL_SERVER_IP" false
|
||||
else
|
||||
# CNAME auf externen Ziel-Host (nur wenn verschieden)
|
||||
if [ "$TARGET_MAIL_SERVER" != "mail.$DOMAIN_NAME" ]; then
|
||||
ensure_record "CNAME" "mail.$DOMAIN_NAME" "$TARGET_MAIL_SERVER" false
|
||||
fi
|
||||
fi
|
||||
|
||||
# imap, smtp, pop, webmail → CNAME auf mail.<domain>
|
||||
ensure_record "CNAME" "imap.$DOMAIN_NAME" "mail.$DOMAIN_NAME" false
|
||||
ensure_record "CNAME" "smtp.$DOMAIN_NAME" "mail.$DOMAIN_NAME" false
|
||||
ensure_record "CNAME" "pop.$DOMAIN_NAME" "mail.$DOMAIN_NAME" false
|
||||
ensure_record "CNAME" "webmail.$DOMAIN_NAME" "mail.$DOMAIN_NAME" false
|
||||
|
||||
# Füge das zu deinem Skript hinzu (Schritt 9 optional):
|
||||
# ------------------------------------------------------------------
|
||||
# SCHRITT 9: SRV Records (Service Discovery)
|
||||
# SCHRITT 9: Autodiscover / Autoconfig
|
||||
# ------------------------------------------------------------------
|
||||
echo ""
|
||||
echo "--- 9. SRV Records (Service Discovery) ---"
|
||||
# Das hilft Outlook, direkt "email-srvr.com" zu nutzen statt "mail.domain.tld"
|
||||
# Format: _service._proto.name TTL class SRV priority weight port target
|
||||
echo "--- 9. Autodiscover / Autoconfig ---"
|
||||
ensure_record "CNAME" "autodiscover.$DOMAIN_NAME" "mail.$DOMAIN_NAME" false
|
||||
ensure_record "CNAME" "autoconfig.$DOMAIN_NAME" "mail.$DOMAIN_NAME" false
|
||||
|
||||
# IMAP SRV
|
||||
ensure_record "SRV" "_imap._tcp.$DOMAIN_NAME" "0 5 143 $TARGET_MAIL_SERVER" false
|
||||
# IMAPS SRV (Port 993)
|
||||
ensure_record "SRV" "_imaps._tcp.$DOMAIN_NAME" "0 5 993 $TARGET_MAIL_SERVER" false
|
||||
# SUBMISSION SRV (Port 587)
|
||||
ensure_record "SRV" "_submission._tcp.$DOMAIN_NAME" "0 5 587 $TARGET_MAIL_SERVER" false
|
||||
|
||||
echo " ✅ SRV Records gesetzt (Server: $TARGET_MAIL_SERVER)"
|
||||
# ------------------------------------------------------------------
|
||||
# SCHRITT 10: SRV Records
|
||||
# ------------------------------------------------------------------
|
||||
echo ""
|
||||
echo "--- 10. SRV Records ---"
|
||||
ensure_record "SRV" "_imap._tcp.$DOMAIN_NAME" "0 5 143 mail.$DOMAIN_NAME" false
|
||||
ensure_record "SRV" "_imaps._tcp.$DOMAIN_NAME" "0 5 993 mail.$DOMAIN_NAME" false
|
||||
ensure_record "SRV" "_submission._tcp.$DOMAIN_NAME" "0 5 587 mail.$DOMAIN_NAME" false
|
||||
|
||||
echo ""
|
||||
echo "✅ Fertig."
|
||||
echo "============================================================"
|
||||
echo "✅ Fertig für Domain: $DOMAIN_NAME"
|
||||
echo ""
|
||||
echo " Mailclient-Konfiguration für Kunden:"
|
||||
echo " IMAP: imap.$DOMAIN_NAME Port 993 (SSL)"
|
||||
echo " SMTP: smtp.$DOMAIN_NAME Port 587 (STARTTLS) oder 465 (SSL)"
|
||||
echo " POP3: pop.$DOMAIN_NAME Port 995 (SSL)"
|
||||
echo " Webmail: webmail.$DOMAIN_NAME"
|
||||
echo "============================================================"
|
||||
282
caddy/Caddyfile
282
caddy/Caddyfile
|
|
@ -19,285 +19,3 @@ autoconfig.bayarea-cc.com, autoconfig.bizmatch.net {
|
|||
respond "Autodiscover Service Online" 200
|
||||
}
|
||||
|
||||
# Prod: Neue Domains
|
||||
www.bizmatch.net {
|
||||
handle /pictures/* {
|
||||
root * /home/aknuth/git/bizmatch-project/bizmatch-server # Prod-Ordner
|
||||
file_server
|
||||
}
|
||||
# Statische Dateien (CSS, JS, Bilder) – lange cachen, da sich der Name bei Änderungen ändert
|
||||
header /assets/* Cache-Control "public, max-age=31536000, immutable"
|
||||
header /*.css Cache-Control "public, max-age=31536000, immutable"
|
||||
header /*.js Cache-Control "public, max-age=31536000, immutable"
|
||||
|
||||
# Die index.html und API-Antworten – NIEMALS cachen
|
||||
header /index.html Cache-Control "no-cache, no-store, must-revalidate"
|
||||
|
||||
#handle {
|
||||
# root * /home/aknuth/git/bizmatch-project-prod/bizmatch/dist/bizmatch/browser # Neuer Prod-Dist-Ordner
|
||||
# try_files {path} {path}/ /index.html
|
||||
# file_server
|
||||
#}
|
||||
handle {
|
||||
reverse_proxy host.docker.internal:4200
|
||||
}
|
||||
log {
|
||||
output file /var/log/caddy/access.prod.log # Separate Logs
|
||||
}
|
||||
encode gzip zstd
|
||||
}
|
||||
bizmatch.net {
|
||||
redir https://www.bizmatch.net{uri} permanent
|
||||
import email_settings
|
||||
}
|
||||
www.qrmaster.net {
|
||||
handle {
|
||||
reverse_proxy host.docker.internal:3050
|
||||
}
|
||||
log {
|
||||
output file /var/log/caddy/qrmaster.log
|
||||
format console
|
||||
}
|
||||
encode gzip
|
||||
}
|
||||
qrmaster.net {
|
||||
redir https://www.qrmaster.net{uri} permanent
|
||||
}
|
||||
bayarea-cc.com {
|
||||
# TLS-Direktive entfernen, falls Cloudflare die Verbindung terminiert
|
||||
# tls {
|
||||
# dns cloudflare {env.CLOUDFLARE_API_TOKEN}
|
||||
# }
|
||||
|
||||
handle /api {
|
||||
reverse_proxy host.docker.internal:3001
|
||||
}
|
||||
handle {
|
||||
root * /app
|
||||
try_files {path} /index.html
|
||||
file_server
|
||||
}
|
||||
log {
|
||||
output stderr
|
||||
format console
|
||||
}
|
||||
encode gzip
|
||||
import email_settings
|
||||
}
|
||||
www.bayarea-cc.com {
|
||||
redir https://bayarea-cc.com{uri} permanent
|
||||
}
|
||||
setup.bayarea-cc.com {
|
||||
# Wir setzen das Root-Verzeichnis auf den neuen Pfad im Container
|
||||
root * /var/www/email-setup
|
||||
|
||||
# Webserver-Standardverhalten
|
||||
file_server
|
||||
|
||||
# Wenn jemand nur die Domain aufruft, zeige setup.html
|
||||
try_files {path} /setup.html
|
||||
}
|
||||
cielectrical.bayarea-cc.com {
|
||||
# wenn du API innerhalb von Next bedienst, weiterleiten an den Next Prozess
|
||||
handle {
|
||||
reverse_proxy host.docker.internal:3000
|
||||
}
|
||||
log {
|
||||
output file /var/log/caddy/cielectrical.log
|
||||
format console
|
||||
}
|
||||
encode gzip
|
||||
}
|
||||
hamptonbrown.bayarea-cc.com {
|
||||
# wenn du API innerhalb von Next bedienst, weiterleiten an den Next Prozess
|
||||
handle {
|
||||
reverse_proxy host.docker.internal:3010
|
||||
}
|
||||
log {
|
||||
output file /var/log/caddy/hamptonbrown.log
|
||||
format console
|
||||
}
|
||||
encode gzip
|
||||
}
|
||||
nqsltd.bayarea-cc.com {
|
||||
# wenn du API innerhalb von Next bedienst, weiterleiten an den Next Prozess
|
||||
handle {
|
||||
reverse_proxy host.docker.internal:3020
|
||||
}
|
||||
log {
|
||||
output file /var/log/caddy/nqsltd.log
|
||||
format console
|
||||
}
|
||||
encode gzip
|
||||
}
|
||||
gregknoppcpa.bayarea-cc.com {
|
||||
# wenn du API innerhalb von Next bedienst, weiterleiten an den Next Prozess
|
||||
handle {
|
||||
reverse_proxy host.docker.internal:3030
|
||||
}
|
||||
log {
|
||||
output file /var/log/caddy/gregknoppcpa.log
|
||||
format console
|
||||
}
|
||||
encode gzip
|
||||
}
|
||||
buddelectric.bayarea-cc.com {
|
||||
# wenn du API innerhalb von Next bedienst, weiterleiten an den Next Prozess
|
||||
handle {
|
||||
reverse_proxy host.docker.internal:3040
|
||||
}
|
||||
log {
|
||||
output file /var/log/caddy/buddelectric.log
|
||||
format console
|
||||
}
|
||||
encode gzip zstd
|
||||
}
|
||||
iitwelders.bayarea-cc.com {
|
||||
# wenn du API innerhalb von Next bedienst, weiterleiten an den Next Prozess
|
||||
handle {
|
||||
reverse_proxy host.docker.internal:8080
|
||||
}
|
||||
log {
|
||||
output file /var/log/caddy/iitwelders.log
|
||||
format console
|
||||
}
|
||||
encode gzip
|
||||
}
|
||||
fancytextstuff.com {
|
||||
# wenn du API innerhalb von Next bedienst, weiterleiten an den Next Prozess
|
||||
handle {
|
||||
reverse_proxy host.docker.internal:3010
|
||||
}
|
||||
log {
|
||||
output file /var/log/caddy/fancytext.log
|
||||
format console
|
||||
}
|
||||
encode gzip
|
||||
}
|
||||
www.fancytextstuff.com {
|
||||
redir https://fancytextstuff.com{uri} permanent
|
||||
}
|
||||
auth.bizmatch.net {
|
||||
reverse_proxy https://bizmatch-net.firebaseapp.com {
|
||||
header_up Host bizmatch-net.firebaseapp.com
|
||||
header_up X-Forwarded-For {remote_host}
|
||||
header_up X-Forwarded-Proto {scheme}
|
||||
header_up X-Real-IP {remote_host}
|
||||
}
|
||||
}
|
||||
gitea.bizmatch.net {
|
||||
reverse_proxy gitea:3500
|
||||
}
|
||||
|
||||
dev.bizmatch.net {
|
||||
handle /pictures/* {
|
||||
root * /home/aknuth/git/bizmatch-project/bizmatch-server
|
||||
file_server
|
||||
}
|
||||
|
||||
handle {
|
||||
root * /home/aknuth/git/bizmatch-project/bizmatch/dist/bizmatch/browser
|
||||
try_files {path} {path}/ /index.html
|
||||
file_server
|
||||
}
|
||||
|
||||
log {
|
||||
output file /var/log/caddy/access.log {
|
||||
roll_size 10MB
|
||||
roll_keep 5
|
||||
roll_keep_for 48h
|
||||
}
|
||||
}
|
||||
|
||||
encode gzip
|
||||
|
||||
}
|
||||
|
||||
|
||||
api.bizmatch.net {
|
||||
reverse_proxy host.docker.internal:3001 { # Neu: Proxy auf Prod-Port 3001
|
||||
header_up X-Real-IP {http.request.header.CF-Connecting-IP}
|
||||
header_up X-Forwarded-For {http.request.header.CF-Connecting-IP}
|
||||
header_up X-Forwarded-Proto {http.request.header.X-Forwarded-Proto}
|
||||
header_up CF-IPCountry {http.request.header.CF-IPCountry}
|
||||
}
|
||||
}
|
||||
mailsync.bizmatch.net {
|
||||
reverse_proxy host.docker.internal:5000 {
|
||||
header_up X-Real-IP {http.request.header.CF-Connecting-IP}
|
||||
header_up X-Forwarded-For {http.request.header.CF-Connecting-IP}
|
||||
header_up X-Forwarded-Proto {http.request.header.X-Forwarded-Proto}
|
||||
header_up CF-IPCountry {http.request.header.CF-IPCountry}
|
||||
}
|
||||
}
|
||||
|
||||
# Roundcube für docker-mailserver
|
||||
app.email-bayarea.com {
|
||||
reverse_proxy roundcube:80
|
||||
|
||||
log {
|
||||
output stderr
|
||||
format console
|
||||
}
|
||||
|
||||
encode gzip
|
||||
}
|
||||
# Roundcube für docker-mailserver
|
||||
config.email-bayarea.com {
|
||||
|
||||
root * /home/aknuth/git/config-email/frontend/dist
|
||||
try_files {path} {path}/ /index.html
|
||||
file_server
|
||||
|
||||
log {
|
||||
output file /var/log/caddy/config-email.log
|
||||
}
|
||||
|
||||
encode gzip
|
||||
}
|
||||
# Roundcube für docker-mailserver
|
||||
api.email-bayarea.com {
|
||||
reverse_proxy host.docker.internal:3002
|
||||
|
||||
log {
|
||||
output stderr
|
||||
format console
|
||||
}
|
||||
|
||||
encode gzip
|
||||
}
|
||||
annavillesda.org {
|
||||
# API requests to backend
|
||||
handle /api/* {
|
||||
reverse_proxy host.docker.internal:3070
|
||||
}
|
||||
|
||||
# Frontend static files
|
||||
handle {
|
||||
root * /home/aknuth/git/annaville-sda-site/dist
|
||||
try_files {path} {path}/ /index.html
|
||||
file_server
|
||||
}
|
||||
|
||||
log {
|
||||
output file /var/log/caddy/access.prod.log
|
||||
}
|
||||
|
||||
encode gzip
|
||||
}
|
||||
www.annavillesda.org {
|
||||
redir https://annavillesda.org{uri} permanent
|
||||
}
|
||||
# -----------------
|
||||
# just for certificate generation
|
||||
# -----------------
|
||||
mail.andreasknuth.de {
|
||||
reverse_proxy nginx-mailcow:8080
|
||||
}
|
||||
web.email-bayarea.com {
|
||||
reverse_proxy nginx-mailcow:8080
|
||||
}
|
||||
# Dieser Block dient nur dazu, das Zertifikat für den Mailserver zu beschaffen/erneuern.
|
||||
mail.email-srvr.com {
|
||||
respond "Mailserver Certificate Authority is running." 200
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,14 +23,7 @@ services:
|
|||
- $PWD/email-setup:/var/www/email-setup
|
||||
- caddy_data:/data
|
||||
- caddy_config:/config
|
||||
- /home/aknuth/git/bizmatch-project/bizmatch/dist/bizmatch/browser:/home/aknuth/git/bizmatch-project/bizmatch/dist/bizmatch/browser
|
||||
- /home/aknuth/git/bizmatch-project-prod/bizmatch/dist/bizmatch/browser:/home/aknuth/git/bizmatch-project-prod/bizmatch/dist/bizmatch/browser
|
||||
- /home/aknuth/git/bizmatch-project/bizmatch-server/pictures:/home/aknuth/git/bizmatch-project/bizmatch-server/pictures
|
||||
- /home/aknuth/git/bizmatch-project-prod/bizmatch-server/pictures:/home/aknuth/git/bizmatch-project-prod/bizmatch-server/pictures
|
||||
- /home/aknuth/git/annaville-sda-site/dist:/home/aknuth/git/annaville-sda-site/dist:ro # ← DAS FEHLT!
|
||||
- /home/aknuth/git/bay-area-affiliates/dist/bay-area-affiliates/browser:/app
|
||||
- /home/aknuth/log/caddy:/var/log/caddy
|
||||
- /home/aknuth/git/config-email/frontend/dist:/home/aknuth/git/config-email/frontend/dist:ro
|
||||
environment:
|
||||
- CLOUDFLARE_API_TOKEN=${CLOUDFLARE_API_TOKEN}
|
||||
- CLOUDFLARE_EMAIL=${CLOUDFLARE_EMAIL}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,137 @@
|
|||
#!/bin/bash
|
||||
# update-caddy-certs.sh
|
||||
# Liest alle Domains aus dem DMS und generiert die notwendigen
|
||||
# Caddyfile-Blöcke für Wildcard-Zertifikate.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# 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
|
||||
|
||||
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
|
||||
DRY_RUN=${DRY_RUN:-"false"}
|
||||
|
||||
echo "============================================================"
|
||||
echo " 📜 Caddy Wildcard-Cert Konfig Generator"
|
||||
echo " DMS Container: $DMS_CONTAINER"
|
||||
echo " Output: $OUTPUT_FILE"
|
||||
[ "$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 Accounts gefunden!"
|
||||
exit 1
|
||||
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
|
||||
|
||||
# --- 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"
|
||||
|
||||
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"
|
||||
done
|
||||
|
||||
# --- Ausgabe ---
|
||||
if [ "$DRY_RUN" = "true" ]; then
|
||||
echo ""
|
||||
echo "--- VORSCHAU (DRY RUN) ---"
|
||||
printf '%b' "$CONTENT"
|
||||
echo "--- ENDE VORSCHAU ---"
|
||||
else
|
||||
printf '%b' "$CONTENT" > "$OUTPUT_FILE"
|
||||
echo ""
|
||||
echo " ✅ Geschrieben: $OUTPUT_FILE"
|
||||
fi
|
||||
|
||||
# --- Prüfen ob Import im Caddyfile vorhanden ---
|
||||
CADDYFILE="$CADDY_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 ""
|
||||
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
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- Caddy reload ---
|
||||
echo ""
|
||||
echo "============================================================"
|
||||
echo "🔄 Nächste Schritte:"
|
||||
echo ""
|
||||
echo "1. Caddyfile prüfen - 'import mail_certs' muss vorhanden sein"
|
||||
echo ""
|
||||
echo "2. Caddy Konfiguration validieren:"
|
||||
echo " docker exec caddy caddy validate --config /etc/caddy/Caddyfile"
|
||||
echo ""
|
||||
echo "3. Caddy neu laden (kein Downtime):"
|
||||
echo " docker exec caddy caddy reload --config /etc/caddy/Caddyfile"
|
||||
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 " ls /var/lib/docker/volumes/caddy_data/_data/caddy/certificates/"
|
||||
echo " acme-v02.api.letsencrypt.org-directory/"
|
||||
echo "============================================================"
|
||||
Loading…
Reference in New Issue