Gate local repo writes behind OIDC tokens #169

Merged
benvin merged 2 commits from benvin/upload-write-auth into master 2026-10-25 15:17:34 +11:00
Member

Local repo writes (file uploads/deletes, object evictions, native publishes, docker push) are open to anyone who can reach the service; only remote/virtual config is token-gated.

  • add ARTIFACTAPI_AUTH_UPLOADS (default false) gating every local write path behind the OIDC verifier
  • accept the token as Bearer, Token, Basic password (any user) or bare header value on every gated path, including remote/virtual config writes
  • serve a /v2/token Bearer challenge flow so docker login with the token enables push; missing, expired or invalid credentials get an anonymous token so pulls stay open
  • document minting a Vault identity token and passing it per client
Local repo writes (file uploads/deletes, object evictions, native publishes, docker push) are open to anyone who can reach the service; only remote/virtual config is token-gated. - add `ARTIFACTAPI_AUTH_UPLOADS` (default false) gating every local write path behind the OIDC verifier - accept the token as Bearer, Token, Basic password (any user) or bare header value on every gated path, including remote/virtual config writes - serve a `/v2/token` Bearer challenge flow so `docker login` with the token enables push; missing, expired or invalid credentials get an anonymous token so pulls stay open - document minting a Vault identity token and passing it per client
unkin-agent added 1 commit 2026-10-25 13:24:46 +11:00
Gate local repo writes behind OIDC tokens
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/build Pipeline was successful
32da0fd750
Add ARTIFACTAPI_AUTH_UPLOADS to require a verified token for local
uploads, deletes, evictions, /api/v1 publishes and /v2 push. Accept the
token as Bearer, Token, Basic password or bare header, and serve a
Docker registry token endpoint so pulls stay anonymous.
Author
Member
  • nit: internal/auth/oidc.go:144 — token() is shared with Require, so Basic and bare-header credentials are now also accepted on /api/v2/remotes and /virtuals config writes, which the why does not mention → keep config writes Bearer-only, or state the widening in the body.
  • nit: internal/auth/oidc.go:170 — /v2/token with any Authorization header that is not a valid JWT returns 401, so a pull from a client with unrelated stored creds for this host fails instead of falling back to the anonymous token → on verify failure for /v2/token, issue the anonymous token (writes stay gated by writesOnly), or document the behaviour.
- nit: internal/auth/oidc.go:144 — `token()` is shared with `Require`, so Basic and bare-header credentials are now also accepted on `/api/v2/remotes` and `/virtuals` config writes, which the why does not mention → keep config writes Bearer-only, or state the widening in the body. - nit: internal/auth/oidc.go:170 — `/v2/token` with any Authorization header that is not a valid JWT returns 401, so a pull from a client with unrelated stored creds for this host fails instead of falling back to the anonymous token → on verify failure for `/v2/token`, issue the anonymous token (writes stay gated by `writesOnly`), or document the behaviour.
unkin-agent added 1 commit 2026-10-25 13:36:35 +11:00
Issue anonymous registry token for rejected credentials
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/build Pipeline was successful
61cb4c872c
Author
Member
  • nit: internal/auth/oidc.go:writeToken — /v2/token echoes a long-lived JWT with no Cache-Control → set Cache-Control: no-store (and Pragma: no-cache) on the response.
  • nit: internal/server/server.go:177 — challenge realm is built from client-supplied r.Host / X-Forwarded-Proto, and docker sends the Basic credential to that realm → build the realm from a configured external URL, or confirm the ingress pins Host.
- nit: internal/auth/oidc.go:writeToken — `/v2/token` echoes a long-lived JWT with no `Cache-Control` → set `Cache-Control: no-store` (and `Pragma: no-cache`) on the response. - nit: internal/server/server.go:177 — challenge realm is built from client-supplied `r.Host` / `X-Forwarded-Proto`, and docker sends the Basic credential to that realm → build the realm from a configured external URL, or confirm the ingress pins Host.
benvin merged commit f0ee593164 into master 2026-10-25 15:17:34 +11:00
benvin deleted branch benvin/upload-write-auth 2026-10-25 15:17:35 +11:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: unkin/artifactapi#169