Amazon Elastic Container Registry is a fully-managed Docker container registry. It makes it easy for developers to store and manage Docker images inside their AWS environment. ECR supports two types of image scanning. Enhanced image scanning requires an integration with Amazon Inspector. It will scan your repositories continuously. Basic image scanning will use the Common Vulnerabilities and Exposures (CVEs) database (open-source Clair) to find vulnerabilities in your images. You can trigger scans on image push or manually.

Although it’s a very useful feature to find vulnerabilities in your images, it’s still up to you to do something with this information. By using Amazon Eventbridge, you can configure some action in response to the result of a scan.

In this case we will automatically tag images which contain CRITICAL vulnerabilities with a vulnerable prefix. Images with this prefix will be removed from your repository in 5 days. You can not give the same tag to multiple images in one repository. That’s why we will use prefixes.

architecture of tagging solution

The Eventbridge rule (*) to create this condition:

EventTrigger:
  Type: "AWS::Events::Rule"
  Properties:
    Description: Trigger Lambda if a scan contains critical vulnerabilities
    EventPattern:
      source:
        - "aws.ecr" 
      detail-type:
        - "ECR Image Scan" 
      detail:
        finding-severity-counts:
          CRITICAL:
            - exists: true
            - numeric: [ ">", 0 ]
    State:  "ENABLED"
    Targets:
    - Arn: !GetAtt LambdaFunction.Arn
      Id: scan-ecr

(*) I was not able to make this working with an “OR” condition to tag the image when it contains critical “OR” high vulnerabilities. If you need an OR condition you can create multiple rules (with the risk to trigger your Lambda twice when you have both high and critical vulnerabilities). Another solution could be to forward all scan events and do the filtering inside the Lambda.

As you can see we’ve configured a LambdaFunction as target of our rule. To make this work we still need to provide the permission for our Eventbridge rule to trigger the Lambda.

PermissionForEventsToInvokeLambda: 
  Type: "AWS::Lambda::Permission"
  Properties: 
    FunctionName: !GetAtt LambdaFunction.Arn
    Action: "lambda:InvokeFunction"
    Principal: "events.amazonaws.com"
    SourceArn: !GetAtt EventTrigger.Arn

The Lambda will add the vulnerable tag prefix by using the ecr client its put_image function. By using this function we can add a tag to the image without the need to pull or push the Docker image.

def add_image_tag(repo, manifest, digest):
    """Add 'vulnerable' tag prefix to image."""
    try:
        response = client.put_image(
            repositoryName=repo,
            imageManifest=manifest,
            imageTag="vulnerable-" + digest[7:],
            imageDigest=digest,
        )
    except ClientError as error:
        logging.error("Unexpected error")
        raise error

    LOGGER.info("Image tag 'vulnerable' added!")
    return response

As you can see, we only assign the following policies to our Lambda role.

  • “ecr:BatchGetImage”
  • “ecr:PutImage”

We don’t need to assign any additional policies like ecr:InitiateLayerUpload or ecr:UploadLayerPart which are required when you really want to push a Docker image.

To test our setup we can pull, tag and push an image to our registry.

$ docker pull node:11-stretch-slim
$ docker tag node:11-stretch-slim 123456789012.dkr.ecr.eu-west-1.amazonaws.com/node:11-stretch-slim
$ docker push 123456789012.dkr.ecr.eu-west-1.amazonaws.com/node:11-stretch-slim

Just after the push, the scan is still running.

ecr scan in progress

After the scan the image is tagged with a vulnerable prefix!

ecr scan done

We can push some additional images to test the implementation.

ecr test additional images

The solution is working! Still there are some “missing” features. As already mentioned, the OR Condition in the Eventbridge rule is not working. So we’re currently only filtering for CRITICAL images. Next it would be nice if we could use Docker image tag prefixes in IAM policies, so we can deny that vulnerable images are being pulled. In my solution I’m removing images with this tag prefix after 5 days.

{
  "rulePriority": 1,
  "description": "Remove vulnerable images after 5 days",
  "selection": {
    "tagStatus": "tagged",
    "tagPrefixList": ["vulnerable"],
    "countType": "sinceImagePushed",
    "countUnit": "days",
    "countNumber": 5
  },
  "action": { "type": "expire" }
}

That being said, I hope you enjoyed reading this post and that you will start thinking about vulnerabilities in Docker images! The full solution is available on my GitHub.

buy me a coffee