AWS CDKでTrivyによるコンテナイメージスキャンを行う

「Trivy」を使用して、AWS CDKレイヤーでコンテナイメージのスキャンを行うConstructライブラリをConstruct Hubに公開しました。


目次

目次


概要

AWS CDKで、Trivyを使用して、ECRにアップロードするコンテナイメージのスキャン(セキュリティ・脆弱性検査)を行うConstructを、Construct Hubに公開しました。


Trivy

Trivyとは、コンテナイメージやファイルシステム、はたまたAWSアカウントなど幅広いターゲットに対する脆弱性検査・診断が行えるセキュリティツールです。

本記事では、Dockerfileをもとにビルドしたコンテナイメージに対するスキャンをCDKで実施する用途で使用します。

aquasecurity.github.io


使い所

AWS CDKでは、シンプルにCDKだけで(CDKレイヤー = cdk deploy内で)、Dockerのコンテナイメージのビルド・デプロイが行えます。

    const repository = new Repository(this, "ImageRepository", {
      removalPolicy: RemovalPolicy.DESTROY,
      autoDeleteImages: true,
    });

    const image = new DockerImageAsset(this, "DockerImage", {
      directory: resolve(__dirname, "../../.."),
    });

    const ecrDeployment = new ECRDeployment(this, "DeployImage", {
      src: new DockerImageName(image.imageUri),
      dest: new DockerImageName(`${repository.repositoryUri}:${props.ecrTag}`),
    });


しかし簡単にビルド・プッシュはできますが、そのビルドイメージを同じCDKデプロイの中でイメージスキャンまでする方法は確立されていませんでした

ECRのスキャン機能もありますがこれは非同期で走るものなので、脆弱性検知時にpushを止めるために使うなどは難しい現状でした(自前でカスタムリソースLambdaを作ったりなどすればできますが・・・)。


また、CI/CDパイプライン(CDK外)でイメージ検査をするにしても、そこでシェルやコマンドなどでビルドをしてさらにCDK内でもビルドをしていると、無駄に2回ビルドをしてしまって余計な時間がかかってしまうことがあります(キャッシュを有効活用しているケースを除く)。

さらにCDKでのデプロイフェーズでイメージスキャンをしない場合、もし脆弱性を検出してもそのままアプリケーションのECRにpushされてしまいます (CDの前のCIでイメージスキャンをして止めているケースももちろんあると思いますがそうではないケースも込みで)。


本Constructは

  • CI/CDパイプラインでなく、CDKレイヤー(deploy内)で、Trivyによるイメージスキャンを行う
  • 無駄なビルドを避け、CDKデプロイでビルドしたイメージのアセットを使い回す
  • 脆弱性を検知した際に、アプリケーションのECRへのイメージpushを止める

ということが可能なConstructになります。


image-scanner-with-trivy

今回作ったConstructは、「image-scanner-with-trivy」という名前でConstruct Hubに公開しています。

constructs.dev


また、Construct Hub公開のためには事前にnpmにpublishしている必要があり、npmパッケージとしてのページは以下になります。

www.npmjs.com


ついでにGitHubもよければご覧ください。

github.com


Constructの使い方

インストール

まず、CDKリポジトリでインストールします。

npm install image-scanner-with-trivy


宣言

new ImageScannerWithTrivyが該当Constructになります。

import { ImageScannerWithTrivy } from 'image-scanner-with-trivy';

const repository = new Repository(this, 'ImageRepository', {
  removalPolicy: RemovalPolicy.DESTROY,
  autoDeleteImages: true,
});

const image = new DockerImageAsset(this, 'DockerImage', {
  directory: resolve(__dirname, './'),
});

// Add properties you want for trivy options (ignoreUnfixed, severity, scanners, trivyIgnore, etc).
const imageScanner = new ImageScannerWithTrivy(this, 'ImageScannerWithTrivy', {
  imageUri: image.imageUri,
  repository: image.repository,
});

// By adding `addDependency`, if the vulnerabilities are detected by `ImageScannerWithTrivy`, the following `ECRDeployment` will not be executed, deployment will fail.
const ecrDeployment = new ECRDeployment(this, 'DeployImage', {
  src: new DockerImageName(image.imageUri),
  dest: new DockerImageName(`${repository.repositoryUri}:latest`),
});

ecrDeployment.node.addDependency(imageScanner);


オプション例

trivyのオプションも色々と指定が可能です。以下のAPIリファレンスをご覧ください。(scanners, severity, ignoreUnfixed, trivyIgnore, etc...と揃っています。)

※もちろん明示的に指定しなくても使えますし、「いい感じにCI/CDでのCDKデプロイで効かせたいオプション」をデフォルトで内部で指定しています。

github.com


例えば以下のような感じでオプション指定ができます。trivy自体で使うものではない、Construct特有のオプションも入っていますが、詳細は上記APIリファレンス内のdescriptionをご覧ください。

new ImageScannerWithTrivy(stack, 'ImageScannerWithTrivy', {
  imageUri: image.imageUri,
  repository: image.repository,
  ignoreUnfixed: true,
  severity: [Severity.CRITICAL],
  scanners: [Scanners.VULN, Scanners.SECRET],
  exitCode: 1,
  exitOnEol: 1,
  trivyIgnore: ['CVE-2023-37920', 'CVE-2019-14697 exp:2023-01-01', 'generic-unwanted-rule'],
  memorySize: 4096,
  platform: 'linux/arm64',
});


脆弱性検出時にイメージプッシュを止める

上記CDKコード例のようにecrDeployment.node.addDependency(imageScannerWithTrivy)とし、ECRDeploymentImageScannerWithTrivyに依存するようにすることで先にImageScannerWithTrivyが実行されるようになり、ImageScannerWithTrivyによって脆弱性を検出してエラーになった際にアプリケーションのECRへのイメージpushを止めることができます。

const imageScanner = new ImageScannerWithTrivy(this, 'ImageScannerWithTrivy', {
  imageUri: image.imageUri,
  repository: image.repository,
});

// By adding `addDependency`, if the vulnerabilities are detected by `ImageScannerWithTrivy`, the following `ECRDeployment` will not be executed, deployment will fail.
const ecrDeployment = new ECRDeployment(this, 'DeployImage', {
  src: new DockerImageName(image.imageUri),
  dest: new DockerImageName(`${repository.repositoryUri}:latest`),
});

ecrDeployment.node.addDependency(imageScanner);


公式ドキュメントに掲載!

なんと、Trivyの公式ドキュメントのエコシステムページに本ライブラリを掲載して頂きました!

aquasecurity.github.io


Dockle版

今回はTrivyを使いましたが、Dockleを使用するライブラリも公開しているので良かったらぜひ!

go-to-k.hatenablog.com


最後に

CDKで同期的なコンテナイメージスキャンをしたくて自作ライブラリを作りました。

良かったらぜひ使ってみてください。