パブリッククラウドの罠を越えろ!GCP「プライベートGoogleアクセス」の裏側とパケットの旅
こんにちは、SREチームのシニアエンジニアです。
クラウドインフラの設計で、皆さんはこんな要件に直面したことがないでしょうか?
「セキュリティ要件の厳格化に伴い、社内システムやマイクロサービスが稼働するCompute Engine(GCE)のVMインスタンスから外部グローバルIPアドレスを完全に剥奪する。しかし、Cloud Storage(GCS)へのオブジェクトアップロードや、BigQueryへのクエリ発行、さらにはGoogle APIsが提供する各種Web APIへのリクエストは、どうしてもインターネットを経由させずにセキュアに疎通させたい」
外部IPを持たない、いわゆる「完全プライベートなVM」から、どうやってGoogleのパブリックAPI群にアクセスすればいいのか。インターネットの荒海に出られない彼らは、一体どこを通ってGCSやBigQueryの門を叩いているのでしょうか?
今回は、GCPのネットワークアーキテクチャにおいて非常に重要な位置を占める「プライベートGoogleアクセス(Private Google Access: PGA)」を取り上げます。DNSの名前解決のカラクリから、VPC内部でパケットがどのようにルーティングされ、Googleのフロントエンドに到達するのか。その裏側の泥臭いメカニズムと、実務で絶対にハマるデバッグの極意を紐解いていきましょう。
—
1. なぜ「プライベートGoogleアクセス」が必要なのか?
まず前提として、GCPのVPCネットワークは完全に仮想化されたプライベート空間です。外部IPを持たないVMインスタンスは、デフォルトの状態ではインターネット(外の世界)と通信できません。Cloud NATを導入すれば外に出られますが、それだと「インターネットゲートウェイ経由でパブリックIPを持つGoogleのサービスにアクセスする」ことになり、セキュリティポリシーやデータ転送コストの観点でベストプラクティスとは言えません。
そこで登場するのがプライベートGoogleアクセス(PGA)です。
PGAを有効にすると、外部IPを持たないVMであっても、Googleが所有する特別なプライベートIPアドレス範囲宛てのパケットをVPC内部から送出できるようになります。これにより、パケットが一度もインターネットに出ることなく、Googleのバックボーンネットワーク内だけでAPIリクエストが完結するのです。
—
2. DNSとルーティングの裏側:パケットはどこへ向かうのか?
「プライベートIPでアクセスする」と言われても、アプリケーション側は通常通り storage.googleapis.com や bigquery.googleapis.com といったパブリックなドメイン名を使ってリクエストを送りますよね。
ここで、ネットワークエンジニアなら誰もが疑問に思うはずです。
*「パブリックなドメインの名前解決をしたら、返ってくるのはグローバルIPアドレスのはず。それをどうやってプライベートIPでルーティングしているんだ?」*
この疑問を解くカギは、GCPの特殊なルーティングマジックとGoogleが保有する専用のIPアドレスブロックにあります。
特殊なルート「199.36.153.8/24」の正体
GCPのVPC内でプライベートGoogleアクセスを有効にすると、Googleの裏側で自動的にシステムルート(System Route)が作成されます。その宛先IPアドレス範囲が 199.36.153.8/24 です。
このIPアドレスレンジは、RFC 1918で定められた私有IPアドレス(10.0.0.0/8など)ではありません。実は、Google自身が所有し、インターネット上ではパブリックにアナウンスしているIPアドレスの一部です。しかし、GCPのVPC内においては、このレンジへのルートの「ネクストホップ」が特殊なデフォルトゲートウェイ(インターネットではなく、Googleのサービスフロントエンドへ直結する仮想ルーター)に設定されます。
パケットが流れるまでの全シーケンス
アプリケーションが storage.googleapis.com にリクエストを投げる瞬間から、パケットが処理されるまでの流れを追ってみましょう。
1. DNSの名前解決:
VM上のアプリケーションが storage.googleapis.com の名前解決を試みます。VMがGCP標準のメタデータサーバ(169.254.169.254)をDNSフルリゾルバとして使っている場合、GoogleのDNSサーバーは、通常のパブリックIPではなく、プライベートGoogleアクセス用の特別なIPアドレス(199.36.153.8/24 のいずれか)を返します。
*(※独自のカスタムDNSサーバーを立てている場合は、後述するCNAMEの設定が必要になります)*
2. パケットの送出とルーティング:
VMのOSは、返された 199.36.153.8/x 宛てのTCPパケット(HTTPS)を作成します。OSのルーティングテーブルに従い、VPCのデフォルトゲートウェイへパケットを投げます。
3. GCP仮想ネットワークによるインターセプト:
VPCのルーターは、宛先が 199.36.153.8/24 であることを検知すると、通常のインターネットへはルーティングせず、Google APIのグローバルなフロントエンド(BFE: Google Front End)へ直接パケットを転送します。
4. 安全な到達:
パケットは一度もパブリックインターネットの光ファイバーやルーターを経由せず、Googleの堅牢なダークファイバー(バックボーン)上を安全に駆け抜け、目的のGCSサービスに到達します。
—
3. 実務での設定方法:TerraformとGCP Console
理屈が分かったところで、実際にインフラを構築するコードを見てみましょう。Terraformを使って、サブネット単位でプライベートGoogleアクセスを有効にする設定例です。
Terraform設定例
# GCP VPCネットワークの定義
resource "google_compute_network" "custom_vpc" {
name = "production-vpc"
auto_create_subnetworks = false
}
# サブネットの定義(ここでプライベートGoogleアクセスを有効化する)
resource "google_compute_subnetwork" "private_subnet" {
name = "app-tier-subnet"
ip_cidr_range = "10.100.0.0/24"
region = "asia-northeast1"
network = google_compute_network.custom_vpc.id
# ★ここが最大のキモ:プライベートGoogleアクセスの有効化
private_ip_google_access = true
}
# 外部IPを持たないアプリケーション用VMインスタンス
resource "google_compute_instance" "app_instance" {
name = "backend-app-vm"
machine_type = "e2-medium"
zone = "asia-northeast1-a"
boot_disk {
initialize_params {
image = "debian-cloud/debian-11"
}
}
network_interface {
subnetwork = google_compute_subnetwork.private_subnet.id
# access_configブロック(外部IP)を意図的に記述しないことで、
# 完全に外部IPを持たないプライベートVMが完成する
}
}
この設定を適用するだけで、app-tier-subnet に所属するすべてのVMは、外部IPを持たなくてもGoogle APIsへのルーティング経路を手に入れることができます。
—
4. アプリケーションコードからの実例と検証Tips
「本当に外部IPなしで動いているのか?」、現場では必ず疑ってかかるのがプロのSREです。デバッグのために、実際にVM内で実行して疎通確認を行うコードやコマンドを見てみましょう。
1. curlによる疎通確認とIPの確認
外部IPを持たないVMにSSH(IAPトンネル経由など)でログインし、curlでGCSのエンドポイントを叩いてみます。
# DNS名前解決の結果を確認する(199.36.153.x のようなIPが返ってくるはず)
nslookup storage.googleapis.com
# 実際にHTTPSリクエストを投げてステータスコードを確認
curl -v https://storage.googleapis.com/
もしここで 199.36.153.x 以外のパブリックIPが返ってきたり、タイムアウトする場合は、サブネットの private_ip_google_access = true が正しく設定されていないか、VMのルーティングが破損しています。
2. Python (requests / google-cloud-storage) での実装例
PythonのGoogle公式クライアントライブラリを使う場合、特別なプロキシ設定を書く必要はありません。ライブラリは内部で標準のGoogle APIエンドポイントを叩くため、PGAが有効なVPC環境であれば、自動的にプライベート経路を通って通信が行われます。
import os
from google.cloud import storage
def upload_file_via_pga(bucket_name, source_file_name, destination_blob_name):
"""
外部IPを持たないGCEインスタンスから、プライベートGoogleアクセス経由で
安全にCloud Storageへファイルをアップロードするサンプル
"""
# 認証情報は環境変数 (GOOGLE_APPLICATION_CREDENTIALS) や
# サービスアカウントのアタッチメントから自動で取得される前提
storage_client = storage.Client()
bucket = storage_client.bucket(bucket_name)
blob = bucket.blob(destination_blob_name)
# アップロード実行(通信はすべてGCPのプライベートバックボーンを経由する)
blob.upload_from_filename(source_file_name)
print(f"ファイル {source_file_name} が、インターネットを経由せずプライベート経由で gs://{bucket_name}/{destination_blob_name} にアップロードされました。")
if __name__ == "__main__":
# 実行例のパラメータ
BUCKET = "my-secure-production-bucket"
SRC_FILE = "/tmp/report.csv"
DEST_BLOB = "reports/report.csv"
# 実行
# upload_file_via_pga(BUCKET, SRC_FILE, DEST_BLOB)
pass
—
5. 現場でよくある「ハマりどころ」とトラブルシューティング
最後に、プライベートGoogleアクセスを導入する現場でSREがよく踏む地雷と、その回避策を共有します。
ハマりどころ 1: オンプレミスや別VPCからのカスタムDNS構成
社内ネットワーク(オンプレミス)からCloud VPNやInterconnect経由でGCPのVPCに接続し、オンプレミス側のサーバーからプライベートGoogleアクセスを利用したい場合、デフォルトのままではうまくいきません。
なぜなら、オンプレミスのDNSサーバーが storage.googleapis.com を通常のパブリックIPとして名前解決してしまうからです。
対策:
オンプレミスのDNSサーバーで、*.googleapis.com や restricted.googleapis.com などのゾーンに対し、Cloud DNSのフォワーディングゾーンや特殊なCNAME設定を組み合わせ、restricted.googleapis.com(外部IPを持たない環境向けの専用エンドポイント)へ名前解決を誘導する構成をとる必要があります。
ハマりどころ 2: ファイアウォール(VPCファイアウォールルール)の誤解
「外部IPがないから、外向き(Egress)の通信はすべてブロックしよう」と、Egressルールで 0.0.0.0/0 を拒否(Deny)しつつ、特定のIPだけ許可するような厳格なファイアウォールを書くことがあります。
このとき、宛先IPとして 199.36.153.8/24 をEgressの許可ルールに追加し忘れていると、PGA宛てのパケットが自身のVPCファイアウォールによってドロップされてしまいます。
対策:
VPCファイアウォールのEgressルールを設計する際は、デフォルトの許可(Allow all)を上書きしてホワイトリスト方式にする場合、必ず 199.36.153.8/24 への通信を許可するルールを含めるようにしてください。
—
まとめ
プライベートGoogleアクセスは、一見すると「設定をポチッと有効にするだけの魔法のスイッチ」のように思えますが、その裏側ではDNS、特殊なルーティングレンジ(199.36.153.8/24)、そしてGCPのバックボーンネットワークが緻密に連携した高度なメカニズムが動いています。
「なぜこのパケットがインターネットに出ないのか」「名前解決の仕組みはどうなっているのか」というパケットの旅のストーリーを頭に入れておけば、万が一のネットワークトラブルやセキュリティ監査の際にも、迷うことなく迅速に原因を特定し、スマートに対処できるはずです。
セキュアで頑健なクラウドネットワークの構築に、ぜひこの知識を役立ててください。それでは、良きSREライフを!
コメント