This repository demonstrates the use of the AWS Cloud Development Kit (CDK) and AWS Elastic Container Registry (ECR) to distribute built software to end-users, developers, and GitHub Actions via a cloud-hosted Docker registry.
CDK is used for Infrastructure as Code, which manages related groups of AWS resources as stacks, which are deployed via AWS CloudFormation
Each stack in the application consists of an ECR instance with users and groups of varying levels of access.
The application consists of three such stacks:
- A Development stack, used to manage feature branch images
- A Master stack, used to manage master branch images
- A Release stack, used to manage manually released images
AWS Identity and Access Management (IAM) is used to secure the registry and provide appropriate levels of access to the relevant parties.
Using ECR, CDK, and IAM for managing Docker images and users grants the following benefits:
- Automated scanning of image dependencies for vulnerabilities.
- Automated archiving of images when no longer relevant.
- Version controlled management of infrastructure via code.
- Easier integration of software with other AWS services.
- Easier integration with third-party authorization methods.
- Finer grained and more robust access and security features.
(Note that this diagram reflects the desired architecture, rather than how the code is setup currently. See the tables below for the actual state of permissions)
Permissions to the registry are granted at four levels:
- Authorize access: Required by any user to log in to the registry.
- Read access: Required by a user to download images from the registry.
- Write access: Required by a user to push images to the registry.
- Metadata access: Required by a user to view AWS metadata about the registry.
The relevant identities are split into three categories:
- Developers: People who will be working on and testing the source code.
- End Users: People who will be downloading and using the built code.
- GitHub Actions: Automated GitHub runners that will be building, testing, and releasing the code.
The following tables show which identities have which permissions in each stack.
Development
Permissions | Developers | End Users | GitHub |
---|---|---|---|
Authorize | ✔️️ | ❌ | ️✔️ |
Read | ✔️ | ❌ | ✔️ |
Write | ✔️ | ❌ | ✔️ |
Metadata | ✔️ | ❌ | ✔️ |
Master
Permissions | Developers | End Users | GitHub |
---|---|---|---|
Authorize | ✔️️ | ❌ | ️✔️ |
Read | ✔️ | ❌ | ❌️ |
Write | ❌️ | ❌ | ✔️ |
Metadata | ✔️ | ❌ | ❌️ |
Release
Permissions | Developers | End Users | GitHub |
---|---|---|---|
Authorize | ✔️️ | ✔ | ️✔️ |
Read | ✔️ | ✔ | ❌️ |
Write | ❌️ | ❌ | ✔️ |
Metadata | ✔️ | ❌ | ❌️ |
You should begin by creating an AWS account to deploy to.
- Log in to the AWS Console and navigate to the IAM Dashboard.
- Create a user for yourself with the AdministratorAccess policy.
- Run
aws configure
and enter your user ID and secret key when prompted.
The AWS account will need to be bootstrapped to facilitate deployments through CDK.
- Install pip and npm.
- Run
npm install && pip3 -r requirements.txt
. - Run
cdk bootstrap
- GitHub runners are trusted by GitHub.
- A GitHub runner uses the Configure AWS Credentials action.
- GitHub passes an OIDC token to AWS to identify the runner as trusted.
- If the runner is for the correct repository and branch, the runner is granted the deployment role.
- The runner executes a number of CDK commands.
- With the deployment role, the runner can access CloudFormation and assume tighter-scoped roles provided by CDK.
This method of authentication bypasses the need for any AWS credentials to be stored directly on the GitHub.
An identity provider is required to allow GitHub runners to identify themselves within AWS.
- Log in to the AWS Console and navigate to the IAM Dashboard.
- Create a new identity provider, GithubOIDCIdentityProvider, of type OpenId Connect:
Provider URL
: https://token.actions.githubusercontent.comAudience
: sts.amazonaws.com
A deployment policy is required to grant trusted users permission to deploy to the account through CDK.
- Log in to the AWS Console and navigate to the IAM Dashboard.
- Create a new policy, CDKDeploymentPolicy
- Use the contents of policies/deployment.json as policy document.
The policy will allow trusted users to assume the required CDK roles and give them full access to CloudFormation.
A role is required to associate identified GitHub runners with the deployment policy.
- Log in to the AWS Console and navigate to the IAM Dashboard.
- Create a new role, GitHubDeploymentRole.
- Configure the role to use the GitHubOIDCIdentityProvider for identification.
- Assign the CDKDeploymentPolicy to the role.
- Replace the trust policy of the role with the contents of policies/trust_github.json
The role can only be assumed by runners for the master branch of the repository, identified through GitHub.
The repository needs to be configured with an environment to use the deployment role.
- From this repository, navigate to Settings > Environments.
- Create a new environment, Deployment.
- Configure the environment to require approval from a trusted user, and set the deployment branch to master.
- Add the following secrets to the environment:
AWS_DEFAULT_REGION
: The name of the deployment region.AWS_DEPLOYMENT_ROLE_ARN
: The ARN of GithubDeploymentRole.AWS_GITHUB_PROVIDER_ARN
: The ARN of GithubIdentityProvider.
Deployment workflows should use the Deployment environment and the Configure AWS Credentials action when deploying, along with permissions to obtain an OIDC token:
---
name: Deploy to AWS
...
jobs:
Deploy:
...
environment: Deployment
permissions:
id-token: write
contents: read
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
role-to-assume: ${{ secrets.AWS_DEPLOYMENT_ROLE_ARN }}
aws-region: ${{ secrets.AWS_DEFAULT_REGION }}
- name: Deploy a stack
run: ...
The following section outlines the workflow for developers working on this repository.
- Run
python3 -m venv .venv
to create a virtual environment. - Run
source .venv/bin/activate
to activate the virtual environment. - Run
npm install && pip3 install -r requirements.txt -r requrements-dev.txt
to install the dependencies.
- Make changes to the code.
- Run
black app.py aws_ecr_test/ --max-line-length 90
to reformat the code. - Push the changes to GitHub.
- Navigate to the Actions page of the repository.
- Manually run the Deploy to AWS workflow for each stack.
Once the stacks have been deployed, the target repository and developers will need access to push and pull images from the deployed registries.
- From the target repository, Navigate to Settings > Environments.
- Create a new environment, Development.
- Add the following secrets to the environment:
AWS_DEFAULT_REGION
: The name of the deployment region.AWS_GITHUB_ROLE_ARN
: The ARN of SampleImageDevelopment...GitHubRole.AWS_ECR_REGISTRY_ARN
: The ARN of SampleImageDevelopment...ImageRepository.
- Repeat steps 2 and 3 for each deployment environment.
- Non-development environments should use master only as the deployment branch.
- Release environments should require approval by a trusted user.
- Log in to the AWS Console and Navigate to the IAM Dashboard.
- Create a user for the developer with programmatic and password access (MFA enabled).
- Assign the user to the following groups:
- SampleImageDevelopment...DeveloperGroup
- SampleImageMaster...DeveloperGroup
- SampleImageRelease...DeveloperGroup
- The image for a feature branch can be manually pushed using
make docker-push
. - The image for a feature branch can be manually pulled using
make docker-pull
. - Pushing to a feature branch with an open pull request will automatically push a changed image to the development registry.
- Merging a feature branch with master will automatically push a changed image to the master registry.
- From the target repository, Navigate to Actions.
- Run the Release workflow (a trusted user will need to approve the workflow).
- The image will be pushed to the release registry.