Skip to content

Add support for Docker's credential stores and helpers #45269

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from

Conversation

nosan
Copy link
Contributor

@nosan nosan commented Apr 23, 2025

I left TODO in DefaultDockerRegistryAuthentication for consideration.

I didn't have time to update the documentation.

See #44633

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Apr 23, 2025
@nosan nosan changed the title Improve Docker registry authentication handling Add support for Docker's credential stores and helpers Apr 23, 2025
@nosan nosan force-pushed the 44633 branch 3 times, most recently from 7e7684d to 0990c9a Compare April 24, 2025 08:11
This commit includes Docker CLI default authentication, leveraging
credential helpers, the credential store, and static credentials.

Previously, empty credentials were used by default. With this commit,
a DefaultDockerRegistryAuthentication is introduced. It reads the Docker
configuration file and, based on its contents, determines the appropriate
authentication details for the requested ImageReference

Signed-off-by: Dmytro Nosan <dimanosan@gmail.com>
nosan added 2 commits April 24, 2025 13:03
Signed-off-by: Dmytro Nosan <dimanosan@gmail.com>
Signed-off-by: Dmytro Nosan <dimanosan@gmail.com>
@nosan
Copy link
Contributor Author

nosan commented Apr 24, 2025

@philwebb
I'm not sure if you've started reviewing or working on this PR yet, but just as an FYI, I've pushed a few additional polish commits (I did not squash them).

Signed-off-by: Dmytro Nosan <dimanosan@gmail.com>
@philwebb
Copy link
Member

Thanks, I've started and I'm hoping to finish something before RC1 but it's going to be tight :)

philwebb pushed a commit that referenced this pull request Apr 25, 2025
Update `DockerConfigurationMetadata` with support for `credsStore`,
`credHelpers` and `auth` sections. These values will be required to
support credential helper based authentication.

See gh-45269

Signed-off-by: Dmytro Nosan <dimanosan@gmail.com>
philwebb added a commit that referenced this pull request Apr 25, 2025
Add `DockerRegistryAuthentication` implementation that uses standard
Docker config to authenticate requests.

Prior to this commit, we only supported username/password and token
based authentication. This commit allows authentication based on
the contents of the Docker configuration file, including support
for executing credential helpers.

See gh-45269

Signed-off-by: Dmytro Nosan <dimanosan@gmail.com>
Co-authored-by: Phillip Webb <phil.webb@broadcom.com>
philwebb added a commit that referenced this pull request Apr 25, 2025
Update the Maven and Gradle plugins to make use of the new Docker
configuration authentication support.

See gh-45269

Signed-off-by: Dmytro Nosan <dimanosan@gmail.com>
Co-authored-by: Phillip Webb <phil.webb@broadcom.com>
@philwebb philwebb closed this in e50da4f Apr 25, 2025
@philwebb
Copy link
Member

Thanks very very very much @nosan! This one was very timely and I'm super glad to get it into RC1.

I've did a little work on refactoring some of the platform code then I've rebased and split up your PR into more granular commits. Some of the PR was harder to apply due to my refactoring so I've combined a bit of your original PR with some updates that I made and made a single commit with us both as authors. I hope that's OK.

For the TODOs:

  • I've kept the cache because I found with some manual testing I got an exception from the helper. I figured if the helper does a REST call and it's rate limited the cache would help. I don't think we need to worry about busting the cache because the build process is pretty short lived.

  • I don't think ImageReference should provide the serverUrl. That feels like a helper concern.

  • I aligned with Testcontainers on helper being empty

  • For the errors, I added a callback so we can log properly with Maven

I've tested things manually on both Mac and Windows and things all seem to work very well.

Thanks again for the PR!

@philwebb philwebb added type: enhancement A general enhancement and removed status: waiting-for-triage An issue we've not yet triaged labels Apr 25, 2025
@philwebb philwebb added this to the 3.5.0-RC1 milestone Apr 25, 2025
@nosan
Copy link
Contributor Author

nosan commented Apr 25, 2025

Thank you so much for taking the time to include this in 3.5.0! That is awesome!

I aligned with Testcontainers on the helper being empty.

I'm not entirely sure it should align with Testcontainers. I tested the Docker CLI with an empty helper, and it appears that an empty helper takes precedence over the credential store.

The algorithm should be as follows:

  1. Check if a helper exists. (map.contains(...))
  2. If it exists and has a value!="", use it.
  3. If the value is empty, skip it and fall back to using static credentials.

I believe Spring Boot should follow the same behavior as the Docker CLI to avoid inconsistencies between the two. Users are more likely to compare Spring Boot's approach to the Docker CLI rather than Testcontainers. For this reason, I used the Docker CLI as the reference implementation.

@philwebb
Copy link
Member

That would be this logic I guess in the updated code? Is the error in using the getCredsStore? We might have to treat it as a bug and fix it after RC1 at this point.

@nosan
Copy link
Contributor Author

nosan commented Apr 25, 2025

Is the error in using the getCredsStore

Nothing, Docker CLI treats it as a helper.


That would be this logic I guess in the updated code?

This should be:

String name = this.dockerConfig.getCredHelpers().getOrDefault(serverUrl, this.dockerConfig.getCredsStore());

to align with Go code from Docker CLI:

func getConfiguredCredentialStore(c *ConfigFile, registryHostname string) string {
	if c.CredentialHelpers != nil && registryHostname != "" {
		if helper, exists := c.CredentialHelpers[registryHostname]; exists {
			return helper
		}
	}
	return c.CredentialsStore
}

@philwebb philwebb reopened this Apr 25, 2025
philwebb added a commit that referenced this pull request Apr 25, 2025
@philwebb
Copy link
Member

Thanks, hopefully fixed in 79f7529

@philwebb philwebb closed this Apr 25, 2025
@nosan
Copy link
Contributor Author

nosan commented Apr 25, 2025

Thanks, @philwebb

//Builder.ImageFetcher
String authHeader = authHeader(this.registryAuthentication, reference);
			Assert.state(authHeader == null || reference.getDomain().equals(this.domain),
					() -> String.format("%s '%s' must be pulled from the '%s' authenticated registry",
							StringUtils.capitalize(type.getDescription()), reference, this.domain));
						

If someone tries to use a BUILDER image from a public registry and a RUNNER image from a private registry both with the default authentication, an exception is thrown because the domains are different. My understanding is that this check was originally added to ensure the credentials provided by the user are consistent for both the builder and runner images. With the new configuration strategy, this is no longer valid as the credentials can now differ.

@philwebb
Copy link
Member

Yes, I just hit that one as well as a bunch of test failures. I really messed up the merge of this one 😭! I'm trying to fix it up now.

philwebb added a commit that referenced this pull request Apr 25, 2025
Attempt to fix a few issues that were accidentally introduced
by missing some code from the original pull-request.

See gh-45269
@nosan
Copy link
Contributor Author

nosan commented Apr 25, 2025

I really messed up the merge of this one

I don't think so! You have done an incredible piece of work in such a short time! That is amazing.

I should have provided more information about that PR to make it much easier to review,
as it contains quite a lot of changes and tricky moments. Next time, I will try to emphasise all the important moments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: enhancement A general enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants