配置多个使用相同 TLS 证书的网关时出现 404 错误
目录
问题描述
现象
通过 Istio Ingress Gateway 使用 HTTP/2 协议访问服务时,出现 404 错误。这是 Istio Community 中的已知问题。
根本原因
配置多个网关使用相同的 TLS 证书时,HTTP/2 浏览器在建立初始连接后访问次要主机会产生 404 错误。这是由于浏览器中 HTTP/2 连接复用导致的。
示例场景:
- 域名
a.example.com
和 b.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 配置
前提条件
- 集群节点安装了
jq
v1.7 及以上版本
- 具备使用 kubectl 的集群访问权限
操作步骤
- 使用验证脚本识别冲突的 Gateway
- 合并 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
- 更新 VirtualServices 以引用合并后的 Gateway
- 删除冗余的 Gateway
- 验证配置:
kubectl get gw -A
istioctl analyze
根因解决方案 2:使用 421 响应码
注意事项
- 需要客户端支持 421 状态码
- 兼容 Chrome/Firefox/Safari 15.1 及以上版本
前提条件
- Istio 版本 ≥ 1.12
- 具备集群管理员权限
操作步骤
- 应用 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
- 验证实现:
curl -I -H "Host: b.example.com" https://gateway-ip
预防措施
- 证书管理:
- 使用通配符证书(*.example.com)
- 避免跨环境复用证书
- 网关设计:
- 每个域名模式实现单一网关
- 使用基于命名空间的证书隔离
- 定期审计:
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