PCG logo
Article

How to leverage image vulnerability scanning on AWS ECR

This article covers:

  • Description and details of the AWS ECR image scan feature
  • Outline and details of an automated usage proposal
  • The single parts of the proposed approach shown in detail

The AWS feature

AWS Elastic Container Registry has been able to support the scanning of images for vulnerabilities using the open source project Clair for quite some time now. ClairExternal Link is an open source project used for the static analysis of vulnerabilities in application containers (currently including OCI and Docker). Made available by AWS directly and implemented into ECR, it is a very useful feature to minimize the risk of using endangered software - and stay compliant. The scanning for vulnerabilities should be a good standard in any Dockerized scenario as public images and their heirs can contain many security risks (Top-ten-docker-imagesExternal Link) - which might be overlooked while developing applications that are constantly changed and improved - and new versions of images are pushed to your ECR many times a day.

AWS ECR supports automatically scanning any pushed image per repository or to trigger a scan manually - which can be configured any time in the settings of an individual repository:

image-8fb209a3aac4

Scanning and the list of results can accessed via the management console:

image-379515135900

Findings are shown in the overview with a short summary:

image-6edd71a633e9

Details explain exactly what threat or risk was found:

image-9cf8ca9706e5

For more details about the feature, please see the documentationExternal Link.

So much for the manual usage of the feature. Nobody really wants to scan and check in the console every time a new container is built and pushed 😃

Using the automated image scanning in AWS ECR is a good start to automate, but if nobody looks into it, it doesn’t help much. Getting notified with the results automatically will greatly enhance your awareness for security risks that could be implemented in your application by using insecure images.

Outline and details of the proposed solution

The quick description:

  • Enable “scan on push” - for all repositories in your AWS account (manually enabling is shown in the section above).
  • This will trigger an image scan, each time an image is pushed to one of your repositories.
  • An event of the type “ECR Image Scan” is published to the AWS event bridge when the scan is finished. The event details contain data about the image and number of findings
  • Define an event rule that transforms the data from the event to readable data and forwards it to an SNS topic
  • From the SNS the next target

The data of a typical event used in this example looks like this:

Code Copied!copy-button
{
    "imageDetails": [
        {
            "registryId": "123456789012",
            "repositoryName": "demo-repo",
            "imageDigest": "sha256:7f5b2640fe6fb4f46592dfd3410c4a79dac4f89e4782432e0378abcd1234",
            "imageTags": [
                "2.0.20190115"
            ],
            "imageSizeInBytes": 61283455,
            "imagePushedAt": 1572489492.0,
            "imageScanStatus": {
                "status": "COMPLETE",
                "description": "The scan was completed successfully."
            },
            "imageScanFindingsSummary": {
                "imageScanCompletedAt": 1572489494.0,
                "vulnerabilitySourceUpdatedAt": 1572454026.0,
                "findingSeverityCounts": {
                    "HIGH": 9,
                    "LOW": 5,
                    "MEDIUM": 18
                }
            }
        }
    ]
}

The tiny parts:

For demonstration of the parts needed and the deployment, terraform was used, resources step by step:

1. ECR Repository with “scan on push” enabled:

Code Copied!copy-button
resource "aws_ecr_repository" "ecr_repo" {
  name = "ecr_repo"
  image_scanning_configuration {
    scan_on_push = true
  }
}

2. An AWS Event Bridge rule (formerly Cloudwatch rule, in terraform still Cloudwatch) to receive and handle events:

Code Copied!copy-button
resource "aws_cloudwatch_event_rule" "ecr_scan_event" {
  name          = "ecr_scan_event"
  description   = "Triggered when image scan was completed."
  event_pattern = <<EOF
  {
  "detail-type": ["ECR Image Scan"],
  "source": ["aws.ecr"],
  "detail": {
    "repository-name": [{
      "prefix": "${aws_ecr_repository.ecr_repo.name}"
    }]
  }
}
  EOF
  role_arn = aws_iam_role.ecr_scan_role.arn
}

3. A target for the event rule… an SNS topic:

Code Copied!copy-button
resource "aws_cloudwatch_event_target" "ecr_scan_event_target" {
  rule = aws_cloudwatch_event_rule.ecr_scan_event.name
  arn  = aws_sns_topic.ecr_scan_sns_topic.arn
  input_transformer {
    input_paths    = { "findings" : "$.detail.finding-severity-counts", "repo" : "$.detail.repository-name", "digest" : "$.detail.image-digest", "time" : "$.time", "status" : "$.detail.scanstatus", "tags" : "$.detail.image-tags", "account" : "$.account", "region" : "$.region" }
    input_template = <<EOF
"ECR Image scan results:"
"Time: <time>"
"Acc : <account>"
"Repo: <repo>"
"SHA : <digest>"
"Tags: <tags>"
"Find: <findings>"
EOF
  }
}

4. A SNS subscription which forwards to lambda:

Code Copied!copy-button
resource "aws_sns_topic_subscription" "ecr_scan_sns_topic_subscription" {
  topic_arn = aws_sns_topic.ecr_scan_sns_topic.id
  protocol  = "lambda"
  endpoint  = aws_lambda_function.ecr_scan_notification_lambda.arn
}

5. The target lambda function:

Code Copied!copy-button
resource "aws_lambda_function" "ecr_scan_notification_lambda" {
  function_name = "ecr_scan_notification_lambda"
  filename      = "${path.module}/slackify.zip"
  role          = aws_iam_role.ecr_scan_notification_lambda_role.arn
  runtime       = "python3.8"
  handler       = "slackify.lambda_handler"
  depends_on = [data.archive_file.slackify-zip]
  environment {
    variables = {
      SLACK_WEBHOOK = var.slack_webhook      
    }
  }
}

The lambda function itself is a simple Python script, posting the message in the correct format to the HTTP webhook of the Slack channel:

python
Code Copied!copy-button
import urllib3 
import json
import os

http = urllib3.PoolManager() 

def lambda_handler(event, context): 
    slackWebhook = os.environ.get('SLACK_WEBHOOK')
    msg = {
        "text": event['Records'][0]['Sns']['Message']
    }
    encoded_msg = json.dumps(msg).encode('utf-8')
    resp = http.request('POST',slackWebhook, body=encoded_msg)
    print(
        {
            "message": event['Records'][0]['Sns']['Message'], 
            "status_code": resp.status, 
            "response": resp.data
        }
    )

The sample code can be found hereExternal Link, please feel free to use.

Conclusion

A notification in Slack could look like this:

image-c7c5e364cce8

It contains all information needed to act upon - the details of the security risks found in the image can be retrieved from the console (as shown earlier in the article).

The approach shown here is easy to use, easy to implement, easy to to adapt to more options - and definitely worth the while to enhance security for any containerized solution running on AWS.


Services Used

Continue Reading

Article
Automation
Automated Control Rollout in AWS Control Tower

Control Tower Controls help you to set up guardrails making your environment more secure and helping you ensuring governance across all OUs and accounts.

Learn more
News
Above the Clouds: PCG's Stellar Performance at the AWS LeadMaster Challenge 2024

Wow, what a triumph! Public Cloud Group has just swept the AWS Summit 2024 Lead Master Challenge.

Learn more
Article
AWS Events 2025: The Future is Cloud

As a leading AWS Premier Partner, we're thrilled to present the exciting lineup of AWS events for 2025.

Learn more
Article
Protecting Lambda URLs with Cognito, IAM, Lambda@Edge and CDK

In this article, we’ll look at how to secure Lambda URLs using IAM access control. With complete code to try yourself!

Learn more
See all

Let's work together

United Kingdom
Arrow Down