Cloud VPN HA 構成で VPC 間をセキュアに接続
- Authors
- Name
- ごとれん
- X
- @ren510dev
目次
- 目次
- はじめに
- Cloud VPN
- VPC Peering との棲み分け
- Cloud VPN の仕組み
- HA-VPN
- VPN の HA 構成
- フェールオーバの仕組み
- 構成要素
- セッションステータス
- 可用性 SLA を最大化するトポロジ
- 2 つの VPN Gateway で 1 つずつインターフェースを持つパターン
- 1 つの VPN Gateway で 2 つのインターフェースを持つパターン
- 1 つの VPN Gateway で 1 つのインターフェースを持つパターン
- フルメッシュ構成
- アンチパターン
- HA-VPN フルメッシュ構成の検証
- VPC の構築
- Cloud VPN の構築
- 構成確認
- プライベート IP アドレスで疎通確認
- Compute Engine インスタンスの準備
- ファイアウォールの追加
- ping / tcpdump 確認
- まとめ
- 参考・引用

はじめに
複数の Google Cloud プロジェクトに跨るリソース間の通信が発生する場面では、VPC ネットワークを通じて安全に接続できる仕組みが必要です。
Google Cloud では、主に Cloud VPN によるプライベート接続で VPC 間にトンネリングを構築する方法、VPC Peering で VPC 間を直接接続する方法、比較的規模の大きいサービスでは Shared VPC を利用してネットワークを共有する方法等があります。 中でも Cloud VPN を使用した接続は、プロジェクト間を柔軟かつセキュアに接続したい、異なるネットワーク構成を維持したまま通信させたいといった場合に有効です。
Cloud VPN は、トラフィックの暗号化や論理的なネットワーク分離を重視するケース、推移的ルーティングが必要となる場面で多く採用されています。
今回のブログでは、Cloud VPN を利用して異なるプロジェクトの VPC をセキュアに接続する方法について紹介したいと思います。
Cloud VPN

Cloud VPN は IPsec を利用して、異なるネットワーク同士をセキュアに接続するためのソリューションです。 例えば、自社のデータセンターと Google Cloud 上の VPC 間を接続したり、Google Cloud プロジェクト上の VPC 間を接続したりする際に利用されます。
Cloud VPN では、外部をネットワーク経由するトラフィックが暗号化されるため、盗聴や改竄のリスクを抑えることができます。 通信の際は、片方の VPN Gateway がデータを暗号化して送り出し、もう一方の VPN Gateway がそのデータを復号して受け取る仕組みになっています。
VPC が別プロジェクトに存在する場面、VPC Peering の制限を避けたい場面では Cloud VPN を使った接続が有効です。
VPC Peering との棲み分け
Cloud VPN の他にも VPC Peering を利用することで、拠点間もしくは Google Cloud の VPC 間を接続することができますが、Cloud VPN と比較して次のような制限があります。
名前解決の制限
VPC Peering では、基本的にピア VPC の内部 DNS 名を使った名前解決が直接できません。
例えば、ピア接続された先のインスタンスに対して vm-name.internal
のように名前でアクセスしたくても、それが解決されない場合があります。
この制限を回避するためには、ピア VPC にある Cloud DNS の Private Zone と DNS ポリシを共有する必要があります。 つまり DNS の共有設定をしない限り、接続先のリソースに対して名前ではなく IP アドレスでアクセスする必要があり、運用コストが増加する懸念があります。
トラフィック制御の制限
VPC Peering では 推移的ルーティング(Transitive Routing) が利用できません。
例えば、A–B 間、B–C 間が VPC Peering で接続されていても、A から C へ直接通信することはできず、別途フルメッシュ接続を構築する必要があります。
また、このような推移的ルーティングが必要になる場合、代わりに Cloud VPN や Cloud Router を使った Hub-and-Spoke 構成 を取ることが推奨されています。

特に Google Cloud では、Memorystore や Cloud SQL、Vertex AI を VPC に紐付ける際は、Private Service Access を構成する必要があり、VPC Peering では直接的な接続ができない場合があるため、注意が必要です。
Cloud VPN の仕組み
Cloud VPN は、双方の VPC 境界に VPN Gateway を設置し、その間に UDP トンネルを構築します。 ネットワークトンネルは IKE(Internet Key Exchange) プロトコルで確立され、内部は事前共有キーで暗号化された IPSec 通信となります。
後述する HA-VPN 構成では BGP の経路広報を利用して、VPN トンネルを通じたルートテーブルの交換が自動的に処理されます。
HA-VPN
HA-VPN は、オンプレミスや他のネットワークと Google Cloud の VPC を IPsec VPN を通じてセキュアに接続するための高可用な VPN ソリューションです。 HA-VPN では、最大 99.99% の SLA が保証されています。
VPN の HA 構成
HA-VPN の VPN Gateway には 2 つのインターフェースがあり、それぞれに 1 つずつグローバル IP アドレスが割り当てられます。 各インターフェースは、複数の VPN トンネルを張ることが可能で、トラフィックを冗長構成で処理できます。
なお、確保したグローバル IP アドレスは VPN Gateway を削除すると自動的に解放されます。
Google Cloud では、VPC 間またはオンプレミスと VPC 間の安定した通信を維持するために、各方向に 2 本、合計 4 本の VPN トンネルを構成する ことが推奨されています。
フェールオーバの仕組み
4 本のトンネルで構成された HA-VPN 環境ではいずれか 1 本の経路に障害が発生した場合、トラフィックは自動的に他のトンネルにフェールオーバされます。
これにより、物理的なネットワーク障害や IPSec セッションの断絶、ルーティングの経路不達といった事象が発生しても、継続した通信が可能です。
HA-VPN では、各トンネル上で BGP プロトコルを利用して動的に経路情報を交換します。 あるトンネル上の BGP セッションが切断されると、Cloud Router はその経路を無効化し、代替パスの中から最も優先度の高いルートを最短ホップや最小 MED 等に基づいて選択し、通信を継続します。
フェールオーバ中の切り替えはマネージドに行われ、通常は BGP の Hold Timer によって、数秒から数十秒程度で処理されます。 フェールオーバによるトンネルの切り替え処理は、BGP ルータがピアと通信できないと判断するまでのタイミングに依存します。
トンネルが復旧すると、Cloud Router 間で BGP セッションが再確立され、ルート情報も再度交換されます。 再び使用可能になったトンネルの経路は、他のトンネルとの優先順位をもとにルーティングテーブルに追加され、必要に応じてトラフィックがそちらへ流れるようになります。
構成要素
Cloud VPN の HA 構成には以下が含まれます。
- VPN Gateway
- VPC との 接続に利用する口 を作る
- 各 VPN Gateway はインターフェースを 2 つ持つ
- 複数のトンネルにより冗長構成を実現
- Cloud Router と組み合わせることで BGP プロトコルによる経路交換が可能
- Cloud Router
- 接続のためのネットワーク経路 を作る
- IPsec トンネルで接続されたピアルータと BGP セッションを張る
- サブネットや経路情報の自動交換・動的ルーティングが可能
- IP アドレスの手動登録の必要がなくなる
- VPN Tunnel
- VPN Gateway に対応する 実際の VPN トンネル を作る
- IKE プロトコルを利用してトンネル通信を IPsec で暗号化
- 内部は Cloud Router 経由で BGP セッションによる経路広報によって構築される
- Router Interface
- VPN Gateway に接続するためのインターフェース を作る
- Cloud Router が使用するネットワークインターフェースに接続用 IP を割り振る
- VPN トンネルとの紐付けを 1 対 1 で行う
- BGP Session
- Cloud Router 間で BGP セッションを張る
- 自ルータとピアルータ間で経路情報を動的に交換
- 冗長トンネルで自動フェイルオーバ(障害対応時のリカバリルートを見つける)を実行
- ルートの優先度制御(Priority)も可能
セッションステータス
VPN トンネルのステータス
ステータス | 概要 |
---|---|
• Allocating resources | • トンネルが作成された直後の初期状態 • ソースが割り当てられている途中でトンネルが間もなく起動する |
• Waiting for full config | • ルート情報やルーティング構成がまだ準備できていない状態 • 経路情報の待機中。 |
• First Handshake | • ピアの VPN との Phase 1(IKE SA)ネゴシエーションを試行中 • 少なくとも 1 回は失敗している可能性がある |
• Established | • トンネルが正常に確立されルートも構成済み • VPN 通信が安定して動作している状態 |
• No Incoming Packets | • ピアの VPN Gateway からの受信パケットが確認できない状態 • トラフィックが一方向のみ、または到達していない可能性がある |
BGP のステータス
ステータス | 概要 |
---|---|
• Connect | • BGP ピアは正常に設定されているが BGP セッションはまだ確立されていない • 対応する BGP ピアステータス:DOWN |
• Disabled | • BGP ピアは意図的に停止されている • 対応する BGP ピアステータス:DOWN |
• Established | • BGP ピアは正常に構成され BGP セッションが確立されている • 対応する BGP ピアステータス:UP |
• Idle | • BGP ピアは構成されているが内部エラーが発生している • 対応する BGP ピアステータス:DOWN |
• The VPN tunnel • Interconnect attachment is unknown. | • BGP ピアに関連付けられた VPN トンネルまたは VLAN アタッチメントが見つからない • 対応する BGP ピアステータス:DOWN |
• The VPN Tunnel is not in Established state. | • VPN トンネルがダウンしている • 対応する BGP ピアステータス:DOWN |
• This interface • BGP peer is invalid. | • Cloud Router が Network Connectivity Center 用に設定されているが、インターフェースが紐付けられていない • 対応する BGP ピアステータス:DOWN |
• No interface found for the bgp peer. | • Cloud Router が BGP ピアに紐付くインターフェースを見つけられない • 対応する BGP ピアステータス:DOWN |
可用性 SLA を最大化するトポロジ
HA-VPN で 99.99% の可用性 SLA を満たすためには、HA-VPN Gateway にある 2 つのインターフェースの両方から、それぞれトンネルを張る必要があります。 このため、各インターフェースに少なくとも 1 本以上のトンネルが必要になります。
後述する フルメッシュ構成 は、より高い冗長性やレジリエンスが要求される場合を除いて、可用性 SLA を満たすための必須条件ではありません。 ピア側にインターフェースが 1 つしかない場合でも、HA-VPN Gateway の 2 つのインターフェースから、それぞれその単一のピアインターフェースに対してトンネルを張ることで SLA を満たす構成を作ることは可能です。
具体的には、以下の構成パターンがあります。
2 つの VPN Gateway で 1 つずつインターフェースを持つパターン
- Connect two peer VPN gateways
- 2 つの ピアの VPN Gateway で単一のグローバル IP アドレスを持つ
- HA-VPN Gateway はピアの VPN Gateway のインターフェースに 1 つずつ 計 2 本のトンネル を構築する

この構成パターンでは、ピアネットワーク側に 2 つの VPN Gateway が存在し、それぞれに 1 つずつグローバル IP アドレスが割り当てられています。 Google Cloud 側の HA-VPN Gateway は、それらの 2 つに対してそれぞれ 1 本ずつトンネルを張り、合計 2 本の VPN トンネルを構成します。
これにより、ピア側のいずれかの機器に障害が発生した場合でも、自動でもう一方の経路に切り替えて通信を継続することができます。
また、一方の VPN Gateway を停止してソフトウェアアップデートやメンテナンスを行っても、サービスを中断することなく運用を続けることが可能です。
この構成は、TWO_IPS_REDUNDANCY と呼ばれ、99.99% の可用性 SLA が保証されています。
1 つの VPN Gateway で 2 つのインターフェースを持つパターン
- Connect one peer VPN gateway with two IP addresses
- 単一の HA-VPN Gateway が 2 つのグローバル IP アドレスを持つ
- HA-VPN Gateway はピアの VPN Gateway 上の各インターフェースに 1 つずつ 計 2 本のトンネル を構築する

この構成パターンでは、ピア側に 1 つの VPN Gateway しか存在しないものの、2 つのグローバル IP アドレスが割り当てられています。 Google Cloud 側の HA-VPN Gateway は、それぞれの IP アドレスに対して 1 本ずつ、合計 2 本のトンネルを構成します。
ピアの VPN 機器自体は 1 つであっても、インターフェースやネットワーク経路を分けることで冗長性を実現し、片方の経路に問題が発生した場合でも自動的に切り替えることができます。
この構成も、TWO_IPS_REDUNDANCY に該当し、99.99% の可用性 SLA が保証されています。
上の構成と比較して、ネットワーク機器を増設することなく冗長性を確保したい場合に適した方法になります。
1 つの VPN Gateway で 1 つのインターフェースを持つパターン
- Connect one peer VPN gateway with one IP address
- 1 つの HA-VPN Gateway が 1 つのグローバル IP アドレスを持つ 1 つのピア VPN Gateway に接続
- HA-VPN Gateway では 2 本のトンネル を使用し、両方がピアの VPN Gateway の 1 つのインターフェースに接続される

この構成パターンでは、ピア側に 1 つの VPN Gateway があり、割り当てられているグローバル IP アドレスも 1 つのみです。 Google Cloud 側の HA-VPN Gateway は、その 1 つのグローバル IP に対して異なるインターフェースから 2 本のトンネルを張り、Google Cloud の内部で冗長構成を実現します。
ピア側では特別な冗長構成を取る必要がなく、構成が比較的シンプルで導入が容易なのがメリットです。
また、トンネルの一方に異常があっても、もう一方に自動でトラフィックをフェールオーバできるため、可用性が維持されます。
この構成は、SINGLE_IP_INTERNALLY_REDUNDANT と呼ばれ、上 2 つと同様に 99.99% の可用性 SLA が保証されています。
フルメッシュ構成
- HA-VPN topologies - Connect VPC networks by using HA-VPN gateways
- 単一の HA-VPN Gateway が 2 つのグローバル IP アドレスを持つ
- HA-VPN Gateway はピアの HA-VPN Gateway 上の各インターフェースに 1 つずつ 計 4 本のトンネル を構築する

すべての HA-VPN Gateway およびインターフェース間にトンネルを張ったトポロジは フルメッシュ構成 と呼ばれます。
フルメッシュ構成では、2 つの HA-VPN Gateway で、それぞれ 2 つずつインターフェースを持ちます。 HA-VPN Gateway の 2 つのインターフェースは、ピアの VPN Gateway の対応するインターフェースへのトンネルを 1 つずつ構築し、計 4 本のトンネル を張ります。
- HA-VPN の Interface 0 を ピア の Interface 0 に接続
- HA-VPN の Interface 0 を ピア の Interface 1 に接続
- HA-VPN の Interface 1 を ピア の Interface 0 に接続
- HA-VPN の Interface 1 を ピア の Interface 1 に接続
アンチパターン
HA-VPN の片方のインターフェースしか使用せず、もう一方が未使用の状態(トンネルが張られていない)では SLA 上の冗長性要件を満たせないため、99.99% の可用性は保証されません。 このため、必ず両方のインターフェースにトンネルを構成する必要があります。
例えば、以下のように HA-VPN インターフェース 0 からピア interface 0 にしか接続されていない場合、可用性 SLA は満たされていません。

HA-VPN フルメッシュ構成の検証
HA-VPN のフルメッシュ構成を検証してみます。
下記の通り、HA-VPN + BGP で計 4 本のトンネルリングを構築します。
VPN トンネル
経路系統 | HA-VPN I/F | Tunnel Name |
---|---|---|
1 | A IF 0 → B IF 0 | a-router-interface-00 |
2 | A IF 0 → B IF 1 | a-router-interface-01 |
3 | B IF 0 → A IF 0 | b-router-interface-00 |
4 | B IF 0 → A IF 1 | b-router-interface-01 |
BGP セッション
- BGP CIDR 1:169.254.1.0/30
- BGP CIDR 2:169.254.1.0/30
経路系統 | BGP IP | BGP Peer IP | BGP ASN | Peer BGP ASN |
---|---|---|---|---|
1 | 169.254.1.1 | 169.254.1.2 | 65001 | 65002 |
2 | 169.254.1.5 | 169.254.1.6 | 65001 | 65002 |
3 | 169.254.1.2 | 169.254.1.1 | 65002 | 65001 |
4 | 169.254.1.6 | 169.254.1.5 | 65002 | 65001 |

Project A と Project B は便宜上、次の名前とします。
- Project A:sunrise
### locals.tf
locals {
project = "sunrise" # Project A
peer_project = "sunset" # Project B
region = "asia-east1"
name = "cloud-vpn-playground"
subnet_ip = "10.0.0.0/24"
gateway_count = ["0", "1"]
tunnel_ip_ranges = {
"0" = "169.254.1.1/30" # IP range: 169.254.1.0-3
"1" = "169.254.1.5/30" # IP range: 169.254.1.4-7
}
peer_bgp_ips = {
"0" = "169.254.1.2"
"1" = "169.254.1.6"
}
router_asn = 65001
peer_router_asn = 65002
}
- Project B:sunset
### locals.tf
locals {
project = "sunset" # Project B
peer_project = "sunrise" # Project A
region = "asia-east1"
name = "cloud-vpn-playground"
subnet_ip = "10.0.1.0/24"
gateway_count = ["0", "1"]
tunnel_ip_ranges = {
"0" = "169.254.1.2/30" # IP range: 169.254.1.0-3
"1" = "169.254.1.6/30" # IP range: 169.254.1.4-7
}
peer_bgp_ips = {
"0" = "169.254.1.1"
"1" = "169.254.1.5"
}
router_asn = 65002
peer_router_asn = 65001
}
こちらの locals.tf
を利用して以下の Terraform をそれぞれ実行します。
VPC の構築
### vpc.tf
module "vpc" {
source = "terraform-google-modules/network/google"
version = "11.0.0"
project_id = local.project
network_name = "${local.name}-vpc"
routing_mode = "GLOBAL"
subnets = [
{
subnet_name = "${local.name}-${local.region}-01"
subnet_ip = local.subnet_ip
subnet_region = local.region
description = "VPC subnet for Project A"
subnet_private_access = "true"
subnet_flow_logs = "true"
},
]
firewall_rules = [
{
name = "${module.vpc.network_name}-allow-internal"
description = "Allow internal traffic"
direction = "INGRESS"
allow = [
{
protocol = "icmp"
},
{
protocol = "tcp"
ports = ["1-65535"]
},
{
protocol = "udp"
ports = ["1-65535"]
}
]
ranges = module.vpc.subnets_ips
},
{
name = "${module.vpc.network_name}-allow-iap-ssh"
description = "Allow IAP SSH traffic"
direction = "INGRESS"
allow = [
{
protocol = "tcp"
ports = ["22", "3389"]
},
{
protocol = "icmp"
}
]
ranges = ["35.235.240.0/20"] # https://cloud.google.com/iap/docs/using-tcp-forwarding#create-firewall-rule
}
]
}
Cloud VPN の構築
まず、VPN Gateway と Cloud Router を両プロジェクトの VPC にそれぞれ作成します。
### vpn_gateway.tf
resource "google_compute_ha_vpn_gateway" "vpn_gateway" {
name = "${local.project}-to-${local.peer_project}-vpn-gateway"
project = local.project
description = "HA VPC VPN Gateway for ${local.project} to ${local.peer_project}"
network = "${local.name}-vpc"
region = local.region
}
resource "google_compute_router" "router" {
name = "${local.project}-to-${local.peer_project}-router"
project = local.project
description = "HA VPC VPN Router for ${local.project} to ${local.peer_project}"
region = local.region
network = "${local.name}-vpc"
bgp {
asn = local.router_asn
}
}
次に、Secret Manager を利用してトンネル通信を暗号化するための共有シークレット(IKE)を定義します。
Terraform では Secret Store のみを作成し、実際のシークレット値はコンソールから手動で設定します。 なお、IKE は Project A と Project B で同じ値を設定する必要があります。
### secret_manager.tf
resource "google_secret_manager_secret" "vpn_shared_secret" {
secret_id = "${local.project}-to-${local.peer_project}-vpn-ike-shared_secret"
replication {
auto {}
}
labels = {
resource-name = "${local.project}-to-${local.peer_project}-vpn-ike-shared_secret"
}
}
data "google_secret_manager_secret_version" "vpn_shared_secret_version" {
secret = google_secret_manager_secret.vpn_shared_secret.id
version = "latest"
}
data "google_secret_manager_secret_version_access" "vpn_shared_secret_access" {
secret = data.google_secret_manager_secret_version.vpn_shared_secret_version.secret
version = data.google_secret_manager_secret_version.vpn_shared_secret_version.version
}

両プロジェクトで VPN Gateway と Cloud Router を作成した後、VPN トンネルの定義を追加します。
### vpn_gateway.tf
resource "google_compute_vpn_tunnel" "vpn_tunnel" {
for_each = toset(local.gateway_count)
name = "${local.project}-to-${local.peer_project}-vpn-tunnel-${format("%02d", tonumber(each.key))}"
project = local.project
description = "HA VPC VPN Tunnel for ${local.project} to ${local.peer_project}"
region = local.region
vpn_gateway = google_compute_ha_vpn_gateway.vpn_gateway.id
vpn_gateway_interface = tonumber(each.key)
peer_gcp_gateway = "${local.project}-to-${local.peer_project}-vpn-gateway"
shared_secret = data.google_secret_manager_secret_version_access.vpn_shared_secret_access.secret_data ## プロジェクトA-B で共通の値を設定する(Secret Manager から取得)
ike_version = 2
router = google_compute_router.router.name
labels = {
resource-name = "${local.project}-to-${local.peer_project}-vpn-tunnel-${format("%02d", tonumber(each.key))}",
}
depends_on = [
google_compute_ha_vpn_gateway.vpn_gateway,
google_compute_router.router,
google_secret_manager_secret.vpn_shared_secret
]
}
BGP セッションを張るためのルータインターフェースと BGP ピアを定義します。
### vpn_gateway.tf
resource "google_compute_router_interface" "router_interface" {
for_each = google_compute_vpn_tunnel.vpn_tunnel
name = "${local.project}-router-interface-${format("%02d", tonumber(each.key))}"
project = local.project
router = google_compute_router.router.name
region = local.region
ip_range = local.tunnel_ip_ranges[each.key]
vpn_tunnel = each.value.name
depends_on = [
google_compute_vpn_tunnel.vpn_tunnel,
google_compute_router.router
]
}
resource "google_compute_router_peer" "bgp_peer" {
for_each = google_compute_router_interface.router_interface
name = "${local.project}-to-${local.peer_project}-bgp-session-${format("%02d", tonumber(each.key))}"
project = local.project
router = google_compute_router.router.name
region = local.region
peer_asn = local.peer_router_asn
interface = each.value.name
peer_ip_address = local.peer_bgp_ips[each.key]
advertised_route_priority = 100
depends_on = [
google_compute_router_interface.router_interface,
google_compute_router.router
]
}
構成確認
プロジェクト間の VPN トンネルと BGP セッションが確立されていることを確認します。
VPN Tunnel
- Project A の VPN トンネル

$ gcloud compute vpn-tunnels describe surise-to-sunset-vpn-tunnel-00 \
--region=asia-east1 \
--project=surise
$ gcloud compute vpn-tunnels describe surise-to-sunset-vpn-tunnel-01 \
--region=asia-east1 \
--project=surise

- Project B の VPN トンネル
$ gcloud compute vpn-tunnels describe sunset-to-surise-vpn-tunnel-00 \
--region=asia-east1 \
--project=sunset
$ gcloud compute vpn-tunnels describe sunset-to-surise-vpn-tunnel-01 \
--region=asia-east1 \
--project=sunset
Cloud Router
- Project A の Cloud Router

$ gcloud compute routers describe surise-to-sunset-router \
--region=asia-east1 \
--project=surise
- Project B の Cloud Router

$ gcloud compute routers describe sunset-to-surise-router \
--region=asia-east1 \
--project=sunset
BGP Session
- Project A の BGP セッション

$ gcloud compute routers get-status surise-to-sunset-router \
--region=asia-east1 \
--project=surise
- Project B の BGP セッション

$ gcloud compute routers get-status sunset-to-surise-router \
--region=asia-east1 \
--project=sunset
プライベート IP アドレスで疎通確認
Project A と Project B の VPC に配置した Compute Engine インスタンス間で、プライベート IP アドレスによる疎通確認を行います。
Compute Engine インスタンスの準備
### compute_engine.tf
resource "google_service_account" "default" {
account_id = "${local.name}-gce"
display_name = "${local.name}-gce"
description = "GCE Service Account for ${local.name}"
}
resource "google_compute_instance" "default" {
name = "${local.name}-instance-01"
description = "Cloud VPN Playground Instance 01"
machine_type = "e2-micro"
zone = "asia-east1-a"
tags = [
"${local.name}-instance-01",
"allow-icmp", ## ICMP を許可するためのタグ
]
can_ip_forward = false
boot_disk {
auto_delete = true
initialize_params {
image = "debian-cloud/debian-12"
size = 10
type = "pd-balanced"
}
}
network_interface {
network = "${local.name}-vpc"
subnetwork = "${local.name}-${local.region}-01"
network_ip = local.instance_ip
access_config {
nat_ip = ""
public_ptr_domain_name = ""
network_tier = "PREMIUM"
}
}
service_account {
email = google_service_account.default.email
scopes = [
"https://www.googleapis.com/auth/devstorage.read_only",
"https://www.googleapis.com/auth/logging.write",
"https://www.googleapis.com/auth/monitoring.write",
"https://www.googleapis.com/auth/service.management.readonly",
"https://www.googleapis.com/auth/servicecontrol",
"https://www.googleapis.com/auth/trace.append"
]
}
labels = {
resource-name = "${local.name}-instance-01"
})
depends_on = [
google_service_account.default
]
}
- Project A のインスタンス

$ gcloud compute instances describe cloud-vpn-playground-instance-01 \
--zone=asia-east1-a \
--project=sunrise
- Project B のインスタンス

$ gcloud compute instances describe cloud-vpn-playground-instance-01 \
--zone=asia-east1-a \
--project=sunset
ファイアウォールの追加
ピアプロジェクトのインスタンスからの ICMP パケットを許可するため、ファイアウォールルールを追加します。
### firewall.tf
resource "google_compute_firewall" "allow_icmp_from_vpn_peer" {
name = "${local.name}-vpc-allow-internal-tagbased-icmp"
network = "${local.name}-vpc"
direction = "INGRESS"
priority = 1000
allow {
protocol = "icmp"
}
# NOTE: Project A は 10.0.1.0/24(Project B)からの通信を許可
# NOTE: Project B は 10.0.0.0/24(Project A)からの通信を許可
source_ranges = ["10.0.1.0/24"]
target_tags = ["allow-icmp"]
description = "Allow ICMP (ping) from peer VPC over VPN"
}
- Project A のファイアウォールルール

$ gcloud compute firewall-rules describe cloud-vpn-playground-vpc-allow-internal-tagbased-icmp \
--project=sunrise
- Project B のファイアウォールルール

$ gcloud compute firewall-rules describe cloud-vpn-playground-vpc-allow-internal-tagbased-icmp \
--project=sunset
ping / tcpdump 確認
両プロジェクトの VPC に配置したインスタンス間で、プライベート IP アドレスによる ICMP 疎通が取れるかを確認します。
- Project A → Project B
$ ping -c 5 10.0.1.100
PING 10.0.1.100 (10.0.1.100) 56(84) bytes of data.
64 bytes from 10.0.1.100: icmp_seq=1 ttl=62 time=2.64 ms
64 bytes from 10.0.1.100: icmp_seq=2 ttl=62 time=0.927 ms
64 bytes from 10.0.1.100: icmp_seq=3 ttl=62 time=0.988 ms
64 bytes from 10.0.1.100: icmp_seq=4 ttl=62 time=0.906 ms
64 bytes from 10.0.1.100: icmp_seq=5 ttl=62 time=0.901 ms
--- 10.0.1.100 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4004ms
rtt min/avg/max/mdev = 0.901/1.272/2.638/0.683 ms
$ sudo tcpdump -n icmp and src host 10.0.0.100
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on ens4, link-type EN10MB (Ethernet), snapshot length 262144 bytes
08:33:38.787757 IP 10.0.0.100 > 10.0.1.100: ICMP echo request, id 1249, seq 1, length 64
08:33:39.788648 IP 10.0.0.100 > 10.0.1.100: ICMP echo request, id 1249, seq 2, length 64
08:33:40.789807 IP 10.0.0.100 > 10.0.1.100: ICMP echo request, id 1249, seq 3, length 64
08:33:41.790852 IP 10.0.0.100 > 10.0.1.100: ICMP echo request, id 1249, seq 4, length 64
08:33:42.791438 IP 10.0.0.100 > 10.0.1.100: ICMP echo request, id 1249, seq 5, length 64
- Project B → Project A
$ ping -c 5 10.0.0.100
PING 10.0.0.100 (10.0.0.100) 56(84) bytes of data.
64 bytes from 10.0.0.100: icmp_seq=1 ttl=62 time=2.96 ms
64 bytes from 10.0.0.100: icmp_seq=2 ttl=62 time=0.915 ms
64 bytes from 10.0.0.100: icmp_seq=3 ttl=62 time=0.920 ms
64 bytes from 10.0.0.100: icmp_seq=4 ttl=62 time=0.957 ms
64 bytes from 10.0.0.100: icmp_seq=5 ttl=62 time=0.913 ms
--- 10.0.0.100 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4046ms
rtt min/avg/max/mdev = 0.913/1.332/2.957/0.812 ms
$ sudo tcpdump -n icmp and src host 10.0.1.100
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on ens4, link-type EN10MB (Ethernet), snapshot length 262144 bytes
08:34:13.498877 IP 10.0.1.100 > 10.0.0.100: ICMP echo request, id 917, seq 1, length 64
08:34:14.499081 IP 10.0.1.100 > 10.0.0.100: ICMP echo request, id 917, seq 2, length 64
08:34:15.518945 IP 10.0.1.100 > 10.0.0.100: ICMP echo request, id 917, seq 3, length 64
08:34:16.543027 IP 10.0.1.100 > 10.0.0.100: ICMP echo request, id 917, seq 4, length 64
08:34:17.544119 IP 10.0.1.100 > 10.0.0.100: ICMP echo request, id 917, seq 5, length 64
Cloud VPN を通じてプライベート IP アドレスで ICMP 疎通が取れていることが分かります。
まとめ
今回のブログでは、Cloud VPN を利用して異なるプロジェクトの VPC をセキュアに接続する方法について紹介しました。
Cloud VPN(HA-VPN)は、IPsec による暗号化と Cloud Router を活用した BGP による動的ルーティングに対応し、可用性と柔軟性に優れる接続方式です。 VPC Peering に比べ、推移的ルーティングや DNS 解決等の制約が少なく、プロジェクト間の独立性を保ったまま接続することが可能で、マルチプロジェクト構成でのセキュアな通信手段として信頼性の高い選択肢と言えます。