diff --git a/CONTEXT.md b/CONTEXT.md index 5a381475..5cd34ebc 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -119,8 +119,8 @@ _Avoid_: "external", "outside". _Avoid_: bare "lan", "private", "intranet". **Segment**: -One isolated L2/L3 network with pfSense as its gateway — realised as one Proxmox bridge feeding one dedicated pfSense interface (dManagementsVms 10.0.10.0/24, dKubernetes 10.0.20.0/24, dCCTV 10.0.30.0/24). pfSense itself never terminates 802.1Q; any tagging happens on the bridge or a switch. -_Avoid_: "VLAN" as the primary name (VLAN 10/20 are informal aliases; dCCTV has no tag on the wire at all). +One isolated L2/L3 network with pfSense as its gateway — realised as a Proxmox-bridge-level tag feeding one dedicated untagged pfSense interface (dManagementsVms 10.0.10.0/24 = vmbr1 tag 10, dKubernetes 10.0.20.0/24 = vmbr1 tag 20, dCCTV 10.0.30.0/24 = vmbr0 tag 30). pfSense itself never terminates 802.1Q. +_Avoid_: "VLAN" as the primary name (the tags 10/20/30 are transport detail; the Segment is the concept). **CCTV segment**: The untrusted camera **Segment** (`dCCTV`) — devices in it may be pulled from (RTSP/ISAPI) but may initiate nothing except NTP to their gateway. Deliberately outside every trusted source-IP allowlist (ADR-0017). diff --git a/docs/adr/0017-cctv-segment-dedicated-pfsense-leg.md b/docs/adr/0017-cctv-segment-dedicated-pfsense-leg.md index 2a73bda2..7b06f0e4 100644 --- a/docs/adr/0017-cctv-segment-dedicated-pfsense-leg.md +++ b/docs/adr/0017-cctv-segment-dedicated-pfsense-leg.md @@ -1,6 +1,6 @@ -# CCTV segment on a dedicated pfSense leg, not an 802.1Q trunk +# CCTV segment: dedicated pfSense interface, VLAN-30 trunk on the LAN1 cable -Status: accepted (2026-07-02) +Status: accepted (2026-07-02, rev 3 — single-switch) ![Network topology — dCCTV segment, flows, and camera-day steps](./0017-cctv-segment-topology.svg) @@ -13,14 +13,22 @@ to pfSense" — but nothing in this network terminates dot1q on pfSense; the site idiom is one vlan-aware Proxmox bridge → one tagged VM NIC → one clean untagged pfSense interface per segment. -**Decision:** the CCTV segment (`dCCTV`, 10.0.30.1/24) rides a dedicated -physical leg — R730 `eno2` (spare) → new bridge `vmbr2` → pfSense `net3` -(vtnet3), untagged end-to-end. The new TL-SG105PE PoE switch is a **dedicated -CCTV island**: camera in a PoE port, one port patched to eno2, no VLAN table -at all, mgmt IP inside the segment (10.0.30.6 via Kea). The existing garage -TL-SG105E (192.168.1.6 — apartment uplink, R730 LAN1, 4G router 192.168.1.7, -UPS mgmt; exactly one free port) is untouched — it has no PoE and no spare -port pair, which is also why the two roles cannot share one switch. +**Decision (rev 3):** ONE switch — the new TL-SG105PE **replaces** the old +garage TL-SG105E (Viktor prefers not running two switches; retired unit +becomes a cold spare, its 192.168.1.6 mgmt IP passes to the PE). Five ports, +all used: apartment uplink, 4G router 192.168.1.7, UPS mgmt (all untagged +VLAN 1), the camera (untagged VLAN 30, PoE), and the **trunk to R730 `eno1` +carrying home LAN untagged + CCTV tagged 30** over the existing LAN1 cable. +pfSense `net3` (vtnet3) sits on `vmbr0` with `tag=30` — exactly the site +idiom used for dManagementsVms/dKubernetes (bridge-level tag → clean untagged +vNIC; pfSense still terminates no dot1q itself). The earlier dedicated +`eno2`/`vmbr2` leg is kept **dormant as a fallback** (rev 2 wired it; moving +net3 back to vmbr2 restores pure physical isolation in one `qm set`). +This narrows the earlier 802.1Q objection rather than contradicting it: the +rejection assumed *unmanaged* switches, where any LAN device could inject +tagged frames; with the managed PE as the only device on eno1, VLAN-30 +membership is {camera port, trunk port} only, so tag-30 ingress from every +other port — and from the exposed camera cable — is dropped or contained. Cameras are untrusted: default-deny on dCCTV with a single NTP-to-gateway exception; Frigate (k8s) pulls RTSP in; ha-sofia (192.168.1.8) may reach ISAPI/RTSP directly; home-LAN clients route in via an AX6000 static @@ -29,29 +37,35 @@ route (10.0.30.0/24 via 192.168.1.2). 10.0.30.0/24 is deliberately NOT in the ## Considered options -- **802.1Q tag over the existing LAN path (eno1/vmbr0)** — rejected: vmbr0 is - vlan-aware with `bridge-vids 2-4094`, so ANY device on the home LAN could - inject tagged frames straight into the camera segment (defeats the - cable-tap threat model); tag-passing through the unmanaged SW1 is - undefined; and it reconfigures the live bridge carrying the host IP and - pfSense WAN. +- **802.1Q over the LAN path behind an UNMANAGED switch** (the original plan + read this way) — rejected: any LAN device could inject tagged frames into + vmbr0 (`bridge-vids 2-4094`) and tag-passing through a dumb switch is + undefined. Rev 3 adopts the tagged path ONLY because the managed PE now + polices VLAN-30 membership at the single entry point to eno1; no bridge + reconfiguration was needed (vmbr0 was already vlan-aware). +- **Dedicated physical leg (eno2 → vmbr2 → net3), one switch per role** + (rev 1/2 as-built) — superseded by rev 3: it forced either a second switch + (6 connections vs 5 ports once the PE also replaced the old switch) or new + hardware. Strongest isolation of all options; kept dormant as the fallback. - **AX6000 as the camera gateway** — rejected earlier in the design (consumer router, no inter-VLAN firewall). ## Consequences -- eno2 is consumed; eno3/eno4 remain the last spare NICs on the R730. -- Two Easy Smart switches live in the rack: the OLD TL-SG105E at 192.168.1.6 - remains the load-bearing shared one (apartment uplink, R730 LAN1, pfSense's - backup-WAN path via the 4G router, UPS mgmt — one port free); the NEW - TL-SG105PE carries only CCTV. The Easy Smart mgmt-answers-on-every-port - quirk is therefore contained: the PE's mgmt UI is only L2-adjacent to - cameras, and pfSense still gates all L3. -- Adding a future camera = one free PoE port on the PE + a Kea - reservation; no pfSense/PVE/VLAN work. -- 2026-07-02 correction: an earlier revision of this ADR described ONE shared - PE switch with a port-based VLAN split — written before discovering the - live 192.168.1.6 device is a separate, older non-PoE TL-SG105E. No VLAN - table exists anywhere in the final design. +- The switch is now single-point and load-bearing for everything in the rack + (apartment uplink, pfSense backup-WAN via 4G, UPS mgmt, CCTV) AND its VLAN + table + mgmt password are part of the isolation boundary — the Easy Smart + mgmt UI answers on every port, so the password is the gate between a + compromised camera and the switch config. All 5 ports are consumed: the + next camera forces an 8-port PoE upgrade (the wiring plan already fits it). +- `eno2`/`vmbr2` stay cabled-ready but dormant (fallback to rev 2's physical + leg); eno3/eno4 remain free. +- The old TL-SG105E is retired to cold spare; the PE inherits 192.168.1.6 + (Kea reservation by MAC). +- Revision history (all 2026-07-02): rev 1 assumed one shared PE with a + port-VLAN split (conflated the two devices); rev 2 split into two switches + after inspecting 192.168.1.6 (old non-PoE SG105E, 4/5 ports used); rev 3 + consolidated back to one switch — the PE replacing the SG105E — per + Viktor's preference, moving CCTV onto a managed tagged trunk. - Frigate's ADR-0016 VRAM budget was bumped 2000 → 2300 MiB for the extra NVDEC stream. diff --git a/docs/adr/0017-cctv-segment-topology.svg b/docs/adr/0017-cctv-segment-topology.svg index acba5936..007b7e16 100644 --- a/docs/adr/0017-cctv-segment-topology.svg +++ b/docs/adr/0017-cctv-segment-topology.svg @@ -1,8 +1,8 @@ - + @@ -17,11 +17,10 @@ - - ADR-0017 — CCTV segment on a dedicated pfSense leg - Sofia/Vermont · as-built 2026-07-02 · dashed = camera-day · no VLANs anywhere — isolation is physical + ADR-0017 — CCTV segment behind pfSense, VLAN-30 trunk on the LAN1 cable + Sofia/Vermont · rev 3 (single switch) 2026-07-02 · dashed = camera-day · the ONLY 802.1Q is the trunk between the switch and eno1 - + @@ -29,7 +28,7 @@ DENY · camera → LAN / other segments / internet (default deny on dCCTV) - + GARAGE ENTRANCE @@ -39,67 +38,57 @@ DNS: garage-cam.viktorbarzin.lan PoE from switch · cloud/P2P off - - - cat6 in conduit · PoE + + cat6 in conduit · PoE → P4 - + - RACK — GARAGE · TWO SWITCHES + RACK — GARAGE · ONE SWITCH - - - TL-SG105PE NEW · dedicated CCTV island · mgmt 10.0.30.6 (Kea) · no VLAN table + + TL-SG105PE replaces the SG105E · mgmt 192.168.1.6 (Kea) · all 5 ports used - - camera · PoE - any of P1–P4 - - → R730 eno2 - uplink (P5) - - 3 × spare PoE - future cameras + + P1 · V1 + apartment + uplink + + P2 · V1 + 4G router + 192.168.1.7 + + P3 · V1 + UPS mgmt + + P4 · V30 + camera + PoE ON + + P5 · trunk + V1 untagged + + V30 tagged + 802.1Q: VLAN 1 untagged {P1,P2,P3,P5} · VLAN 30 {P4 untagged/PVID 30, P5 tagged} + tag-30 ingress on P1/P2/P3 is dropped (not members) — the trunk is the only tagged path + old TL-SG105E → retired, cold spare · backup-WAN (4G) + UPS keep their ports - - - TL-SG105E · 192.168.1.6 existing · no PoE · UNTOUCHED by this design - - - P1 · 1G - - P2 · 100M - - P3 · 100M - - P4 · free - - P5 · 1G - - 1G ports: apartment uplink + R730 LAN1 · 100M ports: 4G router .7 (pfSense backup-WAN) + UPS mgmt + + + + LAN1 cable - - - patch - - - - R730 LAN1 - - + DELL R730 — PVE HOST 192.168.1.127 (IN THE RACK) - - + eno1 → vmbr0 - LAN1 · vlan-aware + untag V1 + tag 30 - - eno2 → vmbr2 - NEW · dedicated leg + + eno2 → vmbr2 + dormant fallback leg vmbr1 @@ -112,17 +101,16 @@ gateway + firewall for every segment - net0 · WAN 192.168.1.2 (home LAN) + net0 · WAN 192.168.1.2 · vmbr0 untagged net1 · dManagementsVms 10.0.10.1 net2 · dKubernetes 10.0.20.1 - net3 · dCCTV 10.0.30.1/24 · NEW + net3 · dCCTV 10.0.30.1/24 · vmbr0 tag 30 - - - + + @@ -139,7 +127,7 @@ go2rtc LB 10.0.20.204 restream → HA live view (MSE/HLS) - + HOME LAN 192.168.1.0/24 @@ -151,45 +139,40 @@ apartment clients laptops, phones - CAMERA DAY: static route 10.0.30.0/24 via 192.168.1.2 - - apartment uplink · SG105E · eno1 + apartment uplink · switch P1 · trunk · eno1 - - + ALLOW · Frigate → camera RTSP :554 (routed k8s → dCCTV; opt1 allow-all) - ALLOW · ha-sofia → camera :80 ISAPI + :554 enters pfSense WAN · reply-to off · needs the AX6000 route - ALLOW · camera → 10.0.30.1:123 (NTP) - + - home LAN 192.168.1.0/24 - - CCTV island / dCCTV 10.0.30.0/24 - - dKubernetes - - dManagementsVms - - allowed flow - - denied - - camera-day step - ADR-0017 · rev 2 + home LAN / VLAN 1 + + CCTV / VLAN 30 / dCCTV 10.0.30.0/24 + + dKubernetes + + dManagementsVms + + allowed flow + + denied + + camera-day step + ADR-0017 · rev 3 diff --git a/docs/architecture/networking.md b/docs/architecture/networking.md index 970fc421..2d3eff37 100644 --- a/docs/architecture/networking.md +++ b/docs/architecture/networking.md @@ -89,7 +89,7 @@ graph TB | phpIPAM | v1.7.0 | phpipam.viktorbarzin.me | IP address management, device inventory, DNS sync | | vmbr0 | Linux bridge | 192.168.1.127/24 | Physical bridge on eno1, uplink to LAN | | vmbr1 | Linux bridge (VLAN-aware) | Internal | VLAN trunk for VM isolation | -| vmbr2 | Linux bridge | Physical (eno2) | dCCTV segment leg: eno2 → TL-SG105PE (dedicated CCTV switch in the rack) → cameras; pfSense net3 is the only L3 exit (ADR-0017) | +| vmbr2 | Linux bridge | Physical (eno2) | DORMANT fallback leg for dCCTV (ADR-0017 rev 3) — live dCCTV rides vmbr0 tag 30 over the LAN1 trunk | | Technitium DNS | Container | 10.0.20.201 (LB) / 10.96.0.53 (ClusterIP) | Internal DNS (viktorbarzin.lan) + full recursive resolver | | Cloudflare DNS | SaaS | External | ~50 public domains under viktorbarzin.me | | Cloudflared | Container | K8s (3 replicas) | Tunnel ingress, replaces port forwarding | @@ -103,9 +103,9 @@ graph TB Isolated camera segment for owned cameras at the Sofia site (first: `vermont-garage`, HiLook IPC-T241H-C at the garage entrance). Decision + rejected alternatives: `docs/adr/0017-cctv-segment-dedicated-pfsense-leg.md`. -**Physical path**: camera → TL-SG105PE PoE port → R730 `eno2` → `vmbr2` (bridge-ports eno2, not vlan-aware) → pfSense `net3`/vtnet3 = interface **dCCTV `10.0.30.1/24`**. The TL-SG105PE is a dedicated CCTV island — camera + eno2 uplink + 3 spare PoE ports, **no VLAN table anywhere**, mgmt at `10.0.30.6` (Kea). It is a second switch: the pre-existing garage TL-SG105E (`192.168.1.6`; apartment uplink, R730 LAN1, 4G router `192.168.1.7`, UPS mgmt, one free port; no PoE) is not involved in the CCTV path at all. +**Physical path (rev 3, single switch)**: camera → TL-SG105PE PoE port (untagged VLAN 30) → trunk port (home LAN untagged + CCTV **tagged 30**) → the existing LAN1 cable → R730 `eno1` → `vmbr0` (vlan-aware) → pfSense `net3`/vtnet3 = `vmbr0 tag=30` = interface **dCCTV `10.0.30.1/24`**. The TL-SG105PE **replaces** the old garage TL-SG105E (retired to cold spare) and carries everything: apartment uplink, 4G router `192.168.1.7`, UPS mgmt (VLAN 1), camera (VLAN 30), trunk — all 5 ports used. VLAN-30 membership is {camera port, trunk port} only, so tagged injection from other ports is dropped. `eno2`/`vmbr2` remain dormant as the fallback physical leg (rev 2). -**Addressing**: Kea DHCP pool `10.0.30.100-199`; devices get MAC reservations (camera `10.0.30.70`, PE switch mgmt `10.0.30.6`). Kea DDNS auto-registers names in Technitium; `phpipam-pfsense-import` picks up leases hourly. +**Addressing**: Kea DHCP pool `10.0.30.100-199`; devices get MAC reservations (camera `10.0.30.70`; the PE switch mgmt inherits the retired switch's `192.168.1.6` on the home LAN). Kea DDNS auto-registers names in Technitium; `phpipam-pfsense-import` picks up leases hourly. **Firewall** (all on pfSense): - dCCTV in: pass `udp OPT4-net → 10.0.30.1:123` (NTP) — everything else hits the interface's default deny. Cameras cannot reach LAN, other segments, or the internet.