Proxmox Network & Firewall Design¶
Overview¶
This document describes the network architecture and firewall design for the Proxmox cluster hosted at OVH. The design uses Proxmox SDN with two zone types to provide controlled network segmentation, automatic DHCP distribution, and internet access for guest workloads.
Terminology¶
| Term | Definition |
|---|---|
| Node | A Proxmox host (pve1, pve2, pve3) |
| Host | The Proxmox node itself, in the context of its network stack and firewall |
| Guest | Any workload running on Proxmox — both VMs and LXC containers |
| VNet | A virtual network segment defined in Proxmox SDN |
| Simple Zone | An SDN zone type that provides node-local networking with built-in DHCP and SNAT |
| VLAN Zone | An SDN zone type that maps to an existing Linux bridge with VLAN tagging |
Note
Node and host often refer to the same physical system. Node is used in the infrastructure context; host is used when discussing network and firewall behavior.
Assumptions¶
- 3 Proxmox nodes on OVH, each with a public and a private connection
- Proxmox SDN is enabled
- IPAM:
pve - DHCP is served by
dnsmasqrunning on each node - Internet access for guests is provided via SNAT
- IPv4 forwarding is enabled on all nodes (
net.ipv4.ip_forward=1)
SDN Zones¶
Two zone types are defined.
| Zone ID | Type | Details |
|---|---|---|
simple |
Simple | Node-local, automatic DHCP enabled |
vlan |
VLAN | Bridge: vmbr1 |
VNets and Subnets¶
| VNet | Zone | Subnet | Gateway | SNAT | DHCP Range |
|---|---|---|---|---|---|
ingress |
simple | 172.16.5.0/24 |
172.16.5.1 |
Yes | 172.16.5.2 – 172.16.5.10 |
isolated |
simple | 172.16.10.0/24 |
172.16.10.1 |
Yes | 172.16.10.100 – 172.16.10.200 |
k8group |
simple | 172.16.20.0/24 |
172.16.20.1 |
Yes | 172.16.20.100 – 172.16.20.200 |
mgmt |
vlan (tag 100) | 172.16.100.0/24 |
172.16.100.1 |
Yes | — |
Network Roles¶
ingress — entry point for externally reachable services (reverse proxy, ingress).
- Accepts inbound internet traffic via DNAT
- Forwards requests to the
isolatednetwork - Exits to the internet via SNAT
isolated — internal application workloads.
- Default restricted access
- Accepts traffic from
ingresson specific ports only - Exits to the internet via SNAT
k8group — dedicated network for Kubernetes workloads.
- DNS and egress access
- Isolated from other application VNets
mgmt — shared management plane for internal services (VLAN 100).
- Hosts services that need to reach each other: Guacamole, OpenBao, and similar infrastructure components
- Node-to-node cluster traffic
- Firewall rules are under active refinement
Warning
Subnet separation alone does not provide isolation. Traffic between guests in the same VNet must also be restricted at the firewall level.
DHCP and DNS¶
Simple Zone provides built-in DHCP through dnsmasq running on each node. The zone-specific instance dnsmasq@simple.service must be used — the default dnsmasq.service must remain inactive.
# Verify correct state
systemctl status dnsmasq # must be inactive
systemctl status dnsmasq@simple.service # must be active
Note
The default dnsmasq.service and dnsmasq@simple.service must not run simultaneously.
Node Routing¶
IPv4 forwarding must be enabled on all nodes for VNet transit, SNAT, and DNAT to work.
echo "net.ipv4.ip_forward=1" >> /etc/sysctl.d/99-proxmox.conf
sysctl -p /etc/sysctl.d/99-proxmox.conf
# Verify
sysctl net.ipv4.ip_forward
# Expected: net.ipv4.ip_forward = 1
Warning
This setting must be applied on every node.
Firewall Layers¶
Proxmox firewall rules are evaluated at four distinct levels.
Known issue — dual firewall services
On pve3, both proxmox-firewall.service and pve-firewall.service are currently active. This is an incorrect state. Only proxmox-firewall.service should be running. pve-firewall.service must be stopped and disabled.
systemctl stop pve-firewall
systemctl disable pve-firewall
Cluster Level¶
Sets default policies and shared rules that apply to all nodes.
Configured default policies:
policy_in = DROP
policy_forward = DROP
policy_out = ACCEPT
Host Level¶
Rules applied to the Proxmox node's own network stack.
IN: traffic destined for the host itself (e.g., DHCP, DNS served bydnsmasq)FORWARD: traffic passing through the host between VNets
Note
If traffic is destined for the host, it is IN. If it is passing through the host to another destination, it is FORWARD.
Guest Level¶
Rules applied to individual VMs or LXC containers.
IN: traffic entering the guestOUT: traffic leaving the guest
VNet Level¶
Rules scoped to traffic within the same VNet. Only FORWARD applies here. Traffic arriving from a different VNet is evaluated at the host/cluster FORWARD layer.
Security Groups¶
Three security groups are defined at the cluster level.
cluster_management¶
Protects Proxmox node-to-node communication and management access.
| Direction | Protocol | Source | Destination | Port | Purpose |
|---|---|---|---|---|---|
| IN | — | +dc/management_private |
— | — | All traffic from private management IPs |
| IN | tcp | — | +dc/management_public |
22, 8006 | SSH and Proxmox web UI |
| IN | udp | — | +dc/management_public |
5405, 5412 | Corosync cluster communication |
| IN | tcp | — | +dc/management_public |
22 | SSH access |
cluster_in¶
Handles host-level IN rules for DHCP and DNS services.
| Direction | Macro | Protocol | Source | Port | Purpose |
|---|---|---|---|---|---|
| IN | DHCPfwd | — | — | — | DHCP forwarding |
| IN | DNS | — | — | — | DNS (all sources) |
| IN | DNS | — | — | — | DNS (all sources, second rule) |
| IN | — | udp | +sdn/ingress-all |
67 | DHCP from ingress guests |
| IN | — | udp | +sdn/isolated-all |
67 | DHCP from isolated guests |
cluster_forward¶
Main transit rules for traffic passing through the host between VNets and to the internet.
| # | Protocol | Source | Destination | Port | Macro | Comment |
|---|---|---|---|---|---|---|
| — | — | — | DNS | +sdn/ingress-all |
— | DNS egress for ingress network |
| — | — | — | DNS | +sdn/isolated-all |
— | DNS egress for isolated network |
| — | icmp | +sdn/ingress-no-gateway |
— | — | — | ICMP egress from ingress guests |
| — | tcp | +sdn/ingress-all |
— | 80, 443 | HTTP | Internet egress from ingress |
| — | tcp | +sdn/isolated-all |
— | 80, 443 | — | Internet egress from isolated |
| — | tcp | +sdn/ingress-all |
+sdn/isolated-all |
8080, 8443 | — | ingress → isolated (backend) |
| — | tcp | +sdn/ingress-all |
+sdn/isolated-all |
8081 | — | ingress → isolated (alt port) |
| — | — | — | DNS | +sdn/k8group-all |
— | DNS egress for k8group |
| — | tcp | +sdn/k8group-all |
— | 80, 443 | — | Internet egress from k8group |
Application-specific rules (Guacamole → VM SSH, monitoring ports) are added as named rules within this group with descriptive comments.
Traffic Flows¶
1. Host-local: DHCP¶
Guest → Bridge → Host (IN) → dnsmasq → DHCP Offer → Guest
Handled by cluster_in security group, rules for ports 67/udp.
2. Host-local: DNS¶
Guests query the host dnsmasq on port 53, which forwards to upstream DNS.
Guest → dnsmasq (IN) → upstream DNS (FORWARD)
Handled by cluster_in (DNS macro for IN) and cluster_forward (DNS macro for FORWARD).
3. North-South: Internet → Ingress Guest¶
Inbound traffic arrives at the node's WAN IP and is forwarded to an ingress guest via DNAT.
Internet → Node WAN IP → DNAT → Ingress Guest
DNAT configuration (/etc/network/nat.nft):
table inet nat {
chain prerouting {
type nat hook prerouting priority -100;
iifname "vmbr0" ip protocol tcp tcp dport 80 dnat to 172.16.5.4:80
iifname "vmbr0" ip protocol tcp tcp dport 443 dnat to 172.16.5.4:443
iifname "vmbr0" ip protocol tcp tcp dport 8443 dnat to 172.16.5.4:8443
iifname "vmbr0" ip protocol tcp tcp dport 4317 dnat to 172.16.5.4:4317
iifname "vmbr0" ip protocol tcp tcp dport 4318 dnat to 172.16.5.4:4318
}
chain postrouting {
type nat hook postrouting priority 100;
# Guacamole → arc SSH via OVH private network
oifname "vmbr1" ip saddr 172.16.10.115 ip daddr 192.168.10.2 ip protocol tcp tcp dport 22206 snat to 192.168.10.3
}
}
172.16.5.4 is the ingress guest (reverse proxy). Inbound internet traffic on ports 80, 443, 8443, 4317, and 4318 is forwarded to it.
The postrouting SNAT rule enables Guacamole (at 172.16.10.115) to reach the arc server (192.168.10.2:22206) through the OVH private network interface (vmbr1), appearing as source 192.168.10.3.
4. East-West: Ingress → Isolated¶
The ingress reverse proxy forwards requests to backend services in the isolated network.
Ingress Guest → Host (FORWARD) → Isolated Guest
Handled by cluster_forward rules for +sdn/ingress-all → +sdn/isolated-all on ports 8080, 8443, 8081.
5. South-North: Guest → Internet (SNAT)¶
Guest traffic exits through the node via Simple Zone SNAT.
Guest → Host (FORWARD) → SNAT → Internet
Handled by cluster_forward rules for port 80, 443 egress per VNet.
NAT Service¶
DNAT rules must persist across reboots and Proxmox firewall reloads.
# /etc/systemd/system/nat.service
[Unit]
Description=Load NAT nftables rules after Proxmox firewall
After=proxmox-firewall.service
Wants=proxmox-firewall.service
PartOf=proxmox-firewall.service
[Service]
Type=oneshot
ExecStart=/usr/sbin/nft -f /etc/network/nat.nft
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable nat.service
systemctl start nat.service
Management IPSets¶
Two IPSets are defined at the datacenter level.
| IPSet | Purpose |
|---|---|
management_private |
Node-to-node cluster communication addresses |
management_public |
External addresses allowed to reach the Proxmox management interface |
Warning
Define and verify these IPSets before enabling any DROP policies to avoid losing host access.
Operational Checks¶
# 1. Firewall services — only proxmox-firewall must be active
systemctl is-active proxmox-firewall.service # must be active
systemctl is-active pve-firewall.service # must be inactive
# 2. IPv4 forwarding
sysctl net.ipv4.ip_forward # must return 1
# 3. nftables
systemctl status nftables # must be active
# 4. dnsmasq (zone-based)
systemctl status dnsmasq # must be inactive
systemctl status dnsmasq@simple.service # must be active
# 5. NAT service
systemctl status nat.service # must be active