sync: auto-sync from HOWARD-HOME at 2026-06-21 12:25:00
Author: Howard Enos Machine: HOWARD-HOME Timestamp: 2026-06-21 12:25:00
This commit is contained in:
@@ -186,6 +186,83 @@ PS
|
||||
echo "[OK] uploaded $(basename "$remote") ($nch chunk(s)) -> $(echo "$r"|jq -r '.stdout'|tr -d '\r')"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# DETACHED, NO-CAP execution. A scanner must never be killed by an RMM command
|
||||
# timeout - large drives can scan for hours. So we launch GuruScan as a
|
||||
# scheduled task with ExecutionTimeLimit=0 (unlimited); the launch command
|
||||
# returns immediately, the scan runs to completion on its own, and we poll a
|
||||
# disk done-marker with NO overall cap.
|
||||
# ---------------------------------------------------------------------------
|
||||
# gs_launch_detached "<invoke-guruscan-args>" <tag>
|
||||
gs_launch_detached() {
|
||||
local gsargs="$1" tag="$2"
|
||||
local wl="$WORK_DIR/wrapper_$tag.ps1"
|
||||
cat > "$wl" <<PS
|
||||
\$ErrorActionPreference='Continue'
|
||||
\$marker='C:\\GuruScan\\_done_${tag}.txt'
|
||||
\$log='C:\\GuruScan\\_log_${tag}.txt'
|
||||
Remove-Item \$marker,\$log -Force -ErrorAction SilentlyContinue
|
||||
try { & C:\\GuruScan\\Invoke-GuruScan.ps1 ${gsargs} *> \$log; \$rc=\$LASTEXITCODE }
|
||||
catch { \$rc=-1; \$_ | Out-String | Add-Content \$log }
|
||||
"DONE rc=\$rc at \$(Get-Date -Format o)" | Set-Content \$marker
|
||||
PS
|
||||
upload_file "$wl" "C:\\GuruScan\\_detached_${tag}.ps1" || return 1
|
||||
local sf="$WORK_DIR/launch_$tag.ps1"
|
||||
cat > "$sf" <<PS
|
||||
\$ErrorActionPreference='Stop'
|
||||
\$tn='GuruScan-${tag}'
|
||||
try{ Unregister-ScheduledTask -TaskName \$tn -Confirm:\$false -ErrorAction SilentlyContinue }catch{}
|
||||
\$a=New-ScheduledTaskAction -Execute 'powershell.exe' -Argument '-NonInteractive -ExecutionPolicy Bypass -WindowStyle Hidden -File C:\\GuruScan\\_detached_${tag}.ps1'
|
||||
\$pr=New-ScheduledTaskPrincipal -UserId 'SYSTEM' -LogonType ServiceAccount -RunLevel Highest
|
||||
\$st=New-ScheduledTaskSettingsSet -ExecutionTimeLimit ([TimeSpan]::Zero) -StartWhenAvailable -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -MultipleInstances IgnoreNew
|
||||
Register-ScheduledTask -TaskName \$tn -Action \$a -Principal \$pr -Settings \$st -Force | Out-Null
|
||||
Start-ScheduledTask -TaskName \$tn
|
||||
Start-Sleep -Seconds 2
|
||||
Write-Output ('LAUNCHED ' + \$tn + ' state=' + ((Get-ScheduledTask -TaskName \$tn).State))
|
||||
PS
|
||||
run_ps "$sf" 90 24 "launch-$tag" || return 1
|
||||
}
|
||||
|
||||
# gs_wait_detached <tag> <label> -- polls for the done-marker; NO cap (24h safety net)
|
||||
gs_wait_detached() {
|
||||
local tag="$1" label="$2" i=0 maxi=2880
|
||||
local sf="$WORK_DIR/poll_$tag.ps1"
|
||||
cat > "$sf" <<PS
|
||||
\$ErrorActionPreference='Continue'
|
||||
\$marker='C:\\GuruScan\\_done_${tag}.txt'
|
||||
if(Test-Path \$marker){ Write-Output ('MARKER ' + ((Get-Content \$marker -Raw) -replace '\s+',' ').Trim()) }
|
||||
else {
|
||||
\$act=@(); foreach(\$n in @('a2cmd','HitmanPro_x64','rkill')){ \$p=Get-Process -Name \$n -ErrorAction SilentlyContinue; if(\$p){ \$act += (\$n+' CPU='+[math]::Round((\$p|Select-Object -First 1).CPU,0)+'s') } }
|
||||
if(\$act.Count){ Write-Output ('RUNNING ' + (\$act -join ', ')) } else { Write-Output 'RUNNING (between scanners / starting)' }
|
||||
}
|
||||
PS
|
||||
echo "[INFO] waiting for '$label' to finish (NO cap; checking every 30s)..."
|
||||
while [ $i -lt $maxi ]; do
|
||||
run_ps "$sf" 30 8 "poll-$tag" >/dev/null 2>&1 || true
|
||||
local out; out="$(jq -r '.stdout' "$WORK_DIR/last_result.json" 2>/dev/null | tr -d '\r')"
|
||||
if printf '%s' "$out" | grep -q '^MARKER '; then
|
||||
echo "[OK] '$label' finished -> $(printf '%s' "$out" | grep '^MARKER ')"
|
||||
return 0
|
||||
fi
|
||||
i=$((i+1))
|
||||
if [ $((i % 4)) -eq 0 ]; then echo " [$label] $((i/2)) min elapsed: $(printf '%s' "$out" | grep -E '^(RUNNING|MARKER)' | head -1)"; fi
|
||||
sleep 30
|
||||
done
|
||||
echo "[WARN] '$label' exceeded 24h safety net"; return 1
|
||||
}
|
||||
|
||||
# fetch the captured console log of a detached run (contains GURUSCAN_RESULT_JSON)
|
||||
gs_fetch_detached_log() {
|
||||
local tag="$1"
|
||||
local sf="$WORK_DIR/getlog_$tag.ps1"
|
||||
cat > "$sf" <<PS
|
||||
\$p='C:\\GuruScan\\_log_${tag}.txt'
|
||||
if(Test-Path \$p){ Get-Content \$p -Raw } else { Write-Output 'NO-LOG' }
|
||||
PS
|
||||
run_ps "$sf" 60 24 "fetchlog-$tag" >/dev/null 2>&1 || true
|
||||
jq -r '.stdout' "$WORK_DIR/last_result.json" 2>/dev/null
|
||||
}
|
||||
|
||||
# ===========================================================================
|
||||
phase_prep() {
|
||||
echo ""; echo "=== PHASE: prep ==="
|
||||
@@ -283,21 +360,14 @@ PS
|
||||
|
||||
# ===========================================================================
|
||||
phase_scan() {
|
||||
echo ""; echo "=== PHASE: scan (clean mode, full chain, headless - LONG) ==="
|
||||
local sf="$WORK_DIR/scan.ps1"
|
||||
cat > "$sf" <<'PS'
|
||||
$ErrorActionPreference='Continue'
|
||||
& C:\GuruScan\Invoke-GuruScan.ps1 -Headless
|
||||
PS
|
||||
# Emsisoft timeout_min=120, HitmanPro=60. Give the command 2.5h and poll up to 3h.
|
||||
run_ps "$sf" 9000 2160 "guruscan-run" || { _logerr "GuruScan run failed" --context "host=$AGENT_HOST"; echo "[ERROR] scan phase failed"; return 1; }
|
||||
# surface the structured result line
|
||||
local out; out="$(jq -r '.stdout' "$WORK_DIR/last_result.json" 2>/dev/null)"
|
||||
echo ""; echo "=== PHASE: scan (clean mode, full chain, headless, DETACHED - NO CAP) ==="
|
||||
gs_launch_detached "-Headless" "scan" || { _logerr "detached scan launch failed" --context "host=$AGENT_HOST"; echo "[ERROR] launch failed"; return 1; }
|
||||
gs_wait_detached "scan" "full-scan" || true
|
||||
local out; out="$(gs_fetch_detached_log "scan")"
|
||||
echo ""
|
||||
echo "$out" | grep 'GURUSCAN_RESULT_JSON:' | sed 's/^GURUSCAN_RESULT_JSON://' | jq '.' 2>/dev/null \
|
||||
|| echo "[WARN] no GURUSCAN_RESULT_JSON line in stdout (see full output above)"
|
||||
local cmd_id; cmd_id="$(cat "$WORK_DIR/last_cmd_id" 2>/dev/null||echo ?)"
|
||||
post_alert "[RMM] GuruScan test: scan finished on $AGENT_HOST -> cmd:${cmd_id:0:8}"
|
||||
printf '%s\n' "$out" | grep 'GURUSCAN_RESULT_JSON:' | sed 's/^.*GURUSCAN_RESULT_JSON://' | jq '.' 2>/dev/null \
|
||||
|| echo "[WARN] no GURUSCAN_RESULT_JSON in detached log"
|
||||
post_alert "[RMM] GuruScan test: detached scan finished on $AGENT_HOST"
|
||||
}
|
||||
|
||||
# ===========================================================================
|
||||
@@ -412,12 +482,10 @@ PS
|
||||
ve_seed_ps > "$WORK_DIR/ve_seed.ps1"
|
||||
run_ps "$WORK_DIR/ve_seed.ps1" 60 24 "seed-for-$eng" || { echo "[ERROR] seed failed for $eng"; return 1; }
|
||||
|
||||
# 2) run ONLY this engine in clean mode (long; Emsisoft updates+scans C:\)
|
||||
cat > "$WORK_DIR/ve_run.ps1" <<PS
|
||||
\$ErrorActionPreference='Continue'
|
||||
& C:\\GuruScan\\Invoke-GuruScan.ps1 -Scanners $eng -Headless
|
||||
PS
|
||||
run_ps "$WORK_DIR/ve_run.ps1" 2700 600 "run-$eng" || echo "[WARN] $eng run reported non-zero"
|
||||
# 2) run ONLY this engine in clean mode, DETACHED with NO cap (Emsisoft
|
||||
# updates+scans all of C:\ - can take far longer than any RMM timeout)
|
||||
gs_launch_detached "-Scanners $eng -Headless" "ve_$eng" || { echo "[ERROR] launch failed for $eng"; return 1; }
|
||||
gs_wait_detached "ve_$eng" "run-$eng" || true
|
||||
|
||||
# 3) check which seeded copies survived + read that run's result json
|
||||
cat > "$WORK_DIR/ve_check.ps1" <<'PS'
|
||||
|
||||
@@ -186,6 +186,12 @@ vaulted dedicated key `infrastructure/uos-server-ssh-key` (works from any fleet
|
||||
`references/site-manager-api.md`. Use this for consoles NOT adopted into the UOS server.
|
||||
|
||||
## Applying changes — IMPORTANT boundary
|
||||
**Error logging (mandatory):** on a GENUINE functional failure (controller/SSH login or connect fail,
|
||||
unexpected REST/pfSense error, php fatal), the scripts log it via `.claude/scripts/log-skill-error.sh`
|
||||
before surfacing — do NOT log handled conditions (missing cred, bad args, site-not-found, "no results").
|
||||
The live scripts (`pfsense-ssh.sh`, `gw-control.sh`, `gw-audit.sh`) self-log; if you add a new script,
|
||||
do the same at its failure branches.
|
||||
|
||||
Config changes are automated across many APs (no per-AP UI clicking) via the controller REST API
|
||||
(`PUT .../rest/device/<id>` radio_table) — **`scripts/apply-radio.sh`**. Actions (all radio_table):
|
||||
```bash
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
set -euo pipefail
|
||||
REPO="$(git rev-parse --show-toplevel 2>/dev/null || echo .)"
|
||||
UOS="$REPO/.claude/scripts/uos-mongo.sh"; VAULT="$REPO/.claude/scripts/vault.sh"
|
||||
# Mandatory skill error logging (skill-creator rule): log GENUINE functional failures only.
|
||||
logerr(){ bash "$REPO/.claude/scripts/log-skill-error.sh" "unifi-wifi/apply-radio" "$1" --context "${2:-}" >/dev/null 2>&1 || true; }
|
||||
SITEARG="${1:?usage: apply-radio.sh <site> <band> <action> [value] [--zone Z] [--ap NAME] [--apply]}"
|
||||
BAND="${2:?band ng|na|6e}"; ACT="${3:?action: power|width|channel|minrssi|disable|enable}"
|
||||
shift 3
|
||||
@@ -97,7 +99,7 @@ EOF
|
||||
fi
|
||||
|
||||
export AR_SITE="$SITE" AR_BAND="$BAND" AR_FIELDS="$FIELDS" AR_ZONE="$ZONE" AR_AP="$APN" REPO
|
||||
python - <<'PY'
|
||||
rc=0; python - <<'PY' || rc=$?
|
||||
import os,sys,json,ssl,urllib.request,http.cookiejar
|
||||
H="172.16.3.29";PORT=11443;base=f"https://{H}:{PORT}"
|
||||
ctx=ssl.create_default_context();ctx.check_hostname=False;ctx.verify_mode=ssl.CERT_NONE
|
||||
@@ -145,3 +147,5 @@ try:
|
||||
except Exception as e:print("[APPLY] done; rollback save failed:",e)
|
||||
print("[validate] watch the target APs live: watch-ap.sh <ap-ip> (before/after). Roll out per --zone.")
|
||||
PY
|
||||
[ "$rc" -ne 0 ] && logerr "apply-radio REST write failed" "site=$SITE band=$BAND act=$ACT rc=$rc"
|
||||
exit $rc
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
set -uo pipefail
|
||||
REPO="$(git rev-parse --show-toplevel 2>/dev/null || echo .)"
|
||||
UOS="$REPO/.claude/scripts/uos-mongo.sh"; VAULT="$REPO/.claude/scripts/vault.sh"
|
||||
# Mandatory skill error logging (skill-creator rule): log GENUINE functional failures only.
|
||||
logerr(){ bash "$REPO/.claude/scripts/log-skill-error.sh" "unifi-wifi/apply-wlan" "$1" --context "${2:-}" >/dev/null 2>&1 || true; }
|
||||
SITEARG="${1:?usage: apply-wlan.sh <site> <minrate|steer> ... [--wlan NAME] [--apply]}"
|
||||
ACT="${2:?action: minrate|bandsteer|bands|steer|bsstm|wlan|dtim|mcast|bcfilter|rrm|ftroam|isolation|hidessid|macfilter|aps}"; shift 2
|
||||
WLAN=""; APPLY=0
|
||||
@@ -154,3 +156,6 @@ try:
|
||||
print(f"\n[APPLY] {done} changed, {fail} failed. Rollback saved: {rp}")
|
||||
except Exception as e:print("[APPLY] done; rollback save failed:",e)
|
||||
PY
|
||||
rc=$?
|
||||
[ "$rc" -ne 0 ] && logerr "apply-wlan REST write failed" "site=$SITE act=$ACT rc=$rc"
|
||||
exit $rc
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
set -euo pipefail
|
||||
REPO="$(git rev-parse --show-toplevel 2>/dev/null || echo .)"
|
||||
UOS="$REPO/.claude/scripts/uos-mongo.sh"
|
||||
# Mandatory skill error logging (skill-creator rule): log GENUINE functional failures only.
|
||||
# (Pure Mongo read/analysis; no external call with an uninstrumented failure branch -> helper for consistency.)
|
||||
logerr(){ bash "$REPO/.claude/scripts/log-skill-error.sh" "unifi-wifi/audit-site" "$1" --context "${2:-}" >/dev/null 2>&1 || true; }
|
||||
arg="${1:?usage: audit-site.sh <site-name|site_id>}"
|
||||
|
||||
if [[ "$arg" =~ ^[0-9a-f]{24}$ ]]; then
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
set -uo pipefail
|
||||
REPO="$(git rev-parse --show-toplevel 2>/dev/null || echo .)"
|
||||
UOS="$REPO/.claude/scripts/uos-mongo.sh"; VAULT="$REPO/.claude/scripts/vault.sh"
|
||||
# Mandatory skill error logging (skill-creator rule): log GENUINE functional failures only.
|
||||
logerr(){ bash "$REPO/.claude/scripts/log-skill-error.sh" "unifi-wifi/channel-plan" "$1" --context "${2:-}" >/dev/null 2>&1 || true; }
|
||||
HOST="${UOS_HOST:-172.16.3.29}"; PORT="${UOS_HTTPS_PORT:-11443}"
|
||||
SITEARG="${1:?usage: channel-plan.sh <site> <ng|na> [--apply]}"; BAND="${2:?band ng|na}"; APPLY=0
|
||||
CHANS=""; DFSPOL=""
|
||||
@@ -135,3 +137,6 @@ os.makedirs(os.path.dirname(rp),exist_ok=True); open(rp,'w').write(json.dumps(ro
|
||||
print(f"\n[APPLY] {done} changed, {fail} failed. Rollback: {rp}")
|
||||
print("[validate] re-run survey-collect / watch-ap after settle; roll out per-zone in practice.")
|
||||
PY
|
||||
rc=$?
|
||||
[ "$rc" -ne 0 ] && logerr "channel-plan REST call failed" "site=$SITE band=$BAND rc=$rc"
|
||||
exit $rc
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
set -uo pipefail
|
||||
REPO="$(git rev-parse --show-toplevel 2>/dev/null || echo .)"
|
||||
UOS="$REPO/.claude/scripts/uos-mongo.sh"; VAULT="$REPO/.claude/scripts/vault.sh"
|
||||
# Mandatory skill error logging (skill-creator rule): log GENUINE functional failures only.
|
||||
logerr(){ bash "$REPO/.claude/scripts/log-skill-error.sh" "unifi-wifi/client-control" "$1" --context "${2:-}" >/dev/null 2>&1 || true; }
|
||||
SITEARG="${1:?usage: client-control.sh <site> <block|unblock|kick> <mac> [--apply]}"
|
||||
ACT="${2:?action: block|unblock|kick}"; MAC="$(echo "${3:?mac required}" | tr 'A-Z' 'a-z')"; APPLY=0
|
||||
shift 3; while [ $# -gt 0 ]; do case "$1" in --apply) APPLY=1; shift;; *) shift;; esac; done
|
||||
@@ -51,3 +53,6 @@ try:
|
||||
print(f" [{'ok' if meta.get('rc')=='ok' else 'FAIL'}] {os.environ['CC_CMD']} {os.environ['CC_MAC']} -> {meta}")
|
||||
except Exception as e:print(" [FAIL]",e)
|
||||
PY
|
||||
rc=$?
|
||||
[ "$rc" -ne 0 ] && logerr "client-control REST cmd failed" "site=$SITE act=$ACT cmd=$CMD rc=$rc"
|
||||
exit $rc
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
set -uo pipefail
|
||||
REPO="$(git rev-parse --show-toplevel 2>/dev/null || echo .)"
|
||||
UOS="$REPO/.claude/scripts/uos-mongo.sh"; VAULT="$REPO/.claude/scripts/vault.sh"
|
||||
# Mandatory skill error logging (skill-creator rule): log GENUINE functional failures only.
|
||||
# (Mongo read + local-JSON analysis; the controller fetch is best-effort/soft-degrading -> helper for consistency, no live failure branch.)
|
||||
logerr(){ bash "$REPO/.claude/scripts/log-skill-error.sh" "unifi-wifi/coverage-thin" "$1" --context "${2:-}" >/dev/null 2>&1 || true; }
|
||||
HOST="${UOS_HOST:-172.16.3.29}"; PORT="${UOS_HTTPS_PORT:-11443}"
|
||||
SITEARG="${1:?usage: coverage-thin.sh <site> [days=14] (NEIGHBOR_JSON=<matrix> required)}"; DAYS="${2:-14}"
|
||||
NJ="${NEIGHBOR_JSON:-}"; [ -n "$NJ" ] && [ -f "$NJ" ] || { echo "[ERROR] NEIGHBOR_JSON=<matrix.json> required (run neighbor-collect.sh with NBR_JSON=...)"; exit 1; }
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
set -uo pipefail
|
||||
REPO="$(git rev-parse --show-toplevel 2>/dev/null || echo .)"
|
||||
UOS="$REPO/.claude/scripts/uos-mongo.sh"; VAULT="$REPO/.claude/scripts/vault.sh"
|
||||
# Mandatory skill error logging (skill-creator rule): log GENUINE functional failures only.
|
||||
logerr(){ bash "$REPO/.claude/scripts/log-skill-error.sh" "unifi-wifi/device-control" "$1" --context "${2:-}" >/dev/null 2>&1 || true; }
|
||||
SITEARG="${1:?usage: device-control.sh <site> <action> <target> [--apply]}"
|
||||
ACT="${2:?action: adopt|restart|locate|unlocate|upgrade|poe-cycle}"; TGT="${3:?target (mac, or AP name for poe-cycle)}"; APPLY=0
|
||||
shift 3; while [ $# -gt 0 ]; do case "$1" in --apply) APPLY=1; shift;; *) shift;; esac; done
|
||||
@@ -75,3 +77,6 @@ else:
|
||||
meta=json.loads(r).get('meta',{}); print(f" [{'ok' if meta.get('rc')=='ok' else 'FAIL'}] {os.environ['DC_CMD']} {tgt} -> {meta}")
|
||||
except Exception as e:print(" [FAIL]",e)
|
||||
PY
|
||||
rc=$?
|
||||
[ "$rc" -ne 0 ] && logerr "device-control REST cmd failed" "site=$SITE act=$ACT rc=$rc"
|
||||
exit $rc
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
set -uo pipefail
|
||||
REPO="$(git rev-parse --show-toplevel 2>/dev/null || echo .)"
|
||||
VAULT="$REPO/.claude/scripts/vault.sh"
|
||||
# Mandatory skill error logging (skill-creator rule): log GENUINE functional failures only.
|
||||
logerr(){ bash "$REPO/.claude/scripts/log-skill-error.sh" "unifi-wifi/dfs-check" "$1" --context "${2:-}" >/dev/null 2>&1 || true; }
|
||||
HOST="${UOS_HOST:-172.16.3.29}"; PORT="${UOS_HTTPS_PORT:-11443}"
|
||||
SITEARG="${1:?usage: dfs-check.sh <site-name|id> [ap-ssh-vault-path]}"
|
||||
VP="${2:-clients/cascades-tucson/unifi-ap-ssh}"
|
||||
@@ -26,7 +28,7 @@ CP="$(bash "$VAULT" get-field infrastructure/uos-server-network-api-rw credentia
|
||||
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]}))' "$CU" "$CP")")
|
||||
[ "$code" = "200" ] || { echo "[ERROR] controller login HTTP $code"; exit 1; }
|
||||
[ "$code" = "200" ] || { echo "[ERROR] controller login HTTP $code"; logerr "UOS controller login failed (HTTP $code)" "host=$HOST:$PORT site=$SITEARG"; exit 1; }
|
||||
SHORT="$(curl -sk -b "$CJ" "$base/proxy/network/api/self/sites" | python -c "
|
||||
import sys,json; d=json.load(sys.stdin).get('data',[]); q='''$SITEARG'''.lower()
|
||||
for s in d:
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
set -uo pipefail
|
||||
REPO="$(git rev-parse --show-toplevel 2>/dev/null || echo .)"
|
||||
UOS="$REPO/.claude/scripts/uos-mongo.sh"; VAULT="$REPO/.claude/scripts/vault.sh"
|
||||
# Mandatory skill error logging (skill-creator rule): log GENUINE functional failures only.
|
||||
logerr(){ bash "$REPO/.claude/scripts/log-skill-error.sh" "unifi-wifi/gw-audit" "$1" --context "${2:-}" >/dev/null 2>&1 || true; }
|
||||
HOST="${UOS_HOST:-172.16.3.29}"; PORT="${UOS_HTTPS_PORT:-11443}"
|
||||
SITEARG="${1:?usage: gw-audit.sh <site-name|id> [--pfsense <slug>]}"; shift || true
|
||||
PFARG="" # optional: pfSense client slug (or full vault path) when UOS site name != client slug
|
||||
@@ -24,7 +26,7 @@ P="$(bash "$VAULT" get-field infrastructure/uos-server-network-api-rw credential
|
||||
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")")
|
||||
[ "$code" = "200" ] || { echo "[ERROR] controller login HTTP $code"; exit 1; }
|
||||
[ "$code" = "200" ] || { echo "[ERROR] controller login HTTP $code"; logerr "UOS controller login failed (HTTP $code)" "host=$HOST:$PORT site=$SITEARG"; exit 1; }
|
||||
SHORT="$(curl -sk -b "$CJ" "$base/proxy/network/api/self/sites" | python -c "
|
||||
import sys,json; d=json.load(sys.stdin).get('data',[]); q='''$SITEARG'''.lower()
|
||||
for s in d:
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
set -uo pipefail
|
||||
REPO="$(git rev-parse --show-toplevel 2>/dev/null || echo .)"
|
||||
UOS="$REPO/.claude/scripts/uos-mongo.sh"; VAULT="$REPO/.claude/scripts/vault.sh"
|
||||
# Mandatory skill error logging (skill-creator rule): log GENUINE functional failures only.
|
||||
logerr(){ bash "$REPO/.claude/scripts/log-skill-error.sh" "unifi-wifi/gw-control" "$1" --context "${2:-}" >/dev/null 2>&1 || true; }
|
||||
SITEARG="${1:?usage: gw-control.sh <site> <action> [args] [--apply]}"
|
||||
ACT="${2:?action: pf-list|pf-disable|pf-enable|pf-delete|pf-set-ports|pf-set-src|fw-list|fw-disable|fw-enable|block-ips}"
|
||||
shift 2
|
||||
@@ -258,3 +260,6 @@ except urllib.error.HTTPError as e:
|
||||
except Exception as e:
|
||||
print("[FAIL]",e);sys.exit(1)
|
||||
PY
|
||||
rc=$?
|
||||
[ "$rc" -ne 0 ] && logerr "gw-control REST write failed" "site=$SITE act=$ACT rc=$rc"
|
||||
exit $rc
|
||||
|
||||
@@ -41,6 +41,8 @@ PY="$(command -v py 2>/dev/null || command -v python 2>/dev/null || command -v p
|
||||
[ -z "$PY" ] && { echo "[$SELF] python required" >&2; exit 1; }
|
||||
|
||||
REPO_ROOT="${CLAUDETOOLS_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"
|
||||
# Mandatory skill error logging (skill-creator rule): log GENUINE functional failures only.
|
||||
logerr(){ bash "$REPO_ROOT/.claude/scripts/log-skill-error.sh" "unifi-wifi/gw-sitemanager" "$1" --context "${2:-}" >/dev/null 2>&1 || true; }
|
||||
|
||||
# --- API key from vault (or env override) ---
|
||||
KEY="${UNIFI_SM_KEY:-}"
|
||||
@@ -54,7 +56,7 @@ api() { # $1 = path -> raw JSON on stdout; non-200 -> empty + stderr note
|
||||
resp=$(curl -s -w "\n__C__%{http_code}" -H "X-API-KEY: $KEY" -H "Accept: application/json" "$BASE$path" 2>/dev/null)
|
||||
code=$(printf '%s' "$resp" | sed -n 's/.*__C__//p')
|
||||
body=$(printf '%s' "$resp" | sed 's/__C__[0-9]*$//')
|
||||
if [ "$code" != "200" ]; then echo "[$SELF] GET $path -> HTTP $code" >&2; return 1; fi
|
||||
if [ "$code" != "200" ]; then echo "[$SELF] GET $path -> HTTP $code" >&2; logerr "Site Manager API call failed (HTTP $code)" "path=$path"; return 1; fi
|
||||
printf '%s' "$body"
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
set -euo pipefail
|
||||
REPO="$(git rev-parse --show-toplevel 2>/dev/null || echo .)"
|
||||
VAULT="$REPO/.claude/scripts/vault.sh"
|
||||
# Mandatory skill error logging (skill-creator rule): log GENUINE functional failures only.
|
||||
logerr(){ bash "$REPO/.claude/scripts/log-skill-error.sh" "unifi-wifi/live-stats" "$1" --context "${2:-}" >/dev/null 2>&1 || true; }
|
||||
HOST="${UOS_HOST:-172.16.3.29}"; PORT="${UOS_HTTPS_PORT:-11443}"
|
||||
SITEARG="${1:?usage: live-stats.sh <site-name|short> [--clients]}"; WANT_CLIENTS="${2:-}"
|
||||
|
||||
@@ -41,7 +43,7 @@ base="https://$HOST:$PORT"
|
||||
# UniFi OS login -> session cookie
|
||||
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")")
|
||||
[ "$code" = "200" ] || { echo "[ERROR] login HTTP $code"; exit 1; }
|
||||
[ "$code" = "200" ] || { echo "[ERROR] login HTTP $code"; logerr "UOS controller login failed (HTTP $code)" "host=$HOST:$PORT site=$SITEARG"; exit 1; }
|
||||
|
||||
# resolve site short name (classic API keys on the short name, not the _id or display name)
|
||||
SHORT="$(curl -sk -b "$CJ" "$base/proxy/network/api/self/sites" | python -c "
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
# Usage: bash .claude/skills/unifi-wifi/scripts/model-rank.sh <site-name|site_id> [days=7] [band=ng|na|6e|all]
|
||||
set -euo pipefail
|
||||
REPO="$(git rev-parse --show-toplevel 2>/dev/null || echo .)"
|
||||
# Mandatory skill error logging (skill-creator rule): log GENUINE functional failures only.
|
||||
# (Pure Mongo read/analysis; --console path execs rf-analyze.py -> helper for consistency, no live failure branch.)
|
||||
logerr(){ bash "$REPO/.claude/scripts/log-skill-error.sh" "unifi-wifi/model-rank" "$1" --context "${2:-}" >/dev/null 2>&1 || true; }
|
||||
# Cloud connector path (non-UOS console): `model-rank.sh --console "<name>" [days] [band]` routes to
|
||||
# rf-analyze.py (Site Manager API; see references/site-manager-api.md). The UOS Mongo path below is unchanged.
|
||||
if [ "${1:-}" = "--console" ]; then
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
set -uo pipefail
|
||||
REPO="$(git rev-parse --show-toplevel 2>/dev/null || echo .)"
|
||||
UOS="$REPO/.claude/scripts/uos-mongo.sh"; D="$REPO/.claude/skills/unifi-wifi/scripts"
|
||||
# Mandatory skill error logging (skill-creator rule): log GENUINE functional failures only.
|
||||
# (Orchestrator -> the sub-scripts it calls log their own failures; no direct uninstrumented call here -> helper for consistency.)
|
||||
logerr(){ bash "$REPO/.claude/scripts/log-skill-error.sh" "unifi-wifi/monitor-run" "$1" --context "${2:-}" >/dev/null 2>&1 || true; }
|
||||
ARG="${1:?usage: monitor-run.sh <site|all>}"
|
||||
clean(){ grep -viE 'post-quantum|store now|upgraded|openssh.com|WARNING: connection'; }
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
set -uo pipefail
|
||||
REPO="$(git rev-parse --show-toplevel 2>/dev/null || echo .)"
|
||||
VAULT="$REPO/.claude/scripts/vault.sh"; UOS="$REPO/.claude/scripts/uos-mongo.sh"
|
||||
# Mandatory skill error logging (skill-creator rule): log GENUINE functional failures only.
|
||||
logerr(){ bash "$REPO/.claude/scripts/log-skill-error.sh" "unifi-wifi/neighbor-collect" "$1" --context "${2:-}" >/dev/null 2>&1 || true; }
|
||||
HOST="${UOS_HOST:-172.16.3.29}"; PORT="${UOS_HTTPS_PORT:-11443}"
|
||||
# Data source for the AP name/BSSID/IP map: UOS direct-login (default) OR the cloud CONNECTOR
|
||||
# (`--console <name> [--site <short>]`) for non-UOS / remote consoles. The AP SSH harvest below is
|
||||
@@ -69,7 +71,7 @@ else
|
||||
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]}))' "$CU" "$CP")")
|
||||
[ "$code" = "200" ] || { echo "[ERROR] controller login HTTP $code"; exit 1; }
|
||||
[ "$code" = "200" ] || { echo "[ERROR] controller login HTTP $code"; logerr "UOS controller login failed (HTTP $code)" "host=$HOST:$PORT site=$SITEARG"; exit 1; }
|
||||
SHORT="$(curl -sk -b "$CJ" "$base/proxy/network/api/self/sites" | python -c "
|
||||
import sys,json; d=json.load(sys.stdin).get('data',[]); q='''$SITEARG'''.lower()
|
||||
for s in d:
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
# env: ROAM_MIN(4) CAP(85) ZONE_DISABLE_PCT(40) REDUN_NG(2) REDUN_OTHER(1)
|
||||
set -euo pipefail
|
||||
REPO="$(git rev-parse --show-toplevel 2>/dev/null || echo .)"
|
||||
# Mandatory skill error logging (skill-creator rule): log GENUINE functional failures only.
|
||||
# (Mongo read + local-JSON analysis; --console path execs rf-analyze.py -> helper for consistency, no live failure branch.)
|
||||
logerr(){ bash "$REPO/.claude/scripts/log-skill-error.sh" "unifi-wifi/optimize-radios" "$1" --context "${2:-}" >/dev/null 2>&1 || true; }
|
||||
# Cloud connector path (non-UOS console): `optimize-radios.sh --console "<name>" [days] [band]` routes to
|
||||
# rf-analyze.py (Site Manager API; see references/site-manager-api.md). UOS Mongo path below is unchanged.
|
||||
# NEIGHBOR_JSON / ROAM_MIN / CAP / ZONE_DISABLE_PCT / REDUN_* env vars pass through to the analyzer.
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
set -uo pipefail
|
||||
REPO="$(git rev-parse --show-toplevel 2>/dev/null || echo .)"
|
||||
VAULT="$REPO/.claude/scripts/vault.sh"
|
||||
# Mandatory skill error logging (skill-creator rule): log GENUINE functional failures only.
|
||||
logerr(){ bash "$REPO/.claude/scripts/log-skill-error.sh" "unifi-wifi/pfsense-backend" "$1" --context "${2:-}" >/dev/null 2>&1 || true; }
|
||||
VP="${1:?usage: pfsense-backend.sh <vault-path> <action> [args] [--apply]}"
|
||||
ACT="${2:?action: audit|pf-list|pf-disable|pf-enable|pf-delete|pf-set-ports|fw-list|fw-disable|fw-enable|block-ips|setup}"
|
||||
shift 2
|
||||
@@ -215,3 +217,6 @@ try:
|
||||
except urllib.error.HTTPError as e: err(e)
|
||||
except Exception as e: print("[FAIL]",e); sys.exit(1)
|
||||
PY
|
||||
rc=$?
|
||||
[ "$rc" -ne 0 ] && logerr "pfsense-backend REST call failed" "vp=$VP act=$ACT rc=$rc"
|
||||
exit $rc
|
||||
|
||||
@@ -32,6 +32,11 @@ REPO="$(git rev-parse --show-toplevel 2>/dev/null || echo .)"
|
||||
VAULT="$REPO/.claude/scripts/vault.sh"
|
||||
HERE="$(cd "$(dirname "$0")" && pwd)"
|
||||
GWC_PHP="$HERE/pfsense-gwc.php"
|
||||
# Mandatory skill error logging (skill-creator rule): log GENUINE functional failures only
|
||||
# (SSH connect/auth fail, php fatal, easyrule non-success) — NOT handled conditions (missing cred,
|
||||
# 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]}"
|
||||
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=()
|
||||
@@ -64,19 +69,26 @@ ASKP="$TMP/a.sh"; printf '#!/bin/sh\nprintf "%%s\\n" "$PP"\n' >"$ASKP"; chmod +x
|
||||
pfssh(){ SSH_ASKPASS="$ASKP" SSH_ASKPASS_REQUIRE=force DISPLAY=:0 ssh -p "$PORT" \
|
||||
-o ConnectTimeout=12 -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=/dev/null \
|
||||
-o PreferredAuthentications=password -o PubkeyAuthentication=no -o NumberOfPasswordPrompts=1 \
|
||||
"$U@$HOST" 'sh -s' 2>/dev/null; }
|
||||
"$U@$HOST" 'sh -s' 2>/dev/null; local rc=$?
|
||||
[ "$rc" = 255 ] && logerr "SSH connect/auth failed (rc=255)" "host=$HOST:$PORT slug=$SLUG act=$ACT"
|
||||
return $rc; }
|
||||
|
||||
# shell-quote a value for safe embedding in the remote sh script
|
||||
sq(){ printf "'%s'" "$(printf '%s' "${1:-}" | sed "s/'/'\\\\''/g")"; }
|
||||
# ship pfsense-gwc.php to /tmp on the box (base64 over the wire) and run it with argv: action apply T V1 V2
|
||||
run_gwc(){
|
||||
[ -f "$GWC_PHP" ] || { echo "[ERROR] helper not found: $GWC_PHP"; exit 1; }
|
||||
[ -f "$GWC_PHP" ] || { echo "[ERROR] helper not found: $GWC_PHP"; logerr "gwc helper missing" "path=$GWC_PHP"; exit 1; }
|
||||
local b64; b64="$(base64 "$GWC_PHP" | tr -d '\n')"
|
||||
{ printf 'A=%s; AP=%s; T=%s; V1=%s; V2=%s\n' "$(sq "$1")" "$(sq "$2")" "$(sq "$3")" "$(sq "$4")" "$(sq "$5")"
|
||||
local out
|
||||
out="$( { printf 'A=%s; AP=%s; T=%s; V1=%s; V2=%s\n' "$(sq "$1")" "$(sq "$2")" "$(sq "$3")" "$(sq "$4")" "$(sq "$5")"
|
||||
printf 'printf %%s %s | openssl base64 -A -d > /tmp/pfsense-gwc.php\n' "$(sq "$b64")"
|
||||
printf 'php /tmp/pfsense-gwc.php "$A" "$AP" "$T" "$V1" "$V2" 2>&1\n' # 2>&1: surface php fatals (display_errors is Off on pfSense)
|
||||
printf 'rm -f /tmp/pfsense-gwc.php\n'
|
||||
} | pfssh
|
||||
} | pfssh )"
|
||||
printf '%s\n' "$out"
|
||||
case "$out" in
|
||||
*"Fatal error"*|*"[FAIL]"*) logerr "pfsense-gwc functional error" "slug=$SLUG act=$1 host=$HOST:$PORT" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
echo "[INFO] pfSense $ACT @ $U@$HOST:$PORT (vault:$VP)"
|
||||
@@ -101,7 +113,8 @@ case "$ACT" in
|
||||
echo " [dry-run] would run (per ip): easyrule $VERB $BLOCK_IF <ip>. Add --apply to write."; exit 0; fi
|
||||
CMDS=""; IFS=',' read -ra ARR <<< "$IPS"
|
||||
for ip in "${ARR[@]}"; do ip="$(printf '%s' "$ip" | tr -d ' ')"; [ -n "$ip" ] && CMDS+="echo \"-- $VERB $ip\"; easyrule $VERB $BLOCK_IF $ip; "; done
|
||||
printf '%s\n' "$CMDS" | pfssh ;;
|
||||
out="$(printf '%s\n' "$CMDS" | pfssh)"; printf '%s\n' "$out"
|
||||
case "$out" in *[Ss]uccess*) ;; *) logerr "easyrule $VERB did not report success" "if=$BLOCK_IF ips=$IPS" ;; esac ;;
|
||||
|
||||
showblock)
|
||||
printf 'easyrule showblock %s\n' "$BLOCK_IF" | pfssh ;;
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
set -uo pipefail
|
||||
REPO="$(git rev-parse --show-toplevel 2>/dev/null || echo .)"
|
||||
UOS="$REPO/.claude/scripts/uos-mongo.sh"; VAULT="$REPO/.claude/scripts/vault.sh"
|
||||
# Mandatory skill error logging (skill-creator rule): log GENUINE functional failures only.
|
||||
logerr(){ bash "$REPO/.claude/scripts/log-skill-error.sh" "unifi-wifi/radio-usage" "$1" --context "${2:-}" >/dev/null 2>&1 || true; }
|
||||
HOST="${UOS_HOST:-172.16.3.29}"; PORT="${UOS_HTTPS_PORT:-11443}"
|
||||
SITEARG="${1:?usage: radio-usage.sh <site> [band=ng|na|6e] [days=30] [--ap <name>]}"; shift
|
||||
BAND="ng"; DAYS="30"; APNAME=""; POS=()
|
||||
@@ -44,7 +46,7 @@ if [ -n "$APNAME" ]; then
|
||||
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]}))' "$CU" "$CP")")
|
||||
[ "$code" = "200" ] || { echo "[ERROR] controller login HTTP $code"; exit 1; }
|
||||
[ "$code" = "200" ] || { echo "[ERROR] controller login HTTP $code"; logerr "UOS controller login failed (HTTP $code)" "host=$HOST:$PORT site=$SITE band=$BAND"; exit 1; }
|
||||
SHORT="$(curl -sk -b "$CJ" "$base/proxy/network/api/self/sites" | python -c "import sys,json;[print(s['name']) for s in json.load(sys.stdin).get('data',[]) if s.get('_id')=='$SITE']")"
|
||||
[ -n "$SHORT" ] || SHORT="$SITEARG"
|
||||
curl -sk -b "$CJ" "$base/proxy/network/api/s/$SHORT/stat/device" -o "$TMP/dev.json"
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
set -uo pipefail
|
||||
REPO="$(git rev-parse --show-toplevel 2>/dev/null || echo .)"
|
||||
UOS="$REPO/.claude/scripts/uos-mongo.sh"
|
||||
# Mandatory skill error logging (skill-creator rule): log GENUINE functional failures only.
|
||||
# (Mongo read + vault-listing overview; no external call with an uninstrumented failure branch -> helper for consistency.)
|
||||
logerr(){ bash "$REPO/.claude/scripts/log-skill-error.sh" "unifi-wifi/sites" "$1" --context "${2:-}" >/dev/null 2>&1 || true; }
|
||||
VROOT="${VAULT_ROOT:-$(jq -r '.vault_path // empty' "$REPO/.claude/identity.json" 2>/dev/null)}"
|
||||
[ -n "$VROOT" ] || VROOT="$REPO/../vault"
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
set -uo pipefail
|
||||
REPO="$(git rev-parse --show-toplevel 2>/dev/null || echo .)"
|
||||
VAULT="$REPO/.claude/scripts/vault.sh"
|
||||
# Mandatory skill error logging (skill-creator rule): log GENUINE functional failures only.
|
||||
logerr(){ bash "$REPO/.claude/scripts/log-skill-error.sh" "unifi-wifi/survey-collect" "$1" --context "${2:-}" >/dev/null 2>&1 || true; }
|
||||
HOST="${UOS_HOST:-172.16.3.29}"; PORT="${UOS_HTTPS_PORT:-11443}"
|
||||
SITEARG="${1:?usage: survey-collect.sh <site-name|id> [ap-ssh-vault-path]}"
|
||||
VP="${2:-clients/cascades-tucson/unifi-ap-ssh}"
|
||||
@@ -28,7 +30,7 @@ CP="$(bash "$VAULT" get-field infrastructure/uos-server-network-api-rw credentia
|
||||
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]}))' "$CU" "$CP")")
|
||||
[ "$code" = "200" ] || { echo "[ERROR] controller login HTTP $code"; exit 1; }
|
||||
[ "$code" = "200" ] || { echo "[ERROR] controller login HTTP $code"; logerr "UOS controller login failed (HTTP $code)" "host=$HOST:$PORT site=$SITEARG"; exit 1; }
|
||||
SHORT="$(curl -sk -b "$CJ" "$base/proxy/network/api/self/sites" | python -c "
|
||||
import sys,json; d=json.load(sys.stdin).get('data',[]); q='''$SITEARG'''.lower()
|
||||
for s in d:
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
set -uo pipefail
|
||||
REPO="$(git rev-parse --show-toplevel 2>/dev/null || echo .)"
|
||||
UOS="$REPO/.claude/scripts/uos-mongo.sh"; VAULT="$REPO/.claude/scripts/vault.sh"
|
||||
# Mandatory skill error logging (skill-creator rule): log GENUINE functional failures only.
|
||||
logerr(){ bash "$REPO/.claude/scripts/log-skill-error.sh" "unifi-wifi/switch-audit" "$1" --context "${2:-}" >/dev/null 2>&1 || true; }
|
||||
HOST="${UOS_HOST:-172.16.3.29}"; PORT="${UOS_HTTPS_PORT:-11443}"
|
||||
SITEARG="${1:?usage: switch-audit.sh <site-name|id> [--all-ports]}"; ALL="${2:-}"
|
||||
TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT
|
||||
@@ -18,7 +20,7 @@ P="$(bash "$VAULT" get-field infrastructure/uos-server-network-api-rw credential
|
||||
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")")
|
||||
[ "$code" = "200" ] || { echo "[ERROR] controller login HTTP $code"; exit 1; }
|
||||
[ "$code" = "200" ] || { echo "[ERROR] controller login HTTP $code"; logerr "UOS controller login failed (HTTP $code)" "host=$HOST:$PORT site=$SITEARG"; exit 1; }
|
||||
SHORT="$(curl -sk -b "$CJ" "$base/proxy/network/api/self/sites" | python -c "
|
||||
import sys,json; d=json.load(sys.stdin).get('data',[]); q='''$SITEARG'''.lower()
|
||||
for s in d:
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
set -euo pipefail
|
||||
REPO="$(git rev-parse --show-toplevel 2>/dev/null || echo .)"
|
||||
VAULT="$REPO/.claude/scripts/vault.sh"
|
||||
# Mandatory skill error logging (skill-creator rule): log GENUINE functional failures only (SSH connect/auth fail rc=255).
|
||||
logerr(){ bash "$REPO/.claude/scripts/log-skill-error.sh" "unifi-wifi/watch-ap" "$1" --context "${2:-}" >/dev/null 2>&1 || true; }
|
||||
AP="${1:?usage: watch-ap.sh <ap-ip> [interval] [vault-path]}"; INT="${2:-2}"; VP="${3:-clients/cascades-tucson/unifi-ap-ssh}"
|
||||
U="$(bash "$VAULT" get-field "$VP" credentials.username 2>/dev/null)"
|
||||
P="$(bash "$VAULT" get-field "$VP" credentials.password 2>/dev/null)"
|
||||
@@ -25,12 +27,12 @@ P="$(bash "$VAULT" get-field "$VP" credentials.password 2>/dev/null)"
|
||||
SSH_OPTS=(-o ConnectTimeout=8 -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=/dev/null \
|
||||
-o PreferredAuthentications=password -o PubkeyAuthentication=no -o NumberOfPasswordPrompts=1)
|
||||
if command -v sshpass >/dev/null 2>&1; then
|
||||
run_ssh() { SSHPASS="$P" sshpass -e ssh "${SSH_OPTS[@]}" "$@"; }
|
||||
run_ssh() { local rc; SSHPASS="$P" sshpass -e ssh "${SSH_OPTS[@]}" "$@" || { rc=$?; [ "$rc" = 255 ] && logerr "watch-ap SSH connect/auth failed (rc=255)" "ap=$AP vp=$VP"; return $rc; }; }
|
||||
echo "[INFO] auth: sshpass"
|
||||
else
|
||||
ASKPASS="$(mktemp)"; printf '#!/bin/sh\nprintf "%%s\\n" "$WATCH_AP_PW"\n' > "$ASKPASS"; chmod +x "$ASKPASS"
|
||||
trap 'rm -f "$ASKPASS"' EXIT
|
||||
run_ssh() { WATCH_AP_PW="$P" SSH_ASKPASS="$ASKPASS" SSH_ASKPASS_REQUIRE=force DISPLAY="${DISPLAY:-:0}" ssh "${SSH_OPTS[@]}" "$@"; }
|
||||
run_ssh() { local rc; WATCH_AP_PW="$P" SSH_ASKPASS="$ASKPASS" SSH_ASKPASS_REQUIRE=force DISPLAY="${DISPLAY:-:0}" ssh "${SSH_OPTS[@]}" "$@" || { rc=$?; [ "$rc" = 255 ] && logerr "watch-ap SSH connect/auth failed (rc=255)" "ap=$AP vp=$VP"; return $rc; }; }
|
||||
echo "[INFO] auth: SSH_ASKPASS fallback (sshpass not installed)"
|
||||
fi
|
||||
|
||||
|
||||
@@ -17,6 +17,10 @@ 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: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]
|
||||
|
||||
2026-06-21 | Howard-Home | guruscan/whitelist-design | [correction] over-engineered whitelist as dynamic service-discovery; correct approach is a simple static hard list of install folders (scanners that support exclude-lists ignore listed folders; RKill won't, accepted)
|
||||
|
||||
2026-06-21 | Howard-Home | bitdefender | GravityZone API error [policies.getPolicyDetails]: Invalid value for 'policyId' parameter. [ctx: cmd=policy]
|
||||
|
||||
Reference in New Issue
Block a user