Abandon UniFi IaC, add manual network segmentation plan (#189)
## Summary - Abandon the UniFi Pulumi IaC approach after provider bugs caused a network outage (no-op update reset undeclared properties on the default LAN network) - Remove untracked IaC artifacts (`pulumi/unifi/`, `mise-tasks/unifi-preview`, `mise-tasks/unifi-up`) locally - Mark `add-unifi-pulumi-stack` plan as Abandoned with explanation - Create new `segment-home-network` plan for manual three-network segmentation (Main/IoT/Guest) via UX7 web UI - Rewrite UniFi reference card to remove all Pulumi/IaC references - Update plan and how-to indexes ## Test plan - [x] `docs-check-links` passes - [x] `docs-check-index` passes - [x] Pre-commit hooks pass - [ ] Review segmentation plan for completeness before executing manually 🤖 Generated with [Claude Code](https://claude.com/claude-code) Reviewed-on: https://forge.ops.eblu.me/eblume/blumeops/pulls/189
This commit is contained in:
parent
eec1edf43d
commit
657bb28fd1
7 changed files with 176 additions and 283 deletions
1
docs/changelog.d/segment-home-network.infra.md
Normal file
1
docs/changelog.d/segment-home-network.infra.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Abandon UniFi Pulumi IaC (provider bugs caused network outage); add manual three-network segmentation plan for UX7 web UI.
|
||||
|
|
@ -1 +0,0 @@
|
|||
Updated UniFi Pulumi plan: switched provider to ubiquiti-community/unifi, added config backup step, fixed safety guard and 1Password paths.
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
title: How-To
|
||||
modified: 2026-02-11
|
||||
modified: 2026-02-14
|
||||
tags:
|
||||
- how-to
|
||||
---
|
||||
|
|
@ -62,7 +62,8 @@ Migration and transition plans for upcoming infrastructure changes.
|
|||
| [[plans]] | Index of all plans |
|
||||
| [[completed]] | Completed plans archive |
|
||||
| [[migrate-forgejo-from-brew]] | Transition Forgejo from Homebrew to source-built binary |
|
||||
| [[add-unifi-pulumi-stack]] | Add Pulumi IaC for UniFi Express 7 |
|
||||
| [[add-unifi-pulumi-stack]] | Add Pulumi IaC for UniFi Express 7 (abandoned) |
|
||||
| [[segment-home-network]] | Manual three-network segmentation for UniFi Express 7 |
|
||||
| [[adopt-dagger-ci]] | Adopt Dagger as CI/CD build engine |
|
||||
| [[upstream-fork-strategy]] | Stacked-branch forking strategy for upstream projects |
|
||||
| [[adopt-oidc-provider]] | Deploy OIDC identity provider for SSO across services |
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
title: "Plan: Add UniFi Pulumi Stack"
|
||||
modified: 2026-02-13
|
||||
modified: 2026-02-14
|
||||
tags:
|
||||
- how-to
|
||||
- plans
|
||||
|
|
@ -10,258 +10,23 @@ tags:
|
|||
|
||||
# Plan: Add UniFi Pulumi Stack
|
||||
|
||||
> **Status:** Planned (not yet executed)
|
||||
> **Blocked by:** 1Password credential setup (API key)
|
||||
> **Status:** Abandoned
|
||||
> **Superseded by:** [[segment-home-network]]
|
||||
|
||||
## Background
|
||||
## Why Abandoned
|
||||
|
||||
The UniFi Express 7 (UX7) is the home WiFi router, currently unmanaged. This plan adds a Pulumi stack (`pulumi/unifi/`) to bring it under IaC control, following the same conventions as `pulumi/tailscale/` and `pulumi/gandi/`.
|
||||
Attempted Feb 2026 with the `ubiquiti-community/unifi` Terraform provider via `pulumi package add`. Two issues made the approach unviable:
|
||||
|
||||
### Why IaC for the Router?
|
||||
1. **API key auth skips UniFi OS auto-detection** ([provider bug #74](https://github.com/ubiquiti-community/terraform-provider-unifi/issues/74)) — requires username/password instead, which is unsuitable for IaC
|
||||
2. **"No-op" update on the default LAN network reset undeclared properties** — bricked the network, requiring a factory reset and backup restore
|
||||
|
||||
- **Reproducibility** — WiFi networks, firewall rules, and DHCP settings are declared in code
|
||||
- **Audit trail** — changes go through PR review like all other infrastructure
|
||||
- **Consistency** — joins the existing Pulumi stacks for Tailscale ACLs and DNS
|
||||
- **Network segmentation** — declare main/guest/IoT WiFi networks with proper firewall zones
|
||||
The provider ecosystem (ubiquiti-community, filipowm, pulumiverse) is too immature for critical single-device infrastructure like the home router. A provider bug that causes a network outage on a no-op update is an unacceptable risk.
|
||||
|
||||
### Ethernet Requirement (Resolved)
|
||||
## What Survives
|
||||
|
||||
The UX7 has one LAN port. Modifying WiFi settings over WiFi would sever the management connection mid-apply. This was resolved by installing:
|
||||
|
||||
1. **Two switches** (UniFi Switch Flex Mini recommended) daisy-chained:
|
||||
- Switch A by the router: connects UX7, sifaka
|
||||
- Switch B on the desk (~12ft cable): connects indri and gilbert
|
||||
2. **Cat6 Ethernet cables**: one ~12ft run between switches, plus short cables for each device
|
||||
|
||||
```
|
||||
UniFi Express 7 [LAN port]
|
||||
└── Switch A (by router/sifaka)
|
||||
├── sifaka (short cable)
|
||||
└── ~12ft Cat6 ──→ Switch B (on desk)
|
||||
├── indri (Cat6)
|
||||
└── gilbert (USB-C adapter)
|
||||
```
|
||||
|
||||
Daisy-chaining is standard Layer 2 networking — no speed loss per device, no subnet impact. The only shared bottleneck is the 1 Gbps uplink between the two switches, which is more than adequate for homelab use. UniFi Flex Minis will appear in the UX7's controller for monitoring and eventual Pulumi management.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before starting the execution session:
|
||||
|
||||
- [ ] Purchase 2x UniFi Switch Flex Mini (USW-Flex-Mini)
|
||||
- [ ] Purchase Cat6 cables: 1x ~12ft, 3-4x short (~3ft)
|
||||
- [ ] Cable everything up and verify all devices have network connectivity
|
||||
- [ ] Verify the machine running Pulumi (gilbert) has an active wired Ethernet connection as its default route: `route -n get default` should show a non-Wi-Fi interface
|
||||
- [ ] **Back up the UX7 configuration** via `https://192.168.1.1` → Settings → System → Backup, and download a `.unf` backup file. Store it safely before making any IaC changes. This provides a rollback path if a provider bug corrupts network or firewall state.
|
||||
- [ ] Create an API key on the UX7 via `https://192.168.1.1` → Settings → Control Plane → API (preferred over username/password for IaC)
|
||||
- [ ] Store UniFi API key in 1Password: vault `blumeops`, item `unifi`, category `API_CREDENTIAL`
|
||||
- [ ] Verify Pulumi CLI version is >= v3.147.0 (`pulumi version`)
|
||||
|
||||
## Provider: ubiquiti-community/unifi via `pulumi package add`
|
||||
|
||||
We use `pulumi package add terraform-provider ubiquiti-community/unifi` to consume the [ubiquiti-community fork](https://github.com/ubiquiti-community/terraform-provider-unifi) of the UniFi Terraform provider directly from Pulumi. This approach:
|
||||
|
||||
- **Generates a local Python SDK** in `./sdks/unifi/` and adds a package reference to `Pulumi.yaml`
|
||||
- **Supports API key authentication** — cleaner than username/password for IaC
|
||||
- **Actively maintained** — v0.41.12 (Jan 2026), responsive maintainer, 12 releases since Oct 2025
|
||||
- **Uses Pulumi Cloud state** — same as existing stacks, free tier
|
||||
- **No fork/bridge maintenance** — just re-run `pulumi package add` on new provider versions
|
||||
- **Broader ecosystem** — part of the [ubiquiti-community](https://github.com/ubiquiti-community) org alongside go-unifi, unifi-api, and other tools
|
||||
|
||||
### Why Not Other Providers?
|
||||
|
||||
| Provider | Why Not |
|
||||
|----------|---------|
|
||||
| `pulumiverse_unifi` | Bridges `paultyng/terraform-provider-unifi`, which is abandoned. No API key auth, no newer resource types. |
|
||||
| `filipowm/unifi` | Maintainer unresponsive since April 2025. Critical bug ([#94](https://github.com/filipowm/terraform-provider-unifi/issues/94)): applying `unifi_network` resources wipes all zone-based firewall rules. Unmerged community fix PRs. |
|
||||
| `paultyng/unifi` | Abandoned since March 2023. No API key auth, no zone-based firewall. |
|
||||
|
||||
### Zone-Based Firewall: Deferred
|
||||
|
||||
The ubiquiti-community provider does not yet support zone-based firewall resources ([#77](https://github.com/ubiquiti-community/terraform-provider-unifi/issues/77)). Zone-based firewall rules will be managed manually in the UX7 web UI until provider support lands. This is acceptable because:
|
||||
|
||||
- The initial goal is bringing networks, WLANs, and DHCP under IaC
|
||||
- Network segmentation (which needs firewall zones) is a future phase
|
||||
- The filipowm provider — the only one with zone firewall support — has a showstopper bug that makes it unusable for this purpose anyway
|
||||
|
||||
## Network Segmentation Goals
|
||||
|
||||
Once the stack is operational, we plan to configure these network zones:
|
||||
|
||||
| Network | VLAN | Subnet | Purpose | Devices |
|
||||
|---------|------|--------|---------|---------|
|
||||
| BlumeOps Services | TBD | `192.168.10.0/24` | Infrastructure and services | indri, sifaka, k8s pods |
|
||||
| User Devices | 1 | `192.168.1.0/24` | Trusted personal devices | gilbert, ringtail |
|
||||
| Guest | TBD | `192.168.2.0/24` | Guest WiFi, internet-only | Visitors |
|
||||
| IoT / Appliances | TBD | `192.168.3.0/24` | Smart devices, isolated | Frame TV, dishwasher, etc. |
|
||||
|
||||
### Motivation: NFS Share Exposure
|
||||
|
||||
The immediate security driver for segmentation is NFS. Currently, sifaka's NFS exports (`/volume1/torrents`, `/volume1/music`, `/volume1/photos`) whitelist `192.168.1.0/24` and `100.64.0.0/10` (Docker NAT). This means **any device on the WiFi** — including IoT appliances, guest devices, or a compromised smart TV — can mount and write to these shares.
|
||||
|
||||
After segmentation, NFS exports will be restricted to the BlumeOps Services subnet (`192.168.10.0/24`) and the Docker NAT range (`100.64.0.0/10`). Only indri, sifaka, and k8s pods will have NFS access.
|
||||
|
||||
### Zone-Based Firewall Rules
|
||||
|
||||
| Source | Destination | Policy |
|
||||
|--------|-------------|--------|
|
||||
| BlumeOps Services | Internet | Allow |
|
||||
| BlumeOps Services | User Devices | Allow (for management, e.g., SSH from ringtail) |
|
||||
| User Devices | BlumeOps Services | Allow (trusted users need access to services) |
|
||||
| User Devices | Internet | Allow |
|
||||
| Guest | Internet | Allow |
|
||||
| Guest | All other zones | **Block** |
|
||||
| IoT / Appliances | Internet | Allow |
|
||||
| IoT / Appliances | User Devices | **Block** (except mDNS for AirPlay/casting) |
|
||||
| IoT / Appliances | BlumeOps Services | **Allow specific ports** (Jellyfin, Navidrome for streaming) |
|
||||
|
||||
### NFS Export Changes
|
||||
|
||||
After the network migration, update sifaka's NFS export rules:
|
||||
|
||||
| Share | Before | After |
|
||||
|-------|--------|-------|
|
||||
| `/volume1/torrents` | `192.168.1.0/24`, `100.64.0.0/10` | `192.168.10.0/24`, `100.64.0.0/10` |
|
||||
| `/volume1/music` | `192.168.1.0/24`, `100.64.0.0/10` | `192.168.10.0/24`, `100.64.0.0/10` |
|
||||
| `/volume1/photos` | `192.168.1.0/24`, `100.64.0.0/10` | `192.168.10.0/24`, `100.64.0.0/10` |
|
||||
|
||||
This is a manual change in the Synology DSM NFS settings (not managed by Pulumi — sifaka's NFS config is outside the UniFi provider's scope). The k8s PersistentVolume definitions (`argocd/manifests/*/pv-nfs.yaml`) resolve sifaka by hostname and don't need subnet changes.
|
||||
|
||||
These will be declared after the initial import is stable.
|
||||
|
||||
## Pulumi Stack Structure
|
||||
|
||||
Following the conventions of `pulumi/tailscale/` and `pulumi/gandi/`:
|
||||
|
||||
```
|
||||
pulumi/unifi/
|
||||
├── Pulumi.yaml # name: blumeops-unifi, python runtime, uv toolchain
|
||||
│ # includes parameterized package reference for ubiquiti-community/unifi
|
||||
├── Pulumi.home-network.yaml # Stack config: router_url, site
|
||||
├── pyproject.toml # Python >=3.11, pulumi>=3.0.0
|
||||
├── sdks/unifi/ # Generated Python SDK from pulumi package add
|
||||
│ └── ... # (auto-generated, committed to repo)
|
||||
├── __main__.py # Main program with safety guard
|
||||
├── .gitignore # .venv/, __pycache__/, *.py[cod]
|
||||
└── uv.lock # Generated by uv sync, committed
|
||||
```
|
||||
|
||||
### Provider Configuration
|
||||
|
||||
**Authentication** (via environment variables in mise tasks):
|
||||
|
||||
| Variable | Value | Notes |
|
||||
|----------|-------|-------|
|
||||
| `UNIFI_API_KEY` | `op read "op://blumeops/unifi/credential"` | API key created in UX7 control plane |
|
||||
| `UNIFI_API` | `https://192.168.1.1` | No `/api` suffix — SDK auto-discovers `/proxy/network` for UniFi OS |
|
||||
| `UNIFI_INSECURE` | `true` | UX7 uses a self-signed TLS certificate |
|
||||
|
||||
### Safety Guard
|
||||
|
||||
The `__main__.py` must fail fast before creating any Pulumi resources if the default network route goes through Wi-Fi. This prevents accidentally modifying WiFi settings while connected over WiFi (which would sever the management connection mid-apply).
|
||||
|
||||
The check works as follows:
|
||||
|
||||
1. Run `route -n get default` and extract the `interface:` field (e.g., `en5`)
|
||||
2. Run `networksetup -listallhardwareports` and find which hardware port owns that interface
|
||||
3. If the hardware port is `Wi-Fi`, abort with an error
|
||||
|
||||
This is host-agnostic — it works on both gilbert (where the Ethernet adapter is `AX88179A` on `en5`) and indri (where it's `Ethernet` on `en0`).
|
||||
|
||||
## Execution Steps
|
||||
|
||||
### Step 1: Create Stack Directory and Install Provider
|
||||
|
||||
```fish
|
||||
mkdir -p pulumi/unifi
|
||||
cd pulumi/unifi
|
||||
# Create Pulumi.yaml, pyproject.toml, .gitignore, __main__.py
|
||||
pulumi package add terraform-provider ubiquiti-community/unifi
|
||||
# This generates sdks/unifi/ and updates Pulumi.yaml with package reference
|
||||
pulumi install
|
||||
uv sync
|
||||
```
|
||||
|
||||
### Step 2: Create Stack Files
|
||||
|
||||
Create `Pulumi.yaml`, `Pulumi.home-network.yaml`, `pyproject.toml`, `.gitignore`, and `__main__.py`. The main program should declare:
|
||||
|
||||
- **Ethernet safety guard** (verify default route is not Wi-Fi)
|
||||
- **Default LAN network** resource (corporate, `192.168.1.0/24`, DHCP)
|
||||
- **WiFi WLAN** resources (commented out initially — need SSID names and IDs from the controller)
|
||||
- **Exports** for router IP, network ID, subnet
|
||||
|
||||
### Step 3: Create Mise Tasks
|
||||
|
||||
Create `mise-tasks/unifi-preview` and `mise-tasks/unifi-up` following the pattern from `tailnet-up`/`dns-up`:
|
||||
|
||||
```bash
|
||||
UNIFI_API_KEY=$(op read "op://blumeops/unifi/credential")
|
||||
export UNIFI_API="https://192.168.1.1"
|
||||
export UNIFI_INSECURE="true"
|
||||
```
|
||||
|
||||
### Step 4: Initialize Stack
|
||||
|
||||
```fish
|
||||
cd pulumi/unifi
|
||||
uv sync
|
||||
pulumi stack init home-network
|
||||
```
|
||||
|
||||
### Step 5: Import Existing Resources
|
||||
|
||||
Discover resource IDs from the UniFi controller API or web UI, then import:
|
||||
|
||||
```fish
|
||||
# Import default network
|
||||
pulumi import unifi:index/network:Network default-lan <network-id>
|
||||
|
||||
# Later, import WLANs
|
||||
pulumi import unifi:index/wlan:Wlan home-wifi <wlan-id>
|
||||
```
|
||||
|
||||
Adjust `__main__.py` resource properties to match the actual controller state until `pulumi preview` shows no diff.
|
||||
|
||||
### Step 6: Documentation Updates
|
||||
|
||||
- Update `docs/reference/infrastructure/unifi.md` — remove `(planned)` markers, update provider to ubiquiti-community
|
||||
- Add changelog fragment
|
||||
|
||||
### Step 7: Verify
|
||||
|
||||
- `mise run unifi-preview` shows no unexpected diffs
|
||||
- Pre-commit hooks pass
|
||||
- `docs-check-links` and `docs-check-index` pass
|
||||
|
||||
## Known Limitations
|
||||
|
||||
- **macOS-specific guard** — the `networksetup` and `route` checks only work on macOS, which is fine since the stack is run from gilbert or indri, both permanently macOS
|
||||
- **User group ID discovery** — the provider may not expose a `get_user_group` data source. Must be discovered manually from the controller API (`/proxy/network/api/s/default/rest/usergroup`) and hardcoded
|
||||
|
||||
## Future Considerations
|
||||
|
||||
- **Zone-based firewall rules** — manage via Pulumi once ubiquiti-community adds support ([#77](https://github.com/ubiquiti-community/terraform-provider-unifi/issues/77)). Until then, configure manually in the UX7 web UI.
|
||||
- **Network segmentation** — depends on zone-based firewall support; see goals above
|
||||
- **UnPoller** — add Prometheus metrics exporter for UniFi gear, integrates with existing Grafana stack
|
||||
- **Switch management** — manage the USW-Flex-Minis via the same Pulumi stack once adopted into the UX7 controller
|
||||
- **Provider updates** — re-run `pulumi package add terraform-provider ubiquiti-community/unifi` to update
|
||||
|
||||
## Reference Pattern Files
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `pulumi/tailscale/__main__.py` | Pulumi program pattern (resources, exports, data sources) |
|
||||
| `pulumi/gandi/__main__.py` | Config resolution pattern (`pulumi.Config().require()`) |
|
||||
| `pulumi/tailscale/Pulumi.yaml` | Project definition pattern |
|
||||
| `pulumi/gandi/Pulumi.eblu-me.yaml` | Stack config pattern |
|
||||
| `mise-tasks/tailnet-up` | Mise task credential pattern (`op read`) |
|
||||
| `docs/reference/infrastructure/gandi.md` | Infrastructure reference card pattern |
|
||||
The network segmentation goals from this plan remain valid and are carried forward in [[segment-home-network]], which describes how to configure three-network segmentation manually through the UX7 web UI.
|
||||
|
||||
## Related
|
||||
|
||||
- [[hosts]] - Device inventory (UniFi Express 7)
|
||||
- [[unifi]] - Reference card
|
||||
- [[power]] - UPS power chain
|
||||
- [[indri]] - Server connected via Cat6 Ethernet
|
||||
- [[tailscale]] - Tailnet networking
|
||||
- [[segment-home-network]] — Manual segmentation plan (replacement)
|
||||
- [[unifi]] — Reference card
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
title: Plans
|
||||
modified: 2026-02-11
|
||||
modified: 2026-02-14
|
||||
tags:
|
||||
- how-to
|
||||
- plans
|
||||
|
|
@ -15,7 +15,8 @@ Plans differ from regular how-to guides in that they describe work that has been
|
|||
| Plan | Status | Description |
|
||||
|------|--------|-------------|
|
||||
| [[migrate-forgejo-from-brew]] | Planned | Transition Forgejo from Homebrew to source-built binary with LaunchAgent |
|
||||
| [[add-unifi-pulumi-stack]] | Planned | Add Pulumi IaC for UniFi Express 7 home network |
|
||||
| [[add-unifi-pulumi-stack]] | Abandoned | Add Pulumi IaC for UniFi Express 7 (provider bugs — see doc) |
|
||||
| [[segment-home-network]] | Planned | Manual three-network segmentation for UniFi Express 7 |
|
||||
| [[upstream-fork-strategy]] | Planned | Stacked-branch forking strategy for tracking upstream projects |
|
||||
| [[adopt-oidc-provider]] | Planning | Deploy OIDC identity provider for SSO across services |
|
||||
| [[harden-zot-registry]] | Planned | Add authentication and tag immutability to zot registry |
|
||||
|
|
|
|||
124
docs/how-to/plans/segment-home-network.md
Normal file
124
docs/how-to/plans/segment-home-network.md
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
---
|
||||
title: "Plan: Segment Home Network"
|
||||
modified: 2026-02-14
|
||||
tags:
|
||||
- how-to
|
||||
- plans
|
||||
- networking
|
||||
---
|
||||
|
||||
# Plan: Segment Home Network
|
||||
|
||||
> **Status:** Planned (not yet executed)
|
||||
> **Replaces:** [[add-unifi-pulumi-stack]] (abandoned — provider bugs)
|
||||
|
||||
## Background
|
||||
|
||||
All devices currently share a single flat `192.168.1.0/24` network. This means IoT appliances (Frame TV, dishwasher) and guest devices can reach NFS shares, management interfaces, and all other services on the LAN.
|
||||
|
||||
This plan segments the home network into three zones using the UX7 web UI. The IaC approach was abandoned after the `ubiquiti-community/unifi` Terraform provider bricked the network on a no-op update — see [[add-unifi-pulumi-stack]] for details.
|
||||
|
||||
### Security Driver: NFS Exposure
|
||||
|
||||
Sifaka's NFS exports (`/volume1/torrents`, `/volume1/music`, `/volume1/photos`) whitelist `192.168.1.0/24`. Today, **any device on the WiFi** — including IoT appliances or guest devices — can mount and write to these shares. After segmentation, only Main network devices (192.168.1.0/24) have NFS access. IoT (192.168.3.0/24) and Guest (192.168.2.0/24) are on different subnets and cannot reach NFS even without firewall rules.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [ ] **Back up the UX7 configuration** via `https://192.168.1.1` → Settings → System → Backup. Download the `.unf` backup file before making any changes.
|
||||
- [ ] Verify all wired devices (indri, sifaka, gilbert) have connectivity
|
||||
- [ ] Know which devices should go on each network
|
||||
|
||||
## Three Networks
|
||||
|
||||
| Network | SSID | VLAN | Subnet | Bands | Purpose |
|
||||
|---------|------|------|--------|-------|---------|
|
||||
| Main | Radio New Vegas | 1 (default) | 192.168.1.0/24 | All | Trusted devices (indri, sifaka, gilbert, mouse) |
|
||||
| IoT | (TBD by user) | 3 | 192.168.3.0/24 | 2.4GHz only | Smart devices (Frame TV, appliances) |
|
||||
| Guest | (TBD by user) | 2 | 192.168.2.0/24 | All | Visitors, internet-only |
|
||||
|
||||
## UX7 Configuration Steps
|
||||
|
||||
All configuration is done through the UX7 web UI at `https://192.168.1.1`.
|
||||
|
||||
### 1. Create IoT Network
|
||||
|
||||
Settings → Networks → Create New:
|
||||
|
||||
- **Name:** IoT
|
||||
- **VLAN ID:** 3
|
||||
- **Gateway/Subnet:** 192.168.3.1/24
|
||||
- **DHCP:** Enabled, range 192.168.3.6–192.168.3.254
|
||||
|
||||
### 2. Create Guest Network
|
||||
|
||||
Settings → Networks → Create New:
|
||||
|
||||
- **Name:** Guest
|
||||
- **VLAN ID:** 2
|
||||
- **Gateway/Subnet:** 192.168.2.1/24
|
||||
- **DHCP:** Enabled, range 192.168.2.6–192.168.2.254
|
||||
|
||||
### 3. Create IoT WLAN
|
||||
|
||||
Settings → WiFi → Create New:
|
||||
|
||||
- **SSID:** (user's choice)
|
||||
- **Network:** IoT
|
||||
- **Band:** 2.4GHz only
|
||||
- **Security:** WPA2/WPA3
|
||||
|
||||
### 4. Create Guest WLAN
|
||||
|
||||
Settings → WiFi → Create New:
|
||||
|
||||
- **SSID:** (user's choice)
|
||||
- **Network:** Guest
|
||||
- **Security:** WPA2/WPA3
|
||||
- **Guest policies:** Enabled (client isolation)
|
||||
|
||||
### 5. Enable mDNS Reflector
|
||||
|
||||
Settings → Networks → Global Network Settings:
|
||||
|
||||
- Enable **Multicast DNS** — this allows AirPlay/casting discovery across VLANs (Main ↔ IoT)
|
||||
|
||||
## Firewall Rules (Zone-Based)
|
||||
|
||||
Configured at Settings → Firewall & Security → Firewall Rules.
|
||||
|
||||
UX7 zones correspond to networks. Default inter-VLAN policy is **allow**, so we add **block** rules. **Rule ordering matters** — allow rules must come before matching block rules.
|
||||
|
||||
| # | Name | Action | Source | Destination | Protocol/Port | Notes |
|
||||
|---|------|--------|--------|-------------|---------------|-------|
|
||||
| 1 | Guest → LAN block | Block | Guest | Main | All | Internet-only isolation |
|
||||
| 2 | Guest → IoT block | Block | Guest | IoT | All | No cross-zone access |
|
||||
| 3 | IoT → Main streaming allow | Allow | IoT | Main (indri IP) | TCP 443 | Jellyfin/Navidrome via Caddy — must be BEFORE the block rule |
|
||||
| 4 | IoT → Main block | Block | IoT | Main | All | Protect NFS and trusted devices |
|
||||
|
||||
### Notes on Firewall Rules
|
||||
|
||||
**IoT streaming:** Jellyfin (port 8096) and Navidrome bind behind [[caddy]] on indri:443. IoT devices (Frame TV) access media via `https://jellyfin.ops.eblu.me` which resolves to indri's LAN IP. Rule 3 allows IoT → indri:443 only. All other Main network access from IoT is blocked by rule 4.
|
||||
|
||||
**NFS exports:** No changes needed to sifaka's NFS configuration. The exports whitelist `192.168.1.0/24` — after segmentation, only Main network devices are on that subnet. IoT (192.168.3.0/24) and Guest (192.168.2.0/24) can't reach NFS because they're on different subnets. The firewall rules provide defense-in-depth.
|
||||
|
||||
## Verification
|
||||
|
||||
After applying the configuration:
|
||||
|
||||
- [ ] From Main device: internet works, can reach all services, can mount NFS
|
||||
- [ ] From IoT device: internet works, can stream Jellyfin, CANNOT mount NFS
|
||||
- [ ] From Guest device: internet works, CANNOT reach any internal service
|
||||
- [ ] AirPlay/casting from Main to IoT TV works (mDNS reflector)
|
||||
- [ ] All wired devices (indri, sifaka, gilbert) unaffected on default VLAN
|
||||
|
||||
## Future Considerations
|
||||
|
||||
- **UnPoller** — add Prometheus metrics exporter for UniFi gear, integrates with existing Grafana stack
|
||||
- **IaC revisit** — if the ubiquiti-community provider matures and fixes the destructive-update bug, IaC could be reconsidered
|
||||
|
||||
## Related
|
||||
|
||||
- [[add-unifi-pulumi-stack]] — Previous IaC approach (abandoned)
|
||||
- [[unifi]] — Reference card
|
||||
- [[hosts]] — Device inventory
|
||||
- [[power]] — UPS power chain
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
title: UniFi
|
||||
modified: 2026-02-10
|
||||
modified: 2026-02-14
|
||||
tags:
|
||||
- infrastructure
|
||||
- networking
|
||||
|
|
@ -8,7 +8,7 @@ tags:
|
|||
|
||||
# UniFi
|
||||
|
||||
Home WiFi router and network controller, managed via Pulumi IaC.
|
||||
Home WiFi router and network controller, managed via the UX7 web UI.
|
||||
|
||||
## Quick Reference
|
||||
|
||||
|
|
@ -17,8 +17,7 @@ Home WiFi router and network controller, managed via Pulumi IaC.
|
|||
| **Model** | UniFi Express 7 (UX7) |
|
||||
| **LAN IP** | `192.168.1.1` |
|
||||
| **Management URL** | `https://192.168.1.1` |
|
||||
| **IaC** | `pulumi/unifi/` (planned) |
|
||||
| **Stack** | `home-network` (planned) |
|
||||
| **Management** | Web UI only (no IaC — see [[add-unifi-pulumi-stack]]) |
|
||||
| **Power** | Battery-backed via UPS (see [[power]]) |
|
||||
|
||||
## What It Does
|
||||
|
|
@ -26,9 +25,19 @@ Home WiFi router and network controller, managed via Pulumi IaC.
|
|||
The UX7 is the home WiFi access point and network gateway. It provides:
|
||||
|
||||
- WiFi (main, guest, IoT networks)
|
||||
- DHCP for `192.168.1.0/24`
|
||||
- Built-in UniFi controller for managing adopted devices (switches, APs)
|
||||
- Firewall and traffic management
|
||||
- DHCP for all network subnets
|
||||
- Built-in UniFi controller for managing adopted devices (switches)
|
||||
- Zone-based firewall and traffic management
|
||||
|
||||
## Networks
|
||||
|
||||
| Network | VLAN | Subnet | Purpose |
|
||||
|---------|------|--------|---------|
|
||||
| Main | 1 (default) | 192.168.1.0/24 | Trusted devices (indri, sifaka, gilbert, mouse) |
|
||||
| Guest | 2 | 192.168.2.0/24 | Visitors, internet-only |
|
||||
| IoT | 3 | 192.168.3.0/24 | Smart devices (Frame TV, appliances) |
|
||||
|
||||
See [[segment-home-network]] for the full segmentation plan and firewall rules.
|
||||
|
||||
## Network Topology
|
||||
|
||||
|
|
@ -42,36 +51,29 @@ ISP Modem
|
|||
└── gilbert (USB-C adapter)
|
||||
```
|
||||
|
||||
All wired devices share the `192.168.1.0/24` subnet. The two daisy-chained UniFi Switch Flex Minis provide enough ports for all devices while using the UX7's single LAN port.
|
||||
|
||||
## Pulumi Configuration (Planned)
|
||||
|
||||
The Pulumi program will live in `pulumi/unifi/`:
|
||||
|
||||
- `__main__.py` — declares networks, WLANs, and firewall zones
|
||||
- `Pulumi.home-network.yaml` — stack config (router URL, site)
|
||||
- `sdks/unifi/` — generated Python SDK from `pulumi package add terraform-provider filipowm/unifi`
|
||||
|
||||
Provider: [filipowm/terraform-provider-unifi](https://github.com/filipowm/terraform-provider-unifi) v1.0.0, consumed via `pulumi package add terraform-provider`.
|
||||
|
||||
See [[add-unifi-pulumi-stack]] for the full implementation plan.
|
||||
All wired devices share the default VLAN (192.168.1.0/24). The two daisy-chained UniFi Switch Flex Minis provide enough ports for all devices while using the UX7's single LAN port.
|
||||
|
||||
## Operations
|
||||
|
||||
| Task | Command |
|
||||
|------|---------|
|
||||
| Preview changes | `mise run unifi-preview` (planned) |
|
||||
| Apply changes | `mise run unifi-up` (planned) |
|
||||
| Web management | `https://192.168.1.1` |
|
||||
| Task | Method |
|
||||
|------|--------|
|
||||
| Manage networks/WiFi/firewall | `https://192.168.1.1` web UI |
|
||||
| Backup configuration | Settings → System → Backup |
|
||||
| Restore from backup | Settings → System → Backup → Restore |
|
||||
|
||||
## Authentication
|
||||
|
||||
The provider uses an API key created in the UX7 control plane (Settings → Control Plane → API). The key is stored in 1Password (`op://blumeops/unifi/credential`) and injected via mise task environment variables.
|
||||
Local admin account on the UX7. Credentials stored in 1Password (vault `blumeops`). WiFi passphrase stored in 1Password item "Radio New Vegas" (Wireless Router type) in vault `blumeops`.
|
||||
|
||||
## Why Not IaC?
|
||||
|
||||
Attempted Feb 2026 with the `ubiquiti-community/unifi` Terraform provider via Pulumi. A "no-op" update on the default LAN network reset undeclared properties, bricking the network and requiring a factory reset. The provider ecosystem is too immature for single-device infrastructure. See [[add-unifi-pulumi-stack]] for details.
|
||||
|
||||
## Related
|
||||
|
||||
- [[add-unifi-pulumi-stack]] - Implementation plan
|
||||
- [[hosts]] - Device inventory
|
||||
- [[power]] - UPS power chain
|
||||
- [[indri]] - Primary server (wired connection required for management)
|
||||
- [[tailscale]] - Tailnet networking
|
||||
- [[segment-home-network]] — Network segmentation plan
|
||||
- [[add-unifi-pulumi-stack]] — Previous IaC approach (abandoned)
|
||||
- [[hosts]] — Device inventory
|
||||
- [[power]] — UPS power chain
|
||||
- [[indri]] — Primary server (wired connection)
|
||||
- [[tailscale]] — Tailnet networking
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue