diff --git a/basic_setup/cloudflareDns.sh b/basic_setup/cloudflareDns.sh index 296c1c2..d27b00b 100755 --- a/basic_setup/cloudflareDns.sh +++ b/basic_setup/cloudflareDns.sh @@ -157,24 +157,6 @@ echo "--- Autodiscover & Caddy Konfiguration ---" create_dns_record "A" "autodiscover.${DOMAIN_NAME}" "$CADDY_SERVER_IP" "false" create_dns_record "A" "autoconfig.${DOMAIN_NAME}" "$CADDY_SERVER_IP" "false" -# SRV-Records für Apple & Mobile Clients -# Wir decken jetzt ALLE Varianten ab, damit das iPhone sicher den richtigen Port findet. - -# 1. IMAP SSL (Port 993) - Das bevorzugt das iPhone! -create_srv_record "_imaps" "_tcp" "993" "$MAIL_SERVER_HOSTNAME" - -# 2. IMAP STARTTLS (Port 143) - Als Fallback (optional, falls Port 143 offen ist) -# create_srv_record "_imap" "_tcp" "143" "$MAIL_SERVER_HOSTNAME" -# (Wenn du 993 im "_imap" Eintrag lassen willst, ist das okay, aber _imaps ist wichtiger) -create_srv_record "_imap" "_tcp" "993" "$MAIL_SERVER_HOSTNAME" - -# 3. SMTP SSL (Port 465) - Das bevorzugt das iPhone oft für den Ausgang -create_srv_record "_smtps" "_tcp" "465" "$MAIL_SERVER_HOSTNAME" - -# 4. SMTP STARTTLS (Port 587) - Der Standard -create_srv_record "_submission" "_tcp" "587" "$MAIL_SERVER_HOSTNAME" - - # ========================================== # 4. SPF & DMARC # ========================================== diff --git a/basic_setup/cloudflareDnsPart.sh b/basic_setup/cloudflareDnsPart.sh deleted file mode 100755 index 703d2b1..0000000 --- a/basic_setup/cloudflareDnsPart.sh +++ /dev/null @@ -1,108 +0,0 @@ -#!/bin/bash - -# ========================================== -# KONFIGURATION -# ========================================== - -AWS_REGION="us-east-2" - -if [ -z "$DOMAIN_NAME" ]; then - echo "Fehler: DOMAIN_NAME ist nicht gesetzt (z.B. export DOMAIN_NAME='bayarea-cc.com')." - exit 1 -fi -if [ -z "$CF_API_TOKEN" ]; then - echo "Fehler: CF_API_TOKEN fehlt." - exit 1 -fi - -# ========================================== -# ZONE ID ERMITTELN -# ========================================== - -echo "Zone ID für $DOMAIN_NAME abrufen..." -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') -echo "Zone ID: $CF_ZONE_ID" - -# ========================================== -# FUNKTIONEN -# ========================================== - -create_dns_record() { - local TYPE=$1 - local NAME=$2 - local CONTENT=$3 - local PROXIED=$4 - local TTL=$5 - local PRIORITY=$6 - - if [ -z "$PROXIED" ]; then PROXIED="false"; fi - if [ -z "$TTL" ]; then TTL=3600; fi - - echo "Erstelle $TYPE-Eintrag für $NAME..." - - 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 - - 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" | jq -r '.success' -} - - -# ========================================== -# 1. AWS SES Setup (DKIM & Verifizierung) -# ========================================== -echo "--- AWS SES Konfiguration ---" -DKIM_TOKENS=$(aws ses get-identity-dkim-attributes \ - --identities ${DOMAIN_NAME} --region ${AWS_REGION} \ - --query "DkimAttributes.\"${DOMAIN_NAME}\".DkimTokens" --output text) - -VERIFICATION_TOKEN=$(aws ses get-identity-verification-attributes \ - --identities ${DOMAIN_NAME} --region ${AWS_REGION} \ - --query "VerificationAttributes.\"${DOMAIN_NAME}\".VerificationToken" --output text) - -if [ -n "$VERIFICATION_TOKEN" ]; then - create_dns_record "TXT" "_amazonses.${DOMAIN_NAME}" "${VERIFICATION_TOKEN}" "false" -fi - -# ========================================== -# 2. MX Records (AWS SES Ingest) -# ========================================== -echo "--- MX Records (AWS SES) ---" -# Hier leiten wir eingehende Mails an Amazon S3/SQS Pipeline -create_dns_record "MX" "${DOMAIN_NAME}" "inbound-smtp.${AWS_REGION}.amazonaws.com" "false" 3600 10 - -# ========================================== -# 4. SPF & DMARC -# ========================================== - -echo "Fertig. Konfiguration für $DOMAIN_NAME abgeschlossen." \ No newline at end of file diff --git a/basic_setup/cloudflareMigrationDns.sh b/basic_setup/cloudflareMigrationDns.sh index ddc7dc1..9d3b0a9 100644 --- a/basic_setup/cloudflareMigrationDns.sh +++ b/basic_setup/cloudflareMigrationDns.sh @@ -1,358 +1,242 @@ #!/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) +# cloudflareMigrationDns.sh +# Setzt DNS Records für Amazon SES Migration + Cloudflare +# Unterstützt: DKIM, SPF (Merge), DMARC, MX (Safety Check), Autodiscover set -e -# ========================================== -# KONFIGURATION & CHECKS -# ========================================== - +# --- KONFIGURATION --- 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 +# 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 " 📧 Migration DNS Setup für: $DOMAIN_NAME" +echo " 🛡️ DNS Migration Setup für: $DOMAIN_NAME" +echo " 🌍 Region: $AWS_REGION" +[ "$DRY_RUN" = "true" ] && echo " ⚠️ DRY RUN MODE - Keine Änderungen!" echo "============================================================" -if [ "$DRY_RUN" = "true" ]; then - echo " ⚠️ DRY RUN - Es werden KEINE Änderungen vorgenommen!" -fi -echo "" -# ========================================== -# 1. MAIL FROM Subdomain aus SES ermitteln -# ========================================== +# 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') -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 . +if [ "$ZONE_ID" == "null" ] || [ -z "$ZONE_ID" ]; then + echo "❌ Zone nicht gefunden." exit 1 fi +echo " ✅ Zone ID: $ZONE_ID" -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" +# ------------------------------------------------------------------ +# 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 -# ========================================== -# 3. Bestehende Records prüfen (Konflikte erkennen) -# ========================================== + echo " ⚙️ Prüfe $type $name..." -echo "" -echo "--- [3/7] Bestehende DNS Records prüfen ---" + # 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') -# 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 - }" + # 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="{ - \"type\": \"$TYPE\", \"name\": \"$NAME\", \"content\": \"$CONTENT\", - \"ttl\": $TTL, \"proxied\": $PROXIED - }" + 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 - 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)" + # LOGIK + if [ "$rec_id" == "null" ]; then + # --- CREATE --- + if [ "$DRY_RUN" = "true" ]; then + echo " [DRY] Würde ERSTELLEN: $content" else - echo " ✗ $TYPE $NAME FEHLER: $ERROR_MSG" + 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 } -# ========================================== -# 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 +# ------------------------------------------------------------------ +# 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 " ⚠️ Keine DKIM Tokens gefunden. SES Identity evtl. noch nicht verifiziert?" + echo " Nutze vorgegebene MAIL FROM: $MAIL_FROM_DOMAIN" 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) +# ------------------------------------------------------------------ +# 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 -if [ -n "$VERIFICATION_TOKEN" ] && [ "$VERIFICATION_TOKEN" != "None" ]; then - create_dns_record "TXT" "_amazonses.${DOMAIN_NAME}" "${VERIFICATION_TOKEN}" "false" +# ------------------------------------------------------------------ +# 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 -# ========================================== -# 5. MX Record (SES Inbound) -# ========================================== - +# ------------------------------------------------------------------ +# SCHRITT 4: MAIL FROM Subdomain (MX + SPF) +# ------------------------------------------------------------------ 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 "--- 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 "--- [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!) +echo "--- 5. Root Domain SPF ---" if [ -n "$OLD_PROVIDER_SPF" ]; then - SPF_RECORD="v=spf1 include:amazonses.com ${OLD_PROVIDER_SPF} ~all" - echo " ℹ️ Migrations-SPF (enthält alten Provider):" + # Merge: SES + Alter Provider + FINAL_SPF="v=spf1 include:amazonses.com $OLD_PROVIDER_SPF ~all" + echo " ℹ️ Modus: Migration (SES + Alt)" else - SPF_RECORD="v=spf1 include:amazonses.com ~all" - echo " ℹ️ Standard-SPF (kein alter Provider angegeben):" + # Nur SES + FINAL_SPF="v=spf1 include:amazonses.com ~all" + echo " ℹ️ Modus: SES only" fi -echo " ${SPF_RECORD}" +ensure_record "TXT" "$DOMAIN_NAME" "$FINAL_SPF" false -create_dns_record "TXT" "${DOMAIN_NAME}" "${SPF_RECORD}" "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" -# DMARC -create_dns_record "TXT" "_dmarc.${DOMAIN_NAME}" "v=DMARC1; p=none; pct=100; rua=mailto:postmaster@${DOMAIN_NAME}" "false" +# 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 -# ========================================== -# ZUSAMMENFASSUNG -# ========================================== +# ------------------------------------------------------------------ +# 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 "============================================================" -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 "============================================================" \ No newline at end of file +echo "✅ Fertig." \ No newline at end of file