Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Dockleを使ってベストプラクティスに沿ったDockerfileを作ろう

 Dockleを使ってベストプラクティスに沿ったDockerfileを作ろう

■動画
https://youtu.be/8xsOqzcsjpw
■概要
コンテナセキュリティの中でも、イメージへ対策を行うための有名なツールの一つにDockleがあります。
DockleはOSSで提供されるコンテナイメージのセキュリティ診断ツールです。
イメージスキャンをすることで、ベストプラクティスから外れた設定を検知できます。
CIに組み込むことが容易であり、本動作ではAWS CodeBuildを用いてDockleを導入した例を示します。
■参考URL
・CURRY HOUSE Babbulkund Instagramアカウント (P.2)
https://www.instagram.com/babbulkund/
・CURRY HOUSE Babbulkund Twitterアカウント (P.2)
https://twitter.com/babbulkund
・Dockle (P.14)
https://github.com/goodwithtech/dockle
・Tomoya AmachiさんTwitterアカウント (P.14)
https://twitter.com/tomoyamachi
・Dockleチェック項目詳細説明と修正方法の例 (P.18)
https://github.com/goodwithtech/dockle/blob/master/CHECKPOINT.md
・Trivy (P.20)
https://github.com/aquasecurity/trivy
・DevelopersIO 2022にて「OSSで始めるコンテナセキュリティ」というタイトルで登壇しました (P.21)
https://dev.classmethod.jp/articles/developersio-2022-container-security-with-oss-tools/
・Dockerセキュリティ対策の現状と対策ツールの仕組み&使い方 (P.21)
https://speakerdeck.com/tomoyamachi/dockersekiyuriteidui-ce-falsexian-zhuang-todui-ce-turufalseshi-zu-mi-and-shi-ifang
・Dockleインストールコマンド (P.27)
https://github.com/goodwithtech/dockle#installation

Shun Tokuyama

July 24, 2023
Tweet

More Decks by Shun Tokuyama

Other Decks in Technology

Transcript

  1. 自己紹介 2 トクヤマシュン • 2023 AWS All Certifications Engineer •

    所属:AWS事業本部コンサルティング部 • 役割:AWSソリューションアーキテクト • 好きなAWSサービス:AWS Fargate • 副業 ◦ 兵庫県明石市でカレー屋をやっています ▪ CURRY HOUSE Babbulkund (カレーハウス バブルクンド) ▪ 土曜日のみ営業 ▪ :@babbulkund 、  :@babbulkund、
  2. コンテナのいいところ 8 • 可搬性 ◦ 一度作成したコンテナイメージはさまざまな環境で利用可能 ▪ 例)開発環境でテストしたイメージをそのまま本番環境で利用 • 運用性

    ◦ コンテナオーケストレーターを使って運用の安定化が可能 ▪ 例)障害時に自動で起動中のコンテナ数を保ちサービス継続 ▪ AWS サービスだとAmazon ECSやAmazon EKSが オーケストレーターに該当 • などなど
  3. Dockleとは? • OSSで提供されるコンテナイメージのセキュリティ診断ツール ◦ 作者:Tomoya Amachi(@tomoyamachi)さん • 下記のチェック項目に基づいて、コンテナイメージに対するリスクを検出 ◦ 1.

    CIS’s Docker Image Checkpoints ▪ Center For Internet Security(略称:CIS)によって定義されるベストプラクティス ◦ 2. Dockle Checkpoints for Docker ▪ Docker公式のベストプラクティスなどからDockleが定めた項目 ◦ 3. Dockle Checkpoints for Linux ▪ nixCraftのLinuxベストプラクティスなどからDockleが定めた項目 14 https://github.com/goodwithtech/dockle
  4. CIS’s Docker Image Checkpoints の検査項目詳細 15 コード チェック項目 Dockle 対応状況

    レベル CIS-DI-0001 コンテナ用のユーザーを作成しているか ◯ WARN CIS-DI-0002 信頼されたコンテナベースイメージを利用しているか △※1 FATAL CIS-DI-0003 コンテナ内で不必要なパッケージをインストールしていないか × FATAL CIS-DI-0004 セキュリティパッチをスキャンし、 必要な場合に再ビルドしてインストールしているか × FATAL CIS-DI-0005 Docker コンテントトラストを有効にしているか ◯ INFO CIS-DI-0006 Dockerfile内にHEALTHCHECKを導入しているか ◯ WARN CIS-DI-0007 パッケージのupdateを単独実行していないか ◯ FATAL CIS-DI-0008 setuidとsetgidを使っていないか ◯ INFO CIS-DI-0009 ADDコマンドではなくCOPYコマンドを利用しているか ◯ FATAL CIS-DI-0010 シークレットをDockerfile内に記載していないか ◯ FATAL CIS-DI-0011 検証済みのパッケージのみをインストールしているか × INFO ※1 DockleはDockerコンテントトラストによる検証結果をチェック
  5. Dockle Checkpoints for Docker の検査項目詳細 16 コード チェック項目 Dockle 対応状況

    レベル DKL-DI-0001 sudoコマンドを使っていないか ◯ FATAL DKL-DI-0002 センシティブなディレクトリをボリュームマウントしていないか ◯ FATAL DKL-DI-0003 apt-get dist-upgradeを利用していないか ◯ WARN DKL-DI-0004 apk add利用時に--no-cacheオプションを付与しているか ◯ FATAL DKL-DI-0005 apt-getのキャッシュクリアをしているか ◯ FATAL DKL-DI-0006 latestタグを利用していないか ◯ WARN
  6. Dockle Checkpoints for Linux の検査項目詳細 17 コード チェック項目 Dockle 対応状況

    レベル DKL-LI-0001 Null root passwordを設定していないか ◯ FATAL DKL-LI-0002 ユニークなUID/GROUPを設定しているか ◯ FATAL DKL-LI-0003 必要なファイルのみを含めているか ◯ INFO
  7. 参考)Dockleとよく比較されるTrivyについて • Aqua Security社からOSSで提供されている、コンテナイメージのセキュリティ診断ツール • Aqua Security社によって定義される項目に対応して、ベストプラクティスに沿っているかチェック ◦ Dockleとはスキャン項目が異なる •

    TrivyはOSやアプリケーション依存ライブラリの脆弱性チェックも可能 ◦ Dockleと組み合わせることでセキュリテイの更なる向上が見込める ▪ 例:CIS-DI-0004 セキュリティパッチをスキャンし、必要な場合に再ビルドしてインストールしているか • Dockleではチェック不可だがTrivyではチェック可能 20 https://github.com/aquasecurity/trivy
  8. AWS CodeBuild環境の設定 26 • AWS CodeBuildとは ◦AWS上で動作する完全マネージド型のビルドサービス ◦ソースコードのコンパイルやテスト、ビルドを実行可能 • ビルド環境の設定

    ◦イメージ:aws/codebuild/amazonlinux2-x86_64-standard:4.0 ◦ビルド内容 • buildspec.yamlで定義 ◦ Dockleのインストールおよびスキャン用のコマンドを記載します ▪ 記載方法は次スライドで説明
  9. CodeBuildへのDockle導入設定(1/2) 27 version: 0.2 env: variables: DOCKER_BUILDKIT: "1" phases: install:

    runtime-versions: docker: 20 pre_build: commands: # Dockleインストール - 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 # Amazon ECRログイン - AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text) - echo Logging in to ECR - aws ecr --region ${AWS_REGION} get-login-password | docker login --username AWS --password-stdin https://${REPOSITORY_URI} - COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7) - IMAGE_TAG=${COMMIT_HASH:=latest} • Dockleのインストール ◦pre_buildフェーズにインストールコマンド※1を記載するだけでOK ※1:ビルド環境に応じたインストールコマンドが公式から提供されています    https://github.com/goodwithtech/dockle#installation buildspec.yaml (前半)
  10. CodeBuildへのDockle導入設定(2/2) 28 • Dockleを使ったイメージスキャン ◦post_buildフェーズにインストールコマンドを記載するだけでOK ◦ここではFATALレベルの違反を検出すると終了するよう設定 build: commands: - echo

    Build started on `date` - docker image build -t ${REPOSITORY_URI}:${COMMIT_HASH} . post_build: commands: - echo Build completed on `date` # Dockleによるイメージチェック。FATALを検出すると終了 - dockle -f json --exit-code 1 --exit-level "FATAL" ${REPOSITORY_URI}:${COMMIT_HASH} - exit `echo $?` - echo Pushing the Docker images - docker image push ${REPOSITORY_URI}:${COMMIT_HASH} buildspec.yaml (後半)
  11. 失敗時のCodeBuildログ 34 [Container] yyyy/MM/dd HH:mm:ss Running command dockle -f json

    --exit-code 1 --exit-level "FATAL" ${REPOSITORY_URI}:${COMMIT_HASH} { "image": "123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/dockle-test-repo:57c4b69", "summary": { "fatal": 1, "warn": 1, "info": 3, "skip": 0, "pass": 11 }, "details": [  〜(略)〜 ] } • Dockleでは下記ログが出力されます ◦image:イメージスキャン対象イメージ名・タグ名 ◦summary:レベルごとの検出結果数 • 今回はfatal:1件、warn:1件、info:3件を違反として検出 ◦details:違反項目の詳細をそれぞれ表示 CodeBuild Log
  12. FATALレベルの検出項目調査 35 { "code": "CIS-DI-0010", "title": "Do not store credential

    in environment variables/files", "level": "FATAL", "alerts": [ "Suspicious ENV key found : NGINX_GPGKEY on /bin/sh -c set -x \u0026\u0026 groupadd --system --gid 101 nginx \u0026\u0026 useradd --system --gid nginx --no-create-home --home /nonexistent --comment \"nginx user\" --shell /bin/false --uid 101 nginx \u0026\u0026 apt-get update \u0026\u0026 apt-get install --no-install-recommends --no-install-suggests -y gnupg1 ca-certificates \u0026\u0026 NGINX_GPGKEY=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62; NGINX_GPGKEY_PATH=/usr/share/keyrings/nginx-archive-keyring.gpg; export GNUPGHOME=\"$(mktemp -d)\"; found=''; for server in hkp://keyserver.ubuntu.com:80 pgp.mit.edu ; do echo \"Fetching GPG key $NGINX_GPGKEY from $server\"; gpg1 --keyserver \"$server\" --keyserver-options timeout=10 --recv-keys \"$NGINX_GPGKEY\" \u0026\u0026 found=yes \u0026\u0026 break; done; test -z \"$found\" \u0026\u0026 echo \u003e\u00262 \"error: failed to fetch GPG key $NGINX_GPGKEY\" \u0026\u0026 exit 1; gpg1 --export \"$NGINX_GPGKEY\" \u003e \"$NGINX_GPGKEY_PATH\" ; rm -rf \"$GNUPGHOME\"; apt-get remove --purge --auto-remove -y gnupg1 \u0026\u0026 rm -rf /var/lib/apt/lists/* \u0026\u0026 dpkgArch=\"$(dpkg --print-architecture)\" \u0026\u0026 nginxPackages=\" nginx=${NGINX_VERSION}-${PKG_RELEASE} nginx-module-xslt=${NGINX_VERSION}-${PKG_RELEASE} nginx-module-geoip=${NGINX_VERSION}-${PKG_RELEASE} nginx-module-image-filter=${NGINX_VERSION}-${PKG_RELEASE} nginx-module-njs=${NGINX_VERSION}+${NJS_VERSION}-${PKG_RELEASE} \" \u0026\u0026 case \"$dpkgArch\" in amd64|arm64) echo \"deb [signed-by=$NGINX_GPGKEY_PATH] https://nginx.org/packages/mainline/debian/ bookworm nginx\" \u003e\u003e /etc/apt/sources.list.d/nginx.list \u0026\u0026 apt-get update ;; *) echo \"deb-src [signed-by=$NGINX_GPGKEY_PATH] https://nginx.org/packages/mainline/debian/ bookworm nginx\" \u003e\u003e /etc/apt/sources.list.d/nginx.list \u0026\u0026 tempDir=\"$(mktemp -d)\" \u0026\u0026 chmod 777 \"$tempDir\" \u0026\u0026 savedAptMark=\"$(apt-mark showmanual)\" \u0026\u0026 apt-get update \u0026\u0026 apt-get build-dep -y $nginxPackages \u0026\u0026 ( cd \"$tempDir\" \u0026\u0026 DEB_BUILD_OPTIONS=\"nocheck parallel=$(nproc)\" apt-get source --compile $nginxPackages ) \u0026\u0026 apt-mark showmanual | xargs apt-mark auto \u003e /dev/null \u0026\u0026 { [ -z \"$savedAptMark\" ] || apt-mark manual $savedAptMark; } \u0026\u0026 ls -lAFh \"$tempDir\" \u0026\u0026 ( cd \"$tempDir\" \u0026\u0026 dpkg-scanpackages . \u003e Packages ) \u0026\u0026 grep '^Package: ' \"$tempDir/Packages\" \u0026\u0026 echo \"deb [ trusted=yes ] file://$tempDir ./\" \u003e /etc/apt/sources.list.d/temp.list \u0026\u0026 apt-get -o Acquire::GzipIndexes=false update ;; esac \u0026\u0026 apt-get install --no-install-recommends --no-install-suggests -y $nginxPackages gettext-base curl \u0026\u0026 apt-get remove --purge --auto-remove -y \u0026\u0026 rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/nginx.list \u0026\u0026 if [ -n \"$tempDir\" ]; then apt-get purge -y --auto-remove \u0026\u0026 rm -rf \"$tempDir\" /etc/apt/sources.list.d/temp.list; fi \u0026\u0026 ln -sf /dev/stdout /var/log/nginx/access.log \u0026\u0026 ln -sf /dev/stderr /var/log/nginx/error.log \u0026\u0026 mkdir /docker-entrypoint.d (You can suppress it with --accept-key)" ] }, - 検出項目 - CIS-DI-0010:シークレットをDockerfile内に記載していないか - 検出理由 - シークレット情報と疑われる環境変数がイメージ内に存在 - NGINX_GPGKEY、NGINX_GPGKEY_PATH CodeBuild Log
  13. WARNレベルの検出項目調査 36 { "code": "CIS-DI-0001", "title": "Create a user for

    the container", "level": "WARN", "alerts": [ "Last user should not be root" ] } - 検出項目 - CIS-DI-0001:コンテナ用のユーザーを作成しているか - 検出理由 - Dockerfile内でコンテナ用のユーザーを作成していない CodeBuild Log
  14. INFOレベルの検出項目調査 37 { "code": "CIS-DI-0005", "title": "Enable Content trust for

    Docker", "level": "INFO", "alerts": [ "export DOCKER_CONTENT_TRUST=1 before docker pull/build" ] }, { "code": "CIS-DI-0006", "title": "Add HEALTHCHECK instruction to the container image", "level": "INFO", "alerts": [ "not found HEALTHCHECK statement" ] }, { "code": "CIS-DI-0008", "title": "Confirm safety of setuid/setgid files", "level": "INFO", "alerts": [ "setuid file: urwxr-xr-x usr/bin/chfn", "setuid file: urwxr-xr-x usr/bin/su", "setuid file: urwxr-xr-x usr/bin/chsh", "setgid file: grwxr-xr-x usr/bin/wall", "setuid file: urwxr-xr-x usr/bin/passwd", "setgid file: grwxr-xr-x usr/bin/expiry", "setuid file: urwxr-xr-x usr/bin/mount", "setgid file: grwxr-xr-x usr/bin/chage", "setuid file: urwxr-xr-x usr/bin/umount", "setuid file: urwxr-xr-x usr/bin/newgrp", "setgid file: grwxr-xr-x usr/sbin/unix_chkpwd", "setuid file: urwxr-xr-x usr/bin/gpasswd" ] } - 検出項目 - CIS-DI-0005:Docker コンテントトラストを有効にしているか - 検出項目 - CIS-DI-0006:Dockerfile内にHEALTHCHECKを導入しているか - 検出項目 - CIS-DI-0008:setuidとsetgidを使っていないか CodeBuild Log
  15. FATALレベルの検出項目修正 39 - 修正対象 - buildspec.yaml - 修正内容 - スキャン実行時に-akオプションをつけ、2つの環境変数を受容するように修正

    - NGINX_GPGKEY、NGINX_GPGKEY_PATH - イメージ内に存在するこれら2つの環境変数がシークレットと疑われています - NGINXの公式イメージに元から含まれており、シークレット情報ではありません build: commands: - echo Build started on `date` - docker image build -t ${REPOSITORY_URI}:${COMMIT_HASH} . post_build: commands: - echo Build completed on `date` # dockleによるイメージチェック。FATALを検出すると終了 - dockle -f json -ak NGINX_GPGKEY -ak NGINX_GPGKEY_PATH --exit-code 1 --exit-level "FATAL" ${REPOSITORY_URI}:${COMMIT_HASH} - exit `echo $?` - echo Pushing the Docker images - docker image push ${REPOSITORY_URI}:${COMMIT_HASH} 追加 buildspec.yaml