#!/bin/sh
set -eu

# ALLOWLIST, GOST_PORT, and GOST_MARK must stay in sync with the literals in
# gost.yaml (file.path, addr, so_mark), which cannot read environment variables.
ALLOWLIST=/opt/egress-sidecar/allowlist.txt
GOST_PORT=12345
GOST_MARK=114514
NFTABLES_RULESET_NAME=gost_egress
# Must exceed the allowlist `reload: 1s` interval in gost.yaml so gost has
# picked up the new allowlist before nftables enforcement turns on.
RELOAD_WAIT=2

usage() {
  cat <<'EOF'
Usage:
  network-policy show
  network-policy allow-all
  network-policy deny-all
  network-policy allow [TARGET...]
  network-policy rules

TARGET can be a domain, wildcard domain, IP, CIDR, or IP range. With no targets,
allow mode denies all controlled TCP egress.
EOF
}

ensure_file() {
  mkdir -p "$(dirname "$ALLOWLIST")"
  touch "$ALLOWLIST"
}

setup_nftables() {
  nft --file - <<EOF
add table inet $NFTABLES_RULESET_NAME
flush table inet $NFTABLES_RULESET_NAME

table inet $NFTABLES_RULESET_NAME {
  chain output {
    type nat hook output priority dstnat; policy accept;

    meta mark $GOST_MARK return
    fib daddr type local return
    meta l4proto tcp redirect to :$GOST_PORT
  }

  chain egress {
    type filter hook output priority filter; policy accept;

    meta mark $GOST_MARK accept
    fib daddr type local accept
    ip protocol icmp accept
    ip6 nexthdr icmpv6 accept
    meta l4proto != tcp reject
  }
}
EOF
}

remove_nftables() {
  nft delete table inet "$NFTABLES_RULESET_NAME" 2>/dev/null || true
}

nftables_enabled() {
  nft list table inet "$NFTABLES_RULESET_NAME" >/dev/null 2>&1
}

show() {
  if nftables_enabled; then
    echo "mode: controlled egress"
  else
    echo "mode: allow-all (transparent proxy disabled)"
  fi
  echo "allowlist: $ALLOWLIST"
  if [ ! -s "$ALLOWLIST" ]; then
    if nftables_enabled; then
      echo "(empty: deny all controlled TCP egress)"
    else
      echo "(empty)"
    fi
    return
  fi
  nl --body-numbering=a "$ALLOWLIST"
}

wait_reload() {
  sleep "$RELOAD_WAIT"
}

ensure_file

cmd="${1:-}"
case "$cmd" in
  show)
    show
    ;;
  allow-all)
    remove_nftables
    : > "$ALLOWLIST"
    echo "ok: allow all egress"
    ;;
  deny-all)
    : > "$ALLOWLIST"
    wait_reload
    setup_nftables
    echo "ok: deny all controlled TCP egress"
    ;;
  allow)
    shift
    : > "$ALLOWLIST"
    for item in "$@"; do
      if [ -n "$item" ]; then
        printf '%s\n' "$item" >> "$ALLOWLIST"
      fi
    done
    wait_reload
    setup_nftables
    echo "ok: allowlist applied"
    show
    ;;
  rules)
    if nftables_enabled; then
      nft list table inet "$NFTABLES_RULESET_NAME"
    else
      echo "mode: allow-all"
    fi
    ;;
  *)
    usage
    exit 2
    ;;
esac
