30 KiB
UDM Pro SE Zone-Based Firewall Migration
1. Scope
This document captures the migration of the existing UDM Pro SE network from its current VLAN layout to Zone-Based Firewalling with custom zones.
Public ingress is via Netbird, not WAN port-forwarding. A Netbird VPS is the public face; a Netbird agent runs on the Caddy host inside DMZ. No inbound listeners on the home WAN.
Public-safe notation:
- Internal RFC1918 represented as
10.0.<vlan>.0/24. - Internal domain represented as
home.example.com.
Current deployment:
- Technitium runs in Docker on the Raspberry Pi at
10.0.10.5on Office VLAN 10. - Office VLAN 10 is assigned to
TRUSTED, so Technitium is a trusted-zone service. Explicit DNS allow rules are still required from restricted zones.
Required rollout order
Complete the migration in this dependency order:
- Technitium DNS first: make
10.0.10.5authoritative for the internal zone and verify direct DNS queries from every current VLAN. - Caddy and DMZ second: create VLAN 15, move Caddy to
10.0.15.5, update Technitium'scaddyrecord, and verify reverse-proxy and Netbird traffic. - Remaining zones last: assign FAMILY, MEDIA, IOT, and GUEST policies one zone at a time. Verify DHCP-provided DNS before enabling DNS DNAT for that zone.
Do not enable DNS DNAT or terminal drop rules until the direct DNS and Caddy checks for the affected zone pass.
2. Design Goals
- Preserve all 6 current UDM Pro SE VLANs and add only VLAN 15 for the Caddy DMZ.
- Use custom UniFi zones built from scratch — avoid implicit behavior in the default
IoT/Hotspotzones, and avoid UniFi 9.x's intra-zone default-allow turning multi-VLAN zones into trust footguns. - Keep the Proxmox host and all app VMs on VLAN 10 (no need to trunk additional VLANs to the hypervisor — Caddy lives on a dedicated host).
- Isolate Caddy in its own DMZ zone. Caddy is the only thing exposed to the Netbird overlay (the public attack surface), so a Caddy compromise cannot pivot to TRUSTED without traversing explicit firewall pinholes.
- Restrict MEDIA (TV/PS5/Apple TV) and FAMILY devices to Caddy + DNS + internet only — no access to the UDM gateway, NAS, workstations, or each other.
- Default-deny posture for IoT and Guest, with explicit allows only for DNS + internet.
- Use DNAT redirect (not block) for DNS enforcement on non-TRUSTED zones — silently captures hardcoded resolvers without breaking Smart TV / Chromecast firmware that fails on source-IP mismatches.
3. Current → Target VLAN Map
| VLAN | VLAN ID | CIDR | Status | Target Zone | Notes |
|---|---|---|---|---|---|
| LAN | 1 | 10.0.1.0/24 | Kept | TRUSTED | UDM, switch, AP, and infrastructure management |
| Office | 10 | 10.0.10.0/24 | Kept | TRUSTED | Workstations, Proxmox apps, and Technitium |
| DMZ | 15 | 10.0.15.0/24 | New | DMZ | Dedicated Caddy host (Netbird agent here) |
| Family | 20 | 10.0.20.0/24 | Kept | FAMILY | Family devices |
| IoT | 30 | 10.0.30.0/24 | Kept | IOT | Custom zone, not UniFi default IoT zone |
| Media | 40 | 10.0.40.0/24 | Kept | MEDIA | TV/PS5/Apple TV, restricted to Caddy + DNS |
| Guest | 50 | 10.0.50.0/24 | Kept | GUEST | Custom zone, not UniFi default Hotspot |
Net: 6 current VLANs → 7 target VLANs. VLAN 15 is the only new network.
LAN and Office intentionally share TRUSTED, where unrestricted intra-zone
routing is acceptable.
4. Target Zones
UniFi 9.x permits custom zones. The defaults (Internal, IoT, Hotspot, DMZ, VPN) are not used — they stay empty. Gateway and External are mandatory and cannot be removed.
Note on UniFi intra-zone behavior: UniFi 9.x's default-allow inside a zone is not configurable. Two VLANs in the same zone can freely initiate L3 unicast to each other regardless of subnet boundaries. This design therefore puts each policy boundary in its own zone — no aggregating FAMILY+MEDIA or IOT+GUEST into shared zones.
| Zone | Type | VLAN(s) | Members | Consolidates |
|---|---|---|---|---|
| TRUSTED | Custom | 1 (10.0.1.0/24), 10 (10.0.10.0/24) |
UDM, switch + AP mgmt, workstations, Proxmox host + app VMs, UNAS Pro, and Technitium DNS (10.0.10.5) |
LAN + Office |
| DMZ | Custom | 15 (10.0.15.0/24) |
Dedicated Caddy host (10.0.15.5) running Netbird agent |
New |
| FAMILY | Custom | 20 (10.0.20.0/24) |
Family phones / tablets / laptops | Family |
| MEDIA | Custom | 40 (10.0.40.0/24) |
PS5, Samsung TV, Apple TV | Media |
| IOT | Custom | 30 (10.0.30.0/24) |
Smart-home devices, Zigbee coordinator (SLZB-06U) | IoT |
| GUEST | Custom | 50 (10.0.50.0/24) |
Visitor devices | Guest |
Gateway |
Built-in (mandatory) | — | UDM Pro SE itself | — |
External |
Built-in (mandatory) | — | WAN | — |
5. Address References in UniFi 10.4.57
UniFi Network 10.4.57 does not expose reusable named IP-address groups for
zone-based policies. The names CADDY, DNS, BACKEND_APPS,
IOT_BACKENDS, and RFC1918 in this document are design labels only.
There is nothing to create in UniFi at this step. After completing the zones in §4, verify that the addresses listed below are correct for your devices, then continue with the migration sequence in §11. Create each firewall policy only when its rollout phase tells you to do so.
Step 5 is complete when you have confirmed:
- Caddy will use static/reserved address
10.0.15.5. - Technitium DNS uses static/reserved address
10.0.10.5. - The Caddy upstream addresses and ports in §5.4 match the current
docker-compose-files/caddy/Caddyfile_template. - SLZB-06U uses
10.0.30.5and the Home Automation host uses10.0.30.6. - The RFC1918 CIDRs are recorded as
10.0.0.0/8,172.16.0.0/12, and192.168.0.0/16.
Do not use Settings → Policy Engine → Objects. That page applies Secure, Route, and QoS policies to selected clients or networks; it does not create firewall address objects.
How the policy tables map to the Create Policy panel
When a later phase tells you to create a policy, open Settings → Policy Engine → Policy Table → Create New Policy. In the right-side Create Policy panel, use the table row as follows:
| Table column/value | Exact control to use in the Create Policy panel |
|---|---|
| Policy name | Keep Firewall selected at the top, then type the value into Name. |
| Source zone | Open the Source Zone dropdown and select the named custom zone, such as MEDIA or DMZ. |
Source IP = Any |
Under Source Zone, keep the Any radio button selected. This means every client in the selected source zone. |
| Source IP = an address | Under Source Zone, select the IP radio button. An address-entry field appears; enter the listed source IP. Do not select Device, Network, MAC, or Identity. |
Source port = Any |
In the Port row inside Source Zone, keep Any selected. All policies in this guide use source port Any. |
| Action = Allow/Block | In the separate Action card, select Allow or Block exactly as listed. Do not use Reject unless the guide explicitly says so. |
| Destination zone | Open the Destination Zone dropdown and select the listed zone. Select Any only for a policy whose destination zone is explicitly Any. |
| Destination IP = an address/CIDR | Under Destination Zone, select the IP radio button. Enter every listed IP address or CIDR into the field that appears. Do not select App, Domain, or Region. |
Destination IP = Any |
Under Destination Zone, keep the Any radio button selected. |
| Destination ports | In the Port row inside Destination Zone, select Specific and enter the listed destination port or ports. Use List only if you have separately created a reusable named port list. |
| IP Version | Select IPv4 because this guide uses IPv4 addresses. |
| Protocol = TCP | Select TCP. |
| Protocol = TCP and UDP | Select TCP/UDP. |
| Protocol/ports = Any | Select protocol All and leave both source and destination ports as Any. |
| Connection State | Keep All selected unless a policy explicitly says otherwise. |
| Match IPsec | Leave disabled. |
| Syslog Logging | Enable temporarily while testing if you want match logs; otherwise leave disabled. |
| Schedule | Keep Always selected. |
After filling the panel, click Add Policy. In the Policy Table, use Reorder to keep specific allow policies above broad block policies.
Do not create or enable all policies below at once. Create the DNS and Caddy allows first, test them, and add broad block policies only at the end of each zone's rollout.
5.1 Worked example: ALLOW MEDIA to CADDY web
Use this example to learn how the columns in the policy tables below map to
the Create Policy panel. This policy permits devices in the MEDIA zone to
reach only Caddy at 10.0.15.5 on HTTP and HTTPS.
- Open Settings → Policy Engine → Policy Table → Create New Policy.
- At the top of Create Policy, select Firewall.
- Set Name to
ALLOW MEDIA to CADDY web. - Under Source Zone:
- Select zone
MEDIA. - Select Any for the source matcher.
- Leave source Port set to Any.
- Select zone
- Under Action, select Allow.
- Under Destination Zone:
- Select zone
DMZ. - Select IP, then enter
10.0.15.5. - Under Port, select Specific, then enter destination ports
80and443.
- Select zone
- Under IP Version, select IPv4.
- Under Protocol, select TCP.
- Under Connection State, leave All selected. UniFi's default return traffic policy permits replies to this allowed connection.
- Leave Match IPsec disabled.
- Enable Syslog Logging during rollout if you want to confirm policy matches; it may be disabled after validation.
- Under Schedule, select Always.
- Optionally set Description to
Allow Media clients to reach the Caddy reverse proxy on HTTP/HTTPS. - Click Add Policy.
- In the Policy Table, ensure this allow policy is above
BLOCK MEDIA to RFC1918and any terminal MEDIA block policy.
The finished policy should read approximately:
Name: ALLOW MEDIA to CADDY web
Action: Allow
Source Zone: MEDIA
Source: Any
Source Port: Any
Destination Zone: DMZ
Destination IP: 10.0.15.5
Destination Ports: 80, 443
IP Version: IPv4
Protocol: TCP
Connection State: All
Schedule: Always
For every remaining policy in this guide, follow the same panel order and replace only the values shown in that policy's table row.
5.2 Policies targeting Caddy (CADDY)
Create both policies:
| Policy name | Action | Source zone | Source IP | Destination zone | Destination IP | Protocol / destination ports |
|---|---|---|---|---|---|---|
ALLOW FAMILY to CADDY web |
Allow | FAMILY |
Any | DMZ |
10.0.15.5 |
TCP 80,443 |
ALLOW MEDIA to CADDY web |
Allow | MEDIA |
Any | DMZ |
10.0.15.5 |
TCP 80,443 |
These policies do not clash with zones. The zone selects the broad path, such
as MEDIA to DMZ; destination IP 10.0.15.5 narrows that path to Caddy
only.
5.3 Policies targeting Technitium DNS (DNS)
Create one allow policy for each listed source zone:
| Policy name | Action | Source zone | Source IP | Destination zone | Destination IP | Protocol / destination port |
|---|---|---|---|---|---|---|
ALLOW DMZ to DNS |
Allow | DMZ |
Any | TRUSTED |
10.0.10.5 |
TCP and UDP 53 |
ALLOW FAMILY to DNS |
Allow | FAMILY |
Any | TRUSTED |
10.0.10.5 |
TCP and UDP 53 |
ALLOW MEDIA to DNS |
Allow | MEDIA |
Any | TRUSTED |
10.0.10.5 |
TCP and UDP 53 |
ALLOW IOT to DNS |
Allow | IOT |
Any | TRUSTED |
10.0.10.5 |
TCP and UDP 53 |
ALLOW GUEST to DNS |
Allow | GUEST |
Any | TRUSTED |
10.0.10.5 |
TCP and UDP 53 |
5.4 Caddy to TRUSTED backend policies (BACKEND_APPS)
Create these policies from the current
docker-compose-files/caddy/Caddyfile_template. Keep separate policies for
different destination ports so one backend is not accidentally allowed to use
another backend's port.
Every policy in this table uses:
- Action:
Allow - Source zone:
DMZ - Source IP:
10.0.15.5 - Destination zone:
TRUSTED - Protocol: TCP
| Policy name | Destination IP | Destination port(s) | Current upstream purpose |
|---|---|---|---|
ALLOW CADDY to LAN web devices |
10.0.1.5, 10.0.1.8 |
80 |
Lite-8 and TP-Link switch UIs |
ALLOW CADDY to Technitium UI |
10.0.10.5 |
5380 |
Technitium admin UI |
ALLOW CADDY to NAS apps |
10.0.10.24 |
5000,5055,6767,7878,8000,8080,8096,8191,8282,8989,9696 |
NAS and media-stack apps |
ALLOW CADDY to PBS |
10.0.10.8 |
8007 |
Proxmox Backup Server |
ALLOW CADDY to Proxmox |
10.0.10.10 |
8006 |
Proxmox |
ALLOW CADDY to Bento PDF |
10.0.10.6 |
8080 |
Bento PDF |
ALLOW CADDY to Beszel |
10.0.10.13 |
8090 |
Beszel |
ALLOW CADDY to Drawio |
10.0.10.9 |
8080 |
Drawio |
ALLOW CADDY to Gramps |
10.0.10.17 |
5000 |
Gramps |
ALLOW CADDY to Immich |
10.0.10.15 |
2283 |
Immich |
ALLOW CADDY to IT Tools |
10.0.10.7 |
80 |
IT Tools |
ALLOW CADDY to Paperless |
10.0.10.11 |
8000 |
Paperless-ngx |
ALLOW CADDY to Speedtest |
10.0.10.16 |
80 |
Speedtest |
ALLOW CADDY to YTD |
10.0.10.14 |
3033 |
MeTube/YTD |
Delete or disable a policy when its matching Caddy upstream is removed.
5.5 Caddy to IOT backend policies (IOT_BACKENDS)
Both policies use action Allow, source zone DMZ, source IP 10.0.15.5,
destination zone IOT, and protocol TCP:
| Policy name | Destination IP | Destination port(s) | Purpose |
|---|---|---|---|
ALLOW CADDY to Zigbee |
10.0.30.5 |
80 |
SLZB-06U web UI |
ALLOW CADDY to Home Automation |
10.0.30.6 |
1880,8080,8123 |
Node-RED, Zigbee2MQTT, and Home Assistant |
5.6 RFC1918 block policies
After the specific allow policies above work, create one RFC1918 block policy
for each restricted source zone. For each policy, set destination zone to
Any and enter all three destination CIDRs:
10.0.0.0/8172.16.0.0/12192.168.0.0/16
| Policy name | Action | Source zone | Source IP | Destination zone | Destination IPs | Protocol / ports |
|---|---|---|---|---|---|---|
BLOCK DMZ to RFC1918 |
Block | DMZ |
Any | Any | All three RFC1918 CIDRs | Any |
BLOCK FAMILY to RFC1918 |
Block | FAMILY |
Any | Any | All three RFC1918 CIDRs | Any |
BLOCK MEDIA to RFC1918 |
Block | MEDIA |
Any | Any | All three RFC1918 CIDRs | Any |
BLOCK IOT to RFC1918 |
Block | IOT |
Any | Any | All three RFC1918 CIDRs | Any |
BLOCK GUEST to RFC1918 |
Block | GUEST |
Any | Any | All three RFC1918 CIDRs | Any |
Policy order is mandatory: place the specific Caddy, DNS, and backend allow policies above these RFC1918 block policies. Otherwise, the broad RFC1918 blocks will match and drop the intended traffic first.
6. Public Ingress via Netbird
Home WAN has no inbound listeners. Public traffic flow:
Internet → Netbird VPS (public face) → WireGuard mesh → Caddy host (Netbird agent, VLAN 15)
↓
reverse-proxies to BACKEND_APPS on VLAN 10
Implications:
- No port-forward rule on the UDM.
- No
External → DMZfirewall rule needed. - Caddy receives app traffic on its Netbird tunnel interface (
wt0), not on its VLAN 15 interface from WAN. - The DMZ zone still earns its keep: the Netbird tunnel is the public attack surface. Compromise of the VPS or Caddy must not pivot to TRUSTED except via explicit pinholes.
- Caddy needs outbound WAN for Netbird signal/STUN + ACME DNS-01 — handled by the standing
DMZ → Externalallow.
TLS termination model assumed: TLS passthrough at the VPS (home Caddy handles certs, sees real SNI). If the VPS terminates TLS instead, Caddy receives plaintext over the tunnel and does not need outbound :53 to public DNS providers.
7. Zone Policy Matrix
Policy objective: default drop. Apply each table top-to-bottom and include the listed terminal block rule so the design does not depend on an implicit UniFi default.
7.1 TRUSTED
| # | From | To | Action | Protocol/Port | Purpose |
|---|---|---|---|---|---|
| 1 | TRUSTED | TRUSTED | Allow | any | Intra-zone (Proxmox ↔ apps ↔ NAS ↔ workstations) |
| 2 | TRUSTED | any | Allow | any | Admin path; Trusted is the management zone |
DNS for TRUSTED: DHCP option 6 → 10.0.10.5. This design does not force DNAT
for TRUSTED because workstations are trusted. Office clients and Technitium
share 10.0.10.0/24, so their DNS traffic does not traverse the UDM Pro SE and
cannot be redirected there. LAN VLAN 1 traffic is routed, but remains
unenforced by policy.
7.2 DMZ
| # | From | To | Action | Protocol/Port | Purpose |
|---|---|---|---|---|---|
| 1 | DMZ (CADDY) |
TRUSTED (BACKEND_APPS) |
Allow | TCP, app-specific ports | Caddy → backend pinhole |
| 2 | DMZ | DNS |
Allow | UDP/TCP 53 | Internal name resolution |
| 3 | DMZ (CADDY) |
IOT (IOT_BACKENDS) |
Allow | TCP 80, 1880, 8080, 8123 | Proxied IoT and Home Automation UIs |
| 4 | DMZ | External |
Allow | any | Netbird signal/STUN + ACME DNS-01 + Docker pulls |
| 5 | DMZ | Gateway |
Block | any | No UDM admin from Caddy |
| 6 | DMZ | RFC1918 (minus explicit pinholes) |
Block | any | No lateral movement |
| 7 | DMZ | any | Block | any | Drop all other unmatched traffic |
7.3 MEDIA
| # | From | To | Action | Protocol/Port | Purpose |
|---|---|---|---|---|---|
| 1 | MEDIA | CADDY |
Allow | TCP 80, 443 | Split-horizon access to internal services |
| 2 | MEDIA | DNS |
Allow | UDP/TCP 53 | Name resolution (via DNAT redirect — see §8) |
| 3 | MEDIA | External |
Allow | any | Internet (streaming, PSN, updates) |
| 4 | MEDIA | Gateway |
Block | any | No UDM admin |
| 5 | MEDIA | RFC1918 |
Block | any | No access to TRUSTED/FAMILY/IOT/GUEST |
| 6 | MEDIA | any | Block | any | Drop all other unmatched traffic |
7.4 FAMILY
| # | From | To | Action | Protocol/Port | Purpose |
|---|---|---|---|---|---|
| 1 | FAMILY | CADDY |
Allow | TCP 80, 443 | Split-horizon access to internal services |
| 2 | FAMILY | DNS |
Allow | UDP/TCP 53 | Name resolution (via DNAT redirect — see §8) |
| 3 | FAMILY | External |
Allow | any | Internet |
| 4 | FAMILY | Gateway |
Block | any | No UDM admin |
| 5 | FAMILY | RFC1918 |
Block | any | No access to TRUSTED/MEDIA/IOT/GUEST |
| 6 | FAMILY | any | Block | any | Drop all other unmatched traffic |
7.5 IOT
| # | From | To | Action | Protocol/Port | Purpose |
|---|---|---|---|---|---|
| 1 | IOT | IOT | Allow | any | Intra-zone (hub ↔ devices) |
| 2 | IOT | DNS |
Allow | UDP/TCP 53 | Name resolution (via DNAT redirect — see §8) |
| 3 | IOT | External |
Allow | any | Cloud updates / vendor services |
| 4 | IOT | RFC1918 |
Block | any | No lateral movement |
| 5 | IOT | Gateway |
Block | any | No UDM admin |
| 6 | IOT | any | Block | any | Drop all other unmatched traffic |
Home Assistant, Zigbee2MQTT, Node-RED, and SLZB-06U remain inside IOT. Their east-west traffic is permitted by the IOT intra-zone rule.
| # | From | To | Action | Protocol/Port | Purpose |
|---|---|---|---|---|---|
| - | IOT (10.0.30.6) |
IOT (10.0.30.5) |
Allow | TCP 6638 | Zigbee2MQTT ↔ Zigbee coordinator |
7.6 GUEST
| # | From | To | Action | Protocol/Port | Purpose |
|---|---|---|---|---|---|
| 1 | GUEST | DNS |
Allow | UDP/TCP 53 | Name resolution (via DNAT redirect — see §8) |
| 2 | GUEST | External |
Allow | any | Internet |
| 3 | GUEST | RFC1918 |
Block | any | Total isolation from internal |
| 4 | GUEST | Gateway |
Block | any | No UDM admin |
| 5 | (network setting) | — | Client isolation ON | — | Guest-to-guest blocked at L2 |
| 6 | GUEST | any | Block | any | Drop all other unmatched traffic |
7.7 Compact zone-to-zone matrix
| ↓ From / To → | TRUSTED | DMZ | FAMILY | MEDIA | IOT | GUEST | Gateway | External |
|---|---|---|---|---|---|---|---|---|
| TRUSTED | ✅ any | ✅ any | ✅ any | ✅ any | ✅ any | ✅ any | ✅ any | ✅ any |
| DMZ | ⚠ pinhole¹ | — | ❌ | ❌ | ⚠ proxy pinholes | ❌ | ❌ | ✅ any |
| FAMILY | ⚠ DNS² | ⚠ proxy³ | — | ❌ | ❌ | ❌ | ❌ | ✅ any |
| MEDIA | ⚠ DNS² | ⚠ proxy³ | ❌ | — | ❌ | ❌ | ❌ | ✅ any |
| IOT | ⚠ DNS² | ❌ | ❌ | ❌ | ✅ any | ❌ | ❌ | ✅ any |
| GUEST | ⚠ DNS² | ❌ | ❌ | ❌ | ❌ | ❌ (isolated) | ❌ | ✅ any |
Legend: ✅ allow · ❌ block · ⚠ allow only listed flows.
Footnotes:
- DMZ → TRUSTED pinhole: Caddy IP only, targeting
BACKEND_APPSIPs on app-specific TCP ports. Include only routes actually published indocker-compose-files/caddy/Caddyfile_template, including Technitium TCP 5380 whendns01is enabled. Drop all other DMZ → TRUSTED traffic. - DNS via DNAT redirect (§8) — destination
10.0.10.5:53on UDP/TCP only; all other:53is silently rewritten. - Proxy access: TCP
80, 443to Caddy IP only.
8. DNS Handling — DNAT Redirect Strategy
Use UDM Pro SE Destination NAT (DNAT) policies to redirect DNS traffic to
Technitium. This is invisible to the client (no source-IP mismatch error) and
replaces the brittle "block :53 to !DNS" approach.
Why DNAT instead of block
A hard :53 block breaks devices that hardcode 8.8.8.8 (some Smart TVs, Chromecasts). DNAT silently rewrites those queries to Technitium. Client thinks it got a reply from 8.8.8.8; the UDM's reverse-NAT puts the masked source IP back on the return packet.
Why TRUSTED does not use DNAT in this design
TRUSTED does not get a DNAT rule because its workstations are trusted, not
because every TRUSTED client can be intercepted consistently. Office clients
share a subnet with Technitium, so their DNS traffic does not cross the UDM Pro
SE. LAN VLAN 1 queries do cross it, but enforcing only part of TRUSTED adds
complexity without matching the policy objective.
Rules
| Zone | DHCP option 6 | DNAT rule | :53 block |
|---|---|---|---|
| TRUSTED | 10.0.10.5 |
None | None (trust workstations) |
| DMZ | 10.0.10.5 |
— | None (Caddy needs ACME DNS-01 out to WAN) |
| FAMILY | 10.0.10.5 |
dst !10.0.10.5 UDP/TCP 53 → 10.0.10.5:53 |
— (DNAT replaces) |
| MEDIA | 10.0.10.5 |
same as FAMILY | — |
| IOT | 10.0.10.5 |
same as FAMILY | — |
| GUEST | 10.0.10.5 |
same as FAMILY | — |
Practically, this is one DNAT entry with source = group of {FAMILY, MEDIA, IOT, GUEST}.
Technitium prerequisite — Allowed Networks
The repository compose template defaults TECHNITIUM_RECURSION_ACL to include
RFC1918 networks. Verify that the running server still permits every client
subnet under Settings → DNS Server → Allowed Networks (ACL), especially if
the UI configuration has overridden the container startup defaults:
10.0.10.0/24(TRUSTED)10.0.1.0/24(TRUSTED infrastructure)10.0.15.0/24(DMZ — Caddy split-horizon lookups)10.0.20.0/24(FAMILY)10.0.30.0/24(IOT)10.0.40.0/24(MEDIA)10.0.50.0/24(GUEST)
9. Caddy Host Hardening (VLAN 15)
The Caddy host is a dedicated machine (or dedicated VM on its own hypervisor — not the main Proxmox host). It is the only member of DMZ.
- Untagged access port on the switch — feeding port is access-mode VLAN 15, no trunk.
- SSH source-locked to TRUSTED:
ufw allow from 10.0.1.0/24 to any port 22 proto tcp ufw allow from 10.0.10.0/24 to any port 22 proto tcp ufw deny 22/tcp - Lock down
wt0(Netbird interface) — only Caddy listening ports accept tunnel traffic:ufw allow in on wt0 to any port 80,443 proto tcp ufw deny in on wt0 - Permit intended LAN-side proxy clients on Caddy's VLAN interface:
ufw allow from 10.0.1.0/24 to any port 80,443 proto tcp ufw allow from 10.0.10.0/24 to any port 80,443 proto tcp ufw allow from 10.0.20.0/24 to any port 80,443 proto tcp ufw allow from 10.0.40.0/24 to any port 80,443 proto tcp - No Docker socket mount.
docker-compose.ymlfor Caddy must not include/var/run/docker.sock:/var/run/docker.sock. Prevents container-escape → host root via socket abuse. - Drop capabilities:
cap_drop: [ALL]+cap_add: [NET_BIND_SERVICE]in the Caddy compose service. Container can bind 80/443, nothing else. - Netbird agent runs as a host systemd service, not inside the Caddy container. Keeps tunnel state out of the proxy blast radius.
- ACME via DNS-01 only — no inbound HTTP listener needed (and none possible without port-forward). Caddy needs outbound
:443to your DNS provider's API.
10. Wi-Fi SSID → VLAN Mapping
| SSID | VLAN | Zone | Purpose |
|---|---|---|---|
home-trusted |
10 | TRUSTED | Personal phones, laptops, workstations |
home-family |
20 | FAMILY | Family devices |
home-iot |
30 | IOT | Smart-home devices (2.4 GHz typically) |
home-guest |
50 | GUEST | Visitors — client isolation on |
Media devices (TV, PS5, Apple TV) are wired to TL-SG108 with port-based VLAN 40 tagging on the uplink to the Lite-8.
11. Migration Sequence
Phase 0 — Back up and inventory
- Back up the UDM Pro SE configuration.
- Confirm the six current VLANs, DHCP scopes, switch port profiles, and SSID mappings.
- Record the current Caddy address, backend application addresses, and ports.
- Inventory the address values represented by the
DNS,CADDY,BACKEND_APPS,IOT_BACKENDS, andRFC1918design labels in §5. These values will be entered directly into Policy Table rules.
Phase 1 — Establish Technitium DNS
- Deploy Technitium at
10.0.10.5by following the Technitium deployment guide. - Configure the internal primary zone and verify recursion, blocking, and direct lookups from LAN and Office.
- Add all current and target client networks to Technitium Allowed Networks
(§8), including the future DMZ subnet
10.0.15.0/24. - Configure every current VLAN's DHCP option 6 to advertise
10.0.10.5. - Add temporary firewall allows for UDP/TCP 53 from every current VLAN to
destination IP
10.0.10.5. Do not add DNS DNAT yet. - Renew a client lease on each VLAN and verify both direct and normal resolver
queries:
dig @10.0.10.5 example.com dig example.com - Continue only after Technitium's query log shows successful requests from LAN, Office, Family, IoT, Media, and Guest.
Phase 2 — Move Caddy into DMZ
- Create VLAN 15 (
10.0.15.0/24) and theDMZcustom zone. - Stand up the dedicated Caddy host at
10.0.15.5by following the Caddy Raspberry Pi deployment guide; apply the host hardening in §9. - Allow DMZ to
DNS, DMZ toExternal, and Caddy to the requiredBACKEND_APPSports before adding DMZ block rules. - Install the Netbird agent and verify its tunnel to the VPS.
- Update Technitium's
caddy.home.example.comrecord to10.0.15.5. - Verify Caddy from LAN and Office, then from the Netbird path.
- Add the remaining DMZ block rules only after proxying, DNS, ACME, and Netbird checks pass.
Phase 3 — Apply remaining zones
Apply zones one at a time in this order: FAMILY → MEDIA → IOT → GUEST. For each zone:
- Create the custom zone and assign its existing VLAN.
- Add allow rules for
DNS,CADDYwhere applicable, andExternal. - Renew a client DHCP lease and verify normal DNS, internet access, and intended Caddy access.
- Add the RFC1918, Gateway, and terminal block rules.
- Repeat the functional and isolation tests.
- Enable DNS DNAT for that zone last, then verify a query sent to a public
resolver appears in Technitium's query log:
dig @1.1.1.1 example.com
Keep the previous zone/firewall configuration backup until 24-48h of soak passes without rule misses.
12. Validation Checklist
DNS gate
- DHCP on every VLAN advertises
10.0.10.5. - Direct and normal DNS queries work from every VLAN.
- Technitium's query log identifies requests from every client subnet.
- Technitium web administration is reachable only from
TRUSTED.
Caddy gate
- UDM Pro SE shows VLAN 15 with Caddy at
10.0.15.5. caddy.home.example.comresolves to10.0.15.5.- Caddy resolves internal names through Technitium and public ACME names without DNS DNAT interception.
- Caddy reaches only required
BACKEND_APPSports and cannot reach UDM administration or unrelated trusted hosts. - Caddy's Let's Encrypt DNS-01 renewal and Netbird ingress both succeed.
Zone gate
- FAMILY and MEDIA can resolve DNS, reach intended Caddy services, and access the internet, but cannot reach other internal hosts.
- IOT can resolve DNS and access required cloud services; Zigbee2MQTT can still reach SLZB-06U over TCP 6638.
- GUEST can resolve DNS and access the internet, but cannot reach internal hosts or other guests.
- DNS DNAT tests from FAMILY, MEDIA, IOT, and GUEST appear in Technitium's query log.
- The 6 custom zones contain the intended 7 VLANs; unused default zones are empty.
13. Open Decisions
- TLS termination at VPS or home Caddy? Default in this doc: passthrough to home Caddy. If switched to VPS-terminates, drop the outbound
:443to DNS provider requirement (move ACME to VPS). - Apple TV / Chromecast cross-zone casting: if family phones (FAMILY) need to cast to Apple TV (MEDIA), enable mDNS reflector between FAMILY ↔ MEDIA and add explicit AirPlay/Chromecast pinholes (TCP 5000/7000 for AirPlay, 8008/8009 for Chromecast). Off by default.
- Wired Media uplink: TL-SG108 is unmanaged — it passes tagged VLAN 40 transparently but cannot enforce port-VLAN binding. If untrusted devices ever land on it, replace with a managed access switch.
- TRUSTED
:53enforcement: currently trust workstation hygiene. Hard enforcement for Office requires moving Technitium or clients across a routed boundary; UDM Pro SE rules cannot intercept same-subnet DNS traffic.