GCLB でのロギングの設定について

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

この記事では、GCP HTTP(S)ロードバランサでロギングを有効化する設定手順について、リスクと対策を解説します。

ポリシーの説明

HTTP(S)ロードバランサのロギング機能は、ロードバランサを通過するすべてのHTTP(S)リクエストの詳細情報を記録します。これには、リクエスト元のIPアドレス、リクエストされたURL、レスポンスコード、レイテンシーなどの重要な情報が含まれます。これらのログは、セキュリティ分析、パフォーマンス監視、トラブルシューティングに不可欠です。

修復方法

コンソールでの修復手順

Google Cloud コンソールを使用して、HTTP(S)ロードバランサでロギングを有効にします。

  1. Google Cloud Console にログインします
  2. ネットワーク サービス > 負荷分散 に移動します
  3. ロギングを有効にしたい HTTP(S)ロードバランサ をクリックします
  4. 編集 ボタンをクリックします
  5. バックエンドの構成 セクションで、各バックエンドサービスについて:
    • バックエンドサービス名をクリック
    • ロギング セクションまでスクロール
    • ロギングを有効にする のチェックボックスをオン
    • サンプリング レート を設定(1.0 = 100%、すべてのリクエストをログに記録)
  6. フロントエンドの構成 に戻り、設定を確認します
  7. 更新 をクリックして変更を保存します
  8. ログが正しく記録されていることを確認:
    • ロギング > ログ エクスプローラー に移動
    • リソースタイプで「HTTP Load Balancer」を選択
    • ログエントリが表示されることを確認

Terraformでの修復手順

HTTP(S)ロードバランサでロギングを有効にするTerraformコードと、主要な修正ポイントを説明します。

# =====================================================
# HTTP(S)ロードバランサの設定(ロギング有効)
# =====================================================

# ヘルスチェックの定義
resource "google_compute_health_check" "http_health_check" {
  name                = "${var.app_name}-health-check"
  check_interval_sec  = 10
  timeout_sec         = 5
  healthy_threshold   = 2
  unhealthy_threshold = 3

  http_health_check {
    request_path = "/health"
    port         = 80
  }
}

# バックエンドサービスの作成(ロギング有効)
resource "google_compute_backend_service" "app_backend" {
  name                  = "${var.app_name}-backend-service"
  protocol              = "HTTP"
  port_name             = "http"
  timeout_sec           = 30
  health_checks         = [google_compute_health_check.http_health_check.id]

  # =====================================================
  # ロギングの有効化設定
  # =====================================================
  log_config {
    enable      = true
    sample_rate = var.log_sample_rate  # 1.0 = 100%のリクエストをログ記録
  }

  # CDN設定(オプション)
  cdn_policy {
    cache_mode = "USE_ORIGIN_HEADERS"

    # キャッシュキーポリシー
    cache_key_policy {
      include_host         = true
      include_protocol     = true
      include_query_string = true
    }
  }

  # バックエンドインスタンスグループ
  backend {
    group           = google_compute_instance_group_manager.app_mig.instance_group
    balancing_mode  = "UTILIZATION"
    capacity_scaler = 1.0
    max_utilization = 0.8
  }

  # セキュリティポリシー(Cloud Armor)の適用
  security_policy = google_compute_security_policy.app_security_policy.id
}

# =====================================================
# Cloud Armorセキュリティポリシー(ロギング付き)
# =====================================================

resource "google_compute_security_policy" "app_security_policy" {
  name = "${var.app_name}-security-policy"

  # デフォルトルール
  rule {
    action   = "allow"
    priority = "2147483647"

    match {
      versioned_expr = "SRC_IPS_V1"
      config {
        src_ip_ranges = ["*"]
      }
    }

    description = "Default allow all rule"
  }

  # DDoS保護ルール
  rule {
    action   = "rate_based_ban"
    priority = "1000"

    match {
      versioned_expr = "SRC_IPS_V1"
      config {
        src_ip_ranges = ["*"]
      }
    }

    rate_limit_options {
      conform_action = "allow"
      exceed_action  = "deny(429)"

      rate_limit_threshold {
        count        = 100
        interval_sec = 60
      }

      ban_duration_sec = 600  # 10分間のBAN
    }

    description = "Rate limiting rule"
  }

  # 地理的制限(オプション)
  dynamic "rule" {
    for_each = length(var.blocked_countries) > 0 ? [1] : []

    content {
      action   = "deny(403)"
      priority = "500"

      match {
        expr {
          expression = join(" || ", [
            for country in var.blocked_countries :
            "origin.region_code == '${country}'"
          ])
        }
      }

      description = "Geo-blocking rule"
    }
  }

  # ログ設定(Cloud Armorのログも有効化)
  adaptive_protection_config {
    layer_7_ddos_defense_config {
      enable = true
    }
  }
}

# =====================================================
# URLマップとHTTP(S)プロキシ
# =====================================================

resource "google_compute_url_map" "app_url_map" {
  name            = "${var.app_name}-url-map"
  default_service = google_compute_backend_service.app_backend.id

  # パスベースのルーティング(オプション)
  host_rule {
    hosts        = [var.domain_name]
    path_matcher = "app-paths"
  }

  path_matcher {
    name            = "app-paths"
    default_service = google_compute_backend_service.app_backend.id

    path_rule {
      paths   = ["/api/*"]
      service = google_compute_backend_service.app_backend.id
    }
  }
}

# HTTPSプロキシ
resource "google_compute_target_https_proxy" "app_https_proxy" {
  name             = "${var.app_name}-https-proxy"
  url_map          = google_compute_url_map.app_url_map.id
  ssl_certificates = [google_compute_managed_ssl_certificate.app_cert.id]
}

# HTTPプロキシ(HTTPSへのリダイレクト用)
resource "google_compute_target_http_proxy" "app_http_proxy" {
  name    = "${var.app_name}-http-proxy"
  url_map = google_compute_url_map.http_redirect.id
}

# HTTPからHTTPSへのリダイレクト
resource "google_compute_url_map" "http_redirect" {
  name = "${var.app_name}-http-redirect"

  default_url_redirect {
    https_redirect = true
    strip_query    = false
  }
}

# =====================================================
# グローバル転送ルール
# =====================================================

# HTTPS転送ルール
resource "google_compute_global_forwarding_rule" "app_https" {
  name                  = "${var.app_name}-https-forwarding-rule"
  ip_protocol          = "TCP"
  port_range           = "443"
  target               = google_compute_target_https_proxy.app_https_proxy.id
  ip_address           = google_compute_global_address.app_ip.id
  load_balancing_scheme = "EXTERNAL_MANAGED"
}

# HTTP転送ルール
resource "google_compute_global_forwarding_rule" "app_http" {
  name                  = "${var.app_name}-http-forwarding-rule"
  ip_protocol          = "TCP"
  port_range           = "80"
  target               = google_compute_target_http_proxy.app_http_proxy.id
  ip_address           = google_compute_global_address.app_ip.id
  load_balancing_scheme = "EXTERNAL_MANAGED"
}

# =====================================================
# ログの保存と分析設定
# =====================================================

# BigQueryデータセット(ログ保存用)
resource "google_bigquery_dataset" "lb_logs" {
  dataset_id                  = "${var.app_name}_lb_logs"
  project                     = var.project_id
  location                    = var.bigquery_location
  default_table_expiration_ms = 7776000000  # 90日

  access {
    role          = "OWNER"
    user_by_email = google_service_account.log_analyzer.email
  }

  access {
    role          = "READER"
    group_by_email = var.security_team_group
  }
}

# ログシンク(ロードバランサログをBigQueryへ)
resource "google_logging_project_sink" "lb_logs_sink" {
  name        = "${var.app_name}-lb-logs-sink"
  destination = "bigquery.googleapis.com/projects/${var.project_id}/datasets/${google_bigquery_dataset.lb_logs.dataset_id}"

  # HTTP(S)ロードバランサのログのみをフィルタリング
  filter = <<-EOT
    resource.type="http_load_balancer"
    resource.labels.url_map_name="${google_compute_url_map.app_url_map.name}"
  EOT

  unique_writer_identity = true

  bigquery_options {
    use_partitioned_tables = true
  }
}

# ログ分析用サービスアカウント
resource "google_service_account" "log_analyzer" {
  account_id   = "${var.app_name}-log-analyzer"
  display_name = "Load Balancer Log Analyzer"
  project      = var.project_id
}

# BigQueryへの権限付与
resource "google_project_iam_member" "log_sink_writer" {
  project = var.project_id
  role    = "roles/bigquery.dataEditor"
  member  = google_logging_project_sink.lb_logs_sink.writer_identity
}

# =====================================================
# 監視とアラート設定
# =====================================================

# アラートポリシー(4xxエラー率)
resource "google_monitoring_alert_policy" "high_4xx_rate" {
  display_name = "${var.app_name} - High 4xx Error Rate"
  project      = var.project_id
  combiner     = "OR"

  conditions {
    display_name = "4xx error rate > 10%"

    condition_threshold {
      filter = <<-EOT
        resource.type = "https_lb_rule"
        resource.label.url_map_name = "${google_compute_url_map.app_url_map.name}"
        metric.type = "loadbalancing.googleapis.com/https/request_count"
        metric.label.response_code_class = "400"
      EOT

      comparison      = "COMPARISON_GT"
      threshold_value = 0.1
      duration        = "300s"

      aggregations {
        alignment_period     = "60s"
        per_series_aligner   = "ALIGN_RATE"
        cross_series_reducer = "REDUCE_SUM"
        group_by_fields      = ["resource.label.backend_service_name"]
      }
    }
  }

  notification_channels = [google_monitoring_notification_channel.ops_team.name]
}

# 通知チャンネル
resource "google_monitoring_notification_channel" "ops_team" {
  display_name = "Operations Team"
  type         = "email"
  project      = var.project_id

  labels = {
    email_address = var.ops_team_email
  }
}

# =====================================================
# 変数定義
# =====================================================

variable "app_name" {
  description = "アプリケーション名"
  type        = string
}

variable "project_id" {
  description = "GCPプロジェクトID"
  type        = string
}

variable "domain_name" {
  description = "アプリケーションのドメイン名"
  type        = string
}

variable "log_sample_rate" {
  description = "ログのサンプリングレート(0.0-1.0)"
  type        = number
  default     = 1.0  # 100%のリクエストをログ記録
}

variable "bigquery_location" {
  description = "BigQueryデータセットのロケーション"
  type        = string
  default     = "asia-northeast1"
}

variable "blocked_countries" {
  description = "ブロックする国コードのリスト"
  type        = list(string)
  default     = []
}

variable "security_team_group" {
  description = "セキュリティチームのグループメール"
  type        = string
}

variable "ops_team_email" {
  description = "運用チームのメールアドレス"
  type        = string
}

# =====================================================
# 出力
# =====================================================

output "load_balancer_ip" {
  value = google_compute_global_address.app_ip.address
}

output "bigquery_dataset_id" {
  value = google_bigquery_dataset.lb_logs.dataset_id
}

 

最後に

この記事では、GCP HTTP(S)ロードバランサでロギングを有効化する設定手順について、リスクと対策を解説しました。

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

参考情報

この記事をシェアする

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

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

料金プランを詳しく見る