Skip to content
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] Docker logins for assets #10999

Closed
1 of 2 tasks
tobli opened this issue Oct 20, 2020 · 21 comments · Fixed by #15364
Closed
1 of 2 tasks

[pipelines] Docker logins for assets #10999

tobli opened this issue Oct 20, 2020 · 21 comments · Fixed by #15364
Assignees
Labels
@aws-cdk/pipelines CDK Pipelines library effort/large Large work item – several weeks of effort feature-request A feature should be added or improved. p1

Comments

@tobli
Copy link

tobli commented Oct 20, 2020

When using CDK Pipelines the autogenerated Assets action will build Docker images, and publish to the cdk-provided ECR. However if included Dockerfiles build on images in a non-public repository (e.g. an ECR in a different account), those builds will fail since the Assets action has no way of specifying sources to docker login in to.

Use Case

Prior to using Pipelines we've used a shared ECR in a dedicated account to both store our internal base images, as well as images built on top of those. A single docker login would cover both pull and push from that repo.

However when switching to Pipelines, the destination repo changes. Pipelines transparently handles login to to that, but provides no configuration option for docker registries that need to be logged in to prior to asset building.

Proposed Solution

Other

The error message from the Assets/DockerAsset1 CodeBuild project was:

Step 1/13 : FROM <account>.dkr.ecr.eu-west-1.amazonaws.com/...
| Get https://<account>.dkr.ecr.eu-west-1.amazonaws.com/...: no basic auth credentials
  • 👋 I may be able to implement this feature request
  • ⚠️ This feature might incur a breaking change

This is a 🚀 Feature Request

@tobli tobli added feature-request A feature should be added or improved. needs-triage This issue or PR still needs to be triaged. labels Oct 20, 2020
@github-actions github-actions bot added the @aws-cdk/pipelines CDK Pipelines library label Oct 20, 2020
@rix0rrr
Copy link
Contributor

rix0rrr commented Oct 26, 2020

How would you expect this to work on the desktop? I.e. if you do cdk deploy?

@rix0rrr rix0rrr added this to the [GA] CDK Pipelines milestone Oct 26, 2020
@rix0rrr rix0rrr added the p1 label Oct 26, 2020
@rix0rrr
Copy link
Contributor

rix0rrr commented Oct 26, 2020

(What I'm saying is this might have implications for the asset manifest and CLI as well)

@rix0rrr rix0rrr added the effort/medium Medium work item – several days of effort label Oct 26, 2020
@tobli
Copy link
Author

tobli commented Oct 26, 2020

TBH I had no expectations of a particular behaviour when deploying locally. I only started using docker image assets after having set up a Pipeline. That said my understanding of cdk synth from having used cdk previously was that it represented the "compile CF templates" step.

@tobli
Copy link
Author

tobli commented Oct 26, 2020

The requirement of having done docker login was also never explicit when developing locally, since I was already logged in. Only when moving the docker build to CodeBuild did this become apparent.

@tobli
Copy link
Author

tobli commented Oct 26, 2020

Here's the hack I added to my pipeline stack (after the pipeline was created) to get past this:

  private static addECRLogin (pipeline: CdkPipeline, sourceECRs: string[]) {
    for (const action of pipeline.stage('Assets')?.actions) {
      const actionProperties = action.actionProperties;
      if (actionProperties.actionName.startsWith('Docker')) {
        // workaround for https://github.com/aws/aws-cdk/issues/10999
        const publishAction = action as PublishAssetsAction;
        const commands: string[] = (publishAction as any).commands;
        for (const sourceECR of sourceECRs) {
          // NOTE: this makes the simplifying assumption that the sourceECR is in the same region as the pipeline
          const command = `aws ecr get-login-password --region ${Stack.of(pipeline).region} | docker login --username AWS --password-stdin ${sourceECR}`;
          if (!commands.includes(command)) {
            // login needs to happen before the asset publication (that's where docker images are built)
            commands.unshift(command);
          }
        }

        new Policy(pipeline, 'AllowECRLoginAndPull', {
          statements: [
            new PolicyStatement({
              actions: [
                'ecr:GetAuthorizationToken',
                'ecr:GetDownloadUrlForLayer',
                'ecr:BatchGetImage',
              ],
              resources: ['*'],
              sid: 'AllowECRLoginAndPull',
            }),
          ],
        }).attachToRole(actionProperties.role!);
      }
    }
  }

@SomayaB SomayaB removed the needs-triage This issue or PR still needs to be triaged. label Oct 26, 2020
@rix0rrr rix0rrr added effort/large Large work item – several weeks of effort effort/medium Medium work item – several days of effort and removed effort/medium Medium work item – several days of effort effort/large Large work item – several weeks of effort labels Nov 6, 2020
@nsquires413
Copy link

@tobli What do you pass in as sourceECRs for this?

@pgarbe
Copy link
Contributor

pgarbe commented Dec 10, 2020

@nsquires413 It's the full ECR repo name, e.g. 123456789012.dkr.ecr.eu-west-1.amazonaws.com

@nsquires413
Copy link

@tobli @pgarbe One more question if you don't mind. I'm trying to translate this method into C#.

Looking at the 'PublishAssetsAction' source code it looks like the it has a private member 'commands'.

It seems like you were still able to access this field by casting the action as 'any'? Is that right? Does anyone know if this is possible in C#?

@tobli
Copy link
Author

tobli commented Dec 10, 2020

It seems like you were still able to access this field by casting the action as 'any'? Is that right? Does anyone know if this is possible in C#?

Correct, it's accessing private field via the cast. That's of course risky, but Typescript lets you do that. If C# does I have no clue I'm afraid.

@rix0rrr rix0rrr changed the title [pipelines] allow docker login before Asset publishing action [pipelines] Docker logins for assets Jan 7, 2021
@rix0rrr
Copy link
Contributor

rix0rrr commented Jan 7, 2021

When we get to implementing this, see Quip doc with reference PameA2ADtrao.

See also #11774

@rix0rrr rix0rrr added effort/large Large work item – several weeks of effort and removed effort/medium Medium work item – several days of effort labels Jan 7, 2021
@quincycs
Copy link
Contributor

I had same problem, but I just put docker login as a pre-build command on the codebuild project.

https://github.com/quincycs/aws-cdk-q-starter/blob/master/cdk-app/src/PipelineStack.ts#L102

This was an easy fix for me, so maybe I don't understand the issue.

@tobli
Copy link
Author

tobli commented Jan 24, 2021

@quincycs this issue arises when using aws-ecr-assets to build a Docker image as part of the pipeline. Cdk Assets will create a new Assets build pipeline step as an implementation detail. It's for that the login step is needed.

@karthikns16
Copy link
Contributor

karthikns16 commented Jan 28, 2021

In python, we don't have access to the publishAssetAction.commands. I ended up writing a script to access the pipeline.template.json using aws_cdk.cx_api and update the buildspec phases for all docker asset. This script was executed as part of synth_command in the pipeline after we run cdk synth.

Example:

pipelines.SimpleSynthAction(
synth_command = "cdk synth; python <script created to update the template>"
)

Output Asset Buildspec.yml

{
    "version": "0.2",
    "phases": {
        "install": {
            "commands": "npm install -g cdk-assets@1.85.0"
        },
        "build": {
            "commands": [
                "cdk-assets --path \"assemblyXXXXX/XXXX.assets.json\" --verbose publish \"XXXXX\""
            ]
        },
        "pre_build": {
            "commands": "aws ecr get-login-password --region XXXX | docker login --username AWS --password-stdin XXXX.dkr.ecr.XXXX.amazonaws.com"
        }
    }
}

@blimmer
Copy link
Contributor

blimmer commented Feb 2, 2021

I needed to login to a dockerhub paid account, so I worked around like this. The auth token is stored in
Secrets Manager for security.

function maybeAddDockerLogin(scope: cdk.Construct, pipeline: pipelines.CdkPipeline) {
  let assetStage: codepipeline.IStage;
  try {
    assetStage = pipeline.stage('Assets');
  } catch {
    // If a stage with a given name cannot be found, CDK throws an exception
    return;
  }

  const dockerAssetActions = assetStage.actions.filter((action) => {
    const actionProperties = action.actionProperties;
    return actionProperties.actionName.startsWith('Docker');
  });

  if (dockerAssetActions.length === 0) {
    return;
  }

  // Replace the last parameter with the name of your secret.
  // This secret is the access token for a paid dockerhub user
  // https://docs.docker.com/docker-hub/access-tokens/
  const dockerHubSecret = secrets.Secret.fromSecretNameV2(scope, 'SharedSecretImport', 'shared/dockerhub-access-token');
  const secretAccessPolicy = new iam.Policy(scope, 'SecretAccessPolicy', {
    statements: [
      new iam.PolicyStatement({
        sid: 'AllowFetchingDockerHubSecret',
        actions: ['secretsmanager:GetSecretValue'],
        resources: [`${dockerHubSecret.secretArn}-??????`],
      }),
    ],
  });

  dockerAssetActions.forEach((action) => {
    const publishAction = action as any;
    const codeBuildProject = publishAction.node.defaultChild.node.defaultChild;
    codeBuildProject.environment.environmentVariables = codeBuildProject.environment.environmentVariables ?? [];
    codeBuildProject.environment.environmentVariables.push({
      name: 'DOCKER_AUTH_TOKEN',
      type: 'SECRETS_MANAGER',
      value: dockerHubSecret.secretName,
    });
    secretAccessPolicy.attachToRole(action.actionProperties.role!);

    const commands: string[] = publishAction.commands;
    const command = 'echo ${DOCKER_AUTH_TOKEN} | docker login --username gingereng --password-stdin';
    if (!commands.includes(command)) {
      // login needs to happen before the asset publication (that's where docker images are built)
      commands.unshift(command);
    }
  });
}

(credit to @tobli's comment above for the majority of this)

@jabrennem
Copy link

hi @tobli - do you know when the 'Assets' stage is created? I tried using your hack in python, but the command pipeline.stage('Assets') throws an error for me saying that there is no Assets stage available. Only self Update, Build, Source, and Staging (my core.Stage). I was hoping to add some commands and policy statements to the code build project.

@karthikns16
Copy link
Contributor

karthikns16 commented Feb 16, 2021

@rix0rrr , could we implement a boolean flag in CDKPipeline construct which is defaults FALSE. If enabled, we could add a blanket ECR login command in pre-build step of PublishAssetAction if the assetType === "Docker". The ECR should be in same account/region as the pipeline?

@david-fractiunate
Copy link

david-fractiunate commented Jun 18, 2021

+1 Push, we need this.... CDK Docker Assets are not usable without it for our team.
Without a docker-login we face:

exited with error code 1: toomanyrequests: You have reached your pull rate limit. You may increase the limit by authenticating and upgrading: https://www.docker.com/increase-rate-limit

to fast...

CDK V.1.109.0 Python

It seems i cannot access the Assets Stage with
peline.stage(stage_name='Assets').actions

jsii.errors.JavaScriptError:
  Error: Pipeline does not contain a stage named 'Assets'. Available stages: Source, Build, UpdatePipeline

Neither could i access the commands:

  action_properties = action.action_properties
      if action_properties.actionName.startswith("Docker"):
        publish_action: PublishAssetsAction = action
        commands: [] = getattr(publish_action, "commands")
        command = f"aws ecr get-login-password --region ${Stack.of(pipeline).region} | docker login --username AWS --password-stdin ${sourceECR}"
        # login needs to happen before the asset publication (that's where docker images are built)
        if command not in commands:
          commands[:0] = [command]

Thanks a lot!

@njlynch njlynch self-assigned this Jun 18, 2021
njlynch added a commit that referenced this issue Jun 24, 2021
Currently, `cdk-assets` does a single `docker login` with credentials fetched
from ECR's `getAuthorizationToken` API. This enables access to (typically) the
assets in the environment's ECR repo (`*--container-assets-*`).

A pain point for users today is throttling when using images from other sources,
especially from DockerHub when using unauthenticated calls.

This change introduces a new configuration file at a well-known location (and
overridable via the CDK_DOCKER_CREDS_FILE environment variable), which allows
specifying per-domain login credentials via either the default ECR auth tokens
or via a secret in SecretsManager.

If the credentials file is present, a Docker credential helper
(docker-credential-cdk-assets) will be set up for each of the configured
domains, and used for the `docker build` commands to enable fetching images from
both DockerHub or configured ECR repos. Then the "normal" credentials will be
assumed for the final publishing step. For backwards compatibility, if no
credentials file is present, the existing `docker login` will be done prior to
the build step as usual.

This PR will be shortly followed by a corresponding PR for the cdk pipelines
library to enable users to specify registries and credentials to be fed into
this credentials file during various stages of the pipeline (e.g., build/synth,
self-update, and asset publishing).

related #10999
related #11774
njlynch added a commit that referenced this issue Jun 24, 2021
Currently, `cdk-assets` does a single `docker login` with credentials fetched
from ECR's `getAuthorizationToken` API. This enables access to (typically) the
assets in the environment's ECR repo (`*--container-assets-*`).

A pain point for users today is throttling when using images from other sources,
especially from DockerHub when using unauthenticated calls.

This change introduces a new configuration file at a well-known location (and
overridable via the CDK_DOCKER_CREDS_FILE environment variable), which allows
specifying per-domain login credentials via either the default ECR auth tokens
or via a secret in SecretsManager.

If the credentials file is present, a Docker credential helper
(docker-credential-cdk-assets) will be set up for each of the configured
domains, and used for the `docker build` commands to enable fetching images from
both DockerHub or configured ECR repos. Then the "normal" credentials will be
assumed for the final publishing step. For backwards compatibility, if no
credentials file is present, the existing `docker login` will be done prior to
the build step as usual.

This PR will be shortly followed by a corresponding PR for the cdk pipelines
library to enable users to specify registries and credentials to be fed into
this credentials file during various stages of the pipeline (e.g., build/synth,
self-update, and asset publishing).

related #10999
related #11774
njlynch added a commit that referenced this issue Jun 24, 2021
Currently, `cdk-assets` does a single `docker login` with credentials fetched
from ECR's `getAuthorizationToken` API. This enables access to (typically) the
assets in the environment's ECR repo (`*--container-assets-*`).

A pain point for users today is throttling when using images from other sources,
especially from DockerHub when using unauthenticated calls.

This change introduces a new configuration file at a well-known location (and
overridable via the CDK_DOCKER_CREDS_FILE environment variable), which allows
specifying per-domain login credentials via either the default ECR auth tokens
or via a secret in SecretsManager.

If the credentials file is present, a Docker credential helper
(docker-credential-cdk-assets) will be set up for each of the configured
domains, and used for the `docker build` commands to enable fetching images from
both DockerHub or configured ECR repos. Then the "normal" credentials will be
assumed for the final publishing step. For backwards compatibility, if no
credentials file is present, the existing `docker login` will be done prior to
the build step as usual.

This PR will be shortly followed by a corresponding PR for the cdk pipelines
library to enable users to specify registries and credentials to be fed into
this credentials file during various stages of the pipeline (e.g., build/synth,
self-update, and asset publishing).

related #10999
related #11774
mergify bot pushed a commit that referenced this issue Jun 25, 2021
Currently, `cdk-assets` does a single `docker login` with credentials fetched
from ECR's `getAuthorizationToken` API. This enables access to (typically) the
assets in the environment's ECR repo (`*--container-assets-*`).

A pain point for users today is throttling when using images from other sources,
especially from DockerHub when using unauthenticated calls.

This change introduces a new configuration file at a well-known location (and
overridable via the CDK_DOCKER_CREDS_FILE environment variable), which allows
specifying per-domain login credentials via either the default ECR auth tokens
or via a secret in SecretsManager.

If the credentials file is present, a Docker credential helper
(docker-credential-cdk-assets) will be set up for each of the configured
domains, and used for the `docker build` commands to enable fetching images from
both DockerHub or configured ECR repos. Then the "normal" credentials will be
assumed for the final publishing step. For backwards compatibility, if no
credentials file is present, the existing `docker login` will be done prior to
the build step as usual.

This PR will be shortly followed by a corresponding PR for the cdk pipelines
library to enable users to specify registries and credentials to be fed into
this credentials file during various stages of the pipeline (e.g., build/synth,
self-update, and asset publishing).

Two refactorings here:
- Moved obtainEcrCredentials from docker.ts to docker-credentials-ts.
- Moved DefaultAwsClient from bin/publish.ts to lib/aws.ts

related #10999
related #11774

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
njlynch added a commit that referenced this issue Jun 30, 2021
Introduce a new set of properties to the pipeline constructs to enable users to
specify Docker registries -- and associated credentials for each -- to be used
during the pipeline build/synth, self-mutate, and asset publishing stages.

These APIs enable the user to specify a Docker registry (e.g., DockerHub, ECR)
and either secrets or use role credentials to authenticate to each registry, as
well as specify which step(s) of the pipeline need these credentials.

DRAFT -- Posting just the basic API for feedback while I finish up tests and
final implementation. Any and all feedback welcome!

fixes #10999
fixes #11774
njlynch added a commit that referenced this issue Jul 1, 2021
Introduce a new set of properties to the pipeline constructs to enable users to
specify Docker registries -- and associated credentials for each -- to be used
during the pipeline build/synth, self-mutate, and asset publishing stages.

These APIs enable the user to specify a Docker registry (e.g., DockerHub, ECR)
and either secrets or use role credentials to authenticate to each registry, as
well as specify which step(s) of the pipeline need these credentials.

fixes #10999
fixes #11774
@mergify mergify bot closed this as completed in #15364 Jul 2, 2021
mergify bot pushed a commit that referenced this issue Jul 2, 2021
Introduce a new set of properties to the pipeline constructs to enable users to
specify Docker registries -- and associated credentials for each -- to be used
during the pipeline build/synth, self-mutate, and asset publishing stages.

These APIs enable the user to specify a Docker registry (e.g., DockerHub, ECR)
and either secrets or use role credentials to authenticate to each registry, as
well as specify which step(s) of the pipeline need these credentials.

fixes #10999
fixes #11774

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
@github-actions
Copy link

github-actions bot commented Jul 2, 2021

⚠️COMMENT VISIBILITY WARNING⚠️

Comments on closed issues are hard for our team to see.
If you need more assistance, please either tag a team member or open a new issue that references this one.
If you wish to keep having a conversation with other community members under this issue feel free to do so.

@aax
Copy link

aax commented Jul 2, 2021

hi @tobli - do you know when the 'Assets' stage is created? I tried using your hack in python, but the command pipeline.stage('Assets') throws an error for me saying that there is no Assets stage available. Only self Update, Build, Source, and Staging (my core.Stage). I was hoping to add some commands and policy statements to the code build project.

@jabrennem The stage is created when you add an application stage which actually contains assets to be published.

upparekh pushed a commit to upparekh/aws-cdk that referenced this issue Jul 8, 2021
Introduce a new set of properties to the pipeline constructs to enable users to
specify Docker registries -- and associated credentials for each -- to be used
during the pipeline build/synth, self-mutate, and asset publishing stages.

These APIs enable the user to specify a Docker registry (e.g., DockerHub, ECR)
and either secrets or use role credentials to authenticate to each registry, as
well as specify which step(s) of the pipeline need these credentials.

fixes aws#10999
fixes aws#11774

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
hollanddd pushed a commit to hollanddd/aws-cdk that referenced this issue Aug 26, 2021
Currently, `cdk-assets` does a single `docker login` with credentials fetched
from ECR's `getAuthorizationToken` API. This enables access to (typically) the
assets in the environment's ECR repo (`*--container-assets-*`).

A pain point for users today is throttling when using images from other sources,
especially from DockerHub when using unauthenticated calls.

This change introduces a new configuration file at a well-known location (and
overridable via the CDK_DOCKER_CREDS_FILE environment variable), which allows
specifying per-domain login credentials via either the default ECR auth tokens
or via a secret in SecretsManager.

If the credentials file is present, a Docker credential helper
(docker-credential-cdk-assets) will be set up for each of the configured
domains, and used for the `docker build` commands to enable fetching images from
both DockerHub or configured ECR repos. Then the "normal" credentials will be
assumed for the final publishing step. For backwards compatibility, if no
credentials file is present, the existing `docker login` will be done prior to
the build step as usual.

This PR will be shortly followed by a corresponding PR for the cdk pipelines
library to enable users to specify registries and credentials to be fed into
this credentials file during various stages of the pipeline (e.g., build/synth,
self-update, and asset publishing).

Two refactorings here:
- Moved obtainEcrCredentials from docker.ts to docker-credentials-ts.
- Moved DefaultAwsClient from bin/publish.ts to lib/aws.ts

related aws#10999
related aws#11774

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
hollanddd pushed a commit to hollanddd/aws-cdk that referenced this issue Aug 26, 2021
Introduce a new set of properties to the pipeline constructs to enable users to
specify Docker registries -- and associated credentials for each -- to be used
during the pipeline build/synth, self-mutate, and asset publishing stages.

These APIs enable the user to specify a Docker registry (e.g., DockerHub, ECR)
and either secrets or use role credentials to authenticate to each registry, as
well as specify which step(s) of the pipeline need these credentials.

fixes aws#10999
fixes aws#11774

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
@FelixRelli
Copy link

For anybody looking at this, #15364 added dockerCredentials in CodePipeline to use:

    const pipeline = new CodePipeline(this, 'MyPipeline', {
      ...,
      publishAssetsInParallel: true,
      dockerCredentials: [
        DockerCredential.ecr(
          [
            Repository.fromRepositoryArn(
              this,
              'PublicEcr',
              'arn:aws:ecr:us-east-1:763104351884:repository/*'
            ),
          ]
        ),
      ],
  }

For some reason publishAssetsInParallel must be set true though (default). Otherwiese got "This BuildSpec contains CloudFormation references and is supported by publishInParallel=false" and the build failed.

@cheruvian
Copy link
Contributor

cheruvian commented Feb 8, 2024

For some reason publishAssetsInParallel must be set true though (default). Otherwiese got "This BuildSpec contains CloudFormation references and is supported by publishInParallel=false" and the build failed.

Just banged my head on this for 4 hours today... -_- This has to be a bug right?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
@aws-cdk/pipelines CDK Pipelines library effort/large Large work item – several weeks of effort feature-request A feature should be added or improved. p1
Projects
None yet
Development

Successfully merging a pull request may close this issue.