email-amazon/basic_setup/cloudflareMigrationDns.sh

242 lines
11 KiB
Bash
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
# cloudflareMigrationDns.sh
# Setzt DNS Records für Amazon SES Migration + Cloudflare
# Unterstützt: DKIM, SPF (Merge), DMARC, MX (Safety Check), Autodiscover
set -e
# --- KONFIGURATION ---
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.
TARGET_MAIL_SERVER=${TARGET_MAIL_SERVER:-"mail.${DOMAIN_NAME}"}
# --- CHECKS ---
if [ -z "$DOMAIN_NAME" ]; then echo "❌ Fehler: DOMAIN_NAME fehlt."; exit 1; fi
if [ -z "$CF_API_TOKEN" ]; then echo "❌ Fehler: CF_API_TOKEN fehlt."; exit 1; fi
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
echo "============================================================"
echo " 🛡️ DNS Migration Setup für: $DOMAIN_NAME"
echo " 🌍 Region: $AWS_REGION"
[ "$DRY_RUN" = "true" ] && echo " ⚠️ DRY RUN MODE - Keine Änderungen!"
echo "============================================================"
# 1. ZONE ID HOLEN
echo "🔍 Suche Cloudflare Zone ID..."
ZONE_ID=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones?name=$DOMAIN_NAME" \
-H "Authorization: Bearer $CF_API_TOKEN" -H "Content-Type: application/json" | jq -r '.result[0].id')
if [ "$ZONE_ID" == "null" ] || [ -z "$ZONE_ID" ]; then
echo "❌ Zone nicht gefunden."
exit 1
fi
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
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
res=$(curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records" \
-H "Authorization: Bearer $CF_API_TOKEN" -H "Content-Type: application/json" --data "$json_data")
if [ "$(echo $res | jq -r .success)" == "true" ]; then
echo " ✅ Erstellt."
else
echo " ❌ Fehler beim Erstellen: $(echo $res | jq -r .errors[0].message)"
fi
fi
else
# --- EXISTS ---
if [ "$rec_content" == "$content" ]; then
echo " 🆗 Identisch vorhanden. Ü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.
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'"
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)"
fi
fi
fi
fi
}
# ------------------------------------------------------------------
# SCHRITT 1: MAIL FROM ermitteln
# ------------------------------------------------------------------
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"
fi
else
echo " Nutze vorgegebene MAIL FROM: $MAIL_FROM_DOMAIN"
fi
# ------------------------------------------------------------------
# SCHRITT 2: DKIM Records (CNAME)
# ------------------------------------------------------------------
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)
for token in $TOKENS; do
ensure_record "CNAME" "${token}._domainkey.$DOMAIN_NAME" "${token}.dkim.amazonses.com" false
done
# ------------------------------------------------------------------
# SCHRITT 3: SES Verification (_amazonses)
# ------------------------------------------------------------------
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
ensure_record "TXT" "_amazonses.$DOMAIN_NAME" "$VERIF_TOKEN" false
fi
# ------------------------------------------------------------------
# SCHRITT 4: MAIL FROM Subdomain (MX + SPF)
# ------------------------------------------------------------------
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)
# ------------------------------------------------------------------
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)
# ------------------------------------------------------------------
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
# ------------------------------------------------------------------
# SCHRITT 7: DMARC
# ------------------------------------------------------------------
echo ""
echo "--- 7. DMARC ---"
ensure_record "TXT" "_dmarc.$DOMAIN_NAME" "v=DMARC1; p=none; rua=mailto:postmaster@$DOMAIN_NAME" false
# ------------------------------------------------------------------
# SCHRITT 8: Autodiscover / Autoconfig
# ------------------------------------------------------------------
echo ""
echo "--- 8. Autodiscover / Autoconfig ---"
# Ziel ist meist der IMAP/SMTP Server
echo " Ziel für Clients: $TARGET_MAIL_SERVER"
ensure_record "CNAME" "autodiscover.$DOMAIN_NAME" "$TARGET_MAIL_SERVER" false
ensure_record "CNAME" "autoconfig.$DOMAIN_NAME" "$TARGET_MAIL_SERVER" false
# Füge das zu deinem Skript hinzu (Schritt 9 optional):
# ------------------------------------------------------------------
# SCHRITT 9: SRV Records (Service Discovery)
# ------------------------------------------------------------------
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
# 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)"
echo ""
echo "✅ Fertig."