Skip to content

ci: read the Docker Hub credential from Secrets Manager via OIDC - #744

Merged
robinnsc merged 1 commit into
mainfrom
ci/dockerhub-oidc-secrets
Nov 2, 2026
Merged

robinnsc merged 1 commit into
mainfrom
ci/dockerhub-oidc-secrets

Conversation

@LeeroyHannigan

@LeeroyHannigan LeeroyHannigan commented Nov 2, 2026 •

Copy link
Copy Markdown
Collaborator

What

The five workflows that log in to Docker Hub (release-image, release-dev-image, promote-image, sign-image, dockerhub-description) now read the Docker Hub username and token at run time from AWS Secrets Manager, fetched through a GitHub OIDC role, instead of from the DOCKERHUB_USERNAME and DOCKERHUB_TOKEN GitHub Actions secrets.

Each Docker Hub login is now preceded by two steps: assume a dedicated OIDC role with aws-actions/configure-aws-credentials (pinned to a commit SHA), then read the publishing secret with aws-actions/aws-secretsmanager-get-secrets (pinned, parse-json-secrets: true), which exports DOCKERHUB_USERNAME and DOCKERHUB_TOKEN for the login step that follows.

The role's trust is limited to the dockerhub GitHub environment, the same gate the existing signing role uses, and its only permission is reading that one secret. promote-image and dockerhub-description gain id-token: write on the job that needs it; the three signing workflows already had it.

Why

The Docker Hub token is a read and write credential for the primary distribution registry, and GHCR and ECR Public are mirrored from it. As a GitHub Actions secret it is a standing credential that lives outside the release pipeline's own controls. Reading it from Secrets Manager through a short-lived OIDC role takes the standing secret out of GitHub and scopes access to one environment and one secret.

No change to publishing behaviour

The login steps authenticate with the same username and token values, now sourced from Secrets Manager. Image namespaces, tags, signing, and promotion logic are unchanged.

Prerequisite (already in place)

The Secrets Manager secret and the OIDC role already exist in the release account.

Follow-up, out of band after this merges and a release verifies

  • Delete the DOCKERHUB_USERNAME and DOCKERHUB_TOKEN GitHub Actions secrets.
  • Rotate the Docker Hub token.

Testing

yaml.safe_load parses all five workflows, and no secrets.DOCKERHUB_* reference remains. The publish paths cannot run without the reviewer-gated dockerhub environment, so the first release dispatch after merge is the functional check.

The release-image, release-dev-image, promote-image, sign-image and
dockerhub-description workflows now fetch the Docker Hub username and
token from AWS Secrets Manager (extenddb/dockerhub-publish) through the
ExtendDBDockerHubPublisher OIDC role, instead of the DOCKERHUB_USERNAME
and DOCKERHUB_TOKEN GitHub Actions secrets. The role is gated to the
dockerhub environment and can read only that one secret. promote-image
and dockerhub-description gain id-token: write on the job that needs it.

@robinnsc robinnsc left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. All five Docker Hub logins go through the new role, and no secrets.DOCKERHUB_* reference is left. Every job that assumes the role runs in the dockerhub environment, and with the repo's default OIDC subject that is the same repo:ExtendDB/extenddb:environment:dockerhub condition ExtendDBContainerSigningCI already relies on. In each job the signing or ECR role is assumed after the Docker Hub one. configure-aws-credentials v6.2.3 decides on OIDC from its own inputs rather than from credentials already in the environment, so the second role assumption is unaffected, and nothing after the Docker Hub role in these jobs needs other AWS credentials (promote-registry verifies against the public key file). The get-secrets pin is the v3.0.2 tag commit. It also merges cleanly with #725, so the Cassandra image picks up the same login path.

The one thing I'd check on the account side is the secret's JSON key names. With the DOCKERHUB alias and parse-json-secrets, the keys have to be username and token (any case) to come out as DOCKERHUB_USERNAME and DOCKERHUB_TOKEN; a key named password, for example, would leave the login with an empty value. Since the publish paths can't run before merge, it might be worth dispatching dockerhub-description for one image right after merge, rather than waiting for the first release. That exercises the role, the secret, the login and the Docker Hub API without spending a release approval. Once that run passes, the DOCKERHUB_TOKEN and DOCKERHUB_USERNAME secrets still in the dockerhub environment could be deleted and the token rotated then, rather than after a release.

uses: aws-actions/aws-secretsmanager-get-secrets@2297f9a879480a9e3af9b293ed15c70caf8e1c88 # v3.0.2
with:
secret-ids: DOCKERHUB,extenddb/dockerhub-publish
parse-json-secrets: true

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A small one: the action exports the values to GITHUB_ENV, so DOCKERHUB_TOKEN is now in the environment of every later step in the job, not just the login. Before this, promote-image, sign-image and dockerhub-description passed the token only to their login step, and here promote-registry, skopeo and (in sign-image) the signing-role step and cosign can now read it. The values are masked in logs and the action clears them in its post step, so this isn't a blocker. If you wanted to keep it step-scoped, one option would be to read the secret inside the login step itself, e.g. aws secretsmanager get-secret-value --secret-id extenddb/dockerhub-publish --query SecretString --output text piped through jq into --password-stdin, with an ::add-mask:: for the username. The same applies to the other four workflows.

@robinnsc
robinnsc added this pull request to the merge queue Nov 2, 2026
Merged via the queue into main with commit e3a855a Nov 2, 2026
28 checks passed
@robinnsc
robinnsc deleted the ci/dockerhub-oidc-secrets branch November 2, 2026 19:39
LeeroyHannigan added a commit that referenced this pull request Nov 2, 2026
Main gained #335, #744, #751 and #752. #335 drops the continuous_backups
table on every backend and moves the 0.1.x catalog to 0.0.5. On SQLite and
PostgreSQL the drop is now migration 5, drop_continuous_backups, with a file
body on each backend. Main's 004_drop_continuous_backups.sql, its
CATALOG_VERSION constants, catalog_converge.rs and the SQLite bootstrapper
test it changed stay removed, as this branch removed them.

With migration 5 in the registry, docs/schema loses the table, the released
SQLite catalog test expects the table gone after the upgrade and after a
reconverge, the CLI migration test checks the same after init, an upgrade
from 0.1.10 and a reconverge, the reconverge check in
devtools/check-postgres-catalog-lock expects migration 5 to run again, the
docs that name the current migration say 5, and the CONTRIBUTING example
becomes migration 6.

integration.yml takes #752's pinned checkout with this branch's
fetch-depth: 0. The upgrade manual keeps this branch's ledger sections,
gains main's catalog 0.0.5 entry and its v0.1.15 behavior change, each with
a note that migration 5 makes the change from v1.0, and names Cassandra's
catalog as 0.0.5. ADR-0023 counts five breaking migrations and says which
catalog its v0.1.14 quote was measured on. The architecture guide and the
SQLite design notes describe the ledger instead of a compiled version.
@navareamol navareamol added security v1 Needed for the 1.0 release release-blocker Must ship for its milestone's release labels Nov 3, 2026
@navareamol navareamol added this to the 1.0 milestone Nov 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release-blocker Must ship for its milestone's release security v1 Needed for the 1.0 release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants