From d0920002a6633287fd7960025f6a166cd8b84eda Mon Sep 17 00:00:00 2001 From: Louis Parkin Date: Wed, 5 Aug 2026 09:20:32 +0200 Subject: [PATCH] STAC-25529: send Cerberus bearer token from the notify workflow StackVista/cerberus#4 (STAC-24889) adds authentication to the Cerberus Lambda: every non-Slack request must carry Authorization: Bearer constant-time compared in internal/auth/auth.go verifyBearer. That PR is still open, so the endpoint is unauthenticated today and the workflow merged in STAC-25519 works. The moment cerberus#4 deploys, the unauthenticated POST gets a 401, curl --fail turns the notify job red, and no Slack message is sent -- reinstating exactly the silent master-failure condition STAC-25519 removed, and which hid the 12-day image-publishing outage in STAC-25510. Sending the header is forward-compatible: the deployed Lambda ignores unknown headers, so this is safe to merge before cerberus#4. CERBERUS_API_TOKEN already exists as a repo-level secret alongside CERBERUS_LAMBDA_URL, so no pulumi-infra change is needed. It is declared `required: false` for the same reason as the URL -- passing `${{ secrets.X }}` for a secret the repo lacks yields an empty string, which GitHub rejects against a *required* secret and fails the call before the guard can warn. The guard now covers both, so a missing secret still warns and exits 0 rather than adding a second red job to an already failed run. Also drops `curl --verbose`, which echoes request headers -- the Authorization header now carries the shared token. GitHub would mask it, but not emitting it beats relying on masking. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/cerberus-notify.yml | 33 +++++++++++++++++++-------- .github/workflows/ci.yml | 1 + 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/.github/workflows/cerberus-notify.yml b/.github/workflows/cerberus-notify.yml index 4f68cc76..690613f6 100644 --- a/.github/workflows/cerberus-notify.yml +++ b/.github/workflows/cerberus-notify.yml @@ -22,12 +22,20 @@ name: Cerberus notify # deleted with the GitLab files, was built around GitLab's CI_* variables, and # predates the `platform` field. # -# Prerequisite: CERBERUS_LAMBDA_URL must reach this repo as a REPO-level secret. -# The org-level copy is visibility=private, which excludes this PUBLIC repo, and -# widening it is not an option: the Cerberus endpoint is unauthenticated, so the -# URL is the whole capability. Until the pulumi-infra change applies, this -# workflow warns and exits 0 rather than adding a second red job to an already -# failed run -- the annotation is the signal. +# Prerequisites: CERBERUS_LAMBDA_URL and CERBERUS_API_TOKEN must both reach this +# repo as REPO-level secrets. The org-level copies are visibility=private, which +# excludes this PUBLIC repo. pulumi-infra provisions the pair together +# (github/repoVariables/resources.yaml). If either is missing this workflow warns +# and exits 0 rather than adding a second red job to an already failed run -- the +# annotation is the signal. +# +# The bearer token is not optional going forward. StackVista/cerberus#4 +# (STAC-24889) adds `Authorization: Bearer ` verification +# to every non-Slack request; before it, the endpoint was entirely +# unauthenticated. Sending the header is forward-compatible -- the currently +# deployed Lambda ignores unknown headers -- so this works either side of that +# deploy. Without it, the first master failure after cerberus#4 ships would get a +# 401 and no Slack message. # # The Slack channel is deliberately not sent. Cerberus resolves it as # `util.GetOrDefault(req.Context, "channel", s.Channel)`, and GetOrDefault treats @@ -48,6 +56,8 @@ on: # step's guard can warn -- the failure mode this workflow exists to avoid. CERBERUS_LAMBDA_URL: required: false + CERBERUS_API_TOKEN: + required: false # Nothing here reads the repository; the payload is built entirely from the # github context. @@ -62,6 +72,7 @@ jobs: - name: Post the failure to Cerberus env: CERBERUS_LAMBDA_URL: ${{ secrets.CERBERUS_LAMBDA_URL }} + CERBERUS_API_TOKEN: ${{ secrets.CERBERUS_API_TOKEN }} REPOSITORY: ${{ github.repository }} BRANCH: ${{ github.ref_name }} PIPELINE: ${{ github.run_id }} @@ -71,16 +82,20 @@ jobs: run: | set -euo pipefail - if [ -z "${CERBERUS_LAMBDA_URL}" ]; then - echo "::warning title=Cerberus not configured::CERBERUS_LAMBDA_URL is not visible to this repo, so the ${SUITE} failure was not reported to Slack. Needs the repo-level secret from pulumi-infra (STAC-25519)." + if [ -z "${CERBERUS_LAMBDA_URL}" ] || [ -z "${CERBERUS_API_TOKEN}" ]; then + echo "::warning title=Cerberus not configured::CERBERUS_LAMBDA_URL and/or CERBERUS_API_TOKEN is not visible to this repo, so the ${SUITE} failure was not reported to Slack. Both are provisioned as repo-level secrets by pulumi-infra (STAC-25519)." exit 0 fi COMMIT_TITLE=$(printf '%s' "${COMMIT_MESSAGE}" | head -n1) - curl --verbose --fail \ + # Not --verbose: it echoes request headers, and the Authorization + # header carries the shared token. GitHub would mask it, but not + # emitting it is better than relying on masking. + curl --fail --silent --show-error \ -X POST "${CERBERUS_LAMBDA_URL}" \ -H "Content-Type: application/json" \ + -H "Authorization: Bearer ${CERBERUS_API_TOKEN}" \ -d "$(jq -n \ --arg repo "${REPOSITORY}" \ --arg branch "${BRANCH}" \ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6879fa09..de1f801e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -348,3 +348,4 @@ jobs: suite: build secrets: CERBERUS_LAMBDA_URL: ${{ secrets.CERBERUS_LAMBDA_URL }} + CERBERUS_API_TOKEN: ${{ secrets.CERBERUS_API_TOKEN }}