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
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"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue