【実務・中級編】 コンテナレジストリのアクセス制御と脆弱性スキャン連携 – インフラ・ネットワーク & クラウドセキュリティ防御ガイド

エンジニア諸君、現場の戦場へようこそ。

「コンテナレジストリにイメージをプッシュして、デプロイする」。今や当たり前のCI/CDパイプラインだが、多くの現場でこの「ゲートウェイ」がザルになっている。君たちが一生懸命書いたアプリケーションコードも、その土台となるコンテナイメージが毒されていれば、一瞬でゼロトラストの夢は崩れ去るんだ。

今日は、コンテナレジストリという「情報の核」をどう守り、どう汚染を防ぐか、その泥臭い実戦論を語る。

なぜレジストリが狙われるのか?(PoC的視点)

攻撃者がレジストリを狙う理由は単純だ。「信頼された場所」だからだ。
例えば、攻撃者がGitHub Actionsの権限を盗み、レジストリにバックドアを仕込んだ「正当なイメージ」をプッシュしたとする。一度レジストリに登録されたものは、本番環境のKubernetesから見れば「信頼できる存在」だ。

ここでのリスクは、「脆弱性を含んだイメージのデプロイ」と「未承認イメージの混入」だ。古いライブラリ(例えば脆弱性が公開された直後の log4j や OpenSSL)が含まれたイメージをデプロイした瞬間、君たちの要塞は内部から崩壊する。

ステップ1:IAMによる厳格なアクセス制御(AWS ECRの例)

「誰でもプッシュできる」設定は論外だ。CI/CDのサービスアカウントには「最小権限」のみを与える。人間がコンソールから触る権限と、マシン(GitHub Actionsなど)が触る権限を明確に分けるのが鉄則だ。

以下は、CI/CD環境に適用すべき、セキュアなIAMポリシーの例だ。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowPushOnlyForCI",
            "Effect": "Allow",
            "Action": [
                "ecr:PutImage",
                "ecr:InitiateLayerUpload",
                "ecr:UploadLayerPart",
                "ecr:CompleteLayerUpload"
            ],
            "Resource": "arn:aws:ecr:region:account-id:repository/my-app-repo",
            "Condition": {
                "StringEquals": {
                    "aws:PrincipalArn": "arn:aws:iam::account-id:role/GitHubActionsRole"
                }
            }
        }
    ]
}

このポリシーの肝は Condition 句にある。たとえ鍵が漏洩しても、指定したロール以外からのプッシュは一切受け付けない。この「出口と入り口の封鎖」こそが、防御の基本だ。

ステップ2:自動脆弱性スキャンの実装(パイプラインの守護神)

プッシュした瞬間にスキャンが走らなければ意味がない。多くのクラウドレジストリには「プッシュ時スキャン」機能がある。これをONにしないのは、夜道を無灯火で走るのと同じだ。

GitHub Actionsで、スキャン結果がNGならデプロイを中断するロジックを組む。これが「ガードレール」だ。

# .github/workflows/deploy.yml の抜粋
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Build and Push
        run: |
          docker build -t my-app:latest .
          docker push my-app:latest
          
      # ここでスキャン結果を待機し、高リスクな脆弱性があれば異常終了させる
      - name: Wait for Vulnerability Scan
        run: |
          # AWS CLIを使ってスキャン結果が "HIGH" 以上の脆弱性がないかチェック
          # 現場の運用に合わせてスキャン完了まで待機するロジックを入れること
          result=$(aws ecr describe-image-scan-findings --repository-name my-app-repo --image-id imageTag=latest --query 'imageScanFindings.findingSeverityCounts.HIGH' --output text)
          if [ "$result" != "None" ]; then
            echo "脆弱性が検出されました!デプロイを中断します。"
            exit 1
          fi

実戦の教訓:完璧な防御は存在しない

コードを書くとき、Dockerfile の FROM 句に注意を払っているか? FROM node:latest と書くのは、自分から「最新の脆弱性をください」と言っているようなものだ。

1. タグの固定: latest は厳禁。SHA256ハッシュで指定するか、バージョンを厳密に固定せよ。
2. マルチステージビルド: ビルド環境のツール(gccやnpm)を最終イメージに残すな。攻撃者に武器を渡すことになる。
3. 定期的な再スキャン: 脆弱性は「昨日までは安全」でも「今日から危険」になり得る。レジストリの設定で「定期スキャン」を有効にするのを忘れるな。

セキュリティは「設定して終わり」の作業ではない。君たちが書いたコードが、インフラという強固な防御壁に守られ、かつその防御壁自体が脆弱であってはならない。

さあ、今すぐ自分のレジストリ設定を確認してくれ。IAMの設定一つ、スキャン設定一つで、君たちのサービスが救われるかもしれないのだから。健闘を祈る。

コメント

タイトルとURLをコピーしました