new scripts & new mail from

This commit is contained in:
2026-02-09 13:13:30 -06:00
parent 38fcf8c4d8
commit 8c3db9db95
3 changed files with 553 additions and 5 deletions

View File

@@ -0,0 +1,358 @@
#!/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 "============================================================"