Files
claudetools/session-logs/2026-06/2026-06-21-howard-security-assessment-deploy.md
Howard Enos 5a0aaa2ff2 sync: auto-sync from HOWARD-HOME at 2026-06-21 18:27:49
Author: Howard Enos
Machine: HOWARD-HOME
Timestamp: 2026-06-21 18:27:49
2026-06-21 18:28:14 -07:00

7.9 KiB

Session — security.azcomputerguru.com: live deploy + smoke test (found/fixed prod auth bug)

User

  • User: Howard Enos (howard)
  • Machine: Howard-Home
  • Role: tech

Session Summary

Continued the security-assessment work (same HOWARD-HOME session; see the two companion logs from today). Howard requested: make UI elements slightly larger, test every field for usable info, and a big new multi-tenant client portal + ACG sales view. Did the unambiguous pieces, deferred the portal to a feature request, then (on Howard's "b" greenlight) deployed to the live IX host and functionally tested — which surfaced a production-breaking bug.

Bumped the wizard UI sizing ~10-12% (text, inputs, spacing, wider sheet, bigger buttons) and committed it. Built app/fieldcheck.cjs and audited every questions.json field: 59 fields / 25 scorable, all 25 emit a usable finding on worst-case answers, no orphan/missing score keys, all findings complete, all 13 requiredControls resolve — every field yields usable info. Captured the portal vision as FR-1 in FEATURES.md (personas, no-auto-sync guardrail, quote→active workflow, 3 auth options) and asked the foundational auth decision; Howard chose to defer it to a feature request and focus on testing. Added an executive summary + top-3 prioritized actions + ACG footer to the export (client = recommendations, internal = + ACG service).

Deployed to IX (172.16.3.10, cPanel account azcomputerguru, docroot /home/azcomputerguru/public_html/security, PHP 8.1.34) over SSH: backed up the live files, uploaded to .new, lint-gated (php -l + JSON validate), then atomic-swapped into place. config.php (live secrets) was never touched. The public URL was already live behind Cloudflare Access (a public hit returns CF Access 403), so this was an update, not first-time setup.

Smoke-testing on the server found a production-breaking bug: api.php's allow-list check did a single strcasecmp($email, ALLOWED_EMAIL) against the WHOLE comma-separated list, so once a 2nd address (howard@) was added on Jun 19, every API call (lookup/save/load/list/export) returned forbidden — the live backend had been unusable. Fixed it to split + membership-check (matching index.php), redeployed, and re-verified: action=list reads the DB; internal export renders posture/exec-summary/findings/ACG- service/REQUIRED/captured-intake; client export renders posture+recommendations with zero upsell leaks and no raw intake. Inserted a weak-answer test row, exported both views, then deleted it — prod DB back to its 2 real rows. Committed everything to submodule main and advanced the claudetools pin so git matches the live server.

Key Decisions

  • Deploy via root SSH + base64 file upload, lint-gated atomic swap. Backup → upload .newphp -l/JSON-validate → mv into place. Never overwrite config.php. Safe for a live site.
  • Functional-test via PHP CLI + a shipped harness, not inline php -r. Inline ssh "php -r '...'" mangled through nested quoting (printed PHP usage); a base64'd .php harness that includes api.php and drives $_GET[action] is reliable. Bypasses the Apache/Cloudflare vhost routing (origin-direct curl 404'd — a vhost/SNI artifact, not a broken deploy).
  • Test row written to the tool's own MySQL then deleted — acceptable for a smoke test (its own DB, not Syncro/RMM), clearly tagged ZZ-SMOKE-DELETE, cleaned up.
  • Portal deferred to FR-1 (Howard's call) rather than building a multi-tenant auth model speculatively.
  • MSYS ssh for password+askpass, not native Windows OpenSSH (which fails the .sh askpass) — matches the working pfsense-ssh.sh pattern.

Problems Encountered

  • PROD BUG — api.php 403 for everyone (single strcasecmp vs comma list). Found via live smoke test; fixed + redeployed + verified. This is why "test every function" mattered.
  • Native Windows OpenSSH askpass failed CreateProcessW error:193 (can't exec a .sh askpass) → used MSYS bare ssh. Logged --friction.
  • Inline php -r over ssh mangled (nested quote stripping) → shipped a base64'd .php harness. Logged --friction (ref feedback_windows_quote_stripping).
  • Origin-direct curl 404'd (vhost/SNI; CF proxies the real path) → tested via PHP CLI instead.
  • Submodule reset to old scaffold earlier (stale gitlink + a concurrent submodule-update) → restored to origin/main and advanced the claudetools pin to stop the churn.

Configuration Changes

security-assessment submodule (all on main, pushed; claudetools pin advanced each time):

  • app/index.php — UI sizing bump (66eb7cb).
  • app/api.php — export exec-summary/top-3/footer (3e3a9ab); allow-list auth fix (f246091).
  • FEATURES.md (new) — FR-1 portal request; app/fieldcheck.cjs (new) — field-audit dev tool (3a2301b).
  • Local scratch app/_deploytest.php + app/_smoke.php created for testing, removed after (not committed). Live IX server: index.php, api.php, questions.json updated in the docroot (backups .bak-20260621-181744); config.php untouched. ClaudeTools: this session log + errorlog.md (2 friction entries).

Credentials & Secrets

None created/discovered. Used existing vault infrastructure/ix-server (root SSH 172.16.3.10:22, password field credentials.password; also a WHM API token credentials.whm-api-token = full-access root). DB creds live in the server's config.php (vault msp-tools/security-assessment-db). No secrets printed/committed.

Infrastructure & Servers

  • IX server 172.16.3.10 (ext 72.194.62.5), Rocky Linux WHM/cPanel, PHP 8.1.34 (ea-php81), root SSH :22.
  • Site docroot: /home/azcomputerguru/public_html/security (cPanel acct azcomputerguru, sub of azcomputerguru.com, vhost 172.16.3.10:80/:443).
  • security.azcomputerguru.com behind Cloudflare Access (Zero Trust app 8ce5f31c-...; allow mike@ + howard@). Origin answers via CF; origin-direct curl needs --resolve/Host and still 404'd (CF-proxied path is the working one).
  • Live DB (acgsec_assess): 2 real rows (id 1 Darrell Delphen, id 2 empty) after test cleanup.

Commands & Outputs

# find docroot
ssh root@172.16.3.10 'grep -i security.azcomputerguru.com /etc/userdatadomains'
#   -> azcomputerguru==root==sub==azcomputerguru.com==/home/azcomputerguru/public_html/security==...ea-php81
# deploy pattern: backup -> base64 upload to .new -> php -l -> chown user -> mv (atomic)
base64 local.php | ssh root@ix "base64 -d > docroot/file.new"
# functional test (bypasses Apache/CF): shipped harness includes api.php
php _deploytest.php list                 # {"items":[{id:2...},{id:1,...}]}  (after the auth fix)
php _deploytest.php export <id> internal # posture/exec/findings/ACG service/Captured intake
php _deploytest.php export <id> client   # posture/recommendations, 0 ACG-service, no Captured intake
# BUG before fix: php _deploytest.php list -> {"error":"forbidden"}

Pending / Incomplete Tasks

  • Live browser UI click-through (through Cloudflare Access) — the only thing not tested; needs a human logged into https://security.azcomputerguru.com (look up a client, answer fields, watch posture, Export both views). Backend fully verified server-side.
  • #1 GuruRMM endpoint prefill — deferred (infra: no Syncro→RMM mapping, no reachable RMM API from IX).
  • FR-1 multi-tenant portal — filed in FEATURES.md, awaiting the auth decision when Howard wants it.

Reference Information

  • Submodule main HEAD f246091 (claudetools pin 27c1d97 at time of fix).
  • Commits this turn: 66eb7cb sizing, 3a2301b FR-1+fieldcheck, 3e3a9ab export exec-summary, f246091 api auth fix.
  • Live server backups: *.bak-20260621-181744 in the docroot (rollback if needed).
  • Export endpoint: api.php?action=export&id=<id>&view=internal|client (origin requires the CF Access email header).
  • Companion logs: 2026-06-21-howard-security-assessment-scoring.md, ...-unifi-pfsense-control-verbs.md, ...-gururmm-bug-018-019.md.