diff --git a/pkg/config/static/static_config.go b/pkg/config/static/static_config.go index 8b1aac36d..9311b9405 100644 --- a/pkg/config/static/static_config.go +++ b/pkg/config/static/static_config.go @@ -314,6 +314,18 @@ func (c *Configuration) SetEffectiveConfiguration() { 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. // This allows the provider to adapt the matcher syntax to the desired rule syntax version. if c.Core != nil && c.Providers.KubernetesIngress != nil { diff --git a/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/00-ingress-with-no-annotation.yml b/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/00-ingress-with-no-annotation.yml new file mode 100644 index 000000000..8b92a5958 --- /dev/null +++ b/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/00-ingress-with-no-annotation.yml @@ -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 diff --git a/pkg/provider/kubernetes/ingress-nginx/kubernetes.go b/pkg/provider/kubernetes/ingress-nginx/kubernetes.go index 5d5d36b55..c44139916 100644 --- a/pkg/provider/kubernetes/ingress-nginx/kubernetes.go +++ b/pkg/provider/kubernetes/ingress-nginx/kubernetes.go @@ -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"` 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 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. // 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 } @@ -934,7 +937,7 @@ func applyCORSConfiguration(routerName string, ingressConfig ingressConfig, rt * 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 if ingressConfig.ForceSSLRedirect != nil { 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, // so no Non-TLS router exists to handle HTTP traffic, and we should create it. 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. RuleSyntax: "default", Middlewares: rt.Middlewares, diff --git a/pkg/provider/kubernetes/ingress-nginx/kubernetes_test.go b/pkg/provider/kubernetes/ingress-nginx/kubernetes_test.go index b883c8fed..95781e8c8 100644 --- a/pkg/provider/kubernetes/ingress-nginx/kubernetes_test.go +++ b/pkg/provider/kubernetes/ingress-nginx/kubernetes_test.go @@ -46,6 +46,76 @@ func TestLoadIngresses(t *testing.T) { 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", paths: []string{ @@ -176,15 +246,17 @@ func TestLoadIngresses(t *testing.T) { Service: "default-ingress-with-ssl-redirect-whoami-80", }, "default-ingress-with-ssl-redirect-rule-0-path-0-http": { + EntryPoints: []string{"web"}, Rule: "Host(`sslredirect.localhost`) && Path(`/`)", RuleSyntax: "default", Middlewares: []string{"default-ingress-with-ssl-redirect-rule-0-path-0-redirect-scheme"}, Service: "noop@internal", }, "default-ingress-without-ssl-redirect-rule-0-path-0-http": { - Rule: "Host(`withoutsslredirect.localhost`) && Path(`/`)", - RuleSyntax: "default", - Service: "default-ingress-without-ssl-redirect-whoami-80", + EntryPoints: []string{"web"}, + Rule: "Host(`withoutsslredirect.localhost`) && Path(`/`)", + RuleSyntax: "default", + Service: "default-ingress-without-ssl-redirect-whoami-80", }, "default-ingress-without-ssl-redirect-rule-0-path-0": { Rule: "Host(`withoutsslredirect.localhost`) && Path(`/`)", @@ -653,6 +725,7 @@ func TestLoadIngresses(t *testing.T) { k8sClient: client, defaultBackendServiceName: test.defaultBackendServiceName, defaultBackendServiceNamespace: test.defaultBackendServiceNamespace, + NonTLSEntryPoints: []string{"web"}, } p.SetDefaults()