email-amazon/basic_setup/cloudflareMigrationDns.sh

238 lines
10 KiB
Bash
Executable File
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, Autodiscover
# NEU: Setzt mail/imap/smtp/pop Subdomains für domain-spezifischen Mailserver-Zugang
set -e
# --- KONFIGURATION ---
AWS_REGION=${AWS_REGION:-"us-east-2"}
DRY_RUN=${DRY_RUN:-"false"}
# 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 ---
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
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 "============================================================"
# 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
# ------------------------------------------------------------------
ensure_record() {
local type=$1
local name=$2
local content=$3
local proxied=${4:-false}
local priority=$5
echo " ⚙️ Prüfe $type $name..."
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')
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}')
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
if [ "$rec_id" == "null" ]; then
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: $(echo $res | jq -r .errors[0].message)"
fi
fi
else
if [ "$rec_content" == "$content" ]; then
echo " 🆗 Identisch. Überspringe."
else
if [ "$type" == "MX" ] && [ "$name" == "$DOMAIN_NAME" ]; then
echo " ⛔ MX existiert aber anders! Gefunden: $rec_content / Erwartet: $content"
echo " Bitte Record ID $rec_id manuell löschen."
return
fi
if [ "$DRY_RUN" = "true" ]; then
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: $(echo $res | jq -r .errors[0].message)"
fi
fi
fi
fi
}
# ------------------------------------------------------------------
# SCHRITT 1: MAIL FROM ermitteln
# ------------------------------------------------------------------
echo ""
echo "--- 1. MAIL FROM Domain ---"
if [ -z "$MAIL_FROM_DOMAIN" ]; then
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 " ⚠️ Kein MAIL FROM in SES. Fallback: $MAIL_FROM_DOMAIN"
fi
else
echo " Nutze: $MAIL_FROM_DOMAIN"
fi
# ------------------------------------------------------------------
# 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)
for token in $TOKENS; do
ensure_record "CNAME" "${token}._domainkey.$DOMAIN_NAME" "${token}.dkim.amazonses.com" false
done
# ------------------------------------------------------------------
# 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" ] && [ -n "$VERIF_TOKEN" ]; 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) ---"
ensure_record "MX" "$MAIL_FROM_DOMAIN" "feedback-smtp.$AWS_REGION.amazonses.com" false 10
ensure_record "TXT" "$MAIL_FROM_DOMAIN" "v=spf1 include:amazonses.com ~all" false
# ------------------------------------------------------------------
# SCHRITT 5: Root Domain SPF
# ------------------------------------------------------------------
echo ""
echo "--- 5. Root Domain SPF ---"
if [ -n "$OLD_PROVIDER_SPF" ]; then
FINAL_SPF="v=spf1 include:amazonses.com $OLD_PROVIDER_SPF ~all"
else
FINAL_SPF="v=spf1 include:amazonses.com ~all"
fi
ensure_record "TXT" "$DOMAIN_NAME" "$FINAL_SPF" false
# ------------------------------------------------------------------
# SCHRITT 6: Root Domain MX
# ------------------------------------------------------------------
# WICHTIG: Der MX Record zeigt auf Amazon SES (inbound-smtp.*.amazonaws.com),
# da eingehende Mails über SES → S3 → SQS → Worker → DMS laufen.
# Der DMS ist NICHT direkt aus dem Internet erreichbar.
# Dieser Record wird daher NICHT angefasst.
echo ""
echo "--- 6. Root Domain MX (nur Info, wird nicht geändert) ---"
EXISTING_MX=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?type=MX&name=$DOMAIN_NAME" \
-H "Authorization: Bearer $CF_API_TOKEN" -H "Content-Type: application/json" | jq -r '.result[0].content')
if [ "$EXISTING_MX" == "null" ] || [ -z "$EXISTING_MX" ]; then
echo " ⚠️ Kein MX Record gefunden! Bitte manuell in SES/Cloudflare setzen:"
echo " inbound-smtp.$AWS_REGION.amazonaws.com (Prio 10)"
else
echo " MX vorhanden: $EXISTING_MX (wird nicht geändert)"
fi
# ------------------------------------------------------------------
# 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 (NEU): Mailclient Subdomains
# ------------------------------------------------------------------
echo ""
echo "--- 8. Mailclient Subdomains (A + CNAME) ---"
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
# ------------------------------------------------------------------
# SCHRITT 9: Autodiscover / Autoconfig
# ------------------------------------------------------------------
echo ""
echo "--- 9. Autodiscover / Autoconfig ---"
ensure_record "CNAME" "autodiscover.$DOMAIN_NAME" "mail.$DOMAIN_NAME" false
ensure_record "CNAME" "autoconfig.$DOMAIN_NAME" "mail.$DOMAIN_NAME" false
echo ""
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 "============================================================"