Skip to content

cli/config: support wildcard patterns for registry hostnames in credHelpers and auths - #7328

Open
ytausch wants to merge 2 commits into
docker:masterfrom
ytausch:credentials-wildcard-hosts
Open

ytausch wants to merge 2 commits into
docker:masterfrom
ytausch:credentials-wildcard-hosts

Conversation

@ytausch

@ytausch ytausch commented Sep 23, 2026 •

Copy link
Copy Markdown

Summary

Allow keys in the credHelpers and auths sections of config.json to be wildcard patterns, so a single entry covers registries that encode an account, region, project, or channel in the hostname:

{
  "credHelpers": {
    "*.dkr.ecr.*.amazonaws.com": "ecr-login",
    "*-docker.pkg.dev": "gcloud"
  },
  "auths": {
    "*.docker.artifactory.example.com": { "auth": "..." }
  }
}

My own use case is the auths one: our Artifactory instance serves each channel on its own subdomain (<channel>.docker.artifactory.example.com), and today I have to docker login to every channel separately.

Matching rules

  • A * matches any sequence of characters within a single label and never matches a .. The pattern and the host must have the same number of labels, so abc.*.def.example.com matches abc.x.def.example.com but not abc.x.y.def.example.com, and *.example.com does not match example.com. A * can also be part of a label (*-docker.pkg.dev).
  • The port is part of the last label: *.example.com does not match foo.example.com:5000, but *.example.com:5000 does.
  • The last two labels must not contain a wildcard, so a pattern can't send credentials to an overly broad set of registries. This is due diligence rather than a complete safeguard: it doesn't know about multi-label public suffixes such as co.uk (so *.co.uk is accepted), and choosing sensible patterns is ultimately the user's responsibility.
  • Patterns must be a bare hostname, optionally with a port, without scheme or path. Docker only looks up credentials by host, so a path in a pattern (*.example.com/team) could never mean what it appears to. A scheme isn't needed; wildcard keys are written by hand and never come from the legacy https://host keys that old docker login versions wrote.
  • Invalid patterns are rejected, not ignored. Any auths or credHelpers key containing a * that isn't a valid pattern makes ConfigFile.LoadFromReader return an error listing each offending key, for example:
    WARNING: Error parsing config file (~/.docker/config.json): auths: invalid registry pattern "*.com": wildcards are not allowed in the last two labels
    credHelpers: invalid registry pattern "foo.*.com": wildcards are not allowed in the last two labels
    
    The docker CLI loads its config through LoadDefaultConfigFile, which (as for any other config error) prints this warning and continues; the invalid entries are never used for matching. Library consumers calling config.Load/LoadFromReader get the error.
  • Only * is special. I deliberately didn't use path.Match/filepath.Match: they also treat ?, [ and \ as special characters, and [ appears in IPv6 literals such as [::1]:5000.
  • Keys without a * are not validated or changed in any way, so the Docker Hub key (https://index.docker.io/v1/) and legacy URL keys are unaffected.

Precedence

  1. exact credHelpers entry
  2. most specific matching credHelpers pattern
  3. credsStore
  4. file store: exact key, then the existing legacy URL-normalisation fallback, then the most specific matching auths pattern

"Most specific" means the pattern with the most non-wildcard characters. Ties are broken by lexical order so the result is deterministic despite map iteration order.

When an auths pattern matches, the returned AuthConfig.ServerAddress is the requested host, not the pattern.

GetAllCredentials skips wildcard credHelpers keys. Without that, it would invoke the helper with the literal pattern (e.g. docker-credential-ecr-login get for *.dkr.ecr.*.amazonaws.com).

Implementation

  • New internal package cli/config/internal/hostmatch (Validate, IsPattern, Match, Best). It's internal so this doesn't add public API; cli/config only imports its own sub-packages, and this keeps it that way.
  • credentials.fileStore.Get falls back to the best matching auths pattern.
  • configfile.getConfiguredCredentialStore falls back to the best matching credHelpers pattern.
  • configfile.LoadFromReader validates all auths and credHelpers keys containing a *.
  • Docs: new "Wildcard registry patterns" section in docs/reference/commandline/login.md.

Compatibility

  • Existing configs: hostnames can't contain *, so no existing working entry changes meaning, and exact matches always win. Wildcard keys already exist in the wild, though: kubelet has long supported them in image pull secrets, with the same per-label semantics. Configs like that, reused as a ~/.docker/config.json (e.g. in CI), will start matching. Such configs that use patterns this PR considers invalid (e.g. *.com, or a scheme like https://*.example.com, which kubelet accepts) will now produce a config-parsing warning in the docker CLI and an error for config.Load callers, where the key was previously ignored silently.
  • Older docker CLI versions / tools that vendor an older cli/config (go-containerregistry/crane, buildx, compose, nerdctl): they ignore pattern keys during lookup. For credHelpers patterns, an older GetAllCredentials logs a warning after the helper fails for the literal pattern.
  • containers/image (podman, skopeo, buildah) has its own parser. auths patterns are ignored on lookup. credHelpers patterns are the rough edge: its GetAllCredentials calls the helper with the literal pattern and returns an error if the helper fails. That's something to be aware of when sharing a config with those tools.

Open questions for reviewers

  1. How are auths patterns created? docker login must authenticate against a real host, so docker login '*.example.com' can't work. For now the documented workflow is: log in to one registry, then rename its key to the pattern. A follow-up could add a way for docker login to store under a pattern.
  2. Shadowing on re-login. Once a pattern entry exists, docker login foo.example.com authenticates with the matched credentials and then stores them under the exact key foo.example.com. That exact entry then shadows the pattern, e.g. after a password rotation. Is that acceptable, or should login write back to the matching pattern?
  3. docker logout foo.example.com only erases exact entries, so credentials matched through a pattern stay in place. Should logout mention that?
  4. Invalid patterns (e.g. *.com) are rejected when the config is loaded (see above). Is failing LoadFromReader the right level, given that config.Load callers get a hard error? The alternative would be to only warn in the CLI.
  5. DOCKER_AUTH_CONFIG (memorystore) doesn't support patterns in this PR.
  6. Path-namespaced matching (the other half of Feature Request: Support wildcards in credHelpers config #2928) is out of scope.

Testing

  • Unit tests for the matcher (Validate, Match, Best), for the file store (TestFileStoreGetWildcard), and for the config file (TestGetConfiguredCredentialStoreWildcard, TestGetAllCredentialsSkipsWildcardCredHelpers, TestLoadFromReaderInvalidRegistryPatterns, TestLoadFromReaderValidRegistryPatterns). I checked that the matching tests fail without the source changes.
  • Manual check: with only *.docker.localhost:5055 in auths, docker login channel-a.docker.localhost:5055 reports "Authenticating with existing credentials... [Username: ytausch]", while x.y.docker.localhost:5055 finds no credentials. Adding *.com and foo.*.com entries prints the warning shown above.

Release notes (optional)

Allow wildcard patterns such as `*.example.com` as registry keys in the `credHelpers` and `auths` sections of the CLI config file.

AI Note

I was assisted by Claude Code (Opus 5.5) in creating this PR but also did a manual pass-through.

Allow keys in the "credHelpers" and "auths" sections of the CLI config
file to be wildcard patterns, such as "*.dkr.ecr.*.amazonaws.com" or
"*.docker.artifactory.example.com", so that a single entry can be used
for registries that encode an account, region, or project in their
hostname.

A "*" matches any sequence of characters within a single hostname label,
and never matches a ".". The last two labels of a pattern must not
contain a wildcard, so that patterns such as "*.com" cannot be used to
send credentials to an overly broad set of registries.

Exact matches always take precedence over patterns. If multiple patterns
match, the most specific one (the one with the most non-wildcard
characters) is used, and ties are broken by lexical order to make the
result deterministic.

Wildcard patterns in "credHelpers" are skipped by GetAllCredentials, as
they are not registry hostnames that can be looked up in the helper.

Signed-off-by: Yannik Tausch <dev@ytausch.de>
Instead of silently ignoring keys in the "auths" and "credHelpers"
sections that contain a "*" wildcard but are not valid patterns (for
example, "*.com", "foo.*.com", or "https://*.example.com"), return an
error from ConfigFile.LoadFromReader that lists each invalid key.

The docker CLI prints this error as a warning when loading the config
file, as it does for other config errors; invalid patterns are never
used for matching.

Signed-off-by: Yannik Tausch <dev@ytausch.de>
@ytausch
ytausch marked this pull request as ready for review September 23, 2026 17:37
@ytausch
ytausch requested review from a team and thaJeztah as code owners September 23, 2026 17:37
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.

1 participant