container-security について
container-securityは、Azure Container Registryでコンテナイメージを構築するチーム向けの実践的なスキルです。ベースイメージのインポート、ビルド、Trivyスキャン、検出事項の分析、Dockerfileの強化、再ビルドと検証の流れを説明します。
主な機能
- 01ベースイメージのACRへのインポート
- 02ACRを使ったイメージのビルド
- 03TrivyによるイメージとDockerfileのスキャン
- 04CVEの分類と修正方法の検討
- 05Dockerfile設定の強化
- 06イメージの再ビルドと安全性検証
活用シーン
- 01ACRクラウドビルドでのDocker Hubレート制限の回避
- 02コンテナイメージの脆弱性の確認と優先順位付け
- 03SSHアクセスとコンテナ実行権限の強化
- 04イメージ再ビルド後のセキュリティ変更の確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ julianobarbosa/ claude-code-skills --skill container-securityインストール後、Claude Code で/container-securityを入力して読み込まれたか確認します。
同梱ファイルevals/evals.json
詳細
コンテナーイメージのセキュリティ
コンテナーイメージを保護するための完全なワークフローです。スキャン、分析、強化、検証を行います。
ワークフローの概要
Import base image to ACR → Build → Scan with Trivy → Analyze CVEs → Harden Dockerfile → Rebuild → Re-scan → Verify
1. ベースイメージを ACR にインポートする
ベースイメージをプライベート ACR にインポートして、Docker Hub のレート制限を回避します。Azure のインフラストラクチャが代わりにイメージを取得するため、Docker Hub の認証は必要ありません。
# Import a public image into ACR
az acr import --name <registry> \
--source docker.io/<image>:<tag> \
--image <local-path>/<image>:<tag>
# Example: import code-server
az acr import --name example-app \
--source docker.io/codercom/code-server:4.107.1 \
--image addons/code-server:4.107.1
# Verify
az acr repository show --name <registry> --repository <local-path>/<image>
次に、Dockerfile の FROM を更新して ACR 内のイメージを参照します。
# Before (hits Docker Hub rate limits in ACR cloud builds)
FROM codercom/code-server:latest
# After (pulls from local ACR — no rate limit)
FROM example-app.azurecr.io/addons/code-server:4.107.1
重要:ACR クラウドビルド(az acr build)は、Docker Hub に対して認証されません。Docker Hub を参照する FROM は、いずれレート制限に達します。必ず先にインポートしてください。
2. ACR でビルドする
az acr build --registry <registry> --image <repo>:<tag> -f Dockerfile .
ACR ビルダーの制限 — 以下の Docker 機能はサポートされていません。
COPY <<'EOF'のヒアドキュメント構文(BuildKit 専用) — 代わりにRUN printfまたはRUN catを使用してください- マルチプラットフォームビルドには
--platformフラグが必要です - BuildKit 固有の
RUN --mountディレクティブ
3. Trivy でスキャンする
オプション A:ACR Task 経由の Trivy(CI に推奨)
まず、Trivy 自体を ACR にインポートします(1 回のみ)。
az acr import --name <registry> \
--source docker.io/aquasec/trivy:latest \
--image tools/trivy:latest
次にスキャンします。
# Table format for human review
az acr run --registry <registry> \
--cmd "<registry>.azurecr.io/tools/trivy:latest image \
--severity HIGH,CRITICAL \
<registry>.azurecr.io/<image>:<tag>" /dev/null
# JSON format for programmatic analysis
az acr run --registry <registry> \
--cmd "<registry>.azurecr.io/tools/trivy:latest image \
--severity HIGH,CRITICAL --format json \
<registry>.azurecr.io/<image>:<tag>" /dev/null
オプション B:ローカルで Trivy を実行する(Docker デーモンが必要)
az acr login --name <registry>
trivy image --severity HIGH,CRITICAL <registry>.azurecr.io/<image>:<tag>
オプション C:ローカルの Dockerfile を Trivy でスキャンする(ビルド不要)
trivy config --severity HIGH,CRITICAL,MEDIUM Dockerfile
4. CVE の検出結果を分析する
CRITICAL と HIGH の検出結果をすべて分類します。
| カテゴリー | 対応 | 例 |
|---|---|---|
| こちらで修正可能 | Dockerfile で新しいバージョンを固定する | 古い Go 標準ライブラリでビルドされたツールバイナリ |
| 上流で修正可能 | 追跡、記録し、再確認する | ベースイメージに脆弱性のある内部依存関係が含まれている |
| OS レベル、パッチ適用待ち | 記録し、Debian/Ubuntu のトラッカーを監視する | libsqlite3、openssl |
| will_not_fix | リスクを受け入れるか、代替パッケージを探す | Debian の zlib1g |
JSON 出力から CRITICAL の概要を抽出します。
import json
data = json.load(open('trivy-results.json'))
for result in data.get('Results', []):
for v in result.get('Vulnerabilities', []):
if v.get('Severity') == 'CRITICAL':
print(f"{v['VulnerabilityID']} | {v['PkgName']} {v.get('InstalledVersion','')} | fix: {v.get('FixedVersion','')} | {v.get('Status','')}")
5. Dockerfile の強化チェックリスト
バージョンの固定(再現性に関する CVE を排除)
# Bad — unpinned, non-reproducible, may pull vulnerable versions
FROM image:latest
RUN curl .../releases/latest/download/tool | bash
# Good — pinned, reproducible, auditable
FROM registry.azurecr.io/addons/image:4.107.1
ARG TOOL_VERSION=v1.2.3
RUN curl -fsSL "https://github.com/org/tool/releases/download/${TOOL_VERSION}/tool_linux_amd64.tar.gz" \
| tar xz -C /usr/local/bin tool
バージョン変数には ARG を使用します。これにより、更新を 1 行の変更で行え、docker history からも確認できます。
SSH の強化
# Disable password auth, enable key-based only
RUN mkdir -p /run/sshd && \
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config && \
sed -i 's/#PubkeyAuthentication yes/PubkeyAuthentication yes/' /etc/ssh/sshd_config && \
sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin no/' /etc/ssh/sshd_config && \
echo "AllowUsers <user>" >> /etc/ssh/sshd_config && \
ssh-keygen -A && \
mkdir -p /home/<user>/.ssh && \
chmod 700 /home/<user>/.ssh && \
chown <user>:<user> /home/<user>/.ssh
非 root での実行
# Start privileged services as root, then drop privileges
RUN printf '#!/bin/bash\nset -e\n/usr/sbin/sshd\nexec su - <user> -c "<main-process>"\n' \
> /usr/local/bin/entrypoint.sh && \
chmod +x /usr/local/bin/entrypoint.sh
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
特権サービスが不要な場合は、次のようにするだけです。
USER <non-root-user>
ENTRYPOINT ["<main-process>"]
その他の強化
- レイヤーを最小限にする:関連する
RUNコマンドをまとめる - apt キャッシュを削除する:常に
&& rm -rf /var/lib/apt/lists/*で終える - イメージにシークレットを含めない:実行時に注入する(環境変数、マウントしたシークレット)
- ダウンロードを検証する:可能な場合はバイナリのチェックサムまたは GPG を検証する
- 読み取り専用ファイルシステム:可能な場合は実行時に
--read-onlyを追加する
6. 再ビルドして再スキャンする
強化後は、修正を検証するため、必ず再ビルドして再スキャンします。
# Rebuild with new tag
az acr build --registry <registry> --image <repo>:<new-tag> -f Dockerfile .
# Re-scan
az acr run --registry <registry> \
--cmd "<registry>.azurecr.io/tools/trivy:latest image \
--severity HIGH,CRITICAL \
<registry>.azurecr.io/<repo>:<new-tag>" /dev/null
# Compare CRITICAL counts: before vs after
7. 検証チェックリスト
セキュリティサイクルを完了したら、次の項目を確認します。
-
az acr repository show— ベースイメージが ACR に存在する -
az acr build— レート制限エラーなしで完了する - Trivy スキャン — CRITICAL の件数が減っている(残る上流の CVE を記録する)
-
az acr repository show— 最終イメージが ACR に存在する - Dockerfile でバージョンが固定されている(ツールに
:latestを使用していない) - SSH が鍵のみの認証に設定されている(該当する場合)
- コンテナーが非 root ユーザーで実行される
- イメージのレイヤーにシークレットが埋め込まれていない
よくあるパターン
レート制限からの復旧
az acr import でもレート制限に達した場合(バーストインポート時に発生します)、15 分待つか、認証済みの Docker Hub アカウントを使用します。
az acr import --name <registry> \
--source docker.io/<image>:<tag> \
--image <local-path>/<image>:<tag> \
--username <dockerhub-user> --password <dockerhub-token>
バージョンを固定した複数ツール対応の Dockerfile
ARG K9S_VERSION=v0.50.18
ARG ARGOCD_VERSION=v3.3.3
ARG YQ_VERSION=v4.52.4
ARG KUSTOMIZE_VERSION=v5.8.0
RUN curl -fsSL "https://github.com/derailed/k9s/releases/download/${K9S_VERSION}/k9s_Linux_amd64.tar.gz" | tar xz -C /usr/local/bin k9s
RUN curl -sSL -o /usr/local/bin/argocd "https://github.com/argoproj/argo-cd/releases/download/${ARGOCD_VERSION}/argocd-linux-amd64" && chmod +x /usr/local/bin/argocd
RUN curl -fsSL "https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/yq_linux_amd64" -o /usr/local/bin/yq && chmod +x /usr/local/bin/yq
RUN curl -fsSL "https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2F${KUSTOMIZE_VERSION}/kustomize_${KUSTOMIZE_VERSION}_linux_amd64.tar.gz" | tar xz -C /usr/local/bin kustomize
注意点
RUN apt-get upgradeではTrivyのCVEデータベースは更新されず、インストール済みパッケージのみが更新されます: Trivyはパッケージのバージョンを独自のデータベースと照合するため、再ビルド後もイメージが赤く表示されます。--db-repositoryを使ってスキャナーのデータベースバージョンを固定し、インポートし直したベースから再ビルドしてください。- ACRのクラウドビルドはDocker Hubに対して認証されません:
FROM docker.io/...は数回のビルドでは機能しますが、その後、匿名ユーザーのレート制限(IPごとに6時間で100回のプル。Azureの送信NATと共有)に達します。必ず先にaz acr importを実行してください。症状として、再試行すると「機能する」断続的なtoomanyrequestsエラーが発生します。 COPY <<'EOF'のヒアドキュメントはACRビルダーでは失敗しますが、ローカルでは機能します: ACRはBuildKitではなく、従来のDockerビルドを使用します。ノートPCでは問題なくビルドできるヒアドキュメント構文も、CIではunexpected EOFで失敗します。RUN printfを使うか、コンテキストからCOPYでファイルをステージングしてください。- Trivyの
--severity HIGH,CRITICALを指定すると、修正可能なMEDIUMの検出結果が隠れます: フィルタリングすると、目に見えるCRITICALよりも容易に対処できる、パッケージレベルのパッチに関する情報が失われます。リリースごとに一度はフィルタリングせずに実行して全体像を把握し、その後、トリアージ用にフィルタリングしてください。 - 非rootの
USERディレクティブは、マルチステージビルドのCOPY --fromには適用されません: ステージ間でコピーされたファイルは、コピー元ステージのUID/GIDを保持します。root所有のバイナリをコピーした非rootの最終ステージでは、実行時にexecが失敗します。明示的にCOPY --chown=<user>を使ってください。 - Debianの
will_not_fixCVEは、イメージをアップグレードしても修正されません:will_not_fixとマークされたzlib1gやlibsslなどは、すべてのDebianリリースに含まれています。ベースタグを切り替えても解決せず、解決するにはディストリビューション(distroless、alpine、ubi)を切り替える必要があります。許容するリスクとして記録し、追い続けないでください。