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
This commit is contained in:
2026-06-21 13:53:09 -07:00
parent 1c8fc09590
commit 00f5f1d491
4 changed files with 110 additions and 7 deletions

View File

@@ -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/<slug>/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:

View File

@@ -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/<x>/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/<x>/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 <name> <pubkey> <allowed-ip>` (gated), `vpn-rm-peer`,
`vpn-show-config <peer>` (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/<slug>/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 <site|all>` = 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

View File

@@ -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=<ro-user> --set password=<pw>
```
3. Verify: `bash .claude/skills/unifi-wifi/scripts/gw-audit.sh <site>` — 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/<slug>/unifi-ap-ssh \
--kind generic --name '<Client> UniFi AP device-auth SSH' --tag unifi \
--set username=<u> --set password=<pw>
```
4. Verify: `bash .../scripts/neighbor-collect.sh <site> clients/<slug>/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/<slug>/pfsense-firewall \
--kind generic --name '<Client> pfSense firewall' --tag pfsense \
--set host=<ip> --set username=<admin> --set password=<pw> [--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:
# <site_id>\t<cred_path>\t<port|->\t<site_name>
bash .claude/skills/unifi-wifi/scripts/gateway-map.sh validate # confirm the row resolves
```
3. Verify: `bash .../scripts/gw-audit.sh '<UOS site name>'` auto-selects the pfSense backend;
`bash .../scripts/gw-control.sh '<site>' fw-list` lists its rules. One-off without a map row:
add `--pfsense <slug-or-vault-path>`.
---
## 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
```

View File

@@ -20,9 +20,18 @@ SITEARG="${1:?usage: gw-audit.sh <site-name|id> [--pfsense <slug>]}"; 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")")