Add KeyCape login overlay gateway for OpenBao browser UI
Streamline bao.coulomb.social login as "Sign in with KeyCape" via a versioned nginx gateway that injects overlay assets and proxies to OpenBao. Disable chart ingress in favor of the overlay ingress, wire make openbao-deploy, and add openbao-verify-login-overlay with upstream drift detection.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,6 +4,7 @@ helm/*.yaml
|
|||||||
!helm/*.yaml.template
|
!helm/*.yaml.template
|
||||||
!helm/openbao-values.yaml
|
!helm/openbao-values.yaml
|
||||||
!helm/openbao-middleware.yaml
|
!helm/openbao-middleware.yaml
|
||||||
|
!helm/openbao-ui-overlay-k8s.yaml
|
||||||
# Kubernetes manifests (no secrets) are safe to commit
|
# Kubernetes manifests (no secrets) are safe to commit
|
||||||
!helm/*-cluster.yaml
|
!helm/*-cluster.yaml
|
||||||
!helm/*-networkpolicies.yaml
|
!helm/*-networkpolicies.yaml
|
||||||
|
|||||||
39
Makefile
39
Makefile
@@ -14,9 +14,14 @@ OPENBAO_NAMESPACE ?= openbao
|
|||||||
OPENBAO_RELEASE ?= openbao
|
OPENBAO_RELEASE ?= openbao
|
||||||
OPENBAO_VALUES ?= helm/openbao-values.yaml
|
OPENBAO_VALUES ?= helm/openbao-values.yaml
|
||||||
OPENBAO_MIDDLEWARE ?= helm/openbao-middleware.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_VERIFY_AUTH_ARGS ?=
|
OPENBAO_VERIFY_AUTH_ARGS ?=
|
||||||
OPENBAO_RESTORE_EVIDENCE ?= /tmp/netkingdom-openbao-restore-drill/evidence.json
|
OPENBAO_RESTORE_EVIDENCE ?= /tmp/netkingdom-openbao-restore-drill/evidence.json
|
||||||
OPENBAO_EMERGENCY_EVIDENCE ?= /tmp/netkingdom-openbao-emergency-drill/evidence.json
|
OPENBAO_EMERGENCY_EVIDENCE ?= /tmp/netkingdom-openbao-emergency-drill/evidence.json
|
||||||
|
ARGOCD_NAMESPACE ?= argocd
|
||||||
|
ARGOCD_BOOTSTRAP_DIR ?= argocd/bootstrap
|
||||||
|
ARGOCD_REPOSITORY_SECRET ?=
|
||||||
|
|
||||||
##@ CloudNative PG (cnpg) — primary database operator
|
##@ CloudNative PG (cnpg) — primary database operator
|
||||||
|
|
||||||
@@ -103,6 +108,16 @@ openbao-dry-run: openbao-repo ## Render the OpenBao Helm release without applyin
|
|||||||
-f $(OPENBAO_VALUES) \
|
-f $(OPENBAO_VALUES) \
|
||||||
--dry-run
|
--dry-run
|
||||||
|
|
||||||
|
openbao-overlay-apply: ## Apply KeyCape login overlay gateway and assets
|
||||||
|
OPENBAO_UI_OVERLAY_DIR=$(OPENBAO_UI_OVERLAY_DIR) \
|
||||||
|
OPENBAO_UI_OVERLAY_K8S=$(OPENBAO_UI_OVERLAY_K8S) \
|
||||||
|
KUBECTL='$(KUBECTL)' OPENBAO_NAMESPACE=$(OPENBAO_NAMESPACE) \
|
||||||
|
scripts/openbao-ui-overlay-apply.sh
|
||||||
|
|
||||||
|
openbao-verify-login-overlay: ## Verify public KeyCape login overlay is active
|
||||||
|
OPENBAO_UI_OVERLAY_DIR=$(OPENBAO_UI_OVERLAY_DIR) \
|
||||||
|
scripts/openbao-verify-login-overlay.sh $(OPENBAO_VERIFY_LOGIN_OVERLAY_ARGS)
|
||||||
|
|
||||||
openbao-deploy: openbao-repo ## Deploy / upgrade OpenBao to the openbao namespace
|
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) create namespace $(OPENBAO_NAMESPACE) --dry-run=client -o yaml | $(KUBECTL) apply -f -
|
||||||
$(KUBECTL) apply -f $(OPENBAO_MIDDLEWARE)
|
$(KUBECTL) apply -f $(OPENBAO_MIDDLEWARE)
|
||||||
@@ -111,6 +126,7 @@ openbao-deploy: openbao-repo ## Deploy / upgrade OpenBao to the openbao namespac
|
|||||||
--namespace $(OPENBAO_NAMESPACE) \
|
--namespace $(OPENBAO_NAMESPACE) \
|
||||||
-f $(OPENBAO_VALUES) \
|
-f $(OPENBAO_VALUES) \
|
||||||
--wait --timeout 5m
|
--wait --timeout 5m
|
||||||
|
$(MAKE) openbao-overlay-apply
|
||||||
|
|
||||||
openbao-status: ## Show OpenBao pods, services, PVCs, and seal/init status
|
openbao-status: ## Show OpenBao pods, services, PVCs, and seal/init status
|
||||||
$(KUBECTL) get pods,svc,pvc -n $(OPENBAO_NAMESPACE) \
|
$(KUBECTL) get pods,svc,pvc -n $(OPENBAO_NAMESPACE) \
|
||||||
@@ -149,6 +165,27 @@ openbao-validate-emergency-evidence: ## Validate non-secret OpenBao emergency se
|
|||||||
OPENBAO_EMERGENCY_EVIDENCE='$(OPENBAO_EMERGENCY_EVIDENCE)' \
|
OPENBAO_EMERGENCY_EVIDENCE='$(OPENBAO_EMERGENCY_EVIDENCE)' \
|
||||||
scripts/openbao-validate-emergency-drill-evidence.sh
|
scripts/openbao-validate-emergency-drill-evidence.sh
|
||||||
|
|
||||||
|
##@ ArgoCD GitOps bootstrap
|
||||||
|
|
||||||
|
argocd-bootstrap-dry-run: ## Server-side dry-run ArgoCD AppProjects and root Application
|
||||||
|
$(KUBECTL) apply --dry-run=server -k $(ARGOCD_BOOTSTRAP_DIR)
|
||||||
|
|
||||||
|
argocd-bootstrap-deploy: ## Apply ArgoCD AppProjects and root Application
|
||||||
|
$(KUBECTL) apply -k $(ARGOCD_BOOTSTRAP_DIR)
|
||||||
|
|
||||||
|
argocd-repo-apply: ## Apply a SOPS-encrypted ArgoCD repository Secret (set ARGOCD_REPOSITORY_SECRET)
|
||||||
|
@test -n "$(ARGOCD_REPOSITORY_SECRET)" || \
|
||||||
|
(echo "ERROR: set ARGOCD_REPOSITORY_SECRET=argocd/repositories/<repo>.repository.sops.yaml"; exit 1)
|
||||||
|
sops -d $(ARGOCD_REPOSITORY_SECRET) | $(KUBECTL) apply -f -
|
||||||
|
|
||||||
|
argocd-status: ## Show Railiance ArgoCD projects, root app, and registered repos
|
||||||
|
$(KUBECTL) get appprojects.argoproj.io -n $(ARGOCD_NAMESPACE) \
|
||||||
|
railiance-bootstrap railiance-tenants
|
||||||
|
$(KUBECTL) get applications.argoproj.io -n $(ARGOCD_NAMESPACE) \
|
||||||
|
railiance-apps-root
|
||||||
|
$(KUBECTL) get secrets -n $(ARGOCD_NAMESPACE) \
|
||||||
|
-l argocd.argoproj.io/secret-type=repository
|
||||||
|
|
||||||
##@ Backup
|
##@ Backup
|
||||||
|
|
||||||
backup: ## Backup platform services (PostgreSQL logical dump) — age-encrypted to Nextcloud
|
backup: ## Backup platform services (PostgreSQL logical dump) — age-encrypted to Nextcloud
|
||||||
@@ -161,4 +198,4 @@ help: ## Show this help
|
|||||||
/^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-22s\033[0m %s\n", $$1, $$2 } \
|
/^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-22s\033[0m %s\n", $$1, $$2 } \
|
||||||
/^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) }' $(MAKEFILE_LIST)
|
/^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) }' $(MAKEFILE_LIST)
|
||||||
|
|
||||||
.PHONY: db-deploy db-status db-shell db-logs apps-pg-deploy apps-pg-status apps-pg-shell apps-pg-logs net-kingdom-pg-inter-hub-networkpolicy-deploy pg-deploy pg-status pg-pgpool-check valkey-deploy valkey-status openbao-repo openbao-dry-run openbao-deploy openbao-status openbao-verify openbao-verify-post-unseal openbao-configure-initial openbao-configure-ssh openbao-verify-ssh openbao-verify-authenticated openbao-validate-restore-evidence openbao-validate-emergency-evidence backup help
|
.PHONY: db-deploy db-status db-shell db-logs apps-pg-deploy apps-pg-status apps-pg-shell apps-pg-logs net-kingdom-pg-inter-hub-networkpolicy-deploy pg-deploy pg-status pg-pgpool-check valkey-deploy valkey-status openbao-repo openbao-dry-run openbao-overlay-apply openbao-verify-login-overlay openbao-deploy openbao-status openbao-verify openbao-verify-post-unseal openbao-configure-initial openbao-configure-ssh openbao-verify-ssh openbao-verify-authenticated openbao-validate-restore-evidence openbao-validate-emergency-evidence argocd-bootstrap-dry-run argocd-bootstrap-deploy argocd-repo-apply argocd-status backup help
|
||||||
|
|||||||
@@ -52,9 +52,11 @@ make openbao-deploy
|
|||||||
make openbao-status
|
make openbao-status
|
||||||
```
|
```
|
||||||
|
|
||||||
`make openbao-deploy` also applies `helm/openbao-middleware.yaml`, which
|
`make openbao-deploy` applies `helm/openbao-middleware.yaml` (Traefik
|
||||||
defines the Traefik rate-limit and HSTS middlewares referenced by the OpenBao
|
rate-limit and HSTS), upgrades the OpenBao Helm release, then applies the
|
||||||
Ingress.
|
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`).
|
||||||
|
|
||||||
On Railiance01 directly:
|
On Railiance01 directly:
|
||||||
|
|
||||||
@@ -300,7 +302,13 @@ The browser operator surface is:
|
|||||||
https://bao.coulomb.social
|
https://bao.coulomb.social
|
||||||
```
|
```
|
||||||
|
|
||||||
Use the KeyCape-backed auth method:
|
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.
|
||||||
|
|
||||||
|
Hidden defaults (also in `helm/openbao-ui-overlay/presets.json`):
|
||||||
|
|
||||||
```text
|
```text
|
||||||
method: OIDC
|
method: OIDC
|
||||||
@@ -309,6 +317,19 @@ mount path: netkingdom
|
|||||||
role: platform-admin
|
role: platform-admin
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Deploy or refresh the overlay:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make openbao-overlay-apply
|
||||||
|
make openbao-verify-login-overlay
|
||||||
|
make openbao-verify-login-overlay OPENBAO_VERIFY_LOGIN_OVERLAY_ARGS=--check-upstream-drift
|
||||||
|
```
|
||||||
|
|
||||||
|
After an OpenBao image or chart upgrade, follow
|
||||||
|
`helm/openbao-ui-overlay/README.md` to refresh overlay selectors and
|
||||||
|
`patches/<version>/manifest.sha256` fingerprints if upstream login markup
|
||||||
|
changed.
|
||||||
|
|
||||||
The OpenBao UI redirects the browser to KeyCape at `kc.coulomb.social`, then
|
The OpenBao UI redirects the browser to KeyCape at `kc.coulomb.social`, then
|
||||||
returns to:
|
returns to:
|
||||||
|
|
||||||
|
|||||||
121
helm/openbao-ui-overlay-k8s.yaml
Normal file
121
helm/openbao-ui-overlay-k8s.yaml
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
# 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.
|
||||||
|
#
|
||||||
|
# ConfigMap data is applied by scripts/openbao-ui-overlay-apply.sh from
|
||||||
|
# helm/openbao-ui-overlay/*.
|
||||||
|
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
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
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/name: openbao-ui-gateway
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: openbao-ui-gateway
|
||||||
|
app.kubernetes.io/part-of: railiance-platform
|
||||||
|
railiance-platform/component: secrets
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: nginx
|
||||||
|
image: nginx:1.27-alpine
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
containerPort: 8080
|
||||||
|
protocol: TCP
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /ui/platform-overlay/presets.json
|
||||||
|
port: http
|
||||||
|
initialDelaySeconds: 3
|
||||||
|
periodSeconds: 10
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /ui/platform-overlay/presets.json
|
||||||
|
port: http
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 20
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 25m
|
||||||
|
memory: 32Mi
|
||||||
|
limits:
|
||||||
|
cpu: 200m
|
||||||
|
memory: 128Mi
|
||||||
|
volumeMounts:
|
||||||
|
- name: nginx-config
|
||||||
|
mountPath: /etc/nginx/nginx.conf
|
||||||
|
subPath: nginx.conf
|
||||||
|
readOnly: true
|
||||||
|
- name: overlay-assets
|
||||||
|
mountPath: /etc/nginx/overlay
|
||||||
|
readOnly: true
|
||||||
|
volumes:
|
||||||
|
- name: nginx-config
|
||||||
|
configMap:
|
||||||
|
name: openbao-ui-gateway-nginx
|
||||||
|
- name: overlay-assets
|
||||||
|
configMap:
|
||||||
|
name: openbao-ui-overlay
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
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
|
||||||
|
spec:
|
||||||
|
type: ClusterIP
|
||||||
|
selector:
|
||||||
|
app.kubernetes.io/name: openbao-ui-gateway
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
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
|
||||||
67
helm/openbao-ui-overlay/README.md
Normal file
67
helm/openbao-ui-overlay/README.md
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
# 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`.
|
||||||
|
|
||||||
|
## Mechanism (T01 decision)
|
||||||
|
|
||||||
|
OpenBao ships UI assets inside the container image. There is no supported API
|
||||||
|
to customize the login form ([`/sys/config/ui`](https://openbao.org/api-docs/system/config-ui/)
|
||||||
|
only configures response headers).
|
||||||
|
|
||||||
|
We use an **nginx UI gateway** (`openbao-ui-gateway`) that:
|
||||||
|
|
||||||
|
1. Proxies all traffic to `openbao.openbao.svc.cluster.local:8200`.
|
||||||
|
2. Serves overlay assets from a ConfigMap at `/ui/platform-overlay/`.
|
||||||
|
3. Injects `overlay.css` and `overlay.js` into HTML responses via `sub_filter`.
|
||||||
|
|
||||||
|
Overlay assets live entirely in this directory. Upgrading OpenBao does not
|
||||||
|
require hand-editing files inside the OpenBao pod.
|
||||||
|
|
||||||
|
Track upstream [openbao/openbao#2936](https://github.com/openbao/openbao/issues/2936)
|
||||||
|
for native custom CSS. When available, keep `presets.json` and branding assets
|
||||||
|
and retire nginx `sub_filter` injection if the upstream API covers the same
|
||||||
|
behaviour.
|
||||||
|
|
||||||
|
## Layout
|
||||||
|
|
||||||
|
| File | Purpose |
|
||||||
|
| --- | --- |
|
||||||
|
| `VERSION` | OpenBao image tag this overlay targets (`openbao-values.yaml`) |
|
||||||
|
| `presets.json` | Hidden login defaults (`netkingdom`, `platform-admin`, …) |
|
||||||
|
| `overlay.css` | Hide raw OpenBao login fields |
|
||||||
|
| `overlay.js` | Apply presets, branding, mount deep-link |
|
||||||
|
| `nginx.conf` | Gateway proxy + HTML injection |
|
||||||
|
| `patches/<version>/manifest.sha256` | Upstream UI fingerprints for drift detection |
|
||||||
|
|
||||||
|
## Deploy
|
||||||
|
|
||||||
|
From `railiance-platform`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make openbao-overlay-apply # overlay only
|
||||||
|
make openbao-deploy # middleware + overlay + Helm upgrade
|
||||||
|
make openbao-verify-login-overlay
|
||||||
|
```
|
||||||
|
|
||||||
|
## Reapply after an OpenBao upgrade
|
||||||
|
|
||||||
|
1. Bump `server.image.tag` in `helm/openbao-values.yaml`.
|
||||||
|
2. Deploy: `make openbao-deploy`.
|
||||||
|
3. Fetch live UI assets and compare hashes:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -sS https://bao.coulomb.social/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
|
||||||
|
sha256sum /tmp/index.html /tmp/vault.js
|
||||||
|
```
|
||||||
|
|
||||||
|
4. If hashes differ from `patches/<old-version>/manifest.sha256`, update
|
||||||
|
`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.
|
||||||
|
|
||||||
|
Workplan: `helix-forge/workplans/HF-WP-0003-openbao-keycape-login-overlay.md`
|
||||||
1
helm/openbao-ui-overlay/VERSION
Normal file
1
helm/openbao-ui-overlay/VERSION
Normal file
@@ -0,0 +1 @@
|
|||||||
|
2.5.4
|
||||||
45
helm/openbao-ui-overlay/nginx.conf
Normal file
45
helm/openbao-ui-overlay/nginx.conf
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
worker_processes auto;
|
||||||
|
error_log /dev/stderr notice;
|
||||||
|
pid /tmp/nginx.pid;
|
||||||
|
|
||||||
|
events {
|
||||||
|
worker_connections 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
include /etc/nginx/mime.types;
|
||||||
|
default_type application/octet-stream;
|
||||||
|
access_log /dev/stdout;
|
||||||
|
sendfile on;
|
||||||
|
keepalive_timeout 65;
|
||||||
|
server_tokens off;
|
||||||
|
|
||||||
|
upstream openbao_upstream {
|
||||||
|
server openbao.openbao.svc.cluster.local:8200;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 8080;
|
||||||
|
|
||||||
|
location /ui/platform-overlay/ {
|
||||||
|
alias /etc/nginx/overlay/;
|
||||||
|
add_header Cache-Control "public, max-age=300";
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://openbao_upstream;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
# Disable upstream compression so sub_filter can rewrite HTML.
|
||||||
|
proxy_set_header Accept-Encoding "";
|
||||||
|
proxy_buffering on;
|
||||||
|
|
||||||
|
sub_filter_types text/html;
|
||||||
|
sub_filter_once on;
|
||||||
|
sub_filter '</head>' '<link rel="stylesheet" href="/ui/platform-overlay/overlay.css"><script src="/ui/platform-overlay/overlay.js" defer></script></head>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
37
helm/openbao-ui-overlay/overlay.css
Normal file
37
helm/openbao-ui-overlay/overlay.css
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
/* KeyCape login overlay for OpenBao UI — see presets.json and overlay.js */
|
||||||
|
|
||||||
|
html.keycape-overlay-active .toolbar-namespace-picker,
|
||||||
|
html.keycape-overlay-active nav.tabs,
|
||||||
|
html.keycape-overlay-active label[for="namespace"],
|
||||||
|
html.keycape-overlay-active label[for="role"],
|
||||||
|
html.keycape-overlay-active label[for="custom-path"],
|
||||||
|
html.keycape-overlay-active #namespace,
|
||||||
|
html.keycape-overlay-active #role,
|
||||||
|
html.keycape-overlay-active #custom-path,
|
||||||
|
html.keycape-overlay-active select[name="auth-method"],
|
||||||
|
html.keycape-overlay-active .auth-form .box.has-slim-padding.is-shadowless,
|
||||||
|
html.keycape-overlay-active .auth-form .has-bottom-margin-s {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.keycape-overlay-active .splash-page-header .brand-icon-large {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.keycape-overlay-active h1.title.is-3 {
|
||||||
|
font-size: 1.45rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.keycape-overlay-banner {
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
background: #f4f6f8;
|
||||||
|
border-bottom: 1px solid #d9dee3;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: #3d4f5f;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.keycape-overlay-active .login-form .auth-form {
|
||||||
|
padding-top: 0.25rem;
|
||||||
|
}
|
||||||
161
helm/openbao-ui-overlay/overlay.js
Normal file
161
helm/openbao-ui-overlay/overlay.js
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const PRESETS_URL = "/ui/platform-overlay/presets.json";
|
||||||
|
const DEFAULT_PRESETS = {
|
||||||
|
namespace: "",
|
||||||
|
method: "oidc",
|
||||||
|
mount: "netkingdom",
|
||||||
|
role: "platform-admin",
|
||||||
|
title: "Sign in with KeyCape",
|
||||||
|
signInLabel: "Sign in with KeyCape",
|
||||||
|
banner:
|
||||||
|
"Platform operators authenticate through KeyCape at kc.coulomb.social.",
|
||||||
|
};
|
||||||
|
|
||||||
|
let presets = { ...DEFAULT_PRESETS };
|
||||||
|
|
||||||
|
function isAuthPage() {
|
||||||
|
const path = window.location.pathname;
|
||||||
|
return (
|
||||||
|
/\/ui\/vault\/auth(?:\/|$)/.test(path) ||
|
||||||
|
/\/ui\/?$/.test(path)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideNode(node) {
|
||||||
|
if (!node) return;
|
||||||
|
const field =
|
||||||
|
node.closest(".field.is-horizontal") ||
|
||||||
|
node.closest(".field") ||
|
||||||
|
node.closest(".box") ||
|
||||||
|
node;
|
||||||
|
field.style.display = "none";
|
||||||
|
field.setAttribute("aria-hidden", "true");
|
||||||
|
}
|
||||||
|
|
||||||
|
function setInputValue(input, value) {
|
||||||
|
if (!input || input.value === value) return;
|
||||||
|
input.value = value;
|
||||||
|
input.dispatchEvent(new Event("input", { bubbles: true }));
|
||||||
|
input.dispatchEvent(new Event("change", { bubbles: true }));
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureAuthMountSelected() {
|
||||||
|
const mount = presets.mount || "netkingdom";
|
||||||
|
const withValue = mount.endsWith("/") ? mount : `${mount}/`;
|
||||||
|
const params = new URLSearchParams(window.location.search);
|
||||||
|
const current = params.get("with") || "";
|
||||||
|
|
||||||
|
if (current.replace(/\/$/, "") === withValue.replace(/\/$/, "")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isAuthPage() || window.location.pathname.includes("/oidc/")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
params.set("with", withValue);
|
||||||
|
const next = `${window.location.pathname}?${params.toString()}`;
|
||||||
|
if (next !== `${window.location.pathname}${window.location.search}`) {
|
||||||
|
window.location.replace(next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyDom() {
|
||||||
|
if (!isAuthPage()) return;
|
||||||
|
|
||||||
|
hideNode(document.querySelector(".toolbar-namespace-picker"));
|
||||||
|
document
|
||||||
|
.querySelectorAll(
|
||||||
|
'#namespace, input[name="namespace"], label[for="namespace"]'
|
||||||
|
)
|
||||||
|
.forEach(hideNode);
|
||||||
|
|
||||||
|
document
|
||||||
|
.querySelectorAll('select[name="auth-method"], #auth-method')
|
||||||
|
.forEach((el) => hideNode(el.closest(".field") || el));
|
||||||
|
|
||||||
|
document
|
||||||
|
.querySelectorAll('#custom-path, input[name="custom-path"]')
|
||||||
|
.forEach(hideNode);
|
||||||
|
|
||||||
|
document
|
||||||
|
.querySelectorAll('#role, input[name="role"], label[for="role"]')
|
||||||
|
.forEach(hideNode);
|
||||||
|
|
||||||
|
document.querySelectorAll("nav.tabs").forEach((el) => {
|
||||||
|
el.style.display = "none";
|
||||||
|
el.setAttribute("aria-hidden", "true");
|
||||||
|
});
|
||||||
|
|
||||||
|
document
|
||||||
|
.querySelectorAll(".auth-form .has-bottom-margin-s")
|
||||||
|
.forEach(hideNode);
|
||||||
|
|
||||||
|
document.querySelectorAll("h1.title.is-3").forEach((heading) => {
|
||||||
|
if (/Sign in to OpenBao|Authenticate/.test(heading.textContent)) {
|
||||||
|
heading.textContent = presets.title;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document
|
||||||
|
.querySelectorAll('#auth-submit, button[data-test="auth-submit"]')
|
||||||
|
.forEach((button) => {
|
||||||
|
button.textContent = presets.signInLabel;
|
||||||
|
});
|
||||||
|
|
||||||
|
document
|
||||||
|
.querySelectorAll('#namespace, input[name="namespace"]')
|
||||||
|
.forEach((input) => setInputValue(input, presets.namespace || ""));
|
||||||
|
|
||||||
|
document
|
||||||
|
.querySelectorAll('#role, input[name="role"]')
|
||||||
|
.forEach((input) => setInputValue(input, presets.role || "platform-admin"));
|
||||||
|
|
||||||
|
if (!document.getElementById("keycape-overlay-banner")) {
|
||||||
|
const banner = document.createElement("div");
|
||||||
|
banner.id = "keycape-overlay-banner";
|
||||||
|
banner.className = "keycape-overlay-banner";
|
||||||
|
banner.textContent = presets.banner;
|
||||||
|
const loginForm = document.querySelector(".login-form");
|
||||||
|
if (loginForm) {
|
||||||
|
loginForm.prepend(banner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.documentElement.classList.add("keycape-overlay-active");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadPresets() {
|
||||||
|
try {
|
||||||
|
const response = await fetch(PRESETS_URL, { cache: "no-store" });
|
||||||
|
if (!response.ok) return;
|
||||||
|
const data = await response.json();
|
||||||
|
presets = { ...DEFAULT_PRESETS, ...data };
|
||||||
|
} catch (_error) {
|
||||||
|
presets = { ...DEFAULT_PRESETS };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function observe() {
|
||||||
|
const observer = new MutationObserver(() => applyDom());
|
||||||
|
observer.observe(document.body, { childList: true, subtree: true });
|
||||||
|
applyDom();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function init() {
|
||||||
|
await loadPresets();
|
||||||
|
if (!isAuthPage()) return;
|
||||||
|
|
||||||
|
ensureAuthMountSelected();
|
||||||
|
|
||||||
|
if (document.readyState === "loading") {
|
||||||
|
document.addEventListener("DOMContentLoaded", observe);
|
||||||
|
} else {
|
||||||
|
observe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init();
|
||||||
|
})();
|
||||||
8
helm/openbao-ui-overlay/patches/2.5.4/manifest.sha256
Normal file
8
helm/openbao-ui-overlay/patches/2.5.4/manifest.sha256
Normal file
@@ -0,0 +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
|
||||||
|
# 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
|
||||||
|
# sha256sum /tmp/vault.js
|
||||||
|
f0214b5be89377395f8d6521c34139877529bd95ba703901c78b527ab0f1c231 ui/assets/vault-bae6b876038fbf475728f993b5a62002.js
|
||||||
9
helm/openbao-ui-overlay/presets.json
Normal file
9
helm/openbao-ui-overlay/presets.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"namespace": "",
|
||||||
|
"method": "oidc",
|
||||||
|
"mount": "netkingdom",
|
||||||
|
"role": "platform-admin",
|
||||||
|
"title": "Sign in with KeyCape",
|
||||||
|
"signInLabel": "Sign in with KeyCape",
|
||||||
|
"banner": "Platform operators authenticate through KeyCape at kc.coulomb.social."
|
||||||
|
}
|
||||||
@@ -30,24 +30,10 @@ server:
|
|||||||
cpu: 500m
|
cpu: 500m
|
||||||
memory: 512Mi
|
memory: 512Mi
|
||||||
|
|
||||||
|
# Public browser ingress is owned by helm/openbao-ui-overlay-k8s.yaml so the
|
||||||
|
# KeyCape login overlay gateway can inject overlay assets.
|
||||||
ingress:
|
ingress:
|
||||||
enabled: true
|
enabled: false
|
||||||
annotations:
|
|
||||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
|
||||||
traefik.ingress.kubernetes.io/router.middlewares: >-
|
|
||||||
openbao-openbao-rate-limit@kubernetescrd,
|
|
||||||
openbao-openbao-hsts@kubernetescrd
|
|
||||||
ingressClassName: traefik
|
|
||||||
pathType: Prefix
|
|
||||||
activeService: true
|
|
||||||
hosts:
|
|
||||||
- host: bao.coulomb.social
|
|
||||||
paths:
|
|
||||||
- /
|
|
||||||
tls:
|
|
||||||
- secretName: bao-tls
|
|
||||||
hosts:
|
|
||||||
- bao.coulomb.social
|
|
||||||
|
|
||||||
authDelegator:
|
authDelegator:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|||||||
69
scripts/openbao-ui-overlay-apply.sh
Executable file
69
scripts/openbao-ui-overlay-apply.sh
Executable file
@@ -0,0 +1,69 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
OPENBAO_NAMESPACE="${OPENBAO_NAMESPACE:-openbao}"
|
||||||
|
KUBECTL="${KUBECTL:-kubectl}"
|
||||||
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
OVERLAY_DIR="${OPENBAO_UI_OVERLAY_DIR:-$ROOT_DIR/helm/openbao-ui-overlay}"
|
||||||
|
K8S_MANIFEST="${OPENBAO_UI_OVERLAY_K8S:-$ROOT_DIR/helm/openbao-ui-overlay-k8s.yaml}"
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Environment:
|
||||||
|
OPENBAO_NAMESPACE Kubernetes namespace. Default: openbao
|
||||||
|
KUBECTL kubectl command, including --kubeconfig if needed
|
||||||
|
OPENBAO_UI_OVERLAY_DIR Overlay asset directory
|
||||||
|
OPENBAO_UI_OVERLAY_K8S Gateway manifest path
|
||||||
|
USAGE
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ "${1:-}" = "-h" ] || [ "${1:-}" = "--help" ]; then
|
||||||
|
usage
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
for required in overlay.css overlay.js presets.json nginx.conf VERSION; do
|
||||||
|
if [ ! -f "$OVERLAY_DIR/$required" ]; then
|
||||||
|
echo "missing overlay asset: $OVERLAY_DIR/$required" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ ! -f "$K8S_MANIFEST" ]; then
|
||||||
|
echo "missing gateway manifest: $K8S_MANIFEST" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
$KUBECTL create namespace "$OPENBAO_NAMESPACE" --dry-run=client -o yaml | $KUBECTL apply -f -
|
||||||
|
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
$KUBECTL create configmap openbao-ui-overlay \
|
||||||
|
--namespace "$OPENBAO_NAMESPACE" \
|
||||||
|
--from-file="$OVERLAY_DIR/overlay.css" \
|
||||||
|
--from-file="$OVERLAY_DIR/overlay.js" \
|
||||||
|
--from-file="$OVERLAY_DIR/presets.json" \
|
||||||
|
--from-file="$OVERLAY_DIR/VERSION" \
|
||||||
|
--dry-run=client -o yaml | $KUBECTL apply -f -
|
||||||
|
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
$KUBECTL create configmap openbao-ui-gateway-nginx \
|
||||||
|
--namespace "$OPENBAO_NAMESPACE" \
|
||||||
|
--from-file=nginx.conf="$OVERLAY_DIR/nginx.conf" \
|
||||||
|
--dry-run=client -o yaml | $KUBECTL apply -f -
|
||||||
|
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
$KUBECTL apply -f "$K8S_MANIFEST"
|
||||||
|
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
$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"
|
||||||
143
scripts/openbao-verify-login-overlay.sh
Executable file
143
scripts/openbao-verify-login-overlay.sh
Executable file
@@ -0,0 +1,143 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
BASE_URL="${OPENBAO_UI_BASE_URL:-https://bao.coulomb.social}"
|
||||||
|
OVERLAY_DIR="${OPENBAO_UI_OVERLAY_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/helm/openbao-ui-overlay}"
|
||||||
|
CHECK_DRIFT="${CHECK_UPSTREAM_DRIFT:-0}"
|
||||||
|
|
||||||
|
ok() { printf '[OK] %s\n' "$*"; }
|
||||||
|
err() { printf '[ERR] %s\n' "$*" >&2; }
|
||||||
|
step() { printf '\n==> %s\n' "$*"; }
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Environment:
|
||||||
|
OPENBAO_UI_BASE_URL Public UI base URL. Default: https://bao.coulomb.social
|
||||||
|
OPENBAO_UI_OVERLAY_DIR Local overlay directory for drift fingerprints
|
||||||
|
CHECK_UPSTREAM_DRIFT Set to 1 to compare live UI hashes with patches/
|
||||||
|
USAGE
|
||||||
|
}
|
||||||
|
|
||||||
|
while [ "$#" -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
--check-upstream-drift)
|
||||||
|
CHECK_DRIFT=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-h|--help)
|
||||||
|
usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
err "unknown argument: $1"
|
||||||
|
usage >&2
|
||||||
|
exit 2
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
require_pattern() {
|
||||||
|
local label="$1"
|
||||||
|
local haystack="$2"
|
||||||
|
local pattern="$3"
|
||||||
|
if ! grep -Eq "$pattern" <<<"$haystack"; then
|
||||||
|
err "$label"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
ok "$label"
|
||||||
|
}
|
||||||
|
|
||||||
|
step "Overlay asset endpoints"
|
||||||
|
index_html="$(curl -fsS "$BASE_URL/ui/")"
|
||||||
|
overlay_js="$(curl -fsS "$BASE_URL/ui/platform-overlay/overlay.js")"
|
||||||
|
overlay_css="$(curl -fsS "$BASE_URL/ui/platform-overlay/overlay.css")"
|
||||||
|
presets_json="$(curl -fsS "$BASE_URL/ui/platform-overlay/presets.json")"
|
||||||
|
|
||||||
|
require_pattern \
|
||||||
|
"index.html injects overlay.js" \
|
||||||
|
"$index_html" \
|
||||||
|
'/ui/platform-overlay/overlay\.js'
|
||||||
|
|
||||||
|
require_pattern \
|
||||||
|
"index.html injects overlay.css" \
|
||||||
|
"$index_html" \
|
||||||
|
'/ui/platform-overlay/overlay\.css'
|
||||||
|
|
||||||
|
require_pattern \
|
||||||
|
"overlay.js activates KeyCape overlay" \
|
||||||
|
"$overlay_js" \
|
||||||
|
'keycape-overlay-active'
|
||||||
|
|
||||||
|
require_pattern \
|
||||||
|
"presets.json targets netkingdom mount" \
|
||||||
|
"$presets_json" \
|
||||||
|
'"mount"[[:space:]]*:[[:space:]]*"netkingdom"'
|
||||||
|
|
||||||
|
require_pattern \
|
||||||
|
"presets.json targets platform-admin role" \
|
||||||
|
"$presets_json" \
|
||||||
|
'"role"[[:space:]]*:[[:space:]]*"platform-admin"'
|
||||||
|
|
||||||
|
require_pattern \
|
||||||
|
"overlay.css hides namespace picker" \
|
||||||
|
"$overlay_css" \
|
||||||
|
'toolbar-namespace-picker'
|
||||||
|
|
||||||
|
require_pattern \
|
||||||
|
"overlay branding title present in presets" \
|
||||||
|
"$presets_json" \
|
||||||
|
'Sign in with KeyCape'
|
||||||
|
|
||||||
|
step "Hidden-field selectors still present in overlay.js"
|
||||||
|
require_pattern \
|
||||||
|
"overlay.js hides namespace input" \
|
||||||
|
"$overlay_js" \
|
||||||
|
'#namespace|input\[name="namespace"\]'
|
||||||
|
|
||||||
|
require_pattern \
|
||||||
|
"overlay.js hides role input" \
|
||||||
|
"$overlay_js" \
|
||||||
|
'#role|input\[name="role"\]'
|
||||||
|
|
||||||
|
require_pattern \
|
||||||
|
"overlay.js hides mount path input" \
|
||||||
|
"$overlay_js" \
|
||||||
|
'#custom-path|input\[name="custom-path"\]'
|
||||||
|
|
||||||
|
if [ "$CHECK_DRIFT" = "1" ]; then
|
||||||
|
step "Upstream UI drift check"
|
||||||
|
version_file="$OVERLAY_DIR/VERSION"
|
||||||
|
if [ ! -f "$version_file" ]; then
|
||||||
|
err "missing overlay VERSION file: $version_file"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
version="$(tr -d '[:space:]' < "$version_file")"
|
||||||
|
manifest="$OVERLAY_DIR/patches/$version/manifest.sha256"
|
||||||
|
if [ ! -f "$manifest" ]; then
|
||||||
|
err "missing fingerprint manifest: $manifest"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
vault_asset="$(grep -Eo '/ui/assets/vault-[a-f0-9]+\.js' <<<"$index_html" | head -1 || true)"
|
||||||
|
if [ -z "$vault_asset" ]; then
|
||||||
|
err "could not locate vault.js asset path in index.html"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
live_vault_hash="$(curl -fsS "$BASE_URL$vault_asset" | sha256sum | awk '{print $1}')"
|
||||||
|
|
||||||
|
expected_vault_hash="$(awk '!/^#/ && /ui\/assets\/vault-/ {print $1; exit}' "$manifest")"
|
||||||
|
expected_vault_path="$(awk '!/^#/ && /ui\/assets\/vault-/ {print $2; exit}' "$manifest")"
|
||||||
|
|
||||||
|
if [ -n "$expected_vault_hash" ] && [ "$live_vault_hash" != "$expected_vault_hash" ]; then
|
||||||
|
err "vault bundle hash drift for ${vault_asset:-unknown}: expected $expected_vault_hash got $live_vault_hash"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
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"
|
||||||
Reference in New Issue
Block a user