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

@@ -337,6 +337,13 @@ func extractTLSSecretName(host string, tls []ingress.IngressTLS) string {
}
func (c *controller) ConvertGateway(convertOptions *common.ConvertOptions, wrapper *common.WrapperConfig, httpsCredentialConfig *cert.Config) error {
if convertOptions == nil {
return fmt.Errorf("convertOptions is nil")
}
if wrapper == nil {
return fmt.Errorf("wrapperConfig is nil")
}
// Ignore canary config.
if wrapper.AnnotationsConfig.IsCanary() {
return nil
@@ -382,7 +389,7 @@ func (c *controller) ConvertGateway(convertOptions *common.ConvertOptions, wrapp
Protocol: string(protocol.HTTP),
Name: common.CreateConvertedName("http-"+strconv.FormatUint(uint64(c.options.GatewayHttpPort), 10)+"-ingress", string(c.options.ClusterId)),
},
Hosts: []string{rule.Host},
Hosts: []string{common.WildcardHost(rule.Host)},
})
// Add new gateway, builder
@@ -395,6 +402,45 @@ func (c *controller) ConvertGateway(convertOptions *common.ConvertOptions, wrapp
}
}
passthroughOwner := common.PassthroughTLSHostOwner(convertOptions, rule.Host)
standaloneSSLPassthrough := convertOptions.PassthroughTLSHostOwners == nil && wrapper.AnnotationsConfig.IsSSLPassthrough()
if common.SameConfig(passthroughOwner, cfg) || standaloneSSLPassthrough {
if rule.HTTP == nil || len(rule.HTTP.Paths) == 0 {
continue
}
if _, ok := rootHTTPIngressPath(rule.HTTP.Paths); !ok {
continue
}
domainBuilder.Protocol = common.HTTPS
if wrapperGateway.IsHTTPS() {
if common.SameConfig(preDomainBuilder.Ingress, cfg) {
continue
}
domainBuilder.Event = common.DuplicatedTls
domainBuilder.PreIngress = preDomainBuilder.Ingress
convertOptions.IngressDomainCache.Invalid = append(convertOptions.IngressDomainCache.Invalid,
domainBuilder.Build())
continue
}
wrapperGateway.Gateway.Servers = append(wrapperGateway.Gateway.Servers,
common.CreateSSLPassthroughServer(rule.Host, c.options.GatewayHttpsPort, c.options.ClusterId))
convertOptions.IngressDomainCache.Valid[rule.Host] = domainBuilder
continue
}
if wrapper.AnnotationsConfig.IsSSLPassthrough() {
if rule.HTTP != nil {
if _, ok := rootHTTPIngressPath(rule.HTTP.Paths); ok && passthroughOwner != nil {
domainBuilder.Protocol = common.HTTPS
domainBuilder.Event = common.DuplicatedTls
domainBuilder.PreIngress = passthroughOwner
convertOptions.IngressDomainCache.Invalid = append(convertOptions.IngressDomainCache.Invalid,
domainBuilder.Build())
}
}
continue
}
// There are no tls settings, so just skip.
if len(ingressV1.TLS) == 0 {
continue
@@ -443,6 +489,14 @@ func (c *controller) ConvertGateway(convertOptions *common.ConvertOptions, wrapp
domainBuilder.Protocol = common.HTTPS
domainBuilder.SecretName = path.Join(c.options.ClusterId.String(), cfg.Namespace, secretName)
if passthroughOwner != nil {
domainBuilder.Event = common.DuplicatedTls
domainBuilder.PreIngress = passthroughOwner
convertOptions.IngressDomainCache.Invalid = append(convertOptions.IngressDomainCache.Invalid,
domainBuilder.Build())
continue
}
// There is a matching secret and the gateway has already a tls secret.
// We should report the duplicated tls secret event.
if wrapperGateway.IsHTTPS() {
@@ -460,7 +514,7 @@ func (c *controller) ConvertGateway(convertOptions *common.ConvertOptions, wrapp
Protocol: string(protocol.HTTPS),
Name: common.CreateConvertedName("https-"+strconv.FormatUint(uint64(c.options.GatewayHttpsPort), 10)+"-ingress", string(c.options.ClusterId)),
},
Hosts: []string{rule.Host},
Hosts: []string{common.WildcardHost(rule.Host)},
Tls: &networking.ServerTLSSettings{
Mode: networking.ServerTLSSettings_SIMPLE,
CredentialName: credentials.ToKubernetesIngressResource(c.options.RawClusterId, secretNamespace, secretName),
@@ -475,12 +529,32 @@ func (c *controller) ConvertGateway(convertOptions *common.ConvertOptions, wrapp
}
func (c *controller) ConvertHTTPRoute(convertOptions *common.ConvertOptions, wrapper *common.WrapperConfig) error {
if convertOptions == nil {
return fmt.Errorf("convertOptions is nil")
}
if wrapper == nil {
return fmt.Errorf("wrapperConfig is nil")
}
// Canary ingress will be processed in the end.
if wrapper.AnnotationsConfig.IsCanary() {
convertOptions.CanaryIngresses = append(convertOptions.CanaryIngresses, wrapper)
return nil
}
if convertOptions.Route2Ingress == nil {
convertOptions.Route2Ingress = map[string]*common.WrapperConfigWithRuleKey{}
}
if convertOptions.IngressRouteCache == nil {
convertOptions.IngressRouteCache = common.NewIngressRouteCache()
}
if convertOptions.VirtualServices == nil {
convertOptions.VirtualServices = map[string]*common.WrapperVirtualService{}
}
if convertOptions.HTTPRoutes == nil {
convertOptions.HTTPRoutes = map[string][]*common.WrapperHTTPRoute{}
}
cfg := wrapper.Config
ingressV1, ok := cfg.Spec.(ingress.IngressSpec)
if !ok {
@@ -515,12 +589,7 @@ func (c *controller) ConvertHTTPRoute(convertOptions *common.ConvertOptions, wra
wrapperVS, exist := convertOptions.VirtualServices[rule.Host]
if !exist {
wrapperVS = &common.WrapperVirtualService{
VirtualService: &networking.VirtualService{
Hosts: []string{rule.Host},
},
WrapperConfig: wrapper,
}
wrapperVS = common.NewWrapperVirtualService(rule.Host, wrapper)
convertOptions.VirtualServices[rule.Host] = wrapperVS
}
@@ -549,7 +618,11 @@ func (c *controller) ConvertHTTPRoute(convertOptions *common.ConvertOptions, wra
pathType = common.PrefixRegex
}
} else {
switch *httpPath.PathType {
ingressPathType := defaultPathType
if httpPath.PathType != nil {
ingressPathType = *httpPath.PathType
}
switch ingressPathType {
case ingress.PathTypeExact:
pathType = common.Exact
case ingress.PathTypePrefix:
@@ -626,9 +699,84 @@ func (c *controller) ConvertHTTPRoute(convertOptions *common.ConvertOptions, wra
}
}
if common.HasPassthroughTLSHostOwner(convertOptions, cfg) ||
(convertOptions.PassthroughTLSHostOwners == nil && wrapper.AnnotationsConfig.IsSSLPassthrough()) {
return c.ConvertTLSRoute(convertOptions, wrapper)
}
return nil
}
func (c *controller) ConvertTLSRoute(convertOptions *common.ConvertOptions, wrapper *common.WrapperConfig) error {
if convertOptions == nil {
return fmt.Errorf("convertOptions is nil")
}
if wrapper == nil {
return fmt.Errorf("wrapperConfig is nil")
}
if convertOptions.VirtualServices == nil {
convertOptions.VirtualServices = map[string]*common.WrapperVirtualService{}
}
cfg := wrapper.Config
ingressV1, ok := cfg.Spec.(ingress.IngressSpec)
if !ok {
common.IncrementInvalidIngress(c.options.ClusterId, common.Unknown)
return fmt.Errorf("convert type is invalid in cluster %s", c.options.ClusterId)
}
if len(ingressV1.Rules) == 0 {
common.IncrementInvalidIngress(c.options.ClusterId, common.EmptyRule)
return fmt.Errorf("invalid ingress rule %s:%s in cluster %s, `rules` must be specified", cfg.Namespace, cfg.Name, c.options.ClusterId)
}
for _, rule := range ingressV1.Rules {
if !common.IsPassthroughTLSHostOwner(convertOptions, cfg, rule.Host) {
IngressLog.Warnf("ignore duplicated ssl passthrough ingress rule %s:%s for host %q in cluster %s", cfg.Namespace, cfg.Name, rule.Host, c.options.ClusterId)
continue
}
if rule.HTTP == nil || len(rule.HTTP.Paths) == 0 {
IngressLog.Warnf("invalid ssl passthrough ingress rule %s:%s for host %q in cluster %s, no paths defined", cfg.Namespace, cfg.Name, rule.Host, c.options.ClusterId)
continue
}
httpPath, ok := rootHTTPIngressPath(rule.HTTP.Paths)
if !ok {
IngressLog.Warnf("ignore ssl passthrough ingress rule %s:%s for host %q in cluster %s, root path is not defined", cfg.Namespace, cfg.Name, rule.Host, c.options.ClusterId)
continue
}
wrapperVS, exist := convertOptions.VirtualServices[rule.Host]
if !exist {
wrapperVS = common.NewWrapperVirtualService(rule.Host, wrapper)
convertOptions.VirtualServices[rule.Host] = wrapperVS
} else if wrapperVS.HasTLSRouteForHost(rule.Host) {
continue
}
routeDestination, event := c.backendToTLSRouteDestination(&httpPath.Backend, cfg.Namespace, wrapper.AnnotationsConfig.Destination)
if event != common.Normal {
common.IncrementInvalidIngress(c.options.ClusterId, event)
continue
}
wrapperVS.VirtualService.Tls = append(wrapperVS.VirtualService.Tls,
common.CreateTLSRoute(rule.Host, routeDestination))
}
return nil
}
func rootHTTPIngressPath(paths []ingress.HTTPIngressPath) (*ingress.HTTPIngressPath, bool) {
for idx := range paths {
if paths[idx].Path == "" || paths[idx].Path == "/" {
return &paths[idx], true
}
}
return nil, false
}
func (c *controller) generateHttpMatches(pathType common.PathType, path string, wrapperVS *common.WrapperVirtualService) []*networking.HTTPMatchRequest {
var httpMatches []*networking.HTTPMatchRequest
@@ -689,12 +837,7 @@ func (c *controller) ApplyDefaultBackend(convertOptions *common.ConvertOptions,
wirecardVS, exist := convertOptions.VirtualServices[host]
if !exist || !wirecardVS.ConfiguredDefaultBackend {
if !exist {
wirecardVS = &common.WrapperVirtualService{
VirtualService: &networking.VirtualService{
Hosts: []string{host},
},
WrapperConfig: wrapper,
}
wirecardVS = common.NewWrapperVirtualService(host, wrapper)
convertOptions.VirtualServices[host] = wirecardVS
}
@@ -782,7 +925,11 @@ func (c *controller) ApplyCanaryIngress(convertOptions *common.ConvertOptions, w
pathType = common.PrefixRegex
}
} else {
switch *httpPath.PathType {
ingressPathType := defaultPathType
if httpPath.PathType != nil {
ingressPathType = *httpPath.PathType
}
switch ingressPathType {
case ingress.PathTypeExact:
pathType = common.Exact
case ingress.PathTypePrefix:
@@ -1074,6 +1221,54 @@ func (c *controller) backendToRouteDestination(backend *ingress.IngressBackend,
}, common.Normal
}
func (c *controller) backendToTLSRouteDestination(backend *ingress.IngressBackend, namespace string,
config *annotations.DestinationConfig,
) ([]*networking.RouteDestination, common.Event) {
if backend == nil {
return nil, common.InvalidBackendService
}
if backend.Service == nil {
if config != nil && len(config.McpDestination) > 0 {
return httpRouteDestinationToRouteDestination(config.McpDestination), common.Normal
}
return nil, common.InvalidBackendService
}
service := backend.Service
port := &networking.PortSelector{}
if service.Port.Number > 0 {
port.Number = uint32(service.Port.Number)
} else {
resolvedPort, err := resolveNamedPort(service, namespace, c.serviceLister)
if err != nil {
return nil, common.PortNameResolveError
}
port.Number = uint32(resolvedPort)
}
return []*networking.RouteDestination{
{
Destination: &networking.Destination{
Host: util.CreateServiceFQDN(namespace, service.Name),
Port: port,
},
Weight: 100,
},
}, common.Normal
}
func httpRouteDestinationToRouteDestination(destinations []*networking.HTTPRouteDestination) []*networking.RouteDestination {
out := make([]*networking.RouteDestination, 0, len(destinations))
for _, destination := range destinations {
out = append(out, &networking.RouteDestination{
Destination: destination.Destination,
Weight: destination.Weight,
})
}
return out
}
func resolveNamedPort(service *ingress.IngressServiceBackend, namespace string, serviceLister listerv1.ServiceLister) (int32, error) {
svc, err := serviceLister.Services(namespace).Get(service.Name)
if err != nil {