From 00f5f1d49180a4523d2908a076f2a367a90b644b Mon Sep 17 00:00:00 2001 From: Howard Enos Date: Sun, 21 Jun 2026 13:53:09 -0700 Subject: [PATCH] sync: auto-sync from HOWARD-HOME at 2026-06-21 13:52:24 Author: Howard Enos Machine: HOWARD-HOME Timestamp: 2026-06-21 13:52:24 --- .claude/skills/unifi-wifi/SKILL.md | 2 + .../skills/unifi-wifi/references/ROADMAP.md | 27 ++++++- .../unifi-wifi/references/onboarding.md | 73 +++++++++++++++++++ .claude/skills/unifi-wifi/scripts/gw-audit.sh | 15 +++- 4 files changed, 110 insertions(+), 7 deletions(-) create mode 100644 .claude/skills/unifi-wifi/references/onboarding.md diff --git a/.claude/skills/unifi-wifi/SKILL.md b/.claude/skills/unifi-wifi/SKILL.md index 7ce32f58..7eca6a3e 100644 --- a/.claude/skills/unifi-wifi/SKILL.md +++ b/.claude/skills/unifi-wifi/SKILL.md @@ -20,6 +20,8 @@ live-stats, model-rank, optimize-radios, apply-radio) work on ANY site with **ze watch-ap) additionally need that client's `clients//unifi-ap-ssh` vaulted + L3 reach (site VPN); if missing they print the exact vault command and exit (controller-side still works). Default AP-cred path is Cascades — override with the script's vault-path arg per client. +**Onboarding a new client** (AP-side collectors, pfSense gateway control, read-only controller cred): +copy-paste procedures in **`references/onboarding.md`**. ## Status (2026-06-15) - **[WORKING] WiFi monitoring + RF tuning** — complete data-gathering for any UOS site/client: diff --git a/.claude/skills/unifi-wifi/references/ROADMAP.md b/.claude/skills/unifi-wifi/references/ROADMAP.md index 44071226..975d6f39 100644 --- a/.claude/skills/unifi-wifi/references/ROADMAP.md +++ b/.claude/skills/unifi-wifi/references/ROADMAP.md @@ -45,9 +45,12 @@ side, multi-client enablement, and non-WiFi scope. Build/validate new apply acti `survey-collect` now emits SURVEY_JSON for it. ## B. Multi-client enablement (use on any client we manage) +> Procedure is prepped + copy-paste ready in **`references/onboarding.md`** (§B). Each item below is blocked +> only on the external input (a cred / site reach), not on any code. - [ ] Per-client AP device-auth cred: vault `clients//unifi-ap-ssh`, pass as the script arg (only - Cascades exists today). Keys vaulted per-client as needed. + Cascades exists today). Keys vaulted per-client as needed. → steps: onboarding.md §B1. - [ ] Per-client L3 reach to APs (site VPN / route) for the AP-side collectors (Cascades split-tunnel done). +- [ ] (pfSense sites) vault `clients//pfsense-firewall` + add a gateway-map row → onboarding.md §B2. - [x] Controller-only mode documented + discoverable — `scripts/sites.sh` lists all sites + AP-cred readiness; controller-side scripts validated on other clients (Glabman, Sonoran Glass). AP-side scripts print the exact vault command when a client's cred is missing (and note controller-side works). @@ -65,15 +68,31 @@ side, multi-client enablement, and non-WiFi scope. Build/validate new apply acti set-ports/set-src, fw-disable/enable, block-ips (RW REST: rest/portforward|firewallrule|firewallgroup, gated/DRY-RUN, rollback). Dry-run validated 2026-06-16 on Grabb & Durando USG-3P (closes the brute-forced PPTP forward 1723→GND-SERVER + its GRE WAN_IN rule). First production apply pending. -- [ ] **Deeper VPN policy** — gateway-hosted VPN *server* stand-up (USG L2TP/IPsec remote-user, RADIUS), - client DHCP/DNS policy. Config beyond port-forward/WAN-firewall; future. Access layer reaches it. +- [ ] **Deeper VPN policy** — gateway-hosted VPN *server* stand-up + client DHCP/DNS policy. Build-ready + design below; no external input needed to START (Cascades pfSense is reachable for dev), so this is the next + real *feature* build when we choose to take it. The motivating play: retire client Windows RRAS PPTP + (cf. Grabb) onto a modern gateway VPN. + - **Backend split (mirror the gateway compatibility layer):** pfSense is the better VPN host -> do it there + first via the existing SSH backend; UniFi USG (L2TP/IPsec + RADIUS) is a later parallel driver. + - **pfSense WireGuard (preferred) — verbs to add to `pfsense-gwc.php` + `pfsense-ssh.sh`:** + `vpn-list` (tunnels + peers, read), `vpn-add-peer ` (gated), `vpn-rm-peer`, + `vpn-show-config ` (emit the client config/QR text to hand off via discord-dm). pfSense 25.07 has + native WireGuard (`/usr/local/pkg/wireguard` config in `$config`); same write_config()+`(rc.)reload` + pattern as the pf-*/fw-* verbs. OpenVPN is the fallback for clients that need it. + - **DHCP/DNS policy:** read side already exists (`pfsense-ssh.sh dhcp`); add gated `dhcp-set-range` / + `dns-set` verbs through the same gwc.php path if/when needed. + - **Open decisions (for /shape-spec when we start):** WireGuard vs OpenVPN default per client; where peer + keys/configs are vaulted (`clients//vpn-*`); whether to drive UniFi USG VPN via REST or leave it + pfSense-only initially. Access layer + creds already reach the gateways. ## D. Robustness / ops - [x] **VPN-flap resilience** — AP-side loops now retry per AP (3x, capture-to-var so a failed try never appends partial data); dfs-check distinguishes unreachable from no-events. Validated (74/74). Still foreground. - [x] **Scheduling / fleet monitoring** — `monitor-run.sh ` = cron-friendly read-only health digest (gateway + switch/PoE flags + WiFi flag count) per site. Validated. (Cron the `all` sweep nightly.) -- [ ] Vault read-only `infrastructure/uos-server-network-api` (least-privilege; RW does double duty now). +- [ ] Vault read-only `infrastructure/uos-server-network-api` (least-privilege). **Code-ready:** `gw-audit` + already prefers this cred and auto-uses it once vaulted (falls back to RW + prints a hint until then). + One step — create the View-Only admin + vault it: **`references/onboarding.md` §D**. ## E. pfSense gateway support — gateway "compatibility layer" (NEW, proposed 2026-06-16) **Why:** a very common ACG topology is **UniFi APs/switches (on the UOS controller) behind a pfSense diff --git a/.claude/skills/unifi-wifi/references/onboarding.md b/.claude/skills/unifi-wifi/references/onboarding.md new file mode 100644 index 00000000..48a475e9 --- /dev/null +++ b/.claude/skills/unifi-wifi/references/onboarding.md @@ -0,0 +1,73 @@ +# unifi-wifi — per-client / per-cred onboarding (the "one step once you have X" procedures) + +Prep for the ROADMAP items that are blocked only on an external input (an account, a cred, site reach). +Each is written so it's a single, copy-paste step the moment the input exists. Run all vault writes via the +`vault` skill helper. Paths assume repo root. + +--- + +## §D — read-only controller cred (least privilege) + +**Why:** `gw-audit.sh` logs into the UniFi controller for a READ-ONLY audit. It currently falls back to the +RW admin (`infrastructure/uos-server-network-api-rw`) and prints a hint. The moment a read-only cred is +vaulted at `infrastructure/uos-server-network-api`, `gw-audit` uses it automatically — **no code change**. + +1. Create a read-only controller admin: UniFi OS (`https://unifi.azcomputerguru.com`, or `:11443`) -> + **Settings -> Admins & Users -> Admins -> Add** -> role **View Only** (limited admin). Use a local + account (not SSO) so it works for the REST login. Set a strong password. +2. Vault it: + ```bash + bash .claude/skills/vault/scripts/vault-helper.sh new infrastructure/uos-server-network-api \ + --kind generic --name 'UOS controller read-only API' --tag unifi \ + --set username= --set password= + ``` +3. Verify: `bash .claude/skills/unifi-wifi/scripts/gw-audit.sh ` — the "using RW controller cred" + hint should be gone (it now uses the RO cred). Writes (`gw-control --apply`) still use the RW cred. + +--- + +## §B — enable a client for AP-side collectors (and pfSense gateway control) + +`sites.sh` shows readiness; this is the full enable path. Controller-side scripts (audit-site, live-stats, +model-rank, optimize-radios, apply-radio, channel-plan) already work on ANY UOS site with zero setup — the +steps below are only for the **AP-side collectors** and the **pfSense gateway** backend. + +### B1. AP device-auth cred (for neighbor-collect / survey-collect / dfs-check / watch-ap) +1. Get the site's Device Authentication user/pass: UniFi OS -> **Settings -> System -> Device + Authentication** (per site). +2. Ensure **L3 reach** to that site's AP mgmt VLAN (site VPN / route) from the machine running the script. +3. Vault it: + ```bash + bash .claude/skills/vault/scripts/vault-helper.sh new clients//unifi-ap-ssh \ + --kind generic --name ' UniFi AP device-auth SSH' --tag unifi \ + --set username= --set password= + ``` +4. Verify: `bash .../scripts/neighbor-collect.sh clients//unifi-ap-ssh` (or pass the cred path + as the script's vault-path arg). `sites.sh` will show the client as `[ready]`. + +### B2. pfSense gateway control (if the site's gateway is a pfSense, not a UniFi USG/UXG/UCG) +1. Vault the pfSense admin cred (add `port` only if non-22, e.g. 2248 for the ACG office): + ```bash + bash .claude/skills/vault/scripts/vault-helper.sh new clients//pfsense-firewall \ + --kind generic --name ' pfSense firewall' --tag pfsense \ + --set host= --set username= --set password= [--set port=2248] + ``` + (Infra-vaulted boxes work too — e.g. `infrastructure/pfsense-firewall`; pass the full path, option A.) +2. Find the site_id and bind it for AUTO-SELECT (no `--pfsense` needed afterwards): + ```bash + bash .claude/skills/unifi-wifi/scripts/gateway-map.sh suggest # shows the site_id + that the cred is now vaulted + # add a TAB-separated row to references/site-gateways.tsv: + # \t\t\t + bash .claude/skills/unifi-wifi/scripts/gateway-map.sh validate # confirm the row resolves + ``` +3. Verify: `bash .../scripts/gw-audit.sh ''` auto-selects the pfSense backend; + `bash .../scripts/gw-control.sh '' fw-list` lists its rules. One-off without a map row: + add `--pfsense `. + +--- + +## Quick readiness check +```bash +bash .claude/skills/unifi-wifi/scripts/sites.sh # AP-cred readiness + gateway map +bash .claude/skills/unifi-wifi/scripts/gateway-map.sh suggest # unmapped pfSense sites + unbound creds +``` diff --git a/.claude/skills/unifi-wifi/scripts/gw-audit.sh b/.claude/skills/unifi-wifi/scripts/gw-audit.sh index 674d5c55..f331132a 100644 --- a/.claude/skills/unifi-wifi/scripts/gw-audit.sh +++ b/.claude/skills/unifi-wifi/scripts/gw-audit.sh @@ -20,9 +20,18 @@ SITEARG="${1:?usage: gw-audit.sh [--pfsense ]}"; shift || t PFARG="" # optional: pfSense client slug (or full vault path) when UOS site name != client slug while [ $# -gt 0 ]; do case "$1" in --pfsense) PFARG="${2:?--pfsense needs a slug/vault-path}"; shift 2;; *) shift;; esac; done TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT -U="$(bash "$VAULT" get-field infrastructure/uos-server-network-api-rw credentials.username 2>/dev/null)" -P="$(bash "$VAULT" get-field infrastructure/uos-server-network-api-rw credentials.password 2>/dev/null)" -[ -n "$U" ] && [ -n "$P" ] || { echo "[ERROR] no controller cred (infrastructure/uos-server-network-api-rw)"; exit 1; } +# §D least-privilege: this audit is READ-ONLY, so prefer a read-only controller cred when one is vaulted; +# fall back to the RW admin (which does double duty today). Vault the RO cred at infrastructure/ +# uos-server-network-api and it is used automatically — no code change needed (see references/onboarding.md). +CREDP="" +for c in infrastructure/uos-server-network-api infrastructure/uos-server-network-api-rw; do + if [ -n "$(bash "$VAULT" get-field "$c" credentials.password 2>/dev/null)" ]; then CREDP="$c"; break; fi +done +[ -n "$CREDP" ] || { echo "[ERROR] no controller cred (infrastructure/uos-server-network-api[-rw])"; logerr "no UOS controller cred vaulted" "tried RO+RW"; exit 1; } +case "$CREDP" in *-rw) echo "[INFO] using RW controller cred for read-only audit (vault a read-only cred at infrastructure/uos-server-network-api for least privilege)";; esac +U="$(bash "$VAULT" get-field "$CREDP" credentials.username 2>/dev/null)" +P="$(bash "$VAULT" get-field "$CREDP" credentials.password 2>/dev/null)" +[ -n "$U" ] && [ -n "$P" ] || { echo "[ERROR] incomplete controller cred at vault:$CREDP"; exit 1; } base="https://$HOST:$PORT"; CJ="$TMP/cj" code=$(curl -sk -c "$CJ" -o /dev/null -w '%{http_code}' -X POST "$base/api/auth/login" -H 'Content-Type: application/json' \ --data-binary "$(python -c 'import json,sys;print(json.dumps({"username":sys.argv[1],"password":sys.argv[2]}))' "$U" "$P")")