grasys blog

Cloud Storage の Bucket IP filtering 徹底検証 〜IP 制限 × VPC Peering でアクセス管理を最適化する構成パターン〜

こんにちは。

Cloud Storage の Bucket IP filtering を知っていますか?
まだ preview の機能ですが、いろいろ「調査し甲斐のある」ものです。

Bucket IP filtering とは

Cloud Storage Bucket IP filtering は、Cloud Storage のバケットに対して、特定の IP アドレス範囲からのアクセスのみを許可するような、アクセス制御機能を提供します [バケット IP フィルタリング]。
2024 年 11 月 14 日に Public Preview としてリリース されています。

有効なユースケース(仮説)

いくつか、Bucket IP filtering が有効に働くユースケースがありそうです。例えば「IP の設定によって楽をしたい」ケースで有用だと考えられます。具体的には、以下のような場合です。

  • 共有 VPC を使っている
  • VPC Peering を用いており、接続先が多い
  • オンプレミス環境と VPN を構成しており、多数のクライアントがいる
  • 多拠点にクライアント PC があり、特定 IP のみからアクセスできるバケットを作成したい

このような場合に、接続先の Google Cloud プロジェクトや、オンプレミス環境に対して、サービスアカウントの設定を細かく行うのは大変です。また、サービスアカウントがなくても、特定の IP アドレスからのアクセスのみを許可することで、設定を簡略化できます。

本ブログの目的と概要

このブログでは、プロジェクトを作成して Bucket IP filtering を設定するところから、実際の検証までの手順を解説していきます。Bucket IP filtering の動作を確認することと、先ほどあげたユースケースで実際に有効に働くのか検証することを目的としています。

特に、VPC Peering を使っている場合に、どのようなアクセスが許可され、どのようなアクセスが拒否されるかを確認します。具体的には、ホスト プロジェクト (PROJECT_HOST) とゲスト プロジェクト (PROJECT_GUEST) を作成し、ホスト プロジェクトにいくつかの Cloud Storage バケットを作成します。ホスト プロジェクトとゲスト プロジェクトの間に VPC Peering を設定し、VM 同士が通信できるようにします。ホスト プロジェクトのバケットに、Bucket IP filtering を設定して、アクセス可否を確認します。

異なる組織間で VPC Peering を行っている場合に、権限の許可・申請の煩雑さや、Peering 先「以外」の VPC ネットワークからのアクセスを防ぐことができれば安全性も高まります。

まとめから読みたい方はこちらへ飛んでください → まとめと考察

検証環境の構築

基本的な操作は、すべて gcloud コマンドで行い、コピーするだけで再現できるようにしています。

ネットワークやバケットのリージョンには us-central1 を指定します。

環境定義

まずは、環境の定義を行い、環境変数として記憶させておきます。PROJECT_HOSTPROJECT_GUEST に、それぞれのプロジェクト ID を設定してください。YOUR_IP に、アクセスを許可するグローバル IP アドレスを設定してください。YOUR_ACCOUNT に、操作者の Google アカウントを設定してください。BILLING_ACCOUNT に、Google Cloud の課金アカウントを設定してください。

export PROJECT_HOST=project-host-2025-03
export PROJECT_GUEST=project-guest-2025-03
export YOUR_IP=198.51.100.1
export YOUR_ACCOUNT=test@example.com
export BILLING_ACCOUNT="01A000-000000-000000"

Google Cloud プロジェクトの作成・API の有効化

for project in $PROJECT_HOST $PROJECT_GUEST; do
  gcloud projects create $project
  gcloud billing projects link $project --billing-account=$BILLING_ACCOUNT
  gcloud services enable compute.googleapis.com --project=$project
done

バケットの作成

ホスト側のプロジェクトに、次のような Cloud Storage バケットを作成します。用途に与えた項目を確認しやすいように設計しています。

バケット名用途アクセス許可Bucket IP filteringnetworkIP 範囲
public-2025-03公開確認用allUsers無効指定なし指定なし
private-2025-03非公開確認用host プロジェクトのみ無効指定なし指定なし
ip-peering-all-2025-03VPC Peering 用allUsers有効network-peering-host10.0.0.0/16
ip-peering-private-2025-03VPC Peering 用host プロジェクトのみ有効network-peering-host10.0.0.0/16
ip-allow-all-2025-03特定 IP 用allUsers有効publicNetworkSource, network-peering-host10.0.0.0/16, アクセス元の外部 IP
ip-allow-private-2025-03特定 IP 用host プロジェクトと検証者のアカウント有効publicNetworkSource, network-peering-host10.0.0.0/16, アクセス元の外部 IP
gcloud storage buckets create gs://public-2025-03 --project=$PROJECT_HOST --uniform-bucket-level-access
gcloud storage buckets add-iam-policy-binding gs://public-2025-03 --member=allUsers --role=roles/storage.objectViewer --project=$PROJECT_HOST

gcloud storage buckets create gs://private-2025-03 --project=$PROJECT_HOST --uniform-bucket-level-access

gcloud storage buckets create gs://ip-peering-all-2025-03 --project=$PROJECT_HOST --location=us-central1 --uniform-bucket-level-access
gcloud storage buckets add-iam-policy-binding gs://ip-peering-all-2025-03 --member=allUsers --role=roles/storage.objectViewer --project=$PROJECT_HOST

gcloud storage buckets create gs://ip-peering-private-2025-03 --project=$PROJECT_HOST --location=us-central1 --uniform-bucket-level-access

gcloud storage buckets create gs://ip-allow-all-2025-03 --project=$PROJECT_HOST --location=us-central1 --uniform-bucket-level-access
gcloud storage buckets add-iam-policy-binding gs://ip-allow-all-2025-03 --member=allUsers --role=roles/storage.objectViewer --project=$PROJECT_HOST

gcloud storage buckets create gs://ip-allow-private-2025-03 --project=$PROJECT_HOST --location=us-central1 --uniform-bucket-level-access
gcloud storage buckets add-iam-policy-binding gs://ip-allow-private-2025-03 --member="user:${YOUR_ACCOUNT}" --role=roles/storage.objectAdmin --project=$PROJECT_HOST

※ GCS バケットは Google Cloud 全体で一意である必要があるため、ユニークな名前を付けてください。

ダミーファイルを作成しておきます。

mkdir -p dummy_files
for bucket in public private ip-peering-all ip-peering-private ip-vpn-all ip-vpn-private ip-allow-all ip-allow-private; do
  echo "${bucket}_2025-03.txt" > dummy_files/${bucket}_2025-03.txt
  gcloud storage cp dummy_files/${bucket}_2025-03.txt gs://${bucket}-2025-03
done

VPC ネットワークの作成

デフォルトの VPC ネットワークは、本番環境で使用することは推奨されていないため、デフォルトの VPC ネットワークを削除し、新しい VPC ネットワークを作成します。

for project in $PROJECT_HOST $PROJECT_GUEST; do
  gcloud compute firewall-rules delete default-allow-rdp --project=$project --quiet
  gcloud compute firewall-rules delete default-allow-ssh --project=$project --quiet
  gcloud compute firewall-rules delete default-allow-icmp --project=$project --quiet
  gcloud compute firewall-rules delete default-allow-internal --project=$project --quiet

  gcloud compute networks delete default --project=$project --quiet
done

PROJECT_HOST に VPC ネットワーク network-peering-host を作成し、PROJECT_GUEST に VPC ネットワーク network-peering-guest を作成します。
network-peering-host 10.0.0.0/24
network-peering-guest 10.0.1.0/24
がピアリングされるように設定します。

gcloud compute networks create network-peering-host --project=$PROJECT_HOST --subnet-mode=custom
gcloud compute networks subnets create subnet-peering-host \
    --network=network-peering-host \
    --region=us-central1 \
    --range=10.0.0.0/24 \
    --project=$PROJECT_HOST

gcloud compute networks create network-peering-guest --project=$PROJECT_GUEST --subnet-mode=custom
gcloud compute networks subnets create subnet-peering-guest \
    --network=network-peering-guest \
    --region=us-central1 \
    --range=10.0.1.0/24 \
    --project=$PROJECT_GUEST

Public IP がなくても、SSH で接続できるように、IAP の設定 を行います。

for project in $PROJECT_HOST $PROJECT_GUEST; do
    gcloud projects add-iam-policy-binding $project \
        --member="user:${YOUR_ACCOUNT}" \
        --role=roles/iap.tunnelResourceAccessor
    gcloud projects add-iam-policy-binding $project \
        --member="user:${YOUR_ACCOUNT}" \
        --role=roles/compute.instanceAdmin.v1
done

gcloud compute firewall-rules create allow-ssh-ingress-from-iap-peering \
    --direction=INGRESS \
    --action=allow \
    --rules=tcp:22 \
    --source-ranges=35.235.240.0/20 \
    --network=network-peering-host \
    --project=$PROJECT_HOST

gcloud compute firewall-rules create allow-ssh-ingress-from-iap-peering \
    --direction=INGRESS \
    --action=allow \
    --rules=tcp:22 \
    --source-ranges=35.235.240.0/20 \
    --network=network-peering-guest \
    --project=$PROJECT_GUEST

また、Public IP がなくても、Google Cloud のサービスにアクセスできるように、限定公開の Google アクセスを構成 します。

gcloud compute networks subnets update subnet-peering-host --project=$PROJECT_HOST \
    --region=us-central1 \
    --enable-private-ip-google-access

gcloud compute networks subnets update subnet-peering-guest --project=$PROJECT_GUEST \
    --region=us-central1 \
    --enable-private-ip-google-access

# 確認したい場合には以下を実行
# gcloud compute networks subnets describe subnet-peering-host --project=$PROJECT_HOST \
#     --region=us-central1 \
#     --format="get(privateIpGoogleAccess)"

VM が相互に通信できるように、Firewall Rule を設定します。

gcloud compute firewall-rules create allow-internal-ingress-from-peering \
    --direction=INGRESS \
    --action=allow \
    --rules=all \
    --source-ranges=10.0.0.0/16 \
    --network=network-peering-host \
    --project=$PROJECT_HOST

gcloud compute firewall-rules create allow-internal-ingress-from-peering \
    --direction=INGRESS \
    --action=allow \
    --rules=all \
    --source-ranges=10.0.0.0/16 \
    --network=network-peering-guest \
    --project=$PROJECT_GUEST

プロジェクト間の VPC Peering の設定

今まで作成したネットワーク network-peering-host と network-peering-guest の間に VPC Peering を設定します。

# PROJECT_HOST での peering 設定
gcloud compute networks peerings create peering-host-guest \
  --network=network-peering-host \
  --peer-project=$PROJECT_GUEST \
  --peer-network=network-peering-guest \
  --project=$PROJECT_HOST

# PROJECT_GUEST での peering 設定
gcloud compute networks peerings create peering-guest-host \
  --network=network-peering-guest \
  --peer-project=$PROJECT_HOST \
  --peer-network=network-peering-host \
  --project=$PROJECT_GUEST
gcloud compute networks peerings list --project=$PROJECT_HOST
gcloud compute networks peerings list --project=$PROJECT_GUEST

VM インスタンスの作成

それぞれの VPC ネットワークに VM インスタンスを作成します。詳細は解説しませんが、no-address を指定して、Public IP を持たない VM インスタンスを作成しています。

gcloud compute instances create vm-host-peering \
    --project=$PROJECT_HOST \
    --zone=us-central1-f \
    --machine-type=e2-micro \
    --network-interface=stack-type=IPV4_ONLY,subnet=subnet-peering-host,no-address \
    --scopes=https://www.googleapis.com/auth/cloud-platform \
    --boot-disk-size 10GB \
    --boot-disk-type pd-standard \
    --image-project debian-cloud \
    --image-family debian-12

gcloud compute instances create vm-guest-peering \
    --project=$PROJECT_GUEST \
    --zone=us-central1-f \
    --machine-type=e2-micro \
    --network-interface=stack-type=IPV4_ONLY,subnet=subnet-peering-guest,no-address \
    --scopes=https://www.googleapis.com/auth/cloud-platform \
    --boot-disk-size 10GB \
    --boot-disk-type pd-standard \
    --image-project debian-cloud \
    --image-family debian-12

各 VM インスタンスには、次のようにして SSH 接続できます。

gcloud compute ssh --zone=us-central1-f vm-host-peering --tunnel-through-iap --project $PROJECT_HOST
gcloud compute ssh --zone=us-central1-f vm-guest-peering --tunnel-through-iap --project $PROJECT_GUEST

検証

バケットへのアクセス確認その 1

まずは、各 VM インスタンスから、バケットにアクセスできるか確認します。ls_all_buckets.sh を各 VM インスタンスから実行して、バケット一覧を確認します。
アウトプットのサービスアカウントに含まれるプロジェクト番号は DUMMY_PROJECT_NUMBER に置き換えています。

for bucket in public private ip-peering-all ip-peering-private ip-allow-all ip-allow-private; do
  gcloud storage ls gs://${bucket}-2025-03
done

vm-host-peering:

gs://public-2025-03/public_2025-03.txt
gs://private-2025-03/private_2025-03.txt
gs://ip-peering-all-2025-03/ip-peering-all_2025-03.txt
gs://ip-peering-private-2025-03/ip-peering-private_2025-03.txt
gs://ip-allow-all-2025-03/ip-allow-all_2025-03.txt
gs://ip-allow-private-2025-03/ip-allow-private_2025-03.txt

vm-guest-peering:

gs://public-2025-03/public_2025-03.txt
ERROR: (gcloud.storage.ls) [DUMMY_PROJECT_NUMBER-compute@developer.gserviceaccount.com] does not have permission to access b instance [private-2025-03] (or it may not exist): DUMMY_PROJECT_NUMBER-compute@developer.gserviceaccount.com does not have storage.objects.list access to the Google Cloud Storage bucket. Permission 'storage.objects.list' denied on resource (or it may not exist). This command is authenticated as DUMMY_PROJECT_NUMBER-compute@developer.gserviceaccount.com which is the active account specified by the [core/account] property.
gs://ip-peering-all-2025-03/ip-peering-all_2025-03.txt
ERROR: (gcloud.storage.ls) [DUMMY_PROJECT_NUMBER-compute@developer.gserviceaccount.com] does not have permission to access b instance [ip-peering-private-2025-03] (or it may not exist): DUMMY_PROJECT_NUMBER-compute@developer.gserviceaccount.com does not have storage.objects.list access to the Google Cloud Storage bucket. Permission 'storage.objects.list' denied on resource (or it may not exist). This command is authenticated as DUMMY_PROJECT_NUMBER-compute@developer.gserviceaccount.com which is the active account specified by the [core/account] property.
gs://ip-allow-all-2025-03/ip-allow-all_2025-03.txt
ERROR: (gcloud.storage.ls) [DUMMY_PROJECT_NUMBER-compute@developer.gserviceaccount.com] does not have permission to access b instance [ip-allow-private-2025-03] (or it may not exist): DUMMY_PROJECT_NUMBER-compute@developer.gserviceaccount.com does not have storage.objects.list access to the Google Cloud Storage bucket. Permission 'storage.objects.list' denied on resource (or it may not exist). This command is authenticated as DUMMY_PROJECT_NUMBER-compute@developer.gserviceaccount.com which is the active account specified by the [core/account] property.

allUsers に対しての許可がないと、ゲスト プロジェクトのサービスアカウントからはアクセスできません。それ以外は、まだ、Bucket IP filtering が有効になっていないため、アクセスできています。

Bucket IP filtering の設定

ip-peering-all-2025-03, ip-peering-private-2025-03 には、ホスト プロジェクトの VPC ネットワーク network-peering-host からのアクセスのみを許可するように、Bucket IP filtering を設定します。
ip-allow-all-2025-03, ip-allow-private-2025-03 には、ホスト プロジェクトの VPC ネットワーク network-peering-host に加えて、特定の IP アドレスからのアクセスのみを許可します。

ip-filter-peering.json を作成します。

cat <<EOF > ip-filter-peering.json
{
  "mode": "Enabled",
  "vpcNetworkSources": [
    {
      "network": "projects/$PROJECT_HOST/global/networks/network-peering-host",
      "allowedIpCidrRanges": [
        "10.0.0.0/16"
      ]
    }
  ]
}
EOF

ip-filter-allow.json を作成します。

cat <<EOF > ip-filter-allow.json
{
  "mode": "Enabled",
  "publicNetworkSource": {
    "allowedIpCidrRanges": [
      "${YOUR_IP}/32"
    ],
  },
  "vpcNetworkSources": [
    {
      "network": "projects/$PROJECT_HOST/global/networks/network-peering-host",
      "allowedIpCidrRanges": [
        "10.0.0.0/16"
      ]
    }
  ]
}
EOF

Bucket IP filtering を設定します。

for bucket in ip-peering-all ip-peering-private; do
  gcloud alpha storage buckets update gs://${bucket}-2025-03 --project=$PROJECT_HOST --ip-filter-file=ip-filter-peering.json
done

for bucket in ip-allow-all ip-allow-private; do
  gcloud alpha storage buckets update gs://${bucket}-2025-03 --project=$PROJECT_HOST --ip-filter-file=ip-filter-allow.json
done

バケットへのアクセス確認その 2

再度、アクセスできるか確認します。今度は、設定者のローカル環境からも確認します。

ゲスト側の VM インスタンスからは、public-2025-03 以外にアクセスできません。VPC Peering を設定しても、限定公開の Google アクセスのエンドポイントに接続するのは「ゲスト側のネットワークからのアクセス」になるため、Bucket IP filtering によってアクセスが制限されます。ホスト側のネットワークに届く前に、Google 側のエンドポイントに接続するような仕組みになっているためです。よく考えてみると当たり前ですね。

ローカル環境からは、ip-peering-private-2025-03ip-allow-private-2025-03 にはアクセスできません。IAM 権限があっても、Bucket IP filtering によってアクセスが制限されます。

よく見るとエラーメッセージが異なっています。IP filtering によってアクセスが拒否された場合、エラーメッセージに IP filtering condition という文言が含まれていますね。

Permission ‘storage.objects.list’ denied on resource (or it may not exist).

There is an IP filtering condition that is preventing access to the resource.

vm-host-peering:

gs://public-2025-03/public_2025-03.txt
gs://private-2025-03/private_2025-03.txt
gs://ip-peering-all-2025-03/ip-peering-all_2025-03.txt
gs://ip-peering-private-2025-03/ip-peering-private_2025-03.txt
gs://ip-allow-all-2025-03/ip-allow-all_2025-03.txt
gs://ip-allow-private-2025-03/ip-allow-private_2025-03.txt

vm-guest-peering:

gs://public-2025-03/public_2025-03.txt
ERROR: (gcloud.storage.ls) [DUMMY_PROJECT_NUMBER-compute@developer.gserviceaccount.com] does not have permission to access b instance [private-2025-03] (or it may not exist): DUMMY_PROJECT_NUMBER-compute@developer.gserviceaccount.com does not have storage.objects.list access to the Google Cloud Storage bucket. Permission 'storage.objects.list' denied on resource (or it may not exist). This command is authenticated as DUMMY_PROJECT_NUMBER-compute@developer.gserviceaccount.com which is the active account specified by the [core/account] property.
ERROR: (gcloud.storage.ls) [DUMMY_PROJECT_NUMBER-compute@developer.gserviceaccount.com] does not have permission to access b instance [ip-peering-all-2025-03] (or it may not exist): There is an IP filtering condition that is preventing access to the resource. This command is authenticated as DUMMY_PROJECT_NUMBER-compute@developer.gserviceaccount.com which is the active account specified by the [core/account] property.
ERROR: (gcloud.storage.ls) [DUMMY_PROJECT_NUMBER-compute@developer.gserviceaccount.com] does not have permission to access b instance [ip-peering-private-2025-03] (or it may not exist): DUMMY_PROJECT_NUMBER-compute@developer.gserviceaccount.com does not have storage.objects.list access to the Google Cloud Storage bucket. Permission 'storage.objects.list' denied on resource (or it may not exist). This command is authenticated as DUMMY_PROJECT_NUMBER-compute@developer.gserviceaccount.com which is the active account specified by the [core/account] property.
ERROR: (gcloud.storage.ls) [DUMMY_PROJECT_NUMBER-compute@developer.gserviceaccount.com] does not have permission to access b instance [ip-allow-all-2025-03] (or it may not exist): There is an IP filtering condition that is preventing access to the resource. This command is authenticated as DUMMY_PROJECT_NUMBER-compute@developer.gserviceaccount.com which is the active account specified by the [core/account] property.
ERROR: (gcloud.storage.ls) [DUMMY_PROJECT_NUMBER-compute@developer.gserviceaccount.com] does not have permission to access b instance [ip-allow-private-2025-03] (or it may not exist): DUMMY_PROJECT_NUMBER-compute@developer.gserviceaccount.com does not have storage.objects.list access to the Google Cloud Storage bucket. Permission 'storage.objects.list' denied on resource (or it may not exist). This command is authenticated as DUMMY_PROJECT_NUMBER-compute@developer.gserviceaccount.com which is the active account specified by the [core/account] property.

local:

gs://public-2025-03/public_2025-03.txt
gs://private-2025-03/private_2025-03.txt
ERROR: (gcloud.storage.ls) [test@example.com] does not have permission to access b instance [ip-peering-all-2025-03] (or it may not exist): There is an IP filtering condition that is preventing access to the resource. This command is authenticated as test@example.com which is the active account specified by the [core/account] property.
ERROR: (gcloud.storage.ls) [test@example.com] does not have permission to access b instance [ip-peering-private-2025-03] (or it may not exist): There is an IP filtering condition that is preventing access to the resource. This command is authenticated as test@example.com which is the active account specified by the [core/account] property.
gs://ip-allow-all-2025-03/ip-allow-all_2025-03.txt
gs://ip-allow-private-2025-03/ip-allow-private_2025-03.txt

追加検証

Private Service Connect バックエンドの構成

Peering 先のネットワークが、もう一方のネットワークの「中」から Google Cloud の API にアクセスするにはどのようにすれば良いでしょうか?VPN を構成しているときにも同様の問題が発生します。

VPC Peering では、推移的なピア接続関係はサポートされないため、Peering 先(ゲスト側)からサブネット外の IP アドレスにアクセスすることはできません。
Private Service Connect エンドポイントを使用して Google API に接続する方法の解説 に、以下のようにあります。

エンドポイントは、ピアリングされた VPC ネットワークからアクセスできません。

バックエンド経由でリージョンの Google API にアクセスする では、リージョンの Google API エンドポイントに対してアクセスする方法が解説されています。また、この方法は、サブネットの中の IP アドレスをエンドポイント IP として使うことができるため、VPC Peering 先からアクセスさせることができます。プレビューとして、クロスリージョン内部アプリケーション ロードバランサを利用して、グローバル Google API にアクセスする方法も解説されているので、そちらも参考にしてみてください。

このブログでは、リージョンの Google API エンドポイントに対してアクセスする方法を解説構成し、接続を確認してみることにします。

細部は省略しますが、以下の手順で構成します。X509 証明書が必要なので先に作成しておきます。
storage.us-central1.rep.googleapis.com は リージョン サービス エンドポイント の一覧にはありませんが、指定することができました。

openssl genrsa -out cert.key 2048
openssl req -new -key cert.key -out cert.csr
openssl x509 -req -days 3650 -in cert.csr -signkey cert.key -out cert.crt
# FQDN は適当に設定してください
gcloud compute network-endpoint-groups create neg-regional \
    --project=$PROJECT_HOST \
    --network-endpoint-type=private-service-connect \
    --psc-target-service=storage.us-central1.rep.googleapis.com \
    --region=us-central1

gcloud compute backend-services create bs-psc \
    --project=$PROJECT_HOST \
    --load-balancing-scheme=INTERNAL_MANAGED \
    --protocol=HTTPS \
    --region=us-central1

gcloud compute backend-services add-backend bs-psc \
    --project=$PROJECT_HOST \
    --network-endpoint-group=neg-regional \
    --region=us-central1

gcloud compute url-maps create bs-psc-map \
    --project=$PROJECT_HOST \
    --default-service=bs-psc \
    --region=us-central1

gcloud compute ssl-certificates create ssl-cert \
    --project=$PROJECT_HOST \
    --region=us-central1 \
    --certificate=cert.crt \
    --private-key=cert.key

gcloud compute target-https-proxies create proxy \
    --project=$PROJECT_HOST \
    --url-map=bs-psc-map \
    --region=us-central1 \
    --ssl-certificates=ssl-cert

gcloud compute addresses create psc-address \
    --project=$PROJECT_HOST \
    --ip-version=IPV4 \
    --region=us-central1 \
    --subnet=subnet-peering-host

gcloud compute networks subnets create proxy-only-subnet \
    --project=$PROJECT_HOST \
    --purpose=REGIONAL_MANAGED_PROXY \
    --role=ACTIVE \
    --region=us-central1 \
    --network=network-peering-host \
    --range=10.0.2.0/24

gcloud compute firewall-rules create fw-allow-proxy-only-subnet \
    --project=$PROJECT_HOST \
    --network=network-peering-host \
    --action=allow \
    --direction=ingress \
    --target-tags=allow-proxy-only-subnet \
    --source-ranges=10.0.2.0/24 \
    --rules=all

gcloud compute forwarding-rules create psc-fwd-rule \
    --project=$PROJECT_HOST \
    --load-balancing-scheme=INTERNAL_MANAGED \
    --network=network-peering-host \
    --subnet=subnet-peering-host \
    --address=psc-address \
    --ports=443 \
    --region=us-central1 \
    --target-https-proxy=proxy \
    --target-https-proxy-region=us-central1

エンドポイントの IP は以下のようにして確認できます。

gcloud compute addresses describe psc-address \
    --project=$PROJECT_HOST \
    --region=us-central1
address: 10.0.0.3
addressType: INTERNAL
creationTimestamp: '2025-03-20T10:00:45.700-07:00'
description: ''
id: '7237678285292959570'
ipVersion: IPV4
kind: compute#address
labelFingerprint: 42WmSpB8rSM=
name: psc-address
networkTier: PREMIUM
purpose: GCE_ENDPOINT
region: https://www.googleapis.com/compute/v1/projects/project-host-2025-03/regions/us-central1
selfLink: https://www.googleapis.com/compute/v1/projects/project-host-2025-03/regions/us-central1/addresses/psc-address
status: IN_USE
subnetwork: https://www.googleapis.com/compute/v1/projects/project-host-2025-03/regions/us-central1/subnetworks/subnet-peering-host
users:
- https://www.googleapis.com/compute/v1/projects/project-host-2025-03/regions/us-central1/forwardingRules/psc-fwd-rule

ここでは、10.0.0.3 がエンドポイントの IP として選択されました。
ゲスト側の VM インスタンスから、このエンドポイントにアクセスすると、リージョンの Google API エンドポイントに「ホスト ネットワーク内から」アクセスできるようになるはずです。

ピアリングを行うと、ピアリング先の IP 範囲が送信先になっていれば、ピアリング先のネットワークに中継するようなルーティングが設定されます。
ネットワーク の「好きな IP 範囲」を特定の「ピアリング先」に手動で向けるような設定はできません。ここでは、10.0.0.3 は peering-guest-host にもちろん含まれているため、転送されます。

バケットへのアクセス確認その 3

vm-guest-peering からいろいろ試してみましょう。storage.googleapis.com へは、先程 gcloud コマンドで試したのと同様、アクセスすることができません。一方で、10.0.0.3 にはアクセスできます。もちろん、ip-peering-private-2025-03allUsers に対して公開されていないので、アクセスできません。
-H "Host: storage.us-central1.rep.googleapis.com" といった指定の有無は関係ないようでした。

curl https://storage.googleapis.com/ip-peering-all-2025-03/ip-peering-all_2025-03.txt
<?xml version='1.0' encoding='UTF-8'?><Error><Code>AccessDenied</Code><Message>Access denied.</Message><Details>There is an IP filtering condition that is preventing access to the resource.</Details></Error>

curl https://us-central1-storage.googleapis.com/ip-peering-all-2025-03/ip-peering-all_2025-03.txt
<?xml version='1.0' encoding='UTF-8'?><Error><Code>AccessDenied</Code><Message>Access denied.</Message><Details>There is an IP filtering condition that is preventing access to the resource.</Details></Error>

curl https://storage.us-central1.rep.googleapis.com/ip-peering-all-2025-03/ip-peering-all_2025-03.txt
# 応答なし

curl -k -H "Host: storage.us-central1.rep.googleapis.com" https://10.0.0.3/ip-peering-all-2025-03/ip-peering-all_2025-03.txt
ip-peering-all_2025-03.txt

curl -k -H "Host: storage.googleapis.com" https://10.0.0.3/ip-peering-private-2025-03/ip-peering-private-2025-03.txt
<?xml version='1.0' encoding='UTF-8'?><Error><Code>AccessDenied</Code><Message>Access denied.</Message><Details>Anonymous caller does not have storage.objects.get access to the Google Cloud Storage object. Permission 'storage.objects.get' denied on resource (or it may not exist).</Details></Error>

以下の形式でもアクセスが可能です。

curl -k -H "Host: storage.googleapis.com" "https://10.0.0.3/storage/v1/b/ip-peering-all-2025-03/o/ip-peering-all_2025-03.txt?alt=media"
ip-peering-all_2025-03.txt

証明書チェックを無効にし、エンドポイントを指定すると gcloud コマンドでもアクセスできます。--log-http オプションを付けると、リクエストとレスポンスの内容を確認できます。

gcloud config set auth/disable_ssl_validation True
Updated property [auth/disable_ssl_validation].
gcloud config set api_endpoint_overrides/storage https://10.0.0.3/
WARNING: The value set for [api_endpoint_overrides/storage] was found to be associated with a universe domain outside of the current config universe [googleapis.com]. Please create a new gcloud configuration for each universe domain you make requests to using `gcloud config configurations create` with the `--universe-domain` flag or switch to a configuration associated with [https://10.0.0.3/].
Are you sure you wish to set property [api_endpoint_overrides/storage] to https://10.0.0.3/?

Do you want to continue (Y/n)?  

Updated property [api_endpoint_overrides/storage].
gcloud storage ls gs://ip-peering-all-2025-03
/usr/bin/../lib/google-cloud-sdk/lib/third_party/urllib3/connectionpool.py:1102: InsecureRequestWarning: Unverified HTTPS request is being made to host '10.0.0.3'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings
  warnings.warn(
gs://ip-peering-all-2025-03/ip-peering-all_2025-03.txt

バケットへのアクセス確認その 4

どのネットワーク経由で Google Cloud API が呼ばれるかがポイントとなっていそうでした。ip-filter-peering_2.json を作成し、ゲスト プロジェクトの VPC ネットワーク network-peering-guest からのアクセスも許可するように、Bucket IP filtering を再設定してみます。

cat <<EOF > ip-filter-peering_2.json
{
  "mode": "Enabled",
  "vpcNetworkSources": [
    {
      "network": "projects/$PROJECT_HOST/global/networks/network-peering-host",
      "allowedIpCidrRanges": [
        "10.0.0.0/16"
      ]
    },
    {
      "network": "projects/$PROJECT_GUEST/global/networks/network-peering-guest",
      "allowedIpCidrRanges": [
        "10.0.1.0/24"
      ]
    }
  ]
}
EOF

これを、vm-host-peering から適用します。「バケットの操作」そのものが制限されている状態なので、許可されているネットワーク内から操作する必要があります。

for bucket in ip-peering-all ip-peering-private; do
  gcloud alpha storage buckets update gs://${bucket}-2025-03 --project=$PROJECT_HOST --ip-filter-file=ip-filter-peering_2.json
done

もし、何らかの事情で、バケットに対して操作するネットワークへの接続方法を失った場合には、「バイパスロール」という仕組みが用意されています。バケットの IP フィルタリング ルールをバイパスする を参照してください。IP filtering の設定を無効化するか、上書きすることによって、権限を回復することができます。

さて、再度 vm-guest-peering からのアクセスを試します。vm-guest-peering が属するネットワーク network-peering-guest からのアクセスも許可されている状態です。gcloud コマンドの証明書チェックとエンドポイントの指定を無効化します。

gcloud config unset auth/disable_ssl_validation
gcloud config unset api_endpoint_overrides/storage

その後、以下のように、gcloud コマンドでも、curl コマンドでも、アクセスできることが確認できます。

gcloud storage ls gs://ip-peering-all-2025-03
gs://ip-peering-all-2025-03/ip-peering-all_2025-03.txt
curl https://storage.googleapis.com/ip-peering-all-2025-03/ip-peering-all_2025-03.txt
ip-peering-all_2025-03.txt
curl https://us-central1-storage.googleapis.com/ip-peering-all-2025-03/ip-peering-all_2025-03.txt
ip-peering-all_2025-03.txt

まとめと考察

今回の調査を通じて分かったことをまとめてみます。

  • IAM で許可されていても、Bucket IP filtering の設定によってアクセスを制限することができる
  • 別のプロジェクトのネットワークに対しても、Bucket IP filtering を設定し、アクセスを許可することができる
  • Private Service Connect を利用することで、リージョンの Google API エンドポイントに対して、ネットワーク内からアクセスすることができる

Bucket IP filtering は IAM の権限と「両方が揃った場合」に初めてアクセスが許可される仕組みです。認可条件の 1要素が追加されたと考えると、セキュリティ強化に一役買うでしょう。

共有 VPC や VPC Peering でネットワークが接続されている場合、サービスアカウントに権限を付与しても、特定のネットワークだけからアクセスできるようにすることもできます。
あるいは、サービス側の個別のプロジェクトについて、サービスアカウントに権限を付与しなくても、ネットワークの設定によってアクセスを制御することができます。
Bucket IP filtering が有効に働きそうなユースケースとして、以下を挙げていました。

  • 共有 VPC を使っている
  • VPC Peering を用いており、接続先が多い
  • オンプレミス環境と VPN を構成しており、多数のクライアントがいる
  • 多拠点にクライアント PC があり、特定 IP のみからアクセスできるバケットを作成したい

共有 VPC や VPC Peering を用いている場合には、サービス アカウントの制御と、ネットワークの制御を組み合わせて、セキュリティを強化することができます。もちろん、サービス アカウントの制御を緩く、ネットワークの制御を強くすることで、面倒な、サービス アカウントを管理する手間を減らすこともできそうです。

今回の調査には含まれていませんが、Private Service Connect エンドポイントを使用して Google API に接続する方法や、オンプレミス ホスト用の限定公開の Google アクセス を利用すると、オンプレミス ネットワークと Cloud VPN または Cloud Interconnect で接続している場合に、Bucket IP filtering によるアクセス許可を適用することができそうです。もしうまくいかなくても、今回のロードバランサを介する方法を使うことで、サービス アカウントの権限を持たないクライアントからのアクセスを許可することができそうです。IAM では allUsers への公開を行いつつ Bucket IP filtering を設定すると、プライベート IP の範囲からのアクセスだけを許可することができます。

グローバル IP を個別許可することもできるため、拠点の外部 IP を登録することで、拠点内のクライアント PC からのアクセスだけを許可することもできそうです。


採用情報
お問い合わせ