Add reuse-webhook-smoke and rotation runbook links
RAILIANCE-WP-0011-T04: smoke checks unsigned 401, signed no-op 200, ESO Ready, and /v1/federated; point operator docs at platform rotation runbook.
This commit is contained in:
parent
85e9163bb0
commit
0968f1d5d5
3 changed files with 101 additions and 8 deletions
5
Makefile
5
Makefile
|
|
@ -330,6 +330,9 @@ reuse-runtime-es-deploy: check-railiance01-kubeconfig reuse-openbao-store-deploy
|
|||
reuse-runtime-es-status: check-railiance01-kubeconfig ## Show reuse-surface runtime ExternalSecret sync status
|
||||
KUBECONFIG="$(REUSE_KUBECONFIG)" kubectl get externalsecret,secret -n $(REUSE_NAMESPACE) reuse-surface-runtime reuse-surface-env
|
||||
|
||||
reuse-webhook-smoke: check-railiance01-kubeconfig ## Webhook + ESO + federated smoke (RAILIANCE-WP-0011-T04)
|
||||
bash tools/reuse-webhook-smoke.sh
|
||||
|
||||
##@ Help
|
||||
|
||||
help: ## Show this help
|
||||
|
|
@ -337,4 +340,4 @@ help: ## Show this help
|
|||
/^[a-zA-Z0-9_-]+:.*?##/ { printf " \033[36m%-20s\033[0m %s\n", $$1, $$2 } \
|
||||
/^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) }' $(MAKEFILE_LIST)
|
||||
|
||||
.PHONY: check-tools check-sops k8s-server-dry-run apps-pg-status check-railiance01-kubeconfig check-inter-hub-image-tag check-inter-hub-image vergabe-dry-run vergabe-deploy vergabe-ingress-deploy vergabe-status vergabe-migrate vergabe-seed vergabe-superuser vergabe-logs vergabe-db-url-secret eso-deploy forgejo-openbao-eso-token-apply forgejo-openbao-store-deploy forgejo-mailer-es-deploy forgejo-mailer-es-status forgejo-dry-run forgejo-server-dry-run forgejo-deploy forgejo-ingress-deploy forgejo-ssh-nodeport-deploy forgejo-status forgejo-smoke forgejo-npm-smoke forgejo-verify forgejo-operator-bootstrap forgejo-runner-registration-sops-bootstrap forgejo-secrets-check forgejo-logs forgejo-runner-registration-deploy forgejo-runner-deploy forgejo-runner-status forgejo-runner-logs inter-hub-render-baseline inter-hub-dry-run inter-hub-server-dry-run inter-hub-deploy inter-hub-status inter-hub-release-info inter-hub-smoke inter-hub-logs reuse-dry-run reuse-deploy reuse-status reuse-smoke reuse-logs reuse-forgejo-webhook reuse-openbao-eso-token-apply reuse-openbao-store-deploy reuse-runtime-es-deploy reuse-runtime-es-status help
|
||||
.PHONY: check-tools check-sops k8s-server-dry-run apps-pg-status check-railiance01-kubeconfig check-inter-hub-image-tag check-inter-hub-image vergabe-dry-run vergabe-deploy vergabe-ingress-deploy vergabe-status vergabe-migrate vergabe-seed vergabe-superuser vergabe-logs vergabe-db-url-secret eso-deploy forgejo-openbao-eso-token-apply forgejo-openbao-store-deploy forgejo-mailer-es-deploy forgejo-mailer-es-status forgejo-dry-run forgejo-server-dry-run forgejo-deploy forgejo-ingress-deploy forgejo-ssh-nodeport-deploy forgejo-status forgejo-smoke forgejo-npm-smoke forgejo-verify forgejo-operator-bootstrap forgejo-runner-registration-sops-bootstrap forgejo-secrets-check forgejo-logs forgejo-runner-registration-deploy forgejo-runner-deploy forgejo-runner-status forgejo-runner-logs inter-hub-render-baseline inter-hub-dry-run inter-hub-server-dry-run inter-hub-deploy inter-hub-status inter-hub-release-info inter-hub-smoke inter-hub-logs reuse-dry-run reuse-deploy reuse-status reuse-smoke reuse-logs reuse-forgejo-webhook reuse-openbao-eso-token-apply reuse-openbao-store-deploy reuse-runtime-es-deploy reuse-runtime-es-status reuse-webhook-smoke help
|
||||
|
|
|
|||
|
|
@ -69,13 +69,18 @@ kubectl --kubeconfig ~/.kube/config-hosteurope rollout restart deployment/reuse-
|
|||
kubectl --kubeconfig ~/.kube/config-hosteurope rollout status deployment/reuse-surface -n reuse
|
||||
```
|
||||
|
||||
**Bootstrap / rotate** (update OpenBao fields — never commit or paste into chat):
|
||||
**Rotation** (never commit or paste values into chat):
|
||||
|
||||
Full procedure: `railiance-platform/docs/reuse-surface-runtime-secrets-rotation-runbook.md`
|
||||
|
||||
```bash
|
||||
# Operator: update both fields in OpenBao, then refresh ESO and roll the Deployment
|
||||
# After updating field(s) in OpenBao — force ESO sync, roll hub, reconcile webhook if needed
|
||||
kubectl --kubeconfig ~/.kube/config-hosteurope annotate externalsecret \
|
||||
reuse-surface-runtime -n reuse force-sync="$(date +%s)" --overwrite
|
||||
make reuse-runtime-es-status
|
||||
kubectl --kubeconfig ~/.kube/config-hosteurope rollout restart deployment/reuse-surface -n reuse
|
||||
make reuse-forgejo-webhook # required when rotating REUSE_SURFACE_FORGEJO_WEBHOOK_SECRET
|
||||
make reuse-forgejo-webhook # required when REUSE_SURFACE_FORGEJO_WEBHOOK_SECRET changed
|
||||
make reuse-webhook-smoke
|
||||
```
|
||||
|
||||
One-time lane bootstrap (CCR-2026-0005):
|
||||
|
|
@ -113,12 +118,10 @@ Requires a Forgejo admin API token at `/tmp/forgejo-tegwick-api-token` (or
|
|||
make reuse-forgejo-webhook
|
||||
```
|
||||
|
||||
Manual verification (unsigned body should return `401`, not `503`):
|
||||
Smoke (unsigned `401`, signed no-op `200`, ESO Ready, `/v1/federated`):
|
||||
|
||||
```bash
|
||||
curl -fsS -o /dev/null -w "%{http_code}\n" -X POST \
|
||||
https://reuse.coulomb.social/v1/webhooks/forgejo \
|
||||
-H "Content-Type: application/json" -d '{}'
|
||||
make reuse-webhook-smoke
|
||||
```
|
||||
|
||||
Live status (2026-07-07): org webhook id `1` on `coulomb` →
|
||||
|
|
|
|||
87
tools/reuse-webhook-smoke.sh
Executable file
87
tools/reuse-webhook-smoke.sh
Executable file
|
|
@ -0,0 +1,87 @@
|
|||
#!/usr/bin/env bash
|
||||
# Smoke checks for reuse-surface Forgejo webhook receiver (RAILIANCE-WP-0011-T04).
|
||||
# Uses the live cluster Secret for HMAC — never prints the secret value.
|
||||
set -euo pipefail
|
||||
|
||||
REUSE_URL="${REUSE_URL:-https://reuse.coulomb.social}"
|
||||
WEBHOOK_PATH="${REUSE_WEBHOOK_PATH:-/v1/webhooks/forgejo}"
|
||||
KUBECONFIG_PATH="${KUBECONFIG:-$HOME/.kube/config-hosteurope}"
|
||||
SECRET_NS="${REUSE_SECRET_NAMESPACE:-reuse}"
|
||||
SECRET_NAME="${REUSE_SECRET_NAME:-reuse-surface-env}"
|
||||
SECRET_KEY="${REUSE_WEBHOOK_SECRET_KEY:-REUSE_SURFACE_FORGEJO_WEBHOOK_SECRET}"
|
||||
|
||||
pass=0
|
||||
fail=0
|
||||
|
||||
ok() { echo " PASS $*"; pass=$((pass + 1)); }
|
||||
bad() { echo " FAIL $*" >&2; fail=$((fail + 1)); }
|
||||
|
||||
echo "reuse-surface webhook smoke — ${REUSE_URL}${WEBHOOK_PATH}"
|
||||
echo "kubeconfig: ${KUBECONFIG_PATH}"
|
||||
echo
|
||||
|
||||
unsigned_code="$(
|
||||
curl -sS -o /dev/null -w "%{http_code}" -X POST \
|
||||
"${REUSE_URL}${WEBHOOK_PATH}" \
|
||||
-H "Content-Type: application/json" -d '{}' 2>/dev/null || true
|
||||
)"
|
||||
unsigned_code="${unsigned_code:-000}"
|
||||
if [[ "${unsigned_code}" == "401" ]]; then
|
||||
ok "unsigned POST returns 401"
|
||||
else
|
||||
bad "unsigned POST expected 401, got ${unsigned_code}"
|
||||
fi
|
||||
|
||||
if kubectl --kubeconfig "${KUBECONFIG_PATH}" get externalsecret reuse-surface-runtime \
|
||||
-n "${SECRET_NS}" -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}' 2>/dev/null \
|
||||
| grep -q True; then
|
||||
ok "ExternalSecret reuse-surface-runtime Ready"
|
||||
else
|
||||
bad "ExternalSecret reuse-surface-runtime not Ready"
|
||||
fi
|
||||
|
||||
export REUSE_URL WEBHOOK_PATH KUBECONFIG_PATH SECRET_NS SECRET_NAME SECRET_KEY
|
||||
if python3 <<'PY'
|
||||
import base64, hashlib, hmac, json, os, subprocess, sys, urllib.error, urllib.request
|
||||
|
||||
reuse_url = os.environ["REUSE_URL"]
|
||||
webhook_path = os.environ["WEBHOOK_PATH"]
|
||||
kc = os.environ["KUBECONFIG_PATH"]
|
||||
ns = os.environ["SECRET_NS"]
|
||||
name = os.environ["SECRET_NAME"]
|
||||
key = os.environ["SECRET_KEY"]
|
||||
|
||||
raw = subprocess.check_output([
|
||||
"kubectl", "--kubeconfig", kc, "get", "secret", name, "-n", ns,
|
||||
"-o", f"jsonpath={{.data.{key}}}",
|
||||
], text=True)
|
||||
secret = base64.b64decode(raw).decode()
|
||||
payload = {"commits": [{"added": ["README.md"], "modified": [], "removed": []}]}
|
||||
body = json.dumps(payload).encode()
|
||||
sig = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
|
||||
req = urllib.request.Request(
|
||||
f"{reuse_url.rstrip('/')}{webhook_path}",
|
||||
data=body,
|
||||
headers={"Content-Type": "application/json", "X-Forgejo-Signature": sig},
|
||||
method="POST",
|
||||
)
|
||||
with urllib.request.urlopen(req, timeout=15) as resp:
|
||||
data = json.loads(resp.read().decode())
|
||||
if resp.status != 200 or data.get("accepted") is not False:
|
||||
raise SystemExit(f"unexpected response: {data!r}")
|
||||
PY
|
||||
then
|
||||
ok "signed no-op POST returns 200 accepted=false"
|
||||
else
|
||||
bad "signed no-op POST failed"
|
||||
fi
|
||||
|
||||
if curl -fsS "${REUSE_URL}/v1/federated" -o /dev/null 2>/dev/null; then
|
||||
ok "GET /v1/federated returns 200"
|
||||
else
|
||||
bad "GET /v1/federated failed"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "summary: ${pass} passed, ${fail} failed"
|
||||
[[ "${fail}" -eq 0 ]]
|
||||
Loading…
Add table
Add a link
Reference in a new issue