Dockleとは

DockleはセキュリティのためのDockerイメージのLinter。Dockerfileのベストプラクティス のチェック、Dockerイメージのセキュリティ診断を行なう。

Dockleの使い方

Dockleのインストール

Dockleのインストールは以下のコマンドで行なう。

Macの場合はHomebrewでインストールできる。

$ brew install goodwithtech/r/dockle

他のOSの場合はReleases · goodwithtech/dockle から対応する パッケージをダウンロードしてインストールする。

確認用のDockerイメージの作成

Dockleの実行を確認するために以下のDockerfileを作成する。

$ mkdir dockle-test
$ cd dockle-test

Dockerfileは以下のようにする。

FROM public.ecr.aws/amazonlinux/amazonlinux:2023.2.20231011.0

ADD docker-compose.yml /docker-compose.yml

docker-compose.ymlを以下のようにする。

version: "3"

services:
  linux:
    build: .

Dockerイメージをビルドする。

$ docker compose build

Dockleの実行

Dockerイメージのビルドが完了したらDockleを実行する。

まずイメージの名前を確認する。

$ docker images
REPOSITORY                                                  TAG       IMAGE ID       CREATED         SIZE
dockle-test_linux                                           latest    6a4a5cc5b2f7   10 days ago     179MB

名前はdockle-test_linuxでタグはlatestとなっている。

Dockleの実行はDockerイメージ名:タグ名を指定して以下のように行なう。

$ dockle イメージ名:タグ名

今回の場合は以下のようになる。

$ dockle dockle-test_linux:latest

実行結果は以下のように標準出力に出力される。

FATAL - CIS-DI-0009: Use COPY instead of ADD in Dockerfile
  * Use COPY : ADD docker-compose.yml /docker-compose.yml # buildkit
WARN  - CIS-DI-0001: Create a user for the container
  * Last user should not be root
WARN  - DKL-DI-0006: Avoid latest tag
  * Avoid 'latest' tag
INFO  - CIS-DI-0005: Enable Content trust for Docker
  * export DOCKER_CONTENT_TRUST=1 before docker pull/build
INFO  - CIS-DI-0006: Add HEALTHCHECK instruction to the container image
  * not found HEALTHCHECK statement
INFO  - DKL-LI-0003: Only put necessary files
  * unnecessary file : docker-compose.yml 

レベル

Dockleの実行結果には5段階のレベルが付いている。

  • FATAL
  • WARN
  • INFO
  • SKIP
  • PASS

チームやプロジェクトでどの段階までを許容するかを決めておき、そのレベル以上の結果が出た場合はエラーとするなどの運用を行なう。
どのレベル以上をエラーとするかは--exit-levelオプション(-lオプション)で指定する。(後述)
デフォルトではWARN以上の結果が出た場合は--exit-codeで指定した終了ステータス(デフォルトは0)となる。

検出対象からの除外

コマンドオプションで除外する

dockleコマンドに--ignoreオプション(-iオプション)を付け、除外したいルールのIDを指定すると検出対象から除外できる。

$ dockle -i DKL-DI-0006 dockle-test_linux:latest
FATAL   - CIS-DI-0009: Use COPY instead of ADD in Dockerfile
        * Use COPY : ADD docker-compose.yml /docker-compose.yml # buildkit
WARN    - CIS-DI-0001: Create a user for the container
        * Last user should not be root
INFO    - CIS-DI-0005: Enable Content trust for Docker
        * export DOCKER_CONTENT_TRUST=1 before docker pull/build
INFO    - CIS-DI-0006: Add HEALTHCHECK instruction to the container image
        * not found HEALTHCHECK statement
INFO    - DKL-LI-0003: Only put necessary files
        * unnecessary file : docker-compose.yml

検出結果からDKL-DI-0006が除外されている。

複数のIDを指定する場合は--ignoreオプション(-iオプション)を繰り返し指定する。

$ dockle -i DKL-DI-0006 -i CIS-DI-0001 dockle-test_linux:latest
FATAL   - CIS-DI-0009: Use COPY instead of ADD in Dockerfile
        * Use COPY : ADD docker-compose.yml /docker-compose.yml # buildkit
INFO    - CIS-DI-0005: Enable Content trust for Docker
        * export DOCKER_CONTENT_TRUST=1 before docker pull/build
INFO    - CIS-DI-0006: Add HEALTHCHECK instruction to the container image
        * not found HEALTHCHECK statement
INFO    - DKL-LI-0003: Only put necessary files
        * unnecessary file : docker-compose.yml

DKL-DI-0006、CIS-DI-0001が除外できている。

環境変数で除外する

DOCKLE_IGNORES環境変数に除外したいルールのIDをカンマ区切りで指定すると検出対象から除外できる。

$ export DOCKLE_IGNORES=DKL-DI-0006
$ dockle dockle-test_linux:latest
FATAL   - CIS-DI-0009: Use COPY instead of ADD in Dockerfile
        * Use COPY : ADD docker-compose.yml /docker-compose.yml # buildkit
WARN    - CIS-DI-0001: Create a user for the container
        * Last user should not be root
INFO    - CIS-DI-0005: Enable Content trust for Docker
        * export DOCKER_CONTENT_TRUST=1 before docker pull/build
INFO    - CIS-DI-0006: Add HEALTHCHECK instruction to the container image
        * not found HEALTHCHECK statement
INFO    - DKL-LI-0003: Only put necessary files
        * unnecessary file : docker-compose.yml

複数のルールを除外する場合はカンマ区切りで指定する。

$ export DOCKLE_IGNORES=DKL-DI-0006,CIS-DI-0001
$ dockle dockle-test_linux:latest
FATAL   - CIS-DI-0009: Use COPY instead of ADD in Dockerfile
        * Use COPY : ADD docker-compose.yml /docker-compose.yml # buildkit
INFO    - CIS-DI-0005: Enable Content trust for Docker
        * export DOCKER_CONTENT_TRUST=1 before docker pull/build
INFO    - CIS-DI-0006: Add HEALTHCHECK instruction to the container image
        * not found HEALTHCHECK statement
INFO    - DKL-LI-0003: Only put necessary files
        * unnecessary file : docker-compose.yml

.dockleignoreで除外する

.dockleignoreファイルを作成し、除外したいルールのIDを記述すると検出対象から除外できる。

$ echo "DKL-DI-0006" > .dockleignore
$ echo "CIS-DI-0001" >> .dockleignore
$ cat .dockleignore
DKL-DI-0006
CIS-DI-0001
$ dockle dockle-test_linux:latest
FATAL   - CIS-DI-0009: Use COPY instead of ADD in Dockerfile
        * Use COPY : ADD docker-compose.yml /docker-compose.yml # buildkit
INFO    - CIS-DI-0005: Enable Content trust for Docker
        * export DOCKER_CONTENT_TRUST=1 before docker pull/build
INFO    - CIS-DI-0006: Add HEALTHCHECK instruction to the container image
        * not found HEALTHCHECK statement
INFO    - DKL-LI-0003: Only put necessary files
        * unnecessary file : docker-compose.yml

チェック項目

Dockleでチェックする項目は以下の3カテゴリー。

Overview of best practices for writing Dockerfiles | Docker Docs

GitHub Actionsでの実行

GitHub ActionsでDockleを実行するにはerzz/dockle-action を使用すると簡単に設定できる。

name: dockle

on:
  pull_request:
  push:
    branches:
      - master

jobs:
  dockle-test:
    runs-on: ubuntu-latest
    steps:
      # Gitリポジトリをチェックアウト
      - name: Checkout code
        uses: actions/checkout@v4
      # Dockerイメージを作成
      - name: Build image
        run: |
          cd dockle-test
          docker build -t dockle-test:${{ github.sha }} .          
      # Dockleでチェック
      - name: Run Dockle
        uses: erzz/dockle-action@v1
        with:
          image: 'dockle-test:${{ github.sha }}'
          failure-threshold: fatal
          exit-code: 1

上記の例ではFATALレベルの結果が出た場合はエラー(終了ステータスコード1)となるように設定している。
求めるレベルに応じてfailure-thresholdexit-codeを設定する。

AWS CodeBuildでの実行

AWS CodeBuildに組み込むにはbuildspec.ymlにて以下の手順で記述する。

  • pre_buildフェーズ: Dockleをインストール
  • buildフェーズ: Dockerイメージをビルド
  • post_buildフェーズ: Dockleでチェック

以下はbuildspec.ymlの例。

version: 0.2

phases:
  pre_build:
    commands:
    # ECRへログイン
    - AWS_ACCOUNT_ID=$(echo ${CODEBUILD_BUILD_ARN} | cut -f 5 -d :)
    - aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin ${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com

    # Dockleの最新RPMをGitHubからダウンロードしてインストール
    - VERSION=$(curl --silent "https://api.github.com/repos/goodwithtech/dockle/releases/latest" | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/\1/') && rpm -ivh https://github.com/goodwithtech/dockle/releases/download/v${VERSION}/dockle_${VERSION}_Linux-64bit.rpm

    # Dockerイメージ名を変数IMAGEへ設定
    - REPO=$(aws ecr describe-repositories --repository-names dockle-test --output text --query "repositories[0].repositoryUri")
    - IMAGE=$REPO:latest

  build:
    commands:
    # Dockerイメージをビルド
    - docker image build -t $IMAGE ./dockle-test

  post_build:
    commands:
    # Dockleでチェック
    - dockle --exit-code 1 --exit-level FATAL $IMAGE
    - exit `echo $?`

    # チェック済みのイメージをECRへプッシュ
    - docker image push $IMAGE

Tips

ANSI エスケープシーケンスを消す(–no-output)

Dockleの実行結果をpbcopyなど別のコマンドに渡すと色をつけるためのANSIエスケープシーケンスが含まれてしまう。

$ dockle dockle-test_linux:latest | pbcopy

コピーした結果は以下のようになる。

[31mFATAL[0m  - [36mCIS-DI-0009[0m: Use COPY instead of ADD in Dockerfile
  * Use COPY : ADD docker-compose.yml /docker-compose.yml # buildkit
[35mINFO[0m - [36mCIS-DI-0005[0m: Enable Content trust for Docker
  * export DOCKER_CONTENT_TRUST=1 before docker pull/build
[35mINFO[0m - [36mCIS-DI-0006[0m: Add HEALTHCHECK instruction to the container image
  * not found HEALTHCHECK statement
[35mINFO[0m - [36mDKL-LI-0003[0m: Only put necessary files
  * unnecessary file : docker-compose.yml 

[31mなどのANSIエスケープシーケンスが含まれており、人が読む場合は読みづらく、別のコマンドに渡す場合は処理が必要となり不便である。

--no-outputオプションを付けるとANSIエスケープシーケンスを消してくれる。

$ dockle --no-color dockle-test_linux:latest | pbcopy

コピーした結果は以下のようになる。

FATAL - CIS-DI-0009: Use COPY instead of ADD in Dockerfile
  * Use COPY : ADD docker-compose.yml /docker-compose.yml # buildkit
INFO  - CIS-DI-0005: Enable Content trust for Docker
  * export DOCKER_CONTENT_TRUST=1 before docker pull/build
INFO  - CIS-DI-0006: Add HEALTHCHECK instruction to the container image
  * not found HEALTHCHECK statement
INFO  - DKL-LI-0003: Only put necessary files
  * unnecessary file : docker-compose.yml 

ANSIエスケープシーケンスが含まれない。

指定したレベルで終了ステータスを1にする(–exit-code、–exit-level)

dockleコマンドの終了ステータスコードのデフォルトは0で、CIに組み込む場合は成功とみなされる。
問題を検出した場合にプログラムへ失敗と認識してほしい場合は終了ステータスコードを1など、0以外に設定する必要がある。

問題検出時の終了ステータスコードを指定にするには--exit-codeオプション(-eオプション)を付ける。

$ dockle --exit-code 1 dockle-test_linux:latest
WARN  - CIS-DI-0001: Create a user for the container
  * Last user should not be root
WARN  - DKL-DI-0006: Avoid latest tag
  * Avoid 'latest' tag

$ echo $?
1

上記のとおり--exit-codeオプション(-cオプション)を付けるとWARNレベル以上の問題を検出した場合に終了ステータスコードが1となる。

問題検出のしきい値を変更したい場合は--exit-levelオプション(-lオプション)を付ける。

$ dockle --exit-code 1 --exit-level FATAL dockle-test_linux:latest
WARN  - CIS-DI-0001: Create a user for the container
  * Last user should not be root
WARN  - DKL-DI-0006: Avoid latest tag
  * Avoid 'latest' tag

$ echo $?
0

$ dockle --exit-code 1 --exit-level FATAL dockle-test_linux:latest
FATAL  - CIS-DI-0009: Use COPY instead of ADD in Dockerfile
  * Use COPY : ADD docker-compose.yml /docker-compose.yml # buildkit
WARN  - CIS-DI-0001: Create a user for the container
  * Last user should not be root
WARN  - DKL-DI-0006: Avoid latest tag
  * Avoid 'latest' tag

$ echo $?
1

上記では--exit-levelオプション(-lオプション)にFATALを指定しているのでFATALレベルの問題を検出した場合にのみ終了ステータスコードが1となっている。