Skip to content

feat(docker): auto-resolve registry auth from ~/.docker/config.json#257

Merged
jarlah merged 2 commits intomainfrom
fix/182-registry-auth
Apr 22, 2026
Merged

feat(docker): auto-resolve registry auth from ~/.docker/config.json#257
jarlah merged 2 commits intomainfrom
fix/182-registry-auth

Conversation

@jarlah
Copy link
Copy Markdown
Member

@jarlah jarlah commented Apr 22, 2026

Summary

  • Adds Testcontainers.Docker.Auth.resolve/2, which reads ~/.docker/config.json (respecting DOCKER_CONFIG), looks up credentials for the target image's registry, and returns a ready-to-send X-Registry-Auth header value (URL-safe base64 of JSON with username, password, serveraddress, per the Docker Engine API spec).
  • Wires auto-resolution into Testcontainers.maybe_pull_image/2 via a pull_with_fallback/2 helper: when Container.auth is not set, auto-resolution is attempted; explicit Container.auth still takes precedence.
  • serveraddress is normalized to a bare host (scheme and trailing path stripped; index.docker.io canonicalized to docker.io). This matches the Docker Engine API spec and fixes a 400 from podman, which rejects full-URL serveraddress values.
  • If auto-resolved credentials cause the pull to fail with an HTTP 4xx, the pull is retried once without the header. Stale or unrelated entries in ~/.docker/config.json (common on shared CI runners) therefore can't break pulls that would otherwise succeed anonymously. Explicit Container.auth is never retried — failures there stay failures.
  • Missing / invalid config files are handled silently at debug level so this feature never breaks existing pull flows.
  • Scope: auths entries only. credsStore / credHelpers are intentionally out of scope for this PR — if encountered we log at debug level and fall back to anonymous, rather than shelling out to a helper binary. Support for those can be added in a follow-up.

Fixes #182

Test plan

  • mix compile
  • mix credo --strict
  • mix dialyzer
  • mix test — 205 tests, 0 failures
  • test/docker/auth_test.exs covers:
    • Docker Hub image resolves to the docker.io entry with a scheme-free, path-free serveraddress (regression test)
    • Private-registry image resolves to the matching host entry
    • Unknown registry / missing file / invalid JSON all return nil
    • Header is URL-safe base64 without padding (matches Docker Engine API spec)
    • normalize_server_address/1 strips schemes and paths, canonicalizes index.docker.iodocker.io
  • Manual smoke test against a real private registry (not covered by automated tests to avoid requiring live credentials).

jarlah added 2 commits April 22, 2026 08:28
Pulling images from private registries previously failed with HTTP 407
because the library never looked at the user's Docker credentials. Align
with other Testcontainers implementations by reading `~/.docker/config.json`
(honouring `DOCKER_CONFIG`) and passing an `X-Registry-Auth` header on
image pulls when no explicit auth is configured on the container.

Only the `auths` map is consulted. Credential helpers (`credsStore`,
`credHelpers`) are intentionally out of scope for this change: when
encountered they produce a debug log and resolution falls back to
anonymous access so the pull can still proceed (or fail as before) rather
than shelling out to a helper binary.

The header value is URL-safe base64 (without padding) of a JSON payload
with `username`, `password`, and `serveraddress`, per the Docker Engine
API spec. Explicitly configured `Container.auth` continues to take
precedence over auto-resolution, and missing/invalid config files are
handled silently at debug level.
Podman rejects an X-Registry-Auth header whose `serveraddress` is a full
URL (e.g. `https://index.docker.io/v1/`) with HTTP 400. The Docker Engine
API spec calls for a bare domain/IP (optionally with port). Normalize the
value in `Testcontainers.Docker.Auth.build_header/2` by stripping the
scheme and any trailing path, and canonicalize `index.docker.io` to
`docker.io` to match the Docker CLI.

When auth is auto-resolved from the user's `~/.docker/config.json`
(rather than set explicitly on the container config) and the pull fails
with a 4xx, retry once without the header. This protects pulls against
stale or otherwise invalid credentials that happen to sit in the config
file on shared environments such as CI runners, where they would
previously have broken the first user-visible pull.

Fixes a regression seen on the rootless-podman CI job.
@jarlah jarlah merged commit 7d0f0bb into main Apr 22, 2026
8 checks passed
@jarlah jarlah deleted the fix/182-registry-auth branch April 22, 2026 07:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Getting an HTTP 407 error, testcontainers unable to auth to docker?

1 participant