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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(ecr): use ec2 instance credentials when no credentials are provided #114

Merged
merged 2 commits into from Dec 16, 2021

Conversation

Flydiverny
Copy link
Contributor

@Flydiverny Flydiverny commented Dec 6, 2021

The tests are a bit silly and verbose but I wanted to add some kind of test 馃槃

Fixes #64

#64 (comment)
Copy pastad in below:


I did some testing to simulate what happens in the login-action

Given this step below I will get the expected output

      - uses: actions/github-script@v5
        with:
          script: |
            await exec.getExecOutput(await io.which('aws', true), ['sts', 'get-caller-identity'])
          result-encoding: string
{
    "UserId": "REDACTED:i-REDACTED",
    "Account": "REDACTED",
    "Arn": "arn:aws:sts::REDACTED:assumed-role/REDACTED/i-REDACTED"
}

Where as if I added the process.env vars as the script does the step fails

      - uses: actions/github-script@v5
        with:
          script: |
            let username = ''
            let password = ''
            process.env.AWS_ACCESS_KEY_ID = username || process.env.AWS_ACCESS_KEY_ID;
            process.env.AWS_SECRET_ACCESS_KEY = password || process.env.AWS_SECRET_ACCESS_KEY;
            await exec.getExecOutput(await io.which('aws', true), ['sts', 'get-caller-identity'])
          result-encoding: string
An error occurred (InvalidClientTokenId) when calling the GetCallerIdentity operation: The security token included in the request is invalid.
Error: Unhandled error: Error: The process '/usr/local/bin/aws' failed with exit code 254

a simple change to how we set the env vars should resolve it

      - uses: actions/github-script@v5
        with:
          script: |
            let username = ''
            let password = ''
            if (username) {
              process.env.AWS_ACCESS_KEY_ID = username;
            }
            if (password) {
              process.env.AWS_SECRET_ACCESS_KEY = password;
            }
            await exec.getExecOutput(await io.which('aws', true), ['sts', 'get-caller-identity'])
          result-encoding: string

Which will get me my EC2 credentials again :)

Created a PR #114

Originally posted by @Flydiverny in #64 (comment)

@Flydiverny
Copy link
Contributor Author

@crazy-max Would appreciate a review here, what's the best way to end up in your todo? 馃槆馃檹

@crazy-max
Copy link
Member

@Flydiverny Looks like dist has not been generated. See https://github.com/docker/login-action/blob/master/.github/CONTRIBUTING.md#submitting-a-pull-request

Comment on lines +65 to +70
if (username) {
process.env.AWS_ACCESS_KEY_ID = username;
}
if (password) {
process.env.AWS_SECRET_ACCESS_KEY = password;
}
Copy link
Member

@crazy-max crazy-max Dec 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic is the same as the current one: https://onecompiler.com/nodejs/3xmb4n6tq

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The outcome is sadly different

const util = require('util');
const exec = util.promisify(require('child_process').exec);
async function printenv() {
    process.env.TEST_ENV_BANANA = undefined
    process.env.TEST_ENV_APPLE = undefined
    delete process.env.TEST_ENV_APPLE
  try {
      const { stdout, stderr } = await exec('printenv | grep TEST_ENV_');
      console.log('stdout:', stdout);
      console.log('stderr:', stderr);
  } catch (err) {
     console.error(err);
  };
};
printenv();

image

As you can see in the case of TEST_ENV_BANANA the environment variable is set in the subprocess.
Unlike TEST_ENV_APPLE which was deleted instead of undefined
This causes issue when using EC2 metadata api for getting AWS credentials, as now the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY will both be set to undefined eg they have a value and therefor the AWS cli will try to use them instead of falling back thru the credential chain and use the ec2 metadata api.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the problem is when

 process.env.AWS_SECRET_ACCESS_KEY = password ||  process.env.AWS_SECRET_ACCESS_KEY

if process.env.AWS_SECRET_ACCESS_KEY and password are both undefined we end up assigning process.env.AWS_SECRET_ACCESS_KEY = undefined which causes this env var to be set for subprocesses.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's an updated onecompiler link where you can see that the result will differ
https://onecompiler.com/nodejs/3xmbfxhf6
https://onecompiler.com/nodejs/3xmbg2nzw

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh ok so this is related to sub steps in the workflow right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes!
If you want to use this action on a self-hosted runner on AWS, the runner can have access to login to AWS ECR without having to set any credentials

      - name: Login to ECR
        uses: docker/login-action@v1
        with:
          registry: <aws-account-number>.dkr.ecr.<region>.amazonaws.com

Eventually the action will run the aws cli to get the login token, line 77 or 81 here. Which would be affected by any process.env changes done in node here since it a child process

login-action/src/aws.ts

Lines 74 to 85 in 1cce165

export const getDockerLoginCmds = async (cliVersion: string, registry: string, region: string, accountIDs: string[]): Promise<string[]> => {
let ecrCmd = (await isPubECR(registry)) ? 'ecr-public' : 'ecr';
if (semver.satisfies(cliVersion, '>=2.0.0') || (await isPubECR(registry))) {
return execCLI([ecrCmd, 'get-login-password', '--region', region]).then(pwd => {
return [`docker login --username AWS --password ${pwd} ${registry}`];
});
} else {
return execCLI([ecrCmd, 'get-login', '--region', region, '--registry-ids', accountIDs.join(' '), '--no-include-email']).then(dockerLoginCmds => {
return dockerLoginCmds.trim().split(`\n`);
});
}
};

We use the docker/login-action for or other workflows so it would be nice to be able to use it on our self-hosted runners as well without having to specify the credentials in those workflows :)

Why node.js propagates env vars as undefined I have no clue 馃槃

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I think instead we could set the env var to the current process in getExecOutput. I will open a PR with this solution and come back to you. I keep yours open in case mine doesn't fit your needs.

Signed-off-by: Markus Maga <markus@maga.se>
@Flydiverny
Copy link
Contributor Author

@Flydiverny Looks like dist has not been generated. See https://github.com/docker/login-action/blob/master/.github/CONTRIBUTING.md#submitting-a-pull-request

My bad! Updated :)

Copy link
Member

@crazy-max crazy-max left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Will see what we can do about #114 (comment) in a follow-up.

@crazy-max crazy-max merged commit b776a64 into docker:master Dec 16, 2021
@Flydiverny Flydiverny deleted the fix-ec2-instance-credentials branch December 16, 2021 13:02
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.

Login doesn't support using EC2 instance credentials with ECR login
2 participants