Proxmox VE and Proxmox Mail Gateway are Debian, so os_detect classified them as
debian and harden-firewall.sh installed the iptables backend on top of
pve-firewall.
The conflict is not the obvious one. pve-firewall does NOT delete third-party
rules: it restores with `iptables-restore -n` (--noflush), only ever flushes
chains matching its own patterns (PVEFW-*, tapNiM-*, vethNiM-*, fwbrN-*,
GROUP-*), appends `-A INPUT -j PVEFW-INPUT` only when that hook is missing, and
never sets a built-in chain's policy. Our rules survive it. We are the ones
doing damage:
- `-P INPUT DROP` is ours alone, and PVEFW-HOST-IN RETURNs on accept rather than
ACCEPTing (it still has to check tap rules), so traffic Proxmox explicitly
allowed falls out of its chain onto our DROP -- we silently override the
platform's own accepts.
- netfilter-persistent restores at boot with a full iptables-restore (no
--noflush), wiping PVE's hook along with everything else until the daemon
re-appends it ~10s later.
- a deny-by-default chain has to enumerate the whole platform to stay usable:
8006, 5405-5412/udp corosync, 60000-60050, 5900-5999, 3128, 22, 111/udp, plus
Ceph when hyperconverged -- and `-i lo`, or pveproxy loses pvedaemon on :85.
- under the nftables backend (PVE 8.2+) an nft DROP beats an iptables ACCEPT, so
our rules would not even be authoritative.
So don't manage a firewall there at all:
- oslib: is_proxmox() -- matches hosts shipping pve-firewall (VE/PMG), not PBS.
- harden-firewall.sh: a third backend, "pve", that deliberately does nothing.
apply explains and exits 0, allow/deny refuse loudly rather than fake success
for a rule they didn't add, list shows pve-firewall status. It overrides an
explicit FW_BACKEND; FW_IGNORE_PVE=1 is the one escape hatch.
- harden-ssh.sh / harden-jumphost.sh: skip the firewall and install the
standalone INPUT -> sshguard boot hook instead. That jump is safe alongside
pve-firewall -- inserted with -I, it sits ahead of the appended PVEFW-INPUT
hook and keeps first look at NEW connections.
Detection only helps hosts built from here on, so `disable` cleans up one that
was hardened earlier: it detects leftovers (and says which signal fired), sets
INPUT ACCEPT *before* flushing so it can't drop the SSH session it runs over,
deletes /etc/firewall and the engine, disables boot restore and renames the
saved rulesets aside rather than persisting the open state, re-adds the sshguard
jump, and restarts pve-firewall. `apply` points at it when it spots leftovers.
Documented plainly that skipped is NOT protected: Proxmox's firewall is off by
default (cluster-wide enable defaults to 0, and the daemon tears its chains down
every ~10s while it is), so these hosts have no host firewall until someone
enables it -- and the node panel's "Firewall: Yes" is ignored while the
datacenter one says No.
Also: svc_disable + fw_restore_services/fw_saved_files in oslib
(fw_enable_restore now derives from the former), and usage() prints the whole
header block instead of a hardcoded line range.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>