#1 権限は必要最小限に
Dockerfileで指定されない限り、デフォルトだとrootユーザーが使用されます。これは大きなセキュリティリスクに繋がる可能性があるため、アプリケーション専用グループ、専用ユーザーを作成しましょう。
# グループを作成
RUN groupadd -r app
# ユーザーを作成
RUN useradd -g app app
# 所有権と権限を設定
RUN chown -R app:app /app
# 再帰的な実行は長い時間がかかることがあるため、パフォーマンスを気にするなら COPY と一緒の方が良い
# COPY --chown=<user>:<group> <hostPath> <containerPath>
COPY --chown=app:app . /app
# ユーザーを切り替え
USER app
#2 マルチステージビルド
Dockerイメージは多くの場合、必要以上に大きくなり、デプロイ、セキュリティ、および開発エクスペリエンスに影響を与えます。また、ツール、開発時のみの依存関係、ランタイム、コンパイラなどの不要なアセットをリリースすることになります。
基本的な考え方は、ビルドステージとランタイムステージを分離することです。
FROM golang:1.17.2-alpine AS builder
WORKDIR /app
COPY . .
RUN go build -o app
FROM gcr.io/distroless/static AS production
COPY --from=builder app ./
CMD ["/app"]
ちなみにDistrolessイメージとは
今回ランタイムイメージとして使用したDistrolessとは、Googleが公開しているアプリケーションとそのランタイム依存のみが含まれたDebian10(buster)に基づいて作成されたコンテナイメージです。Distrolessにはアプリケーションの実行に必要な依存しか含まれていません。そのため、Shellはおろか、aptやcdといった機能も有していません。要は何も入っていないため、とってもセキュアなランタイム環境としてかつ軽量に使用ができるということです。
#3 セキュリティの脆弱性スキャン
docker scan <your-image>
docker scan
でDockerイメージのセキュリティ脆弱性をスキャンできます。CIの一部で使用することをお勧めします。試しに「node:17-alpine
」と「node:17
」を docker scan
してみます。node:17
では多くの脆弱性が存在していることが明らかです。
node:17-alpine
$ docker scan node:17-alpine
Testing node:17-alpine...
Package manager: apk
Project name: docker-image|node
Docker image: node:17-alpine
Platform: linux/amd64
Base image: node:17.5.0-alpine3.15
✔ Tested 16 dependencies for known vulnerabilities, no vulnerable paths found.
According to our scan, you are currently using the most secure version of the selected base image
node:17
$ docker scan node:17
Testing node:17...
--- 省略 ---
Package manager: deb
Project name: docker-image|node
Docker image: node:17
Platform: linux/amd64
Base image: node:17.5.0-bullseye
Tested 410 dependencies for known vulnerabilities, found 223 vulnerabilities.
Base Image Vulnerabilities Severity
node:17.5.0-bullseye 223 8 critical, 16 high, 4 medium, 195 low
Recommendations for base image upgrade:
Alternative image types
Base Image Vulnerabilities Severity
node:current-slim 44 0 critical, 0 high, 0 medium, 44 low
node:17-buster-slim 68 0 critical, 1 high, 0 medium, 67 low
node:17-stretch-slim 84 1 critical, 2 high, 0 medium, 81 low
node:buster 411 5 critical, 16 high, 3 medium, 387 low
#4 小さいサイズの公式イメージを使用する
必要なシステムツールとライブラリのみをバンドルし、攻撃対象領域を最小限に抑え、より安全なイメージを確保する、よりスリムなOSディストリビューションでより小さなイメージを使用することをお勧めします。
例えば、Alpine Linux はmusllibcとbusyboxに基づくセキュリティ指向の軽量Linuxディストリビューションです。
FROM node:17 # 【NG】
FROM node:17-alpine # 【OK】
#5 キャッシュを使用する
Dockerイメージにはさまざまなレイヤーが含まれており、Dockerfileでは各コマンドまたは命令がイメージレイヤーを作成します。したがって、Dockerイメージを再構築し、Dockerfileまたはレイヤーが変更されていない場合、Dockerはキャッシュされたレイヤーを使用してイメージを構築します。これにより、イメージの再構築が大幅に高速化されます。
node_modules以下は、 Dockerレイヤーキャッシングを利用してキャッシュする方法の例です。これは、Dockerfileに応じて、複数の状況で実装できます。
FROM node:17-alpine
WORKDIR /app
COPY package*.json .
RUN npm install --production
COPY . .
RUN npm run build
CMD ["node", "dist/index.js"]
参考資料