email-amazon/basic_setup/cloudflareMigrationDns.sh

358 lines
12 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 - DNS Setup für sanfte E-Mail-Migration
#
# Dieses Script ist speziell für die Migration von einem alten Provider
# zu Bay Area Email (Amazon SES + DMS). Es:
# - Erkennt automatisch die MAIL FROM Subdomain aus SES
# - Prüft auf CNAME-Konflikte bevor Records gesetzt werden
# - Enthält den alten Provider SPF während der Migration
# - Setzt KEINE Autodiscover/SRV Records (das kommt erst in Phase 3)
#
# Voraussetzungen:
# - awsses.sh wurde bereits ausgeführt (SES Identity existiert)
# - Domain ist bereits in Cloudflare
#
# Verwendung:
# export DOMAIN_NAME="buddelectric.net"
# export CF_API_TOKEN="xxx"
# export OLD_PROVIDER_SPF="include:_spf.hostedemail.com" # SPF des alten Providers
# ./cloudflareMigrationDns.sh
#
# Optionale Parameter:
# export OLD_PROVIDER_SPF="" # Kein alter SPF nötig
# export DRY_RUN="true" # Nur anzeigen, nichts ändern
# export SKIP_MX="true" # MX nicht setzen (für Tests)
set -e
# ==========================================
# KONFIGURATION & CHECKS
# ==========================================
AWS_REGION=${AWS_REGION:-"us-east-2"}
DRY_RUN=${DRY_RUN:-"false"}
SKIP_MX=${SKIP_MX:-"false"}
if [ -z "$DOMAIN_NAME" ]; then
echo "❌ Fehler: DOMAIN_NAME ist nicht gesetzt."
echo " export DOMAIN_NAME='buddelectric.net'"
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 " 📧 Migration DNS Setup für: $DOMAIN_NAME"
echo "============================================================"
if [ "$DRY_RUN" = "true" ]; then
echo " ⚠️ DRY RUN - Es werden KEINE Änderungen vorgenommen!"
fi
echo ""
# ==========================================
# 1. MAIL FROM Subdomain aus SES ermitteln
# ==========================================
echo "--- [1/7] MAIL FROM Subdomain aus SES ermitteln ---"
SES_IDENTITY=$(aws sesv2 get-email-identity \
--email-identity "${DOMAIN_NAME}" \
--region "${AWS_REGION}" \
--output json 2>/dev/null)
if [ $? -ne 0 ] || [ -z "$SES_IDENTITY" ]; then
echo "❌ SES Identity für ${DOMAIN_NAME} nicht gefunden!"
echo " Bitte zuerst ./awsses.sh ausführen."
exit 1
fi
MAIL_FROM_DOMAIN=$(echo "$SES_IDENTITY" | jq -r '.MailFromAttributes.MailFromDomain // empty')
if [ -z "$MAIL_FROM_DOMAIN" ]; then
echo "⚠️ Keine MAIL FROM Domain in SES konfiguriert. Verwende mail.${DOMAIN_NAME}"
MAIL_FROM_DOMAIN="mail.${DOMAIN_NAME}"
fi
# Extrahiere den Subdomain-Prefix (z.B. "mailfrom" aus "mailfrom.buddelectric.net")
MAIL_FROM_PREFIX=$(echo "$MAIL_FROM_DOMAIN" | sed "s/\.${DOMAIN_NAME}$//")
echo " ✓ MAIL FROM Domain: ${MAIL_FROM_DOMAIN} (Prefix: ${MAIL_FROM_PREFIX})"
# ==========================================
# 2. Cloudflare Zone ID ermitteln
# ==========================================
echo ""
echo "--- [2/7] Cloudflare Zone ID ermitteln ---"
ZONE_RESPONSE=$(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")
if [ "$(echo $ZONE_RESPONSE | jq -r '.success')" != "true" ]; then
echo "❌ Fehler beim Abrufen der Zone ID:"
echo $ZONE_RESPONSE | jq .
exit 1
fi
CF_ZONE_ID=$(echo $ZONE_RESPONSE | jq -r '.result[0].id')
if [ "$CF_ZONE_ID" = "null" ] || [ -z "$CF_ZONE_ID" ]; then
echo "❌ Zone für $DOMAIN_NAME nicht in Cloudflare gefunden!"
exit 1
fi
echo " ✓ Zone ID: $CF_ZONE_ID"
# ==========================================
# 3. Bestehende Records prüfen (Konflikte erkennen)
# ==========================================
echo ""
echo "--- [3/7] Bestehende DNS Records prüfen ---"
# Alle Records der Zone abrufen
EXISTING_RECORDS=$(curl -s -X GET \
"https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records?per_page=100" \
-H "Authorization: Bearer $CF_API_TOKEN" \
-H "Content-Type: application/json")
# Prüfe auf CNAME-Konflikt mit MAIL FROM Subdomain
MAIL_FROM_CNAME=$(echo "$EXISTING_RECORDS" | jq -r \
--arg name "${MAIL_FROM_DOMAIN}" \
'.result[] | select(.type == "CNAME" and .name == $name) | .content')
if [ -n "$MAIL_FROM_CNAME" ]; then
echo ""
echo " ⚠️ KONFLIKT ERKANNT!"
echo " ${MAIL_FROM_DOMAIN} hat einen CNAME → ${MAIL_FROM_CNAME}"
echo " Ein CNAME erlaubt keine weiteren Records (MX, TXT)."
echo ""
echo " Optionen:"
echo " 1) CNAME löschen lassen (falls kein Client diesen Hostnamen nutzt)"
echo " 2) awsses.sh mit MAIL_FROM_SUBDOMAIN='mailfrom' nochmal ausführen"
echo ""
# Prüfe ob es ein alternativer MAIL FROM wäre
if [ "$MAIL_FROM_PREFIX" = "mail" ]; then
echo " 💡 Empfehlung: Führe folgendes aus und starte dann dieses Script neu:"
echo ""
echo " export MAIL_FROM_SUBDOMAIN=\"mailfrom\""
echo " export DOMAIN_NAME=\"${DOMAIN_NAME}\""
echo " ./awsses.sh"
echo ""
echo " Dann dieses Script erneut starten."
exit 1
fi
fi
# Prüfe auf bestehenden MX Record
EXISTING_MX=$(echo "$EXISTING_RECORDS" | jq -r \
--arg name "${DOMAIN_NAME}" \
'.result[] | select(.type == "MX" and .name == $name) | "\(.priority) \(.content)"')
if [ -n "$EXISTING_MX" ]; then
echo " Bestehende MX Records:"
echo "$EXISTING_MX" | while read line; do echo " $line"; done
echo " → Diese müssen manuell gelöscht werden bevor der neue MX gesetzt wird!"
echo " (Script erstellt nur neue Records, löscht keine alten)"
fi
# Prüfe auf bestehenden SPF
EXISTING_SPF=$(echo "$EXISTING_RECORDS" | jq -r \
--arg name "${DOMAIN_NAME}" \
'.result[] | select(.type == "TXT" and .name == $name and (.content | contains("v=spf1"))) | .content')
if [ -n "$EXISTING_SPF" ]; then
echo " Bestehender SPF: $EXISTING_SPF"
echo " → Muss manuell gelöscht/ersetzt werden!"
fi
echo ""
# ==========================================
# HILFSFUNKTIONEN
# ==========================================
create_dns_record() {
local TYPE=$1
local NAME=$2
local CONTENT=$3
local PROXIED=${4:-"false"}
local TTL=${5:-3600}
local PRIORITY=$6
if [ "$DRY_RUN" = "true" ]; then
echo " [DRY RUN] Würde erstellen: $TYPE $NAME$CONTENT"
[ -n "$PRIORITY" ] && echo " Priorität: $PRIORITY"
return
fi
local JSON_DATA=""
if [ "$TYPE" = "MX" ]; then
if [ -z "$PRIORITY" ]; then PRIORITY=10; fi
JSON_DATA="{
\"type\": \"$TYPE\", \"name\": \"$NAME\", \"content\": \"$CONTENT\",
\"ttl\": $TTL, \"priority\": $PRIORITY, \"proxied\": $PROXIED
}"
elif [ "$TYPE" = "TXT" ]; then
CONTENT=$(echo "$CONTENT" | sed 's/"//g')
JSON_DATA="{
\"type\": \"$TYPE\", \"name\": \"$NAME\", \"content\": \"\\\"$CONTENT\\\"\",
\"ttl\": $TTL, \"proxied\": $PROXIED
}"
else
JSON_DATA="{
\"type\": \"$TYPE\", \"name\": \"$NAME\", \"content\": \"$CONTENT\",
\"ttl\": $TTL, \"proxied\": $PROXIED
}"
fi
RESULT=$(curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records" \
-H "Authorization: Bearer $CF_API_TOKEN" \
-H "Content-Type: application/json" \
--data "$JSON_DATA")
SUCCESS=$(echo "$RESULT" | jq -r '.success')
if [ "$SUCCESS" = "true" ]; then
echo "$TYPE $NAME$CONTENT"
else
ERROR_MSG=$(echo "$RESULT" | jq -r '.errors[0].message // "Unbekannt"')
ERROR_CODE=$(echo "$RESULT" | jq -r '.errors[0].code // 0')
if [ "$ERROR_CODE" = "81057" ]; then
echo "$TYPE $NAME existiert bereits (übersprungen)"
else
echo "$TYPE $NAME FEHLER: $ERROR_MSG"
fi
fi
}
# ==========================================
# 4. DKIM Records (aus SES API)
# ==========================================
echo "--- [4/7] SES DKIM Records ---"
DKIM_TOKENS=$(aws ses get-identity-dkim-attributes \
--identities ${DOMAIN_NAME} --region ${AWS_REGION} \
--query "DkimAttributes.\"${DOMAIN_NAME}\".DkimTokens" --output text 2>/dev/null)
if [ -n "$DKIM_TOKENS" ] && [ "$DKIM_TOKENS" != "None" ]; then
for TOKEN in ${DKIM_TOKENS}; do
create_dns_record "CNAME" "${TOKEN}._domainkey.${DOMAIN_NAME}" "${TOKEN}.dkim.amazonses.com" "false"
done
else
echo " ⚠️ Keine DKIM Tokens gefunden. SES Identity evtl. noch nicht verifiziert?"
fi
# SES Verification Token
VERIFICATION_TOKEN=$(aws ses get-identity-verification-attributes \
--identities ${DOMAIN_NAME} --region ${AWS_REGION} \
--query "VerificationAttributes.\"${DOMAIN_NAME}\".VerificationToken" --output text 2>/dev/null)
if [ -n "$VERIFICATION_TOKEN" ] && [ "$VERIFICATION_TOKEN" != "None" ]; then
create_dns_record "TXT" "_amazonses.${DOMAIN_NAME}" "${VERIFICATION_TOKEN}" "false"
fi
# ==========================================
# 5. MX Record (SES Inbound)
# ==========================================
echo ""
echo "--- [5/7] MX Record ---"
if [ "$SKIP_MX" = "true" ]; then
echo " ⏭ MX übersprungen (SKIP_MX=true)"
echo " → Setze MX manuell wenn du bereit bist:"
echo " ${DOMAIN_NAME} MX 10 inbound-smtp.${AWS_REGION}.amazonaws.com"
else
echo " ⚠️ ACHTUNG: Alte MX Records müssen VORHER manuell gelöscht werden!"
echo " Alter MX vorhanden? Siehe Prüfung oben."
echo ""
create_dns_record "MX" "${DOMAIN_NAME}" "inbound-smtp.${AWS_REGION}.amazonaws.com" "false" 3600 10
fi
# ==========================================
# 6. MAIL FROM Subdomain (MX + SPF)
# ==========================================
echo ""
echo "--- [6/7] MAIL FROM Subdomain: ${MAIL_FROM_DOMAIN} ---"
create_dns_record "MX" "${MAIL_FROM_DOMAIN}" "feedback-smtp.${AWS_REGION}.amazonses.com" "false" 3600 10
create_dns_record "TXT" "${MAIL_FROM_DOMAIN}" "v=spf1 include:amazonses.com ~all" "false"
# ==========================================
# 7. SPF & DMARC (mit altem Provider!)
# ==========================================
echo ""
echo "--- [7/7] SPF & DMARC ---"
# SPF mit beiden Providern (Migration!)
if [ -n "$OLD_PROVIDER_SPF" ]; then
SPF_RECORD="v=spf1 include:amazonses.com ${OLD_PROVIDER_SPF} ~all"
echo " Migrations-SPF (enthält alten Provider):"
else
SPF_RECORD="v=spf1 include:amazonses.com ~all"
echo " Standard-SPF (kein alter Provider angegeben):"
fi
echo " ${SPF_RECORD}"
create_dns_record "TXT" "${DOMAIN_NAME}" "${SPF_RECORD}" "false"
# DMARC
create_dns_record "TXT" "_dmarc.${DOMAIN_NAME}" "v=DMARC1; p=none; pct=100; rua=mailto:postmaster@${DOMAIN_NAME}" "false"
# ==========================================
# ZUSAMMENFASSUNG
# ==========================================
echo ""
echo "============================================================"
if [ "$DRY_RUN" = "true" ]; then
echo " ⚠️ DRY RUN abgeschlossen - keine Änderungen gemacht"
else
echo " ✅ Migration DNS Setup abgeschlossen"
fi
echo "============================================================"
echo ""
echo " Domain: $DOMAIN_NAME"
echo " MAIL FROM: $MAIL_FROM_DOMAIN"
echo " SPF: $SPF_RECORD"
if [ "$SKIP_MX" = "true" ]; then
echo " MX: ⏭ ÜBERSPRUNGEN (manuelle Aktivierung nötig)"
else
echo " MX: inbound-smtp.${AWS_REGION}.amazonaws.com"
fi
echo ""
echo " 📋 MANUELLE SCHRITTE (falls noch nicht erledigt):"
echo " ─────────────────────────────────────────────────"
echo " 1. Alte MX Records für ${DOMAIN_NAME} löschen"
echo " 2. Alten SPF Record für ${DOMAIN_NAME} löschen"
echo " 3. Alten DKIM Record löschen (falls vorhanden)"
if [ -n "$MAIL_FROM_CNAME" ]; then
echo " 4. CNAME ${MAIL_FROM_DOMAIN}${MAIL_FROM_CNAME} löschen"
fi
echo ""
echo " ⚠️ NICHT LÖSCHEN während Migration:"
echo " ─────────────────────────────────────"
echo " - imap.${DOMAIN_NAME} (Clients holen noch dort ab)"
echo " - pop.${DOMAIN_NAME} (Clients holen noch dort ab)"
echo " - smtp.${DOMAIN_NAME} (Clients senden noch dort)"
echo " - webmail.${DOMAIN_NAME} (falls vorhanden)"
echo ""
echo " Diese Records werden erst in Phase 3 (nach vollständiger"
echo " Client-Umstellung) gelöscht mit cloudflareDns.sh"
echo "============================================================"