
Thursday 12th June 2025
One of the most important things when building a multitenant datacenter is to prevent tenants from speaking directly to each other, without any restrictions.
Imagine living in an apartment complex without locks on the front doors. You can of course install your own lock on your own door but it doesn’t stop you from entering other tenants.
After finally setting up the bridge firewall on VyOS, I quickly realized that I could solve this much simpler. And when I did that I realized that I could make it even simpler by only using the PVE firewall. That happens sometimes.
To be clear: The goal here is to prevent all tenant-to-tenant traffic by only using the PVE firewall.
Overview
I have reverted back to use only one shared segment per VRF:
Explanation:
Linux bridge vmbr2 is used to by the router to communicate with other guests. For more information, read Proxmox Setup Part 2: Network
VyOS configuration changes
I kept the bridge configuration but removed isolation:
set interfaces bridge br0 enable-vlan
set interfaces bridge br0 mac '00:00:00:00:01:01'
set interfaces bridge br0 member interface eth2 allowed-vlan '4000-4020'
set interfaces bridge br0 mtu '9198'
set interfaces bridge br0 stp
...
set interfaces bridge br0 vif 4003 address '2001:DB8:1234:3000::1/64'
set interfaces bridge br0 vif 4003 address 'FE80::3:1/64'
set interfaces bridge br0 vif 4003 address '10.3.254.1/24'
set interfaces bridge br0 vif 4003 description 'PUB-Linknet'
set interfaces bridge br0 vif 4003 ipv6 address no-default-link-local
set interfaces bridge br0 vif 4003 vrf 'PUB'
I have completely removed the firewall configuration on VyOS:
vyos@LBS-RO1# del firewall
vyos@LBS-RO1# commit
vyos@LBS-RO1# save
PVE Configuration Changes
I have removed the SDN configuration made in the post about VyOS bridge firewall and reverted back to use a shared bridge on all guests:
SAUNA-VM1 (same on all guests):
Explanation:
Bridge interface vmbr2 is configured 4 times but with different VLAN tags. You have to configure the network cards this way because the PVE firewall won’t have any effect on VLANs configured inside the host*
Notice that “firewall” is enabled on vmbr2.
You may have noticed that the same MAC address is configured on all four vmbr2 subinterfaces. That is not necessary for this purpose. Just leftovers from an older lab.
*Note: perhaps VNet firewall works differently (under Datacenter > SDN > VNet Firewall). I have not tested that yet.
VyOS router:
Explanation:
Notice that no firewall option is enabled on the router. This is because the router needs to be able to talk to all guests.
Configuration
All configuration is done through the Proxmox GUI.
Step 1: Create IP Sets
At first I tried to avoid IPSets because they need to be updated when new tenants gets created. However, it’s not too bad. My initial fear was that I had to configure individual source-destination rules for each tenant, but actually I just need to add one entry for each new tenant in the IPv6 IP Set. Considering the simplicity of the solution, this is a compromise I’m willing to live with.
Navigate to Datacenter > Firewall > IPSet
There are four IP Sets related to this solution:
This IP Set lists all the IPv6 prefixes that tenants use:
This IP Set lists all IPv4 private address space:
The two above lists all the gateway IP’s for uplink routers. I’m only showing IPv6 as an example:
Step 2: Create Security Groups
Navigate to Datacenter > Firewall > Security Groups
Explanation
I have three Security Groups defined but I’m currently only using the “all_tenants” SG. The others are defined in case I need to make exceptions for specific tenants.
The Security Group “all_tenants” include the following 5 rules:
Explanation:
The reject rules denies all traffic between tenants
The first two accept rules makes exceptions for the uplink routers
Notice that the rules are put in the outgoing direction, meaning the traffic gets filtered as it leaves the interfaces from the guests.
Also notice that the IP Sets configured earlier can be found in the “Destination” field.
Step 3: Edit the guest OS firewall options
Navigate to GuestID > Firewall > Options
The important options are:
Firewall: Yes
Input Policy: ACCEPT
Output Policy: ACCEPT
Step 4: Apply the Security Group to the guests
Navigate to GuestID > Firewall and click Insert: Security Group
Explanation:
Security Group “sauna” is empty but included as a placeholder for future use.
“all_tenants” is deliberately placed in the bottom.
Notice that guest-specific aliases and IP Sets can be configured as well.
Repeat step 3 and step 4 for each guest.
Note: The firewall rules will only have effect in network adapters with “Firewall” enabled.
Verification
With a ping command from SAUNA-VM1 I can confirm that I can ping the uplink router, both with IPv6 GUA and Link Local:
sauna@sauna-vm1:~$ sudo ip vrf exec PUB ping -I 2001:DB8:1234:9001::1 2001:DB8:1234:3000::1
PING 2001:DB8:1234:3000::1 (2001:DB8:1234:3000::1) from 2001:DB8:1234:9001::1 : 56 data bytes
64 bytes from 2001:DB8:1234:3000::1: icmp_seq=1 ttl=64 time=0.453 ms
64 bytes from 2001:DB8:1234:3000::1: icmp_seq=2 ttl=64 time=0.317 ms
64 bytes from 2001:DB8:1234:3000::1: icmp_seq=3 ttl=64 time=0.497 ms
^C
--- 2001:DB8:1234:3000::1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2080ms
rtt min/avg/max/mdev = 0.317/0.422/0.497/0.076 ms
sauna@sauna-vm1:~$ ping fe80::3:1%ens21
PING fe80::3:1%ens21 (fe80::3:1%ens21) 56 data bytes
64 bytes from fe80::3:1%PUB: icmp_seq=1 ttl=64 time=0.409 ms
64 bytes from fe80::3:1%PUB: icmp_seq=2 ttl=64 time=0.586 ms
64 bytes from fe80::3:1%PUB: icmp_seq=3 ttl=64 time=0.508 ms
^C
--- fe80::3:1%ens21 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2053ms
rtt min/avg/max/mdev = 0.409/0.501/0.586/0.072 ms
I can also ping an address on the Internet:
sauna@sauna-vm1:~$ sudo ip vrf exec PUB ping -I 2001:DB8:1234:9001::1 2620:FE::FE
[sudo] password for sauna:
PING 2620:FE::FE (2620:fe::fe) from 2001:db8:1234:9001::1 : 56 data bytes
64 bytes from 2620:fe::fe: icmp_seq=1 ttl=58 time=5.12 ms
64 bytes from 2620:fe::fe: icmp_seq=2 ttl=58 time=6.04 ms
64 bytes from 2620:fe::fe: icmp_seq=3 ttl=58 time=5.67 ms
^C
--- 2620:FE::FE ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 5.118/5.609/6.040/0.378 ms
Tenant-to-tenant traffic is however completely blocked:
sauna@sauna-vm1:~$ sudo ip vrf exec PUB ping -I 2001:DB8:1234:9001::1 2001:DB8:1234:3001::1
PING 2001:DB8:1234:3001::1 (2001:DB8:1234:3001::1) from 2001:DB8:1234:9001::1 : 56 data bytes
^C
--- 2001:DB8:1234:3001::1 ping statistics ---
4 packets transmitted, 0 received, 100% packet loss, time 3094ms
Note: one caveat is that this also affects groups of guests within the same tenant. The tenant specific Security Group could be used to make exceptions for this. However, I have not found a reason why I should let them talk to each other on the outside subnet, when they are free to communicate on the inside.