#!/usr/bin/env bash
# MonsterOps — guarded nftables apply. The single sudo entry point the Firewall
# Manager uses to load a ruleset, replacing a raw `nft -f -` sudo grant.
#
# `nft -f -` reads the ruleset from stdin, which sudoers cannot inspect, so a
# passwordless grant for it lets a compromised service user load ANY ruleset
# (flush the whole firewall, add a NAT/redirect, touch operator tables). This
# wrapper reads the ruleset once, runs it through mo-nft-guard.awk — which passes
# only a ruleset scoped to `table inet monsterops` — and applies the SAME bytes
# (no re-read, so nothing can change between check and apply).
#
# Usage (via sudo, from the app):  mo-nft-apply [--check]   ruleset on stdin
#   --check   validate with `nft -c -f -` (dry run) instead of applying.
set -euo pipefail

GUARD="/usr/local/sbin/mo-nft-guard.awk"

check=0
case "${1-}" in
  --check) check=1; shift ;;
  "") ;;
  *) echo "mo-nft-apply: unexpected argument: $1" >&2; exit 64 ;;
esac
[ "$#" -eq 0 ] || { echo "mo-nft-apply: too many arguments" >&2; exit 64; }

NFT="$(command -v nft 2>/dev/null || true)"; : "${NFT:=/usr/sbin/nft}"
[ -x "$NFT" ] || { echo "mo-nft-apply: nft not found" >&2; exit 69; }
[ -r "$GUARD" ] || { echo "mo-nft-apply: guard missing: $GUARD" >&2; exit 69; }

ruleset="$(cat)"   # read stdin once; validate and apply the exact same bytes

if ! reason="$(printf '%s' "$ruleset" | awk -f "$GUARD")"; then
  echo "mo-nft-apply: refused ruleset — ${reason:-guard rejected input}" >&2
  exit 2
fi

if [ "$check" -eq 1 ]; then
  printf '%s' "$ruleset" | "$NFT" -c -f -
else
  printf '%s' "$ruleset" | "$NFT" -f -
fi
