I wanted to understand which path a connection was taking, so I started at the Ethernet uplink and followed it through Proxmox. That gave me a clearer way to explain where Sophos applies its rules, and it turned a list of containers into something I could reason about.
This entry is that walkthrough, using my lab record of 8 November 2026. Addresses, MAC addresses and identifiers are left out; bridge names, VLAN IDs and guest IDs are kept because they help you follow the path.
A laptop behind CGNAT
The whole lab runs on one laptop: an Acer Aspire A514 with an i5-1135G7 and about 36 GiB of RAM, running Proxmox VE 9.2 (the version my record reports). Upstream is a shared ISP router behind carrier-grade NAT, so I cannot put it in bridge mode or forward ports into the lab. That one constraint explains a lot of the design: the Minecraft server reaches players through VPS tunnels, and remote access uses connectors that dial out.
Three bridges, one firewall
A Proxmox bridge behaves like a virtual switch. Anything attached to the same bridge can reach anything else on it without passing a firewall, so which bridge a guest sits on matters as much as its rules.
- vmbr0, the WAN bridge, sits on the laptop’s onboard NIC. Sophos’s WAN port is here, and so is the host’s recovery management address.
- vmbr1, the LAN bridge, sits on a USB Ethernet adapter and carries the legacy untagged LAN. Most containers, the Proxmox web UI and both private access connectors live here.
- vmbr2, the VLAN trunk, is an internal VLAN-aware bridge attached to a trunk port on Sophos. Seven VLANs are defined on it: 5 MGMT, 10 SERVERS, 20 LAB_TEST, 30 TRUSTED, 40 SOC, 50 DMZ and 60 IOT_GUEST.
Sophos Firewall runs as VM 102 with one virtual NIC on each bridge. It is the gateway for the LAN and for every VLAN, with IPS and third-party threat feeds enabled.
- WAN
- Laptop uplink
WAN bridge
- Sophos Firewall
LAN bridge
Outside traffic crosses the WAN bridge and Sophos before it reaches the LAN bridge. Once two guests share a bridge, their traffic does not cross Sophos at all, which is why I do not claim that everything passes through the firewall.
Open the full lab consoleWhat the VLANs actually hold
All seven VLANs are configured on Sophos Port3, the interface that sits on vmbr2, and each has its own /24 gateway. The legacy untagged LAN on vmbr1 is a separate network with its own addressing. Most guests still live on that LAN; the SafeLine container (LXC 202, stopped) is tagged on VLAN 20, and VLAN 50 is the DMZ where this journal now runs, in CT 410. No access point or switch port tags these VLANs, so they are internal guest networks inside the laptop, not Wi-Fi or wired segments yet.
| VLAN | Name | Gateway | Used for |
|---|---|---|---|
| 5 | MGMT | /24 on Sophos | Management interfaces |
| 10 | SERVERS | /24 on Sophos | Server guests |
| 20 | LAB_TEST | /24 on Sophos | Thesis and Kali environment; SafeLine (LXC 202) is tagged here |
| 30 | TRUSTED | /24 on Sophos | Trusted devices |
| 40 | SOC | /24 on Sophos | Detection tools |
| 50 | DMZ | /24 on Sophos | This journal’s origin, CT 410 (confirmed) |
| 60 | IOT_GUEST | /24 on Sophos | Guests; internal only, no AP or switch tags it |
Lease example, VLAN 20: gateway 192.0.2.1/24 · lease 192.0.2.57 · DNS 192.0.2.1
Example addresses (RFC 5737), not my lab’s. The legacy untagged LAN on vmbr1 is separate and not shown.
Where an exception has to sit
Sophos reads its rules top to bottom and stops at the first one that matches. That makes the order a design decision, not a list:
- 1DMZ_SITE_UPDATESallowDMZ (network object) → WAN · HTTP, HTTPS, custom Cloudflare service (TCP/UDP 7844)Lets the DMZ reach the internet for package updates and for the Cloudflare tunnel.The one exception the DMZ needs, placed above the drops so it is evaluated first. Its source is the whole DMZ object, not only CT 410.
- 2DROP_NEW_VLANSdrop · logthe newly added VLAN zones (list truncated on screen) → Any · AnyDrops and logs traffic from the new VLANs until each one has its own allow rule.A new VLAN starts closed. The screenshot cuts the source list short, so I only claim the zones I can read.
- 3DROP_LAB20_OUTBOUNDdrop · logLAB_TEST (VLAN 20) → Any · AnyDrops and logs everything leaving the thesis and Kali network.Test guests must not reach the internet or my management interfaces.
- 4Allow_Minecraft_WireGuardallowMinecraft LXC host object → the VPS host object · custom WireGuard serviceLets the Minecraft server’s WireGuard tunnel reach the public VPS.A scoped exception for one host and one service.
- 5LAN_TO_WANallowlegacy LAN → WAN · AnyLets the legacy untagged LAN reach the internet.The broad rule goes low, so narrower rules above it decide first.
- 6Automatic MTA policygeneratedas generated → as generated · SMTP servicesCreated by Sophos for its mail transfer agent; accept, as shown.Generated by the firewall; I left it as it was created.
- 7Implicit dropdropAny → Any · AnyAnything no rule above matched is dropped.The default when nothing allows the traffic.
A scoped exception only works if Sophos evaluates it before an applicable drop. DMZ_SITE_UPDATES sits first for that reason. If VLAN 20 ever needs DNS, that allow rule goes above DROP_LAB20_OUTBOUND, or the drop matches first and the allow never runs.
DMZ_SITE_UPDATES sits first because it is the exception the DMZ needs: HTTP, HTTPS and the Cloudflare tunnel service out to the WAN. The two drops come next, scoped to the networks I want closed: new VLANs, and VLAN 20’s outbound traffic. Allow_Minecraft_WireGuard sits below them and still works, because the Minecraft server’s tunnel comes from the LAN, which neither drop matches. The order would matter the moment I wanted VLAN 20 to reach one thing, say a DNS server: that allow has to go above DROP_LAB20_OUTBOUND, or the drop matches first and the allow never runs. I tested what the VLAN 20 drop actually matched in the NOC case.
What is running
| ID | Type | Role | State when recorded | Note |
|---|---|---|---|---|
| 100 | LXC | template-lxc | Stopped on 8 Nov 2026 | |
| 102 | VM | Sophos Firewall | Running on 8 Nov 2026 | |
| 201 | LXC | Older Cloudflare Tunnel | Stopped on 8 Nov 2026 | |
| 202 | LXC | SafeLine | Stopped on 8 Nov 2026 | tagged VLAN 20 |
| 203 | LXC | Cloudflare Tunnel | Running on 8 Nov 2026 | |
| 204 | LXC | NetBird routing peer | Running on 8 Nov 2026 | |
| 205 | LXC | wazuh | Stopped on 8 Nov 2026 | stopped on 1 Nov 2026; not listed on 8 Nov |
| 206 | LXC | BunkerWeb / private proxy | Running on 8 Nov 2026 | proxy routes not verified |
| 207 | LXC | Minecraft and WireGuard tunnels | Running on 8 Nov 2026 | |
| 208 | LXC | Twingate connector | Running on 8 Nov 2026 | |
| 209 | LXC | NextDNS forwarder | Running on 8 Nov 2026 | |
| 301 | VM | Kali Red (exercise VM) | Stopped on 8 Nov 2026 | |
| 302 | VM | Kali Blue (exercise VM) | Stopped on 8 Nov 2026 | |
| 410 | LXC | homelab-journal (Caddy + cloudflared) | Running on 8 Nov 2026 | VLAN 50 DMZ, confirmed; added after the 8 Nov record |
Purpose: list VMs and their state
I wanted to confirm which VMs were up before tracing anything, because a stopped firewall would make the rest of the walkthrough meaningless.
qm listShow output
VMID NAME STATUS
102 sophos running
301 kali-red stopped
302 kali-blue stopped102 sophosthe gateway for every bridge; if this is down, nothing routes
Laid out from my lab record rather than pasted from a session. Containers have their own command, pct list.
Purpose: see which bridge each Sophos NIC uses
To explain where Sophos applies its rules, I needed to know which bridge each of its ports was on.
qm config 102 | grep ^netShow output
net0: virtio=<mac>,bridge=vmbr1
net1: virtio=<mac>,bridge=vmbr0
net2: virtio=<mac>,bridge=vmbr2bridge=vmbr2the trunk: no tag on the NIC, so every VLAN reaches Sophos's trunk port
MAC addresses redacted. The mapping of NICs to bridges is from my record.
Ways in that are not the front door
Because of CGNAT, every way in starts with an outbound connection.
The Cloudflare Tunnel in LXC 203 is running and kept for recovery access; an older tunnel, LXC 201, is stopped. NetBird in LXC 204 is a routing peer and the Twingate connector in LXC 208 runs as a native service; both grant the Proxmox and Sophos web UIs to an admin group only. I compare them in Who can reach what.
The other containers on the LAN bridge
- SafeLine in LXC 202 is stopped on VLAN 20.
- BunkerWeb in LXC 206 is running with its dashboard reachable. Its proxy routes are not verified, so I do not count it as protecting anything on this map.
- NextDNS in LXC 209 is the DNS forwarder for the LAN. How that path works is in What my DNS logs can and cannot tell me.
What the map is good for
Following one connection from the cable inward gave me a short answer to the question of where rules apply: Sophos applies its rules where traffic crosses from one bridge or VLAN to another, and nowhere else. Two guests on the same bridge talk directly, and Sophos never sees it. The lab console draws the same map with the other paths on it.
Takeaway: before trusting a firewall rule to protect a service, trace which bridge each end sits on. If they share one, the rule is not in the path.
Skills used
- Reading Linux bridge and VLAN layout on Proxmox
- Working out which hops a firewall can and cannot see
- Mapping private access paths onto the network
Tools in this entry
Proxmox VE In my lab
Official documentation for Proxmox VE- Sophos Firewall In my lab
Official documentation for Sophos Firewall Cloudflare Tunnel In my lab
Official documentation for Cloudflare Tunnel- NetBird In my lab
Official documentation for NetBird - Twingate In my lab
Official documentation for Twingate NextDNS In my lab
Official documentation for NextDNS