どうも、若松です。
最近、コンテナイメージをビルドしまくっており、オレオレなコンテナイメージが増殖しています。
そんなおり、以下の記事が目に止まりました。
DockerHubで公開されているコンテナが安全か確かめてみた結果【人気のコンテナ上位800個】
そりゃあ脆弱性あるわなぁと思いつつ、じゃあオレのコンテナイメージは大丈夫か?と思いました。
というわけで上記の記事で使用されていたdockle
とTrivy
を使って、オレオレコンテナイメージをチェックしていきたいと思います。
使用ツール
dockle
CISベンチマークやDockerベストプラクティスに準拠したコンテナイメージかをチェックできます。
WARNやFATALの項目には、どのように改善すればよいかのヒントが添えられていて小憎いです
それぞれの基準は以下の通り
CISベンチマーク
Dockerベストプラクティス
Trivy
OSやアプリケーションに潜む脆弱性をチェックできます。
CVE番号と共に脆弱性の詳細を表示してくれるので、どのように改善するかの計画が立てやすいです。
対象OSとアプリケーションは以下の通り
https://github.com/knqyf263/trivy#vulnerability-detection
インストール
今回はMacのローカルにコンテナイメージがあるため、brew
でインストールしました。
手順はそれぞれのREADMEに記載があるため、省略します。
実行結果
前回の記事で作成したLaravelコンテナを対象にしました。
https://qiita.com/t_wkm2/items/910b4d7079b8413895a6
dockle
$ dockle laravel:latest
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
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
WARN - DKL-DI-0006: Avoid latest tag
* Avoid 'latest' tag
PASS - DKL-LI-0001: Avoid empty password
PASS - DKL-LI-0002: Be unique UID
PASS - DKL-LI-0002: Be unique GROUP
大きな問題はありませんでしたが、WARNが3件出て、改善に向けたアドバイスが表示されていることがわかります。
Trivy
$ trivy laravel:latest
2019-07-16T00:26:44.376+0900 INFO Updating vulnerability database...
2019-07-16T00:26:46.203+0900 WARN You should avoid using the :latest tag as it is cached. You need to specify '--clear-cache' option when :latest image is changed
2019-07-16T00:26:48.159+0900 INFO Detecting Alpine vulnerabilities...
2019-07-16T00:26:48.171+0900 INFO Updating composer Security DB...
2019-07-16T00:26:51.928+0900 INFO Detecting composer vulnerabilities...
2019-07-16T00:26:51.928+0900 INFO Updating composer Security DB...
2019-07-16T00:26:53.211+0900 INFO Detecting composer vulnerabilities...
2019-07-16T00:26:53.214+0900 INFO Updating composer Security DB...
2019-07-16T00:26:54.422+0900 INFO Detecting composer vulnerabilities...
2019-07-16T00:26:54.422+0900 INFO Updating composer Security DB...
2019-07-16T00:26:56.052+0900 INFO Detecting composer vulnerabilities...
var/www/laravel/vendor/psy/psysh/vendor-bin/box/composer.lock
=============================================================
Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)
var/www/laravel/vendor/hamcrest/hamcrest-php/composer.lock
==========================================================
Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)
laravel:latest (alpine 3.10.1)
==============================
Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)
var/www/laravel/vendor/phar-io/manifest/composer.lock
=====================================================
Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)
var/www/laravel/composer.lock
=============================
Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)
脆弱性は検出されませんでしたが、AlpineLinuxに加えてComposerも対象に診断を行ってくれることがわかります。
引っかかる場合の表示
上記でチェックしたコンテナイメージは最新のベースイメージであったため、引っかかる項目が少ない結果となりました。
しかしながら、これではチェックに引っかかった場合の表示がわからないため、ずいぶん前(docker images
で見ると9ヶ月前)にpullして手元にあったhttpd:2.4-alpine
をチェックしてみます。
dockle
$ dockle httpd:2.4-alpine
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
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
FATAL - DKL-DI-0004: Use apk add with --no-cache
* Use --no-cache option if use 'apk add': /bin/sh -c set -eux; runDeps=' apr-dev apr-util-dev apr-util-ldap perl '; apk add --no-cache --virtual .build-deps $runDeps ca-certificates coreutils dpkg-dev dpkg gcc gnupg libc-dev libressl libressl-dev libxml2-dev lua-dev make nghttp2-dev pcre-dev tar zlib-dev ; ddist() { local f="$1"; shift; local distFile="$1"; shift; local success=; local distUrl=; for distUrl in $APACHE_DIST_URLS; do if wget -O "$f" "$distUrl$distFile" && [ -s "$f" ]; then success=1; break; fi; done; [ -n "$success" ]; }; ddist 'httpd.tar.bz2' "httpd/httpd-$HTTPD_VERSION.tar.bz2"; echo "$HTTPD_SHA256 *httpd.tar.bz2" | sha256sum -c -; ddist 'httpd.tar.bz2.asc' "httpd/httpd-$HTTPD_VERSION.tar.bz2.asc"; export GNUPGHOME="$(mktemp -d)"; for key in A93D62ECC3C8EA12DB220EC934EA76E6791485A8 B9E8213AEFB861AF35A41F2C995E35221AD84DFF ; do gpg --keyserver ha.pool.sks-keyservers.net --recv-keys "$key"; done; gpg --batch --verify httpd.tar.bz2.asc httpd.tar.bz2; command -v gpgconf && gpgconf --kill all || :; rm -rf "$GNUPGHOME" httpd.tar.bz2.asc; mkdir -p src; tar -xf httpd.tar.bz2 -C src --strip-components=1; rm httpd.tar.bz2; cd src; patches() { while [ "$#" -gt 0 ]; do local patchFile="$1"; shift; local patchSha256="$1"; shift; ddist "$patchFile" "httpd/patches/apply_to_$HTTPD_VERSION/$patchFile"; echo "$patchSha256 *$patchFile" | sha256sum -c -; patch -p0 < "$patchFile"; rm -f "$patchFile"; done; }; patches $HTTPD_PATCHES; gnuArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)"; ./configure --build="$gnuArch" --prefix="$HTTPD_PREFIX" --enable-mods-shared=reallyall --enable-mpms-shared=all ; make -j "$(nproc)"; make install; cd ..; rm -r src man manual; sed -ri -e 's!^(\s*CustomLog)\s+\S+!\1 /proc/self/fd/1!g' -e 's!^(\s*ErrorLog)\s+\S+!\1 /proc/self/fd/2!g' "$HTTPD_PREFIX/conf/httpd.conf"; runDeps="$runDeps $( scanelf --needed --nobanner --format '%n#p' --recursive /usr/local | tr ',' '\n' | sort -u | awk 'system("[ -e /usr/local/lib/" $1 " ]") == 0 { next } { print "so:" $1 }' )"; apk add --virtual .httpd-rundeps $runDeps; apk del .build-deps
PASS - DKL-DI-0005: Clear apt-get caches
PASS - DKL-DI-0006: Avoid latest tag
FATAL - DKL-LI-0001: Avoid empty password
* No password user found! username : root
PASS - DKL-LI-0002: Be unique UID
PASS - DKL-LI-0002: Be unique GROUP
FATALが出ているのが確認できます。
最新のAlpineLinuxでは出ないものなので、定期的にチェックは必要だなと思います。
Trivy
$ trivy httpd:2.4-alpine
2019-07-16T00:48:09.514+0900 INFO Updating vulnerability database...
2019-07-16T00:48:13.096+0900 INFO Detecting Alpine vulnerabilities...
httpd:2.4-alpine (alpine 3.7.1)
===============================
Total: 13 (UNKNOWN: 0, LOW: 2, MEDIUM: 5, HIGH: 5, CRITICAL: 1)
+------------+------------------+----------+-------------------+---------------+--------------------------------+
| LIBRARY | VULNERABILITY ID | SEVERITY | INSTALLED VERSION | FIXED VERSION | TITLE |
+------------+------------------+----------+-------------------+---------------+--------------------------------+
| bzip2 | CVE-2019-12900 | HIGH | 1.0.6-r6 | 1.0.6-r7 | bzip2: out-of-bounds write in |
| | | | | | function BZ2_decompress |
+------------+------------------+ +-------------------+---------------+--------------------------------+
| expat | CVE-2018-20843 | | 2.2.5-r0 | 2.2.7-r0 | expat: large number of colons |
| | | | | | in input makes parser consume |
| | | | | | high amount... |
+------------+------------------+----------+-------------------+---------------+--------------------------------+
| libxml2 | CVE-2018-14404 | MEDIUM | 2.9.7-r0 | 2.9.8-r1 | libxml2: NULL pointer |
| | | | | | dereference in |
| | | | | | xpath.c:xmlXPathCompOpEval() |
| | | | | | can allow attackers to cause |
| | | | | | a... |
+ +------------------+ + + +--------------------------------+
| | CVE-2018-14567 | | | | libxml2: Infinite loop when |
| | | | | | --with-lzma is used allows for |
| | | | | | denial of service... |
+ +------------------+----------+ + +--------------------------------+
| | CVE-2018-9251 | LOW | | | libxml2: infinite loop in |
| | | | | | xz_decomp function in xzlib.c |
+------------+------------------+----------+-------------------+---------------+--------------------------------+
| perl | CVE-2018-18311 | HIGH | 5.26.2-r1 | 5.26.3-r0 | perl: Integer overflow |
| | | | | | leading to buffer overflow in |
| | | | | | Perl_my_setenv() |
+ +------------------+ + + +--------------------------------+
| | CVE-2018-18314 | | | | perl: Heap-based buffer |
| | | | | | overflow in S_regatom() |
+ +------------------+ + + +--------------------------------+
| | CVE-2018-18312 | | | | perl: Heap-based |
| | | | | | buffer overflow in |
| | | | | | S_handle_regex_sets() |
+ +------------------+----------+ + +--------------------------------+
| | CVE-2018-18313 | MEDIUM | | | perl: Heap-based buffer read |
| | | | | | overflow in S_grok_bslash_N() |
+------------+------------------+----------+-------------------+---------------+--------------------------------+
| postgresql | CVE-2019-10164 | CRITICAL | 10.5-r0 | 10.9-r0 | PostgreSQL: stack-based |
| | | | | | buffer overflow via setting a |
| | | | | | password |
+ +------------------+----------+ +---------------+--------------------------------+
| | CVE-2019-10129 | MEDIUM | | 10.8-r0 | postgresql: Memory disclosure |
| | | | | | in partition routing |
+ +------------------+----------+ + +--------------------------------+
| | CVE-2019-10130 | LOW | | | postgresql: Selectivity |
| | | | | | estimators bypass row security |
| | | | | | policies |
+------------+------------------+----------+-------------------+---------------+--------------------------------+
| sqlite | CVE-2018-20346 | MEDIUM | 3.21.0-r1 | 3.25.3-r0 | CVE-2018-20505 CVE-2018-20506 |
| | | | | | sqlite: Multiple flaws in |
| | | | | | sqlite which can be triggered |
| | | | | | via... |
+------------+------------------+----------+-------------------+---------------+--------------------------------+
ライブラリ毎に表示されるので見やすいですね。
CRITICALが出ているので、ここは最低でも塞いで起きたいところです。
まとめ
簡単にイメージスキャンができることがわかっていただけたと思います。
今回は素のままのコマンド実行でしたが、表示形式の変更やファイルへの出力などのオプションもあるので、うまく使って様々な箇所に応用していきましょう。