Add ExternalSecret for ISSUE_CORE_API_KEY on Railiance
Sync the shared issue-core ingestion key from OpenBao into actcore-runtime-secret via External Secrets, with an interim coulombcore ClusterSecretStore bootstrap script and deploy docs. Removes manual key injection from bootstrap-secrets.sh.
This commit is contained in:
parent
7dab19b0d7
commit
9a7ae8b59a
7 changed files with 128 additions and 20 deletions
3
Makefile
3
Makefile
|
|
@ -60,6 +60,9 @@ railiance-up: ## Build image and start full railiance stack (no Elasticsearch)
|
||||||
railiance-down: ## Stop and remove railiance stack containers
|
railiance-down: ## Stop and remove railiance stack containers
|
||||||
docker compose -f docker-compose.railiance.yml down
|
docker compose -f docker-compose.railiance.yml down
|
||||||
|
|
||||||
|
openbao-eso-token-apply: ## Mint coulombcore OpenBao ESO token for railiance01 activity-core
|
||||||
|
OPENBAO_TOKEN_FILE="$(HOME)/.local/openbao/platform-admin.token" bash scripts/openbao-eso-token-apply.sh
|
||||||
|
|
||||||
# ── Local dev processes ───────────────────────────────────────────────────────
|
# ── Local dev processes ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
start-worker: ## Start Temporal worker (reads env from .env if present)
|
start-worker: ## Start Temporal worker (reads env from .env if present)
|
||||||
|
|
|
||||||
12
SCOPE.md
12
SCOPE.md
|
|
@ -181,12 +181,12 @@ The two evaluation modes:
|
||||||
runtime projection failure. The ops inventory probe path has live fallback
|
runtime projection failure. The ops inventory probe path has live fallback
|
||||||
evidence in State Hub; Inter-Hub per-entity submission remains deferred.
|
evidence in State Hub; Inter-Hub per-entity submission remains deferred.
|
||||||
- **Task emission posture**: the issue-core REST sink is implemented and
|
- **Task emission posture**: the issue-core REST sink is implemented and
|
||||||
Railiance runtime ConfigMap sets `ISSUE_SINK_TYPE=rest`. The OpenBao ingestion
|
Railiance runtime ConfigMap sets `ISSUE_SINK_TYPE=rest`. `ISSUE_CORE_API_KEY`
|
||||||
lane (`RAILIANCE-WP-0009`) is finished; live emission still requires the
|
is synced into `actcore-runtime-secret` by ExternalSecret
|
||||||
operator to populate `ISSUE_CORE_API_KEY` in `actcore-runtime-secret` with the
|
`actcore-issue-core-runtime` (OpenBao path shared with issue-core). Bootstrap
|
||||||
same value as issue-core. `weekly-sbom-staleness` is the canonical promotion
|
the ESO token with `make openbao-eso-token-apply`. `weekly-sbom-staleness` is
|
||||||
candidate; use `scripts/smoke_issue_core_emission.py` before trusting production
|
the canonical promotion candidate; use `scripts/smoke_issue_core_emission.py`
|
||||||
task creation.
|
before trusting production task creation.
|
||||||
- **Stability**: construction risk has shifted to operational hardening and
|
- **Stability**: construction risk has shifted to operational hardening and
|
||||||
adoption risk. The last recorded full-suite pass in the workplans was
|
adoption risk. The last recorded full-suite pass in the workplans was
|
||||||
2026-06-04 (`128 passed, 1 skipped`), with later targeted coverage added for
|
2026-06-04 (`128 passed, 1 skipped`), with later targeted coverage added for
|
||||||
|
|
|
||||||
|
|
@ -73,9 +73,10 @@ review and one live `IssueCoreRestSink` smoke against the target endpoint.
|
||||||
ISSUE_CORE_URL=http://127.0.0.1:8765 ISSUE_CORE_API_KEY=... \
|
ISSUE_CORE_URL=http://127.0.0.1:8765 ISSUE_CORE_API_KEY=... \
|
||||||
uv run python scripts/smoke_issue_core_emission.py --live
|
uv run python scripts/smoke_issue_core_emission.py --live
|
||||||
```
|
```
|
||||||
5. Set `ISSUE_SINK_TYPE=rest` in `actcore-runtime-config`, ensure
|
5. Set `ISSUE_SINK_TYPE=rest` in `actcore-runtime-config`, apply
|
||||||
`actcore-runtime-secret` includes `ISSUE_CORE_API_KEY`, and restart
|
`k8s/railiance/15-externalsecret-issue-core.yaml` so External Secrets merges
|
||||||
`actcore-worker` / `actcore-event-router`.
|
`ISSUE_CORE_API_KEY` into `actcore-runtime-secret`, and restart
|
||||||
|
`actcore-worker` / `actcore-event-router` after the ExternalSecret is Ready.
|
||||||
6. Trigger one known-safe run (weekly SBOM staleness on a stale fixture or
|
6. Trigger one known-safe run (weekly SBOM staleness on a stale fixture or
|
||||||
manual `/activity-definitions/<id>/trigger`) and confirm `task_spawn_log`
|
manual `/activity-definitions/<id>/trigger`) and confirm `task_spawn_log`
|
||||||
stores the real `issue_id` returned by issue-core.
|
stores the real `issue_id` returned by issue-core.
|
||||||
|
|
|
||||||
35
k8s/railiance/15-externalsecret-issue-core.yaml
Normal file
35
k8s/railiance/15-externalsecret-issue-core.yaml
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
# Sync ISSUE_CORE_API_KEY from OpenBao into actcore-runtime-secret.
|
||||||
|
#
|
||||||
|
# Prereqs on railiance01:
|
||||||
|
# - External Secrets Operator (namespace external-secrets)
|
||||||
|
# - ClusterSecretStore openbao-activity-core (railiance-platform addon)
|
||||||
|
# - Secret external-secrets/openbao-activity-core-eso-token (scripts/openbao-eso-token-apply.sh)
|
||||||
|
#
|
||||||
|
# OpenBao path: platform/workloads/issue-core/issue-core/issue-core-runtime
|
||||||
|
# property: ISSUE_CORE_API_KEY (shared with issue-core ingestion)
|
||||||
|
#
|
||||||
|
# Merge keeps bootstrap-owned keys (ACTCORE_DB_URL, webhook secrets) intact.
|
||||||
|
apiVersion: external-secrets.io/v1beta1
|
||||||
|
kind: ExternalSecret
|
||||||
|
metadata:
|
||||||
|
name: actcore-issue-core-runtime
|
||||||
|
namespace: activity-core
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: activity-core
|
||||||
|
app.kubernetes.io/part-of: activity-core
|
||||||
|
annotations:
|
||||||
|
argocd.argoproj.io/sync-wave: "0"
|
||||||
|
spec:
|
||||||
|
refreshInterval: 1h
|
||||||
|
secretStoreRef:
|
||||||
|
kind: ClusterSecretStore
|
||||||
|
name: openbao-activity-core
|
||||||
|
target:
|
||||||
|
name: actcore-runtime-secret
|
||||||
|
creationPolicy: Merge
|
||||||
|
deletionPolicy: Retain
|
||||||
|
data:
|
||||||
|
- secretKey: ISSUE_CORE_API_KEY
|
||||||
|
remoteRef:
|
||||||
|
key: platform/workloads/issue-core/issue-core/issue-core-runtime
|
||||||
|
property: ISSUE_CORE_API_KEY
|
||||||
|
|
@ -10,6 +10,8 @@ name and access policy.
|
||||||
- `00-namespace.yaml`: namespace and shared labels
|
- `00-namespace.yaml`: namespace and shared labels
|
||||||
- `10-infrastructure.yaml`: PostgreSQL for app data, PostgreSQL for Temporal,
|
- `10-infrastructure.yaml`: PostgreSQL for app data, PostgreSQL for Temporal,
|
||||||
NATS JetStream, Temporal, and Temporal UI
|
NATS JetStream, Temporal, and Temporal UI
|
||||||
|
- `15-externalsecret-issue-core.yaml`: OpenBao → `ISSUE_CORE_API_KEY` merge into
|
||||||
|
`actcore-runtime-secret` via External Secrets
|
||||||
- `20-runtime.yaml`: migrate/sync jobs plus API, worker, and event-router
|
- `20-runtime.yaml`: migrate/sync jobs plus API, worker, and event-router
|
||||||
- `bootstrap-secrets.sh`: idempotently creates generated Kubernetes secrets
|
- `bootstrap-secrets.sh`: idempotently creates generated Kubernetes secrets
|
||||||
|
|
||||||
|
|
@ -24,12 +26,15 @@ the ConfigMap projection from that file before enabling the probe schedule.
|
||||||
`OPS_HUB_KEY` is created only as an empty Secret placeholder until the operator
|
`OPS_HUB_KEY` is created only as an empty Secret placeholder until the operator
|
||||||
provisions the Inter-Hub ops-hub key.
|
provisions the Inter-Hub ops-hub key.
|
||||||
|
|
||||||
`ISSUE_SINK_TYPE` is `rest` in `actcore-runtime-config`. Populate
|
`ISSUE_SINK_TYPE` is `rest` in `actcore-runtime-config`. `ISSUE_CORE_API_KEY`
|
||||||
`ISSUE_CORE_API_KEY` in `actcore-runtime-secret` with the same value as the
|
is synced from OpenBao into `actcore-runtime-secret` by ExternalSecret
|
||||||
issue-core runtime secret (`issue-core/issue-core-runtime` via OpenBao lane
|
`actcore-issue-core-runtime` (same path as issue-core:
|
||||||
`issue-core-ingestion-api-key`) before trusting live task emission. Roll back
|
`platform/workloads/issue-core/issue-core/issue-core-runtime`). Prereqs:
|
||||||
to audit mode by setting `ISSUE_SINK_TYPE=null` and restarting worker and
|
`ClusterSecretStore/openbao-activity-core` and ESO token bootstrap
|
||||||
event-router deployments. See `docs/issue-core-emission-boundary.md`.
|
(`OPENBAO_TOKEN_FILE=~/.local/openbao/platform-admin.token ./scripts/openbao-eso-token-apply.sh`).
|
||||||
|
Roll back to audit mode by setting
|
||||||
|
`ISSUE_SINK_TYPE=null` and restarting worker and event-router deployments.
|
||||||
|
See `docs/issue-core-emission-boundary.md`.
|
||||||
|
|
||||||
The same runtime projection now includes the active
|
The same runtime projection now includes the active
|
||||||
`daily-statehub-wsjf-triage.md` ActivityDefinition plus its JSON output schema
|
`daily-statehub-wsjf-triage.md` ActivityDefinition plus its JSON output schema
|
||||||
|
|
@ -61,6 +66,11 @@ ssh railiance01
|
||||||
cd ~/activity-core
|
cd ~/activity-core
|
||||||
bash k8s/railiance/bootstrap-secrets.sh
|
bash k8s/railiance/bootstrap-secrets.sh
|
||||||
kubectl apply -f k8s/railiance/10-infrastructure.yaml
|
kubectl apply -f k8s/railiance/10-infrastructure.yaml
|
||||||
|
# Bootstrap OpenBao ESO token + apply ExternalSecret (once per cluster):
|
||||||
|
OPENBAO_TOKEN_FILE=~/.local/openbao/platform-admin.token ./scripts/openbao-eso-token-apply.sh
|
||||||
|
kubectl apply -f ~/railiance-platform/argocd/platform-addons/openbao-secretstore/openbao-activity-core.clustersecretstore.yaml
|
||||||
|
kubectl apply -f k8s/railiance/15-externalsecret-issue-core.yaml
|
||||||
|
kubectl -n activity-core wait --for=condition=Ready externalsecret/actcore-issue-core-runtime --timeout=120s
|
||||||
kubectl -n activity-core wait --for=condition=ready pod -l app.kubernetes.io/name=actcore-app-db --timeout=180s
|
kubectl -n activity-core wait --for=condition=ready pod -l app.kubernetes.io/name=actcore-app-db --timeout=180s
|
||||||
kubectl -n activity-core wait --for=condition=ready pod -l app.kubernetes.io/name=actcore-temporal-db --timeout=180s
|
kubectl -n activity-core wait --for=condition=ready pod -l app.kubernetes.io/name=actcore-temporal-db --timeout=180s
|
||||||
kubectl -n activity-core wait --for=condition=ready pod -l app.kubernetes.io/name=actcore-nats --timeout=180s
|
kubectl -n activity-core wait --for=condition=ready pod -l app.kubernetes.io/name=actcore-nats --timeout=180s
|
||||||
|
|
|
||||||
|
|
@ -37,9 +37,8 @@ if ! secret_exists actcore-runtime-secret; then
|
||||||
--from-literal=ACTCORE_DB_URL="$ACTCORE_DB_URL" \
|
--from-literal=ACTCORE_DB_URL="$ACTCORE_DB_URL" \
|
||||||
--from-literal=WEBHOOK_SECRET_GITEA="" \
|
--from-literal=WEBHOOK_SECRET_GITEA="" \
|
||||||
--from-literal=WEBHOOK_SECRET_GITHUB="" \
|
--from-literal=WEBHOOK_SECRET_GITHUB="" \
|
||||||
--from-literal=OPS_HUB_KEY="" \
|
--from-literal=OPS_HUB_KEY=""
|
||||||
--from-literal=ISSUE_CORE_API_KEY=""
|
|
||||||
fi
|
fi
|
||||||
# When promoting live task emission, patch ISSUE_CORE_API_KEY on
|
# ISSUE_CORE_API_KEY is merged into actcore-runtime-secret by ExternalSecret
|
||||||
# actcore-runtime-secret to match issue-core's runtime secret. Never commit the
|
# actcore-issue-core-runtime (k8s/railiance/15-externalsecret-issue-core.yaml).
|
||||||
# value; use the OpenBao lane documented in issue-core-ingestion-api-key.
|
# Apply that manifest after ClusterSecretStore openbao-activity-core is Ready.
|
||||||
|
|
|
||||||
60
scripts/openbao-eso-token-apply.sh
Executable file
60
scripts/openbao-eso-token-apply.sh
Executable file
|
|
@ -0,0 +1,60 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Mint a read-limited OpenBao token on coulombcore (bao.coulomb.social) and store
|
||||||
|
# it on railiance01 for ClusterSecretStore openbao-activity-core / ExternalSecret
|
||||||
|
# actcore-issue-core-runtime.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
POLICY_NAME="${OPENBAO_ACTIVITY_CORE_POLICY:-workload-kv-read-issue-core-runtime}"
|
||||||
|
POLICY_FILE="${OPENBAO_ACTIVITY_CORE_POLICY_FILE:-$HOME/railiance-platform/openbao/policies/workload-kv-read-issue-core-runtime.hcl}"
|
||||||
|
BAO_ADDR="${BAO_ADDR:-https://bao.coulomb.social}"
|
||||||
|
RAILIANCE01_KUBECONFIG="${RAILIANCE01_KUBECONFIG:-$HOME/.kube/config-hosteurope}"
|
||||||
|
SECRET_NAME="${OPENBAO_ACTIVITY_CORE_ESO_SECRET:-openbao-activity-core-eso-token}"
|
||||||
|
SECRET_NS="${OPENBAO_ACTIVITY_CORE_ESO_NAMESPACE:-external-secrets}"
|
||||||
|
|
||||||
|
if ! command -v bao >/dev/null 2>&1; then
|
||||||
|
echo "ERROR: bao CLI not found" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -f "$POLICY_FILE" ]]; then
|
||||||
|
echo "ERROR: policy file missing: $POLICY_FILE" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "OpenBao addr: $BAO_ADDR"
|
||||||
|
echo "Policy: $POLICY_NAME"
|
||||||
|
|
||||||
|
if [[ -n "${BAO_TOKEN:-}" ]]; then
|
||||||
|
:
|
||||||
|
elif [[ -n "${OPENBAO_TOKEN_FILE:-}" && -f "${OPENBAO_TOKEN_FILE}" ]]; then
|
||||||
|
BAO_TOKEN="$(head -n 1 "${OPENBAO_TOKEN_FILE}")"
|
||||||
|
else
|
||||||
|
read -r -s -p "OpenBao operator token (coulombcore / bao.coulomb.social): " BAO_TOKEN
|
||||||
|
echo >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "${BAO_TOKEN:-}" ]]; then
|
||||||
|
echo "ERROR: empty OpenBao token" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
export BAO_ADDR BAO_TOKEN
|
||||||
|
|
||||||
|
health="$(curl -fsS "$BAO_ADDR/v1/sys/health")"
|
||||||
|
if echo "$health" | grep -q '"sealed":true'; then
|
||||||
|
echo "ERROR: OpenBao at $BAO_ADDR reports sealed" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
bao policy write "$POLICY_NAME" "$POLICY_FILE"
|
||||||
|
token_json="$(bao token create -policy="$POLICY_NAME" -display-name="eso-activity-core-issue-sink" -period=720h -format=json)"
|
||||||
|
token="$(python3 -c "import json,sys; print(json.load(sys.stdin)['auth']['client_token'])" <<<"$token_json")"
|
||||||
|
|
||||||
|
KUBECONFIG="$RAILIANCE01_KUBECONFIG" kubectl create namespace "$SECRET_NS" --dry-run=client -o yaml | KUBECONFIG="$RAILIANCE01_KUBECONFIG" kubectl apply -f -
|
||||||
|
KUBECONFIG="$RAILIANCE01_KUBECONFIG" kubectl create secret generic "$SECRET_NAME" \
|
||||||
|
--namespace "$SECRET_NS" \
|
||||||
|
--from-literal=token="$token" \
|
||||||
|
--dry-run=client -o yaml | KUBECONFIG="$RAILIANCE01_KUBECONFIG" kubectl apply -f -
|
||||||
|
|
||||||
|
unset BAO_TOKEN token token_json
|
||||||
|
echo "ok: applied $SECRET_NS/$SECRET_NAME on railiance01 (token not printed)"
|
||||||
Loading…
Add table
Add a link
Reference in a new issue