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

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

ポリシーの説明
HTTP(S)ロードバランサのロギング機能は、ロードバランサを通過するすべてのHTTP(S)リクエストの詳細情報を記録します。これには、リクエスト元のIPアドレス、リクエストされたURL、レスポンスコード、レイテンシーなどの重要な情報が含まれます。これらのログは、セキュリティ分析、パフォーマンス監視、トラブルシューティングに不可欠です。
修復方法
コンソールでの修復手順
Google Cloud コンソールを使用して、HTTP(S)ロードバランサでロギングを有効にします。
- Google Cloud Console にログインします
- ネットワーク サービス > 負荷分散 に移動します
- ロギングを有効にしたい HTTP(S)ロードバランサ をクリックします
- 編集 ボタンをクリックします
- バックエンドの構成 セクションで、各バックエンドサービスについて:
- バックエンドサービス名をクリック
- ロギング セクションまでスクロール
- ロギングを有効にする のチェックボックスをオン
- サンプリング レート を設定(1.0 = 100%、すべてのリクエストをログに記録)
- フロントエンドの構成 に戻り、設定を確認します
- 更新 をクリックして変更を保存します
- ログが正しく記録されていることを確認:
- ロギング > ログ エクスプローラー に移動
- リソースタイプで「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機能で簡単に検出及び管理する事が可能です。 運用が非常に楽に出来る製品になっていますので、ぜひ興味がある方はお問い合わせお待ちしております。 最後までお読みいただきありがとうございました。この記事が皆さんの役に立てば幸いです。