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:
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
73
.claude/skills/unifi-wifi/references/onboarding.md
Normal file
73
.claude/skills/unifi-wifi/references/onboarding.md
Normal 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
|
||||
```
|
||||
@@ -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")")
|
||||
|
||||
Reference in New Issue
Block a user