Step-by-Step Guide: Signing Container Images in ECR from an External Account using AWS Signer and CodeBuildIntroduction
This document provides a step-by-step guide for signing container images stored in an Amazon Elastic Container Registry (ECR) repository in one AWS account, using AWS Signer to sign them from another account through a CodeBuild pipeline. This approach is useful in scenarios where container image development, building, and deployment are distributed across multiple AWS accounts, maintaining high-security standards and control over image signatures.
AWS Signer, in combination with Notation, offers a secure and automated mechanism to ensure the authenticity and integrity of container images before their deployment. In this guide, we will cover the necessary cross-account signing permissions, AWS Signer and ECR configurations, and how to integrate this flow into a buildspec.yml file for execution from CodeBuild.
Step 1: Create the Signing Profile in AWS Signer in the ECR Account
Sign in to the AWS Console in the account where the ECR repository is located (Account A).
Navigate to AWS Signer.
Click on Create Signing Profile.
Assign a name to the profile, such as container_signer_profile.
Select the platform type Notation for container registries.
Configure expiration and format options according to your needs (the default settings are generally suitable).
Create and save the profile ARN (you will use it later).
Step 2: Set Up Accounts and Permissions
2.1. Permissions in the ECR Repository Account (Account A)
ECR Repository Permissions:
Add a permissions policy to the ECR repository to allow the account where CodeBuild runs (Account B) to access the repository and describe images. Use a resource-based policy on the ECR repository:
{
  "Version": "2008-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::[ACCOUNT_B_ID]:root"
      },
      "Action": [
        "ecr:DescribeImages",
        "ecr:BatchCheckLayerAvailability",
        "ecr:GetDownloadUrlForLayer",
        "ecr:ListImages"
      ],
      "Resource": "arn:aws:ecr:[AWS_REGION
[ACCOUNT_A_ID]:repository/[REPO_NAME]"
    }
  ]
}
Signing Profile Permissions in AWS Signer:
Use the following commands to add cross-account signing permissions for Account B:
aws signer add-profile-permission \
  --profile-name [SIGNING_PROFILE_NAME] \
  --action signer:SignPayload \
  --principal [ACCOUNT_B_ID] \
  --statement-id allow-signature-cross-account
aws signer add-profile-permission \
  --profile-name [SIGNING_PROFILE_NAME] \
  --action signer:GetSigningProfile \
  --principal [ACCOUNT_B_ID] \
  --statement-id allow-get-profile-cross-account
2.2. Permissions in the CodeBuild Account (Account B)
CodeBuild Role Permissions:
The role used by CodeBuild must have permission to interact with ECR and AWS Signer in Account A. The role needs the following permissions:
{
 "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ecr:BatchCheckLayerAvailability",
        "ecr:GetDownloadUrlForLayer",
        "ecr:DescribeImages",
        "ecr:PutImage"
      ],
      "Resource": "arn:aws:ecr:[AWS_REGION]:[ACCOUNT_A_ID]:repository/[REPO_NAME]"
    },
    {
      "Effect": "Allow",
      "Action": [
        "signer:SignPayload",
        "signer:GetSigningProfile"
      ],
      "Resource": "arn:aws:signer:[AWS_REGION]:[ACCOUNT_A_ID]:signing-profiles/[SIGNING_PROFILE_NAME]"
}
  ]
}
Step 3: Configure the buildspec.yml
In the buildspec.yml file, follow these steps to execute the signing process.
Login to ECR in Account A:Â Ensure you log in to the repository in Account A:
phases:
  pre_build:
    commands:
      - aws ecr get-login-password --region ${AWS_REGION} | docker
login --username AWS --password-stdin ${AWS_ACCOUNT_A}.dkr.ecr.${AWS_REGION}.amazonaws.com
Get the Image Digest:Â Use the following command to get the digest of the image you want to sign:
build:
  commands:
    - IMAGE_DIGEST=$(aws ecr describe-images --registry-id ${AWS_ACCOUNT_A} --repository-name ${ECR_REPO_NAME} --region ${AWS_REGION} --query 'imageDetails[?contains(imageTags, `test`)].imageDigest' --output text)
    - echo "Image Digest: $IMAGE_DIGEST"
Sign the Image:Â Run the command to sign the image using the signing profile in Account A:
- notation sign --plugin com.amazonaws.signer.notation.plugin --id arn:aws:signer:${AWS_REGION}:${AWS_ACCOUNT_A}:signing-profiles/${SIGNING_PROFILE_NAME} ${AWS_ACCOUNT_A}.dkr.ecr.${AWS_REGION}.amazonaws.com/${ECR_REPO_NAME}@${IMAGE_DIGEST}
Step 4: Validate the Signature
Verify that the signature was successfully applied by checking the process output. Confirm that the image now contains the signature by consulting the ECR repository or using Notation to validate.
Diagram of solution.
Step 5: Troubleshooting
Error 403 (Forbidden):Â Review the permissions on the ECR repository and ensure the CodeBuild role has access to describe and sign the images.
Notation Errors:Â Ensure that the Notation tool is correctly installed and that the versions are compatible.
Step 6: Next Steps.
Consider including a step in your deployment to check if your image is signed before being deployed, if not you must cancel your deployment.Â
MartÃn Carletti
Cloud Engineer
Teracloud
Source Documentation: