Cloud Storage バケットの保持ポリシーロック設定について

このブログシリーズ 「クラウドセキュリティ 実践集」 では、一般的なセキュリティ課題を取り上げ、「なぜ危険なのか?」 というリスクの解説から、 「どうやって直すのか?」 という具体的な修復手順(コンソール、gcloud CLI、Terraformなど)まで、分かりやすく解説します。

この記事では、Cloud Storageバケットのオブジェクト保持ポリシーをロックする設定手順について、リスクと対策を解説します。

ポリシーの説明

バケットの保持ポリシーをロックし、データとポリシーの保護を強化します。

Cloud Storageの保持ポリシー機能は、オブジェクトが指定された期間中削除されないようにすることで、コンプライアンス要件やレコード管理要件を満たすのに役立ちます。保持ポリシーをロックすることで、ポリシー自体の変更や削除を防ぎ、重要なデータの不正な削除や変更から保護します。

保持ポリシーがロックされると、以下の制限が適用されます:

  • 保持ポリシーの削除不可
  • 保持期間の短縮不可(延長のみ可能)
  • バケットの削除不可(保持期間中のオブジェクトが存在する限り)

修復方法

コンソールでの修復手順

Google Cloud コンソールを使用して、Cloud Storageバケットの保持ポリシーをロックします。

重要な注意事項:

  • 保持ポリシーのロックは不可逆的な操作です
  • 一度ロックすると、ポリシーの削除や保持期間の短縮はできません
  • 保持期間の延長のみ可能です
  • バケット内の全オブジェクトに保持ポリシーが適用されます
  • 保持期間中はオブジェクトの削除ができません(コスト影響を考慮してください)
  1. Google Cloud Consoleにログインし、Cloud Storageページに移動します。
  1. 対象のバケットを選択します。
  2. バケットの詳細ページで、保護タブをクリックします。
  3. 保持ポリシーセクションで、まだ保持ポリシーが設定されていない場合:
    • 保持ポリシーを設定をクリック
    • 保持期間(日、月、年単位)を入力
    • 保存をクリック
  4. 保持ポリシーが設定されている状態で、ポリシーをロックをクリックします。
  5. ロック確認ダイアログが表示されます:
    • ロックの不可逆性について警告が表示されます
    • バケット名を入力して確認
    • ポリシーをロックをクリック
  6. ロックが完了すると、保持ポリシーのステータスが「ロック済み」に変わります。

Terraformでの修復手順

Cloud Storageバケットの保持ポリシーを設定し、ロックするTerraformコードと、主要な修正ポイントを説明します。

# -------------------- ① 保持ポリシー付きバケット --------------------
resource "google_storage_bucket" "compliance_bucket" {
  name          = "compliance-data-${var.environment}"
  location      = var.region
  force_destroy = false  # 重要:保持ポリシーのあるバケットは削除保護

  # --- 保持ポリシーの設定 ---
  retention_policy {
    retention_period = 2592000  # 30日間(秒単位)
    # 保持期間の計算例:
    # 1日 = 86400秒
    # 30日 = 2592000秒
    # 90日 = 7776000秒
    # 1年(365日) = 31536000秒
    # 7年(SOX法) = 220752000秒
    # is_locked = true  # Terraformでは直接ロックできません(後述)
  }

  # Event-Based Hold(オプション)
  # 特定のイベント(訴訟など)に基づく追加の保持制御
  default_event_based_hold = false

  # --- その他のセキュリティ設定 ---
  uniform_bucket_level_access = true  # 均一なバケットレベルアクセス

  versioning {
    enabled = true  # バージョニングを有効化
  }

  # 注意:保持ポリシーが設定されている場合、
  # lifecycle_ruleはretention_period経過後にのみ適用されます
  lifecycle_rule {
    condition {
      age = 365  # 保持期間後の追加保存期間
      with_state = "ANY"  # アーカイブ済みオブジェクトも含む
    }
    action {
      type = "Delete"
    }
  }

  # 暗号化設定
  encryption {
    default_kms_key_name = google_kms_crypto_key.bucket_key.id
  }

  # ログ設定
  logging {
    log_bucket = google_storage_bucket.audit_logs.name
    log_object_prefix = "compliance-bucket-logs/"
  }
}

# -------------------- ② KMS暗号鍵 --------------------
resource "google_kms_key_ring" "keyring" {
  name     = "compliance-keyring"
  location = var.region
}

resource "google_kms_crypto_key" "bucket_key" {
  name            = "compliance-bucket-key"
  key_ring        = google_kms_key_ring.keyring.id
  rotation_period = "2592000s"  # 30日ごとに自動ローテーション

  lifecycle {
    prevent_destroy = true  # 暗号鍵の削除を防止
  }
}

# -------------------- ③ 監査ログ用バケット --------------------
resource "google_storage_bucket" "audit_logs" {
  name          = "audit-logs-${var.environment}"
  location      = var.region
  force_destroy = false

  retention_policy {
    retention_period = 7776000  # 90日間
  }

  lifecycle_rule {
    condition {
      age = 2555  # 7年間保持
    }
    action {
      type = "Delete"
    }
  }
}

# -------------------- ④ IAMポリシー --------------------
resource "google_storage_bucket_iam_policy" "compliance_bucket_policy" {
  bucket = google_storage_bucket.compliance_bucket.name

  policy_data = data.google_iam_policy.bucket_policy.policy_data
}

data "google_iam_policy" "bucket_policy" {
  # 読み取り専用アクセス
  binding {
    role = "roles/storage.objectViewer"
    members = [
      "<group:compliance-auditors@example.com>",
    ]
  }

  # 管理者アクセス(制限付き)
  binding {
    role = "roles/storage.admin"
    members = [
      "<group:compliance-admins@example.com>",
    ]

    condition {
      title       = "Require MFA for admin access"
      description = "Require multi-factor authentication for administrative actions"
      expression  = "request.auth.claims[\"<https://cloud.google.com/mfa_verified\>"] == true"
    }
  }
}

# -------------------- ⑤ 保持ポリシーロックのためのnullリソース --------------------
# 注意:Terraformでは保持ポリシーのロックを直接サポートしていないため、
# local-execプロビジョナーを使用する必要があります
# セキュリティ上の注意:local-execは実行環境のgcloud CLIを使用するため、
# CI/CD環境では適切な認証設定が必要です
# -------------------- ⑤ 保持ポリシーロックのためのnullリソース --------------------
# 注意:Terraformでは保持ポリシーのロックを直接サポートしていないため、
# local-execプロビジョナーを使用する必要があります

# オプション1: 対話的確認あり(開発環境向け)
resource "null_resource" "lock_retention_policy" {
  depends_on = [google_storage_bucket.compliance_bucket]

  provisioner "local-exec" {
    command = <<-EOT
      echo "WARNING: About to lock retention policy for ${google_storage_bucket.compliance_bucket.name}"
      echo "This action is IRREVERSIBLE!"
      echo "Current retention period: ${google_storage_bucket.compliance_bucket.retention_policy[0].retention_period} seconds"

      # CI/CD環境では環境変数で自動確認
      if [ "$${CI}" = "true" ] && [ "$${CONFIRM_RETENTION_LOCK}" = "yes" ]; then
        gcloud storage buckets lock-retention-policy gs://${google_storage_bucket.compliance_bucket.name} --quiet
      else
        read -p "Type 'yes' to confirm: " confirm
        if [ "$confirm" = "yes" ]; then
          gcloud storage buckets lock-retention-policy gs://${google_storage_bucket.compliance_bucket.name}
        else
          echo "Lock operation cancelled"
          exit 1
        fi
      fi
    EOT

    interpreter = ["bash", "-c"]
  }

  # ロック状態の変更を検知するためのトリガー
  triggers = {
    bucket_id = google_storage_bucket.compliance_bucket.id
    retention_period = google_storage_bucket.compliance_bucket.retention_policy[0].retention_period
  }
}

# オプション2: Terraform変数による制御(本番環境向け)
variable "enable_retention_lock" {
  description = "Enable retention policy lock (IRREVERSIBLE)"
  type        = bool
  default     = false
}

resource "null_resource" "lock_retention_policy_controlled" {
  count = var.enable_retention_lock ? 1 : 0
  depends_on = [google_storage_bucket.compliance_bucket]

  provisioner "local-exec" {
    command = "gcloud storage buckets lock-retention-policy gs://${google_storage_bucket.compliance_bucket.name} --quiet"
  }
}

# -------------------- ⑥ アラート設定 --------------------
resource "google_monitoring_alert_policy" "retention_policy_alert" {
  display_name = "Retention Policy Change Alert"
  combiner     = "OR"

  conditions {
    display_name = "Retention policy modified"

    condition_matched_log {
      filter = <<-EOT
        resource.type="gcs_bucket"
        AND (
          protoPayload.methodName="storage.buckets.update"
          OR protoPayload.methodName="storage.buckets.lockRetentionPolicy"
        )
        AND protoPayload.resourceName=~"${google_storage_bucket.compliance_bucket.name}"
        AND (
          protoPayload.request.retentionPolicy.retentionPeriod
          OR protoPayload.request.retentionPolicy.is_locked
        )
      EOT
    }
  }

  notification_channels = [google_monitoring_notification_channel.email.name]

  alert_strategy {
    auto_close = "1800s"
  }
}

resource "google_monitoring_notification_channel" "email" {
  display_name = "Compliance Team Email"
  type         = "email"

  labels = {
    email_address = "compliance-team@example.com"
  }
}

 

主要な修正ポイント

  1. retention_period: 適切な保持期間を秒単位で設定(規制要件に応じて調整)
  2. force_destroy = false: バケットの誤削除を防止
  3. null_resource: Terraformが直接サポートしていないロック操作を実行
  4. 監査とアラート: 保持ポリシーの変更を検知して通知
  5. uniform_bucket_level_access = true: ACLではなくIAMによる統一的なアクセス制御
  6. versioning: オブジェクトの履歴管理(保持ポリシーと併用推奨)

最後に

この記事では、Cloud Storageバケットのオブジェクト保持ポリシーをロックする設定手順について、リスクと対策を解説しました。

保持ポリシーのロックは、コンプライアンス要件を満たし、重要なデータを保護するための強力な機能です。ただし、不可逆的な操作であるため、実施前に十分な検討と計画が必要です。

この問題の検出は弊社が提供するSecurifyのCSPM機能で簡単に検出及び管理する事が可能です。 運用が非常に楽に出来る製品になっていますので、ぜひ興味がある方はお問い合わせお待ちしております。 最後までお読みいただきありがとうございました。この記事が皆さんの役に立てば幸いです。

参考情報

この記事をシェアする

クラウドセキュリティ対策実践集一覧へ戻る

貴社の利用状況に合わせた見積もりを作成します。

料金プランを詳しく見る