3分でできる！最高のDockerfileを書いたあとにやるべき1つのこと

# 概要
[Dockerfileを書くためのベストプラクティス](https://qiita.com/zembutsu/items/a96b68277d699f79418d)を読んで、ベストプラクティスなDockerfileを作った/作りたい人が対象です。

そのDockerfileで大丈夫かを3分でチェックできるツールをつくりました。Hadolintのような、__単なるDockerfileのLinterではなく、ビルドしたイメージの中身まで細かく分析します。__
[<img width="400" alt="docklelogo.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14275/75066dfc-e467-677c-0bb8-82f078d1087e.png">](https://github.com/goodwithtech/dockle)
通常のLinterでは原理的に不可能な、**ベースイメージに存在している危険性も含めて調べることができます。**
[![stars](https://img.shields.io/github/stars/goodwithtech/dockle.svg)](https://github.com/goodwithtech/dockle) ←GitHubのStarもらえると嬉しいです。

Dockleの内部で使われているツールはTrivy, Vulsなどと同じなので、そのあたりを踏まえて、動作原理を別記事にまとめました。
[人を震えさせるツール｢Dockle｣の仕組みを解説](https://qiita.com/tomoyamachi/items/8e042e4269427bb3b326)

なお、DockerHubで人気のコンテナに対して試した結果をサイトにして公開しています。操作方法もふくめて、別記事にまとめました。
[DockerHubで公開されているコンテナが安全か確かめてみた【人気のコンテナ上位800個！】](https://qiita.com/tomoyamachi/items/e0e69da521505e73237b#%E6%93%8D%E4%BD%9C%E6%96%B9%E6%B3%95)

さっそく試してみます。

- 2019.6.15 追記: Docker経由での実行方法を追記しました
- 2019.6.17 追記: 
  - `--ignore`オプションを追加しました
  - IGNOREした項目のメッセージを表示しないよう修正しました
  - Linterとの違いについての記述を｢[最後に](#最後に)｣に追加しました
- 2019.6.19 追記: Dockleがどのように動いているかの説明などを別記事にまとめました 
- 2019.7.10 追記: https://containers.goodwith.tech でスキャン結果を公開しました。
 
# 実際にやること

## Homebrew ([Mac](https://brew.sh/) / [Linux](https://docs.brew.sh/Homebrew-on-Linux))

```bash
$ export DOCKER_CONTENT_TRUST=1
$ docker build -t test-image:v1 .
$ brew install goodwithtech/r/dockle
$ dockle test-image:v1
```
## Linux

```bash
$ export DOCKER_CONTENT_TRUST=1
$ docker build -t test-image:v1 .
$ VERSION=$(
 curl --silent "https://api.github.com/repos/goodwithtech/dockle/releases/latest" | \
 grep '"tag_name":' | \
 sed -E 's/.*"v([^"]+)".*/\1/' \
) && curl -L -o dockle.tar.gz https://github.com/goodwithtech/dockle/releases/download/v${VERSION}/dockle_${VERSION}_Linux-64bit.tar.gz
$ tar zxvf dockle.tar.gz
$ ./dockle test-image:v1
```

## Windows

```bash
$ export DOCKER_CONTENT_TRUST=1
$ docker build -t test-image:v1 .
$ VERSION=$(
 curl --silent "https://api.github.com/repos/goodwithtech/dockle/releases/latest" | \
 grep '"tag_name":' | \
 sed -E 's/.*"v([^"]+)".*/\1/' \
) && curl -L -o dockle.zip https://github.com/goodwithtech/dockle/releases/download/v${VERSION}/dockle_${VERSION}_Windows-64bit.zip
$ unzip dockle.zip && rm dockle.zip
$ ./dockle.exe test-image:v1
```

## Docker

```bash
$ VERSION=$(
 curl --silent "https://api.github.com/repos/goodwithtech/dockle/releases/latest" | \
 grep '"tag_name":' | \
 sed -E 's/.*"v([^"]+)".*/\1/' \
) && docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
 goodwithtech/dockle:v${VERSION} test-image:v1
```


リモートのイメージを指定する場合、 `/var/run/docker.sock`の`-v`オプションが不要です。

# 結果

```yaml
PASS    - CIS-DI-0001: Create a user for the container
PASS    - CIS-DI-0005: Enable Content trust for Docker
PASS    - CIS-DI-0006: Add HEALTHCHECK instruction to the container image
PASS    - CIS-DI-0007: Do not use update instructions alone in the Dockerfile
PASS    - CIS-DI-0008: Remove setuid and setgid permissions in the images
PASS    - CIS-DI-0009: Use COPY instead of ADD in Dockerfile
PASS    - CIS-DI-0010: Do not store secrets in ENVIRONMENT variables
PASS    - CIS-DI-0010: Do not store secret files
PASS    - DKL-DI-0001: Avoid sudo command
PASS    - DKL-DI-0002: Avoid sensitive directory mounting
PASS    - DKL-DI-0003: Avoid apt-get/apk/dist-upgrade
PASS    - DKL-DI-0004: Use apk add with --no-cache
PASS    - DKL-DI-0005: Clear apt-get caches
PASS    - DKL-DI-0006: Avoid latest tag
PASS    - DKL-LI-0001: Avoid empty password
PASS    - DKL-LI-0002: Be unique UID
PASS    - DKL-LI-0002: Be unique GROUP
```

ここでチェックされている項目は [CIS Benchmarksの項目](https://www.cisecurity.org/benchmark/docker/)と[Dockerfileのベストプラクティス](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/)の中から抜粋したものです。

失敗すると、どのように直せばいいのか、大体の提案が表示されます。

```yaml
WARN    - CIS-DI-0001: Create a user for the container
        * Last user should not be root
PASS    - CIS-DI-0005: Enable Content trust for Docker
WARN    - CIS-DI-0006: Add HEALTHCHECK instruction to the container image
        * not found HEALTHCHECK statement
PASS    - CIS-DI-0007: Do not use update instructions alone in the Dockerfile
INFO    - CIS-DI-0008: Remove setuid and setgid permissions in the images
        * Found setuid file: usr/lib/openssh/ssh-keysign urwxr-xr-x
FATAL   - CIS-DI-0009: Use COPY instead of ADD in Dockerfile
        * Use COPY : /bin/sh -c #(nop) ADD file:81c0a803075715d1a6b4f75a29f8a01b21cc170cfc1bff6702317d1be2fe71a3 in /app/credentials.json
FATAL   - CIS-DI-0010: Do not store secrets in ENVIRONMENT variables
        * Suspicious ENV key found : MYSQL_PASSWD
FATAL   - CIS-DI-0010: Do not store secret files
        * Suspicious filename found : app/credentials.json
PASS    - DKL-DI-0001: Avoid sudo command
FATAL   - DKL-DI-0002: Avoid sensitive directory mounting
        * Avoid mounting sensitive dirs : /usr
PASS    - DKL-DI-0003: Avoid apt-get/apk/dist-upgrade
PASS    - DKL-DI-0004: Use apk add with --no-cache
FATAL   - DKL-DI-0005: Clear apt-get caches
        * Use 'rm -rf /var/lib/apt/lists' after 'apt-get install' : /bin/sh -c apt-get update && apt-get install -y git
PASS    - DKL-DI-0006: Avoid latest tag
FATAL   - DKL-LI-0001: Avoid empty password
        * No password user found! username : nopasswd
PASS    - DKL-LI-0002: Be unique UID
PASS    - DKL-LI-0002: Be unique GROUP
```

よくわからないときは、[README](https://github.com/goodwithtech/dockle#checkpoint-summary)でコード(`CIS-DI-0001`など)を検索してみると、修正のヒントが見つかるかもしれません。

すべてPASSすればいいですが、PASSしなくても大丈夫です。
あくまで指標としてご利用ください。

たとえばrootユーザを避ける、というベストプラクティスはあります。
しかし、rootユーザで実行することもあります。
CLIツールなどは、基本的にHEALTHCHECKを利用しません。
tarファイルを解凍して追加したいときは、ADDを使うこともあります。


ベストプラクティスから外れるDockerfileが意図したものかを考えるきっかけになれば嬉しいです。

無視したいルールがある場合、 `-ignore, -i`オプションでコードを指定すると、そのルールを無視します。

```shell
$ dockle -i CIS-DI-0001 -i CIS-DI-0006 [IMAGE_NAME]
```

あるいは、`.dockleignore`というファイルに指定したコードを書いても無視してくれます。

```shell:.dockleignore
# rootで実行
CIS-DI-0001
# HEALTHCHECKは使わない
CIS-DI-0006
# latestタグ使う
DKL-DI-0006
```

さきほどダメだったファイルに実行すると、以下のようになります。

```yaml
IGNORE  - CIS-DI-0001: Create a user for the container
INFO    - CIS-DI-0005: Enable Content trust for Docker
        * export DOCKER_CONTENT_TRUST=1 before docker pull/build
IGNORE  - CIS-DI-0006: Add HEALTHCHECK instruction to the container image
PASS    - CIS-DI-0007: Do not use update instructions alone in the Dockerfile
INFO    - CIS-DI-0008: Remove setuid and setgid permissions in the images
        * Found setuid file: usr/lib/openssh/ssh-keysign urwxr-xr-x
IGNORE  - CIS-DI-0009: Use COPY instead of ADD in Dockerfile
FATAL   - CIS-DI-0010: Do not store secrets in ENVIRONMENT variables
        * Suspicious ENV key found : MYSQL_PASSWD
FATAL   - CIS-DI-0010: Do not store secret files
        * Suspicious filename found : app/credentials.json
PASS    - DKL-DI-0001: Avoid sudo command
FATAL   - DKL-DI-0002: Avoid sensitive directory mounting
        * Avoid mounting sensitive dirs : /usr
PASS    - DKL-DI-0003: Avoid apt-get/apk/dist-upgrade
PASS    - DKL-DI-0004: Use apk add with --no-cache
FATAL   - DKL-DI-0005: Clear apt-get caches
        * Use 'rm -rf /var/lib/apt/lists' after 'apt-get install' : /bin/sh -c apt-get update && apt-get install -y git
PASS    - DKL-DI-0006: Avoid latest tag
FATAL   - DKL-LI-0001: Avoid empty password
        * No password user found! username : nopasswd
PASS    - DKL-LI-0002: Be unique UID
PASS    - DKL-LI-0002: Be unique GROUP
```

# 最後に

`Dockle`は、Dockerイメージを分析します。__単なる Dockerfile Linter ではありません。__

だからこそLinterには不可能な、コンテナ内のファイル権限などもチェックできます。
競合ツールに比べ、チェックできる重要項目が多いのです。

面白いと思った方は、スターください!! [![stars](https://img.shields.io/github/stars/goodwithtech/dockle.svg)](https://github.com/goodwithtech/dockle)


もちろん stdinで作ったイメージにも使え、汎用性が高いです。

```shell
docker build -<<EOF
FROM busybox
RUN echo "hello world"
EOF
```

気になる点があれば、[GitHub](https://github.com/tomoyamachi)や[Twitter](https://twitter.com/tomoyamachi)でもいいのでコメント/意見をください。


また、2019/6/17(月)の[Vuls祭り](https://vuls-jp.connpass.com/event/131960/)で、もう少し詳しい話をします。
通常1000円かかるところを、無料で参加できる｢ブログ枠｣も残ってるのでぜひお越しください。