09
Firewall
Proxmox has a built-in, cluster-aware firewall. Rules live in plain text files in /etc/pve/firewall/, so pmxcfs copies them to every node, and each node turns them into kernel rules for itself and for the guests it runs. Guest rules follow a VM when it migrates. There are three levels (datacenter, node, guest), plus reusable security groups, IP sets and aliases. In each chain the first matching rule wins.
Build a packet
destination
direction
source
proto / port
backend
path
eno1→
vmbr0→
fwpr100p0→
fwln100i0→
fwbr100i0 ⟂→
tap100i0→
VM 100
iptables can only filter bridged traffic through br_netfilter, so every firewalled NIC gets its own fwbr bridge and veth pair.
Rules, evaluated top to bottom
evaluating…- ct state established,related → ACCEPT · (new connection, skip)
- 100.fw → GROUP webserverIN DROP -source +blocklist
- 100.fw → GROUP webserverIN ACCEPT -p tcp -dport 80
- 100.fw → GROUP webserverIN ACCEPT -p tcp -dport 443
- 100.fwIN SSH(ACCEPT) -source admin_laptop
- 100.fwIN ACCEPT -p icmp
- 100.fwpolicy_in: DROP
levels
Where each rule lives
A common mistake: rules in cluster.fw do not filter VM traffic. They apply to the hosts (merged with each node's host.fw, host rules first). Guests are filtered only by their own <vmid>.fw, which can pull in security groups that are defined at the datacenter level.
| File | Filters | Notes | |
|---|---|---|---|
| Datacenter | /etc/pve/firewall/cluster.fw | traffic to/from every node | master switch enable: 0|1 (default 0); default input DROP, output ACCEPT; defines groups, IP sets, aliases |
| Node | /etc/pve/nodes/<node>/host.fw | that node only | evaluated before the datacenter rules; per-node options (nftables, log levels, conntrack limits) |
| Guest | /etc/pve/firewall/<vmid>.fw | NICs with firewall=1 | own enable (default 0), policies, ipfilter/macfilter, IP sets like ipfilter-net0 |
| Security group | [group name] | nothing on its own | a named list of rules, inserted wherever a GROUP name rule appears |
| IP set / alias | +name / name | — | reusable address lists; management, blacklist and ipfilter-netX have special meaning (blocklist here is an ordinary set) |
| VNet (nftables) | /etc/pve/sdn/firewall/<vnet>.fw | forwarded traffic | only with the nftables backend; FORWARD direction |
/etc/pve/firewall/cluster.fw
[OPTIONS]
enable: 1
policy_in: DROP
policy_out: ACCEPT
[ALIASES]
admin_laptop 192.168.10.5
[IPSET management]
admin_laptop
[IPSET blocklist]
203.0.113.0/24
[RULES]
IN DROP -source +blocklist
# IN ACCEPT -source 10.8.0.0/24 -p tcp -dport 8006 # add BEFORE enabling if you manage via VPN
[group webserver]
IN DROP -source +blocklist
IN ACCEPT -p tcp -dport 80
IN ACCEPT -p tcp -dport 443/etc/pve/nodes/pve1/host.fw
[OPTIONS]
enable: 1
# opt in to proxmox-firewall on this node (no trailing comments on option lines)
nftables: 1
[RULES]
IN ACCEPT -source local_network -p tcp -dport 9100 # node_exporter (alias, no +)/etc/pve/firewall/100.fw
[OPTIONS]
enable: 1
policy_in: DROP
policy_out: ACCEPT
ipfilter: 1
macfilter: 1
[IPSET ipfilter-net0]
192.168.10.50
[RULES]
GROUP webserver -i net0
IN SSH(ACCEPT) -source admin_laptop -i net0
IN ACCEPT -p icmp
OUT DROP -p tcp -dport 25enable at datacenter level
Don't lock yourself out
Setting enable: 1 in cluster.fw switches every node to input policy DROP. To keep the cluster working, Proxmox adds some rules itself: established connections, corosync (UDP 5405-5412) between nodes, migration ports, and management access (8006 GUI, 22 SSH, 5900-5999 VNC, 3128 SPICE) from the management IP set and the node's local network. Anyone outside those ranges, like an admin on a VPN subnet, is dropped. Try it in the evaluator: node pve1, source 10.8.0.2, port 8006.
Order matters, built-ins come last
The automatic management rules come after your own host and datacenter rules. A broad IN DROP that you write is checked first, so it can block 8006 even from the LAN. Put allow rules first, and check pve-firewall localnet before you enable the firewall.Anti-spoofing: ipfilter and macfilter
macfilter (on by default) drops frames from a guest whose source MAC isn't the NIC's configured MAC. With ipfilter: 1, outgoing packets whose source IP is not in ipfilter-net0 (one set per NIC) are dropped. Containers get their configured IPs added automatically, plus the MAC-derived IPv6 link-local address. That stops a tenant from stealing another guest's IP.checking and recovering
pve-firewall status # running / enabled?
pve-firewall localnet # what local_network resolved to
pve-firewall compile # print the generated ruleset (iptables backend)
# nftables backend (after setting nftables: 1 in host.fw)
systemctl status proxmox-firewall
nft list ruleset | less
# locked out? from the console / IPMI:
pve-firewall stoppve-firewall vs proxmox-firewall
iptables today, nftables next
The classic pve-firewall generates iptables/ipset rules. Its successor proxmox-firewall, written in Rust, generates native nftables rules. It has been opt-in per node since PVE 8.2 (nftables: 1 in host.fw) and is still marked tech preview in the PVE 9 docs, while it moves toward becoming the default. It reads the same config files, so you can switch per node and back. It drops the fwbr detour for Linux bridges and adds forward-direction rules and VNet firewalls for SDN.
- why it mattersRules are evaluated top to bottom and the first match wins, then the policy applies. Host traffic sees host.fw, then cluster.fw, then the built-in management rules. Guest traffic sees only its own <vmid>.fw (security groups are inserted inline).
- why it mattersEnabling the datacenter firewall means input DROP everywhere. Management access only survives for the management IP set and the local network, so add rules for VPN or jump hosts first, and keep console access ready.
- why it mattersTurn on ipfilter and keep macfilter on for multi-tenant guests. Without them a VM can claim any IP or MAC on the bridge. The nftables backend also removes the per-NIC fwbr bridges.