セキュリティ
Dockerを使用する際にも他の技術と同様にセキュリティを考慮する必要があります。
rootユーザを使わない¶
rootユーザはCapabilityで制御されてはいますが、コンテナ内に侵入されたときに最小限の影響に留めるためにも過剰な権限は基本的に不要です。
Dockerのベースイメージは基本的にrootユーザーになっています。
USER
コマンド使用してユーザーを変更し、chmodなどで権限を最適化ましょう。
公式のベースイメージのデフォルトユーザーを確認¶
公式のnodeイメージをベースイメージとし、現在のユーザーを確認する whoami
を実行するイメージを作成してみます。
ビルドし、実行するとrootユーザーになっていることが確認できました。
では、 USER
コマンドを使用しユーザーを設定してみます。
nodeユーザーに変更できたことが確認できました。
野良のDockerイメージをベースイメージにしない¶
非公式のユーザー製のイメージをベースイメージとして使うことは非常に危険です。
悪意を持った野良イメージは過去に何度も登場し、イメージ内に悪意のあるスクリプトが含まれていることがあります。
また、GitHubなどでDockerfileが公開されており、選定するタイミングでは問題がなかったものの更新されリスクのあるベースイメージになってしまうケースもありました。
基本的にベースイメージは公式のものをしようするようにしましょう。
ビルド時に機微情報を与えない¶
ビルド時にパスワードや秘密鍵のような機微情報を与え、最終イメージに残らないようにしましょう。
基本的にビルド後に環境変数としてパスワードなどの機微情報を渡すことがベストプラクティスです。
Private Repositoryのcloneなど、どうしても機微情報が必要な場合は --secret
や --ssh
オプションを使用してセキュアにビルドをしましょう。
現在はデフォルトで有効化されているBuildKitであればレイヤー内で機微情報を削除し、 最終イメージに機微情報が残らなければ 基本的に問題ないです。
.dockerignore ファイルでローカルの不要なパスを無視する¶
Dockerビルド時に無視するパスを記述するファイルで、 .gitignore
のようなイメージに近いです。
.git
や node_modules
のようなビルド時に不要なパスを記述することでビルドが高速かつイメージが軽量になるメリットがあります。
逆に、 .env
のようなDBへの接続情報やAPIのアクセスキーが記載されているファイルをビルドに含めてしまうことは最終イメージに機微情報が入ってしまうセキュリティイスクに繋がります。
e.g. .dockerignore
イメージを塩漬けにしない¶
スナップショットとしてそのタイミングのイメージをビルドするため、良くも悪くもセキュリティリスクは時間とともに増えていきます。
例えば、ベースイメージとなるランタイムのバージョンで脆弱性が発生した場合、次のバージョンのベースイメージに変更する必要があります。
また、ベースイメージのランタイムだけでなく、内部のミドルウェアやライブラリの更新も同様に必要になります。
1度完成したイメージは脆弱性のスキャンや、他の技術同様日頃からセキュリティの情報を追うことは必要です。
イメージの脆弱性スキャンを有効化する¶
イメージに対して脆弱性スキャンをかける技術は一般的に普及しています。
CI/CD¶
docker社が提供するGitHub Actionが存在します。
CI/CDなどのworkflow内で実行することでCVEベースで脆弱性の検知を行なってくれます。
docker/scout-action: Docker Scout GitHub Action
レジストリ¶
クラウドベンダーが提供するマネージドなイメージのレジストリにはイメージのスキャン機能がついていることが多いです。
セキュリティリスクを減らすために有効化をお勧めします。
- AWSであればAmazon Elastic Container Registry
- Amazon ECR でソフトウェアの脆弱性がないかイメージをスキャンする - Amazon ECR
- Google CloudであればArtifact Artifact Registry
- Container scanning overview | Documentation | Google Cloud
Dockerfile内¶
別の章で紹介するマルチステージビルドを利用し、Dockerfile内でセキュリティツールを実行するアプローチもあります。
FROM aquasec/trivy:latest AS trivy
FROM myapp:latest
COPY --from=trivy /usr/local/bin/trivy /usr/local/bin/trivy
RUN trivy filesystem --exit-code 1 --no-progress /
サイドカー¶
Snykを使用し、実行中もサイドカーコンテナとしてリアルタイムで自動検知を行う選択肢もあります。
sidecar vulnerabilities | Snyk
その他¶
他にもどのようなツールがあるのか、CNCFでセキュリティプロダクトの一覧を見ることもお勧めです。
ファイルのマウントが必要な場合は最小限に。¶
ホストのファイルをマウントする際は十分に注意してマウントを行いましょう。
最小限の権限(Read-Onlyにするなど)でマウントするパスは必要最低限にしましょう。
また、特に注意が必要なのはdockerソケットです。
dockerソケットが乗っ取られてしまうとそれを踏み台に任意のdockerイメージを起動されてしまうなど大きなセキュリティリスクになります。
しかし、CIや監視などでdockerソケットのマウントを要求するツールも存在します。
安易に与えるのは控え、ソケットをマウントしなければならない場合はRead-Onlyでマウントするようにしましょう。
中間レイヤーから機微情報を抜き出す例¶
Dockerのビルドはレイヤー毎にスナップショットがとられ、次のビルドの高速化のために中間レイヤーが生成されます。
Dockerのv20からはデフォルトでBuildKitが有効になり容易に中間レイヤーにアクセスできなくなりましたが。
しかし、 docker save
コマンドでtarとして出力してから任意のレイヤーを吐き出すように設定を書き換えて docker load
でアクセスすることは可能です。
今回はわかりやすいよう、BuildKitを無効化し、中間レイヤーから機微情報を抜き出してみます。
一時的に "mypassword" と記載されたテキストファイルを /tmp/pass.txt に配置し、その後 /tmp/pass.txt を削除するイメージを作成してみます。
FROM node:22-slim
RUN echo "mypassword" > /tmp/pass.txt
RUN echo "delete password" > /tmp/pass.txt
CMD cat /tmp/pass.txt
BuildKitを無効化し、ビルドを実行します。
$ DOCKER_BUILDKIT=0 docker build -t pass .
DEPRECATED: The legacy builder is deprecated and will be removed in a future release.
BuildKit is currently disabled; enable it by removing the DOCKER_BUILDKIT=0
environment-variable.
Sending build context to Docker daemon 3.072kB
Step 1/4 : FROM node:22-slim
---> ba83b0f18f30
Step 2/4 : RUN echo "mypassword" > /tmp/pass.txt
---> Using cache
---> b48e7229fe78
Step 3/4 : RUN echo "delete password" > /tmp/pass.txt
---> Using cache
---> cdf556ff4f4b
Step 4/4 : CMD cat /tmp/pass.txt
---> Running in a5c245f8588e
---> Removed intermediate container a5c245f8588e
---> 26a1e198ef63
Successfully built 26a1e198ef63
Successfully tagged pass:latest
成果イメージを実行すると、 delete password と出力され想定通りの出力となっています。
ビルド時にキャッシュされたレイヤーを指定して、 /tmp/pass.txt
の内容を確認してみます。
mypassword と出力されてしまい、中間レイヤーに機微情報が残ってしまっていることが確認できてしまいました。