CheckovによるInfrastructure as Codeセキュリティ:Terraformの設定不備スキャンとCI/CDでのコンプライアンス検証

DevOps tutorial - IT technology blog
DevOps tutorial - IT technology blog

安全でないクラウド設定が本番環境に紛れ込む真の代償

前四半期、あるエンジニアが公開読み取りアクセスが有効でサーバー側暗号化が無効化されたAWS S3バケットを作成するTerraformモジュールをプッシュしました。terraform apply の実行中には何もエラーは起きませんでした。AWS APIは 200 OK を返し、インフラは何の警告も出ることなくプロビジョニングされました。

定期的なSOC 2監査の際にSecOpsチームがこの公開バケットを発見するまでに、14日間が経過していました。幸いにも、約18GBの顧客データベースバックアップがインターネット上に露出していたものの、不正アクセスは行われていませんでした。それでもこの見落としにより、2日間の徹夜、緊急デプロイサイクル、そして4時間にわたる緊迫したポストモーテム(事後検証)レビューという代償を払うことになりました。

このような失敗パターンは驚くほどよく見られます。チームは開発とリリースのスピードを上げるためにInfrastructure as Code(IaC)を採用します。しかし、ガードレールのないスピードは、ミスを大規模に自動化するだけに終わってしまいます。

従来の脆弱性スキャナーはOSパッケージや実行中のコンテナを検査しますが、HashiCorp Configuration Language(HCL)で記述されたクラウドのコントロールプレーン宣言を解析することはできません。本番環境でオープンになっているセキュリティグループを修正するには、エンジニアの調査や検証作業で平均4,000ドルのコストがかかります。一方、まったく同じミスをプルリクエストの段階で検知できれば、30秒もかかりません。

根本原因:安全でないIaCがメインブランチに届いてしまう理由

Terraformは厳密に宣言型です。定義された望ましい状態(desired state)をクラウドプロバイダーと照合・調整しますが、そのアーキテクチャが安全であるかどうかを判断することはありません。組み込みの terraform validate コマンドは、構文、必須引数の定義、基本的な型のみをチェックします。括弧や引用符がHCLとして妥当である限り、VPC全体でポート22を 0.0.0.0/0 に公開するインバウンドルールであっても問題なくパスしてしまいます。

プレッシャーがかかる現場では、人間によるコードレビューも限界を迎えます。1つのプルリクエストが5つのモジュールにわたり400行のTerraformに及ぶと、レビュアーの疲労は急速に蓄積します。エンジニアはモジュールのタグ、変数名、インデントなどに目を奪われ、暗号化されていない2TBのEBSボリュームや、Key Management Service(KMS)のカスタマーマネージドキーが設定されていないS3バケットといった微妙なセキュリティの欠陥を見落としがちになります。プルリクエストがメインブランチにマージされる前に、Policy-as-Code(コードによるポリシー管理)の自動化を導入する必要があります。

基本概念:Checkovがガードレールを適用する仕組み

Checkovは、IaCセキュリティ専用に設計されたオープンソースの静的解析エンジンです。Prisma Cloud(旧Bridgecrew)が開発・保守を行っており、設定ファイルを抽象構文木(AST)にパースします。そして、CIS Benchmarks、NIST 800-53、HIPAA、PCI-DSS 4.0に直接マッピングされた1,000以上の標準ポリシーと照合して評価します。

ランタイムのクラウドセキュリティポスチャ管理(CSPM)ツールとは異なり、Checkovは稼働中のクラウド認証情報やデプロイ済みのリソースを必要としません。コードを完全にオフラインでスキャンします。Kubernetesマニフェスト、CloudFormation、Bicep、ARMテンプレートなどを幅広くサポートしていますが、最も強力に対応しているのがTerraformです。ローカル環境でCheckovを実行すれば、ステートのロックやリソース作成が行われるはるか前、IDEの画面上で直接設定ミスを検知できます。

実践演習:CheckovでTerraformのセキュリティを確保する

ここでは、Checkovをローカルにインストールし、脆弱性のあるTerraform定義に対してテストを実行し、正当な例外を適切に処理した上で、GitHub Actionsで自動検証ゲートを構築するステップバイステップの手順を紹介します。

ステップ 1: Checkovのインストール

CheckovはPythonで記述されています。pip、Homebrew、または公式コンテナイメージからインストールできます。お使いの環境に合わせて選択してください:

# Python pip経由でインストール
pip3 install checkov

# またはHomebrew経由でインストール(macOS/Linux)
brew install checkov

# インストールを確認
checkov --version

ステップ 2: 脆弱性を含むサンプルのTerraformファイルの作成

terraform-demo という名前の空のディレクトリを作成します。その中に、よくある2つの問題(パブリックアクセスブロックのない暗号化されていないS3バケットと、全世界に公開されたSSHインバウンドルール)を含む main.tf ファイルを作成します。

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = "us-east-1"
}

# 欠陥のあるリソース: 暗号化、ロギング、パブリックアクセスブロックが不足
resource "aws_s3_bucket" "finance_reports" {
  bucket = "company-internal-finance-reports-2026"
}

# 欠陥のあるリソース: SSHポート22がグローバルに開放されている
resource "aws_security_group" "bastion_sg" {
  name        = "bastion-sg"
  description = "踏み台ホスト(Bastion)用セキュリティグループ"

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

ステップ 3: 初回スキャンの実行

ローカルフォルダに対してCheckovを実行します:

checkov -d . --framework terraform

2秒も経たないうちに、Checkovはターミナル上に合格および不合格となったチェックを強調表示した評価サマリーを出力します。今回のファイルでは、直ちに複数の違反が検出されます:

  • CKV_AWS_20: S3バケットがACLまたはポリシー経由でパブリックな読み取りアクセスを許可している。
  • CKV_AWS_145: S3バケットがKMSカスタマーマネージドキー(SSE-KMS)で暗号化されていない。
  • CKV_AWS_24: セキュリティグループが 0.0.0.0/0 からポート22へのグローバルなインバウンド接続を許可している。
  • CKV_AWS_53: S3バケットで block_public_acls が有効になっていない。

各アラートには、原因となっている正確な行番号、不合格となったチェックの識別子、および修復手順を解説したURLが表示されます。

ステップ 4: 検出された問題の修正

最近のAWSプロバイダーバージョン(v4およびv5)では、S3バケットのサブ機能が独立した専用リソースに分離されています。SSHアクセスを特定のオフィスIP(198.51.100.12/32)に制限し、サーバー側暗号化を追加し、厳格なパブリックアクセスブロックを設定して main.tf を堅牢化(ハードニング)しましょう:

resource "aws_s3_bucket" "finance_reports" {
  bucket = "company-internal-finance-reports-2026"
}

resource "aws_s3_bucket_server_side_encryption_configuration" "finance_reports_crypto" {
  bucket = aws_s3_bucket.finance_reports.id

  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm = "AES256"
    }
  }
}

resource "aws_s3_bucket_public_access_block" "finance_reports_block" {
  bucket = aws_s3_bucket.finance_reports.id

  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

resource "aws_security_group" "bastion_sg" {
  name        = "bastion-sg"
  description = "踏み台ホスト(Bastion)用セキュリティグループ"

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    # 社内ゲートウェイIPのみに制限
    cidr_blocks = ["198.51.100.12/32"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

スキャンを再実行すると、重大な指摘事項はすべて解消され、安全であることが確認できます。

ステップ 5: ベースラインと正当な例外の管理

既存のコードベースに静的スキャナーを導入すると、初日から300件以上の違反が浮き彫りになることがあります。すべてのプルリクエストを一律で即座にブロックしてしまうと、チームの開発スピードは完全にストップしてしまいます。Checkovは、ベースライン機能とインラインでの抑止(サプレッション)によってこのジレンマを解決します。

製品ロゴを配信するパブリックな静的アセットバケットなど、正当な理由による例外が認められる場合は、明確な理由を記載してインラインでルールを抑制します:

resource "aws_s3_bucket" "public_assets" {
  bucket = "company-brand-static-cdn-assets"
  # checkov:skip=CKV_AWS_20:静的Webサイトアセット用の公開バケット
}

レガシーなコードベースに対しては、新規開発を止めることなく既存の技術的負債をスナップショットとして記録します:

# 既存の違反をベースラインファイルに記録
checkov -d . --create-baseline

# ベースラインに対する新規のデグレ(新たな違反)のみを検知・ブロック
checkov -d . --baseline .checkov.baseline

既存のレガシーな警告は抑制されたままとなります。そしてエンジニアが新たなセキュリティ不備を追加した場合にのみ、パイプラインが即座に失敗します。

ステップ 6: GitHub ActionsによるCheckovの自動化

ローカルでのスキャンは各開発者の自主性に依存します。CIパイプラインを構築することで、ポリシーの適用を必須化できます。main ブランチ宛てのすべてのプルリクエストをスキャンするために、以下のワークフローを .github/workflows/iac-security.yml に追加します:

name: "IaC Security Scan"

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

jobs:
  checkov-scan:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      security-events: write

    steps:
      - name: Checkout Source Code
        uses: actions/checkout@v4

      - name: Run Checkov Scanner
        id: checkov
        uses: bridgecrewio/checkov-action@master
        with:
          directory: "."
          framework: "terraform"
          output_format: "cli,sarif"
          output_file_path: "console,results.sarif"
          soft_fail: false # チェック失敗時にプルリクエストをブロックする

      - name: Upload SARIF Results to GitHub Security Tab
        uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: "results.sarif"

SARIFエクスポートを有効にすると、Checkovはビルドを失敗させるだけでなく、GitHubのプルリクエストの差分ビュー上に該当の違反行を直接アノテーション表示します。開発者は生のコンソールログを掘り起こすことなく、その場ですぐに問題を修正できます。

シフトレフトで安心して眠れる環境へ

デプロイ後にセキュリティ負債を解消するのは、精神的な負担が大きく、高コストで、リスクも伴います。TerraformのライフサイクルにCheckovを組み込むことで、セキュリティチェックは迅速で開発の流れを止めないコードレビューの一環になります。まずはコアモジュールに対するローカルでのチェックから始め、既存の警告をベースラインファイルで退避させ、新たな設定不備が本番環境に到達しないようCIゲートを設けることから始めましょう。

Share: