Ошибки 404 при конфигурации нескольких шлюзов с одним и тем же TLS-сертификатом
Содержание
Описание проблемы
Симптомы
При обращении к сервисам через Istio Ingress Gateway с использованием протокола HTTP/2 возникают ошибки 404. Это известная проблема в сообществе Istio.
Корневая причина
Конфигурация нескольких шлюзов с одним и тем же 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
версии 1.7 и выше на узлах кластера
- Доступ к кластеру с правами kubectl
Шаги
- Определить конфликтующие Gateways с помощью скрипта проверки
- Объединить конфигурации 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
- Удалить избыточные Gateways
- Проверить конфигурацию:
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
Профилактические меры
- Управление сертификатами:
- Использовать wildcard-сертификаты (*.example.com)
- Избегать повторного использования сертификатов в разных окружениях
- Проектирование Gateway:
- Реализовать один шлюз на шаблон домена
- Использовать изоляцию сертификатов по namespace
- Регулярные аудиты:
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