diff --git a/configs/felhom-oob-nft.service b/configs/felhom-oob-nft.service new file mode 100644 index 0000000..572d65a --- /dev/null +++ b/configs/felhom-oob-nft.service @@ -0,0 +1,19 @@ +# felhom-oob-nft.service (TASK H1) — install as /etc/systemd/system/felhom-oob-nft.service. +# +# Loads the STATIC felhom_oob belt table on boot (the agent then fills its sets each tick). Oneshot + +# RemainAfterExit so `systemctl status` reflects "loaded". Independent of pve-firewall/nftables.service +# (the table is a self-contained `inet felhom_oob`, additive — it never flushes the ruleset). +[Unit] +Description=Felhom OOB belt — load the static felhom_oob nft table (H1) +After=network-pre.target +Before=network.target felhom-agent.service + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=/usr/sbin/nft -f /etc/felhom-oob.nft +# On stop, remove the table (best-effort) so an uninstall/disable leaves nft clean. +ExecStop=-/usr/sbin/nft delete table inet felhom_oob + +[Install] +WantedBy=multi-user.target diff --git a/configs/felhom-oob.nft b/configs/felhom-oob.nft new file mode 100644 index 0000000..b60b84a --- /dev/null +++ b/configs/felhom-oob.nft @@ -0,0 +1,37 @@ +#!/usr/sbin/nft -f +# felhom OOB belt — STATIC table (TASK H1). Installed by host-install; loaded on boot by +# felhom-oob-nft.service. The agent mutates ONLY the two SETS (@operator_ips, @ssh_port) via the +# narrow FELHOM_OOB sudoers — it NEVER touches the rules [trap 4], so the enforcement semantics are +# fixed here and cannot be changed by the agent. +# +# Enforcement (scoped to iifname "wg-felhom" — the offsite tunnel — so :22 and every other interface +# are UNTOUCHED [SF-3/OF-5]): +# - ESTABLISHED,RELATED replies always pass (covers the PBS path + reply traffic; PMTU-safe). +# - felhom-sshd's port is reachable from the operator /32 (in @operator_ips) over the tunnel ONLY. +# - any other tunnel source to that port is DROPPED at the host (defense-in-depth vs the endpoint). +# - the port is unreachable OFF the tunnel entirely. +# Empty sets (before the agent's first sync) = the port rules match nothing = no enforcement yet +# (felhom-sshd is not started until the agent renders its config either). priority -5 sits just above +# the default; policy accept so this table only ever ADDS drops for the felhom-sshd port. +# +# Idempotent load: create-then-delete-then-define (a re-run/boot yields a clean table with EMPTY sets; +# the agent refills them within one tick). + +table inet felhom_oob +delete table inet felhom_oob + +table inet felhom_oob { + set operator_ips { + type ipv4_addr + } + set ssh_port { + type inet_service + } + chain input { + type filter hook input priority -5; policy accept; + ct state established,related accept + iifname "wg-felhom" tcp dport @ssh_port ip saddr @operator_ips accept + iifname "wg-felhom" tcp dport @ssh_port drop + tcp dport @ssh_port iifname != "wg-felhom" drop + } +} diff --git a/configs/felhom-op.sudoers b/configs/felhom-op.sudoers new file mode 100644 index 0000000..5118ba2 --- /dev/null +++ b/configs/felhom-op.sudoers @@ -0,0 +1,23 @@ +# felhom-op operator sudoers (TASK H1) — install as /etc/sudoers.d/felhom-op (0440 root:root). +# +# The DEFAULT operator identity (login via felhom-sshd only; key in /etc/felhom-sshd/authorized_keys/ +# felhom-op, outside ~/.ssh, so the customer's sshd never honours it [SF-3]). felhom-op is a normal +# login user with NO ambient privilege — it escalates ONLY through these explicit repair verbs, so a +# leaked operator key's blast radius is this verb set, not the box (locked decision §6). Deep repairs +# that genuinely need root (pct, deeper systemctl) remain available via `root` as a tunnel-gated +# break-glass account (PermitRootLogin prohibit-password on felhom-sshd), but the day-to-day identity +# is felhom-op. +Cmnd_Alias FELHOM_OP_REPAIR = \ + /usr/bin/mkdir -p /run/sshd, \ + /usr/bin/systemctl reset-failed wg-quick@wg-felhom, \ + /usr/bin/systemctl restart wg-quick@wg-felhom, \ + /usr/bin/systemctl start felhom-agent, \ + /usr/bin/systemctl restart felhom-agent, \ + /usr/bin/systemctl reset-failed felhom-sshd, \ + /usr/bin/systemctl restart felhom-sshd, \ + /usr/sbin/pct list, \ + /usr/sbin/pct start [0-9]*, \ + /usr/sbin/pct stop [0-9]*, \ + /usr/sbin/pct unlock [0-9]* + +felhom-op ALL=(root) NOPASSWD: FELHOM_OP_REPAIR