配置多个使用相同 TLS 证书的网关时出现 404 错误

目录

问题描述

现象

通过 Istio Ingress Gateway 使用 HTTP/2 协议访问服务时,出现 404 错误。这是 Istio Community 中的已知问题。

根本原因

配置多个网关使用相同的 TLS 证书时,HTTP/2 浏览器在建立初始连接后访问次要主机会产生 404 错误。这是由于浏览器中 HTTP/2 连接复用导致的。

示例场景

  • 域名 a.example.comb.example.com 共享相同的 TLS 证书
  • 分别配置在不同的 Gateway 资源中
  • 浏览器通过同一连接先访问 a.example.com,再访问 b.example.com

故障排查

验证脚本

在部署 Istio Ingress Gateway 的集群主节点上执行此脚本:

#!/bin/bash
nslist=$(kubectl get ns -o jsonpath='{.items[*].metadata.name}')
declare -A cred_map

echo "begin to check gw"
for ns in $nslist; do
  gateways=$(kubectl get gw -n $ns -o jsonpath='{.items[*].metadata.name}')
  for gateway in $gateways; do
    gateway_yaml=$(kubectl get gw -n $ns $gateway -o yaml)
    secname=$(echo "$gateway_yaml" | grep 'credentialName:' | awk '{print $2}')

    if [[ -n "$secname" ]]; then
      if [[ -n "${cred_map[$secname]}" ]]; then
        echo -e "\033[31m Duplicate TLS detected in gateway: ${cred_map[$secname]} \033[0m"
        hosts=$(kubectl get gw -n $ns $gateway -o json | jq -r '.spec.servers[] | .hosts[]')
        echo -e "\033[31m Conflict gateway: $gateway in $ns \033[0m"
        echo "Affected hosts: $hosts"
      else
        cred_map["$secname"]="$gateway~$ns"
      fi
    fi
  done
done

预期输出

Duplicate TLS detected in gateway: drawdb-gateway~drawdb
Conflict gateway: authory-gateway in nm-edu-authory
Affected hosts: rzzx-test.jiaxiurc.com

根因解决方案 1:合并 Gateway 资源

注意事项

  • 这是社区推荐的解决方案
  • 保持 HTTP/2 性能优势
  • 需要修改现有 Gateway 配置

前提条件

  1. 集群节点安装了 jq v1.7 及以上版本
  2. 具备使用 kubectl 的集群访问权限

操作步骤

  1. 使用验证脚本识别冲突的 Gateway
  2. 合并 Host 配置
    apiVersion: networking.istio.io/v1beta1
    kind: Gateway
    metadata:
      name: unified-gateway
      namespace: istio-system
    spec:
      selector:
        istio: ingressgateway
      servers:
      - hosts:
          - "a.example.com"
          - "b.example.com"
        tls:
          mode: SIMPLE
          credentialName: shared-cert
        port:
          name: https
          number: 443
          protocol: HTTPS
  3. 更新 VirtualServices 以引用合并后的 Gateway
  4. 删除冗余的 Gateway
  5. 验证配置
    kubectl get gw -A
    istioctl analyze

根因解决方案 2:使用 421 响应码

注意事项

  • 需要客户端支持 421 状态码
  • 兼容 Chrome/Firefox/Safari 15.1 及以上版本

前提条件

  1. Istio 版本 ≥ 1.12
  2. 具备集群管理员权限

操作步骤

  1. 应用 EnvoyFilter
    apiVersion: networking.istio.io/v1alpha3
    kind: EnvoyFilter
    metadata:
      name: misdirected-request
      namespace: istio-system
    spec:
      configPatches:
        - applyTo: HTTP_FILTER
          match:
            context: GATEWAY
            listener:
              filterChain:
                filter:
                  name: envoy.filters.network.http_connection_manager
          patch:
            operation: INSERT_BEFORE
            value:
              name: envoy.lua
              typed_config:
                "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
                inlineCode: |
                  function envoy_on_request(request_handle)
                    local authority = request_handle:headers():get(":authority")
                    local sni = request_handle:streamInfo().requestedServerName
    
                    if sni ~= "" and sni ~= authority:match("([^:]+)") then
                      request_handle:respond({[":status"] = "421"}, "Misdirected Request")
                    end
                  end
  2. 验证实现
    curl -I -H "Host: b.example.com" https://gateway-ip

预防措施

  1. 证书管理
    • 使用通配符证书(*.example.com)
    • 避免跨环境复用证书
  2. 网关设计
    • 每个域名模式实现单一网关
    • 使用基于命名空间的证书隔离
  3. 定期审计
    istioctl experimental precheck
    kubectl get secret --all-namespaces -o json | jq '.items[].metadata.name' | sort | uniq -d

相关内容

HTTP/2 连接复用机制

  • 单个 TLS 连接处理多个请求
  • 服务器使用 SNI 路由请求
  • SNI 头不匹配导致路由失败

Istio 文档参考Istio Common Problems - 404 Errors