docs: record unifi firewall enforcement and closeout
Some checks failed
secret-guardrails / artifact-secret-scan (push) Has been cancelled
secret-guardrails / gitleaks (push) Has been cancelled

This commit is contained in:
Fizzlepoof
2026-05-23 02:08:15 +00:00
parent bec21292de
commit 462b39e572
14 changed files with 841 additions and 41 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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`)

View File

@@ -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.

View File

@@ -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/<user_id>` with:
- `virtual_network_override_enabled=true`
- `virtual_network_override_id=6963cb461131084f05461642`
2. `POST cmd/stamgr` with `{"cmd":"kick-sta","mac":"<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

View File

@@ -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.

View File

@@ -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`

View File

@@ -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.

View File

@@ -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.

View File

@@ -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`

View File

@@ -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

View File

@@ -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

View File

@@ -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 UniFis 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

View File

@@ -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