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

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


目次

目次


概要

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


Dockle

Dockleとは、コンテナイメージの脆弱性検査・診断ツールであり、Dockerfileをビルドしたイメージ内の脆弱性に対して警告・対処法などを提案してくれるツールになります。

github.com


使い所

AWS CDKレイヤー(cdk deploy)内で、Dockerのコンテナイメージのビルド・デプロイをしているケースも多いかと思います。

CDKとコンテナのビルド・デプロイを分離しているケースも多いかと思いますが、シンプルにCDKだけでビルド・デプロイが行えるのが利点です。

    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レイヤーでイメージのビルド・デプロイをしていると、そのイメージの検査をする暇がなかったりしますよね。

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

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


本Constructは

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

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


image-scanner-with-dockle

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

constructs.dev


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

www.npmjs.com


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

github.com


Constructの使い方

インストール

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

npm install image-scanner-with-dockle


宣言

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

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

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

    const imageScannerWithDockle = new ImageScannerWithDockle(this, "ImageScannerWithDockle", {
      imageUri: image.imageUri,
      repository: image.repository,
      ignore: ["CIS-DI-0009"],
    });

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


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

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


ignore

無視したいルールはignoreパラメータに指定可能です。何も無視しない場合はignoreパラメータを省略することができます。

    const imageScannerWithDockle = new ImageScannerWithDockle(this, "ImageScannerWithDockle", {
      imageUri: image.imageUri,
      repository: image.repository,
      ignore: ["CIS-DI-0002", "CIS-DI-0003"],
    });


ignoreの種類に関しては以下をご覧ください。

github.com


SingletonFunction

CDKの話になりますが、これはカスタムリソースLambdaを使用しています。

1スタックで複数のイメージをビルドする際に何度もImageScannerWithDockleを作成する(呼び出す)ことになりますが、毎回同じコードのLambdaを生成するのはもったいないですよね。

ここでは、何度呼び出されてもCDKの内部で同じLambdaを使い回すように、SingletonFunctionというConstructを利用して、Lambdaを1つだけ生成して使い回すようにしています。

docs.aws.amazon.com


実際の使用例として、ソースコードでいうとこの辺りです。

github.com


Trivy版

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

go-to-k.hatenablog.com


最後に

CDK好きとして、Dockle好きとして、CDKをフル活用している環境でDockleによるイメージスキャンを行いたいケースがあったので、作ってみました。

どなたかに刺されば幸いです。