Pulling Docker images from ECR private registries with a Cognito User Pool Identity

Jeremy Cowan
5 min readApr 17, 2021

--

Introduction

Imagine you use an ECR private registry to store your Docker images and you’ve recently developed a piece of software that you’d like to distribute as an image to folks outside of your organization. Pushing the image to ECR public, or another public registry, is not an option because you need to restrict who can pull image. Ordinarily, I’d say you should create an IAM policy allowing image pulls from a list of “licensed” AWS accounts, however, you can’t assume that the folks you want to distribute your image to have an AWS account. Moreover, this option doesn’t offer a great mechanism for controlling accessing per user. Creating IAM users for each licensed user is another possibility, but you don’t want to be responsible for managing user identities. What should you do in this instance?

Cognito User Pools and Cognito Federated Identities

You can think of Cognito User Pools as a managed identity provider. It supports both user sign-up and sign-in and gives you the ability create and manage identities independently of IAM.

Cognito Federated Identities allows you to use an external identity provider, including Cognito User Pools, to authenticate users and authorize them to access specific AWS resources like ECR.

The solution begins with the creation of a Cognito User Pool. Users can either register for an account themselves or you can pre-populate the pool with a list of users by importing a file.

All user have a default set of attributes, e.g. email address, phone number, and so forth. As an administrator, you can extend this list with your own custom attributes, such a boolean value for isLicensed. This will become important later when we get to authorization.

User Pools also includes hosted UI for user sign-ups and sign-in. Users can use this page to register for a new account, change their password, and login. This solution only uses the hosted UI for user sign-ups and changing the user’s password; all users are required to change their password after they login for the first time.

Federated Identities allow you to assign temporary AWS credentials, i.e. an access key id and a secret access key, once the user has been authenticated by an identity provider (IdP). We’re using User Pools as the identity provider in this solution, but you could configure Identity Federation to use Facebook, Google, Amazon, or another OIDC compliant identity provided if you so desired. Once the user is authenticated by the IdP, Cognito can assign the user an IAM role. The role that ultimately gets assigned to the user can be controlled by a specific user attribute. Attribute names which are claim names or assertion names, are mapped to tag keys in IAM roles that are used to match the PrincipalTag condition in policies. This enables you to grant access to AWS resources based on attributes. For example, if isLicensed is 1 or true the user can be assigned a role that allows them to pull a specific image from your ECR private registry. See Using Tokens for further information. If granting permissions this way this is too complex, you can use groups instead.

The ECR Credential Helper

The ECR Credential Helper is a small piece of code that facilitates authenticating to an ECR private registry. It is often configured to use the credentials in a user’s shared credentials file, ~/.aws/config. With a shared credentials file you have the option to use credential_process where an executable is called to retrieve a set of AWS credentials. This solution calls a binary, cognito, that runs on your behalf to retrieve authentication credentials for pulling images from ECR. See below for an example of a user’s credentials file:

[profile developer]
credential_process = cognito -u <username> -p <password>

When the executable runs, it returns output that looks as follows:

{
"Version": 1,
"AccessKeyId": "an AWS access key",
"SecretAccessKey": "your AWS secret access key",
"SessionToken": "the AWS session token for temporary credentials",
"Expiration": "ISO8601 timestamp for when the credentials expire"
}

This information is fed to the ECR credential helper to seamlessly access ECR registries associated with the temporary credentials issued by Cognito.

Putting It All Together

After you’ve configured User Pools and Identity Federation, you’re ready to onboard users. For simplicity sake, I’m only going to explain what a user does when an Cognito Identity has been created for them.

Login to the Hosted UI to change the password

When a Cognito user ID is created for a user, they are sent an email with their username and default password. Before they can start pulling images, they need to login to the Hosted UI at least once to change their password.

Install the AWS CLI and ECR Credential Helper and cognito binary

The next step requires the user to install and configure the following packages on their machine:

  1. AWS CLI
  2. ECR Credential Helper
  3. cognito binary

Create a default config profile for the AWS CLI, for example:

[profile developer]
credential_process = cognito -u <username> -p <password>

Update the config.json file in ~/.docker/ for the credential helper as shown below. The aws_account_id and region represent account and region of the registry that licensed users will pull images from, for example:

{
"credHelpers": {
"public.ecr.aws": "ecr-login",
"<aws_account_id>.dkr.ecr.<region>.amazonaws.com": "ecr-login"
}
}

If you’re using a CRI other than Docker, e.g. podman, you would update the the ${XDG_RUNTIME_DIR}/containers/auth.json on Linux and $HOME/.config/containers/auth.json on Windows and macOS, as follows:

{
"auths": {
"localhost:5001": {}
},
"credHelpers": {
"<aws_account_id>.dkr.ecr.<region>.amazonaws.com": "ecr-login"
}
}

Finally, update the machine PATH to include the directory where the user installed the cognito and ECR credential helper binaries.

If configure properly, the user should be able to pull images from the ECR registries they’ve been granted access to through Cognito.

Productizing the solution

This was created as part of a proof of concept. For production use I would probably give a user the ability to change their password from the cognito binary instead of using the Hosted UI. I would also consider using a different Idp, e.g. Facebook or Google, if I didn’t want the added responsibility of managing User Pool identities.

--

--

Jeremy Cowan

Jeremy Cowan is a Principal Container Specialist at AWS