Email VA report of Docker Images in ECR

By January 10, 2020 January 14th, 2020 Blogs

Written by Praful Tamrakar, Senior Cloud Engineer, Powerupcloud Technologies

Amazon ECR is Elastic Container Registry provided by Amazon to store, decrypt and manage container images. Recently AWS announced Image scanning feature for the images stored in the ECR. Amazon ECR uses the Common Vulnerabilities and Exposures (CVEs) database from the open-source CoreOS Clair project and provides you with a list of scan findings.

Building container images in a Continuous Integration (CI) pipeline, pushing these artifacts into ECR has been a common approach adopted widely. But along with this, we would also like to Scan the Container Image and send a Vulnerability Assessment report to the customer. The Email alert will be triggered only if the container has any critical vulnerabilities.

How Can I Scan My Container Images?

Container images can be scanned using lots of third-party tools such as Clair, Sysdig Secure, etc. To use these tools, the required server/database needs to be managed by us. This adds additional effort to the operations team.

To reduce these efforts, we can use the Image scanning feature of the ECR.

  • You can scan your container images stored in ECR manually.
  • Enable Image Scan for the push on your repositories so that each and every image is checked against an aggregated set of Common Vulnerabilities and Exposures (CVEs).
  • Scan Images using an API command thereby allowing you to set up periodic scans for your container images. This ensures continuous monitoring of your images.

Problem Statement

Currently, no direct way to achieve getting the scan results in CloudWatch or CloudTrail.  However,  it can be achieved using the following approach.

Resolution:

  1. Configure an Existing Repository to Scan on Push using AWS CLI
aws ecr put-image-scanning-configuration --repository-name <ECR_REPO_NAME> --image-scanning-configuration scanOnPush=true --region <REGION_CODE>
  1. Getting the image scan findings (scan results from ECR) can be achieved through a Lambda function that will use an API call.
    1. Create an SNS topic with EMAIL as the subscription
    2. Create a lambda function with runtime Python 3.7 or above, and attach AmazonEC2ContainerRegistryPowerUser and AmazonSNSFullAccess to the following AWS provided policy to the Lambda service Role.

b. Paste the following python command which will return the image scan findings summary

import json
from datetime import datetime
from logging import getLogger, INFO
import os
import boto3
from botocore.exceptions import ClientError


logger = getLogger()
logger.setLevel(INFO)

ecr = boto3.client('ecr')
sns = boto3.client('sns')

def get_findings(tag):
    """Returns the image scan findings summary"""
    
    try:
        response = ecr.describe_image_scan_findings(
            repositoryName='<NAME_OF_ECR >',
            registryId='<AWS_ACCOUNT_ID >',
            imageId={
            'imageTag': tag},
        )
        
        criticalresult = {}
        criticalresultList = []
        findings = response['imageScanFindings']['findings']
        for finding in findings:
            if finding['severity'] == "CRITICAL": #Can be CRITICAL | HIGH
                # print(findding['severity'])
                name  = finding['name']
                description = finding['description']
                severity = finding['severity']
                criticalresult["name"] = name
                criticalresult["description"] = description
                criticalresult["severity"] = severity
                criticalresultList.append(criticalresult)
        return criticalresultList
        
            
    except ClientError as err:
        logger.error("Request failed: %s", err.response['Error']['Message'])

def lambda_handler(event, context):
    """AWS Lambda Function to send ECR Image Scan Findings to EMAIL"""
    scan_result = get_findings(event['tag'])
    print (scan_result)
    
    
    sns_response = sns.publish(
    TopicArn='arn:aws:sns:<AWS_REGION_CODE>:<AWS_ACCOUNT_ID>:<SNS_TOPIC>',    
    Message=json.dumps({'default': json.dumps(scan_result)}),
    MessageStructure='json')
    
    print (sns_response)

This email contains the Name, Description and Severity level for the scanned image.

[{
"name": "CVE-2019-2201", 
"description": "In generate_jsimd_ycc_rgb_convert_neon of jsimd_arm64_neon.S, there is a possible out of bounds write due to a missing bounds check. This could lead to remote code execution in an unprivileged process with no additional execution privileges needed. User interaction is needed for exploitation.Product: AndroidVersions: Android-8.0 Android-8.1 Android-9 Android-10Android ID: A-120551338", 
"severity": "CRITICAL"
}]

d. Trigger this from the Jenkins pipeline

stage('Image Scan'){
    node('master'){        
        sh'''

        sleep 60
        if [[ $(aws ecr describe-image-scan-findings --repository-name < ECR_REPO_NAME> --image-id imageTag=${IMAGETAG} --region ap-southeast-1 --registry-id <AWS_ACCOUNT_ID> --output json --query imageScanFindings.findingSeverityCounts.CRITICAL) -gt 0 ]]
          then
           aws lambda invoke --function-name <LAMBDA_FUNCTION_NAME> --invocation-type Event --payload '{"tag":"${IMAGETAG}"}' response.json
        fi

        '''
      
      
         }}

OPTIONAL

You can create a CloudWatch rule that will match the scanning completion event using this event pattern If you don’t want to trigger this lambda function with pipeline but with Cloudwatch event for Event for a Completed Image Push

{
    "version": "0",
    "id": "13cde686-328b-6117-af20-0e5566167482",
    "detail-type": "ECR Image Action",
    "source": "aws.ecr",
    "account": "123456789012",
    "time": "2019-11-16T01:54:34Z",
    "region": "us-west-2",
    "resources": [],
    "detail": {
        "result": "SUCCESS",
        "repository-name": "my-repo",
        "image-digest": "sha256:7f5b2640fe6fb4f46592dfd3410c4a79dac4f89e4782432e0378abcd1234",
        "action-type": "PUSH",
        "image-tag": "latest"
    }
}

This event pattern will match the exact completion’s event API. After that, you can pass all of the matched events to the Lambda function so that it extracts the values of “repository-name”, “image-digest” and “image-tags”, which will be passed to the DescribeImageScanFindings API call. For reference: https://docs.aws.amazon.com/AmazonECR/latest/userguide/ecr-eventbridge.html

Note:

If the ECR repository is not in the same account where Lambda is configured then, you must configure image permission for the image.

  1. Go to ECR console, and select the ECR repository.
  1. Click on Permission in the Left-hand side dashboard.
  2. Click on Edit policy json
{
  "Version": "2008-10-17",
  "Statement": [
    {
      "Sid": "pull and push",
      "Effect": "Allow",
      "Principal": {
        "AWS": [
                    "arn:aws:sts::<AWS_ACCOUNT_ID>:assumed-role/<LAMBDA_ROLE_NAME>"
        ]
      },
      "Action": [
        "ecr:BatchCheckLayerAvailability",
        "ecr:BatchCheckLayerAvailability",
        "ecr:BatchGetImage",
        "ecr:BatchGetImage",
        "ecr:CompleteLayerUpload",
        "ecr:DescribeImageScanFindings",
        "ecr:GetDownloadUrlForLayer",
        "ecr:GetDownloadUrlForLayer",
        "ecr:InitiateLayerUpload",
        "ecr:PutImage",
        "ecr:UploadLayerPart"
      ]
    }
  ]
}
  1. Save it.
  2. Trigger the lambda.

And that’s it..!! Hope you found it useful. Keep following our Blog for more interesting articles.

Join the discussion One Comment

Leave a Reply