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
AWS Lambda: Avoid these common pitfalls

It's a great offering to get results quickly, but like any good tool, it needs to be used correctly.

Learn more
Case Study
Financial Services
Cloud Migration
The VHV Group's Cloud Journey - Strategy for Success

How does an insurance company with more than 4,000 employees balance compliance, modernization, and cost efficiency?

Learn more
Case Study
Financial Services
DevOps
A KYC Archival System for a Digital Bank

Building a KYC archival Cloud platform for a digital bank to store customers’ KYC data.

Learn more
Case Study
Software
DevOps
Accounting Accelerates

What began as a start-up in their parents' basement has developed into a leading provider of cloud-based accounting and financial software within just a few years: sevDesk.

Learn more
See all

Let's work together

United Kingdom
Arrow Down