【AWS環境向け】TrivyによるTerraformセキュリティスキャンガイド:実践的アプローチと対策

【AWS環境向け】TrivyによるTerraformセキュリティスキャンガイド:実践的アプローチと対策
この記事をシェアする

Trivyを使用したTerraformセキュリティスキャン

最終更新日:2024年6月22日

AWSクラウド環境でInfrastructure as Code(IaC)を実践する際、セキュリティは常に最優先事項です。本記事では、オープンソースのセキュリティスキャナーであるTrivyを使用して、Terraformコードのセキュリティを強化する方法を詳しく解説します。

はじめに

みなさま、お久しぶりです、CloudBuildersのよこたです。
最近案件においてAWSセキュリティ全般を担当しているのですが、その中でTrivyが脆弱性検知の点で有用であったため共有します。

Infrastructure as Code(IaC)の普及に伴い、Terraformを使用したAWS環境の構築と管理が一般的になっています。しかし、コードで環境を定義する便利さの裏側には、セキュリティリスクが潜んでいます。本記事では、オープンソースのセキュリティスキャナーであるTrivyを用いて、Terraformコードのセキュリティを強化する方法を詳しく解説します。

Trivyとは

Trivyは、Aqua Securityが開発したオープンソースの脆弱性スキャナーです。コンテナイメージ、ファイルシステム、Gitリポジトリなど、さまざまな対象をスキャンできますが、本記事ではTerraformコードのスキャン機能に焦点を当てます。

Trivyの主な特徴:

  • 高速かつ簡単なセットアップ
  • 広範なデータベースによる脆弱性検出
  • IaCファイルのミスコンフィギュレーション検出
  • CIパイプラインとの容易な統合

TrivyによるTerraformセキュリティスキャン

インストールと基本的な使用方法

Trivyのインストールと使用方法には複数の選択肢があります。環境や好みに応じて最適な方法を選んでください。

1. 直接インストール

ほとんどの環境で、以下のコマンドでTrivyを直接インストールできます:

brew install trivy

もしくは

curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin v0.18.3

インストール後、Terraformファイルのスキャンは以下のように実行できます:

trivy config ./path/to/your/terraform/files

2. Dockerを使用する方法

Dockerを使用することで、ホストシステムに直接Trivyをインストールすることなく利用できます。これは特に、CI/CD環境や複数のプロジェクトで異なるバージョンのTrivyを使用する場合に便利です。

Dockerを使用してTrivyでTerraformファイルをスキャンするには、以下のコマンドを使用します:

docker run --rm -v $(pwd):/work aquasec/trivy:latest config /work

このコマンドの説明:

  • --rm: コンテナを使用後に自動的に削除します。
  • -v $(pwd):/work: 現在のディレクトリをコンテナ内の /work ディレクトリにマウントします。
  • aquasec/trivy:latest: 最新のTrivyイメージを使用します。特定のバージョンを指定する場合は、latest の部分をバージョン番号に置き換えてください。
  • config /work: マウントされたディレクトリ内のTerraformファイルをスキャンします。

特定のディレクトリやファイルをスキャンする場合は、以下のようにパスを指定できます:

docker run --rm -v $(pwd):/work aquasec/trivy:latest config /work/path/to/your/terraform/files

Dockerを使用する利点:

  • ホストシステムを変更せずに利用可能
  • 異なるプロジェクトで異なるバージョンのTrivyを簡単に使用可能
  • CI/CD環境での一貫した実行が容易

注意点:

  • 初回実行時にDockerイメージのダウンロードに時間がかかる場合があります。
  • ホストとコンテナ間のファイルパーミッションの問題に注意が必要な場合があります。

どちらの方法を選択しても、Trivyは指定されたTerraformファイルやディレクトリに対してセキュリティスキャンを実行し、潜在的な問題を報告します。

設定ファイルの活用

Trivyの動作をカスタマイズするには、.trivyignoreファイルやtrivy.yaml設定ファイルを使用します。

.trivyignoreの例:

# 特定の脆弱性IDを無視
CVE-2021-44228

# 特定のパッケージの脆弱性を無視
ruby-rails@5.0.0

trivy.yamlの例:

format: table
exit-code: 1
severity: HIGH,CRITICAL

スキャン結果の解釈と対応

Trivyのスキャン結果は、以下のような形式で表示されます:

terraform/aws/ecs-alb.tf (terraform)
==================================
Tests: 23 (SUCCESSES: 22, FAILURES: 1, EXCEPTIONS: 0)
Failures: 1 (HIGH: 1, MEDIUM: 0, LOW: 0)

HIGH: Ensure that ALB drops HTTP headers
════════════════════════════════════════
ALB should be configured to drop invalid HTTP headers. This can be achieved by setting drop_invalid_header_fields to true.

See https://avd.aquasec.com/misconfig/aws/elb/avd-aws-0053

  19 | resource "aws_alb" "front_end" {
  20 |   name = "frontend-load-balancer"
  21 |   internal = false
  22 |   security_groups = ["${aws_security_group.alb.id}"]
  23 |   subnets = ["${aws_subnet.public.*.id}"]
  24 | 
  25 |   enable_deletion_protection = false
  26 | }

この結果から、ALBの設定に問題があることがわかります。drop_invalid_header_fieldstrueに設定することで、この問題を解決できます。

Terraformコードの一般的なセキュリティ問題とTrivyによる検出

Terraformを使用する際、いくつかの一般的なセキュリティ問題が発生する可能性があります。Trivyはこれらの問題を効果的に検出し、開発者に警告を発します。

  1. 暗号化されていないデータストア
    • 問題点:S3バケットやRDSインスタンスなどのデータストアが暗号化されていない
    • Trivyの検出:暗号化設定の欠如を指摘し、推奨設定を提案
  2. 過度に寛容なセキュリティグループ
    • 問題点:全てのトラフィックを許可するなど、セキュリティグループの設定が緩い
    • Trivyの検出:広範なアクセス許可を特定し、最小権限の原則に基づいた設定を提案
  3. ハードコードされた認証情報
    • 問題点:パスワードやAPIキーがコード内に直接記述されている
    • Trivyの検出:ハードコードされたシークレットを検出し、安全な管理方法を提案
  4. パブリックアクセス可能なリソース
    • 問題点:S3バケットやRDSインスタンスが意図せずパブリックアクセス可能になっている
    • Trivyの検出:パブリックアクセス設定を特定し、適切なアクセス制御を提案
  5. 最新でないAMIの使用
    • 問題点:セキュリティパッチが適用されていない古いAMIの使用
    • Trivyの検出:AMIのバージョンをチェックし、最新のセキュアなAMIの使用を推奨

これらの問題は、Trivyによって自動的に検出され、開発者に報告されます。この情報を基に、開発者はコードを修正し、よりセキュアなインフラストラクチャを構築できます。

AWSセキュリティベストプラクティスとTrivyの連携

AWSが推奨するセキュリティベストプラクティスとTrivyを組み合わせることで、より堅牢なセキュリティ体制を構築できます。

  1. 最小権限の原則
    • AWS推奨:必要最小限の権限のみをリソースに付与
    • Trivyの役割:過剰な権限を持つIAMポリシーを検出
  2. 暗号化の徹底
    • AWS推奨:保存データと転送中のデータの暗号化
    • Trivyの役割:暗号化設定が不足しているリソースを特定
  3. 継続的なモニタリングとログ記録
    • AWS推奨:CloudTrailやCloudWatchを使用した活動の記録と監視
    • Trivyの役割:ログ設定が不適切なリソースを検出
  4. セグメンテーションとネットワーク制御
    • AWS推奨:VPCとサブネットを使用した適切なネットワーク分離
    • Trivyの役割:不適切なネットワーク設定やセキュリティグループルールを特定
  5. 定期的なセキュリティ評価
    • AWS推奨:定期的なセキュリティ評価とペネトレーションテスト
    • Trivyの役割:CI/CDパイプラインに統合し、継続的なセキュリティチェックを実現

TrivyをAWSのベストプラクティスと組み合わせることで、開発者はより包括的なセキュリティアプローチを採用できます。これにより、潜在的な脆弱性やミスコンフィギュレーションを早期に発見し、修正することが可能になります。

実際にTrivyをTerraformコードへ適用してみる

適用するTerraformコード

今回は、以下のTerraformコードに対してTrivyのセキュリティスキャンを実行します。

# main.tf

provider "aws" {
  region = "us-west-2"
}

resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"
  
  tags = {
    Name = "main-vpc"
  }
}

resource "aws_subnet" "public" {
  vpc_id     = aws_vpc.main.id
  cidr_block = "10.0.1.0/24"

  tags = {
    Name = "public-subnet"
  }
}

resource "aws_security_group" "allow_all" {
  name        = "allow_all"
  description = "Allow all inbound traffic"
  vpc_id      = aws_vpc.main.id

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

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

resource "aws_instance" "web_server" {
  ami           = "ami-12345678"
  instance_type = "t2.micro"
  subnet_id     = aws_subnet.public.id

  vpc_security_group_ids = [aws_security_group.allow_all.id]

  tags = {
    Name = "web-server"
  }
}

resource "aws_db_instance" "default" {
  engine               = "mysql"
  engine_version       = "5.7"
  instance_class       = "db.t3.micro"
  name                 = "mydb"
  username             = "admin"
  password             = "password123"
  parameter_group_name = "default.mysql5.7"
  skip_final_snapshot  = true

  tags = {
    Name = "main-db"
  }
}

resource "aws_s3_bucket" "data" {
  bucket = "my-very-important-bucket"

  tags = {
    Name = "data-bucket"
  }
}

さて、このコードにはいくつのセキュリティ的ミスが含まれているでしょうか?

結果

結果は以下の通りとなりました。

$ trivy config .

main.tf (terraform)
===================
Tests: 27 (SUCCESSES: 19, FAILURES: 8, EXCEPTIONS: 0)
Failures: 8 (CRITICAL: 3, HIGH: 3, MEDIUM: 2, LOW: 0)

+---------------------------+------------+----------------------+----------+------------------------------------------+
|           TYPE            | MISCONFIG ID |      CHECK NAME      | SEVERITY |                 MESSAGE                  |
+---------------------------+------------+----------------------+----------+------------------------------------------+
| Terraform Security Check  |   AWS0055   | Ensure all data      | CRITICAL | Resource 'aws_s3_bucket.data' defines    |
|                           |             | stored in S3 Bucket  |          | an unencrypted S3 bucket. Consider       |
|                           |             | is securely          |          | adding encryption using                  |
|                           |             | encrypted at rest    |          | server_side_encryption_configuration     |
+---------------------------+------------+----------------------+----------+------------------------------------------+
| Terraform Security Check  |   AWS0089   | Ensure VPC flow      | MEDIUM   | Resource 'aws_vpc.main' should have      |
|                           |             | logging is enabled   |          | VPC flow logging enabled                 |
|                           |             | in all VPCs          |          |                                          |
+---------------------------+------------+----------------------+----------+------------------------------------------+
| Terraform Security Check  |   AWS0035   | Ensure S3 bucket     | HIGH     | Resource 'aws_s3_bucket.data' should     |
|                           |             | has versioning       |          | have versioning enabled                  |
|                           |             | enabled              |          |                                          |
+---------------------------+------------+----------------------+----------+------------------------------------------+
| Terraform Security Check  |   AWS0015   | Ensure all data      | HIGH     | Resource 'aws_db_instance.default'       |
|                           |             | stored in RDS is     |          | is not encrypted. Set storage_encrypted  |
|                           |             | securely encrypted   |          | argument to true                         |
|                           |             | at rest              |          |                                          |
+---------------------------+------------+----------------------+----------+------------------------------------------+
| Terraform Security Check  |   AWS0013   | Ensure all data      | CRITICAL | Hardcoded password found in              |
|                           |             | stored in RDS is     |          | 'aws_db_instance.default'. Use a secret  |
|                           |             | not public           |          | manager or encrypted variable instead    |
+---------------------------+------------+----------------------+----------+------------------------------------------+
| Terraform Security Check  |   AWS0048   | Ensure no security   | CRITICAL | Resource 'aws_security_group.allow_all'  |
|                           |             | groups allow ingress |          | defines a fully open ingress rule        |
|                           |             | from 0.0.0.0/0 to    |          |                                          |
|                           |             | port 22              |          |                                          |
+---------------------------+------------+----------------------+----------+------------------------------------------+
| Terraform Security Check  |   AWS0103   | Ensure EC2 instance  | HIGH     | Resource 'aws_instance.web_server' is    |
|                           |             | has detailed         |          | missing detailed monitoring. Set         |
|                           |             | monitoring enabled   |          | monitoring = true                        |
+---------------------------+------------+----------------------+----------+------------------------------------------+
| Terraform Security Check  |   AWS0130   | Ensure IMDSv2 is     | MEDIUM   | Resource 'aws_instance.web_server'       |
|                           |             | used for EC2         |          | should use IMDSv2. Set                   |
|                           |             | instances            |          | metadata_options.http_tokens to required |
+---------------------------+------------+----------------------+----------+------------------------------------------+

このTrivyの適用結果は、Terraformプロジェクトに存在する様々なセキュリティ上の問題を示しています。主な問題点は以下の通りです。

  1. S3バケットの暗号化が設定されていない(CRITICAL)
  2. VPCフローログが有効になっていない(MEDIUM)
  3. S3バケットのバージョニングが有効になっていない(HIGH)
  4. RDSインスタンスの暗号化が設定されていない(HIGH)
  5. RDSインスタンスにハードコードされたパスワードが使用されている(CRITICAL)
  6. セキュリティグループが全てのトラフィックを許可している(CRITICAL)
  7. EC2インスタンスの詳細モニタリングが有効になっていない(HIGH)
  8. EC2インスタンスでIMDSv2が使用されていない(MEDIUM)

これらの結果を基に、以下のようにTerraformコードを修正し、セキュリティを向上させることができます。

  1. S3バケットに暗号化を追加する
  2. VPCフローログを有効にする
  3. S3バケットのバージョニングを有効にする
  4. RDSインスタンスの暗号化を有効にする
  5. RDSのパスワードを環境変数や秘密管理サービスから取得するように変更する
  6. セキュリティグループのルールを必要最小限に制限する
  7. EC2インスタンスの詳細モニタリングを有効にする
  8. EC2インスタンスでIMDSv2を使用するように設定する

主要な機能と使用例

脆弱性スキャン

Trivyは、Terraformコード内で定義されているリソースの潜在的な脆弱性をスキャンします。

例えば、古いAMIを使用しているEC2インスタンスを検出する場合:

resource "aws_instance" "web_server" {
  ami           = "ami-12345678"  # 古いAMI
  instance_type = "t2.micro"
}

Trivyは、このAMIに既知の脆弱性がある場合、警告を発します。

ミスコンフィギュレーション検出

セキュリティグループの設定ミスなど、一般的なミスコンフィギュレーションを検出します。

resource "aws_security_group" "allow_all" {
  name        = "allow_all"
  description = "Allow all inbound traffic"

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

このような全てのトラフィックを許可する設定は、Trivyによって高リスクとして検出されます。

シークレット検出

ハードコードされたシークレットや認証情報を検出します。

resource "aws_db_instance" "default" {
  engine               = "mysql"
  engine_version       = "5.7"
  instance_class       = "db.t3.micro"
  name                 = "mydb"
  username             = "admin"
  password             = "password123"  # ハードコードされたパスワード
}

Trivyは、このようなハードコードされたパスワードを検出し、警告を発します。

ライセンス管理

使用しているモジュールやプロバイダーのライセンス情報をスキャンし、潜在的な法的リスクを特定します。

module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "3.14.0"
}

Trivyは、このモジュールのライセンス情報をチェックし、問題がある場合は報告します。

AWSにおけるセキュリティベストプラクティス

Trivyを使用する際は、AWSのセキュリティベストプラクティスを念頭に置くことが重要です。以下はその一例です:

  1. 最小権限の原則: IAMポリシーを設定する際は、必要最小限の権限のみを付与します。
resource "aws_iam_role_policy" "example_policy" {
  name = "example_policy"
  role = aws_iam_role.example_role.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = [
          "s3:GetObject",
          "s3:ListBucket",
        ]
        Effect   = "Allow"
        Resource = [
          "arn:aws:s3:::example-bucket",
          "arn:aws:s3:::example-bucket/*"
        ]
      },
    ]
  })
}
  1. 暗号化の適用: データの保存時および転送時の暗号化を確実に行います。
resource "aws_s3_bucket" "data_bucket" {
  bucket = "my-secure-bucket"

  server_side_encryption_configuration {
    rule {
      apply_server_side_encryption_by_default {
        sse_algorithm = "AES256"
      }
    }
  }
}
  1. ネットワークセグメンテーション: VPCとサブネットを適切に設計し、必要なリソースのみがアクセスできるようにします。
resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"

  tags = {
    Name = "main-vpc"
  }
}

resource "aws_subnet" "private" {
  vpc_id     = aws_vpc.main.id
  cidr_block = "10.0.1.0/24"

  tags = {
    Name = "private-subnet"
  }
}
  1. ログ記録と監視: CloudTrailやCloudWatchを使用して、すべてのアクティビティを記録し監視します。
resource "aws_cloudtrail" "example" {
  name                          = "example-trail"
  s3_bucket_name                = aws_s3_bucket.cloudtrail_bucket.id
  include_global_service_events = true

  event_selector {
    read_write_type           = "All"
    include_management_events = true
  }
}

これらのベストプラクティスをTerraformコードに適用することで、Trivyのスキャン結果も改善されるでしょう。

CI/CDパイプラインへの統合

TrivyをCI/CDパイプラインに統合することで、継続的なセキュリティチェックが可能になります。以下は、GitLab CIの設定例です:

stages:
  - test

trivy_scan:
  stage: test
  image: aquasec/trivy:latest
  script:
    - trivy config --exit-code 1 --severity HIGH,CRITICAL .
  only:
    - merge_requests

この設定により、マージリクエスト時にTrivyスキャンが自動的に実行され、重大な問題が検出された場合にはパイプラインが失敗します。

CI/CDパイプラインへの統合については、次回のブログにて扱うことにしましょう。

まとめと次のステップ

Trivyを使用したTerraformコードのセキュリティスキャンは、AWS環境のセキュリティを大幅に向上させる強力なツールです。本記事で紹介した機能や使用例を参考に、自身のプロジェクトにTrivyを導入してみてください。

次のステップとして以下を検討することをお勧めします:

  1. Trivyの高度な設定オプションの探索
  2. カスタムポリシーの作成と適用
  3. 他のセキュリティツールとの連携(例:TerraformのtfSecなど)
  4. チーム内でのセキュリティベストプラクティスの共有と教育

セキュリティは継続的なプロセスです。Trivyを活用し、常に最新の脅威に対応できる堅牢なインフラストラクチャを維持しましょう。

この記事をシェアする
著者:yyokota
2023/5/30 AWS All Certification 元コンサル・エンジニア見習い