From 462b39e5724e7d950201cdfe5e8036673e2bc12b Mon Sep 17 00:00:00 2001 From: Fizzlepoof Date: Sat, 23 May 2026 02:08:15 +0000 Subject: [PATCH] docs: record unifi firewall enforcement and closeout --- .../bin/unifi_stage_policy_engine_firewall.py | 51 +++- dev/rackpeek/config.yaml | 7 + docs/architecture/ARCHITECTURE_OVERVIEW.md | 10 +- docs/architecture/NETWORKING_MODEL.md | 21 +- ...oogle-cast-batch-move-result-2026-05-23.md | 51 ++++ ...t-pilot-failure-and-rollback-2026-05-23.md | 38 +++ .../google-cast-pilot-result-2026-05-23.md | 43 +++ .../intellirocks-triage-result-2026-05-23.md | 37 +++ .../docs/network-firewall-rule-order.md | 16 +- ...igration-remaining-checklist-2026-05-22.md | 50 ++-- ...-firewall-enforcement-result-2026-05-23.md | 65 +++++ .../unifi-legacy-cia-closeout-2026-05-23.md | 101 +++++++ ...i-minimum-safe-rule-skeleton-2026-05-23.md | 261 ++++++++++++++++++ .../unifi-ssid-cleanup-proposal-2026-05-23.md | 131 +++++++++ 14 files changed, 841 insertions(+), 41 deletions(-) create mode 100644 home/doris-dashboard/docs/google-cast-batch-move-result-2026-05-23.md create mode 100644 home/doris-dashboard/docs/google-cast-pilot-failure-and-rollback-2026-05-23.md create mode 100644 home/doris-dashboard/docs/google-cast-pilot-result-2026-05-23.md create mode 100644 home/doris-dashboard/docs/intellirocks-triage-result-2026-05-23.md create mode 100644 home/doris-dashboard/docs/unifi-firewall-enforcement-result-2026-05-23.md create mode 100644 home/doris-dashboard/docs/unifi-legacy-cia-closeout-2026-05-23.md create mode 100644 home/doris-dashboard/docs/unifi-minimum-safe-rule-skeleton-2026-05-23.md create mode 100644 home/doris-dashboard/docs/unifi-ssid-cleanup-proposal-2026-05-23.md diff --git a/automation/bin/unifi_stage_policy_engine_firewall.py b/automation/bin/unifi_stage_policy_engine_firewall.py index e83504f..46182d9 100644 --- a/automation/bin/unifi_stage_policy_engine_firewall.py +++ b/automation/bin/unifi_stage_policy_engine_firewall.py @@ -61,7 +61,44 @@ def desired_policies() -> list[dict[str, Any]]: 'port_matching_type': 'ANY', 'match_opposite_ports': False, }, - } + }, + { + 'name': 'Block Untrusted to Gateway Admin Surfaces', + 'enabled': True, + 'action': 'BLOCK', + 'protocol': 'tcp', + 'ip_version': 'IPV4', + 'logging': False, + 'create_allow_respond': False, + 'connection_state_type': 'ALL', + 'connection_states': [], + 'match_ip_sec': False, + 'match_opposite_protocol': False, + 'icmp_typename': 'ANY', + 'icmp_v6_typename': 'ANY', + 'description': 'Block IoT, Camera, and Old IoT clients from UniFi gateway admin TCP ports while preserving existing zone defaults for DHCP, DNS, mDNS, and internet access.', + 'source_zone_name': 'Untrusted', + 'destination_zone_name': 'Gateway', + 'schedule': {'mode': 'ALWAYS'}, + 'source': { + 'matching_target': 'IP', + 'matching_target_type': 'SPECIFIC', + 'ips': ['10.5.10.0/24', '10.5.20.0/24', '192.168.1.0/24'], + 'match_mac': False, + 'match_opposite_ips': False, + 'match_opposite_ports': False, + 'port_matching_type': 'ANY', + }, + 'destination': { + 'matching_target': 'IP', + 'matching_target_type': 'SPECIFIC', + 'ips': ['10.5.0.1'], + 'match_opposite_ips': False, + 'match_opposite_ports': False, + 'port_matching_type': 'SPECIFIC', + 'port': '22,80,443,8443,9443', + }, + }, ] @@ -85,6 +122,8 @@ POLICY_MUTABLE_KEYS = { 'description', } +TEMP_POLICY_NAMES = {'DORIS-TEMP'} + def canonical_policy(policy: dict[str, Any]) -> dict[str, Any]: out = {key: deepcopy(policy.get(key)) for key in POLICY_MUTABLE_KEYS if key in policy} @@ -193,6 +232,16 @@ def main() -> int: else: result['changes'].append({'name': materialized['name'], 'action': 'noop'}) + for name in sorted(TEMP_POLICY_NAMES): + existing = custom_by_name.get(name) + if not existing: + continue + result['changes'].append({'name': name, 'action': 'delete-temp'}) + if args.apply: + status, payload = client._json_request('DELETE', f"{policies_path}/{existing['_id']}") + if status >= 300: + result['changes'][-1]['error'] = {'status': status, 'payload': payload} + print(json.dumps(result, indent=2, sort_keys=True)) return 0 diff --git a/dev/rackpeek/config.yaml b/dev/rackpeek/config.yaml index b3eef8d..8fb632f 100644 --- a/dev/rackpeek/config.yaml +++ b/dev/rackpeek/config.yaml @@ -151,6 +151,13 @@ resources: UniFi gateway and controller. UniFi device name: UDM Pro - Old. WAN uplink currently active on eth9. + UniFi policy zones as of 2026-05-23: + - Internal = Management + Trusted + Servers + - Untrusted = IoT + Camera + Old IoT + - Hotspot = Guest + Custom hardening on top of UniFi defaults: + - Allow Internal to Untrusted + - Block Untrusted to Gateway Admin Surfaces - kind: Server name: unifi-usw-24-poe diff --git a/docs/architecture/ARCHITECTURE_OVERVIEW.md b/docs/architecture/ARCHITECTURE_OVERVIEW.md index 9223bb9..0b19f53 100644 --- a/docs/architecture/ARCHITECTURE_OVERVIEW.md +++ b/docs/architecture/ARCHITECTURE_OVERVIEW.md @@ -13,7 +13,15 @@ Full homelab stack as of 2026-05-09. All 6 expansion phases are complete and dep ## Network -- **LAN subnet:** 10.5.1.0/24 +- **Management:** 10.5.0.0/24 +- **Trusted:** 10.5.1.0/24 +- **IoT:** 10.5.10.0/24 +- **Cameras:** 10.5.20.0/24 +- **Servers:** 10.5.30.0/24 +- **Guest:** 10.5.90.0/24 +- **Legacy quarantine:** 192.168.1.0/24 (`Old IoT`) +- **UniFi policy zones:** `Internal` = Management + Trusted + Servers, `Untrusted` = IoT + Cameras + Old IoT, `Hotspot` = Guest +- **Current custom firewall hardening:** `Allow Internal to Untrusted` plus an explicit `Block Untrusted to Gateway Admin Surfaces` rule to keep IoT/Camera/Old IoT clients off the UDM Pro admin TCP ports while leaving the existing zone defaults in place - **Tailscale:** Serenity reachable at 100.94.87.79 - **NFS:** Serenity exports `/mnt/user/data` and `/mnt/user/immich` → mounted on PD at `/mnt/unraid/` via post-init script - **Docker networking:** Cross-stack communication via named external networks (`ai-services`, `ix-databases_shared-databases`) diff --git a/docs/architecture/NETWORKING_MODEL.md b/docs/architecture/NETWORKING_MODEL.md index 04eb5ce..24ee3e7 100644 --- a/docs/architecture/NETWORKING_MODEL.md +++ b/docs/architecture/NETWORKING_MODEL.md @@ -1,7 +1,7 @@ # Homelab Networking Model ## Physical Network -- **Gear:** Unifi throughout +- **Gear:** UniFi throughout - **Gateway / controller:** UDM Pro (`10.5.0.1`, WAN `69.166.182.157`) - **Core / access switching:** - USW-24-PoE (`10.5.0.10`) @@ -9,12 +9,23 @@ - **Wi-Fi access points:** - U7 Pro (`10.5.0.21`) — active - U6 LR (`10.5.0.20`) — currently disconnected in UniFi as of 2026-05-21 -- **Main LAN:** 10.5.1.x (Trusted VLAN) -- **Management VLAN:** 10.5.0.x -- **Other VLANs:** Cameras, Guest, IoT / Old IoT +- **Management subnet:** `10.5.0.0/24` +- **Trusted subnet:** `10.5.1.0/24` (VLAN 51) +- **IoT subnet:** `10.5.10.0/24` (VLAN 510) +- **Camera subnet:** `10.5.20.0/24` (VLAN 520) +- **Servers subnet:** `10.5.30.0/24` (VLAN 30) +- **Guest subnet:** `10.5.90.0/24` (VLAN 590) +- **Legacy quarantine:** `192.168.1.0/24` (`Old IoT`, VLAN 2) +- **UniFi policy zones:** + - `Internal` = Management + Trusted + Servers + - `Untrusted` = IoT + Camera + Old IoT + - `Hotspot` = Guest +- **Current custom policy additions on top of UniFi defaults:** + - `Allow Internal to Untrusted` + - `Block Untrusted to Gateway Admin Surfaces` (blocks IoT/Camera/Old IoT access to the UDM Pro admin TCP ports while leaving DHCP/DNS/mDNS/internet behavior to the existing zone defaults) - **Serenity IP:** 10.5.30.5 - **N.O.M.A.D. IP:** 10.5.30.7 -- **PlausibleDeniability:** 10.5.30.6 (static on LAN) +- **PlausibleDeniability:** 10.5.30.6 (Servers VLAN) ## Remote Access - **Pangolin VPS + Newt:** Services exposed as subdomains via reverse proxy. Newt agents run on PD and N.O.M.A.D. diff --git a/home/doris-dashboard/docs/google-cast-batch-move-result-2026-05-23.md b/home/doris-dashboard/docs/google-cast-batch-move-result-2026-05-23.md new file mode 100644 index 0000000..eefb01b --- /dev/null +++ b/home/doris-dashboard/docs/google-cast-batch-move-result-2026-05-23.md @@ -0,0 +1,51 @@ +# Google/Cast Batch Move Result — 2026-05-23 + +Purpose: record the live move of the Google/Chromecast-class clients onto IoT for household testing. + +## First pilot device +The first device I kicked over earlier was: +- `dc:e5:5b:8f:57:d2` + +That device is now verified on IoT with: +- IP: `10.5.10.161` +- network: `IoT` +- VLAN: `510` + +## Additional Google/cast-class devices moved to IoT +Using the same per-client virtual-network override method plus reassociation kick, these were also moved and verified: + +- `3c:8d:20:f3:92:36` -> `10.5.10.56` +- `90:ca:fa:b6:7f:6e` -> `10.5.10.6` +- `f8:0f:f9:42:08:6d` (`Google-Home-Mini`) -> `10.5.10.196` + +## Current verified state +All four target Google/cast-class clients now show: +- network: `IoT` +- network id: `6963cb461131084f05461642` +- VLAN: `510` +- virtual network override: enabled to the IoT network + +Verified client set: +- `dc:e5:5b:8f:57:d2` -> `10.5.10.161` +- `3c:8d:20:f3:92:36` -> `10.5.10.56` +- `90:ca:fa:b6:7f:6e` -> `10.5.10.6` +- `f8:0f:f9:42:08:6d` -> `10.5.10.196` + +## Method used +For each client: +1. `PUT rest/user/` with: + - `virtual_network_override_enabled=true` + - `virtual_network_override_id=6963cb461131084f05461642` +2. `POST cmd/stamgr` with `{"cmd":"kick-sta","mac":""}` +3. wait for reassociation and DHCP settle to a `10.5.10.x` address + +## Next step +John should now test from the real household path: +- Plex playback +- casting/discovery +- Dispatcharr if relevant + +If behavior is bad, rollback is straightforward per client: +- disable that client virtual network override +- kick the station again +- verify it returns to the prior lane/subnet diff --git a/home/doris-dashboard/docs/google-cast-pilot-failure-and-rollback-2026-05-23.md b/home/doris-dashboard/docs/google-cast-pilot-failure-and-rollback-2026-05-23.md new file mode 100644 index 0000000..c60f846 --- /dev/null +++ b/home/doris-dashboard/docs/google-cast-pilot-failure-and-rollback-2026-05-23.md @@ -0,0 +1,38 @@ +# Google/Cast IoT Test Failure and Rollback — 2026-05-23 + +Purpose: record the failed Chromecast/Plex discovery test after moving Google/cast-class devices to IoT, and the immediate rollback. + +## Observed failure +User-tested result: +- Plex on Chromecast could not see the Plex server after the Google/cast batch was moved to IoT. + +This is consistent with discovery/cast behavior breaking across the current segmentation boundary. + +## Rollback action taken +I reverted the per-client virtual network override and kicked each client to reassociate back to its prior lane. + +Rolled back devices: +- `dc:e5:5b:8f:57:d2` +- `3c:8d:20:f3:92:36` +- `90:ca:fa:b6:7f:6e` +- `f8:0f:f9:42:08:6d` (`Google-Home-Mini`) + +## Verified post-rollback state +All four now show: +- network: `Old IoT` +- network id: `6963e11c1131084f054622f1` +- virtual network override: disabled + +Observed IPs after rollback settle: +- `dc:e5:5b:8f:57:d2` -> `192.168.1.132` +- `3c:8d:20:f3:92:36` -> `192.168.1.192` +- `90:ca:fa:b6:7f:6e` -> `192.168.1.129` +- `f8:0f:f9:42:08:6d` -> `192.168.1.185` + +## Interpretation +The current clean IoT placement is not yet compatible with Plex discovery/cast behavior for these Google devices under the present network/policy setup. + +## Safe next options +1. Leave Google/cast-class devices on `Old IoT` for now. +2. Finish other cleanup first. +3. Later, if desired, design a narrow, intentional cast/discovery exception instead of broad trust expansion. diff --git a/home/doris-dashboard/docs/google-cast-pilot-result-2026-05-23.md b/home/doris-dashboard/docs/google-cast-pilot-result-2026-05-23.md new file mode 100644 index 0000000..4058fdb --- /dev/null +++ b/home/doris-dashboard/docs/google-cast-pilot-result-2026-05-23.md @@ -0,0 +1,43 @@ +# Google/Cast Pilot Result — 2026-05-23 + +Purpose: record the first live Google/cast-class pilot move from Trusted into IoT. + +## Device moved +- MAC: `dc:e5:5b:8f:57:d2` +- Vendor: Google +- Prior lane: `Trusted` +- Prior SSID: `Whiskey Neat Fuck Ice` +- Method: enabled per-client virtual network override to `IoT`, then kicked the station to reassociate + +## UniFi evidence +Before: +- network: `Trusted` +- network id: `6963cb201131084f05461635` +- IP: `10.5.1.132` +- virtual network override: disabled + +Applied: +- `virtual_network_override_enabled=true` +- `virtual_network_override_id=6963cb461131084f05461642` +- `cmd/stamgr` kick-sta for `dc:e5:5b:8f:57:d2` + +Verified after reassociation: +- network: `IoT` +- network id: `6963cb461131084f05461642` +- SSID seen after move: `CIA Via` +- VLAN: `510` +- final observed IP after DHCP settle: `10.5.10.161` + +## Interpretation +The client successfully moved from Trusted into IoT using a client-specific virtual network override and reassociated cleanly onto the IoT network. + +## Still pending +Human validation still needed from the laptop / household use path: +- Plex playback +- casting/discovery behavior +- Dispatcharr if relevant + +If the household behavior is bad, rollback is simple: +- disable the client's virtual network override +- kick the station again +- verify it returns to Trusted / `10.5.1.x` diff --git a/home/doris-dashboard/docs/intellirocks-triage-result-2026-05-23.md b/home/doris-dashboard/docs/intellirocks-triage-result-2026-05-23.md new file mode 100644 index 0000000..355c88c --- /dev/null +++ b/home/doris-dashboard/docs/intellirocks-triage-result-2026-05-23.md @@ -0,0 +1,37 @@ +# Intellirocks Trusted-Lane Triage Result — 2026-05-23 + +Purpose: record the disposition of the remaining Intellirocks client that was still sitting in Trusted. + +## Device +- MAC: `d4:ad:fc:f2:df:d2` +- Vendor: `Shenzhen Intellirocks Tech co.,ltd` +- Prior state: + - network: `Trusted` + - SSID: `Yer a Wifi Harry` + - IP: `10.5.1.154` + +## Rationale +This device did not earn Trusted placement. + +Evidence used: +- the existing cleanup shortlist already identified it as likely a sibling to the other Intellirocks devices on `Old IoT` +- vendor-family clustering suggests embedded / smart-home class, not a human/operator endpoint +- quarantine-first is safer than leaving an unidentified embedded client in Trusted + +## Action taken +Moved the client out of `Trusted` into `Old IoT` using a per-client virtual network override, then kicked it to reassociate. + +Applied: +- `virtual_network_override_enabled=true` +- `virtual_network_override_id=6963e11c1131084f054622f1` +- `cmd/stamgr` kick-sta for `d4:ad:fc:f2:df:d2` + +## Verified result +After reassociation and DHCP settle: +- network: `Old IoT` +- network id: `6963e11c1131084f054622f1` +- IP: `192.168.1.154` +- VLAN: `2` + +## Conclusion +The device no longer has Trusted-lane placement and is now quarantine-aligned with the other unidentified Intellirocks-class devices. diff --git a/home/doris-dashboard/docs/network-firewall-rule-order.md b/home/doris-dashboard/docs/network-firewall-rule-order.md index ef1682d..cc922e1 100644 --- a/home/doris-dashboard/docs/network-firewall-rule-order.md +++ b/home/doris-dashboard/docs/network-firewall-rule-order.md @@ -5,14 +5,14 @@ Goal: enforce clean segmentation between Management, Trusted, Servers, IoT, Guest, Cameras, and Legacy CIA quarantine without using broad lazy exceptions. Assumptions: -- Final networks: - - VLAN 10 Management -> `10.5.10.0/24` - - VLAN 20 Trusted -> `10.5.20.0/24` - - VLAN 30 Servers -> `10.5.30.0/24` - - VLAN 40 IoT -> `10.5.40.0/24` - - VLAN 50 Guest -> `10.5.50.0/24` - - VLAN 60 Cameras -> `10.5.60.0/24` - - Legacy CIA quarantine -> existing legacy subnet/VLAN retained temporarily +- Current live networks: + - Management -> `10.5.0.0/24` + - Trusted -> `10.5.1.0/24` + - IoT -> `10.5.10.0/24` + - Cameras -> `10.5.20.0/24` + - Servers -> `10.5.30.0/24` + - Guest -> `10.5.90.0/24` + - Legacy CIA quarantine -> `192.168.1.0/24` retained temporarily - Use address/object groups where UniFi allows them. - Apply stateful best practice first: established/related before policy rules. - Specific allows always go above broad denies. diff --git a/home/doris-dashboard/docs/network-migration-remaining-checklist-2026-05-22.md b/home/doris-dashboard/docs/network-migration-remaining-checklist-2026-05-22.md index 4fa7873..49baa20 100644 --- a/home/doris-dashboard/docs/network-migration-remaining-checklist-2026-05-22.md +++ b/home/doris-dashboard/docs/network-migration-remaining-checklist-2026-05-22.md @@ -15,6 +15,8 @@ Current intent: only safe, low-drama follow-up work remains. The easy-value clie - These 8 client moves were verified live from UniFi `stat/sta` - Firewall object/group scaffolding exists - Basic custom outbound policy scaffolding exists (`Allow Internal to Untrusted`) +- First live management-plane hardening slice is now applied: `Block Untrusted to Gateway Admin Surfaces` +- Reference: `unifi-firewall-enforcement-result-2026-05-23.md` ## Safe to do now 1. Documentation cleanup @@ -24,7 +26,8 @@ Current intent: only safe, low-drama follow-up work remains. The easy-value clie 2. Finalize remaining task list - keep Google/cast pilot explicitly deferred until John is on the laptop - keep unknown Legacy CIA devices explicitly quarantine-first - - keep Intellirocks-in-Trusted explicitly flagged for later identification/move decision + - Trusted-lane Intellirocks cleanup is now resolved separately; see `intellirocks-triage-result-2026-05-23.md` + - Legacy CIA leftovers are now dispositioned separately; see `unifi-legacy-cia-closeout-2026-05-23.md` 3. Non-disruptive firewall planning cleanup - compare staged firewall objects/policies against desired rule order @@ -47,29 +50,25 @@ Likely candidates still needing that treatment: - `90:ca:fa:b6:7f:6e` ### B. Remaining Trusted-lane cleanup -- `d4:ad:fc:f2:df:d2` likely does not belong in Trusted -- decide: identify better, move to IoT, or quarantine later +- Completed: `d4:ad:fc:f2:df:d2` was removed from Trusted and re-homed to `Old IoT` +- Reference: `intellirocks-triage-result-2026-05-23.md` ### C. Legacy CIA quarantine closeout -Leave quarantined unless identified better: -- `5c:61:99:41:73:40` -- `60:74:f4:54:fd:ec` -- `60:74:f4:7b:6a:11` -- `c0:f5:35:20:5d:94` -- `d4:ad:fc:60:90:6a` -- `d4:ad:fc:ea:7f:65` +- Completed as a disposition decision: the remaining unidentified leftovers stay quarantined on `Old IoT` +- Reference: `unifi-legacy-cia-closeout-2026-05-23.md` ### D. Real firewall enforcement -Still needed if the design is to be truly enforced rather than just staged: -- stateful baseline rules -- management shield -- explicit Trusted admin -> Management allows -- Trusted -> Servers allow -- Guest internet-only enforcement -- IoT narrow internal access only -- Camera narrow internal access only -- Legacy CIA quarantine-only posture -- broad deny structure for restricted lanes +Partially completed: +- live custom rule now blocks `IoT` / `Camera` / `Old IoT` access to the UDM Pro admin TCP ports +- reference: `unifi-firewall-enforcement-result-2026-05-23.md` + +Still needed if the full design is to be enforced rather than just the first safe slice: +- stateful baseline review against UniFi built-ins +- broader management shield only after DHCP/DNS/gateway dependencies are verified +- explicit Trusted admin -> Management allows if broad internal->gateway restrictions are introduced later +- Trusted -> Servers allow only if later intra-Internal restrictions are introduced +- any camera/Protect helper exceptions after port-level verification +- any Google/cast discovery exceptions only after laptop validation Important distinction: - groups/objects exist @@ -78,14 +77,13 @@ Important distinction: ### E. Final cleanup - SSID simplification once Google/cast behavior is understood +- reference SSID retire/keep plan: `unifi-ssid-cleanup-proposal-2026-05-23.md` - final validation sweep - confirm no temporary panic exceptions remain - document any intentionally deferred weird devices ## Suggested execution order from here -1. Produce firewall gap list -2. Wait for laptop session -3. Run one-device Google/cast pilot -4. Decide on Trusted Intellirocks device -5. Finish firewall enforcement carefully -6. Do final SSID simplification and closeout +1. Wait for laptop session +2. Human-validate the already-completed Google/cast pilot from the laptop +3. Finish firewall enforcement carefully +4. Do final SSID simplification and closeout using `unifi-ssid-cleanup-proposal-2026-05-23.md` diff --git a/home/doris-dashboard/docs/unifi-firewall-enforcement-result-2026-05-23.md b/home/doris-dashboard/docs/unifi-firewall-enforcement-result-2026-05-23.md new file mode 100644 index 0000000..a3c58a6 --- /dev/null +++ b/home/doris-dashboard/docs/unifi-firewall-enforcement-result-2026-05-23.md @@ -0,0 +1,65 @@ +# UniFi Firewall Enforcement Result — 2026-05-23 + +Purpose: record the actual first live enforcement change made after the read-only planning pass, plus the exact remaining work that was intentionally not guessed into production. + +## What was live-validated before the change +- UniFi controller reachable at `https://10.5.0.1` +- Live zone model confirmed from the controller API: + - `Internal` = Management + Trusted + Servers + - `Untrusted` = IoT + Camera + Old IoT + - `Hotspot` = Guest +- Existing custom policies before this change: + - `Allow Internal to Untrusted` + - disabled temp policy `DORIS-TEMP` +- Existing UniFi built-in defaults already provided: + - Guest internet-only / hotspot restrictions + - Untrusted zone isolation from `Internal`, `Hotspot`, `Dmz`, `Untrusted`, and `Vpn` + - broad `Untrusted -> Gateway` allow remained in place by default + +## Live change applied +Added one custom Policy Engine rule: + +- `Block Untrusted to Gateway Admin Surfaces` + - source zone: `Untrusted` + - source CIDRs: + - `10.5.10.0/24` + - `10.5.20.0/24` + - `192.168.1.0/24` + - destination zone: `Gateway` + - destination IP: `10.5.0.1` + - destination TCP ports: `22,80,443,8443,9443` + - intent: block IoT / Camera / Old IoT clients from reaching the UDM Pro admin surfaces while leaving DHCP, DNS, mDNS, and normal internet behavior to the existing zone defaults + +## Why this was the safe first enforcement slice +This closes the most obvious management-plane exposure left by the default zone policy without guessing at: +- Protect/NVR helper ports +- Google/cast discovery exceptions +- local-vs-public NTP design +- whether every restricted lane is already using the intended DNS target instead of the gateway + +A broader `Untrusted -> Gateway` block was deliberately not applied because the current live DHCP/DNS details still need cleanup and verification. + +## Validation evidence +- Dry-run from the PD runtime helper showed exactly one new policy create and no mutation to `Allow Internal to Untrusted` +- Apply completed successfully through the same helper +- Follow-up helper run removed the disabled temporary custom policy `DORIS-TEMP` +- Post-apply custom policy readback now shows only: + - `Allow Internal to Untrusted` + - `Block Untrusted to Gateway Admin Surfaces` +- Controller API access from PD remained healthy immediately after apply + +## Things intentionally left for later +Still not safe to guess into production without targeted validation: +- final `HOST-ADMIN-TRUSTED` membership +- full management shield for every internal lane +- explicit Trusted admin allow objects before any broad `Internal -> Gateway` deny +- exact camera/Protect helper ports +- Google/cast discovery/control exceptions +- final SSID retirement / simplification + +## Recommended next live order +1. verify what DNS target the restricted lanes are actually receiving from DHCP today +2. verify whether any restricted clients still need gateway access beyond DHCP/mDNS +3. only then decide whether to convert this first surgical block into the broader management shield design +4. do Google/cast validation from a laptop before any cast-related firewall or SSID cleanup +5. finish SSID simplification after the cast/discovery behavior is understood diff --git a/home/doris-dashboard/docs/unifi-legacy-cia-closeout-2026-05-23.md b/home/doris-dashboard/docs/unifi-legacy-cia-closeout-2026-05-23.md new file mode 100644 index 0000000..a43d9c9 --- /dev/null +++ b/home/doris-dashboard/docs/unifi-legacy-cia-closeout-2026-05-23.md @@ -0,0 +1,101 @@ +# UniFi Legacy CIA Closeout — 2026-05-23 + +Purpose: close out the remaining unidentified `CIA Via` / `Old IoT` clients after the easy-value migrations and the Trusted-lane Intellirocks cleanup. + +## Scope +This document covers the leftover unidentified devices that should remain in legacy quarantine. + +Historical basis used: +- `home/doris-dashboard/docs/unifi-client-cleanup-shortlist-2026-05-22.md` +- `home/doris-dashboard/docs/unifi-client-rehome-results-2026-05-22.md` +- `home/doris-dashboard/docs/old-iot-tomorrow-disposition-list.md` +- `home/doris-dashboard/docs/intellirocks-triage-result-2026-05-23.md` + +Important note: +- The live UniFi read helper in this shell cannot currently re-poll the controller because local `automation/.env` here does not contain the `UNIFI_*` credentials. +- So this closeout is based on the latest verified operator artifacts already captured during the live session, plus the completed Intellirocks Trusted-lane cleanup from 2026-05-23. + +## What is already resolved +These are no longer part of the legacy-quarantine leftovers: +- `MyQ-29B` -> moved `Old IoT` -> `IoT` +- `LG_Smart_Dryer2_open` -> moved `Old IoT` -> `IoT` +- `Samsung-FamilyHub` -> moved `Old IoT` -> `IoT` +- `Main-Floor` ecobee -> moved `Old IoT` -> `IoT` +- `Upstairs` ecobee -> moved `Old IoT` -> `IoT` +- Trusted-lane Intellirocks sibling `d4:ad:fc:f2:df:d2` -> moved `Trusted` -> `Old IoT` + +## Final disposition for remaining unidentified Legacy CIA devices + +### 1) 5c:61:99:41:73:40 +- Last known IP: `192.168.1.172` +- Vendor: `Cloud Network Technology Singapore Pte. Ltd.` +- Final disposition: `keep quarantined on Old IoT` +- Why: + - unnamed + - unclear function + - insufficient evidence to grant clean `IoT` +- Follow-up class: `identify later / possible kill-candidate if nobody can identify it` + +### 2) 60:74:f4:54:fd:ec +- Last known IP: `192.168.1.136` +- Vendor: `Private` +- Final disposition: `keep quarantined on Old IoT` +- Why: + - private/randomized identity + - no useful hostname + - no evidence it deserves promotion +- Follow-up class: `identify later / strong kill-candidate pool` + +### 3) 60:74:f4:7b:6a:11 +- Last known IP: `192.168.1.117` +- Vendor: `Private` +- Final disposition: `keep quarantined on Old IoT` +- Why: + - same reasoning as the other private/randomized leftover + - no positive identification +- Follow-up class: `identify later / strong kill-candidate pool` + +### 4) c0:f5:35:20:5d:94 +- Last known IP: `192.168.1.183` +- Vendor: `AMPAK Technology,Inc.` +- Final disposition: `keep quarantined on Old IoT` +- Why: + - broad embedded/vendor bucket + - no positive identification + - no justification for moving into cleaner `IoT` +- Follow-up class: `identify later / possible kill-candidate` + +### 5) d4:ad:fc:60:90:6a +- Last known IP: `192.168.1.100` +- Vendor: `Shenzhen Intellirocks Tech co., ltd` +- Final disposition: `keep quarantined on Old IoT` +- Why: + - still unidentified + - vendor family now has three low-trust/quarantine-aligned siblings, including the former Trusted device moved back out on 2026-05-23 + - this strengthens the case that these are embedded smart-home / disposable low-trust devices, not operator endpoints +- Follow-up class: `identify later / likely kill-candidate if never claimed` + +### 6) d4:ad:fc:ea:7f:65 +- Last known IP: `192.168.1.101` +- Vendor: `Shenzhen Intellirocks Tech co., ltd` +- Final disposition: `keep quarantined on Old IoT` +- Why: + - same reasoning as sibling Intellirocks device above + - no positive identification +- Follow-up class: `identify later / likely kill-candidate if never claimed` + +## Operational conclusion +The remaining Legacy CIA leftovers do not need migration work right now. + +The correct closeout posture is: +- leave all six unidentified leftovers on `Old IoT` +- do not promote them into `Trusted`, `Management`, or clean `IoT` +- treat them as explicit quarantine residents pending later identification or eventual retirement + +## What this means for the remaining task list +Legacy CIA closeout is complete as a policy/disposition decision. + +The remaining network cleanup work is now mainly: +- Google/cast validation and any resulting placement decisions +- final SSID simplification once Google behavior is understood +- optional future kill-pass for unclaimed legacy leftovers diff --git a/home/doris-dashboard/docs/unifi-minimum-safe-rule-skeleton-2026-05-23.md b/home/doris-dashboard/docs/unifi-minimum-safe-rule-skeleton-2026-05-23.md new file mode 100644 index 0000000..84eac1f --- /dev/null +++ b/home/doris-dashboard/docs/unifi-minimum-safe-rule-skeleton-2026-05-23.md @@ -0,0 +1,261 @@ +# UniFi Minimum Safe Rule Skeleton + +Purpose: convert the desired firewall design, the captured gap list, and the host-group proposal into the smallest sane live-apply rule set and exact operator order. This is still a planning artifact. It is written to minimize the chance of locking out admin access or accidentally broadening trust during first enforcement. + +Evidence used: +- `home/doris-dashboard/docs/network-firewall-rule-order.md` +- `home/doris-dashboard/docs/unifi-firewall-gap-list-2026-05-22.md` +- `home/doris-dashboard/docs/unifi-firewall-host-group-proposal-2026-05-22.md` +- `home/doris-dashboard/docs/network-migration-remaining-checklist-2026-05-22.md` +- skill guidance: `unifi-network-operations` + +## Executive summary + +Do not try to apply the entire final segmentation design in one shot. + +For the first real live firewall enforcement pass, the minimum safe skeleton should be: + +1. Stateful baseline +2. Management shield with explicit admin allows first +3. Trusted -> Servers allow +4. Guest internet-only pair +5. DNS/NTP minimum-function rules for restricted lanes +6. IoT/Cameras/Legacy broad internal deny matrix +7. Only after validation, consider helper exceptions and any Google/cast discovery carve-outs + +That order preserves the management plane, keeps the trusted human lane usable, and delays the riskiest discovery-sensitive exceptions until there is proof they are needed. + +## 1. Preconditions before any live apply + +Do not start the live rule phase unless all of these are true: + +- A trusted admin session is already working from Rocinante or another confirmed operator device +- `HOST-ADMIN-TRUSTED` is defined narrowly and correctly +- `HOST-CORE-SERVICES` is defined +- `HOST-DNS` is defined at least as `10.5.30.53` +- `NET-MGMT`, `NET-TRUSTED`, `NET-SERVERS`, `NET-IOT`, `NET-GUEST`, `NET-CAMERAS`, `NET-LEGACY-CIA`, and `NET-RFC1918-ALL` exist +- `PORT-DNS`, `PORT-NTP`, `PORT-WEB-ADMIN`, and `PORT-SSH` exist +- `HOST-PROTECT-SERVICES` is either verified or deliberately deferred +- Google/cast pilot is still treated as unresolved; do not guess its exceptions into day-one policy +- Legacy CIA devices remain quarantine-first; no broad trust promotion to make the rules easier + +## 2. Object set to have ready for day-one enforcement + +### Must-have now +- `HOST-ADMIN-TRUSTED` +- `HOST-CORE-SERVICES` +- `HOST-DNS` + +### Nice to have, but can be deferred if uncertain +- `HOST-PROTECT-SERVICES` +- `HOST-IOT-HELPERS` +- `HOST-CAMERA-HELPERS` +- `HOST-NTP` +- `HOST-LEGACY-EXCEPTIONS` + +### Port groups to use now +- `PORT-DNS` +- `PORT-NTP` +- `PORT-WEB-ADMIN` +- `PORT-SSH` + +### Port groups to defer +- `PORT-PROTECT` +- `PORT-CAST` + +## 3. Exact first-pass live apply order + +Top-to-bottom intended live order: + +### Block 1: Stateful safety baseline +1. `ALLOW Established/Related` + - Why first: keeps return traffic alive once restrictive rules appear + - Validation immediately after add: + - current UniFi UI session remains usable + - SSH from trusted admin box to one server still works + +2. `DROP Invalid` + - Why second: low drama hygiene rule, safe to place early + - Validation: + - no obvious reachability loss to controller or PD + +### Block 2: Management shield +3. `ALLOW Trusted Admin -> Management Admin Surfaces` + - Source: `HOST-ADMIN-TRUSTED` + - Destination: `NET-MGMT` + - Ports: `PORT-WEB-ADMIN`, `PORT-SSH` + - Validation: + - load UniFi from the trusted admin box + - SSH/ping at least one management-plane infra endpoint if applicable + +4. `ALLOW Trusted Admin -> Gateway Infra Utilities` + - Source: `HOST-ADMIN-TRUSTED` + - Destination: `NET-MGMT` + - Ports/protocols: ICMP and only other clearly required infra utilities + - Validation: + - gateway reachability checks still pass from the trusted admin box + +5. `DROP IoT -> Management` +6. `DROP Cameras -> Management` +7. `DROP Guest -> Management` +8. `DROP Legacy CIA -> Management` +9. `DROP Any Internal -> Management` + - Why this order: explicit admin allows must exist before the broad management shield closes + - Validation after the full block: + - UniFi still reachable from trusted admin box + - no urgent household functionality unexpectedly depended on talking to Management + - Rollback note: + - if management reachability breaks, disable/remove rule 9 first, then 8/7/6/5 in reverse order + +### Block 3: Preserve the human/operator lane +10. `ALLOW Trusted -> Servers Approved Access` + - Source: `NET-TRUSTED` + - Destination: `NET-SERVERS` + - Day-one recommendation: allow broadly enough to preserve normal operator/admin use, then tighten later if desired + - Validation: + - SSH to PD, Serenity, NOMAD, and Rocinante from trusted admin device + - key dashboards/apps reachable from Trusted + +11. `ALLOW Trusted -> Cameras Admin/Viewer Access` + - Recommendation: create disabled or defer unless you already know the exact need + - Reason: safer than inventing camera-viewer requirements blindly + +12. `ALLOW Trusted -> IoT Control Exceptions` + - Recommendation: do not enable broad versions of this on day one + - Reason: this is where cast/discovery sprawl sneaks in + +## 4. Minimum restricted-lane function block + +Apply this before the broad internal denies so the constrained lanes still have basic services. + +### Block 4A: DNS +13. `ALLOW IoT -> DNS` +14. `ALLOW Cameras -> DNS` +15. `ALLOW Legacy CIA -> DNS` + - Destination: `HOST-DNS` + - Ports: `PORT-DNS` + - Validation: + - one client on each lane still resolves DNS + +### Block 4B: NTP +16. `ALLOW IoT -> NTP` +17. `ALLOW Cameras -> NTP` +18. `ALLOW Legacy CIA -> NTP` + - Destination: + - if a real local NTP service is verified, use `HOST-NTP` + - otherwise model as outbound/public NTP according to UniFi’s rule model + - Validation: + - no obvious time-sync failures on representative devices + - Caution: + - do not pretend PD is the universal NTP server unless verified + +### Block 4C: Internet access +19. `ALLOW Guest -> Internet` +20. `ALLOW IoT -> Internet` +21. `ALLOW Cameras -> Internet Updates` +22. `ALLOW Legacy CIA -> Internet` + - Validation: + - guest gets internet but not local access + - IoT devices retain cloud/app functionality where expected + - cameras only keep expected update/cloud behavior + +## 5. Broad internal deny matrix + +Only add this after the basic function rules above are in place. + +23. `DROP Guest -> RFC1918/Internal` +24. `DROP IoT -> Trusted` +25. `DROP IoT -> Servers` +26. `DROP IoT -> Cameras` +27. `DROP Cameras -> Trusted` +28. `DROP Cameras -> Servers` +29. `DROP Cameras -> IoT` +30. `DROP Legacy CIA -> Trusted` +31. `DROP Legacy CIA -> Servers` +32. `DROP Legacy CIA -> Cameras` +33. `DROP Legacy CIA -> IoT` + +Validation after this block: +- guest can browse internet but cannot reach local RFC1918 targets +- IoT can still do DNS/NTP/internet, but cannot hit Trusted/Servers/Cameras except where later explicit exceptions exist +- Cameras can still do DNS/NTP/internet or Protect-only needs if that rule has been added +- Legacy CIA remains hospice-only and cannot laterally move inside the house + +Rollback note: +- if a constrained lane breaks in an unclear way, remove the most recent deny rule in reverse order before touching the earlier management block + +## 6. Rules to defer on the first live pass + +These are real design items, but they should not be guessed into the first enforcement wave. + +### Defer until verified +- `ALLOW Cameras -> Protect Services` +- `ALLOW Servers -> IoT Approved Helpers` +- `ALLOW IoT -> Approved Server Helpers` +- `ALLOW Legacy CIA -> Approved One-Off Exception` +- `ALLOW Trusted -> Cameras Admin/Viewer Access` if ports/needs are unknown +- `ALLOW Trusted -> IoT Control Exceptions` if it would be broad or discovery-heavy + +Why defer: +- `HOST-PROTECT-SERVICES` still wants verification +- `PORT-PROTECT` is intentionally unresolved +- Google/cast behavior is still pending a one-device pilot +- helper rules are where accidental over-permissive policy usually appears + +## 7. Suggested operator wave plan + +If this becomes a real live session, the safest waves are: + +### Wave 1: low-drama core +- rules 1-10 only +- stop and validate + +### Wave 2: restricted-lane minimum function +- rules 13-22 +- stop and validate + +### Wave 3: broad deny matrix +- rules 23-33 +- stop and validate + +### Wave 4: narrow helper/protect exceptions +- only after proof from real failures or explicit use-cases + +## 8. Validation checklist after each wave + +Minimum checks from a trusted admin endpoint: +- UniFi UI still loads +- SSH to PD works +- SSH to NOMAD works +- SSH to Serenity works +- dashboard/homepage still loads if expected + +Restricted-lane checks after waves 2 and 3: +- one Guest client has internet and cannot reach local RFC1918 targets +- one IoT client still has DNS/internet +- one Camera client still behaves normally +- one Legacy CIA client is still contained and not silently promoted by exception + +## 9. Blunt recommendation + +If doing the first live firewall enforcement pass soon, I would treat these as the true minimum safe starting set: + +Definitely include: +- rules 1-10 +- rules 13-22 +- rules 23-33 + +Do not force in yet unless verified: +- rule 11 +- rule 12 +- rules involving Protect, helper hosts, or cast/discovery exceptions + +That produces a real skeleton with management protection, operator reachability, guest internet-only posture, and broad quarantine behavior for IoT/Cameras/Legacy without inventing fragile discovery exceptions on day one. + +## 10. Immediate follow-up after this planning artifact + +Best non-disruptive next planning steps: +1. verify the final intended members of `HOST-ADMIN-TRUSTED` +2. decide whether `HOST-PROTECT-SERVICES` really equals `10.5.0.1` +3. complete the one-device Google/cast pilot before any cast/discovery exception design +4. only then translate this ordered skeleton into exact UniFi Policy Engine objects/payloads for live apply diff --git a/home/doris-dashboard/docs/unifi-ssid-cleanup-proposal-2026-05-23.md b/home/doris-dashboard/docs/unifi-ssid-cleanup-proposal-2026-05-23.md new file mode 100644 index 0000000..77ccee3 --- /dev/null +++ b/home/doris-dashboard/docs/unifi-ssid-cleanup-proposal-2026-05-23.md @@ -0,0 +1,131 @@ +# UniFi SSID Cleanup Proposal — 2026-05-23 + +Purpose: turn the remaining SSID cleanup into an exact low-risk keep/retire proposal before any live Wi-Fi changes. + +## Basis +Artifact-based planning only. No live SSID edits were made in this pass. + +Artifacts used: +- `unifi-live-preflight-snapshot-2026-05-22.md` +- `unifi-readonly-recon-2026-05-22.md` +- `unifi-client-rehome-results-2026-05-22.md` +- `google-cast-pilot-result-2026-05-23.md` +- `unifi-legacy-cia-closeout-2026-05-23.md` +- `network-redesign-plan.md` +- `network-redesign-implementation-runbook.md` + +## Current SSIDs seen in the last verified inventory +- `CIA Via` -> `Old IoT` +- `UNEF's Playhouse` -> `Camera` +- `Whiskey Neat Fuck Ice` -> `Trusted` +- `Yer a Wifi Harry` -> `Trusted` + +## Steady-state target +Long-term, the network should converge on: +- one Trusted SSID +- one IoT SSID +- one Guest SSID +- one Security SSID +- no Legacy CIA SSID +- no duplicate Trusted SSIDs unless a real compatibility reason remains + +## Exact keep / retire proposal + +### 1) `Yer a Wifi Harry` +- Proposed role: `keep` +- Lane: `Trusted` +- Why: + - already one of the two Trusted SSIDs + - should become the single main human/operator SSID + - keeping this as the surviving Trusted SSID lets the duplicate Trusted SSID be retired later without renaming everything at once +- Preconditions to call it final: + - trusted phones/laptops are healthy here + - no critical household endpoint still depends on the other Trusted SSID for compatibility + +### 2) `Whiskey Neat Fuck Ice` +- Proposed role: `temporary keep, then retire` +- Lane: `Trusted` +- Why: + - it is currently duplicate Trusted capacity, not a distinct policy lane + - the Google/cast pilot device `dc:e5:5b:8f:57:d2` was previously on this SSID and successfully landed on `IoT` / `CIA Via` via override + - duplicate Trusted SSIDs create policy ambiguity and make cleanup harder +- Retirement gate: + - confirm all remaining clients on this SSID are either: + - intentionally kept on Trusted and able to use `Yer a Wifi Harry`, or + - moved off Trusted entirely +- Safe retirement method: + 1. inspect current client list on `Whiskey Neat Fuck Ice` + 2. migrate or test any stragglers onto `Yer a Wifi Harry` + 3. disable `Whiskey Neat Fuck Ice` + 4. verify admin path and key household endpoints still behave + 5. only then consider deletion later + +### 3) `CIA Via` +- Proposed role: `temporary keep as quarantine/legacy bridge, then retire last` +- Lane: `Old IoT` / legacy quarantine +- Why: + - recent verified artifacts still show it serving the remaining Google/discovery-sensitive devices and the unidentified quarantine leftovers + - after the Google/cast pilot, the moved Google device reassociated and was seen on SSID `CIA Via` while logically landing on `IoT`; that means this SSID is still part of the current transition path and should not be yanked casually + - the legacy closeout explicitly keeps six unidentified devices quarantined on `Old IoT` +- Retirement gate: + - all intentionally kept devices have been moved to clean `IoT`, `Security`, or `Trusted` as appropriate + - any unclaimed leftovers are either retired/killed or deliberately left for a later maintenance window + - no household-critical Google/cast behavior still depends on this legacy SSID path +- Safe retirement method: + 1. complete human validation of the existing Google/cast pilot from a laptop + 2. decide whether more Google devices move to clean `IoT` now or stay deferred + 3. confirm the six quarantine leftovers are the only residents, or reduce further + 4. when `CIA Via` has no required clients left, disable it first + 5. observe for complaints / breakage + 6. delete only after a calm observation period + +### 4) `UNEF's Playhouse` +- Proposed role: `keep` +- Lane: `Camera` / `Security` +- Why: + - it is already mapped to the security lane + - doorbell and Protect-chime estate justify a distinct security SSID + - keeping security separate from generic IoT matches the redesign intent and future camera growth plan +- Preconditions to call it final: + - doorbell/chimes remain healthy + - no evidence that merging them into general IoT would be safer or simpler + +### 5) Guest SSID +- Proposed role: `create or enable separately when ready` +- Why: + - the target design calls for a true guest lane + - the last verified enabled-SSID list did not show a guest Wi-Fi SSID even though the Guest network object exists + - do not repurpose one of the current live SSIDs for Guest unless the live client list proves it is unused and the change window is calm +- Recommended approach: + - treat Guest as a separate controlled add, not part of the first retirement move + +## Recommended cleanup order +This is the safest order based on current evidence: + +1. Keep `Yer a Wifi Harry` as the surviving main Trusted SSID +2. Keep `UNEF's Playhouse` as the Security SSID +3. Validate the existing Google/cast pilot from the laptop and decide whether more Google devices can leave legacy paths +4. Leave `CIA Via` alive until the legacy/quarantine path is truly drained +5. Retire `Whiskey Neat Fuck Ice` before retiring `CIA Via` +6. Only after `CIA Via` is empty and no longer required, disable and later delete it +7. Add/enable a proper Guest SSID as a separate tidy-up step if still missing + +## Things not to do +- do not delete `CIA Via` just because the easy-value IoT devices already moved +- do not disable both Trusted SSIDs in the same change block +- do not rename and repurpose a live SSID in one step if disabling/creating separately would be clearer +- do not infer that Google/cast is solved globally from one successful pilot reassociation +- do not collapse Security into generic IoT unless there is a deliberate design change + +## Practical one-page operator version +If you want the shortest operator call: +- Keep now: `Yer a Wifi Harry`, `UNEF's Playhouse`, `CIA Via` +- Retire first: `Whiskey Neat Fuck Ice` once client list is clean +- Retire last: `CIA Via` only after Google/legacy drain is complete +- Add separately if needed: proper Guest SSID + +## Resulting remaining live work +Before any actual SSID cleanup, still do: +- laptop-side Plex / cast / discovery validation for the existing Google pilot +- one last live SSID/client inventory pull from the controller +- a per-SSID client count check immediately before disable actions