558 lines
23 KiB
Bash
558 lines
23 KiB
Bash
|
|
#!/usr/bin/env bash
|
||
|
|
# =============================================================================
|
||
|
|
# BUILD-C3 — DNS Apply Script
|
||
|
|
# Wavult Group — Partner-/Geo-modell DNS-implementation
|
||
|
|
# =============================================================================
|
||
|
|
# Förberett av: OpenClaw BUILD-C2 subagent | 2026-06-06
|
||
|
|
# Status: FÖRBERETT — EJ KÖRT
|
||
|
|
#
|
||
|
|
# SÄKERHETSPOLICY:
|
||
|
|
# Detta skript KÖR INGA DNS-ändringar automatiskt.
|
||
|
|
# Varje sektion är en funktion som måste anropas explicit.
|
||
|
|
# Kör: bash c3-apply-dns.sh --help för instruktioner.
|
||
|
|
#
|
||
|
|
# KÖRMILJÖ:
|
||
|
|
# - Kräver AWS CLI med profil 'platform-ai-agent' (eller default-profil)
|
||
|
|
# - Kräver IAM-behörighet: route53:ChangeResourceRecordSets, route53:ListResourceRecordSets
|
||
|
|
# - Kräver: jq (för JSON-parsning)
|
||
|
|
# - Region: eu-north-1 (Route53 är globalt, men default-profil används)
|
||
|
|
#
|
||
|
|
# GODKÄNNANDESTRUKTUR:
|
||
|
|
# ✅ SÄKER — Kan köras utan Eriks godkännande (inga MX-ändringar, inga deletions)
|
||
|
|
# ⚠️ ERIK-OK — Kräver explicit godkännande av Erik innan körning
|
||
|
|
# 🔴 DESTRUKTIV — Tar bort data, kräver verifiering + Erik-godkännande
|
||
|
|
# =============================================================================
|
||
|
|
|
||
|
|
set -euo pipefail
|
||
|
|
|
||
|
|
# ---------------------------------------------------------------------------
|
||
|
|
# Konfiguration
|
||
|
|
# ---------------------------------------------------------------------------
|
||
|
|
AWS_REGION="eu-north-1"
|
||
|
|
DMARC_RUA="dmarc@aamos.systems"
|
||
|
|
MAILU_MX="mail.wavult.com"
|
||
|
|
MAILU_MX_PRIORITY=10
|
||
|
|
# DKIM-selektor och pubkey måste hämtas från Mailu-admin och fyllas i nedan
|
||
|
|
DKIM_SELECTOR="mail"
|
||
|
|
# OBS: Ersätt DKIM_PUBKEY_PLACEHOLDER med faktisk pubkey från Mailu-admin
|
||
|
|
# Hämta via: Mailu Admin → Domain → <domain> → DKIM → kopiera pubkey-strängen
|
||
|
|
DKIM_PUBKEY="HÄMTA-FRÅN-MAILU-ADMIN-FÖRE-KÖRNING"
|
||
|
|
|
||
|
|
# ---------------------------------------------------------------------------
|
||
|
|
# Hjälpfunktioner
|
||
|
|
# ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
log() { echo "[$(date -u '+%Y-%m-%dT%H:%M:%SZ')] $*"; }
|
||
|
|
warn() { echo "[WARN] $*" >&2; }
|
||
|
|
die() { echo "[ERROR] $*" >&2; exit 1; }
|
||
|
|
|
||
|
|
check_deps() {
|
||
|
|
command -v aws >/dev/null || die "aws CLI saknas"
|
||
|
|
command -v jq >/dev/null || die "jq saknas"
|
||
|
|
[[ "$DKIM_PUBKEY" == "HÄMTA-FRÅN-MAILU-ADMIN-FÖRE-KÖRNING" ]] && \
|
||
|
|
die "DKIM_PUBKEY är inte ifyllt — hämta från Mailu-admin och sätt variabeln"
|
||
|
|
}
|
||
|
|
|
||
|
|
# Skicka en Route53 change batch (JSON-sträng) och vänta tills den är INSYNC
|
||
|
|
apply_change_batch() {
|
||
|
|
local zone_id="$1"
|
||
|
|
local batch_json="$2"
|
||
|
|
local description="$3"
|
||
|
|
|
||
|
|
log "Applicerar ändring på zone $zone_id: $description"
|
||
|
|
local change_id
|
||
|
|
change_id=$(aws route53 change-resource-record-sets \
|
||
|
|
--hosted-zone-id "$zone_id" \
|
||
|
|
--change-batch "$batch_json" \
|
||
|
|
--query 'ChangeInfo.Id' --output text)
|
||
|
|
log "Change ID: $change_id — väntar på INSYNC..."
|
||
|
|
aws route53 wait resource-record-sets-changed --id "$change_id"
|
||
|
|
log "✅ $description — INSYNC"
|
||
|
|
}
|
||
|
|
|
||
|
|
# Bygg ett UPSERT-batch för SPF + DMARC (ingen MX = partnerdomän/avveckling)
|
||
|
|
build_no_mx_spf_dmarc_batch() {
|
||
|
|
local domain="$1"
|
||
|
|
jq -n \
|
||
|
|
--arg domain "$domain" \
|
||
|
|
--arg rua "$DMARC_RUA" \
|
||
|
|
'{
|
||
|
|
"Comment": ("SPF -all + DMARC reject for " + $domain + " (no MX)"),
|
||
|
|
"Changes": [
|
||
|
|
{
|
||
|
|
"Action": "UPSERT",
|
||
|
|
"ResourceRecordSet": {
|
||
|
|
"Name": ($domain + "."),
|
||
|
|
"Type": "TXT",
|
||
|
|
"TTL": 300,
|
||
|
|
"ResourceRecords": [{"Value": "\"v=spf1 -all\""}]
|
||
|
|
}
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"Action": "UPSERT",
|
||
|
|
"ResourceRecordSet": {
|
||
|
|
"Name": ("_dmarc." + $domain + "."),
|
||
|
|
"Type": "TXT",
|
||
|
|
"TTL": 300,
|
||
|
|
"ResourceRecords": [{"Value": ("\"v=DMARC1; p=reject; rua=mailto:" + $rua + "\"")}]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
]
|
||
|
|
}'
|
||
|
|
}
|
||
|
|
|
||
|
|
# Bygg ett UPSERT-batch för MX + SPF + DKIM + DMARC p=none (Nivå 2 EU-landsdomän)
|
||
|
|
build_eu_level2_batch() {
|
||
|
|
local domain="$1"
|
||
|
|
local dkim_selector="$2"
|
||
|
|
local dkim_pubkey="$3"
|
||
|
|
jq -n \
|
||
|
|
--arg domain "$domain" \
|
||
|
|
--arg mx "$MAILU_MX" \
|
||
|
|
--argjson prio "$MAILU_MX_PRIORITY" \
|
||
|
|
--arg sel "$dkim_selector" \
|
||
|
|
--arg key "$dkim_pubkey" \
|
||
|
|
--arg rua "$DMARC_RUA" \
|
||
|
|
'{
|
||
|
|
"Comment": ("EU Level-2 mail setup for " + $domain),
|
||
|
|
"Changes": [
|
||
|
|
{
|
||
|
|
"Action": "UPSERT",
|
||
|
|
"ResourceRecordSet": {
|
||
|
|
"Name": ($domain + "."),
|
||
|
|
"Type": "MX",
|
||
|
|
"TTL": 300,
|
||
|
|
"ResourceRecords": [{"Value": (($prio | tostring) + " " + $mx + ".")}]
|
||
|
|
}
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"Action": "UPSERT",
|
||
|
|
"ResourceRecordSet": {
|
||
|
|
"Name": ($domain + "."),
|
||
|
|
"Type": "TXT",
|
||
|
|
"TTL": 300,
|
||
|
|
"ResourceRecords": [{"Value": ("\"v=spf1 mx a:" + $mx + " ~all\"")}]
|
||
|
|
}
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"Action": "UPSERT",
|
||
|
|
"ResourceRecordSet": {
|
||
|
|
"Name": ($sel + "._domainkey." + $domain + "."),
|
||
|
|
"Type": "TXT",
|
||
|
|
"TTL": 300,
|
||
|
|
"ResourceRecords": [{"Value": ("\"v=DKIM1; k=rsa; p=" + $key + "\"")}]
|
||
|
|
}
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"Action": "UPSERT",
|
||
|
|
"ResourceRecordSet": {
|
||
|
|
"Name": ("_dmarc." + $domain + "."),
|
||
|
|
"Type": "TXT",
|
||
|
|
"TTL": 300,
|
||
|
|
"ResourceRecords": [{"Value": ("\"v=DMARC1; p=none; rua=mailto:" + $rua + "; fo=1\"")}]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
]
|
||
|
|
}'
|
||
|
|
}
|
||
|
|
|
||
|
|
# Bygg DELETE-batch för MX-post (kräver att befintlig MX-post anges exakt)
|
||
|
|
build_delete_mx_batch() {
|
||
|
|
local domain="$1"
|
||
|
|
local existing_mx_value="$2" # t.ex. "10 mail.wavult.com."
|
||
|
|
jq -n \
|
||
|
|
--arg domain "$domain" \
|
||
|
|
--arg mxval "$existing_mx_value" \
|
||
|
|
--arg rua "$DMARC_RUA" \
|
||
|
|
'{
|
||
|
|
"Comment": ("REMOVE MX + add SPF -all + DMARC reject for " + $domain),
|
||
|
|
"Changes": [
|
||
|
|
{
|
||
|
|
"Action": "DELETE",
|
||
|
|
"ResourceRecordSet": {
|
||
|
|
"Name": ($domain + "."),
|
||
|
|
"Type": "MX",
|
||
|
|
"TTL": 300,
|
||
|
|
"ResourceRecords": [{"Value": $mxval}]
|
||
|
|
}
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"Action": "UPSERT",
|
||
|
|
"ResourceRecordSet": {
|
||
|
|
"Name": ($domain + "."),
|
||
|
|
"Type": "TXT",
|
||
|
|
"TTL": 300,
|
||
|
|
"ResourceRecords": [{"Value": "\"v=spf1 -all\""}]
|
||
|
|
}
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"Action": "UPSERT",
|
||
|
|
"ResourceRecordSet": {
|
||
|
|
"Name": ("_dmarc." + $domain + "."),
|
||
|
|
"Type": "TXT",
|
||
|
|
"TTL": 300,
|
||
|
|
"ResourceRecords": [{"Value": ("\"v=DMARC1; p=reject; rua=mailto:" + $rua + "\"")}]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
]
|
||
|
|
}'
|
||
|
|
}
|
||
|
|
|
||
|
|
# ---------------------------------------------------------------------------
|
||
|
|
# FAS 1 — SÄKER: Partnerdomäner (Nivå 3) — ingen MX + DMARC reject
|
||
|
|
# ✅ SÄKER — Inga MX-ändringar, inga deletions. Lägger till SPF -all och DMARC reject.
|
||
|
|
# Dessa domäner har ALDRIG haft aktiv MX (bekräftat i C1).
|
||
|
|
# ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
fas1_partner_domains() {
|
||
|
|
log "=== FAS 1: Partnerdomäner — SPF -all + DMARC reject ==="
|
||
|
|
log "✅ SÄKER: Inga destruktiva ändringar, inga MX-poster påverkas"
|
||
|
|
|
||
|
|
# Landvex partner-domäner
|
||
|
|
declare -A PARTNER_ZONES=(
|
||
|
|
["landvex.mx"]="Z035940510YOVC15ORL0P"
|
||
|
|
["landvex.lat"]="Z0452682DQUO68GWV1LF"
|
||
|
|
["landvex.asia"]="Z07961892X5K5ZAGU9P7A"
|
||
|
|
["landvex.in"]="Z0114999EEC7DABEJF1R"
|
||
|
|
["landvex.jp"]="Z06417423FRM1NZ24HQIG"
|
||
|
|
# landvex.co och .tech — se FLAGGA nedan
|
||
|
|
# ["landvex.co"]="Z06390531NTPHQPHH556O" # FLAGGA: Klassificering oklar — Erik-beslut
|
||
|
|
# ["landvex.tech"]="Z03595122SFEDANKC6HLC" # FLAGGA: Klassificering oklar — Erik-beslut
|
||
|
|
|
||
|
|
# Quixzoom partner-domäner
|
||
|
|
["quixzoom.mx"]="Z03592031KP18830ISSFO"
|
||
|
|
["quixzoom.lat"]="Z07960653PSQVVJH6MWKQ"
|
||
|
|
["quixzoom.in"]="Z0116012C853QJX10S7F"
|
||
|
|
["quixzoom.jp"]="Z0691646HX1TESS78DHG"
|
||
|
|
["quixzoom.asia"]="Z03303363QVPGTSXJ4CBP"
|
||
|
|
# quixzoom.co och .tech — se FLAGGA nedan
|
||
|
|
# ["quixzoom.co"]="Z0640526G3V30S6YP4KW" # FLAGGA: Klassificering oklar — Erik-beslut
|
||
|
|
# ["quixzoom.tech"]="Z06392373COSIWKDCKU02" # FLAGGA: Klassificering oklar — Erik-beslut
|
||
|
|
)
|
||
|
|
|
||
|
|
for domain in "${!PARTNER_ZONES[@]}"; do
|
||
|
|
local zone_id="${PARTNER_ZONES[$domain]}"
|
||
|
|
local batch
|
||
|
|
batch=$(build_no_mx_spf_dmarc_batch "$domain")
|
||
|
|
apply_change_batch "$zone_id" "$batch" "Partnerdomän: $domain"
|
||
|
|
done
|
||
|
|
|
||
|
|
log "=== FAS 1 klar ==="
|
||
|
|
}
|
||
|
|
|
||
|
|
# ---------------------------------------------------------------------------
|
||
|
|
# FAS 2 — SÄKER: EU Nivå 2 (landsdomäner) — MX + SPF + DKIM + DMARC p=none
|
||
|
|
# ✅ SÄKER: Aktiverar mailmottagning (redirect → primär via Mailu catch-all).
|
||
|
|
# DMARC p=none = neutral, samlar rapporter utan att blockera.
|
||
|
|
# FÖRUTSÄTTNING: DKIM_PUBKEY måste vara ifyllt med rätt pubkey från Mailu.
|
||
|
|
# ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
fas2_eu_level2_domains() {
|
||
|
|
log "=== FAS 2: EU Nivå 2 (landsdomäner) — MX + SPF + DKIM + DMARC p=none ==="
|
||
|
|
log "✅ SÄKER: Aktiverar mailmottagning, DMARC p=none (neutral)"
|
||
|
|
log "⚠️ KRAV: DKIM_PUBKEY måste vara ifyllt — kontrollera variabeln"
|
||
|
|
|
||
|
|
# Landvex EU Nivå 2 (har idag ingen MX)
|
||
|
|
declare -A EU2_ZONES=(
|
||
|
|
["landvex.se"]="Z094509730UHHMSPHTT2N"
|
||
|
|
["landvex.eu"]="Z03535482N3R7HVG35RLC"
|
||
|
|
["landvex.de"]="Z09315373152U0OFU6S2J"
|
||
|
|
["landvex.fr"]="Z05975142FLYRUIUNWORN"
|
||
|
|
["landvex.nl"]="Z05939403BEIHDPX4CFG1"
|
||
|
|
["landvex.es"]="Z03544502KQL4NRCHY4AU"
|
||
|
|
["landvex.it"]="Z06922101ZAJMZJR3ZLJI"
|
||
|
|
["landvex.fi"]="Z0646854PR1U78P1AJS0"
|
||
|
|
["landvex.co.uk"]="Z06400622V84QB2SX67DT"
|
||
|
|
["landvex.be"]="Z0642291ACB2WGN8KIPV"
|
||
|
|
["landvex.dk"]="Z03432136USE09VHQGF9"
|
||
|
|
["landvex.net"]="Z06400772MZF13N0GK4HC"
|
||
|
|
["landvex.org"]="Z03600372562O0AYE74AX"
|
||
|
|
["landvex-gov.eu"]="Z03523293HYR2LY9FB8V3"
|
||
|
|
["landvex-intelligence.eu"]="Z04528812DA06BOTM71QN"
|
||
|
|
["landvex-infrastructure.eu"]="Z06915882XI394D5IHZZ0"
|
||
|
|
["landvex-systems.com"]="Z03530401ABRF61KQ4UGD"
|
||
|
|
["landvex-secure.com"]="Z06922211OLB8FWUK47U5"
|
||
|
|
|
||
|
|
# Quixzoom EU Nivå 2
|
||
|
|
["quixzoom.eu"]="Z0352300H6B69TZB3FB"
|
||
|
|
["quixzoom.nl"]="Z08393705I60UK5C6T3D"
|
||
|
|
["quixzoom.de"]="Z02042236EVWD34RIL8"
|
||
|
|
["quixzoom.fr"]="Z0590787VZDW5D5XHQAV"
|
||
|
|
["quixzoom.es"]="Z03537332N1170YZA6Y96"
|
||
|
|
["quixzoom.it"]="Z01178703OEP5JBWVMCVN"
|
||
|
|
["quixzoom.fi"]="Z01156752B3NC8OLB0VQ3"
|
||
|
|
["quixzoom.ch"]="Z05948653CJP2I13KROV9"
|
||
|
|
["quixzoom.be"]="Z02403741O3VID61R4CWK"
|
||
|
|
["quixzoom.dk"]="Z03418436Y57RW7ET3I2"
|
||
|
|
["quixzoom.net"]="Z064013718W8XEVMMH8I3"
|
||
|
|
["quixzoom.org"]="Z0646024GDLOX0L6C9L0"
|
||
|
|
|
||
|
|
# Wavult
|
||
|
|
["wavult.se"]="Z09416523AFDKP6WRCHRL"
|
||
|
|
|
||
|
|
# landvex.ch — använd en av de två zones (se duplikat-hantering fas5)
|
||
|
|
# FLAGGA: Verifiera vilken zone som är aktiv (NS-delegation) INNAN körning
|
||
|
|
# ["landvex.ch"]="Z060292760R1ZY0PQH3V" # Duplikat — verifiera FÖR körning
|
||
|
|
# quixzoom.co.uk — samma
|
||
|
|
# ["quixzoom.co.uk"]="Z035263432T28MD24XUIG" # Duplikat — verifiera FÖR körning
|
||
|
|
)
|
||
|
|
|
||
|
|
for domain in "${!EU2_ZONES[@]}"; do
|
||
|
|
local zone_id="${EU2_ZONES[$domain]}"
|
||
|
|
local batch
|
||
|
|
batch=$(build_eu_level2_batch "$domain" "$DKIM_SELECTOR" "$DKIM_PUBKEY")
|
||
|
|
apply_change_batch "$zone_id" "$batch" "EU Nivå 2: $domain"
|
||
|
|
done
|
||
|
|
|
||
|
|
log "=== FAS 2 klar ==="
|
||
|
|
}
|
||
|
|
|
||
|
|
# ---------------------------------------------------------------------------
|
||
|
|
# FAS 3 — SÄKER: pixdrift.com — SPF -all + DMARC reject (har ingen MX)
|
||
|
|
# ✅ SÄKER: Pixdrift är dödd, har aldrig haft MX (bekräftat C1). Inga deletions.
|
||
|
|
# ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
fas3_pixdrift() {
|
||
|
|
log "=== FAS 3: pixdrift.com — SPF -all + DMARC reject ==="
|
||
|
|
log "✅ SÄKER: Pixdrift har ingen MX — lägger bara till SPF och DMARC"
|
||
|
|
|
||
|
|
local zone_id="Z06885272CR0QX3ZHVMTK"
|
||
|
|
local batch
|
||
|
|
batch=$(build_no_mx_spf_dmarc_batch "pixdrift.com")
|
||
|
|
apply_change_batch "$zone_id" "$batch" "Avveckling: pixdrift.com"
|
||
|
|
|
||
|
|
log "=== FAS 3 klar ==="
|
||
|
|
}
|
||
|
|
|
||
|
|
# ---------------------------------------------------------------------------
|
||
|
|
# FAS 4A — ⚠️ ERIK-OK: Verifiera Mailu-loggar FÖR körning
|
||
|
|
# Kontrollera att ingen aktiv korrespondens finns på corpfitt/hypbit
|
||
|
|
# ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
fas4a_verify_dead_domains_mail() {
|
||
|
|
log "=== FAS 4A: Verifiering av aktiv korrespondens (READ-ONLY) ==="
|
||
|
|
log "⚠️ KRÄVER MANUELL KONTROLL: Logga in i Mailu-admin (mail.wavult.com)"
|
||
|
|
log ""
|
||
|
|
log "Kontrollera följande INNAN fas4b körs:"
|
||
|
|
log " 1. Gå till Mailu-admin → Domains → corpfitt.com"
|
||
|
|
log " - Finns det konton/alias? → Ta bort dem i Mailu FÖR att ta bort MX i DNS"
|
||
|
|
log " - Kolla mailloggar (Mailu → Logs) efter inkommande mail senaste 90 dagar"
|
||
|
|
log " 2. Upprepa för hypbit.com"
|
||
|
|
log " 3. Om inga konton/alias + ingen trafik → godkänn fas4b med Erik"
|
||
|
|
log ""
|
||
|
|
log "Alternativt via AWS SSM (root-access):"
|
||
|
|
log " sudo grep -E '(corpfitt|hypbit)' /var/log/mail.log | tail -50"
|
||
|
|
log " (Anpassa loggväg efter Mailu-installationens konfiguration)"
|
||
|
|
log ""
|
||
|
|
log "=== FAS 4A klar — KÖR INTE FAS 4B UTAN ERIKS GODKÄNNANDE ==="
|
||
|
|
}
|
||
|
|
|
||
|
|
# ---------------------------------------------------------------------------
|
||
|
|
# FAS 4B — ⚠️ ERIK-OK: corpfitt.com + hypbit.com — Ta bort MX + DMARC reject
|
||
|
|
# KRÄVER: Fas 4A genomförd + Eriks godkännande + verifiering att inga konton finns
|
||
|
|
# ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
fas4b_decommission_dead_mx() {
|
||
|
|
log "=== FAS 4B: Avveckling MX för döda domäner ==="
|
||
|
|
log "⚠️ KRÄVER ERIKS GODKÄNNANDE"
|
||
|
|
log "⚠️ KRÄVER: Fas 4A genomförd (Mailu-loggar kontrollerade)"
|
||
|
|
log ""
|
||
|
|
|
||
|
|
# Bekräftelseprompt — kräver explicit input
|
||
|
|
read -r -p "Har Erik godkänt denna åtgärd? Har fas4a genomförts? (skriv 'JA ERIK HAR GODKÄNT' för att fortsätta): " confirm
|
||
|
|
if [[ "$confirm" != "JA ERIK HAR GODKÄNT" ]]; then
|
||
|
|
log "Avbröt — inget ändrat."
|
||
|
|
exit 0
|
||
|
|
fi
|
||
|
|
|
||
|
|
# corpfitt.com
|
||
|
|
log "Tar bort MX på corpfitt.com (Zone: Z03586003P55GRO87T6M3)"
|
||
|
|
local corpfitt_batch
|
||
|
|
corpfitt_batch=$(build_delete_mx_batch "corpfitt.com" "10 mail.wavult.com.")
|
||
|
|
apply_change_batch "Z03586003P55GRO87T6M3" "$corpfitt_batch" "Avveckling MX: corpfitt.com"
|
||
|
|
|
||
|
|
# hypbit.com
|
||
|
|
log "Tar bort MX på hypbit.com (Zone: Z06944491GZGBRKHYNYSK)"
|
||
|
|
local hypbit_batch
|
||
|
|
hypbit_batch=$(build_delete_mx_batch "hypbit.com" "10 mail.wavult.com.")
|
||
|
|
apply_change_batch "Z06944491GZGBRKHYNYSK" "$hypbit_batch" "Avveckling MX: hypbit.com"
|
||
|
|
|
||
|
|
log "=== FAS 4B klar ==="
|
||
|
|
}
|
||
|
|
|
||
|
|
# ---------------------------------------------------------------------------
|
||
|
|
# FAS 5 — ⚠️ ERIK-OK: Duplikatzon-konsolidering
|
||
|
|
# KRÄVER: NS-verifiering mot registraren, Eriks godkännande, manuell record-jämförelse
|
||
|
|
# ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
fas5_inspect_duplicate_zones() {
|
||
|
|
log "=== FAS 5: Duplikatzoner — Inspektion (READ-ONLY) ==="
|
||
|
|
log "✅ SÄKER: Denna funktion listar records och NS — inga ändringar"
|
||
|
|
|
||
|
|
declare -A DUP_ZONES=(
|
||
|
|
["landvex.ch"]="Z060292760R1ZY0PQH3V Z00716462L6XS9QZX1HHS"
|
||
|
|
["quixzoom.co.uk"]="Z035263432T28MD24XUIG Z06391212CQRAVQ1FMNHV"
|
||
|
|
["aamos.ai"]="Z0992155TZTAVTB9DKU Z10021043NUR85M9WUOQM"
|
||
|
|
["wavult.com"]="Z03332778KCOYPM9OBKP Z02946821NE63OOAKDTKA"
|
||
|
|
)
|
||
|
|
|
||
|
|
for domain in "${!DUP_ZONES[@]}"; do
|
||
|
|
IFS=' ' read -r zone_a zone_b <<< "${DUP_ZONES[$domain]}"
|
||
|
|
log ""
|
||
|
|
log "--- $domain ---"
|
||
|
|
log "Zone A ($zone_a) records:"
|
||
|
|
aws route53 list-resource-record-sets --hosted-zone-id "$zone_a" \
|
||
|
|
--query 'ResourceRecordSets[*].{Type:Type,Name:Name,Records:ResourceRecords}' \
|
||
|
|
--output table 2>/dev/null || warn "Kunde inte lista records för $zone_a"
|
||
|
|
|
||
|
|
log "Zone B ($zone_b) records:"
|
||
|
|
aws route53 list-resource-record-sets --hosted-zone-id "$zone_b" \
|
||
|
|
--query 'ResourceRecordSets[*].{Type:Type,Name:Name,Records:ResourceRecords}' \
|
||
|
|
--output table 2>/dev/null || warn "Kunde inte lista records för $zone_b"
|
||
|
|
|
||
|
|
log "Extern NS-kontroll för $domain:"
|
||
|
|
dig NS "$domain" +short || warn "dig-fel för $domain"
|
||
|
|
done
|
||
|
|
|
||
|
|
log ""
|
||
|
|
log "=== FAS 5 INSPEKTION KLAR ==="
|
||
|
|
log "Jämför NS-poster ovan mot registrarens delegering."
|
||
|
|
log "Zone vars NS matchar registraren = aktiv. Den andra = phantom (kan raderas)."
|
||
|
|
log "RADERA INTE zones via detta skript — gör det via AWS-konsolen efter manuell verifiering."
|
||
|
|
}
|
||
|
|
|
||
|
|
# ---------------------------------------------------------------------------
|
||
|
|
# FAS 6 — ⚠️ DMARC-skärpning (aktiva maildomäner)
|
||
|
|
# KRÄVER: 2 veckor DMARC-rapportanalys + DKIM-verifiering
|
||
|
|
# ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
fas6_dmarc_tighten_active_domains() {
|
||
|
|
log "=== FAS 6: DMARC-skärpning på aktiva maildomäner ==="
|
||
|
|
log "⚠️ KRÄVER: DKIM verifierat i praktiken (mail-tester.com)"
|
||
|
|
log "⚠️ KRÄVER: 2 veckors DMARC-rapportanalys utan avvikelser"
|
||
|
|
log "⚠️ KRÄVER: Eriks godkännande"
|
||
|
|
log ""
|
||
|
|
|
||
|
|
# Dessa domäner har AKTIV MX och roll-routing — skärpning kräver extra försiktighet
|
||
|
|
local ACTIVE_MAIL_DOMAINS=(
|
||
|
|
"aamos.systems:Z03908683A2AIMRZR85BQ"
|
||
|
|
"landvex.com:Z05966261S4KJRAGIJ6FQ"
|
||
|
|
"quixzoom.com:Z04915922EJ53UAUIOPFG"
|
||
|
|
# wavult.com och aamos.ai har duplikatzoner — lös fas5 först
|
||
|
|
# "wavult.com:Z03332778KCOYPM9OBKP"
|
||
|
|
# "aamos.ai:Z0992155TZTAVTB9DKU"
|
||
|
|
)
|
||
|
|
|
||
|
|
read -r -p "Har DKIM verifierats? Har DMARC-rapporter analyserats 2 veckor? Eriks godkännande? (skriv 'JA VERIFIERAT' för att fortsätta): " confirm
|
||
|
|
if [[ "$confirm" != "JA VERIFIERAT" ]]; then
|
||
|
|
log "Avbröt — inget ändrat."
|
||
|
|
exit 0
|
||
|
|
fi
|
||
|
|
|
||
|
|
read -r -p "Välj DMARC-policy [quarantine/reject]: " policy
|
||
|
|
if [[ "$policy" != "quarantine" && "$policy" != "reject" ]]; then
|
||
|
|
die "Ogiltig policy: $policy. Välj 'quarantine' eller 'reject'."
|
||
|
|
fi
|
||
|
|
|
||
|
|
read -r -p "Välj pct (1-100, t.ex. 10 för gradvis utrullning): " pct
|
||
|
|
if ! [[ "$pct" =~ ^[0-9]+$ ]] || (( pct < 1 || pct > 100 )); then
|
||
|
|
die "Ogiltigt pct-värde: $pct"
|
||
|
|
fi
|
||
|
|
|
||
|
|
for entry in "${ACTIVE_MAIL_DOMAINS[@]}"; do
|
||
|
|
IFS=':' read -r domain zone_id <<< "$entry"
|
||
|
|
log "Skärper DMARC på $domain → p=$policy; pct=$pct"
|
||
|
|
local batch
|
||
|
|
batch=$(jq -n \
|
||
|
|
--arg domain "$domain" \
|
||
|
|
--arg policy "$policy" \
|
||
|
|
--argjson pct "$pct" \
|
||
|
|
--arg rua "$DMARC_RUA" \
|
||
|
|
'{
|
||
|
|
"Comment": ("DMARC tighten to " + $policy + " pct=" + ($pct|tostring) + " for " + $domain),
|
||
|
|
"Changes": [{
|
||
|
|
"Action": "UPSERT",
|
||
|
|
"ResourceRecordSet": {
|
||
|
|
"Name": ("_dmarc." + $domain + "."),
|
||
|
|
"Type": "TXT",
|
||
|
|
"TTL": 300,
|
||
|
|
"ResourceRecords": [{"Value": ("\"v=DMARC1; p=" + $policy + "; pct=" + ($pct|tostring) + "; rua=mailto:" + $rua + "; fo=1\"")}]
|
||
|
|
}
|
||
|
|
}]
|
||
|
|
}')
|
||
|
|
apply_change_batch "$zone_id" "$batch" "DMARC skärpning: $domain → p=$policy pct=$pct"
|
||
|
|
done
|
||
|
|
|
||
|
|
log "=== FAS 6 klar ==="
|
||
|
|
}
|
||
|
|
|
||
|
|
# ---------------------------------------------------------------------------
|
||
|
|
# Hjälptext och dispatcher
|
||
|
|
# ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
usage() {
|
||
|
|
cat <<EOF
|
||
|
|
Wavult Group DNS Implementation Script (BUILD-C3)
|
||
|
|
Förberett: 2026-06-06 | Status: FÖRBERET — EJ KÖRT
|
||
|
|
|
||
|
|
SÄKERHETSKLASSER:
|
||
|
|
✅ SÄKER fas1, fas2, fas3, fas5 (inspect) — inga destruktiva ändringar
|
||
|
|
⚠️ ERIK-OK fas4b, fas6 — kräver Eriks godkännande + bekräftelseprompt
|
||
|
|
🔴 DESTRUKTIV Zonradering görs INTE via detta skript — manuellt via AWS-konsolen
|
||
|
|
|
||
|
|
ANVÄNDNING:
|
||
|
|
bash c3-apply-dns.sh <fas> [--dry-run]
|
||
|
|
|
||
|
|
FASER:
|
||
|
|
fas1 ✅ Partnerdomäner: SPF -all + DMARC reject (10 domäner)
|
||
|
|
fas2 ✅ EU Nivå 2: MX + SPF + DKIM + DMARC p=none (30 domäner)
|
||
|
|
OBS: Fyll i DKIM_PUBKEY-variabeln före körning!
|
||
|
|
fas3 ✅ pixdrift.com: SPF -all + DMARC reject
|
||
|
|
fas4a ✅ Inspektion/verifiering av döda domäner (read-only)
|
||
|
|
fas4b ⚠️ corpfitt+hypbit MX-borttagning (kräver Erik-OK + prompt)
|
||
|
|
fas5 ✅ Duplikatzon-inspektion (read-only)
|
||
|
|
fas6 ⚠️ DMARC-skärpning aktiva maildomäner (kräver Erik-OK + prompt)
|
||
|
|
|
||
|
|
FÖRE KÖRNING:
|
||
|
|
1. Sätt DKIM_PUBKEY i skript-toppen (hämta från Mailu Admin → Domains → DKIM)
|
||
|
|
2. Bekräfta att AWS-profil är korrekt: aws sts get-caller-identity
|
||
|
|
3. Fas2 och fas4b: Verifiera att catch-all konfigurerats i Mailu FÖR DNS-ändring
|
||
|
|
|
||
|
|
EXEMPEL:
|
||
|
|
# Kontrollera AWS-åtkomst
|
||
|
|
aws sts get-caller-identity
|
||
|
|
|
||
|
|
# Kör fas1 (partnerdomäner, säker)
|
||
|
|
bash c3-apply-dns.sh fas1
|
||
|
|
|
||
|
|
# Inspektera duplikatzoner (read-only)
|
||
|
|
bash c3-apply-dns.sh fas5
|
||
|
|
|
||
|
|
# Kör fas4a (verifiera döda domäner, read-only)
|
||
|
|
bash c3-apply-dns.sh fas4a
|
||
|
|
EOF
|
||
|
|
}
|
||
|
|
|
||
|
|
main() {
|
||
|
|
local cmd="${1:-help}"
|
||
|
|
shift || true
|
||
|
|
|
||
|
|
case "$cmd" in
|
||
|
|
fas1) check_deps; fas1_partner_domains ;;
|
||
|
|
fas2) check_deps; fas2_eu_level2_domains ;;
|
||
|
|
fas3) check_deps; fas3_pixdrift ;;
|
||
|
|
fas4a) fas4a_verify_dead_domains_mail ;;
|
||
|
|
fas4b) check_deps; fas4b_decommission_dead_mx ;;
|
||
|
|
fas5) fas5_inspect_duplicate_zones ;;
|
||
|
|
fas6) check_deps; fas6_dmarc_tighten_active_domains ;;
|
||
|
|
help|--help|-h) usage ;;
|
||
|
|
*)
|
||
|
|
echo "Okänt kommando: $cmd"
|
||
|
|
usage
|
||
|
|
exit 1
|
||
|
|
;;
|
||
|
|
esac
|
||
|
|
}
|
||
|
|
|
||
|
|
main "$@"
|