ProxmoxGuide
Isometric illustration of a Proxmox cluster showing three nested firewall layers wrapping the datacenter, a single node, and a virtual machine's network interface
security

Proxmox Firewall Configuration: Datacenter, Node and VM Rules

How the three Proxmox VE firewall levels interact, why rules silently do nothing until every enable flag is set, and safe defaults that won't lock you out.

By ProxmoxGuide Editorial · · 8 min read

The Proxmox VE firewall is three firewalls stacked on top of each other, and most “it isn’t working” reports are really “it isn’t switched on at the level I think it is.”

  • Three config levels: datacenter (/etc/pve/firewall/cluster.fw), node (/etc/pve/nodes/<nodename>/host.fw), and guest (/etc/pve/firewall/<VMID>.fw).
  • The datacenter switch gates everything. It is disabled by default. Until you set enable: 1 there, nothing filters, no matter what rules you wrote lower down.
  • Guests need two switches. The guest’s own firewall enable option and a per-NIC firewall checkbox on each virtual network device.
  • Open an SSH session before you enable it. Turning the firewall on blocks inbound traffic to every host by default.
  • Define the management IPSet first. It generates the access rules you would otherwise hand-write and forget.

Get the enable chain right and the rest of the system is unusually pleasant to work with. Get it wrong and you either see no filtering at all, or you lose the web GUI on a machine that is now a very expensive brick until you find a keyboard.

The three levels and how they nest

All firewall config lives on the Proxmox cluster file system under /etc/pve, which means it replicates to every node automatically and the pve-firewall service regenerates the underlying iptables rules when a file changes. You never copy rules between nodes by hand.

  • Datacenter/etc/pve/firewall/cluster.fw. Cluster-wide options, rules applied to all nodes, plus the shared definitions everything else references: security groups, IPSets, and IP aliases.
  • Node/etc/pve/nodes/<nodename>/host.fw. Host-specific rules and netfilter tuning. Rules at host level take precedence over datacenter rules for the host zone.
  • Guest/etc/pve/firewall/<VMID>.fw. Per-VM and per-container rules, options, IPSets and aliases.

The defaults across those three files are deliberately asymmetric, and that asymmetry is the source of most confusion:

LevelKeyDefault
Datacenter (cluster.fw)enable0 (off)
Node (host.fw)enable1 (on)
Guest (<VMID>.fw)enable0 (off)

Read that table again, because it explains a very common support thread. The node firewall is already enabled by default. It is simply inert, because the datacenter switch above it is off. Someone writes a careful set of node rules, sees enable: 1 sitting in host.fw, and concludes the firewall is broken. It is not broken. It was never armed.

Why your rules appear to do nothing

There are four distinct ways to get silence out of the Proxmox firewall, and they need different fixes.

1. The datacenter firewall is off. The master switch. Set it in the GUI under Datacenter → Firewall → Options, or directly:

# /etc/pve/firewall/cluster.fw
[OPTIONS]
enable: 1

2. The guest firewall option is off. Each VM or container has its own enable flag, defaulting to 0. Cluster-level rules do not implicitly filter guest traffic.

3. The per-NIC firewall checkbox is off. This is the one that burns people. Every virtual network device carries its own firewall flag so you can filter net0 and leave net1 alone. That checkbox is required in addition to the guest’s general enable option. A VM with enable: 1 in its .fw file and an unchecked NIC is unfiltered on that interface. Check it in the VM’s Hardware tab on the network device, not in the Firewall tab where you have been staring.

4. You wrote FORWARD rules on the stock backend. The documentation is blunt here: any forward rules are ignored by the stock pve-firewall and have no effect. Same for anything defined at the SDN VNet level, which is forward-direction only by nature. Both need the newer nftables backend, discussed at the end.

When you want ground truth rather than guesswork, two commands settle it:

pve-firewall status

This reads and compiles every rule, so it surfaces configuration errors and tells you whether the firewall is actually running.

iptables-save

This shows the chains and rules genuinely loaded in the kernel. If your rule is not in that output, it was never applied, so check the enable flags before you start rewriting the rule itself. One caveat: iptables-save only reflects the classic backend. If you have switched a node to the nftables backend described at the end, its rules live in nftables and you want nft list ruleset instead.

Zones and directions

Proxmox groups traffic into three zones, and each zone supports a different set of directions. Mismatching them is failure mode four above.

  • Host — traffic to and from a node, or forwarded by it. Rules can be defined at datacenter or host level. Supports IN, OUT and FORWARD.
  • VM — traffic to and from a VM or container. Supports IN and OUT only. There is no forward concept here.
  • VNet — traffic passing through an SDN VNet, guest to guest or host to guest. FORWARD only, since all of it is transit traffic.

Rules are direction plus action (ACCEPT, DROP, REJECT), optionally via a macro. Prefix a line with | to disable it without deleting it, which is far better practice than commenting rules out and losing them.

[RULES]
IN SSH(ACCEPT) -i net0 -source 192.168.2.192
IN ACCEPT -p tcp -dport 443 -source +management
|IN SSH(ACCEPT) -i net0

Safe defaults: enabling without locking yourself out

Enabling the datacenter firewall blocks inbound traffic to all hosts by default, with a small set of built-in exceptions. Do this in the right order.

Step one: open an SSH session to a node and leave it open. Established connections are exempt from the default rules, so that session survives the change and gives you a way back in. This is the official advice and it is worth following literally.

Step two: create the management IPSet before flipping the switch. This standard IPSet applies to host firewalls only, and the addresses in it are permitted to do normal management work: the web GUI, VNC, SPICE and SSH. Defining it generates the access rules automatically, which is strictly better than hand-writing four allow rules and forgetting one.

# /etc/pve/firewall/cluster.fw
[IPSET management]
192.168.2.0/24 # admin LAN

The local cluster network is added to this set automatically so inter-node communication keeps working.

Step three: sanity-check the auto-detected local network. Proxmox defines a local_network alias by autodetection and builds the cluster-communication rules (corosync, API, SSH) around it. Confirm what it resolved to:

pve-firewall localnet

If you run a single node on a public network, autodetection can be far too generous. Override it explicitly:

[ALIASES]
local_network 1.2.3.4 # pin to the single host IP

Step four: enable, then verify from a second machine while the first SSH session is still open.

With input blocked, these are still allowed to hosts, so you do not need rules for them:

  • Loopback traffic and already-established connections
  • IGMP
  • From management hosts: TCP 8006 (web GUI), 5900–5999 (VNC console), 3128 (SPICE proxy), 22 (SSH), 60000–60050 (live migration)
  • UDP 5405–5412 in the cluster network for corosync, plus cluster multicast
  • ICMP types 3, 4 and 11

That list is why a correctly configured management IPSet is nearly the whole job for a management-plane firewall. Anything else you run on the host, a reverse proxy, a metrics exporter, Proxmox Backup Server traffic, needs its own rule.

Guest rules are not host rules

The most important thing to internalise about guest firewalls: the host exceptions do not apply to them. Guests inherit the datacenter drop and reject behaviour, but not the accept exceptions that keep the GUI and SSH reachable on nodes. The management IPSet does nothing for a VM. The only exceptions a guest gets are the ones its own options produce, for DHCP, NDP, router advertisement and MAC/IP filtering, depending on how it is configured. Enable a guest firewall with a drop policy and no rules of your own, and that guest goes dark for everything else. Plan its ruleset before you enable it.

A few guest options are worth knowing rather than discovering:

  • macfilter is on by default and blocks a guest from spoofing MAC addresses.
  • ipfilter adds implicit ipfilter-net<id> sets per interface to prevent IP spoofing. For containers, configured addresses are added automatically.
  • ndp is on by default so IPv6 neighbour discovery works. IPv6 is filtered transparently alongside IPv4, so you do not maintain a parallel ruleset.
  • radv is what lets a guest advertise itself as a router, and a guest cannot do that unless you set it. Sending router solicitations and receiving advertisements works without it.
  • dhcp needs enabling if the guest gets its address by DHCP and you have a restrictive policy.

For anything you run more than once, define a security group at datacenter level and reference it from each guest rather than duplicating rules. This scales well across a fleet of LXC containers and VMs.

# /etc/pve/firewall/cluster.fw
[group webserver]
IN ACCEPT -p tcp -dport 80
IN ACCEPT -p tcp -dport 443

# /etc/pve/firewall/<VMID>.fw
[RULES]
GROUP webserver

There is also a standard blacklist IPSet at cluster level whose addresses are dropped by every host and guest firewall. Useful, but treat it as a scalpel for specific abusers, not a threat feed.

Logging, and what it will not tell you

Logging is off by default. Set a log level for incoming or outgoing traffic under Firewall → Options, per host or per guest, and read it in Firewall → Log. Two caveats save a lot of confusion:

  • The log level does not control how much traffic is logged. It sets a numeric LOGID prefix for filtering. Only some dropped or rejected packets are logged by the standard rules at all, so a quiet log is not proof that nothing was blocked.
  • Log lines are formatted VMID LOGID CHAIN TIMESTAMP POLICY: PACKET_DETAILS. For host firewall entries, VMID is 0.

For per-rule granularity, append -log <level> to an individual rule. That is independent of the zone’s configured level and is the right tool when you are chasing one specific drop.

The nftables backend

Proxmox offers proxmox-firewall, an nftables-based reimplementation that reads the same configuration files and format. It is not what runs by default: you install the proxmox-firewall package, set nftables: 1 in that node’s host.fw (or tick it under Host → Firewall → Options), and restart every running VM and container on the node before the switch is complete. It is what you need for FORWARD rules and SDN VNet rules, both of which the classic backend ignores. It is still labelled a tech preview and the documentation explicitly states it is not suited for production use, so treat forward-direction filtering as not yet available if the cluster matters.

If you do trial it on a lab node, know the behavioural differences going in: no extra fwbr bridges are created for Linux bridges, though guest interfaces on OVS bridges still get them, REJECT is unavailable for guest traffic and becomes a drop, NDP/router-advertisement/DHCP options always emit rules regardless of policy, and guest rules are evaluated even for connections that already have conntrack entries.

Next steps

See also

#proxmox#firewall#security#networking#homelab

Related

Comments