feat(auth): guard the GitLab API with a service token

What

/api/gitlab/v1 now authenticates by the ADR-020 per-edge service token in the Gitlab-Artifact-Registry-Token header (plan Step 2).

  • Dispatch row 7 (the interim Bearer guard) is removed. The central middleware passes the subtree through on its escaped path (ServeMux routes on the escaped form, so matching the decoded path would exempt %2F variants it routes elsewhere), and the guard mounts on the subtree itself (internal/auth/servicetoken).
  • Configured mode (auth.gitlab_api present): SHA-512 + constant-time validation via iam's pkg/serviceauth; the current and the optional rotation token are both accepted; a set-but-unresolvable ref aborts boot.
  • Fallback mode (block absent): a bearer credential checked by the deployment's configured validator, identity dropped, plus a startup warning. Step 4 removes the fallback before go-live.
  • Both modes answer rejections with the S01 envelope 401 and no WWW-Authenticate.
  • Rejection logging (a Warn per rejected credential, mode-mismatch attribution) lands separately in !1713 (merged), stacked on this branch, so this MR reviews as the guard alone. Until it merges, rejections here have no server-side log line.

The last commit records the review-found plan corrections (prefix-constant direction, Step 4's grown scope, the fallback-validator wording) and amends the S33 spec's fallback sentences.

Review notes

  • Transitional fallback: on a token_exchange deployment the fallback accepts any valid end-user AR token on this platform-internal surface. This is pre-existing dispatch-row-7 behavior, not widened here; the plan and spec now record it as a transitional ADR-020 deviation closed by Step 4. Staging runs bootstrap_token.
  • OpenAPI: documents the apiKey scheme with a transitional-fallback note. The credential swap is breaking inside v1 under docs/dev/api-style.md's catalog; no caller exists yet, which is why it lands now.
  • Diff size: ~1313 added lines, over the 500-LOC guideline: tests +809, internal/ production +182, cmd/ production +126, docs/OpenAPI/Bruno/run-recipe +190, go.mod/go.sum +6. The rejection-logging slice was split into !1713 (merged); the remainder is the plan's named atom — splitting further would merge either a middleware nobody mounts or a mount nobody guards, and 62% of what is left is tests.
  • E2E catalogs: no scenario added or affected. docs/testing/e2e/ covers the client-facing format surfaces; /api/gitlab/v1 is the platform-internal edge and has no catalog file.
  • driver.sh smoke: 24/24, including the two new GitLab API rows (guard 401 + handler 404).

Related to #356 (closed)

Edited by João Pereira

Merge request reports

Loading
Loading