570 lines
30 KiB
Markdown
570 lines
30 KiB
Markdown
# 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.5` on 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:
|
|
|
|
1. **Technitium DNS first:** make `10.0.10.5` authoritative for the internal
|
|
zone and verify direct DNS queries from every current VLAN.
|
|
2. **Caddy and DMZ second:** create VLAN 15, move Caddy to `10.0.15.5`, update
|
|
Technitium's `caddy` record, and verify reverse-proxy and Netbird traffic.
|
|
3. **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
|
|
1. Preserve all 6 current UDM Pro SE VLANs and add only VLAN 15 for the Caddy
|
|
DMZ.
|
|
2. Use **custom UniFi zones** built from scratch — avoid implicit behavior in the default `IoT` / `Hotspot` zones, and avoid UniFi 9.x's intra-zone default-allow turning multi-VLAN zones into trust footguns.
|
|
3. 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).
|
|
4. 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.
|
|
5. 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.
|
|
6. Default-deny posture for IoT and Guest, with explicit allows only for DNS + internet.
|
|
7. 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.5` and the Home Automation host uses `10.0.30.6`.
|
|
- The RFC1918 CIDRs are recorded as `10.0.0.0/8`, `172.16.0.0/12`, and
|
|
`192.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.
|
|
|
|
1. Open **Settings → Policy Engine → Policy Table → Create New Policy**.
|
|
2. At the top of **Create Policy**, select **Firewall**.
|
|
3. Set **Name** to `ALLOW MEDIA to CADDY web`.
|
|
4. Under **Source Zone**:
|
|
- Select zone `MEDIA`.
|
|
- Select **Any** for the source matcher.
|
|
- Leave source **Port** set to **Any**.
|
|
5. Under **Action**, select **Allow**.
|
|
6. Under **Destination Zone**:
|
|
- Select zone `DMZ`.
|
|
- Select **IP**, then enter `10.0.15.5`.
|
|
- Under **Port**, select **Specific**, then enter destination ports `80` and
|
|
`443`.
|
|
7. Under **IP Version**, select **IPv4**.
|
|
8. Under **Protocol**, select **TCP**.
|
|
9. Under **Connection State**, leave **All** selected. UniFi's default return
|
|
traffic policy permits replies to this allowed connection.
|
|
10. Leave **Match IPsec** disabled.
|
|
11. Enable **Syslog Logging** during rollout if you want to confirm policy
|
|
matches; it may be disabled after validation.
|
|
12. Under **Schedule**, select **Always**.
|
|
13. Optionally set **Description** to
|
|
`Allow Media clients to reach the Caddy reverse proxy on HTTP/HTTPS`.
|
|
14. Click **Add Policy**.
|
|
15. In the Policy Table, ensure this allow policy is above
|
|
`BLOCK MEDIA to RFC1918` and any terminal MEDIA block policy.
|
|
|
|
The finished policy should read approximately:
|
|
|
|
```text
|
|
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/8`
|
|
- `172.16.0.0/12`
|
|
- `192.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 → DMZ` firewall 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 → External` allow.
|
|
|
|
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:
|
|
1. **DMZ → TRUSTED pinhole:** Caddy IP only, targeting `BACKEND_APPS` IPs on app-specific TCP ports. Include only routes actually published in `docker-compose-files/caddy/Caddyfile_template`, including Technitium TCP 5380 when `dns01` is enabled. Drop all other DMZ → TRUSTED traffic.
|
|
2. **DNS via DNAT redirect** (§8) — destination `10.0.10.5:53` on UDP/TCP only; all other `:53` is silently rewritten.
|
|
3. **Proxy access:** TCP `80, 443` to 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.
|
|
|
|
1. **Untagged access port** on the switch — feeding port is access-mode VLAN 15, no trunk.
|
|
2. **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
|
|
```
|
|
3. **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
|
|
```
|
|
4. **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
|
|
```
|
|
5. **No Docker socket mount.** `docker-compose.yml` for Caddy must **not** include `/var/run/docker.sock:/var/run/docker.sock`. Prevents container-escape → host root via socket abuse.
|
|
6. **Drop capabilities:** `cap_drop: [ALL]` + `cap_add: [NET_BIND_SERVICE]` in the Caddy compose service. Container can bind 80/443, nothing else.
|
|
7. **Netbird agent runs as a host systemd service**, not inside the Caddy container. Keeps tunnel state out of the proxy blast radius.
|
|
8. **ACME via DNS-01 only** — no inbound HTTP listener needed (and none possible without port-forward). Caddy needs outbound `:443` to 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
|
|
|
|
1. Back up the UDM Pro SE configuration.
|
|
2. Confirm the six current VLANs, DHCP scopes, switch port profiles, and SSID
|
|
mappings.
|
|
3. Record the current Caddy address, backend application addresses, and ports.
|
|
4. Inventory the address values represented by the `DNS`, `CADDY`,
|
|
`BACKEND_APPS`, `IOT_BACKENDS`, and `RFC1918` design labels in §5. These
|
|
values will be entered directly into Policy Table rules.
|
|
|
|
### Phase 1 — Establish Technitium DNS
|
|
|
|
1. Deploy Technitium at `10.0.10.5` by following the
|
|
[Technitium deployment guide](../../docker-compose-files/technitium/README.md).
|
|
2. Configure the internal primary zone and verify recursion, blocking, and
|
|
direct lookups from LAN and Office.
|
|
3. Add all current and target client networks to Technitium Allowed Networks
|
|
(§8), including the future DMZ subnet `10.0.15.0/24`.
|
|
4. Configure every current VLAN's DHCP option 6 to advertise `10.0.10.5`.
|
|
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.
|
|
6. 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
|
|
```
|
|
7. Continue only after Technitium's query log shows successful requests from
|
|
LAN, Office, Family, IoT, Media, and Guest.
|
|
|
|
### Phase 2 — Move Caddy into DMZ
|
|
|
|
1. Create VLAN 15 (`10.0.15.0/24`) and the `DMZ` custom zone.
|
|
2. Stand up the dedicated Caddy host at `10.0.15.5` by following the
|
|
[Caddy Raspberry Pi deployment guide](../../docker-compose-files/caddy/README.md);
|
|
apply the host hardening in §9.
|
|
3. Allow DMZ to `DNS`, DMZ to `External`, and Caddy to the required
|
|
`BACKEND_APPS` ports before adding DMZ block rules.
|
|
4. Install the Netbird agent and verify its tunnel to the VPS.
|
|
5. Update Technitium's `caddy.home.example.com` record to `10.0.15.5`.
|
|
6. Verify Caddy from LAN and Office, then from the Netbird path.
|
|
7. 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:
|
|
|
|
1. Create the custom zone and assign its existing VLAN.
|
|
2. Add allow rules for `DNS`, `CADDY` where applicable, and `External`.
|
|
3. Renew a client DHCP lease and verify normal DNS, internet access, and
|
|
intended Caddy access.
|
|
4. Add the RFC1918, Gateway, and terminal block rules.
|
|
5. Repeat the functional and isolation tests.
|
|
6. 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
|
|
|
|
1. DHCP on every VLAN advertises `10.0.10.5`.
|
|
2. Direct and normal DNS queries work from every VLAN.
|
|
3. Technitium's query log identifies requests from every client subnet.
|
|
4. Technitium web administration is reachable only from `TRUSTED`.
|
|
|
|
### Caddy gate
|
|
|
|
1. UDM Pro SE shows VLAN 15 with Caddy at `10.0.15.5`.
|
|
2. `caddy.home.example.com` resolves to `10.0.15.5`.
|
|
3. Caddy resolves internal names through Technitium and public ACME names
|
|
without DNS DNAT interception.
|
|
4. Caddy reaches only required `BACKEND_APPS` ports and cannot reach UDM
|
|
administration or unrelated trusted hosts.
|
|
5. Caddy's Let's Encrypt DNS-01 renewal and Netbird ingress both succeed.
|
|
|
|
### Zone gate
|
|
|
|
1. FAMILY and MEDIA can resolve DNS, reach intended Caddy services, and access
|
|
the internet, but cannot reach other internal hosts.
|
|
2. IOT can resolve DNS and access required cloud services; Zigbee2MQTT can
|
|
still reach SLZB-06U over TCP 6638.
|
|
3. GUEST can resolve DNS and access the internet, but cannot reach internal
|
|
hosts or other guests.
|
|
4. DNS DNAT tests from FAMILY, MEDIA, IOT, and GUEST appear in Technitium's
|
|
query log.
|
|
5. The 6 custom zones contain the intended 7 VLANs; unused default zones are
|
|
empty.
|
|
|
|
## 13. Open Decisions
|
|
1. **TLS termination at VPS or home Caddy?** Default in this doc: passthrough to home Caddy. If switched to VPS-terminates, drop the outbound `:443` to DNS provider requirement (move ACME to VPS).
|
|
2. **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.
|
|
3. **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.
|
|
4. **TRUSTED `:53` enforcement:** 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.
|