sync: auto-sync from HOWARD-HOME at 2026-06-21 13:04:37
Author: Howard Enos Machine: HOWARD-HOME Timestamp: 2026-06-21 13:04:37
This commit is contained in:
@@ -48,6 +48,10 @@ path is Cascades — override with the script's vault-path arg per client.
|
||||
- **[WORKING] pfSense gateway compatibility layer via SSH** — `scripts/pfsense-ssh.sh <slug> <verb>`.
|
||||
DECISION (Mike 2026-06-16): **no RESTAPI package needed** — VPN + SSH shell reads the same data and makes
|
||||
changes. Cred = `clients/<slug>/pfsense-firewall` (host + admin user/pass), system OpenSSH via askpass.
|
||||
**Cred path (Mike 2026-06-21, option A):** the 1st arg is a **full vault path** if it contains `/`
|
||||
(any infra-vaulted device, e.g. `pfsense-ssh.sh infrastructure/pfsense-firewall audit`), else a client
|
||||
slug -> `clients/<slug>/pfsense-firewall`. A path that resolves to no cred fails loud (`[ERROR] no cred
|
||||
at vault:<path>`). gw-audit/gw-control's `--pfsense` inherits the same convention.
|
||||
SSH port: `--port N` flag > optional vault `port`/`credentials.port` field > default 22 (e.g. the ACG
|
||||
office box on 2248 — store `port: 2248` in its vault entry). Validated live on Cascades (pfSense Plus 25.07).
|
||||
- **Reads (no gate):** `audit` (WAN/DHCP/states/DNS/NIC health), `dhcp` (pool pressure), `pf-list`
|
||||
|
||||
@@ -140,6 +140,11 @@ Writes (DRY-RUN default; `--apply` to commit — `write_config` + `filter_config
|
||||
config history. Cred = `clients/<slug>/pfsense-firewall` (host + admin user/pass), system OpenSSH via askpass.
|
||||
- [x] **SSH port configurable** (2026-06-21): `--port N` > vault `port`/`credentials.port` field > default 22.
|
||||
Unblocks non-standard-port boxes like the ACG office gateway (2248) — store `port: 2248` in its vault entry.
|
||||
- [x] **Cred-path convention = option A** (Mike 2026-06-21): the slug arg is a FULL vault path when it
|
||||
contains `/` (any infra-vaulted device, e.g. `infrastructure/pfsense-firewall`), else `clients/<slug>/pfsense-firewall`.
|
||||
No cred duplication; explicit (no implicit fallback ladder). Resolves loud (`[ERROR] no cred at <path>`) on a
|
||||
bad path. `gw-audit`/`gw-control` `--pfsense` inherits it. So the ACG office box is now reachable:
|
||||
`gw-audit '<site>' --pfsense infrastructure/pfsense-firewall` (+ `port: 2248` in that entry).
|
||||
- [x] **Dispatch rewired:** `gw-control.sh` / `gw-audit.sh` now prefer the SSH backend (keyed on
|
||||
`clients/<slug>/pfsense-firewall`) and route the same verbs to it; dispatch runs BEFORE UOS site resolution so a
|
||||
pfSense-only slug works. REST path is the dormant fallback.
|
||||
@@ -154,8 +159,8 @@ as a dormant alternative (works if a site ever installs the pkg) but is no longe
|
||||
classified UniFi-gateway (model) vs no-UniFi-gateway (pfSense/third-party candidate), plus the list of
|
||||
vaulted `clients/*/pfsense-firewall` creds with resolved host:port (SSH-backend-ready). Generated live
|
||||
(always current), not a static file. Today: 12 UniFi-gw / 36 no-UniFi-gw sites; 1 pfSense cred (Cascades).
|
||||
- [ ] **Auto-select from the map** (driver picks the pfSense cred for a site WITHOUT `--pfsense`): needs a
|
||||
UOS-site-name→slug binding + the cred-path convention (PARKED on Mike's clients/ vs infrastructure/ answer).
|
||||
- [ ] **Auto-select from the map** (driver picks the pfSense cred for a site WITHOUT `--pfsense`): cred-path
|
||||
convention now settled (option A, above) — remaining blocker is just a persisted UOS-site-name→cred binding.
|
||||
- [ ] **VPN convergence:** the "Deeper VPN — gateway-hosted VPN server" item (C) is *easier and better* on
|
||||
pfSense (WireGuard/OpenVPN) than on a USG — fold the Grabb-style "retire Windows RRAS PPTP → gateway VPN"
|
||||
play into the pfSense driver from the start.
|
||||
|
||||
@@ -103,16 +103,22 @@ PY
|
||||
# pfSense gateway/WAN/DHCP audit via the backend so one `gw-audit <site>` covers either gateway vendor.
|
||||
NGW="$(cat "$TMP/ngw" 2>/dev/null || echo 1)"
|
||||
if [ "$NGW" = "0" ]; then
|
||||
# PREFERRED: SSH backend (Mike's 2026-06-16 decision), keyed on clients/<slug>/pfsense-firewall.
|
||||
ssh_slug=""
|
||||
for s in "$PFARG" "$SITEARG"; do
|
||||
[ -n "$s" ] || continue
|
||||
case "$s" in */*) continue;; esac
|
||||
if [ -n "$(bash "$VAULT" get-field "clients/$s/pfsense-firewall" credentials.password 2>/dev/null)" ]; then ssh_slug="$s"; break; fi
|
||||
done
|
||||
if [ -n "$ssh_slug" ]; then
|
||||
echo; echo "[INFO] pfSense gateway (SSH cred vault:clients/$ssh_slug/pfsense-firewall) -> pfSense gateway audit:"
|
||||
bash "$REPO/.claude/skills/unifi-wifi/scripts/pfsense-ssh.sh" "$ssh_slug" audit || true
|
||||
# PREFERRED: SSH backend (Mike 2026-06-16). Target = full vault path if --pfsense contains '/'
|
||||
# (option A, Mike 2026-06-21), else a client slug -> clients/<slug>/pfsense-firewall.
|
||||
ssh_target=""
|
||||
case "$PFARG" in
|
||||
*/*) if [ -n "$(bash "$VAULT" get-field "$PFARG" credentials.password 2>/dev/null || bash "$VAULT" get-field "$PFARG" password 2>/dev/null)" ]; then ssh_target="$PFARG"; fi ;;
|
||||
esac
|
||||
if [ -z "$ssh_target" ]; then
|
||||
for s in "$PFARG" "$SITEARG"; do
|
||||
[ -n "$s" ] || continue
|
||||
case "$s" in */*) continue;; esac
|
||||
if [ -n "$(bash "$VAULT" get-field "clients/$s/pfsense-firewall" credentials.password 2>/dev/null)" ]; then ssh_target="$s"; break; fi
|
||||
done
|
||||
fi
|
||||
if [ -n "$ssh_target" ]; then
|
||||
echo; echo "[INFO] pfSense gateway (SSH backend, target '$ssh_target') -> pfSense gateway audit:"
|
||||
bash "$REPO/.claude/skills/unifi-wifi/scripts/pfsense-ssh.sh" "$ssh_target" audit || true
|
||||
else
|
||||
# REST fallback (dormant): only if a pfSense-api cred is vaulted.
|
||||
cands=()
|
||||
|
||||
@@ -47,15 +47,22 @@ done
|
||||
# so the SAME verb (pf-*/fw-*/block-ips) routes to a pfSense backend here. PREFERRED = SSH backend
|
||||
# (Mike's 2026-06-16 decision: no REST package needed), keyed on a vaulted clients/<slug>/pfsense-firewall
|
||||
# cred. The REST backend (pfsense-backend.sh, clients/<slug>/pfsense-api) is kept only as a dormant fallback.
|
||||
ssh_slug=""
|
||||
for s in "$PFARG" "$SITEARG"; do
|
||||
[ -n "$s" ] || continue
|
||||
case "$s" in */*) continue;; esac # SSH backend takes a slug, not a vault path
|
||||
if [ -n "$(bash "$VAULT" get-field "clients/$s/pfsense-firewall" credentials.password 2>/dev/null)" ]; then ssh_slug="$s"; break; fi
|
||||
done
|
||||
if [ -n "$ssh_slug" ]; then
|
||||
echo "[INFO] pfSense gateway (SSH cred vault:clients/$ssh_slug/pfsense-firewall) -> dispatching '$ACT' to pfsense-ssh.sh"
|
||||
args=("$ssh_slug" "$ACT"); [ ${#POS[@]} -gt 0 ] && args+=("${POS[@]}")
|
||||
ssh_target=""
|
||||
# option A (Mike 2026-06-21): --pfsense may be a FULL vault path (contains '/') for an infra-vaulted
|
||||
# device, else a client slug -> clients/<slug>/pfsense-firewall. pfsense-ssh.sh resolves both.
|
||||
case "$PFARG" in
|
||||
*/*) if [ -n "$(bash "$VAULT" get-field "$PFARG" credentials.password 2>/dev/null || bash "$VAULT" get-field "$PFARG" password 2>/dev/null)" ]; then ssh_target="$PFARG"; fi ;;
|
||||
esac
|
||||
if [ -z "$ssh_target" ]; then
|
||||
for s in "$PFARG" "$SITEARG"; do
|
||||
[ -n "$s" ] || continue
|
||||
case "$s" in */*) continue;; esac
|
||||
if [ -n "$(bash "$VAULT" get-field "clients/$s/pfsense-firewall" credentials.password 2>/dev/null)" ]; then ssh_target="$s"; break; fi
|
||||
done
|
||||
fi
|
||||
if [ -n "$ssh_target" ]; then
|
||||
echo "[INFO] pfSense gateway (SSH backend, target '$ssh_target') -> dispatching '$ACT' to pfsense-ssh.sh"
|
||||
args=("$ssh_target" "$ACT"); [ ${#POS[@]} -gt 0 ] && args+=("${POS[@]}")
|
||||
[ "$APPLY" = "1" ] && args+=(--apply)
|
||||
exec bash "$REPO/.claude/skills/unifi-wifi/scripts/pfsense-ssh.sh" "${args[@]}"
|
||||
fi
|
||||
|
||||
@@ -37,7 +37,7 @@ GWC_PHP="$HERE/pfsense-gwc.php"
|
||||
# bad args, site not found). Soft-fails so it never breaks the caller.
|
||||
SKILL_ID="unifi-wifi/pfsense-ssh"
|
||||
logerr(){ bash "$REPO/.claude/scripts/log-skill-error.sh" "$SKILL_ID" "$1" --context "${2:-}" >/dev/null 2>&1 || true; }
|
||||
SLUG="${1:?usage: pfsense-ssh.sh <slug> <action> [args] [--apply]}"
|
||||
SLUG="${1:?usage: pfsense-ssh.sh <slug-or-vault-path> <action> [args] [--apply]}"
|
||||
ACT="${2:?action: audit|dhcp|pf-list|fw-list|pf-*|fw-*|block-ips|unblock|showblock|run|shell}"; shift 2 || true
|
||||
APPLY=0; BLOCK_IF="wan"; PORT=""; POS=()
|
||||
while [ $# -gt 0 ]; do case "$1" in
|
||||
@@ -46,7 +46,9 @@ while [ $# -gt 0 ]; do case "$1" in
|
||||
--port) PORT="${2:?--port needs a number}"; shift 2;;
|
||||
*) POS+=("$1"); shift;;
|
||||
esac; done
|
||||
VP="clients/$SLUG/pfsense-firewall"
|
||||
# Cred path (Mike 2026-06-21, option A): arg containing '/' = a full vault path (any infra/-vaulted
|
||||
# device, e.g. infrastructure/pfsense-firewall); else clients/<slug>/pfsense-firewall.
|
||||
case "$SLUG" in */*) VP="$SLUG" ;; *) VP="clients/$SLUG/pfsense-firewall" ;; esac
|
||||
HOST="$(bash "$VAULT" get-field "$VP" host 2>/dev/null || bash "$VAULT" get-field "$VP" credentials.host 2>/dev/null || true)"
|
||||
U="$(bash "$VAULT" get-field "$VP" credentials.username 2>/dev/null || true)"
|
||||
PP="$(bash "$VAULT" get-field "$VP" credentials.password 2>/dev/null || true)"; export PP
|
||||
@@ -59,8 +61,12 @@ if [ -z "$PORT" ]; then
|
||||
done
|
||||
fi
|
||||
PORT="${PORT:-22}"
|
||||
# Fail loud (Mike's add): a wholly-empty resolve = path typo/missing -> clear [ERROR], not a confusing
|
||||
# SSH failure later. A partial resolve = the entry exists but is missing a field.
|
||||
if [ -z "$HOST" ] && [ -z "$U" ] && [ -z "$PP" ]; then
|
||||
echo "[ERROR] no cred at vault:$VP (path not found or empty — check the slug/vault-path)"; exit 2; fi
|
||||
if [ -z "$HOST" ] || [ -z "$U" ] || [ -z "$PP" ]; then
|
||||
echo "[BLOCKED] need host + admin creds at vault:$VP (fields: host, credentials.username, credentials.password)"; exit 2; fi
|
||||
echo "[BLOCKED] incomplete cred at vault:$VP (need fields: host, credentials.username, credentials.password)"; exit 2; fi
|
||||
if [ "$ACT" = "shell" ]; then echo "ssh -p ${PORT} ${U}@${HOST} # password in vault:$VP"; exit 0; fi
|
||||
|
||||
TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT
|
||||
|
||||
@@ -17,6 +17,8 @@ Categories (the `[type]` tag): _(none)_ = skill/command execution failure ·
|
||||
|
||||
<!-- Append entries below this line -->
|
||||
|
||||
2026-06-21 | Howard-Home | unifi-wifi/pfsense-ssh | SSH connect/auth failed (rc=255) [ctx: host=192.168.0.1:22 slug=clients/cascades-tucson/pfsense-firewall act=showblock]
|
||||
|
||||
2026-06-21 | Howard-Home | unifi-wifi/pfsense-ssh | SSH connect/auth failed (rc=255) [ctx: host=192.168.0.1:9999 slug=cascades-tucson act=showblock]
|
||||
|
||||
2026-06-21 | Howard-Home | unifi-wifi/pfsense-ssh | SSH connect/auth failed (rc=255) [ctx: host=192.168.0.1:22 slug=cascades-tucson act=showblock]
|
||||
|
||||
@@ -99,11 +99,12 @@ The REST backend (`pfsense-backend.sh`, `clients/<slug>/pfsense-api`) is a dorma
|
||||
`pfsense-firewall` cred) and run the dispatch BEFORE UOS site resolution, so a pfSense-only client
|
||||
slug works without a matching UOS site name (pass `--pfsense <slug>` if the names differ).
|
||||
|
||||
**THIS office box:** listens on SSH **port 2248** (not 22). The skill supports non-default ports as
|
||||
of 2026-06-21 — pass `--port 2248`, or (preferred) store `port: 2248` in the box's vault entry and
|
||||
it's automatic. Cred for it is vaulted at `infrastructure/pfsense-firewall` (verify), but the SSH
|
||||
backend expects the cred at `clients/<slug>/pfsense-firewall`, so an `infrastructure/`-path cred
|
||||
would need a slug alias or a small path tweak before `pfsense-ssh.sh` can read it (verify).
|
||||
**THIS office box:** SSH **port 2248** (not 22). **Fully reachable by the skill as of 2026-06-21.**
|
||||
Cred vaulted at `infrastructure/pfsense-firewall` (verify) — pass it as a **full vault path**
|
||||
(option A, Mike 2026-06-21: a 1st arg containing `/` is a vault path, not a client slug), e.g.
|
||||
`pfsense-ssh.sh infrastructure/pfsense-firewall audit` or
|
||||
`gw-audit '<site>' --pfsense infrastructure/pfsense-firewall`. Add `port: 2248` to that vault entry
|
||||
so the non-standard port is automatic (or pass `--port 2248`). No cred duplication needed.
|
||||
|
||||
**pfSense PHP gotchas** (baked into the scripts; carry forward to any new helper):
|
||||
- Bootstrap with `require_once("config.inc")` ONLY — re-requiring util/functions/filter → "cannot
|
||||
|
||||
Reference in New Issue
Block a user