コドモン Product Team Blog

株式会社コドモンの開発チームで運営しているブログです。エンジニアやPdMメンバーが、プロダクトや技術やチームについて発信します!

Amazon Inspectorを利用したECRの脆弱性検知の取り組み

こんにちは、コドモンSREチームの渡辺です。

コドモンでは、アプリケーションのコンテナ基盤としてECSを利用しています。私たちのチームでは、Amazon Inspector(以下、Inspector)を利用したECRの脆弱性を検知する仕組みを導入しています。

本記事では、検知の仕組みについて紹介します。ECRの脆弱性検知のアーキテクチャを検討されている方の役に立てれば幸いです。

Inspectorとは

Inspectorは、セキュリティ脆弱性評価サービスであり、EC2やECR、LambdaなどAWSリソースに関するセキュリティギャップや脆弱性を検出し、推奨事項を提供してくれます。

詳しくは、公式ドキュメントをご確認ください。

検知の仕組み

  1. BuildしたイメージをECRにPushする
  2. Inspectorがイメージをスキャンする
  3. Criticalな脆弱性を検出した際にEventBridgeがLambdaを発火する
  4. Lambdaがメッセージを加工し、Slackに通知する
  5. 担当者がInspectorで詳細を確認する

Inspectorが検出する脆弱性は、CriticalやHigh、Mediumなど重大性に応じてランク付けされます。

検出された脆弱性に関しては全て対応するのが理想ですが、開発と保守のバランスを取るために、まずはCriticalの検出を通知する運用にしています。

それでは、検知の仕組みを構築するために必要なリソースの設定について説明していきます。

InspectorとECR

Inspectorを利用するために、まずはInspectorをアクティブ化します。Inspectorがアクティブになると、ECRのプライベートレジストリのスキャン設定が拡張版になります。

デフォルトだと連続スキャンフィルターが設定されますが、Push時にスキャンを走らせる場合はプッシュ時にすべてのリポジトリをスキャンするを選択します。

EventBridge

EventBridgeのルールは以下になります。Highなども検知したい場合は、severityに追加します。

{
  "source": ["aws.inspector2"],
  "detail-type": ["Inspector2 Finding"],
  "detail": {
    "severity": ["CRITICAL"],
    "status": ["ACTIVE"]
  }
}

Lambda

Lambdaではeventを解析し、加工したメッセージをSlackに送ります。

import json
import os
import urllib.request

def lambda_handler(event, context):
    findings = event.get('detail', {})

    severity = findings['severity']
    title = findings['title']

    messages = []
    resources = findings.get('resources', {})
    for resource in resources:
        if resource['type'] == "AWS_ECR_CONTAINER_IMAGE":
            messages.append(ecr_inspector_messeage(resource['details']['awsEcrContainerImage']))

    # Prepare the final message
    if resource['type'] == "AWS_ECR_CONTAINER_IMAGE":
        final_message = f"<!here>\n*Alert: AWS Inspector has detected a vulnerability!!!*\n\n*Level*: {severity}\n*Vulnerability Title*: {title}" + "\n".join(messages)
    else:
        return {
            'statusCode': 200,
            'body': json.dumps('Alert do not sent to Slack!')
        }

    #Post the message to the Slack webhook URL
    slack_webhook_url = os.environ.get('SLACK_WEBHOOK_URL')
    data = json.dumps({'text': final_message}).encode('utf-8')
    req = urllib.request.Request(slack_webhook_url, data, {'Content-Type': 'application/json'})
    urllib.request.urlopen(req)

    return {
        'statusCode': 200,
        'body': json.dumps('Alert sent to Slack!')
    }

def ecr_inspector_messeage(aws_ecr_container_image):
    aws_account_id = aws_ecr_container_image['registry']
    image_tag = aws_ecr_container_image['imageTags'][0]
    repository_name = aws_ecr_container_image['repositoryName']

    message = f"""
*AWS Account ID*: {aws_account_id}
*Repository Name*: {repository_name}
*Image Tag*: {image_tag}
"""

    return message

加工したメッセージは、以下の形で通知されます。なお、担当チームが対応手順を確認するための動線としてドキュメントのリンクを記載しています。

抑制ルールの活用

Inspectorには、ルールに一致する検出結果を非表示にする抑制ルールという機能があります。アプリケーションに影響がなく、かつ対応が困難な場合に利用するようにしています。

2023年9月現在、Inspectorの抑制ルールはTerraformで管理できないリソースのため、Terraformを管理しているリポジトリで意思決定を記録しています。担当チームが以下Templateを元にPRを作成し、SREがAWSコンソールで抑制ルールを作成する運用になります。

## hogeの脆弱性抑制ルール(Template)

### 担当チーム
hogeチーム

### 該当ECRリポジトリ
hoge-api

### 抑制ルール適用月
2023年9月

### 脆弱性タイトル
CVE-202x-xxxx - hoge

### 抑制ルールを作成する理由
hogeのバージョンを上げると、fugaのバージョンを上げる必要があるが、fugaのバージョンを上げると、piyoのバージョンを上げる必要がある。
piyoのバージョンを上げると、hogeのバージョンを上げる必要がある。このようなループが発生するため、抑制ルールを作成する。

### 抑制ルール解除月
2024年1月

まとめ

InspectorによるECRの脆弱性検知の仕組みを紹介しました。

AWSのセキュリティサービスは日々アップデートしているので、今後も情報をキャッチアップしながら積極的に改善に取り組んでいきます。

また、運用面で課題が出てきたら開発チームと連携し、臨機応変に対応していきたいです。

最後に

コドモンでは、今後も新しいサービスの開発や既存サービスのリプレイスなどを予定しており、一緒に盛り上げてくださる方を募集しています。

一緒に働くことに興味がございましたら、ぜひ求人情報もご覧ください!