#!/usr/bin/env bash
#
# onx-firewall-init — ONOX firewall infra hazırlığı.
#
# Idempotent. Birden fazla kez çağrılabilir.
#
# Yaptığı işler:
#   1. /var/lib/onox/ dizin yapısı
#   2. nftables `inet onox` tablosu ve gerekli chain'ler
#   3. Bağımlılık kontrolü (nft, jq, curl, dig, whois, grepcidr, sqlite3, fail2ban-client)
#   4. fail2ban filter.d/ içine panel-login + panel-api filter'larını yazar
#
# Input (stdin JSON, opsiyonel):
#   {"check_only": false}
#
# Output (stdout JSON):
#   {"ok": true, "table_created": bool, "missing_deps": [...], "warnings": [...]}
#
# Exit codes:
#   0 = ok
#   1 = bad input
#   2 = missing critical dependency
#   3 = nft komutu hatası
#   4 = permission denied (root değil)

set -euo pipefail

readonly LIB_DIR="/var/lib/onox"
readonly GEOIP_DIR="$LIB_DIR/geoip"
readonly THREAT_CACHE="/var/cache/onox/threats"
readonly LOG_TAG="onox-firewall-init"

# ── Root check ────────────────────────────────────────────────────────────
if [[ $EUID -ne 0 ]]; then
    jq -nc '{ok:false,error:"must run as root"}'
    exit 4
fi

# ── Stdin parse (opsiyonel) ───────────────────────────────────────────────
check_only=false
if [[ -t 0 ]]; then
    input=""
else
    input="$(cat)"
fi
if [[ -n "$input" ]]; then
    check_only="$(echo "$input" | jq -r '.check_only // false')"
fi

# ── Dependency check ──────────────────────────────────────────────────────
declare -a missing=()
declare -a warnings=()

for cmd in nft jq curl; do
    command -v "$cmd" &>/dev/null || missing+=("$cmd")
done

# Soft deps — yoksa uyar ama devam et
for cmd in dig whois grepcidr sqlite3 fail2ban-client logger systemctl; do
    if ! command -v "$cmd" &>/dev/null; then
        warnings+=("missing soft dep: $cmd")
    fi
done

if [[ ${#missing[@]} -gt 0 ]]; then
    jq -nc --argjson missing "$(printf '%s\n' "${missing[@]}" | jq -R . | jq -s .)" \
        '{ok:false,error:"missing critical deps",missing_deps:$missing}'
    exit 2
fi

if [[ "$check_only" == "true" ]]; then
    jq -nc \
        --argjson warnings "$(printf '%s\n' "${warnings[@]:-}" | jq -R . | jq -s '[.[] | select(. != "")]')" \
        '{ok:true,check_only:true,warnings:$warnings}'
    exit 0
fi

# ── /var/lib/onox/ hierarchy ──────────────────────────────────────────────
mkdir -p "$LIB_DIR"
mkdir -p "$GEOIP_DIR/cidrs"
mkdir -p "$THREAT_CACHE"
mkdir -p "$LIB_DIR/lock"

chmod 0750 "$LIB_DIR"
chmod 0755 "$GEOIP_DIR" "$GEOIP_DIR/cidrs"
chmod 0700 "$LIB_DIR/lock"

# ── nftables base table ───────────────────────────────────────────────────
table_created=false
if ! nft list table inet onox &>/dev/null; then
    nft add table inet onox
    table_created=true
fi

# Sets — IPv4 trusted, IPv6 trusted (sıralama önemli, "auto-merge" tek aralık opsiyonu)
nft list set inet onox onox-trusted &>/dev/null || \
    nft 'add set inet onox onox-trusted { type ipv4_addr; flags interval; auto-merge; }'

nft list set inet onox onox-trusted-v6 &>/dev/null || \
    nft 'add set inet onox onox-trusted-v6 { type ipv6_addr; flags interval; auto-merge; }'

# Chains — priorities açık şekilde sıralı:
#  -100: trusted_in  (whitelist  — diğer tüm kontrollerden önce accept eder)
#   -50: threat_in   (threat lists — public blocklist drop)
#   -10: country_in  (country blocks — onx-block-XX set'leri)
#    -5: ratelimit   (SYN flood, conn limit, per-service rate)
nft list chain inet onox trusted_in &>/dev/null || {
    nft 'add chain inet onox trusted_in { type filter hook input priority -100; }'
    nft "add rule inet onox trusted_in ip saddr @onox-trusted counter accept comment \"onox-trusted-v4\""
    nft "add rule inet onox trusted_in ip6 saddr @onox-trusted-v6 counter accept comment \"onox-trusted-v6\""
}

nft list chain inet onox threat_in &>/dev/null || \
    nft 'add chain inet onox threat_in { type filter hook input priority -50; }'

nft list chain inet onox country_in &>/dev/null || \
    nft 'add chain inet onox country_in { type filter hook input priority -10; }'

nft list chain inet onox ratelimit &>/dev/null || \
    nft 'add chain inet onox ratelimit { type filter hook input priority -5; }'

# ── fail2ban panel filters ────────────────────────────────────────────────
if command -v fail2ban-client &>/dev/null; then
    if [[ ! -f /etc/fail2ban/filter.d/panel-login.conf ]]; then
        cat > /etc/fail2ban/filter.d/panel-login.conf <<'EOF'
# ONOX Panel login brute-force filter
# Triggers on Laravel auth.failed log lines:
#   [2026-05-16 10:23:45] production.WARNING: auth.failed {"ip":"1.2.3.4","email":"x@y"}
[Definition]
failregex = ^.*auth\.failed.*"ip":"<HOST>".*$
ignoreregex =
EOF
    fi

    if [[ ! -f /etc/fail2ban/filter.d/panel-api.conf ]]; then
        cat > /etc/fail2ban/filter.d/panel-api.conf <<'EOF'
# ONOX Panel API abuse filter
# Triggers on 401/403/429 responses from /api/* paths
[Definition]
failregex = ^.* "(GET|POST|PUT|PATCH|DELETE) /api/[^"]*" (401|403|429) .* "[^"]*" "<HOST>".*$
            ^.*api\.(unauthorized|rate_limited|forbidden).*"ip":"<HOST>".*$
ignoreregex =
EOF
    fi
fi

# ── Persistence: nft ruleset'i /etc/sysconfig/nftables.conf'a yedekle ─────
# Sistem yeniden başladığında onox tablomuz kaybolmasın.
if [[ -d /etc/sysconfig ]]; then
    nft list table inet onox > /etc/sysconfig/nftables-onox.conf 2>/dev/null || true
fi

logger -t "$LOG_TAG" "Firewall init complete (table_created=$table_created, warnings=${#warnings[@]})"

jq -nc \
    --argjson table_created "$table_created" \
    --argjson warnings "$(printf '%s\n' "${warnings[@]:-}" | jq -R . | jq -s '[.[] | select(. != "")]')" \
    '{ok:true,table_created:$table_created,warnings:$warnings}'
