feat(RAILIANCE-WP-0027): prepare operator-only OpenBao access
Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a02b90-83bf-75c2-81c8-aa705414e4d4
This commit is contained in:
parent
082c76979e
commit
517f68593d
13 changed files with 413 additions and 82 deletions
25
Makefile
25
Makefile
|
|
@ -32,6 +32,7 @@ OPENBAO_VALUES ?= helm/openbao-values.yaml
|
|||
OPENBAO_MIDDLEWARE ?= helm/openbao-middleware.yaml
|
||||
OPENBAO_UI_OVERLAY_DIR ?= helm/openbao-ui-overlay
|
||||
OPENBAO_UI_OVERLAY_K8S ?= helm/openbao-ui-overlay-k8s.yaml
|
||||
OPENBAO_PUBLIC_INGRESS_ROLLBACK ?= helm/openbao-public-ingress.rollback.yaml
|
||||
OPENBAO_VERIFY_AUTH_ARGS ?=
|
||||
OPENBAO_RESTORE_EVIDENCE ?= /tmp/netkingdom-openbao-restore-drill/evidence.json
|
||||
OPENBAO_EMERGENCY_EVIDENCE ?= /tmp/netkingdom-openbao-emergency-drill/evidence.json
|
||||
|
|
@ -215,7 +216,6 @@ openbao-verify-login-overlay: ## Verify public KeyCape login overlay is active
|
|||
|
||||
openbao-deploy: openbao-repo ## Deploy / upgrade OpenBao to the openbao namespace
|
||||
$(KUBECTL) create namespace $(OPENBAO_NAMESPACE) --dry-run=client -o yaml | $(KUBECTL) apply -f -
|
||||
$(KUBECTL) apply -f $(OPENBAO_MIDDLEWARE)
|
||||
$(HELM) upgrade --install $(OPENBAO_RELEASE) openbao/openbao \
|
||||
--version $(OPENBAO_CHART_VERSION) \
|
||||
--namespace $(OPENBAO_NAMESPACE) \
|
||||
|
|
@ -223,6 +223,29 @@ openbao-deploy: openbao-repo ## Deploy / upgrade OpenBao to the openbao namespac
|
|||
--wait --timeout 5m
|
||||
$(MAKE) openbao-overlay-apply
|
||||
|
||||
.PHONY: openbao-public-listener-dry-run openbao-public-listener-status openbao-public-listener-retract openbao-public-listener-rollback
|
||||
|
||||
openbao-public-listener-dry-run: ## Validate the guarded public-listener retraction without mutation
|
||||
KUBECTL='$(KUBECTL)' OPENBAO_NAMESPACE=$(OPENBAO_NAMESPACE) \
|
||||
OPENBAO_UI_OVERLAY_K8S=$(OPENBAO_UI_OVERLAY_K8S) \
|
||||
OPENBAO_PUBLIC_INGRESS_ROLLBACK=$(OPENBAO_PUBLIC_INGRESS_ROLLBACK) \
|
||||
scripts/openbao-public-listener-transition.sh dry-run
|
||||
|
||||
openbao-public-listener-status: ## Report public Ingress and private tunnel readiness
|
||||
KUBECTL='$(KUBECTL)' OPENBAO_NAMESPACE=$(OPENBAO_NAMESPACE) \
|
||||
scripts/openbao-public-listener-transition.sh status
|
||||
|
||||
openbao-public-listener-retract: ## Attended deletion after exact tunnel/login confirmation
|
||||
KUBECTL='$(KUBECTL)' OPENBAO_NAMESPACE=$(OPENBAO_NAMESPACE) \
|
||||
OPENBAO_PUBLIC_INGRESS_ROLLBACK=$(OPENBAO_PUBLIC_INGRESS_ROLLBACK) \
|
||||
scripts/openbao-public-listener-transition.sh apply
|
||||
|
||||
openbao-public-listener-rollback: ## Attended restoration of the prior public Ingress
|
||||
KUBECTL='$(KUBECTL)' OPENBAO_NAMESPACE=$(OPENBAO_NAMESPACE) \
|
||||
OPENBAO_MIDDLEWARE=$(OPENBAO_MIDDLEWARE) \
|
||||
OPENBAO_PUBLIC_INGRESS_ROLLBACK=$(OPENBAO_PUBLIC_INGRESS_ROLLBACK) \
|
||||
scripts/openbao-public-listener-transition.sh rollback
|
||||
|
||||
openbao-status: ## Show OpenBao pods, services, PVCs, and seal/init status
|
||||
$(KUBECTL) get pods,svc,pvc -n $(OPENBAO_NAMESPACE) \
|
||||
-l app.kubernetes.io/instance=$(OPENBAO_RELEASE) -o wide
|
||||
|
|
|
|||
|
|
@ -24,11 +24,12 @@
|
|||
| workplan | RAILIANCE-WP-0023 | finished | — | workplans/RAILIANCE-WP-0023-hub-core-candidate-credential-lanes.md |
|
||||
| workplan | RAILIANCE-WP-0024 | active | — | workplans/RAILIANCE-WP-0024-audit-core-custody-and-recovery-coordination.md |
|
||||
| workplan | RAILIANCE-WP-0025 | finished | — | workplans/RAILIANCE-WP-0025-versioned-ephemeral-custody-lifecycle.md |
|
||||
| workplan | RAILIANCE-WP-0026 | ready | — | workplans/RAILIANCE-WP-0026-attended-login-output-containment.md |
|
||||
| workplan | RAILIANCE-WP-0026 | finished | — | workplans/RAILIANCE-WP-0026-attended-login-output-containment.md |
|
||||
| workplan | RPF-WP-0018 | finished | — | workplans/RPF-WP-0018-policy-surface-alignment.md |
|
||||
| workplan | RPF-WP-0019 | finished | — | workplans/RPF-WP-0019-apps-pg-recoverability-and-controls.md |
|
||||
| workplan | RPF-WP-0020 | finished | — | workplans/RPF-WP-0020-ccr-schema-drift.md |
|
||||
| workplan | RPF-WP-0021 | finished | — | workplans/RPF-WP-0021-core-hub-platform-onboarding.md |
|
||||
| workplan | RAILIANCE-WP-0027 | blocked | — | workplans/RAILIANCE-WP-0027-openbao-operator-only-access.md |
|
||||
| task | RAILIANCE-WP-0005-T01 | done | — | workplans/RAILIANCE-WP-0005-credential-request-and-lease-broker.md |
|
||||
| task | RAILIANCE-WP-0005-T02 | done | — | workplans/RAILIANCE-WP-0005-credential-request-and-lease-broker.md |
|
||||
| task | RAILIANCE-WP-0005-T03 | done | — | workplans/RAILIANCE-WP-0005-credential-request-and-lease-broker.md |
|
||||
|
|
@ -109,7 +110,7 @@
|
|||
| task | RAILIANCE-WP-0025-T04 | done | — | workplans/RAILIANCE-WP-0025-versioned-ephemeral-custody-lifecycle.md |
|
||||
| task | RAILIANCE-WP-0025-T05 | done | — | workplans/RAILIANCE-WP-0025-versioned-ephemeral-custody-lifecycle.md |
|
||||
| task | RAILIANCE-WP-0025-T06 | done | — | workplans/RAILIANCE-WP-0025-versioned-ephemeral-custody-lifecycle.md |
|
||||
| task | RAILIANCE-WP-0026-T01 | todo | — | workplans/RAILIANCE-WP-0026-attended-login-output-containment.md |
|
||||
| task | RAILIANCE-WP-0026-T01 | done | — | workplans/RAILIANCE-WP-0026-attended-login-output-containment.md |
|
||||
| task | RPF-WP-0018-T01 | done | — | workplans/RPF-WP-0018-policy-surface-alignment.md |
|
||||
| task | RPF-WP-0018-T02 | done | — | workplans/RPF-WP-0018-policy-surface-alignment.md |
|
||||
| task | RPF-WP-0018-T03 | done | — | workplans/RPF-WP-0018-policy-surface-alignment.md |
|
||||
|
|
@ -130,3 +131,6 @@
|
|||
| task | RPF-WP-0021-T03 | done | — | workplans/RPF-WP-0021-core-hub-platform-onboarding.md |
|
||||
| task | RPF-WP-0021-T04 | done | — | workplans/RPF-WP-0021-core-hub-platform-onboarding.md |
|
||||
| task | RPF-WP-0021-T05 | done | — | workplans/RPF-WP-0021-core-hub-platform-onboarding.md |
|
||||
| task | RAILIANCE-WP-0027-T01 | done | — | workplans/RAILIANCE-WP-0027-openbao-operator-only-access.md |
|
||||
| task | RAILIANCE-WP-0027-T02 | done | — | workplans/RAILIANCE-WP-0027-openbao-operator-only-access.md |
|
||||
| task | RAILIANCE-WP-0027-T03 | wait | — | workplans/RAILIANCE-WP-0027-openbao-operator-only-access.md |
|
||||
|
|
|
|||
68
docs/openbao-public-listener-transition.md
Normal file
68
docs/openbao-public-listener-transition.md
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
# OpenBao public-listener transition
|
||||
|
||||
Status: source implementation complete; live retraction held.
|
||||
|
||||
This is the S3-owner procedure for `RMASTER-WP-0020-T09`. It preserves the
|
||||
OpenBao Deployment, the `openbao-ui-gateway` Deployment, and its ClusterIP
|
||||
Service while removing the public `bao.coulomb.social` Ingress.
|
||||
|
||||
## Target access
|
||||
|
||||
- Exposure posture: `operator`
|
||||
- Named tunnel: `openbao-ui-railiance01`
|
||||
- Local UI: `http://127.0.0.1:18200`
|
||||
- Cluster target: `openbao/openbao-ui-gateway:8080`
|
||||
- Public Ingress: absent after the transition
|
||||
|
||||
The tunnel is an ops-bridge local forward to the current ClusterIP and does not
|
||||
open a node port. If the Service is recreated with a new ClusterIP, update and
|
||||
revalidate the tunnel before use.
|
||||
|
||||
Browser OIDC derives its callback from the local origin. Before retraction,
|
||||
both the KeyCape `openbao-admin` client and OpenBao
|
||||
`auth/netkingdom/role/platform-admin` must allow exactly:
|
||||
|
||||
```text
|
||||
http://127.0.0.1:18200/ui/vault/auth/netkingdom/oidc/callback
|
||||
```
|
||||
|
||||
Record only a value-safe attended-login result. Do not record the authorization
|
||||
code, token, accessor, callback query, browser storage, or screenshots.
|
||||
|
||||
## Guarded sequence
|
||||
|
||||
```bash
|
||||
make openbao-public-listener-dry-run
|
||||
make openbao-public-listener-status
|
||||
```
|
||||
|
||||
The status command deliberately fails if ops-bridge does not report the named
|
||||
tunnel as lifecycle-healthy, even when an orphan forward still answers. Repair
|
||||
that state before proceeding.
|
||||
|
||||
After KeyCape/OpenBao callback configuration and one attended MFA login through
|
||||
the local URL, use the exact hold point:
|
||||
|
||||
```bash
|
||||
OPENBAO_OPERATOR_LOGIN_VERIFIED=true \
|
||||
OPENBAO_PUBLIC_LISTENER_CONFIRM='RETRACT RMASTER-WP-0020-T09 PUBLIC OPENBAO LISTENER' \
|
||||
make openbao-public-listener-retract
|
||||
```
|
||||
|
||||
The command pins the railiance01 cluster UID, rechecks the ClusterIP gateway and
|
||||
tunnel, deletes only `openbao/openbao-ui-gateway` Ingress, then proves the
|
||||
private path remains healthy and the public name no longer returns a successful
|
||||
HTTP status.
|
||||
|
||||
## Rollback
|
||||
|
||||
During the bounded observation window:
|
||||
|
||||
```bash
|
||||
OPENBAO_PUBLIC_LISTENER_CONFIRM='ROLLBACK RMASTER-WP-0020-T09 PUBLIC OPENBAO LISTENER' \
|
||||
make openbao-public-listener-rollback
|
||||
```
|
||||
|
||||
Rollback reapplies only the retained Traefik middleware and the explicitly
|
||||
named rollback Ingress. It does not mutate OpenBao policy, Raft, PVCs, Secrets,
|
||||
or the CoulombCore retention state.
|
||||
|
|
@ -29,8 +29,9 @@ S5 workloads / operators
|
|||
-> audit storage PVC mounted at /openbao/audit
|
||||
|
||||
Platform operators with approved admin identity
|
||||
-> https://bao.coulomb.social
|
||||
-> Traefik Ingress + TLS
|
||||
-> bridge up openbao-ui-railiance01
|
||||
-> http://127.0.0.1:18200
|
||||
-> SSH local forward to the ClusterIP
|
||||
-> openbao-ui service
|
||||
-> OpenBao UI/API
|
||||
-> KeyCape OIDC at https://kc.coulomb.social for login
|
||||
|
|
@ -39,7 +40,8 @@ Platform operators with approved admin identity
|
|||
- OpenBao is the canonical Railiance S3 secrets service.
|
||||
- SOPS/age remains the Git-at-rest bootstrap mechanism.
|
||||
- The first Railiance01 deployment is single-replica Raft, not true HA.
|
||||
- Browser UI/API exposure is declared for `https://bao.coulomb.social`.
|
||||
- Browser UI exposure is operator-only through the named
|
||||
`openbao-ui-railiance01` tunnel.
|
||||
Operators authenticate through KeyCape/OIDC with MFA and the
|
||||
`platform-admin` role. Do not use the root token through the browser UI.
|
||||
- `kubectl exec` and port-forwarding remain valid break-glass/operator paths
|
||||
|
|
@ -61,11 +63,11 @@ make openbao-deploy
|
|||
make openbao-status
|
||||
```
|
||||
|
||||
`make openbao-deploy` applies `helm/openbao-middleware.yaml` (Traefik
|
||||
rate-limit and HSTS), upgrades the OpenBao Helm release, then applies the
|
||||
KeyCape login overlay gateway (`helm/openbao-ui-overlay-k8s.yaml`). Public
|
||||
ingress for `bao.coulomb.social` targets `openbao-ui-gateway`, not the chart
|
||||
ingress (which stays disabled in `helm/openbao-values.yaml`).
|
||||
`make openbao-deploy` upgrades the OpenBao Helm release, then applies the
|
||||
KeyCape login overlay gateway (`helm/openbao-ui-overlay-k8s.yaml`) as a
|
||||
ClusterIP-only surface. The chart ingress stays disabled. Public-listener
|
||||
retraction and rollback are separate guarded commands documented in
|
||||
`docs/openbao-public-listener-transition.md`.
|
||||
|
||||
On Railiance01 directly:
|
||||
|
||||
|
|
@ -107,8 +109,8 @@ Expected immediately after install:
|
|||
- `openbao-0` is Running.
|
||||
- `openbao`, `openbao-active`, `openbao-internal`, and `openbao-ui` services
|
||||
exist as cluster-internal services.
|
||||
- After DNS points at the cluster ingress, `https://bao.coulomb.social` serves
|
||||
the OpenBao UI over valid TLS.
|
||||
- With `openbao-ui-railiance01` healthy,
|
||||
`http://127.0.0.1:18200` serves the operator UI.
|
||||
- data and audit PVCs are Bound.
|
||||
- `bao status` reports `Initialized: false` and `Sealed: true`.
|
||||
|
||||
|
|
@ -327,17 +329,17 @@ Initial auth model:
|
|||
|
||||
### Browser UI Login
|
||||
|
||||
The browser operator surface is:
|
||||
The browser operator surface is the named local tunnel:
|
||||
|
||||
```text
|
||||
https://bao.coulomb.social
|
||||
http://127.0.0.1:18200
|
||||
```
|
||||
|
||||
Operators see a streamlined **Sign in with KeyCape** mask. The raw OpenBao
|
||||
fields (namespace, method, mount path, role) are hidden presets applied by the
|
||||
UI overlay in `helm/openbao-ui-overlay/`. Public ingress targets the
|
||||
`openbao-ui-gateway` nginx proxy, which injects overlay assets and forwards to
|
||||
the OpenBao service.
|
||||
Run `bridge up openbao-ui-railiance01` first. Operators then see a streamlined
|
||||
**Sign in with KeyCape** mask. The raw OpenBao fields (namespace, method, mount
|
||||
path, role) are hidden presets applied by the UI overlay in
|
||||
`helm/openbao-ui-overlay/`. The ClusterIP-only `openbao-ui-gateway` nginx proxy
|
||||
injects overlay assets and forwards to the OpenBao service.
|
||||
|
||||
Hidden defaults (also in `helm/openbao-ui-overlay/presets.json`):
|
||||
|
||||
|
|
@ -378,10 +380,10 @@ standalone page that exchanges the authorization code, stores the UI session
|
|||
token, and redirects into the Ember app (no popup/`window.opener` flow).
|
||||
|
||||
The OpenBao UI redirects the browser to KeyCape at `kc.coulomb.social`, then
|
||||
returns to:
|
||||
returns through the same local tunnel to:
|
||||
|
||||
```text
|
||||
https://bao.coulomb.social/ui/vault/auth/netkingdom/oidc/callback
|
||||
http://127.0.0.1:18200/ui/vault/auth/netkingdom/oidc/callback
|
||||
```
|
||||
|
||||
The legacy `keycape` mount remains a compatibility alias for existing
|
||||
|
|
@ -394,7 +396,7 @@ The browser callback URI must be present in both:
|
|||
- OpenBao `auth/netkingdom/role/platform-admin` `allowed_redirect_uris`.
|
||||
|
||||
If the compatibility alias is kept enabled, also keep
|
||||
`https://bao.coulomb.social/ui/vault/auth/keycape/oidc/callback` in the
|
||||
`http://127.0.0.1:18200/ui/vault/auth/keycape/oidc/callback` in the
|
||||
KeyCape client and `auth/keycape/role/platform-admin`.
|
||||
|
||||
Use the browser UI for metadata inspection and attended operator workflows.
|
||||
|
|
|
|||
33
helm/openbao-public-ingress.rollback.yaml
Normal file
33
helm/openbao-public-ingress.rollback.yaml
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# Rollback-only public listener for an attended RMASTER-WP-0020-T09 reversal.
|
||||
# This file is never applied by the ordinary platform deployment path.
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: openbao-ui-gateway
|
||||
namespace: openbao
|
||||
labels:
|
||||
app.kubernetes.io/name: openbao-ui-gateway
|
||||
app.kubernetes.io/part-of: railiance-platform
|
||||
railiance-platform/component: secrets
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||
traefik.ingress.kubernetes.io/router.middlewares: >-
|
||||
openbao-openbao-rate-limit@kubernetescrd,
|
||||
openbao-openbao-hsts@kubernetescrd
|
||||
spec:
|
||||
ingressClassName: traefik
|
||||
tls:
|
||||
- secretName: bao-tls
|
||||
hosts:
|
||||
- bao.coulomb.social
|
||||
rules:
|
||||
- host: bao.coulomb.social
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: openbao-ui-gateway
|
||||
port:
|
||||
number: 8080
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
# OpenBao browser UI gateway — injects the KeyCape login overlay and proxies
|
||||
# to the OpenBao service. Public ingress for bao.coulomb.social targets this
|
||||
# gateway instead of the chart-managed OpenBao ingress.
|
||||
# to the OpenBao service. The gateway is ClusterIP-only and is reached through
|
||||
# the named openbao-ui-railiance01 operator tunnel.
|
||||
#
|
||||
# ConfigMap data is applied by scripts/openbao-ui-overlay-apply.sh from
|
||||
# helm/openbao-ui-overlay/*.
|
||||
|
|
@ -87,35 +87,3 @@ spec:
|
|||
port: 8080
|
||||
targetPort: http
|
||||
protocol: TCP
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: openbao-ui-gateway
|
||||
namespace: openbao
|
||||
labels:
|
||||
app.kubernetes.io/name: openbao-ui-gateway
|
||||
app.kubernetes.io/part-of: railiance-platform
|
||||
railiance-platform/component: secrets
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||
traefik.ingress.kubernetes.io/router.middlewares: >-
|
||||
openbao-openbao-rate-limit@kubernetescrd,
|
||||
openbao-openbao-hsts@kubernetescrd
|
||||
spec:
|
||||
ingressClassName: traefik
|
||||
tls:
|
||||
- secretName: bao-tls
|
||||
hosts:
|
||||
- bao.coulomb.social
|
||||
rules:
|
||||
- host: bao.coulomb.social
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: openbao-ui-gateway
|
||||
port:
|
||||
number: 8080
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
# OpenBao KeyCape login overlay
|
||||
|
||||
Streamlines the browser login mask at `https://bao.coulomb.social` to a single
|
||||
**Sign in with KeyCape** action. Namespace, auth method, mount path, and role
|
||||
are preset in `presets.json` and hidden by `overlay.css` / `overlay.js`.
|
||||
Streamlines the browser login mask reached through the named
|
||||
`openbao-ui-railiance01` operator tunnel at `http://127.0.0.1:18200` to a
|
||||
single **Sign in with KeyCape** action. Namespace, auth method, mount path, and
|
||||
role are preset in `presets.json` and hidden by `overlay.css` / `overlay.js`.
|
||||
|
||||
## Mechanism (T01 decision)
|
||||
|
||||
|
|
@ -54,9 +55,9 @@ make openbao-verify-login-overlay
|
|||
3. Fetch live UI assets and compare hashes:
|
||||
|
||||
```bash
|
||||
curl -sS https://bao.coulomb.social/ui/ -o /tmp/index.html
|
||||
curl -sS http://127.0.0.1:18200/ui/ -o /tmp/index.html
|
||||
# locate vault-*.js path in /tmp/index.html, then:
|
||||
curl -sS "https://bao.coulomb.social/ui/assets/vault-....js" -o /tmp/vault.js
|
||||
curl -sS "http://127.0.0.1:18200/ui/assets/vault-....js" -o /tmp/vault.js
|
||||
sha256sum /tmp/index.html /tmp/vault.js
|
||||
```
|
||||
|
||||
|
|
@ -64,6 +65,9 @@ make openbao-verify-login-overlay
|
|||
`overlay.css` / `overlay.js` selectors against the new Ember templates.
|
||||
5. Write `patches/<new-version>/manifest.sha256`, update `VERSION`.
|
||||
6. Run `make openbao-verify-login-overlay CHECK_UPSTREAM_DRIFT=1`.
|
||||
7. Attended browser login through KeyCape MFA.
|
||||
7. Attended browser login through KeyCape MFA. The OpenBao role and KeyCape
|
||||
client must both allow the exact loopback callback
|
||||
`http://127.0.0.1:18200/ui/vault/auth/netkingdom/oidc/callback` before the
|
||||
former public listener is retracted.
|
||||
|
||||
Workplan: `helix-forge/workplans/HF-WP-0003-openbao-keycape-login-overlay.md`
|
||||
Workplan: `helix-forge/workplans/HF-WP-0003-openbao-keycape-login-overlay.md`
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
# OpenBao UI asset fingerprints for image tag 2.5.4.
|
||||
# Regenerate after an OpenBao image bump when login markup drifts.
|
||||
# Compare vault.js only — index.html is intentionally modified by the gateway.
|
||||
# curl -sS https://bao.coulomb.social/ui/ -o /tmp/index.html
|
||||
# curl -sS http://127.0.0.1:18200/ui/ -o /tmp/index.html
|
||||
# vault_path=$(rg -o '/ui/assets/vault-[a-f0-9]+\\.js' /tmp/index.html | head -1)
|
||||
# curl -sS "https://bao.coulomb.social${vault_path}" -o /tmp/vault.js
|
||||
# curl -sS "http://127.0.0.1:18200${vault_path}" -o /tmp/vault.js
|
||||
# sha256sum /tmp/vault.js
|
||||
f0214b5be89377395f8d6521c34139877529bd95ba703901c78b527ab0f1c231 ui/assets/vault-bae6b876038fbf475728f993b5a62002.js
|
||||
f0214b5be89377395f8d6521c34139877529bd95ba703901c78b527ab0f1c231 ui/assets/vault-bae6b876038fbf475728f993b5a62002.js
|
||||
|
|
|
|||
153
scripts/openbao-public-listener-transition.sh
Executable file
153
scripts/openbao-public-listener-transition.sh
Executable file
|
|
@ -0,0 +1,153 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ACTION="${1:-status}"
|
||||
OPENBAO_NAMESPACE="${OPENBAO_NAMESPACE:-openbao}"
|
||||
KUBECTL="${KUBECTL:-kubectl}"
|
||||
INGRESS_NAME="${OPENBAO_PUBLIC_INGRESS_NAME:-openbao-ui-gateway}"
|
||||
SERVICE_NAME="${OPENBAO_UI_SERVICE_NAME:-openbao-ui-gateway}"
|
||||
DEPLOYMENT_NAME="${OPENBAO_UI_DEPLOYMENT_NAME:-openbao-ui-gateway}"
|
||||
TUNNEL_NAME="${OPENBAO_UI_TUNNEL_NAME:-openbao-ui-railiance01}"
|
||||
TUNNEL_URL="${OPENBAO_UI_BASE_URL:-http://127.0.0.1:18200}"
|
||||
PUBLIC_URL="${OPENBAO_PUBLIC_URL:-https://bao.coulomb.social/}"
|
||||
EXPECTED_CLUSTER_UID="${RAILIANCE01_CLUSTER_UID:-a553c742-0115-43d4-99a4-a5ca56fe0786}"
|
||||
PRIVATE_MANIFEST="${OPENBAO_UI_OVERLAY_K8S:-helm/openbao-ui-overlay-k8s.yaml}"
|
||||
ROLLBACK_MANIFEST="${OPENBAO_PUBLIC_INGRESS_ROLLBACK:-helm/openbao-public-ingress.rollback.yaml}"
|
||||
MIDDLEWARE_MANIFEST="${OPENBAO_MIDDLEWARE:-helm/openbao-middleware.yaml}"
|
||||
CONFIRM_RETRACT="RETRACT RMASTER-WP-0020-T09 PUBLIC OPENBAO LISTENER"
|
||||
CONFIRM_ROLLBACK="ROLLBACK RMASTER-WP-0020-T09 PUBLIC OPENBAO LISTENER"
|
||||
|
||||
usage() {
|
||||
cat <<'USAGE'
|
||||
Usage: scripts/openbao-public-listener-transition.sh status|dry-run|preflight|apply|rollback
|
||||
|
||||
`apply` requires both:
|
||||
OPENBAO_OPERATOR_LOGIN_VERIFIED=true
|
||||
OPENBAO_PUBLIC_LISTENER_CONFIRM='RETRACT RMASTER-WP-0020-T09 PUBLIC OPENBAO LISTENER'
|
||||
|
||||
`rollback` requires:
|
||||
OPENBAO_PUBLIC_LISTENER_CONFIRM='ROLLBACK RMASTER-WP-0020-T09 PUBLIC OPENBAO LISTENER'
|
||||
|
||||
The script never reads OpenBao credentials or secret values.
|
||||
USAGE
|
||||
}
|
||||
|
||||
fail() { printf '[ERR] %s\n' "$*" >&2; exit 1; }
|
||||
ok() { printf '[OK] %s\n' "$*"; }
|
||||
|
||||
kube() {
|
||||
# KUBECTL may include an explicit --kubeconfig argument from Make.
|
||||
# shellcheck disable=SC2086
|
||||
$KUBECTL "$@"
|
||||
}
|
||||
|
||||
require_cluster() {
|
||||
local observed
|
||||
observed="$(kube get namespace kube-system -o jsonpath='{.metadata.uid}')"
|
||||
[ "$observed" = "$EXPECTED_CLUSTER_UID" ] ||
|
||||
fail "cluster identity mismatch: expected $EXPECTED_CLUSTER_UID, observed $observed"
|
||||
ok "railiance01 cluster identity pinned"
|
||||
}
|
||||
|
||||
require_private_source() {
|
||||
[ -f "$PRIVATE_MANIFEST" ] || fail "missing private gateway manifest: $PRIVATE_MANIFEST"
|
||||
if grep -Eq '^kind:[[:space:]]*Ingress[[:space:]]*$' "$PRIVATE_MANIFEST"; then
|
||||
fail "ordinary gateway manifest still contains an Ingress"
|
||||
fi
|
||||
ok "ordinary gateway manifest is Ingress-free"
|
||||
}
|
||||
|
||||
require_private_runtime() {
|
||||
local service_type service_port
|
||||
service_type="$(kube -n "$OPENBAO_NAMESPACE" get service "$SERVICE_NAME" -o jsonpath='{.spec.type}')"
|
||||
service_port="$(kube -n "$OPENBAO_NAMESPACE" get service "$SERVICE_NAME" -o jsonpath='{.spec.ports[?(@.name=="http")].port}')"
|
||||
[ "$service_type" = "ClusterIP" ] || fail "gateway Service type is $service_type, not ClusterIP"
|
||||
[ "$service_port" = "8080" ] || fail "gateway Service http port is $service_port, not 8080"
|
||||
kube -n "$OPENBAO_NAMESPACE" rollout status "deployment/$DEPLOYMENT_NAME" --timeout=30s >/dev/null
|
||||
ok "gateway Deployment Ready behind ClusterIP"
|
||||
}
|
||||
|
||||
require_tunnel() {
|
||||
command -v bridge >/dev/null || fail "bridge CLI not found"
|
||||
command -v jq >/dev/null || fail "jq not found"
|
||||
local report
|
||||
if ! report="$(bridge check "$TUNNEL_NAME" --json 2>/dev/null)"; then
|
||||
fail "named tunnel $TUNNEL_NAME is not lifecycle-healthy"
|
||||
fi
|
||||
jq -e --arg name "$TUNNEL_NAME" '.[] | select(.tunnel == $name and .ok == true)' \
|
||||
<<<"$report" >/dev/null || fail "named tunnel $TUNNEL_NAME did not report ok=true"
|
||||
curl -fsS --max-time 10 "$TUNNEL_URL/ui/platform-overlay/presets.json" >/dev/null ||
|
||||
fail "operator UI is not reachable through $TUNNEL_NAME"
|
||||
ok "named operator tunnel is healthy and reaches the UI"
|
||||
}
|
||||
|
||||
status() {
|
||||
require_cluster
|
||||
require_private_source
|
||||
require_private_runtime
|
||||
if kube -n "$OPENBAO_NAMESPACE" get ingress "$INGRESS_NAME" >/dev/null 2>&1; then
|
||||
printf '[INFO] public Ingress is present\n'
|
||||
else
|
||||
printf '[INFO] public Ingress is absent\n'
|
||||
fi
|
||||
require_tunnel
|
||||
}
|
||||
|
||||
dry_run() {
|
||||
require_cluster
|
||||
require_private_source
|
||||
[ -f "$ROLLBACK_MANIFEST" ] || fail "missing rollback manifest: $ROLLBACK_MANIFEST"
|
||||
kube apply --server-side --dry-run=server -f "$PRIVATE_MANIFEST" >/dev/null
|
||||
kube apply --server-side --dry-run=server -f "$ROLLBACK_MANIFEST" >/dev/null
|
||||
kube -n "$OPENBAO_NAMESPACE" delete ingress "$INGRESS_NAME" --dry-run=server >/dev/null
|
||||
ok "private apply, rollback apply, and Ingress deletion pass server dry-run"
|
||||
}
|
||||
|
||||
preflight() {
|
||||
status
|
||||
kube -n "$OPENBAO_NAMESPACE" get ingress "$INGRESS_NAME" >/dev/null 2>&1 ||
|
||||
fail "public Ingress is already absent; use status"
|
||||
ok "public Ingress is present for attended transition"
|
||||
}
|
||||
|
||||
apply_retraction() {
|
||||
[ "${OPENBAO_OPERATOR_LOGIN_VERIFIED:-false}" = "true" ] ||
|
||||
fail "attended operator login has not been explicitly verified"
|
||||
[ "${OPENBAO_PUBLIC_LISTENER_CONFIRM:-}" = "$CONFIRM_RETRACT" ] ||
|
||||
fail "exact retraction confirmation is missing"
|
||||
preflight
|
||||
kube -n "$OPENBAO_NAMESPACE" delete ingress "$INGRESS_NAME" --wait=true
|
||||
require_private_runtime
|
||||
require_tunnel
|
||||
if kube -n "$OPENBAO_NAMESPACE" get ingress "$INGRESS_NAME" >/dev/null 2>&1; then
|
||||
fail "public Ingress still exists after deletion"
|
||||
fi
|
||||
local code
|
||||
code="$(curl -kLsS --max-time 10 -o /dev/null -w '%{http_code}' "$PUBLIC_URL" || true)"
|
||||
case "$code" in
|
||||
2??|3??) fail "public hostname still returns successful HTTP status $code" ;;
|
||||
esac
|
||||
ok "public Ingress absent; private gateway and named tunnel remain healthy"
|
||||
}
|
||||
|
||||
rollback() {
|
||||
[ "${OPENBAO_PUBLIC_LISTENER_CONFIRM:-}" = "$CONFIRM_ROLLBACK" ] ||
|
||||
fail "exact rollback confirmation is missing"
|
||||
require_cluster
|
||||
[ -f "$MIDDLEWARE_MANIFEST" ] || fail "missing middleware manifest: $MIDDLEWARE_MANIFEST"
|
||||
[ -f "$ROLLBACK_MANIFEST" ] || fail "missing rollback manifest: $ROLLBACK_MANIFEST"
|
||||
kube apply -f "$MIDDLEWARE_MANIFEST"
|
||||
kube apply -f "$ROLLBACK_MANIFEST"
|
||||
kube -n "$OPENBAO_NAMESPACE" get ingress "$INGRESS_NAME" >/dev/null
|
||||
ok "prior public Ingress restored from rollback-only manifest"
|
||||
}
|
||||
|
||||
case "$ACTION" in
|
||||
status) status ;;
|
||||
dry-run) dry_run ;;
|
||||
preflight) preflight ;;
|
||||
apply) apply_retraction ;;
|
||||
rollback) rollback ;;
|
||||
-h|--help|help) usage ;;
|
||||
*) usage >&2; exit 2 ;;
|
||||
esac
|
||||
|
|
@ -11,8 +11,9 @@ usage() {
|
|||
cat <<'USAGE'
|
||||
Usage: scripts/openbao-ui-overlay-apply.sh
|
||||
|
||||
Builds and applies the OpenBao KeyCape login overlay ConfigMaps and gateway
|
||||
Deployment/Service/Ingress. Idempotent — safe to run on every openbao-deploy.
|
||||
Builds and applies the OpenBao KeyCape login overlay ConfigMaps and private
|
||||
gateway Deployment/ClusterIP Service. Idempotent — safe on every deploy. It
|
||||
does not create or delete a public Ingress.
|
||||
|
||||
Environment:
|
||||
OPENBAO_NAMESPACE Kubernetes namespace. Default: openbao
|
||||
|
|
@ -71,4 +72,4 @@ $KUBECTL rollout restart deployment/openbao-ui-gateway -n "$OPENBAO_NAMESPACE"
|
|||
# shellcheck disable=SC2086
|
||||
$KUBECTL rollout status deployment/openbao-ui-gateway -n "$OPENBAO_NAMESPACE" --timeout=120s
|
||||
|
||||
printf '[OK] OpenBao UI overlay applied from %s\n' "$OVERLAY_DIR"
|
||||
printf '[OK] OpenBao UI overlay applied from %s\n' "$OVERLAY_DIR"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
BASE_URL="${OPENBAO_UI_BASE_URL:-https://bao.coulomb.social}"
|
||||
BASE_URL="${OPENBAO_UI_BASE_URL:-http://127.0.0.1:18200}"
|
||||
OVERLAY_DIR="${OPENBAO_UI_OVERLAY_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/helm/openbao-ui-overlay}"
|
||||
CHECK_DRIFT="${CHECK_UPSTREAM_DRIFT:-0}"
|
||||
|
||||
|
|
@ -13,11 +13,11 @@ usage() {
|
|||
cat <<'USAGE'
|
||||
Usage: scripts/openbao-verify-login-overlay.sh [--check-upstream-drift]
|
||||
|
||||
Verifies the public OpenBao UI serves the KeyCape login overlay assets and
|
||||
that index.html injection is present.
|
||||
Verifies the operator-tunneled OpenBao UI serves the KeyCape login overlay
|
||||
assets and that index.html injection is present.
|
||||
|
||||
Environment:
|
||||
OPENBAO_UI_BASE_URL Public UI base URL. Default: https://bao.coulomb.social
|
||||
OPENBAO_UI_BASE_URL Operator UI base URL. Default: http://127.0.0.1:18200
|
||||
OPENBAO_UI_OVERLAY_DIR Local overlay directory for drift fingerprints
|
||||
CHECK_UPSTREAM_DRIFT Set to 1 to compare live UI hashes with patches/
|
||||
USAGE
|
||||
|
|
@ -170,4 +170,4 @@ if [ "$CHECK_DRIFT" = "1" ]; then
|
|||
ok "vault bundle hash matches patches/$version/manifest.sha256 (${expected_vault_path:-$vault_asset})"
|
||||
fi
|
||||
|
||||
printf '\nOpenBao login overlay verification passed for %s\n' "$BASE_URL"
|
||||
printf '\nOpenBao login overlay verification passed for %s\n' "$BASE_URL"
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ type: workplan
|
|||
title: "Contain attended OpenBao login output before authentication"
|
||||
domain: financials
|
||||
repo: railiance-platform
|
||||
status: ready
|
||||
status: finished
|
||||
owner: codex
|
||||
topic_slug: railiance
|
||||
created: "2026-08-23"
|
||||
|
|
@ -50,7 +50,7 @@ seal/unseal ceremony, or reuse of the terminal drill scenario.
|
|||
|
||||
```task
|
||||
id: RAILIANCE-WP-0026-T01
|
||||
status: todo
|
||||
status: done
|
||||
priority: high
|
||||
```
|
||||
|
||||
|
|
@ -90,10 +90,19 @@ both reviewers, and `railiance-platform` accepts the exact revision. Completion
|
|||
of this task still does not authorize a live drill; that requires fresh owner
|
||||
receipts, current preflight, and a new human decision under a new scenario id.
|
||||
|
||||
Completed 2026-08-23. Railiance Platform accepts ops-warden revision
|
||||
`0fae0904ce8d8694338dd53a8a79abec5fec788d`. The committed owner receipt at
|
||||
ops-warden revision `bc1966da825fbeaa4d83947a4f942b5d5f085408` has SHA-256
|
||||
`d2ba444ed16989590325697e69d25283dc75a9432c29a72e627e80bf9fd987e4`,
|
||||
reports 42 focused tests and 390 repository tests passing, and records all
|
||||
seven containment outcomes without sensitive material. Independent
|
||||
railiance-infra review at revision `186b030` approves the same implementation.
|
||||
This is source acceptance only and authorizes no live OIDC or drill.
|
||||
|
||||
## Acceptance
|
||||
|
||||
- [ ] The adapter refuses unsafe storage before OIDC.
|
||||
- [ ] No underlying login output can reach the agent transcript.
|
||||
- [ ] Any issued credential is contained and revoked on failure.
|
||||
- [ ] Focused success and failure-path tests pass without credential material.
|
||||
- [ ] Both direct reviewers receive and accept the value-safe fix receipt.
|
||||
- [x] The adapter refuses unsafe storage before OIDC.
|
||||
- [x] No underlying login output can reach the agent transcript.
|
||||
- [x] Any issued credential is contained and revoked on failure.
|
||||
- [x] Focused success and failure-path tests pass without credential material.
|
||||
- [x] Both direct reviewers receive and accept the value-safe fix receipt.
|
||||
|
|
|
|||
66
workplans/RAILIANCE-WP-0027-openbao-operator-only-access.md
Normal file
66
workplans/RAILIANCE-WP-0027-openbao-operator-only-access.md
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
---
|
||||
id: RAILIANCE-WP-0027
|
||||
type: workplan
|
||||
title: "Retract public OpenBao listener behind operator-only access"
|
||||
domain: financials
|
||||
repo: railiance-platform
|
||||
status: blocked
|
||||
owner: codex
|
||||
topic_slug: railiance
|
||||
created: "2026-08-23"
|
||||
updated: "2026-08-23"
|
||||
related:
|
||||
- RMASTER-WP-0020-T09
|
||||
- RAPP-OPENBAO-WP-0002
|
||||
---
|
||||
|
||||
# RAILIANCE-WP-0027 — OpenBao operator-only access
|
||||
|
||||
## Goal
|
||||
|
||||
Implement the S3-owner half of RMASTER-WP-0020-T09 without coupling it to
|
||||
destructive CoulombCore cleanup.
|
||||
|
||||
## T01 — Align the retained compatibility source
|
||||
|
||||
```task
|
||||
id: RAILIANCE-WP-0027-T01
|
||||
status: done
|
||||
priority: high
|
||||
```
|
||||
|
||||
The retained platform manifest now matches the canonical package posture:
|
||||
Deployment plus ClusterIP Service only. Ordinary deploy no longer applies the
|
||||
public-only middleware. The old Ingress remains solely in an explicitly named
|
||||
rollback artifact.
|
||||
|
||||
## T02 — Add guarded retraction and rollback
|
||||
|
||||
```task
|
||||
id: RAILIANCE-WP-0027-T02
|
||||
status: done
|
||||
priority: high
|
||||
```
|
||||
|
||||
`scripts/openbao-public-listener-transition.sh` pins the cluster UID, verifies
|
||||
source and runtime packet posture, requires a lifecycle-healthy named tunnel,
|
||||
and gates live deletion on exact confirmation plus attended-login verification.
|
||||
It deletes only the Ingress and provides an exact rollback path.
|
||||
|
||||
## T03 — Complete the attended operator cutover
|
||||
|
||||
```task
|
||||
id: RAILIANCE-WP-0027-T03
|
||||
status: wait
|
||||
priority: high
|
||||
```
|
||||
|
||||
Blocked on two facts: KeyCape and the OpenBao role must accept the exact
|
||||
loopback callback, and an attended MFA login must pass. The host-namespace
|
||||
preflight already proves `openbao-ui-railiance01` lifecycle-healthy and reaches
|
||||
the expected overlay. Then execute the guarded retraction, coordinate public
|
||||
DNS withdrawal with railiance-infra, and return non-secret acceptance evidence
|
||||
to Railiance Master.
|
||||
|
||||
This workplan authorizes no OpenBao seal/unseal, policy broadening, PVC or
|
||||
Secret mutation, reboot, restore, or RMASTER-WP-0020-T08 cleanup.
|
||||
Loading…
Add table
Add a link
Reference in a new issue