#!/bin/bash # mailadminDns.sh # ------------------------------------------------------------------ # Setzt AUSSCHLIESSLICH den DNS-Record fuer den mailadmin-Zugang. # Faesst NICHTS anderes an (kein SES, SPF, DKIM, MX, keine anderen # Subdomains). Kann gefahrlos auf bereits migrierten Domains laufen. # # Verwendet dieselbe ensure_record-Logik wie cloudflareMigrationDns.sh, # damit das Verhalten (Create/Update/Skip-bei-identisch) identisch ist. # # ------------------------------------------------------------------ # PFLICHT-VARIABLEN: # DOMAIN_NAME Kundendomain, z.B. innungsapp.com # CF_API_TOKEN Cloudflare API Token # # OPTIONALE VARIABLEN: # MAILADMIN_HOST Subdomain-Label, Default: "mailadmin" # -> ergibt mailadmin. # MAILADMIN_TARGET CNAME-Ziel, Default: "mail." # Beispiele: # mail.innungsapp.com (pro-Domain Modell) # mailadmin.bayarea-cc.com (zentrales Modell) # DRY_RUN "true" -> zeigt nur an, aendert nichts # # ------------------------------------------------------------------ # BEISPIELE: # # # Standard: mailadmin.innungsapp.com -> mail.innungsapp.com # DOMAIN_NAME=innungsapp.com CF_API_TOKEN=xxx ./mailadminDns.sh # # # Zentrales Modell: mailadmin.innungsapp.com -> mailadmin.bayarea-cc.com # DOMAIN_NAME=innungsapp.com \ # MAILADMIN_TARGET=mailadmin.bayarea-cc.com \ # CF_API_TOKEN=xxx ./mailadminDns.sh # # # Erst testen ohne zu aendern # DOMAIN_NAME=innungsapp.com CF_API_TOKEN=xxx DRY_RUN=true ./mailadminDns.sh # # ------------------------------------------------------------------ set -e # --- KONFIGURATION --- DRY_RUN=${DRY_RUN:-"false"} MAILADMIN_HOST=${MAILADMIN_HOST:-"mailadmin"} MAILADMIN_TARGET=${MAILADMIN_TARGET:-"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 curl &> /dev/null; then echo "❌ Fehler: 'curl' fehlt."; exit 1; fi RECORD_NAME="${MAILADMIN_HOST}.${DOMAIN_NAME}" echo "============================================================" echo " 🔧 mailadmin DNS Setup" echo " 🌐 Domain: $DOMAIN_NAME" echo " 📍 Record: CNAME $RECORD_NAME → $MAILADMIN_TARGET" [ "$DRY_RUN" = "true" ] && echo " ⚠️ DRY RUN MODE - Keine Änderungen!" echo "============================================================" # Schutz: CNAME auf sich selbst macht keinen Sinn if [ "$RECORD_NAME" == "$MAILADMIN_TARGET" ]; then echo "❌ Fehler: CNAME-Ziel ist identisch mit dem Record-Namen." echo " $RECORD_NAME kann nicht auf sich selbst zeigen." echo " Setze MAILADMIN_TARGET auf einen anderen Host." exit 1 fi # 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 für $DOMAIN_NAME." exit 1 fi echo " ✅ Zone ID: $ZONE_ID" # ------------------------------------------------------------------ # FUNKTION: ensure_record # Identisch zur Logik in cloudflareMigrationDns.sh, reduziert auf # die Typen die wir hier brauchen (CNAME). # ------------------------------------------------------------------ ensure_record() { local type=$1 local name=$2 local content=$3 local proxied=${4:-false} 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') [ -z "$rec_id" ] && rec_id="null" [ -z "$rec_content" ] && rec_content="null" local 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}') if [ "$rec_id" == "null" ]; then if [ "$DRY_RUN" = "true" ]; then echo " [DRY] Würde ERSTELLEN: $type $name → $content" else local 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: $type $name → $content" else echo " ❌ Fehler beim Erstellen: $(echo $res | jq -r '.errors[0].message')" exit 1 fi fi else if [ "$rec_content" == "$content" ]; then echo " 🆗 Identisch ($rec_content). Überspringe." else if [ "$DRY_RUN" = "true" ]; then echo " [DRY] Würde UPDATEN: '$rec_content' → '$content'" else local 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: '$rec_content' → '$content'" else echo " ❌ Fehler beim Updaten: $(echo $res | jq -r '.errors[0].message')" exit 1 fi fi fi fi } # ------------------------------------------------------------------ # Der eigentliche Record # proxied=false ist hier wichtig: der mailadmin laeuft hinter Caddy # mit eigenem TLS-Zertifikat. Cloudflare-Proxy davor wuerde die # Zertifikatskette stoeren bzw. doppeltes TLS-Terminieren. # ------------------------------------------------------------------ echo "" echo "--- mailadmin CNAME ---" ensure_record "CNAME" "$RECORD_NAME" "$MAILADMIN_TARGET" false echo "" echo "============================================================" echo "✅ Fertig." echo "" echo " mailadmin erreichbar unter: https://$RECORD_NAME" echo "" echo " ⚠️ Wichtig: Caddy muss ein Zertifikat für $RECORD_NAME" echo " ausstellen. Stelle sicher, dass der Hostname in der" echo " Caddy-Konfiguration enthalten ist." echo "============================================================"