Cloud Storageバケットの均一なバケットレベルアクセス設定手順

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

この記事では、Cloud Storageバケットで均一なバケットレベルアクセス(Uniform bucket-level access)が無効化されている問題について、リスクと対策を解説します。

ポリシーの説明

Cloud Storageバケットで均一なバケットレベルのアクセス制御を有効化し、一貫した権限管理が適用されている状態を確保します。均一なバケットレベルアクセス(Uniform bucket-level access、以下UBA)は、Google Cloud StorageでIAMのみを使用してアクセス制御を管理する機能です。UBAを有効にすると、オブジェクトレベルのACL(Access Control List)が無効化され、すべてのアクセス制御がバケットレベルのIAMポリシーで一元管理されます。

修復方法

コンソールでの修復手順

Google Cloud コンソールを使用して、Cloud Storageバケットで均一なバケットレベルアクセスを有効化します。

ステップ1: Cloud Storageバケットへのアクセス

  1. Google Cloud Consoleにログイン
  2. ナビゲーションメニューから「Cloud Storage」→「バケット」を選択
  3. 設定を変更したいバケット名をクリック

ステップ2: 権限設定の確認

  1. バケットの詳細ページで「権限」タブをクリック
  2. 現在のアクセス制御モードを確認
    • 「きめ細かい」(Fine-grained)と表示されている場合は、ACLが有効な状態
    • 「均一」(Uniform)と表示されている場合は、既にUBAが有効
  3. 既存のオブジェクトACL設定を確認(必要に応じてエクスポート)

ステップ3: 均一なバケットレベルアクセスの有効化

  1. 「アクセス制御」セクションで「切り替え」または「均一に切り替える」をクリック
  2. 警告ダイアログが表示される:
    • 既存のオブジェクトACLが削除されることの確認
    • 90日間の取消し可能期間についての説明
  3. 「均一なアクセス制御を有効にする」にチェック
  4. 「保存」をクリック

ステップ4: 移行後の確認事項

  1. 既存のアクセス権限の確認:
    • 「IAM」タブですべての必要な権限が設定されているか確認
    • 必要に応じて、失われたACL権限をIAMロールで再設定
    • 特にallUsersallAuthenticatedUsersへの権限付与がないことを確認
  2. アプリケーションの動作確認:
    • ACL APIに依存していたアプリケーションの動作を確認
    • 必要に応じてアプリケーションコードをIAM APIを使用するよう更新
    • 署名付きURL(Signed URLs)の動作確認

ステップ5: 90日間の取消し期間の管理

  1. UBA有効化後90日間は元のACL設定に戻すことが可能
  2. 「権限」タブで残り日数を確認可能
  3. 十分な検証後、恒久的に適用する場合は「今すぐロック」をクリック
  4. ロック後はACLへの復帰は不可能となるため、慎重に判断

Terraformでの修復手順

Cloud Storageバケットで均一なバケットレベルアクセスを有効化するTerraformコードと、主要な修正ポイントを説明します。

# データソース
data "google_project" "current" {}

# 均一なバケットレベルアクセスを有効化したバケット
resource "google_storage_bucket" "secure_bucket" {
  name          = "${var.project_id}-secure-bucket-${var.environment}"
  location      = var.region
  storage_class = "STANDARD"

  # 均一なバケットレベルアクセスの有効化(必須設定)
  uniform_bucket_level_access = true

  # バージョニングの有効化(推奨)
  versioning {
    enabled = true
  }

  # ライフサイクルルール(例:90日後に古いバージョンを削除)
  lifecycle_rule {
    condition {
      age                   = 90
      with_state           = "ARCHIVED"
    }
    action {
      type = "Delete"
    }
  }

  # 暗号化設定(KMSを使用)
  encryption {
    default_kms_key_name = google_kms_crypto_key.bucket_key.id
  }

  # パブリックアクセス防止(UBAと併用推奨)
  public_access_prevention = "enforced"

  # ラベル
  labels = {
    environment = var.environment
    team        = var.team
    compliance  = "uba-enabled"
  }
}

# KMS暗号鍵
resource "google_kms_key_ring" "bucket_keyring" {
  name     = "${var.project_id}-bucket-keyring"
  location = var.region
}

resource "google_kms_crypto_key" "bucket_key" {
  name            = "${var.project_id}-bucket-key"
  key_ring        = google_kms_key_ring.bucket_keyring.id
  rotation_period = "7776000s" # 90日
}

# バケットレベルのIAMバインディング
# 注意: iam_bindingは既存の権限を上書きするため、iam_memberの使用も検討
# 読み取り専用アクセス
resource "google_storage_bucket_iam_binding" "viewers" {
  bucket = google_storage_bucket.secure_bucket.name
  role   = "roles/storage.objectViewer"

  members = [
    "group:${var.viewer_group}@${var.organization_domain}",
  ]

  # 条件付きアクセス(オプション)
  # condition {
  #   title       = "Weekday access only"
  #   description = "Access allowed only on weekdays"
  #   expression  = "request.time.getDayOfWeek() >= 1 && request.time.getDayOfWeek() <= 5"
  # }
}

# 書き込みアクセス
resource "google_storage_bucket_iam_binding" "editors" {
  bucket = google_storage_bucket.secure_bucket.name
  role   = "roles/storage.objectUser"

  members = [
    "group:${var.editor_group}@${var.organization_domain}",
    "serviceAccount:${google_service_account.app_sa.email}",
  ]
}

# 管理者アクセス
resource "google_storage_bucket_iam_binding" "admins" {
  bucket = google_storage_bucket.secure_bucket.name
  role   = "roles/storage.admin"

  members = [
    "group:${var.admin_group}@${var.organization_domain}",
  ]
}

# アプリケーション用サービスアカウント
resource "google_service_account" "app_sa" {
  account_id   = "${var.project_id}-app-sa"
  display_name = "Application Service Account"
  description  = "Service account for application access to storage bucket"
}

# 既存バケットの移行用リソース(既存バケットがある場合)
resource "null_resource" "migrate_existing_bucket" {
  count = var.migrate_existing ? 1 : 0

  provisioner "local-exec" {
    command = <<-EOT
      # 既存のACL設定をバックアップ(推奨)
      gsutil acl get gs://${var.existing_bucket_name} > bucket_acl_backup.json

      # 既存バケットのUBA有効化
      gcloud storage buckets update gs://${var.existing_bucket_name} \
        --uniform-bucket-level-access

      # 移行後のIAMポリシー適用
      gcloud storage buckets add-iam-policy-binding gs://${var.existing_bucket_name} \
        --member="group:${var.viewer_group}@${var.organization_domain}" \
        --role="roles/storage.objectViewer"
    EOT
  }
}

# 監視アラート(UBAが無効化された場合)
resource "google_monitoring_alert_policy" "uba_disabled" {
  display_name = "Uniform Bucket-Level Access Disabled"
  combiner     = "OR"
  enabled      = true

  conditions {
    display_name = "UBA disabled on bucket"

    condition_threshold {
      filter = <<-EOT
        resource.type="gcs_bucket"
        AND protoPayload.methodName="storage.buckets.update"
        AND protoPayload.request.bucket.iamConfiguration.uniformBucketLevelAccess.enabled=false
      EOT

      duration        = "0s"
      comparison      = "COMPARISON_GT"
      threshold_value = 0
    }
  }

  notification_channels = [google_monitoring_notification_channel.security_team.id]

  documentation {
    content = <<-EOT
      ## 均一なバケットレベルアクセスが無効化されました

      対象バケットでUBAが無効化され、オブジェクトACLが有効になりました。

      ### 対応手順
      1. Cloud Loggingで変更の詳細を確認
      2. 変更が承認されたものか確認
      3. 意図しない変更の場合は、即座にUBAを再有効化
      4. セキュリティチームに報告
    EOT

    mime_type = "text/markdown"
  }
}

# 通知チャネル
resource "google_monitoring_notification_channel" "security_team" {
  display_name = "Security Team Email"
  type         = "email"

  labels = {
    email_address = var.security_team_email
  }
}

# 変数定義
variable "project_id" {
  description = "GCP Project ID"
  type        = string
}

variable "environment" {
  description = "Environment name"
  type        = string
}

variable "region" {
  description = "GCP region"
  type        = string
  default     = "asia-northeast1"
}

variable "team" {
  description = "Team name for labeling"
  type        = string
}

variable "organization_domain" {
  description = "Organization domain"
  type        = string
}

variable "viewer_group" {
  description = "Google group for read-only access"
  type        = string
}

variable "editor_group" {
  description = "Google group for read-write access"
  type        = string
}

variable "admin_group" {
  description = "Google group for admin access"
  type        = string
}

variable "security_team_email" {
  description = "Security team email for alerts"
  type        = string
}

variable "migrate_existing" {
  description = "Whether to migrate existing bucket"
  type        = bool
  default     = false
}

variable "existing_bucket_name" {
  description = "Name of existing bucket to migrate"
  type        = string
  default     = ""
}

# 出力
output "bucket_name" {
  value       = google_storage_bucket.secure_bucket.name
  description = "Name of the created bucket"
}

output "bucket_url" {
  value       = google_storage_bucket.secure_bucket.url
  description = "URL of the created bucket"
}

output "uba_enabled" {
  value       = google_storage_bucket.secure_bucket.uniform_bucket_level_access
  description = "Status of uniform bucket-level access"
}

 

まとめ

この記事では、Cloud Storageバケットで均一なバケットレベルアクセス(Uniform bucket-level access)の有効化について、リスクと対策を解説しました。

均一なバケットレベルアクセスを有効化することで、IAMベースの一元的なアクセス制御を実現し、セキュリティと運用効率を大幅に向上させることができます。ACLからの移行は90日間の猶予期間があるため、慎重に計画して実施することが重要です。

この問題の検出は弊社が提供するSecurifyのCSPM機能で簡単に検出及び管理する事が可能です。 運用が非常に楽に出来る製品になっていますので、ぜひ興味がある方はお問い合わせお待ちしております。

最後までお読みいただきありがとうございました。この記事が皆さんの役に立てば幸いです。

参考情報

 

この記事をシェアする

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

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

料金プランを詳しく見る