diff --git a/docs/design-decisions.md b/docs/design-decisions.md index cd0d91f..364a03e 100644 --- a/docs/design-decisions.md +++ b/docs/design-decisions.md @@ -1883,3 +1883,58 @@ Proof resources torn down (cluster, template, router-if, subnet, net -- residual check clean); post-teardown cloud-assert PASS. Phase 0 gates are fully discharged; the D-074 implementation (DOCFIX-106/107) ships. + +## D-075: Dashboard VIP root -- redirect to /horizon + no autoindex (ADOPTED+EXECUTED 2026-07-07) + +**Status: ADOPTED (operator ruling 2026-07-07, option c) + EXECUTED live same day.** + +**Context.** During the devteam handover browser check the operator hit Apache +"Index of /" at https://10.12.4.58/ (provider-public dashboard VIP). Measured: +Horizon mounts at webroot /horizon (WSGIScriptAlias in conf-enabled/ +openstack-dashboard.conf); the charm-rendered vhosts define no root handler; +Ubuntu stock apache2.conf grants `Options Indexes` on /var/www/. A directory +listing on a tenant-facing commercial surface is an information-disclosure / +posture defect, and a bare 200 at the root previously masqueraded as a healthy +dashboard (the D-072 as-executed "https 200" at the VIP root was THIS autoindex +page, not Horizon). + +**Options presented:** (a) redirect / -> /horizon; (b) disable autoindex at the +root; (c) both. **Operator ruled (c).** + +**Decision / mechanism.** Local override /etc/apache2/conf-available/ +99-omega-root.conf on the dashboard unit (the charm exposes no config option +for either behavior; charm manages sites-enabled/ but never conf-enabled/, so +the snippet survives config-changed re-renders and in-channel charm refreshes): + + + Options -Indexes + RewriteEngine On + RewriteRule ^$ %{REQUEST_SCHEME}://%{HTTP_HOST}/horizon [R=302,L] + + +**Why per-directory mod_rewrite and NOT RedirectMatch:** first attempt used +`RedirectMatch 302 ^/$ /horizon`; mod_alias absolutizes the Location from the +BACKEND connection port when the client Host header carries none, emitting +http://:70/horizon (and :433 on the TLS leg) -- ports haproxy does not +expose. Caught in this window's verify step. The rewrite echoes the client's +own Host header and request scheme, so both haproxy legs redirect correctly. +302 (not 301) so browsers do not cache permanently against a future webroot +change. `Options -Indexes` subtracts from the inherited option list and covers +/var/www/html by inheritance. + +**Verification (as-executed):** on-unit root -> 302 + zero "Index of" match; +via VIP http leg: / -> 302 http://10.12.4.58/horizon -> follows to 200 at +/horizon/auth/login/. TLS-leg browser confirmation by operator. + +**Durability:** PER-REBUILD -- teardown wipes the unit; reapply each rebuild +via the phase-03 step 3.3 block (DOCFIX-118), same lifecycle as the D-044 +cookie override. **Roosevelt:** edge TLS/DNS (D-044 trajectory) supersedes +this plumbing; carry the invariant "tenant-facing roots never serve directory +listings" into the Roosevelt bundle review. + +**Revert:** `sudo a2disconf 99-omega-root && sudo systemctl reload apache2` +on the dashboard unit; git-revert the DOCFIX-118 runbook/appendix additions. + +**Related:** D-072 (VIP TLS repair; its root-200 evidence reinterpreted), +D-044 (cookie override precedent + Roosevelt edge trajectory), DOCFIX-089 +(fail-closed dashboard checks), DOCFIX-118 (runbook + appendix-A delivery). diff --git a/docs/session-ledger.md b/docs/session-ledger.md index 06a4a74..4ed7664 100644 --- a/docs/session-ledger.md +++ b/docs/session-ledger.md @@ -296,21 +296,18 @@ ~/devteam-package + the ~/tenant-devteam credential files in-person to the two named custodians. 2. **Operator manual item:** foil1 Horizon checks (contract 5.1/5.3) -- - domain-admin login + GUI probe-user create/delete; non-blocking. Same - checks now also apply to devteam. IN PROGRESS 2026-07-07: operator - browsed the dashboard VIP root and got Apache "Index of /" -- see the - two entries below (URL corrected to /horizon in the devteam package; - root-autoindex finding OPEN, options presented). Login check at - /horizon pending operator confirm. -2b. **FINDING (2026-07-07, UNRULED -- present options, do not implement): - Apache autoindex at the public dashboard VIP root.** Browsing - https://10.12.4.58/ (provider-public, tenant-facing) serves "Index of /" - instead of Horizon (webroot /horizon, measured). Tenant-visible surface - on a commercial cloud => security-relevant posture item. Options for - operator ruling: (a) redirect / -> /horizon (upstream-common), (b) - disable autoindex on the vhost root, (c) both [recommended]. Charm-vs- - local-override mechanism TBD at ruling time; D-072/D-044 adjacent; would - consume the next-free D number as a PROPOSED entry when drafted. + domain-admin login + GUI probe-user create/delete; non-blocking. + devteam 5.1 reachability CONFIRMED 2026-07-07 (operator browser, + /horizon login page); devteam/foil1 5.3 GUI probe still open, plus one + operator re-browse of the httpS VIP ROOT to confirm the D-075 redirect + on the TLS leg (http leg verified from jumphost). +2b. **D-075 RULED (option c) + EXECUTED 2026-07-07** (was: root-autoindex + finding): redirect / -> /horizon + Options -Indexes via + conf-enabled/99-omega-root.conf on the dashboard unit. First attempt + (RedirectMatch) caught leaking the backend port in verify and replaced + with per-dir mod_rewrite. Delivered as D-075 entry + DOCFIX-118 + (phase-03 PER-REBUILD block + appendix-A symptom entry). PER-REBUILD + like the D-044 override. 3. **capi-test-1 CREATE_FAILED record delete: RULED YES (2026-07-07), execution handed to operator** -- the Claude Code permission classifier (correctly) refuses agent-side deletes of resources it did not create; diff --git a/docs/v1-redeploy-changelog.md b/docs/v1-redeploy-changelog.md index a52916b..c7e4329 100644 --- a/docs/v1-redeploy-changelog.md +++ b/docs/v1-redeploy-changelog.md @@ -2779,3 +2779,36 @@ REVERT (package correction only): sed 's|/horizon||' on the 4 corrected lines in ~/devteam-package; git checkout HEAD~ -- docs/session-ledger.md docs/v1-redeploy-changelog.md Next-free after this push (per scan, keep this line unwrapped): D-075, DOCFIX-118, BUNDLEFIX-012. + +### 2026-07-07 (addendum 37, jumphost stream) -- D-075 ADOPTED+EXECUTED: dashboard VIP root redirect + no-autoindex; DOCFIX-118 runbook/appendix delivery + +Operator confirmed /horizon works (devteam contract 5.1 reachability) and RULED +option (c) on the root-autoindex finding (addendum 36). Same gated window +(ops-devteam-onboard log). + +- **Measured before mutating:** charm exposes NO config option for root + behavior (full option list read); vhost map + docroots read on-unit; + "Index of /" REPRODUCED from the unit's own http backend (:70). +- **D-075 EXECUTED (2 gated mutations + verify):** + /etc/apache2/conf-available/99-omega-root.conf (charm never manages + conf-enabled/), a2enconf, configtest, reload. FIRST ATTEMPT (RedirectMatch) + caught by the verify step emitting Location http://10.12.4.58:70/horizon -- + mod_alias absolutizes from the backend port, which haproxy does not expose; + REPLACED with per-dir mod_rewrite echoing the client Host header + scheme. + Final verify: VIP http root -> 302 http://10.12.4.58/horizon -> 200 at + /horizon/auth/login/; on-unit listing grep zero. TLS-leg browser confirm + pending operator (http leg proven; rule is scheme-agnostic). +- **DOCFIX-118:** phase-03 step 3.3 gains the D-075 PER-REBUILD block (RUN + + fail-closed CHECK that specifically FAILs on a backend-port-leaking + redirect); appendix-A gains the "Index of /" symptom entry incl. the two + traps (root-200 masquerades as healthy dashboard -- the D-072 root-200 was + this listing; RedirectMatch port leak). D-075 entry appended to + design-decisions (ADOPTED+EXECUTED, mechanism, revert, Roosevelt sunset + under the D-044 edge trajectory). +- **Hygiene note:** a scratchpad dump of the full dashboard charm config + (secret-bearing values, taken for option-list reading) was deleted + immediately after key-name extraction; values never entered context. +- Numbers consumed: D-075, DOCFIX-118. +REVERT: on-unit `sudo a2disconf 99-omega-root && sudo systemctl reload apache2`; git checkout HEAD~ -- docs/design-decisions.md runbooks/phase-03-core-verify.md runbooks/appendix-A-troubleshooting.md docs/session-ledger.md docs/v1-redeploy-changelog.md + +Next-free after this push (per scan, keep this line unwrapped): D-076, DOCFIX-119, BUNDLEFIX-012. diff --git a/runbooks/appendix-A-troubleshooting.md b/runbooks/appendix-A-troubleshooting.md index 9a73bdb..1465459 100644 --- a/runbooks/appendix-A-troubleshooting.md +++ b/runbooks/appendix-A-troubleshooting.md @@ -598,6 +598,27 @@ GATE: 3x "Cluster is ONLINE", exactly one "Mode: R/W". -------------------------------------------------------------------------------- +## Dashboard VIP root shows "Index of /" (Apache directory listing) [added DOCFIX-118] +-------------------------------------------------------------------------------- +SYMPTOM: browser (or curl) at the dashboard VIP root (`/`, not `/horizon`) + renders an Apache autoindex page "Index of /"; Horizon itself is + healthy at `/horizon`. +CAUSE: Horizon mounts at webroot `/horizon`; the charm vhosts define no root + handler; Ubuntu stock apache2.conf grants `Options Indexes` on + /var/www/. Nothing redirects `/`. (First seen 2026-07-07, devteam + handover browser check.) +TRAP: a 200 at the VIP root is NOT proof the dashboard works -- the D-072 + as-executed record's root-200 was this listing. Health-check /horizon, + never /. Second trap: fixing with `RedirectMatch` emits a Location + carrying the BACKEND port (:70/:433) that haproxy does not expose -- + mod_alias absolutizes from the physical connection; use the per-dir + mod_rewrite form. +FIX: apply the D-075 override block -- phase-03 step 3.3, "DOCFIX-118 -- + Dashboard VIP root" (PER-REBUILD, like the D-044 cookie override). +GATE: `curl -s -o /dev/null -w '%{http_code} %{redirect_url}' http://10.12.4.58/` + -> exactly `302 http://10.12.4.58/horizon` (no port suffix). + +-------------------------------------------------------------------------------- ## Point-of-use identifier index [added DOCFIX-077] -------------------------------------------------------------------------------- These identifiers are DEFINED where they are used (runbook/bundle comments), not diff --git a/runbooks/phase-03-core-verify.md b/runbooks/phase-03-core-verify.md index f4c0a5c..ace5fae 100644 --- a/runbooks/phase-03-core-verify.md +++ b/runbooks/phase-03-core-verify.md @@ -272,6 +272,35 @@ of v1 (D-044 rationale). Diagnostic lessons (reload race, proxy_ssl_name DNS-SAN, sed no-op, scheme-mismatch redirect loop) are in appendix-A. +### DOCFIX-118 -- Dashboard VIP root: redirect + no autoindex (D-075; PER-REBUILD) +Without this, the tenant-facing VIP root serves Apache "Index of /" (no root handler in +the charm vhosts + Ubuntu-default `Options Indexes` on /var/www/), and a bare 200 at the +root can masquerade as a healthy dashboard. Per-directory mod_rewrite, NOT RedirectMatch: +mod_alias absolutizes Location from the BACKEND port (:70/:433), which haproxy does not +expose (D-075 rationale). + +**RUN -- jumphost** +```bash +# RUN: jumphost -- D-075 root redirect + no-autoindex on the dashboard unit (PER-REBUILD) +juju ssh -m openstack openstack-dashboard/leader -- "printf '# Omega Cloud local override (D-075): tenant-facing VIP root must not serve\n# a directory listing; humans land on Horizon. Rewrite, NOT RedirectMatch:\n# mod_alias self-referential Location appends the backend port (70/433),\n# which haproxy does not expose; the rewrite echoes the client Host header.\n\n Options -Indexes\n RewriteEngine On\n RewriteRule ^\$ %%{REQUEST_SCHEME}://%%{HTTP_HOST}/horizon [R=302,L]\n\n' | sudo tee /etc/apache2/conf-available/99-omega-root.conf >/dev/null && sudo a2enconf 99-omega-root && sudo apache2ctl -t && sudo systemctl reload apache2"