Date: Sat, 04 Jul 2026 18:24:49 +0000 From: bugzilla-noreply@freebsd.org To: bugs@FreeBSD.org Subject: [Bug 296521] bin/pfctl: pf's netlink conversion blocks non-VNET jails from using pfctl (no allow.pf / RTNL_F_ALLOW_NONVNET_JAIL equivalent) Message-ID: <bug-296521-227@https.bugs.freebsd.org/bugzilla/>
index | next in thread | raw e-mail
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=296521 Bug ID: 296521 Summary: bin/pfctl: pf's netlink conversion blocks non-VNET jails from using pfctl (no allow.pf / RTNL_F_ALLOW_NONVNET_JAIL equivalent) Product: Base System Version: Unspecified Hardware: Any OS: Any Status: New Severity: Affects Some People Priority: --- Component: kern Assignee: bugs@FreeBSD.org Reporter: laszlo-rdr-keethu9thi@karolyi.hu I've upgraded from 14.4 to 15.1 and hit this. After a couple hours of investigating an interacting on the #freebsd IRC channel, I was asked to make a summary of what I've bumped into. This is the condensed summary of it. Environment: Introduced in 15.0-RELEASE, unchanged through 15.1-RELEASE and head (2026-07). sys/netpfil/pf/pf_nl.c does not exist in 14.4; it exists in 15.0 already with the restriction described below, so this is a 15.0 regression, not 15.1. (Distinct from the securelevel/EPERM issue some hit after upgrading jails to 15.1 -- commit 47c12f20bf58/D56390, added between 15.0 and 15.1. That gates on securelevel and affects VNET jails too. This report is about a separate, unconditional gate that blocks non-VNET jails regardless of securelevel.) Description: 15.0 converted pfctl/libpfctl from ioctl(/dev/pf) to netlink(4) (generic netlink family "pf", sys/netpfil/pf/pf_nl.c). Every command in pf_cmds[] sets `.cmd_priv = PRIV_NETINET_PF`, enforced unconditionally in genl_handle_message() (sys/netlink/netlink_generic.c): if (cmd->cmd_priv != 0 && !nlp_has_priv(nlp, cmd->cmd_priv)) return (EPERM); prison_priv_check() (sys/kern/kern_jail.c) only grants PRIV_NETINET_PF to jails with their own vnet (`pr_flags & PR_VNET`); there is no jail permission that grants it to a non-VNET jail. Result: pfctl inside any non-VNET jail now fails (EPERM) on essentially all operations -- rule load, start/stop, tables, kill-states -- even with /dev/pf exposed via devfs_ruleset, securelevel 0, and jailed root. The old ioctl(/dev/pf) path (pf_ioctl.c) never called priv_check() at all; access was gated purely by opening /dev/pf. That's why this worked through 14.x for non-VNET jails, and why it's a regression rather than a pre-existing restriction. This is the same VNET-only restriction that routing sockets hit for PRIV_NET_ROUTE -- and which *was* given a fix: commit 3a53fe2cc4b7 ("jail: add allow.routing jail permission", D49843) added a PR_ALLOW_ROUTING permission plus an RTNL_F_ALLOW_NONVNET_JAIL command flag, checked in rtnl_handle_message() (sys/netlink/netlink_route.c), so non-VNET jails can be opted in per command. pf's genl_cmd struct has no equivalent flag, and no PR_ALLOW_PF exists. How-To-Repeat: 1. Non-VNET jail, /dev/pf exposed via devfs_ruleset. 2. Inside jail: pfctl -e (or pfctl -f pf.conf) 3. Get: pfctl: DIOCSTART (or DIOCADDRULE, etc.): Operation not permitted. 4. Same command from host, or from a VNET jail: succeeds. Suggested Fix (modeled on D49843): - Add PR_ALLOW_PF jail permission (sys/sys/jail.h, jail.conf(5)). - Add a command flag to struct genl_cmd (sys/netlink/netlink_ctl.h), checked in genl_handle_message() alongside cmd_priv/cmd_securelevel, to let PR_ALLOW_PF satisfy cmd_priv for non-VNET jails. - Tag the relevant pf_cmds[] entries in pf_nl.c with it -- likely everything currently gated by PRIV_NETINET_PF, restoring what non-VNET jails could already do via the old ioctl path pre-15.0. - Document clearly: since non-VNET jails share the host's vnet0, this permission lets the jail rewrite the *host's* actual pf ruleset/state/ tables, not an isolated copy -- same caveat as allow.routing, worth flagging given pf is often used for jail-facing security tooling (e.g. fail2ban-style jails reacting to hostile input). Workarounds in the meantime: - Convert the jail to VNET (isolated pf instance, no gap, but requires epair/bridge rearchitecting). - Manage the jail's pf rules from the host instead (anchor + `pfctl -a jailname`, or a host-side proxy acting on the jail's behalf). - Keeping an old (14.4) pfctl binary in the jail to keep using the ioctl path works but is unsupported/fragile and re-opens the same privilege gap for jails handling untrusted input -- not recommended. -- You are receiving this mail because: You are the assignee for the bug.home | help
Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?bug-296521-227>
