【入門編】 コンテナイメージの脆弱性スキャンとCI/CDパイプラインへの統合 – 暗号理論・認証基盤 & エンドポイントセキュリティ防御ガイド

こんにちは!開発チームの皆さん、日々のコーディングやインフラ構築、本当にお疲れ様です。

突然ですが、皆さんは「家を建てる時」のことを少し想像してみてください。頑丈な玄関ドアを作り、最新のピッキング対策が施された鍵をかけ、窓には二重ロックをつけますよね。では、その家を支える「壁のコンクリート」や「柱の木材」の中に、最初からボロボロの欠陥品が混ざっていたとしたらどうでしょう?どれだけ立派な鍵をかけても、泥棒は壁を軽く蹴破って侵入できてしまいますよね。

私たちが日々開発しているソフトウェアの世界でも、これと全く同じことが起きているんです。今回は、最近のモダンな開発には欠かせない「コンテナイメージの脆弱性スキャン」と、それを自動化するCI/CDパイプラインへの統合について、身近な防犯に例えながら優しく紐解いていきたいと思います。一歩ずつ、一緒に学んでいきましょう!

—

1. コンテナ時代の「見えない欠陥品」にどう立ち向かうか

皆さんは、Dockerなどのコンテナ技術を使ってアプリケーションを動かした経験はありますか?コンテナは、アプリを動かすためのプログラムだけでなく、OSの基本的な部品や便利なライブラリまでを1つの「箱(イメージ)」にまとめて、どこでも同じように動かせるという魔法のような技術です。

このコンテナイメージを作る時、私たちはゼロからすべてを手作りするわけではありません。多くの場合、インターネット上で公開されている公式のベースイメージ(例えば ubuntu や node、python など)を土台にして、その上に自分たちのプログラムを乗せていきます。

ここで問題になるのが、「土台として使った誰かの作った部品(オープンソースソフトウェアやOSのパッケージ)に、もし穴(脆弱性)があったらどうなるか?」ということです。

攻撃者は、私たちが書いた自慢のアプリケーションコードではなく、この土台(サードパーティ製のライブラリ)に隠された古い既知の脆弱性(CVE)を狙ってきます。家の鍵ではなく、裏口の蝶番(ちょうつがい)が最初から錆びて外れかけていた、そんなイメージです。

—

2. 泥棒を防ぐ自動の番人:Trivy(トリビー)の登場

「じゃあ、土台の部品に穴がないか、一つひとつ人間の目で調べなきゃいけないの?」
いいえ、そんな膨大な作業をエンジニアが手動でする必要はありません。ここで登場するのが、コンテナの脆弱性スキャナーである Trivy(トリビー)です。

Trivy は、いわば「コンテナイメージの荷物検査場にいる超優秀なセキュリティ検査員」です。コンテナの箱を開け、中に入っているすべてのファイルやライブラリを瞬時にスキャンして、「おいおい、この中に去年発見された危険なバグが含まれているよ!」と教えてくれます。

Trivyの優れているところ

  • とにかく速い: わずか数秒でイメージ全体の検査が終わります。
  • 網羅性が高い: OSのパッケージ(AptやYumなど)だけでなく、各言語のライブラリ(npm、pip、gemなど)までくまなくチェックしてくれます。
  • 直感的: 発見された脆弱性を深刻度(Critical、High、Medium、Low)ごとに綺麗に分類してくれます。

—

3. CI/CDパイプラインへの統合:工場出荷前の「最終検品」

さて、脆弱性が見つかる場所が分かったところで、これを「いつ」チェックすべきでしょうか?
デプロイ(本番環境への公開)が終わった後に、「あ、先週のイメージに穴がありました」と気づくのでは、泥棒が入った後に鍵をかけるようなものです。

だからこそ、コードをビルドしてコンテナイメージを作り上げる自動化の仕組み、すなわち CI/CDパイプライン(GitHub ActionsやGitLab CIなど)の中にスキャンを組み込む 必要があります。これは、工場で製品を箱詰めする直人のベルトコンベアの途中に「自動X線検査機」を置くようなものです。高リスクな不良品が混ざっていたら、その場でラインをガチャンと止めて、外に出さないようにするわけです。

—

4. 実践!GitHub Actionsで脆弱性ブロックを設定する

それでは、実際にCI/CDパイプライン(今回はGitHub Actionsを例にします)の中で、Trivyを使って高リスクな脆弱性(CriticalやHigh)が見つかった場合に、デプロイを強制的にブロック(失敗)させる設定を見てみましょう。

難しそうに見えるかもしれませんが、以下の設定ファイルをリポジトリに置くだけで、あなたのプロジェクトに頑丈な自動検品システムが備わります。

name: Container Security Scan

on:
  push:
    branches: [ "main" ] # mainブランチにコードがプッシュされたときに自動で動き出します

jobs:
  build-and-scan:
    name: Build, Scan and Block
    runs-on: ubuntu-latest

    steps:
      # 1. リポジトリのコードをチェックアウトする
      - name: Checkout code
        uses: actions/checkout@v4

      # 2. Dockerコンテナイメージをビルドする(例として my-app:latest という名前をつけます)
      - name: Build Docker image
        run: |
          docker build -t my-app:latest .

      # 3. ここが主役!Trivyを使ってコンテナイメージをスキャンする
      - name: Run Trivy vulnerability scanner
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: 'my-app:latest'
          format: 'table'
          exit-code: '1' # ★ここが重要!脆弱性が見つかったら終了コード1を返し、パイプラインを失敗させます
          severity: 'CRITICAL,HIGH' # 許容しない深刻度を指定(今回は深刻度「高」と「致命的」をターゲットに)

      # 4. スキャンを無事にパスした場合のみ、本番環境へのプッシュやデプロイへ進む
      - name: Deploy to Production (Passes only if no High/Critical CVEs)
        if: success()
        run: |
          echo "脆弱性チェックをクリアしました!安全にデプロイを進めます。"
          # ここに実際のデプロイコマンド(ECSやKubernetesへの適用など)を記述します

設定のポイント解説

  • exit-code: '1' の指定: 通常、プログラムは正常終了すると 0 を返しますが、ここであえて 1 を指定することで、「おい、危険な脆弱性が見つかったぞ!」とGitHub Actionsに知らせ、ビルドやデプロイのプロセスを強制的にストップさせます。
  • severity: 'CRITICAL,HIGH' の指定: 世の中には無数の小さな脆弱性(Lowなど)が存在します。すべてを完璧にしようとすると開発が止まってしまうため、まずは実害の大きい「致命的 (CRITICAL)」と「高 (HIGH)」の2つに絞ってブロックするのが、現場で最も現実的かつ効果的なアプローチです。

—

5. 現場のホワイトハッカーからのアドバイス

最後に、実務でコンテナセキュリティを運用する上での「生きた知見」を少しだけお伝えします。

セキュリティの自動化を入れた直後は、高リスクな脆弱性が次々と検出されて、ビルドが何度も失敗するかもしれません。「開発が進まない!」とイライラしてしまうこともあるでしょう。しかし、それはシステムが正常に機能し、今まで見えていなかった「隠れた隙間」を教えてくれている証拠です。

もしどうしてもすぐに対応できないサードパーティライブラリの脆弱性に出くわした場合は、例外的にスキャンを無視する .trivyignore という除外ファイルを使うこともできます。ただし、これは「鍵の壊れた窓に板を打ち付ける」ような応急処置ですので、基本は「ベースイメージをこまめに最新のものにアップデートする(リビルドする)」ことを習慣にしてください。

セキュリティは一度やったら終わりのイベントではなく、毎日の歯磨きのような習慣です。まずは小さな一歩として、今日のプロジェクトにスキャナーを導入してみてはいかがでしょうか?

それでは、安全で快適な開発ライフを!

コメント

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