-
Notifications
You must be signed in to change notification settings - Fork 4k
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
(pipelines): DockerCredential.docker_hub() silently fails to auth against Docker Hub #15737
Comments
As mentioned by @james-mathiesen looks like docker credential helper is invoked for the dockers official registry when config.json has full url. When we login to dockerhub, the below entry is added to config.json in auths section.
If registry is official they use lookup to get server url and which is this |
I think we can pass the full url to fetchDockerLoginCredentials in docker credential cdk asset and check the domain first and fallback to url as well for special cases like this.I doubt if there are any other cases which are similar. I did not reproduce the issue in itself. |
Thanks for the detailed write-up @james-mathiesen , and the patch and further info @nohack! Very useful. It looks like -- in addition to the changes @nohack has posted -- we should also update the below line to store/point to the full URL instead of the domain. That will remove the need to double-configure the entry as seen in the original post.
Looks like a relatively quick fix, followed by a painstaking testing process. :) |
This workaround doesn't appear to be working. I still receive dockerhub rate limit errors in the CDK pipeline synth step after setting up a paid Dockerhub account and corresponding secret configuration in AWS. var dockercreds = Amazon.CDK.AWS.SecretsManager.Secret.FromSecretNameV2(this, "DockerhubSecret", "dockerhub");
var pipeline = new CodePipeline(this, "Pipeline", new CodePipelineProps
{
...
DockerEnabledForSynth = true,
Synth = new ShellStep("Synth", new ShellStepProps
{
...
}),
DockerCredentials = new[]
{
DockerCredential.DockerHub(dockercreds),
DockerCredential.CustomRegistry("https://index.docker.io/v1/", dockercreds) // https://github.com/aws/aws-cdk/issues/15737
}
}); I've verified that the IAM Role that CodeBuild is using has access to the secret. (Note: the originally generated IAM policy did not have access because the secret arn was suffixed with I also get the following output in the synth step as expected:
The asset that fails during synth is built using the var source = Source.Asset(..., new Amazon.CDK.AWS.S3.Assets.AssetOptions
{
Bundling = new BundlingOptions
{
Image = DockerImage.FromRegistry("node:lts"),
Command = new[]
{
"bash", "-c", string.Join(" && ", new []
{
"npm ci",
"npm run build",
"cp -r /asset-input/build/* /asset-output"
})
}
},
...
}); |
The workaround works for me. |
…18313) ### Problem: `DockerCredential.dockerHub()` silently failed to authenticate users, resulting in unexpected and intermittent throttling due to docker's policy for unauthenticated users. ### Reason: `.dockerHub()` added `index.docker.io` to the domain credentials, but the actual docker command [authenticated](https://github.com/moby/moby/blob/1e71c6cffedb79e3def696652753ea43cdc47b99/registry/config.go#L35) with `https://index.docker.io/v1/` which it was unable to find as a domain credential, thus failing to trigger `docker-credential-cdk-assets` during the `docker --config build` call. Furthermore, the credential `DockerCredential.customRegistry('https://index.docker.io/v1/', secret)` alone does not work. This would successfully trigger `docker-credential-cdk-assets` but fail to authenticate because of how `cdk-assets` handles credential lookup. The command strips the endpoint into just a hostname so in this case we try `fetchDockerLoginCredentials(awsClient, config, 'index.docker.io')` which fails: https://github.com/aws/aws-cdk/blob/4fb0309e3b93be276ab3e2d510ffc2ce35823dcd/packages/cdk-assets/bin/docker-credential-cdk-assets.ts#L32-L38 So the workaround for this bug was to specify both domains as credentials, each to satisfy a separate step of the process: ```ts dockerCredentials: [ pipelines.DockerCredential.dockerHub(secret), pipelines.DockerCredential.customRegistry('https://index.docker.io/v1/', secret), ], ``` ### Solution: This PR introduces two separate changes to address both problems. First, we change the hardcoded domain in `DockerCredential.dockerHub()` to be `https://index.docker.io/v1/`. This allows us to successfully trigger `docker-credential-cdk-assets` when the `docker --config build` command is called. Next, to make sure the credential lookup succeeds, we check for both the complete endpoint and the domain name. In this case, we will check for both `https://index.docker.io/v1/` as well as `index.docker.io`. Since `https://index.docker.io/v1/` exists in the credentials helper, authentication will succeed. Why do we still check for the domain `index.docker.io`? I don't know how custom registries or ecr works in this context and believe it to be beyond the scope of the PR. It's possible that they require the domain only for lookup. ### Testing: The change to credential lookups is unit tested in `docker-credentials.test.ts`. I confirmed that the change to `DockerCredential.dockerHub()` is successful by configuring a mock `cdk-docker-creds.json` file and successfully `cdk deploy`ing a docker image that depends on a private repository. This isn't a common use case but ensures that failure to authenticate results in failure every time. Thanks @james-mathiesen for the suggestion. ### Contributors: Thanks to @nohack for the code in `cdk-assets`. Fixes #15737. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
|
…ws#18313) ### Problem: `DockerCredential.dockerHub()` silently failed to authenticate users, resulting in unexpected and intermittent throttling due to docker's policy for unauthenticated users. ### Reason: `.dockerHub()` added `index.docker.io` to the domain credentials, but the actual docker command [authenticated](https://github.com/moby/moby/blob/1e71c6cffedb79e3def696652753ea43cdc47b99/registry/config.go#L35) with `https://index.docker.io/v1/` which it was unable to find as a domain credential, thus failing to trigger `docker-credential-cdk-assets` during the `docker --config build` call. Furthermore, the credential `DockerCredential.customRegistry('https://index.docker.io/v1/', secret)` alone does not work. This would successfully trigger `docker-credential-cdk-assets` but fail to authenticate because of how `cdk-assets` handles credential lookup. The command strips the endpoint into just a hostname so in this case we try `fetchDockerLoginCredentials(awsClient, config, 'index.docker.io')` which fails: https://github.com/aws/aws-cdk/blob/4fb0309e3b93be276ab3e2d510ffc2ce35823dcd/packages/cdk-assets/bin/docker-credential-cdk-assets.ts#L32-L38 So the workaround for this bug was to specify both domains as credentials, each to satisfy a separate step of the process: ```ts dockerCredentials: [ pipelines.DockerCredential.dockerHub(secret), pipelines.DockerCredential.customRegistry('https://index.docker.io/v1/', secret), ], ``` ### Solution: This PR introduces two separate changes to address both problems. First, we change the hardcoded domain in `DockerCredential.dockerHub()` to be `https://index.docker.io/v1/`. This allows us to successfully trigger `docker-credential-cdk-assets` when the `docker --config build` command is called. Next, to make sure the credential lookup succeeds, we check for both the complete endpoint and the domain name. In this case, we will check for both `https://index.docker.io/v1/` as well as `index.docker.io`. Since `https://index.docker.io/v1/` exists in the credentials helper, authentication will succeed. Why do we still check for the domain `index.docker.io`? I don't know how custom registries or ecr works in this context and believe it to be beyond the scope of the PR. It's possible that they require the domain only for lookup. ### Testing: The change to credential lookups is unit tested in `docker-credentials.test.ts`. I confirmed that the change to `DockerCredential.dockerHub()` is successful by configuring a mock `cdk-docker-creds.json` file and successfully `cdk deploy`ing a docker image that depends on a private repository. This isn't a common use case but ensures that failure to authenticate results in failure every time. Thanks @james-mathiesen for the suggestion. ### Contributors: Thanks to @nohack for the code in `cdk-assets`. Fixes aws#15737. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
Hmm. I guess I was just lucky, as now it is not working any more. It works for
but it fails when using
I wonder if it has something to do with that Hmm.. |
Using DockerCredential.docker_hub() from CDKPipeline will silently fail to auth against Docker Hub during all stages of the build. In our case this makes ContainerImage.from_asset() intermittently fail when executed via CDKPipeline and makes the overall CI/CD process unstable.
Because most users rely on public images on Docker Hub the failure mode is subtle. In most cases users will observe only intermittent issues due to Docker Hub enforcement of pull rate limits against the IP addresses in AWS CodeBuild infrastructure.
To reliably reproduce this issue one can create a free Docker Hub account and create a private container image. Construct a pipeline that uses from_asset() to build an image that depends on the private image. Finally, supply a DockerCredential structure to CDKPipeline that is expected to allow access to the private image. This approach will reliably demonstrate that the Docker Hub credentials are not being used.
Private images on Docker Hub are an unusual use case; however using Docker Hub credentials to pull public images from Docker Hub is common practice due to Docker Hub enforcing IP address rate limits for unauthenticated users. The IP address rate limits are especially painful when building on shared infrastructure such as AWS CodeBuild.
Docker Hub offers per-user rate limits for free accounts and it is common practice to setup free accounts to support automated builds. When using shared infrastructure such as AWS CodeBuild, Docker Hub credentials are a practical necessity.
Reproduction Steps
Because the common failure mode is caused by rate limit enforcement on Docker Hub I cannot provide a copy/paste test that will reliably demonstrate the problem without also sharing valid Docker Hub credentials which I do not wish to do.
What did you expect to happen?
I expected the container asset stages in AWS CodeBuild to authenticate against Docker Hub when pulling image dependencies.
By authenticating I expected to be subject to a per-user pull rate limit on Docker Hub despite the build happening on shared infrastructure in AWS CodeBuild.
What actually happened?
The builds intermittently fail due to enforcement of IP address rate limits at Docker Hub.
Environment
Other
Authentication fails because credHelpers in Docker's config.json does not work as the Docker documentation would lead one to believe.
When pulling a public image (e.g.
FROM amazon/aws-cli:2.2.18
) the lookup in credHelpers is for the keyhttps://index.docker.io/v1/
. When the credential helper program is executed the domain passed to the helper isindex.docker.io
. The CDK incorrectly presumes that both values areindex.docker.io
so the credential helper is never invoked. Because most images on Docker Hub are public the lack of authentication is not obvious except when pull rate limits are exceeded.This is a workaround to the problem:
The above workaround creates two very similar entries, one to support each stage of the auth process.
The entry for
https://index.docker.io/v1/
is required to cause docker to invoke the CDK credential helper.The entry for
index.docker.io
is required for the CDK credential helper to find a match for the domain passed into the credential helper.This is 🐛 Bug Report
The text was updated successfully, but these errors were encountered: