feat: Enhance SSL passthrough support (#3943)

Signed-off-by: zijiren233 <pyh1670605849@gmail.com>
This commit is contained in:
zijiren
2026-06-22 21:06:42 +08:00
committed by GitHub
parent f060c9f51d
commit 9c13b6418c
14 changed files with 3178 additions and 46 deletions

View File

@@ -46,6 +46,8 @@ import (
"istio.io/istio/pkg/log"
"istio.io/istio/pkg/util/sets"
v1 "k8s.io/api/core/v1"
networkingv1 "k8s.io/api/networking/v1"
networkingv1beta1 "k8s.io/api/networking/v1beta1"
listersv1 "k8s.io/client-go/listers/core/v1"
"k8s.io/client-go/tools/cache"
@@ -438,6 +440,7 @@ func (m *IngressConfig) convertGateways(configs []common.WrapperConfig) []config
if err != nil {
IngressLog.Errorf("Get higress https configmap err %v", err)
}
m.preparePassthroughTLSHostOwners(&convertOptions, configs)
for idx := range configs {
cfg := configs[idx]
clusterId := common.GetClusterId(cfg.Config.Annotations)
@@ -504,6 +507,8 @@ func (m *IngressConfig) convertVirtualService(configs []common.WrapperConfig) []
}
}
m.preparePassthroughTLSHostOwners(&convertOptions, configs)
// convert http route
for idx := range configs {
cfg := configs[idx]
@@ -570,13 +575,8 @@ func (m *IngressConfig) convertVirtualService(configs []common.WrapperConfig) []
m.ingressRouteCache = convertOptions.IngressRouteCache.Extract()
m.mutex.Unlock()
// Convert http route to virtual service
out := make([]config.Config, 0, len(convertOptions.HTTPRoutes))
for host, routes := range convertOptions.HTTPRoutes {
if len(routes) == 0 {
continue
}
out := make([]config.Config, 0, len(convertOptions.VirtualServices))
for host, wrapperVS := range convertOptions.VirtualServices {
cleanHost := common.CleanHost(host)
// namespace/name, name format: (istio cluster id)-host
gateways := []string{
@@ -585,13 +585,10 @@ func (m *IngressConfig) convertVirtualService(configs []common.WrapperConfig) []
common.CreateConvertedName(constants.IstioIngressGatewayName, cleanHost),
}
wrapperVS, exist := convertOptions.VirtualServices[host]
if !exist {
IngressLog.Warnf("virtual service for host %s does not exist.", host)
}
vs := wrapperVS.VirtualService
vs.Gateways = gateways
routes := convertOptions.HTTPRoutes[host]
// Sort, exact -> prefix -> regex
common.SortHTTPRoutes(routes)
@@ -599,14 +596,18 @@ func (m *IngressConfig) convertVirtualService(configs []common.WrapperConfig) []
vs.Http = append(vs.Http, route.HTTPRoute)
}
firstRoute := routes[0]
if len(vs.Http) == 0 && len(vs.Tls) == 0 {
continue
}
vsName, clusterId := virtualServiceNameAndClusterID(cleanHost, wrapperVS, routes)
out = append(out, config.Config{
Meta: config.Meta{
GroupVersionKind: gvk.VirtualService,
Name: common.CreateConvertedName(constants.IstioIngressGatewayName, firstRoute.WrapperConfig.Config.Namespace, firstRoute.WrapperConfig.Config.Name, cleanHost),
Name: vsName,
Namespace: m.namespace,
Annotations: map[string]string{
common.ClusterIdAnnotation: firstRoute.ClusterId.String(),
common.ClusterIdAnnotation: clusterId.String(),
},
},
Spec: vs,
@@ -625,6 +626,129 @@ func (m *IngressConfig) convertVirtualService(configs []common.WrapperConfig) []
return out
}
func virtualServiceNameAndClusterID(cleanHost string, wrapperVS *common.WrapperVirtualService, routes []*common.WrapperHTTPRoute) (string, cluster.ID) {
if len(routes) > 0 {
firstRoute := routes[0]
return common.CreateConvertedName(constants.IstioIngressGatewayName, firstRoute.WrapperConfig.Config.Namespace, firstRoute.WrapperConfig.Config.Name, cleanHost), firstRoute.ClusterId
}
cfg := wrapperVS.WrapperConfig.Config
return common.CreateConvertedName(constants.IstioIngressGatewayName, cfg.Namespace, cfg.Name, cleanHost), common.GetClusterId(cfg.Annotations)
}
func (m *IngressConfig) preparePassthroughTLSHostOwners(convertOptions *common.ConvertOptions, configs []common.WrapperConfig) {
if convertOptions.PassthroughTLSHostOwners == nil {
convertOptions.PassthroughTLSHostOwners = map[string]*config.Config{}
}
// ingress-nginx enables SSL passthrough at host level when any ingress for the host has the
// annotation, then uses the first root path as the passthrough backend.
passthroughHosts := map[string]struct{}{}
firstRootPathHostOwners := map[string]*config.Config{}
for idx := range configs {
cfg := configs[idx]
if cfg.AnnotationsConfig.IsCanary() {
continue
}
if cfg.AnnotationsConfig.IsSSLPassthrough() {
for _, host := range ingressRuleHosts(cfg.Config.Spec) {
passthroughHosts[host] = struct{}{}
}
}
for _, host := range ingressRootPathHosts(cfg.Config.Spec) {
if _, exist := firstRootPathHostOwners[host]; exist {
continue
}
firstRootPathHostOwners[host] = cfg.Config
}
}
for host := range passthroughHosts {
if owner := firstRootPathHostOwners[host]; owner != nil {
convertOptions.PassthroughTLSHostOwners[host] = owner
}
}
}
func ingressRuleHosts(spec config.Spec) []string {
switch ingressSpec := spec.(type) {
case networkingv1.IngressSpec:
return ingressV1RuleHosts(ingressSpec.Rules)
case networkingv1beta1.IngressSpec:
return ingressV1Beta1RuleHosts(ingressSpec.Rules)
default:
return nil
}
}
func ingressRootPathHosts(spec config.Spec) []string {
switch ingressSpec := spec.(type) {
case networkingv1.IngressSpec:
return ingressV1RootPathHosts(ingressSpec.Rules)
case networkingv1beta1.IngressSpec:
return ingressV1Beta1RootPathHosts(ingressSpec.Rules)
default:
return nil
}
}
func ingressV1RuleHosts(rules []networkingv1.IngressRule) []string {
out := make([]string, 0, len(rules))
for _, rule := range rules {
out = append(out, rule.Host)
}
return out
}
func ingressV1Beta1RuleHosts(rules []networkingv1beta1.IngressRule) []string {
out := make([]string, 0, len(rules))
for _, rule := range rules {
out = append(out, rule.Host)
}
return out
}
func ingressV1RootPathHosts(rules []networkingv1.IngressRule) []string {
out := make([]string, 0, len(rules))
for _, rule := range rules {
if rule.HTTP == nil || !hasV1RootHTTPIngressPath(rule.HTTP.Paths) {
continue
}
out = append(out, rule.Host)
}
return out
}
func ingressV1Beta1RootPathHosts(rules []networkingv1beta1.IngressRule) []string {
out := make([]string, 0, len(rules))
for _, rule := range rules {
if rule.HTTP == nil || !hasV1Beta1RootHTTPIngressPath(rule.HTTP.Paths) {
continue
}
out = append(out, rule.Host)
}
return out
}
func hasV1RootHTTPIngressPath(paths []networkingv1.HTTPIngressPath) bool {
for _, path := range paths {
if path.Path == "" || path.Path == "/" {
return true
}
}
return false
}
func hasV1Beta1RootHTTPIngressPath(paths []networkingv1beta1.HTTPIngressPath) bool {
for _, path := range paths {
if path.Path == "" || path.Path == "/" {
return true
}
}
return false
}
func (m *IngressConfig) convertEnvoyFilter(convertOptions *common.ConvertOptions) {
var envoyFilters []config.Config
mappings := map[string]*common.Rule{}