Skip to main content
RunBook Academy

Proxmox VEXXVII · Multi-Cluster and Multi-TenancySharing one cluster

Network isolation between tenants

Advanced⏱ ~34 minpveshpveumpve-firewall

What you'll learn

  • Explain why SDN.Use on a zone path is the privilege that actually confines a tenant to their own networks
  • Design a zone-per-tenant layout and say which zone type suits which estate
  • Enable the firewall correctly: the cluster flag, the guest flag and the per-interface flag
  • Use ipfilter to stop a tenant spoofing addresses inside a shared segment
  • State the limits of isolate-ports and of the VNet firewall, including the nftables backend requirement

Prerequisites

Verified against Proxmox VE 9.2.4 · Proxmox Backup Server 4.2.5 · Ceph Squid / Tentacle · Debian 13 (Trixie) · Linux kernel 7.0 (PVE 9.2 default) · 2026-08-12

Not yet marked complete on this device.

The previous lesson ended on the row that matters most in the table of things a pool does not isolate: network. Two guests attached to the same bridge can reach each other, and neither the pool they belong to nor the ACLs on that pool have any bearing on it.

Isolation between tenants is a property of two things — which layer-2 domain a guest is attached to, and who is allowed to attach a guest to it. Everything in this lesson is one of those two.

The permission that does the work

Start with the one that surprises people. Consider a tenant with a sensible role: VM.Allocate, the VM.Config.* privileges including VM.Config.Network, VM.Console, VM.PowerMgmt, scoped tightly to /pool/tenant-blue.

VM.Config.Network is documented as “add/modify/remove network devices”. So the tenant can add a network device. To what?

To whichever bridge or VNet they name — unless something stops them. The privilege that stops them is SDN.Use, documented as “access SDN vnets and local network bridges”, and its ACL path is what confines the tenant:

Configuration changeconfine a tenant to their own zone
POOL="tenant-blue"
ZONE="blue"

pveum role add TenantNetwork --privs "SDN.Use SDN.Audit"
pveum acl modify "/sdn/zones/$ZONE" --groups "$POOL-admins" --roles TenantNetwork

pveum user permissions someone@ldap --path "/sdn/zones/$ZONE"

This is the general shape of tenant confinement in PVE, and it is worth stating once as a principle: a tenant is confined by the set of paths their roles are granted on, not by the pool their guests are in. The pool is where the guests are; the grants are what they can touch.

Zone per tenant

With the permission model settled, the topology. Proxmox VE’s SDN offers five zone types and they suit different estates:

Zone typeIsolation mechanismSuits
SimpleAn isolated bridge per VNet, local to each node, with routing or NATSmall estates, lab-adjacent tenancy, tenants who only need outbound
VLAN802.1Q tags on an existing bridgeMost on-premises estates; requires switch configuration to match
QinQStacked service and customer VLAN tagsProviders who have run out of VLAN space or need a per-customer tag space
VXLANLayer-2 overlay over UDP 4789 on an underlay networkEstates where the physical fabric must not know about tenant VLANs
EVPNRoutable layer-3 with BGP, VRF and anycast gateway per nodeLarger providers; the only one giving each tenant a routing domain

The tenancy-relevant point is that the zone is the ACL object. A zone per tenant means a path per tenant — /sdn/zones/blue, /sdn/zones/green — and therefore a clean grant. A shared zone with per-tenant VNets inside it gives you one path to grant on and no way to distinguish the tenants using it.

Read-only / Safesee the zones, VNets and their pending state
pvesh get /cluster/sdn/zones --output-format json | jq -r '.[] | [.zone, .type, (.nodes // "all")] | @tsv'

pvesh get /cluster/sdn/vnets --output-format json | jq -r '.[] | [.vnet, .zone, (.tag // "-"), (.isolate_ports // 0)] | @tsv'
Read-only / Safe
$ pvesh get /cluster/sdn/vnets --output-format json | jq -r '.[] | [.vnet, .zone, (.tag // "-"), (.isolate_ports // 0)] | @tsv'
blue-front	blue	1201	0
blue-back	blue	1202	1
green-front	green	1301	0
green-back	green	1302	1

The three switches

The commonest firewall failure in Proxmox is not a wrong rule. It is a correct rule that is not being enforced, because the firewall has three independent enable flags and all of them must be on.

  1. Cluster-wide: enable: 1 in the [OPTIONS] section of /etc/pve/firewall/cluster.fw. Off by default.
  2. Per guest: enable in /etc/pve/firewall/<VMID>.fw, which defaults to 0.
  3. Per network interface: the firewall=1 flag on the guest’s network device itself, in the guest configuration.

The per-host flag in host.fw is a fourth, though it defaults to enabled once the cluster flag is set.

Read-only / Safeverify enforcement rather than configuration
VMID=141

grep -A5 '^\[OPTIONS\]' /etc/pve/firewall/cluster.fw

cat "/etc/pve/firewall/${VMID}.fw"

qm config "$VMID" | grep '^net'

pve-firewall status

pve-firewall compile | grep -A20 "${VMID}"

Stopping a tenant from lying about their address

Layer-2 isolation puts tenants on separate segments. Inside a shared segment — and there is usually at least one, for management or for a shared service — a guest can claim any IP or MAC it likes unless something prevents it.

PVE’s mechanism is the ipfilter IPSet. A per-guest IPSet named ipfilter-net<N>, matching the guest’s interface, restricts the source addresses that guest may use. The firewall chapter documents these ipfilter-net* sets as per-interface spoofing prevention.

Configuration changepin a guest to the address it was given
VMID=141
NODE="$(hostname)"

pvesh create "/nodes/${NODE}/qemu/${VMID}/firewall/ipset" --name "ipfilter-net0"
pvesh create "/nodes/${NODE}/qemu/${VMID}/firewall/ipset/ipfilter-net0" --cidr 192.0.2.50

pvesh get "/nodes/${NODE}/qemu/${VMID}/firewall/ipset/ipfilter-net0"

Spoofing protection matters more in multi-tenancy than in a single-tenant cluster for a specific reason: on a shared segment, every other tenant’s firewall rules that permit a source address are only as good as the guarantee that the source address is real. Without ipfilter, an allow-list by IP is an allow-list by claim.

isolate-ports, and what it is not

The SDN documentation describes isolate-ports as marking all guest ports as isolated, preventing communication between guests while still permitting traffic to the gateway. That is exactly the semantic you want for a segment where tenants share a VNet but must not see each other — a hosting VLAN, for example.

Two limits, both documented, both important:

It is local to each host. Guests on the same VNet but on different nodes are not isolated from one another by this setting. The documentation says so directly and recommends the VNet firewall for cluster-wide isolation.

The VNet firewall needs the nftables backend. VNet-level rules live in /etc/pve/sdn/firewall/<vnet_name>.fw and require the proxmox-firewall nftables backend, enabled with nftables: 1 in the host configuration. That backend is documented as a tech preview, and the same applies to forward-direction rules — the iptables backend ignores them.

Key takeaways

  • A pool does not isolate networks. Isolation is which layer-2 domain a guest is attached to, plus who may attach a guest to it.
  • SDN.Use — “access SDN vnets and local network bridges” — granted on /sdn/zones/<zone> is what confines a tenant. Without it constrained, a tenant holding VM.Config.Network can attach a NIC to any network the cluster has.
  • Permissions are additive across paths. Read pveum acl list and pveum user permissions <user> --path / at onboarding; a legacy grant at the root defeats a careful grant on a zone.
  • One zone per tenant, because the zone is the ACL object. VNets inside it, prefixed with the tenant name and each with its own tag.
  • The firewall has three independent enable flags: enable: 1 in cluster.fw, enable in <VMID>.fw which defaults to 0, and firewall=1 on the guest NIC. All three, or nothing is enforced.
  • Verify with pve-firewall compile, not by reading .fw files. A guest with no compiled chain has no firewall regardless of its configuration.
  • ipfilter-net<N> IPSets stop a guest spoofing source addresses. On a shared segment, an IP allow-list without them is an allow-list by claim.
  • isolate-ports is local to each host and does not isolate guests on different nodes. The VNet firewall (/etc/pve/sdn/firewall/<vnet>.fw) and forward-direction rules require the proxmox-firewall nftables backend, which is a tech preview.
  • Test isolation from another tenant’s guest, on a schedule. Nothing else proves it.

Knowledge check

Knowledge check · 5 questions

  1. Q1. A tenant has a role with VM.Allocate and the VM.Config.* privileges granted on /pool/tenant-blue, and no SDN grants at all beyond a legacy PVEAdmin role handed out on / years ago. What can they do to the network?

  2. Q2. A penetration test reaches a database port on a tenant guest from another tenant guest. The guest .fw file has a default drop and explicit allows, and pve-firewall status reports the firewall as running. What should you check first?

  3. Q3. Which statements about isolate-ports and the VNet firewall are accurate for PVE 9.2? Select all that apply.

  4. Q4. Traffic between two guests on the same bridge and VLAN on one node can be inspected and filtered by the physical switch between racks.

  5. Q5. Tenants share one management segment for a monitoring service, and each tenant firewall allows that service by source IP. What additional control does this design require, and why?

Passing score: 75%. Answers are checked in this browser.