在网址映射中创建自定义标头

本页面介绍了自定义标头在区域级外部应用负载均衡器、区域级内部应用负载均衡器和跨区域内部应用负载均衡器使用的网址映射中的工作原理。

使用自定义请求和响应标头,您可以指定负载均衡器可以添加到 HTTP(S) 请求和响应中的额外标头。根据负载均衡器检测到的信息,这些标头可以包含以下信息:

  • 客户端的延迟时间
  • 客户端 IP 地址的地理位置
  • TLS 连接的参数

准备工作

如有必要,请更新到 Google Cloud CLI 的最新版本:

gcloud components update

自定义标头的工作原理

自定义标头的工作原理如下:

  • 当负载均衡器向后端发出请求时,负载均衡器会添加请求标头。

    负载均衡器仅会向客户端请求添加自定义请求标头,而不会向健康检查探测添加自定义请求标头。如果后端需要将特定标头用于授权,而健康检查数据包中缺少该标头,则健康检查可能会失败。

  • 相应地,负载均衡器在向客户端返回响应之前,会先设置响应标头。

如需为区域级外部应用负载均衡器、区域级内部应用负载均衡器和跨区域内部应用负载均衡器启用自定义标头,请在网址映射配置文件中指定标头名称和标头值列表。

标头名称必须满足以下要求:

  • 标头名称必须是有效的 HTTP 标头字段名称定义(根据 RFC 7230)。
  • 标头名称不得为 X-User-IP
  • 标头名称不得以 X-GoogleX-Goog-X-GFEX-Amz- 开头。
  • 标头名称不得为 HostauthorityHostauthority 都是 Google Cloud 预留的特殊关键字。您无法为基于 Envoy 的负载均衡器修改这些标头。我们建议您创建其他自定义标头(例如 MyHost),以免干扰预留的标头名称。
  • 同一个标头名称不能在标头列表中出现多次。

标头名称不区分大小写。当标头名称传递至 HTTP/2 后端时,HTTP/2 协议会将标头名称编码为小写。

标头值必须满足以下要求:

  • 标头值必须是有效的 HTTP 标头字段定义(根据 RFC 7230,不允许使用已过时的格式)。
  • 标头值不能为空;空标头会被拒绝。
  • 标头值可以包含一个或多个由花括号括起来的变量,这些变量可扩展为负载均衡器提供的值。如需查看标头值中允许使用的变量的完整列表,请参阅可以出现在标头值中的变量

在标头值中,前导空格和尾随空格无意义,因此不会传递至后端。为了允许在标头值中使用花括号,负载均衡器会将两个左花括号 ({{) 解释为一个左花括号 ({),而将两个右花括号 (}}) 解释为一个右花括号 (})。

添加请求或响应标头

如需添加请求或响应标头,请使用 gcloud CLI 修改网址映射,如下所示:

区域级

    gcloud compute url-maps edit URL_MAP_NAME \
        --region=REGION
    

下面的示例 YAML 文件展示了如何在自定义标头中使用变量:

   defaultService: regions/REGION/backendServices/BACKEND_SERVICE_1
   name: regional-lb-map
   region: region/REGION
   hostRules:
   - hosts:
     - '*'
     pathMatcher: matcher1
   pathMatchers:
   - defaultService: regions/REGION/backendServices/BACKEND_SERVICE_1
     name: matcher1
     routeRules:
       - matchRules:
           - prefixMatch: /PREFIX
         priority: PRIORITY # 0 is highest
         routeAction:
           weightedBackendServices:
             - backendService: regions/REGION/backendServices/BACKEND_SERVICE_1
               weight: 100
               headerAction:
                 requestHeadersToAdd:
                 - headerName: X-header-1-client-region
                   headerValue: "{client_region}"
                 - headerName: X-header-2-client-ip-port
                   headerValue: "{client_ip_address}, {client_port}"
                   replace: True
                 requesteHeadersToRemove:
                 - header-3-name
                 responseHeadersToAdd:
                 - headerName: X-header-4-server-ip-port
                   headerValue: "{server_ip_address}, {server_port}"
                   replace: True
                 responseHeadersToRemove:
                 - header-5-name
                 - header-6-name
    

跨区域

    gcloud compute url-maps edit URL_MAP_NAME \
        --global
    

下面的示例 YAML 文件展示了如何在自定义标头中使用变量:

   defaultService: global/backendServices/BACKEND_SERVICE_1
   name: global-lb-map
   hostRules:
   - hosts:
     - '*'
     pathMatcher: matcher1
   pathMatchers:
   - defaultService: global/backendServices/BACKEND_SERVICE_1
     name: matcher1
     routeRules:
       - matchRules:
           - prefixMatch: /PREFIX
         priority: PRIORITY # 0 is highest
         routeAction:
           weightedBackendServices:
             - backendService: global/backendServices/BACKEND_SERVICE_1
               weight: 100
               headerAction:
                 requestHeadersToAdd:
                 - headerName: X-header-1-client-region
                   headerValue: "{client_region}"
                 - headerName: X-header-2-client-ip-port
                   headerValue: "{client_ip_address}, {client_port}"
                   replace: True
                 requesteHeadersToRemove:
                 - header-3-name
                 responseHeadersToAdd:
                 - headerName: X-header-4-server-ip-port
                   headerValue: "{server_ip_address}, {server_port}"
                   replace: True
                 responseHeadersToRemove:
                 - header-5-name
                 - header-6-name
    

请注意以下行为:

  • 如果包含自定义变量的响应标头被解析为空字符串,则该标头会被移除。
  • 如果包含自定义变量的请求标头被解析为空字符串,则该标头会被保留,但会有一个空字符串占位符。
  • 如果自定义请求标头包含自定义变量,并且传入客户端请求也包含相同的标头,则客户端请求标头值将被替换为负载均衡器的自定义标头提供的新值。

可以出现在标头值中的变量

以下变量可以出现在自定义标头值中。

变量 说明
client_region 与客户端 IP 地址相关联的国家/地区(或区域)。这是一个 Unicode CLDR 区域代码,例如 USFR。(对于大多数国家/地区,这些代码直接对应于 ISO-3166-2 代码。)
client_rtt_msec 负载均衡器与 HTTP(S) 客户端之间的预计往返传输时间,以毫秒为单位。这是负载均衡器的 TCP 堆栈根据 RFC 2988 测量的平滑往返时间 (SRTT) 参数。 平滑 RTT 是一种算法,用于处理 RTT 测量中可能发生的变体和异常。
client_ip_address 客户端的 IP 地址。除非客户端使用代理,或者 X-Forwarded-For 标头已被篡改,否则这通常与 X-Forwarded-For 标头中属于倒数第二个地址的客户端 IP 地址相同。
client_port 客户端的来源端口。
client_encrypted 如果客户端和负载均衡器之间的连接已加密(使用 HTTPS、HTTP/2 或 HTTP/3),则为 true;否则为 false
client_protocol 用于客户端和负载均衡器之间通信的 HTTP 协议。HTTP/1.0HTTP/1.1HTTP/2HTTP/3 之一。
origin_request_header 反映的是请求中跨域资源共享 (CORS) 用例的 Origin 标头的值。
server_ip_address 客户端所连接的负载均衡器的 IP 地址。这在多个负载均衡器共享公共后端时非常有用。这与 X-Forwarded-For 标头中的最后一个 IP 地址相同。
server_port 客户端所连接的目标端口号。
tls_sni_hostname 由客户端在 TLS 或 QUIC 握手期间提供的服务器名称指示(如 RFC 6066 中所定义)。系统会将主机名转换为小写字母并移除结尾的任何英文句点。
tls_version 客户端与负载均衡器在 SSL 握手期间协商的 TLS 版本。可能的值包括 TLSv1TLSv1.1TLSv1.2TLSv1.3。如果客户端使用 QUIC(而不是 TLS)进行连接,则值将为 QUIC
tls_cipher_suite 在 TLS 握手期间协商的加密套件。该值是由 IANA TLS 加密套件注册系统定义的四位十六进制数字,例如表示 TLS_RSA_WITH_AES_128_GCM_SHA256 的 009C。对于 QUIC 和未加密的客户端连接,此值为空。
tls_ja3_fingerprint JA3 TLS/SSL 指纹(如果客户端使用 HTTPS、HTTP/2 或 HTTP/3 进行连接)。

负载均衡器在无法确定变量值时会将变量扩展为空字符串。例如:

  • IP 地址的位置未知时的地理位置变量
  • 未使用 TLS 时的 TLS 参数
  • 请求不包含 Origin 标头时的 {origin_request_header}

地理位置值是根据客户端 IP 地址估算的值。Google 会不时地更新提供这些值的数据,以便提高准确性并反映地理和政治方面的变化。 即使原始 X-Forwarded-For 标头包含有效的位置信息,Google 也会使用负载均衡器收到的数据包中包含的来源 IP 地址信息来估算客户端位置。

双向 TLS 自定义标头

如果在负载均衡器的 TargetHttpsProxy 上配置了双向 TLS (mTLS),则可以使用以下额外的标头变量。

变量 说明
client_cert_present 如果客户端在 TLS 握手期间提供了证书,则为 true;否则为 false
client_cert_chain_verified 如果根据配置的 TrustStore 验证了客户端证书链,则为 true;否则为 false
client_cert_error 代表错误情况的预定义字符串。如需详细了解错误字符串,请参阅 mTLS 客户端验证模式
client_cert_sha256_fingerprint 客户端证书的 Base64 编码 SHA-256 指纹。
client_cert_serial_number 客户端证书的序列号。 如果序列号长度超过 50 字节,则将字符串 client_cert_serial_number_exceeded_size_limit 添加到 client_cert_error,并将序列号设置为空字符串。
client_cert_spiffe_id

主题备用名称 (SAN) 字段中的 SPIFFE ID。如果值无效或超过 2048 字节,则 SPIFFE ID 设置为空字符串。

如果 SPIFFE ID 长度超过 2048 字节,则将字符串 client_cert_spiffe_id_exceeded_size_limit 添加到 client_cert_error

client_cert_uri_sans

URI 类型 SAN 扩展的 Base64 编码列表(以逗号分隔)。 SAN 扩展从客户端证书中提取。 client_cert_uri_sans 字段中不包含 SPIFFE ID。

如果 client_cert_uri_sans 长度超过 512 字节,则将字符串 client_cert_uri_sans_exceeded_size_limit 添加到 client_cert_error,并将英文逗号分隔列表设置为空字符串。

client_cert_dnsname_sans

DNSName 类型 SAN 扩展的 Base64 编码列表(以逗号分隔)。SAN 扩展从客户端证书中提取。

如果 client_cert_dnsname_sans 长度超过 512 字节,则将字符串 client_cert_dnsname_sans_exceeded_size_limit 添加到 client_cert_error,并将英文逗号分隔列表设置为空字符串。

client_cert_valid_not_before 在此时间戳(RFC 3339 日期字符串格式)之前,客户端证书无效。 例如 2022-07-01T18:05:09+00:00
client_cert_valid_not_after 在此时间戳(RFC 3339 日期字符串格式)之后,客户端证书无效。 例如 2022-07-01T18:05:09+00:00
client_cert_issuer_dn

证书中采用 Base64 编码的完整“颁发者”字段。

如果 client_cert_issuer_dn 长度超过 512 字节,则将字符串 client_cert_issuer_dn_exceeded_size_limit 添加到 client_cert_error,并将 client_cert_issuer_dn 设置为空字符串。

client_cert_subject_dn

证书中采用 Base64 编码的完整“主题”字段。

如果 client_cert_subject_dn 长度超过 512 字节,则将字符串 client_cert_subject_dn_exceeded_size_limit 添加到 client_cert_error,并将 client_cert_subject_dn 设置为空字符串。

client_cert_leaf

已建立的 mTLS 连接(证书在其中通过了验证)的客户端叶证书。证书编码符合 RFC 9440 标准:二进制 DER 证书使用 Base64 编码(不含换行符、空格或 Base64 字母表之外的其他字符),两侧都用英文冒号分隔。

如果 client_cert_leaf 超过 16 KB(未编码),则将字符串 client_cert_validated_leaf_exceeded_size_limit 添加到 client_cert_error,并将 client_cert_leaf 设置为空字符串。

client_cert_chain

已建立的 mTLS 连接(客户端证书在其中通过了验证)的客户端证书链的英文逗号分隔证书列表(按标准 TLS 顺序排列),不包括叶证书。证书编码符合 RFC 9440 标准。

如果 client_cert_leafclient_cert_chain 的总大小在 Base64 编码之前超过 16 KB,则将字符串 client_cert_validated_chain_exceeded_size_limit 添加到 client_cert_error,并将 client_cert_chain 设置为空字符串。

限制

以下限制适用于在区域级负载均衡器中使用自定义标头的情况:

  • 您不能在区域级外部应用负载均衡器和区域级内部应用负载均衡器使用的区域级后端服务以及跨区域内部应用负载均衡器使用的全球后端服务上配置自定义标头。
  • 区域级外部应用负载均衡器不支持以下自定义标头变量:
    • cdn_cache_id
    • cdn_cache_status
    • client_region_subdivision
    • client_city
    • client_city_lat_long