Google Cloud VPCフローログの有効化について

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

ポリシーの説明
VPCフローログは、VPCネットワーク内のネットワークインターフェースとの間で送受信されるIPトラフィックに関する情報を記録する機能です。この機能により、ネットワークトラフィックの可視性が向上し、セキュリティ分析、ネットワーク監視、コンプライアンス監査、パフォーマンストラブルシューティングが可能になります。デフォルトでは無効になっているため、明示的に有効化する必要があります。
コスト面を考慮に入れつつも本番環境で動作させる際には有効化するようにしましょう。
修復方法
コンソールでの修復手順
Google Cloud コンソールを使用して、VPCフローログを有効化します。
ステップ1: VPCネットワークの確認
- Google Cloud コンソールにログインします
- ナビゲーションメニューから「VPCネットワーク」を選択します

- フローログを有効化したいVPCネットワークをクリックします

ステップ2: サブネットレベルでのフローログ有効化
- VPCネットワークの詳細画面で「サブネット」タブをクリックします
- フローログを有効化したいサブネットを選択します
- 「編集」ボタンをクリックします

- 「フローログ」セクションで以下を設定します:
- フローログ: オン
- 集約間隔: 5秒(推奨)
- サンプルレート: 1.0(100%、セキュリティ監視用)
- 「保存」をクリックします
ステップ3: ログの保存先設定
- ナビゲーションメニューから「ロギング」→「ログルーター」を選択します
- 「シンクを作成」をクリックします

- 以下の設定を入力します:
- シンク名:
vpc-flow-logs-sink
- シンクの宛先: Cloud Storage(長期保存)またはBigQuery(分析用)を選択
- シンク名:
- フィルタに以下を入力:
resource.type="gce_subnetwork" logName="projects/PROJECT_ID/logs/compute.googleapis.com%2Fvpc_flows"
- 「作成」をクリックします
必要に応じてアラートを設定するなどして監視するようにしましょう。
Terraformでの修復手順
VPCフローログを有効化し、包括的な監視体制を構築するTerraformコードと、主要な修正ポイントを説明します。
# プロバイダー設定
provider "google" {
project = var.project_id
region = var.region
}
# VPCネットワーク
resource "google_compute_network" "vpc_network" {
name = "${var.environment}-vpc"
auto_create_subnetworks = false
description = "VPC network with flow logs enabled"
}
# サブネット(フローログ有効)
resource "google_compute_subnetwork" "subnet_with_flow_logs" {
name = "${var.environment}-subnet-${var.region}"
ip_cidr_range = var.subnet_cidr
region = var.region
network = google_compute_network.vpc_network.id
# プライベートGoogleアクセス
private_ip_google_access = true
# フローログの設定(重要)
log_config {
aggregation_interval = "INTERVAL_5_SEC" # 集約間隔(5秒)
flow_sampling = 1.0 # サンプリングレート(100%)
metadata = "INCLUDE_ALL_METADATA" # すべてのメタデータを含める
# フローログに含まれる標準フィールド:
# - connection.src_ip: 送信元IPアドレス
# - connection.dest_ip: 宛先IPアドレス
# - connection.src_port: 送信元ポート
# - connection.dest_port: 宛先ポート
# - connection.protocol: プロトコル番号
# - bytes_sent: 送信バイト数
# - packets_sent: 送信パケット数
# - start_time/end_time: フロー開始/終了時刻
}
# セカンダリIP範囲(GKE等で使用)
secondary_ip_range {
range_name = "pods"
ip_cidr_range = var.pods_cidr
}
secondary_ip_range {
range_name = "services"
ip_cidr_range = var.services_cidr
}
}
# Cloud Storageバケット(フローログ長期保存用)
resource "google_storage_bucket" "flow_logs_bucket" {
name = "${var.project_id}-vpc-flow-logs-${var.environment}"
location = var.region
force_destroy = false
uniform_bucket_level_access = true
# ライフサイクルルール
lifecycle_rule {
condition {
age = 90 # 90日後にColdlineへ移行
}
action {
type = "SetStorageClass"
storage_class = "COLDLINE"
}
}
lifecycle_rule {
condition {
age = 365 # 1年後に削除
}
action {
type = "Delete"
}
}
# バージョニング
versioning {
enabled = true
}
# 暗号化
encryption {
default_kms_key_name = google_kms_crypto_key.flow_logs_key.id
}
}
# KMS暗号化キー
resource "google_kms_key_ring" "flow_logs_keyring" {
name = "${var.environment}-flow-logs-keyring"
location = var.region
}
resource "google_kms_crypto_key" "flow_logs_key" {
name = "${var.environment}-flow-logs-key"
key_ring = google_kms_key_ring.flow_logs_keyring.id
rotation_period = "7776000s" # 90日
lifecycle {
prevent_destroy = true
}
}
# BigQueryデータセット(フローログ分析用)
resource "google_bigquery_dataset" "flow_logs_dataset" {
dataset_id = "${var.environment}_vpc_flow_logs"
friendly_name = "VPC Flow Logs Dataset"
description = "Dataset for analyzing VPC flow logs"
location = var.region
default_table_expiration_ms = 2592000000 # 30日
# アクセス制御
access {
role = "OWNER"
user_by_email = var.admin_email
}
access {
role = "READER"
group_by_email = var.security_team_email
}
# 暗号化
default_encryption_configuration {
kms_key_name = google_kms_crypto_key.flow_logs_key.id
}
}
# ログシンク(フローログ → Cloud Storage)
resource "google_logging_project_sink" "flow_logs_storage_sink" {
name = "${var.environment}-flow-logs-storage-sink"
destination = "storage.googleapis.com/${google_storage_bucket.flow_logs_bucket.name}"
filter = <<-EOT
resource.type="gce_subnetwork"
logName="projects/${var.project_id}/logs/compute.googleapis.com%2Fvpc_flows"
EOT
unique_writer_identity = true
}
# ログシンク(フローログ → BigQuery)
resource "google_logging_project_sink" "flow_logs_bigquery_sink" {
name = "${var.environment}-flow-logs-bigquery-sink"
destination = "bigquery.googleapis.com/projects/${var.project_id}/datasets/${google_bigquery_dataset.flow_logs_dataset.dataset_id}"
filter = <<-EOT
resource.type="gce_subnetwork"
logName="projects/${var.project_id}/logs/compute.googleapis.com%2Fvpc_flows"
EOT
unique_writer_identity = true
bigquery_options {
use_partitioned_tables = true
}
}
# IAMバインディング
resource "google_storage_bucket_iam_member" "flow_logs_writer" {
bucket = google_storage_bucket.flow_logs_bucket.name
role = "roles/storage.objectCreator"
member = google_logging_project_sink.flow_logs_storage_sink.writer_identity
}
resource "google_bigquery_dataset_iam_member" "flow_logs_editor" {
dataset_id = google_bigquery_dataset.flow_logs_dataset.dataset_id
role = "roles/bigquery.dataEditor"
member = google_logging_project_sink.flow_logs_bigquery_sink.writer_identity
}
# BigQueryビュー(セキュリティ分析用)
resource "google_bigquery_table" "security_analysis_view" {
dataset_id = google_bigquery_dataset.flow_logs_dataset.dataset_id
table_id = "security_analysis_view"
view {
query = <<-EOT
WITH flow_summary AS (
SELECT
jsonPayload.connection.src_ip as src_ip,
jsonPayload.connection.dest_ip as dest_ip,
jsonPayload.connection.src_port as src_port,
jsonPayload.connection.dest_port as dest_port,
jsonPayload.connection.protocol as protocol,
jsonPayload.bytes_sent as bytes_sent,
jsonPayload.packets_sent as packets_sent,
jsonPayload.start_time as start_time,
jsonPayload.end_time as end_time,
resource.labels.subnetwork_name as subnet,
CASE
WHEN jsonPayload.connection.dest_port IN (22, 3389, 445, 135, 139) THEN 'HIGH_RISK_PORT'
WHEN jsonPayload.bytes_sent > 1073741824 THEN 'LARGE_TRANSFER' -- 1GB
WHEN jsonPayload.connection.protocol = 6 AND jsonPayload.packets_sent > 10000 THEN 'POSSIBLE_SCAN'
ELSE 'NORMAL'
END as risk_category
FROM `${var.project_id}.${var.environment}_vpc_flow_logs.compute_googleapis_com_vpc_flows_*`
WHERE _TABLE_SUFFIX = FORMAT_DATE('%Y%m%d', CURRENT_DATE())
)
SELECT
*,
COUNT(*) OVER (
PARTITION BY src_ip, dest_port
ORDER BY TIMESTAMP(start_time)
RANGE BETWEEN INTERVAL 1 HOUR PRECEDING AND CURRENT ROW
) as connections_per_hour
FROM flow_summary
WHERE risk_category != 'NORMAL'
ORDER BY start_time DESC
EOT
use_legacy_sql = false
}
}
# アラートポリシー(異常トラフィック検出)
resource "google_monitoring_alert_policy" "abnormal_traffic" {
display_name = "${var.environment} Abnormal VPC Traffic"
combiner = "OR"
conditions {
display_name = "High volume data transfer detected"
condition_logs_based {
filter = <<-EOT
resource.type="gce_subnetwork"
logName="projects/${var.project_id}/logs/compute.googleapis.com%2Fvpc_flows"
jsonPayload.bytes_sent > 10737418240 -- 10GB
EOT
aggregations {
alignment_period = "300s"
per_series_aligner = "ALIGN_RATE"
cross_series_reducer = "REDUCE_SUM"
group_by_fields = ["jsonPayload.connection.src_ip"]
}
comparison = "COMPARISON_GT"
threshold_value = 1
duration = "60s"
}
}
conditions {
display_name = "Port scanning activity detected"
condition_logs_based {
filter = <<-EOT
resource.type="gce_subnetwork"
logName="projects/${var.project_id}/logs/compute.googleapis.com%2Fvpc_flows"
jsonPayload.connection.dest_port > 0
EOT
aggregations {
alignment_period = "60s"
per_series_aligner = "ALIGN_COUNT_TRUE"
cross_series_reducer = "REDUCE_COUNT_TRUE"
group_by_fields = ["jsonPayload.connection.src_ip"]
}
comparison = "COMPARISON_GT"
threshold_value = 100 # 1分間に100以上の異なるポートへの接続
duration = "60s"
}
}
notification_channels = [google_monitoring_notification_channel.security_team.id]
alert_strategy {
auto_close = "1800s"
}
}
# 通知チャンネル
resource "google_monitoring_notification_channel" "security_team" {
display_name = "Security Team"
type = "email"
labels = {
email_address = var.security_team_email
}
}
# Pub/Subトピック(リアルタイム処理用)
resource "google_pubsub_topic" "flow_logs_topic" {
name = "${var.environment}-flow-logs-topic"
message_retention_duration = "86400s" # 1日
}
# ログシンク(フローログ → Pub/Sub)
resource "google_logging_project_sink" "flow_logs_pubsub_sink" {
name = "${var.environment}-flow-logs-pubsub-sink"
destination = "pubsub.googleapis.com/${google_pubsub_topic.flow_logs_topic.id}"
filter = <<-EOT
resource.type="gce_subnetwork"
logName="projects/${var.project_id}/logs/compute.googleapis.com%2Fvpc_flows"
(jsonPayload.connection.dest_port=22 OR
jsonPayload.connection.dest_port=3389 OR
jsonPayload.bytes_sent > 1073741824)
EOT
unique_writer_identity = true
}
# 変数定義
variable "project_id" {
description = "GCP Project ID"
type = string
}
variable "environment" {
description = "Environment name (prod, staging, dev)"
type = string
}
variable "region" {
description = "GCP region"
type = string
default = "asia-northeast1"
}
variable "subnet_cidr" {
description = "CIDR range for the subnet"
type = string
default = "10.0.0.0/24"
}
variable "pods_cidr" {
description = "CIDR range for GKE pods"
type = string
default = "10.1.0.0/16"
}
variable "services_cidr" {
description = "CIDR range for GKE services"
type = string
default = "10.2.0.0/16"
}
variable "admin_email" {
description = "Admin email for dataset access"
type = string
}
variable "security_team_email" {
description = "Security team email for alerts"
type = string
}
# 出力値
output "flow_logs_bucket" {
value = google_storage_bucket.flow_logs_bucket.url
description = "Cloud Storage bucket for flow logs"
}
output "bigquery_dataset" {
value = google_bigquery_dataset.flow_logs_dataset.id
description = "BigQuery dataset for flow log analysis"
}
output "pubsub_topic" {
value = google_pubsub_topic.flow_logs_topic.id
description = "Pub/Sub topic for real-time flow log processing"
}
最後に
この記事では、Google Cloud VPCフローログを有効化してネットワークトラフィックを監視する設定手順について、リスクと対策を解説しました。
VPCフローログを有効化することで、ネットワークレベルでの包括的な可視性を確保し、セキュリティ脅威の早期検出、コンプライアンス要件の充足、効率的なトラブルシューティングが可能になります。
この問題の検出は弊社が提供するSecurifyのCSPM機能で簡単に検出及び管理する事が可能です。 運用が非常に楽に出来る製品になっていますので、ぜひ興味がある方はお問い合わせお待ちしております。 最後までお読みいただきありがとうございました。この記事が皆さんの役に立てば幸いです。