#!/usr/bin/env bash # Local test (Phase 4 ยง29): renders each .rules file with dummy IPs and validates it. # # `iptables-restore --test` needs root (it talks to netfilter) โ€” if this shell has passwordless # sudo, we use it for a real semantic check. Otherwise we fall back to a structural lint # (balanced *table/COMMIT blocks, every rule starts with a real flag, no unrendered {{ }} # placeholders left over) โ€” good enough to catch a typo'd rule or a missing COMMIT, NOT a # substitute for the real `iptables-restore --test` a human/Ansible with actual root runs # before this ever reaches a live host. HARDWARE-DEPENDENT: proving the real IPs/CIDRs are # correct needs the actual production network, not this script. set -euo pipefail cd "$(dirname "$0")" TMPDIR="$(mktemp -d)" trap 'rm -rf "$TMPDIR"' EXIT render() { sed \ -e 's/{{ management_cidr }}/203.0.113.0\/24/g' \ -e 's/{{ platform_ip }}/10.10.10.10/g' \ -e 's/{{ lab_host_ip }}/10.10.10.20/g' \ -e 's/{{ lab_subnet_range }}/10.42.0.0\/16/g' \ -e 's/{{ proxmox_mgmt_cidr }}/203.0.113.0\/24/g' \ -e "s/{{ lab_gateway_port_range | default('30000:32767') }}/30000:32767/g" \ "$1" > "$2" } structural_lint() { local f="$1" grep -q '^\*filter' "$f" || { echo " missing *filter table declaration"; return 1; } grep -q '^COMMIT' "$f" || { echo " missing COMMIT"; return 1; } grep -v '^#' "$f" | grep -q '{{' && { echo " unrendered {{ }} placeholder left in a non-comment line"; return 1; } # every -A/-I rule line should end in a real target (ACCEPT/DROP/REJECT/LOG/a custom chain) while IFS= read -r line; do [[ "$line" =~ ^-[AI]\ ]] || continue [[ "$line" =~ -j\ [A-Za-z0-9_-]+ ]] || { echo " rule missing a -j target: $line"; return 1; } done < "$f" return 0 } HAVE_SUDO=0 sudo -n true 2>/dev/null && HAVE_SUDO=1 FAIL=0 for f in proxmox-host.rules rn-platform-01.rules rn-lab-host-01.rules; do render "$f" "$TMPDIR/$f" echo "==> $f" if [ "$HAVE_SUDO" = "1" ] && command -v iptables-restore >/dev/null 2>&1; then if sudo -n iptables-restore --test < "$TMPDIR/$f"; then echo "OK: $f passed a real iptables-restore --test" else echo "FAIL: $f did not pass iptables-restore --test" >&2; FAIL=1 fi else if structural_lint "$TMPDIR/$f"; then echo "OK: $f passes structural lint (no root available for a real iptables-restore --test โ€” see script header)" else echo "FAIL: $f failed structural lint" >&2; FAIL=1 fi fi done exit $FAIL