Skip to content

feat: match registry credentials by repository path [NOTASK] - #1165

Open
cristianrcv wants to merge 2 commits into
masterfrom
feat/NOTASK-registry-creds-by-repository
Open

cristianrcv wants to merge 2 commits into
masterfrom
feat/NOTASK-registry-creds-by-repository

Conversation

@cristianrcv

Copy link
Copy Markdown
Contributor

Problem

CredentialServiceImpl.findRegistryCreds picks the first Platform container-reg credential whose registry equals the image host (see the existing TODO @t0randr). When a workspace holds two credentials for the same host, the choice depends on list order. One example is a single ECR account where each repository has its own IAM user:

  • 056956104246.dkr.ecr.eu-west-2.amazonaws.com/gradient-dev/set-a
  • 056956104246.dkr.ecr.eu-west-2.amazonaws.com/gradient-dev/set-b

A push to set-a can then use set-b's IAM user and fail.

Change

  • Matching (CredentialServiceImpl): the lookup now receives the full repository (ContainerPath.repository) instead of the host. It picks the credential whose registry is the longest prefix of the repository, with prefixes compared on / boundaries. Bare-host credentials still match as before. On a tie, the first credential wins, as it did before.
  • Validation (RegistryAuthServiceImpl.validateUser): Platform validates a credential through Wave when it is saved. A path in registry is now stripped before login, and scheme URLs such as https://registry-1.docker.io are left unchanged.
  • Build config (ContainerInspectServiceImpl.credentialsConfigJson): the Docker config keeps one auth entry per host. Build and cache repositories are now added before base-image repositories, so the push target's credential takes that entry.

Known limitations

  • A build still gets one credential per host. A base image pulled from set-b while pushing to set-a on the same host uses set-a's credential. This comes from the Docker config format.
  • Platform's UI only accepts a bare host in the credential's registry field (HOST_NAME_ALLOW_PORTS in container-registry-credentials.component.ts). It needs a matching change before users can enter a path. The API already accepts any string.

Testing

  • New: CredentialsServiceTest "should find the most specific credentials" (8 cases) and RegistryAuthServiceTest "should strip repository path" (7 cases). Both pass.
  • RegistryCredentialsProviderTest is updated for the new signature and passes.
  • 6 tests in RegistryAuthServiceTest and ContainerInspectServiceImplTest fail locally. The same 6 fail on unmodified master; they need registry credentials or network access.
  • Local runs used -x installTypeSpec -x generateOpenApi -x generateSwaggerUI. Without those flags, installTypeSpec currently fails on master because npm install resolves @typespec/openapi@1.17.0 against the pinned compiler 1.16.0.

🤖 Generated with Claude Code

cristianrcv and others added 2 commits October 9, 2026 21:22
Wave picked the first Platform credential whose registry equalled the
image host. Two credentials on the same host (e.g. one ECR account with
per-repository IAM users) were therefore ambiguous and a push could use
the credentials of another repository and fail.

A credential registry can now include a repository path
(e.g. 123.dkr.ecr.eu-west-2.amazonaws.com/team/set-a). The lookup
receives the full repository and picks the credential with the longest
matching prefix, so bare-host credentials keep working as before.
Credential validation strips the path before logging in, and the build
docker config adds the push targets first so their credentials win the
single per-host entry.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
There is no lockfile, so npm resolved the @typespec/openapi peer of
@typespec/openapi3 to 1.17.0, which requires @typespec/compiler ^1.17.0
and conflicts with the pinned 1.16.0. installTypeSpec failed on every
build. Pinning the peer to the same release as the rest of the
toolchain restores the install.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@cristianrcv
cristianrcv requested review from munishchouhan, pditommaso and ramonamela and removed request for munishchouhan October 9, 2026 19:30
@cristianrcv
cristianrcv marked this pull request as ready for review October 9, 2026 19:30
@ramonamela

Copy link
Copy Markdown

LGTM and from the test looks like exactly what we want.
Nevertheless, somebody with more knowledge about the repository should look into it in order to see if the implementation is sound.

@pditommaso

Copy link
Copy Markdown
Collaborator

We need to align this with #278 and #224

@pditommaso

Copy link
Copy Markdown
Collaborator

Thanks @cristianrcv, this is the right direction. The matching rule (longest prefix of the repository at a / boundary, with an empty registry still meaning docker.io) is what #224 asks for, and it fixes the boundary and empty-registry problems that held back #278. Some points to address before merging:

1. Saving a credential with a path fails the SSRF check (blocker)
ValidateController.validateRegistry passes the raw value to SsrfValidator.validateHost. With SSRF protection on (wave.security.ssrf-protection.enabled, default true), quay.io/org fails DNS resolution and the request returns 400 before validateUser and stripRepositoryPath run. So Platform can't save a credential with a path. Please strip the path before the SSRF check (for example, apply stripRepositoryPath in validateRegistry). Please also add a controller test that posts a value with a path to /validate-creds and /v1alpha2/validate-creds with SSRF protection on.

2. Reject tags and digests in the registry value (#224)
#224 says the value can be a registry or a repository, but never a tag, e.g. docker.io/user/repo:xyz is not valid. Right now quay.io/org/repo:latest validates fine, because only the host is used for login, but it can never match a repository. Validation should reject a :tag or @digest after the host (a :port on the host is still allowed). It should also either reject or normalise a trailing /.

3. Validation only checks the host login
#224 left open whether a credential can be validated against a specific repository. This PR checks the login against the host only, so a credential saved for quay.io/org passes even if it has no access to org. That's acceptable for now, but please list it under Known limitations. A repository-scoped token request (scope=repository:<path>:pull) could be a follow-up for registries that support it.

4. Docker Hub library/ prefix
Images are normalised before matching, so ubuntu is matched as docker.io/library/ubuntu. A credential saved as docker.io/ubuntu never matches. Either normalise the credential value the same way, or document that official images need the library/ path.

5. Platform dependency
The Platform form still accepts only a bare host. Please link the Platform issue or PR that changes the field to "Registry or repository name" (#224). Until it ships, the feature is only reachable through the API. Since the Platform part is still open, use "Refs #224" rather than "Closes".

6. Housekeeping

This branch has not been deployed

No deployments
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.

3 participants