#!/usr/bin/env bash
###############################################################################
# firewall_vault.sh — Vault (869377) · 10.69.69.8
# 2 WG tunnels (compass hub) · firewalld · default zone: drop
# Idempotent — safe to re-run at any time.
###############################################################################
set -euo pipefail

readonly SCRIPT_NAME="firewall_vault"
readonly LOG_TAG="[${SCRIPT_NAME}]"
readonly TIMESTAMP_FMT="+%Y-%m-%dT%H:%M:%S%z"

# ── Logging helpers ─────────────────────────────────────────────────────────
log()  { printf '%s %s %s\n' "$(date "${TIMESTAMP_FMT}")" "${LOG_TAG}" "$*"; }
warn() { log "WARN: $*" >&2; }
die()  { log "FATAL: $*" >&2; exit 1; }
trap 'die "Unexpected error on line ${LINENO}"' ERR

# ── Pre-flight checks ──────────────────────────────────────────────────────
[[ $(id -u) -eq 0 ]] || die "Must run as root"
command -v firewall-cmd >/dev/null 2>&1 || die "firewalld not installed"
systemctl is-active --quiet firewalld    || die "firewalld is not running"

log "Starting Vault firewall configuration"

# ── Helpers ─────────────────────────────────────────────────────────────────
ensure_zone() {
    local zone="$1"
    if ! firewall-cmd --permanent --get-zones | grep -qw "${zone}"; then
        log "Creating zone: ${zone}"
        firewall-cmd --permanent --new-zone="${zone}"
    else
        log "Zone already exists: ${zone}"
    fi
}

ensure_port() {
    local zone="$1" port="$2"
    if ! firewall-cmd --permanent --zone="${zone}" --query-port="${port}" 2>/dev/null; then
        log "Adding port ${port} to zone ${zone}"
        firewall-cmd --permanent --zone="${zone}" --add-port="${port}"
    else
        log "Port ${port} already in zone ${zone}"
    fi
}

ensure_source() {
    local zone="$1" src="$2"
    if ! firewall-cmd --permanent --zone="${zone}" --query-source="${src}" 2>/dev/null; then
        log "Adding source ${src} to zone ${zone}"
        firewall-cmd --permanent --zone="${zone}" --add-source="${src}"
    else
        log "Source ${src} already in zone ${zone}"
    fi
}

ensure_interface() {
    local zone="$1" iface="$2"
    if ! firewall-cmd --permanent --zone="${zone}" --query-interface="${iface}" 2>/dev/null; then
        log "Binding interface ${iface} to zone ${zone}"
        firewall-cmd --permanent --zone="${zone}" --add-interface="${iface}"
    else
        log "Interface ${iface} already bound to zone ${zone}"
    fi
}

###############################################################################
# 1. Default zone → drop
###############################################################################
log "Setting default zone to drop"
firewall-cmd --set-default-zone=drop

###############################################################################
# 2. zone-mesh (wg7) — WireGuard mesh: SSH 9922/tcp
###############################################################################
ensure_zone "zone-mesh"
ensure_interface "zone-mesh" "wg7"
ensure_source "zone-mesh" "10.69.69.0/24"
ensure_port "zone-mesh" "9922/tcp"

###############################################################################
# 3. Verify SSH 9922 is accessible in zone-mesh
###############################################################################
log "Verifying SSH port 9922/tcp in zone-mesh"
if firewall-cmd --permanent --zone=zone-mesh --query-port=9922/tcp 2>/dev/null; then
    log "✓ SSH 9922/tcp confirmed in zone-mesh"
else
    die "SSH 9922/tcp NOT found in zone-mesh after configuration — aborting"
fi

###############################################################################
# 4. zone-comp (compass) — compass hub: keyserv 8302/tcp
###############################################################################
ensure_zone "zone-comp"
ensure_interface "zone-comp" "compass"
ensure_source "zone-comp" "172.28.47.0/28"
ensure_port "zone-comp" "8302/tcp"

###############################################################################
# 5. Public zone — UDP tunnel establishment only
###############################################################################
log "Configuring public zone UDP ports for tunnel establishment"
VAULT_PUBLIC_UDP=(41652 42783)
for port in "${VAULT_PUBLIC_UDP[@]}"; do
    ensure_port "public" "${port}/udp"
done

###############################################################################
# 6. Reload & verify
###############################################################################
log "Reloading firewalld"
firewall-cmd --reload

log "═══════════════════════════════════════════════════════════"
log "Vault firewall configuration complete"
log "Default zone: $(firewall-cmd --get-default-zone)"
log "═══════════════════════════════════════════════════════════"

log "Active zones:"
firewall-cmd --get-active-zones

for z in zone-mesh zone-comp public; do
    log "--- Zone: ${z} ---"
    firewall-cmd --zone="${z}" --list-all 2>/dev/null || log "  (zone ${z} not active)"
done

# ── Final SSH verification (runtime, not just permanent) ────────────────────
log "Runtime verification: checking zone-mesh for 9922/tcp"
if firewall-cmd --zone=zone-mesh --query-port=9922/tcp 2>/dev/null; then
    log "✓ Runtime: SSH 9922/tcp ACTIVE in zone-mesh"
else
    warn "Runtime: SSH 9922/tcp not yet active in zone-mesh (may need interface up)"
fi

log "Vault firewall script finished successfully"
