1
0
Fork 0

Prevent Ingress Nginx provider http router to attach to an entrypoint with TLS

Co-authored-by: Gina A. <70909035+gndz07@users.noreply.github.com>
This commit is contained in:
Romain 2026-01-09 17:38:05 +01:00 committed by GitHub
parent 1881434ac6
commit 26f4a669b8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 119 additions and 6 deletions

View file

@ -314,6 +314,18 @@ func (c *Configuration) SetEffectiveConfiguration() {
c.Providers.KubernetesGateway.EntryPoints = entryPoints c.Providers.KubernetesGateway.EntryPoints = entryPoints
} }
// Configure Ingress NGINX provider.
if c.Providers.KubernetesIngressNGINX != nil {
var nonTLSEntryPoints []string
for epName, entryPoint := range c.EntryPoints {
if entryPoint.HTTP.TLS == nil {
nonTLSEntryPoints = append(nonTLSEntryPoints, epName)
}
}
c.Providers.KubernetesIngressNGINX.NonTLSEntryPoints = nonTLSEntryPoints
}
// Defines the default rule syntax for the Kubernetes Ingress Provider. // Defines the default rule syntax for the Kubernetes Ingress Provider.
// This allows the provider to adapt the matcher syntax to the desired rule syntax version. // This allows the provider to adapt the matcher syntax to the desired rule syntax version.
if c.Core != nil && c.Providers.KubernetesIngress != nil { if c.Core != nil && c.Providers.KubernetesIngress != nil {

View file

@ -0,0 +1,23 @@
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-with-no-annotation
namespace: default
spec:
ingressClassName: nginx
rules:
- host: whoami.localhost
http:
paths:
- backend:
service:
name: whoami
port:
number: 80
path: /
pathType: Prefix
tls:
- hosts:
- whoami.localhost
secretName: whoami-tls

View file

@ -80,6 +80,9 @@ type Provider struct {
DefaultBackendService string `description:"Service used to serve HTTP requests not matching any known server name (catch-all). Takes the form 'namespace/name'." json:"defaultBackendService,omitempty" toml:"defaultBackendService,omitempty" yaml:"defaultBackendService,omitempty" export:"true"` DefaultBackendService string `description:"Service used to serve HTTP requests not matching any known server name (catch-all). Takes the form 'namespace/name'." json:"defaultBackendService,omitempty" toml:"defaultBackendService,omitempty" yaml:"defaultBackendService,omitempty" export:"true"`
DisableSvcExternalName bool `description:"Disable support for Services of type ExternalName." json:"disableSvcExternalName,omitempty" toml:"disableSvcExternalName,omitempty" yaml:"disableSvcExternalName,omitempty" export:"true"` DisableSvcExternalName bool `description:"Disable support for Services of type ExternalName." json:"disableSvcExternalName,omitempty" toml:"disableSvcExternalName,omitempty" yaml:"disableSvcExternalName,omitempty" export:"true"`
// NonTLSEntryPoints contains the names of entrypoints that are configured without TLS.
NonTLSEntryPoints []string `json:"-" toml:"-" yaml:"-" label:"-" file:"-"`
defaultBackendServiceNamespace string defaultBackendServiceNamespace string
defaultBackendServiceName string defaultBackendServiceName string
@ -798,7 +801,7 @@ func (p *Provider) applyMiddlewares(namespace, routerKey string, ingressConfig i
// Apply SSL redirect is mandatory to be applied after all other middlewares. // Apply SSL redirect is mandatory to be applied after all other middlewares.
// TODO: check how to remove this, and create the HTTP router elsewhere. // TODO: check how to remove this, and create the HTTP router elsewhere.
applySSLRedirectConfiguration(routerKey, ingressConfig, hasTLS, rt, conf) p.applySSLRedirectConfiguration(routerKey, ingressConfig, hasTLS, rt, conf)
return nil return nil
} }
@ -934,7 +937,7 @@ func applyCORSConfiguration(routerName string, ingressConfig ingressConfig, rt *
rt.Middlewares = append(rt.Middlewares, corsMiddlewareName) rt.Middlewares = append(rt.Middlewares, corsMiddlewareName)
} }
func applySSLRedirectConfiguration(routerName string, ingressConfig ingressConfig, hasTLS bool, rt *dynamic.Router, conf *dynamic.Configuration) { func (p *Provider) applySSLRedirectConfiguration(routerName string, ingressConfig ingressConfig, hasTLS bool, rt *dynamic.Router, conf *dynamic.Configuration) {
var forceSSLRedirect bool var forceSSLRedirect bool
if ingressConfig.ForceSSLRedirect != nil { if ingressConfig.ForceSSLRedirect != nil {
forceSSLRedirect = *ingressConfig.ForceSSLRedirect forceSSLRedirect = *ingressConfig.ForceSSLRedirect
@ -946,7 +949,9 @@ func applySSLRedirectConfiguration(routerName string, ingressConfig ingressConfi
// An Ingress with TLS configuration creates only a Traefik router with a TLS configuration, // An Ingress with TLS configuration creates only a Traefik router with a TLS configuration,
// so no Non-TLS router exists to handle HTTP traffic, and we should create it. // so no Non-TLS router exists to handle HTTP traffic, and we should create it.
httpRouter := &dynamic.Router{ httpRouter := &dynamic.Router{
Rule: rt.Rule, // Only attach to entryPoint which do not activate TLS.
EntryPoints: p.NonTLSEntryPoints,
Rule: rt.Rule,
// "default" stands for the default rule syntax in Traefik v3, i.e. the v3 syntax. // "default" stands for the default rule syntax in Traefik v3, i.e. the v3 syntax.
RuleSyntax: "default", RuleSyntax: "default",
Middlewares: rt.Middlewares, Middlewares: rt.Middlewares,

View file

@ -46,6 +46,76 @@ func TestLoadIngresses(t *testing.T) {
TLS: &dynamic.TLSConfiguration{}, TLS: &dynamic.TLSConfiguration{},
}, },
}, },
{
desc: "No annotation",
paths: []string{
"ingresses/00-ingress-with-no-annotation.yml",
"ingressclasses.yml",
"services.yml",
"secrets.yml",
},
expected: &dynamic.Configuration{
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Services: map[string]*dynamic.TCPService{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"default-ingress-with-no-annotation-rule-0-path-0": {
Rule: "Host(`whoami.localhost`) && PathPrefix(`/`)",
RuleSyntax: "default",
TLS: &dynamic.RouterTLSConfig{},
Service: "default-ingress-with-no-annotation-whoami-80",
},
"default-ingress-with-no-annotation-rule-0-path-0-http": {
EntryPoints: []string{"web"},
Rule: "Host(`whoami.localhost`) && PathPrefix(`/`)",
RuleSyntax: "default",
Middlewares: []string{"default-ingress-with-no-annotation-rule-0-path-0-redirect-scheme"},
Service: "noop@internal",
},
},
Middlewares: map[string]*dynamic.Middleware{
"default-ingress-with-no-annotation-rule-0-path-0-redirect-scheme": {
RedirectScheme: &dynamic.RedirectScheme{
Scheme: "https",
ForcePermanentRedirect: true,
},
},
},
Services: map[string]*dynamic.Service{
"default-ingress-with-no-annotation-whoami-80": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
Strategy: "wrr",
PassHostHeader: ptr.To(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: dynamic.DefaultFlushInterval,
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Certificates: []*tls.CertAndStores{
{
Certificate: tls.Certificate{
CertFile: "-----BEGIN CERTIFICATE-----",
KeyFile: "-----BEGIN CERTIFICATE-----",
},
},
},
},
},
},
{ {
desc: "Basic Auth", desc: "Basic Auth",
paths: []string{ paths: []string{
@ -176,15 +246,17 @@ func TestLoadIngresses(t *testing.T) {
Service: "default-ingress-with-ssl-redirect-whoami-80", Service: "default-ingress-with-ssl-redirect-whoami-80",
}, },
"default-ingress-with-ssl-redirect-rule-0-path-0-http": { "default-ingress-with-ssl-redirect-rule-0-path-0-http": {
EntryPoints: []string{"web"},
Rule: "Host(`sslredirect.localhost`) && Path(`/`)", Rule: "Host(`sslredirect.localhost`) && Path(`/`)",
RuleSyntax: "default", RuleSyntax: "default",
Middlewares: []string{"default-ingress-with-ssl-redirect-rule-0-path-0-redirect-scheme"}, Middlewares: []string{"default-ingress-with-ssl-redirect-rule-0-path-0-redirect-scheme"},
Service: "noop@internal", Service: "noop@internal",
}, },
"default-ingress-without-ssl-redirect-rule-0-path-0-http": { "default-ingress-without-ssl-redirect-rule-0-path-0-http": {
Rule: "Host(`withoutsslredirect.localhost`) && Path(`/`)", EntryPoints: []string{"web"},
RuleSyntax: "default", Rule: "Host(`withoutsslredirect.localhost`) && Path(`/`)",
Service: "default-ingress-without-ssl-redirect-whoami-80", RuleSyntax: "default",
Service: "default-ingress-without-ssl-redirect-whoami-80",
}, },
"default-ingress-without-ssl-redirect-rule-0-path-0": { "default-ingress-without-ssl-redirect-rule-0-path-0": {
Rule: "Host(`withoutsslredirect.localhost`) && Path(`/`)", Rule: "Host(`withoutsslredirect.localhost`) && Path(`/`)",
@ -653,6 +725,7 @@ func TestLoadIngresses(t *testing.T) {
k8sClient: client, k8sClient: client,
defaultBackendServiceName: test.defaultBackendServiceName, defaultBackendServiceName: test.defaultBackendServiceName,
defaultBackendServiceNamespace: test.defaultBackendServiceNamespace, defaultBackendServiceNamespace: test.defaultBackendServiceNamespace,
NonTLSEntryPoints: []string{"web"},
} }
p.SetDefaults() p.SetDefaults()