From 84d9df3ce1e5505445c0ca3f7157d7a19dd2858d Mon Sep 17 00:00:00 2001 From: Jenthe Noordsij <45041769+jnoordsij@users.noreply.github.com> Date: Wed, 21 Oct 2026 14:10:28 +0200 Subject: [PATCH 1/2] Bump to go1.27 --- .github/workflows/validate.yaml | 2 +- .go-version | 2 +- cmd/configuration.go | 20 +- docs/content/migration/v2.md | 31 +++ go.mod | 8 +- integration/https_test.go | 6 +- pkg/middlewares/headers/secure_test.go | 47 ++-- pkg/provider/acme/challenge_tls.go | 8 +- pkg/provider/acme/provider.go | 8 +- pkg/provider/acme/provider_test.go | 20 +- pkg/provider/consulcatalog/config_test.go | 16 +- pkg/provider/docker/swarm_test.go | 64 +++--- pkg/provider/kubernetes/crd/client_test.go | 17 +- pkg/provider/kubernetes/crd/kubernetes.go | 6 +- .../kubernetes/crd/kubernetes_test.go | 20 +- pkg/provider/kubernetes/gateway/kubernetes.go | 6 +- .../kubernetes/gateway/kubernetes_test.go | 151 +++++-------- .../kubernetes/ingress/client_test.go | 48 ++-- pkg/provider/kubernetes/ingress/kubernetes.go | 6 +- .../kubernetes/ingress/kubernetes_test.go | 39 ++-- .../kubernetes/k8s/event_handler_test.go | 209 +++++------------- pkg/provider/kv/kv_test.go | 12 +- pkg/provider/nomad/nomad_test.go | 6 +- pkg/redactor/redactor_config_test.go | 101 ++++----- pkg/server/aggregator_test.go | 10 +- pkg/server/router/tcp/router_test.go | 4 +- pkg/server/service/roundtripper.go | 26 +-- pkg/server/service/roundtripper_test.go | 190 ++++++++++++++++ pkg/server/service/smart_roundtripper.go | 68 +++--- pkg/tls/tlsmanager_test.go | 18 +- 30 files changed, 568 insertions(+), 601 deletions(-) diff --git a/.github/workflows/validate.yaml b/.github/workflows/validate.yaml index bee652f47d..fdf22b77ef 100644 --- a/.github/workflows/validate.yaml +++ b/.github/workflows/validate.yaml @@ -6,7 +6,7 @@ on: - '*' env: - GOLANGCI_LINT_VERSION: v2.13.2 + GOLANGCI_LINT_VERSION: v2.14.0 MISSPELL_VERSION: v0.7.0 jobs: diff --git a/.go-version b/.go-version index 24cffb85f8..b0c101e630 100644 --- a/.go-version +++ b/.go-version @@ -1 +1 @@ -1.26 +1.27 diff --git a/cmd/configuration.go b/cmd/configuration.go index 811245e51f..e579d09121 100644 --- a/cmd/configuration.go +++ b/cmd/configuration.go @@ -18,17 +18,15 @@ type TraefikCmdConfiguration struct { // NewTraefikConfiguration creates a TraefikCmdConfiguration with default values. func NewTraefikConfiguration() *TraefikCmdConfiguration { return &TraefikCmdConfiguration{ - Configuration: static.Configuration{ - Global: &static.Global{ - CheckNewVersion: true, - }, - EntryPoints: make(static.EntryPoints), - Providers: &static.Providers{ - ProvidersThrottleDuration: ptypes.Duration(2 * time.Second), - }, - ServersTransport: &static.ServersTransport{ - MaxIdleConnsPerHost: 200, - }, + Global: &static.Global{ + CheckNewVersion: true, + }, + EntryPoints: make(static.EntryPoints), + Providers: &static.Providers{ + ProvidersThrottleDuration: ptypes.Duration(2 * time.Second), + }, + ServersTransport: &static.ServersTransport{ + MaxIdleConnsPerHost: 200, }, ConfigFile: "", } diff --git a/docs/content/migration/v2.md b/docs/content/migration/v2.md index 01078ef7d3..ad5493d1f4 100644 --- a/docs/content/migration/v2.md +++ b/docs/content/migration/v2.md @@ -1196,3 +1196,34 @@ As the rejection happens before the routing, the rejected requests are not repor They are only visible at the `DEBUG` log level. To tunnel traffic through a forward proxy exposed by Traefik, route it with a [TCP router](../routing/routers/index.md#configuring-tcp-routers) instead. + +## v2.11.61 + +Starting with `v2.11.61`, Traefik is built with Go 1.27. + +### GODEBUG TLS Settings + +Go 1.27 removed the `tlsrsakex`, `tls10server` and `tls3des` `GODEBUG` settings. +Setting them in the `GODEBUG` environment variable has no effect anymore. + +For the incoming connections, the RSA key exchange and 3DES cipher suites, as well as TLS 1.0 and 1.1, +can still be enabled with the `cipherSuites` and `minVersion` fields of the [TLS options](../https/tls.md#tls-options), +referenced in the TLS configuration of a [router](../routing/routers/index.md#tls) or of an [entrypoint](../routing/entrypoints.md#tls). + +For the connections to the backends, Traefik does not offer to configure the cipher suites, +so the RSA key exchange and 3DES cipher suites are not available anymore. +A backend supporting only these cipher suites cannot be reached. + +Please check out the [go documentation](https://go.dev/doc/go1.27#cryptotlspkgcryptotls) for more details. + +### Maximum Number of Request Header Values + +Go 1.27 limits the number of header values a request can carry to 500. + +A request exceeding this limit is rejected with a `431 Request Header Fields Too Large` response, +for HTTP/1 and HTTP/2. +This limit is not configurable. + +As the rejection happens before the routing, the rejected requests are not reported in the access logs. + +Please check out the [go documentation](https://go.dev/doc/go1.27#nethttppkgnethttp) for more details. diff --git a/go.mod b/go.mod index 9d61524db9..47f9e32f47 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/traefik/traefik/v2 -go 1.26.0 +go 1.27.0 require ( github.com/BurntSushi/toml v1.6.0 @@ -60,10 +60,12 @@ require ( github.com/prometheus/client_model v0.6.2 github.com/quic-go/quic-go v0.59.1 github.com/rancher/go-rancher-metadata v0.0.0-20200311180630-7f4c936a06ac // No tag on the repo. + github.com/samber/slog-logrus/v2 v2.5.4 github.com/sirupsen/logrus v1.10.2 github.com/stretchr/testify v1.12.1 github.com/stvp/go-udp-testing v0.0.0-20191102171040-06b61409b154 // No tag on the repo. github.com/testcontainers/testcontainers-go v0.42.0 + github.com/testcontainers/testcontainers-go/modules/k3s v0.42.0 github.com/traefik/paerser v0.2.2 github.com/traefik/yaegi v0.16.1 github.com/uber/jaeger-client-go v2.30.0+incompatible @@ -89,6 +91,7 @@ require ( k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // No tag on the repo. mvdan.cc/xurls/v2 v2.5.0 sigs.k8s.io/gateway-api v0.4.0 + sigs.k8s.io/structured-merge-diff/v6 v6.3.0 ) require ( @@ -330,7 +333,6 @@ require ( github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/samber/lo v1.53.0 // indirect github.com/samber/slog-common v0.21.0 // indirect - github.com/samber/slog-logrus/v2 v2.5.4 github.com/scaleway/scaleway-sdk-go v1.0.0-beta.36 // indirect github.com/secure-systems-lab/go-securesystemslib v0.10.0 // indirect github.com/segmentio/fasthash v1.0.3 // indirect @@ -349,7 +351,6 @@ require ( github.com/stretchr/objx v0.5.3 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.106 // indirect - github.com/testcontainers/testcontainers-go/modules/k3s v0.42.0 github.com/tinylib/msgp v1.6.3 // indirect github.com/tjfoc/gmsm v1.4.1 // indirect github.com/tklauser/go-sysconf v0.3.16 // indirect @@ -410,7 +411,6 @@ require ( k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect sigs.k8s.io/randfill v1.0.0 // indirect - sigs.k8s.io/structured-merge-diff/v6 v6.3.0 sigs.k8s.io/yaml v1.6.0 // indirect software.sslmate.com/src/go-pkcs12 v0.7.3 // indirect ) diff --git a/integration/https_test.go b/integration/https_test.go index 2a9075c17a..16ffdfe0ce 100644 --- a/integration/https_test.go +++ b/integration/https_test.go @@ -1378,10 +1378,8 @@ func (s *HTTPSSuite) modifyCertificateConfFileContent(certFileName, confFileName TLS: &dynamic.TLSConfiguration{ Certificates: []*traefiktls.CertAndStores{ { - Certificate: traefiktls.Certificate{ - CertFile: traefiktls.FileOrContent("fixtures/https/" + certFileName + ".cert"), - KeyFile: traefiktls.FileOrContent("fixtures/https/" + certFileName + ".key"), - }, + CertFile: traefiktls.FileOrContent("fixtures/https/" + certFileName + ".cert"), + KeyFile: traefiktls.FileOrContent("fixtures/https/" + certFileName + ".key"), }, }, }, diff --git a/pkg/middlewares/headers/secure_test.go b/pkg/middlewares/headers/secure_test.go index 9a035b8451..d94f199722 100644 --- a/pkg/middlewares/headers/secure_test.go +++ b/pkg/middlewares/headers/secure_test.go @@ -12,17 +12,12 @@ import ( // Middleware tests based on https://github.com/unrolled/secure func Test_newSecure_sslForceHost(t *testing.T) { - type expected struct { - statusCode int - location string - } - testCases := []struct { - expected - - desc string - host string - cfg dynamic.Headers + desc string + host string + cfg dynamic.Headers + expectedStatusCode int + expectedLocation string }{ { desc: "http should return a 301", @@ -32,10 +27,8 @@ func Test_newSecure_sslForceHost(t *testing.T) { SSLForceHost: true, SSLHost: "powpow.example.com", }, - expected: expected{ - statusCode: http.StatusMovedPermanently, - location: "https://powpow.example.com", - }, + expectedStatusCode: http.StatusMovedPermanently, + expectedLocation: "https://powpow.example.com", }, { desc: "http sub domain should return a 301", @@ -45,10 +38,8 @@ func Test_newSecure_sslForceHost(t *testing.T) { SSLForceHost: true, SSLHost: "powpow.example.com", }, - expected: expected{ - statusCode: http.StatusMovedPermanently, - location: "https://powpow.example.com", - }, + expectedStatusCode: http.StatusMovedPermanently, + expectedLocation: "https://powpow.example.com", }, { desc: "https should return a 200", @@ -58,7 +49,7 @@ func Test_newSecure_sslForceHost(t *testing.T) { SSLForceHost: true, SSLHost: "powpow.example.com", }, - expected: expected{statusCode: http.StatusOK}, + expectedStatusCode: http.StatusOK, }, { desc: "https sub domain should return a 301", @@ -68,10 +59,8 @@ func Test_newSecure_sslForceHost(t *testing.T) { SSLForceHost: true, SSLHost: "powpow.example.com", }, - expected: expected{ - statusCode: http.StatusMovedPermanently, - location: "https://powpow.example.com", - }, + expectedStatusCode: http.StatusMovedPermanently, + expectedLocation: "https://powpow.example.com", }, { desc: "http without force host and sub domain should return a 301", @@ -81,10 +70,8 @@ func Test_newSecure_sslForceHost(t *testing.T) { SSLForceHost: false, SSLHost: "powpow.example.com", }, - expected: expected{ - statusCode: http.StatusMovedPermanently, - location: "https://powpow.example.com", - }, + expectedStatusCode: http.StatusMovedPermanently, + expectedLocation: "https://powpow.example.com", }, { desc: "https without force host and sub domain should return a 301", @@ -94,7 +81,7 @@ func Test_newSecure_sslForceHost(t *testing.T) { SSLForceHost: false, SSLHost: "powpow.example.com", }, - expected: expected{statusCode: http.StatusOK}, + expectedStatusCode: http.StatusOK, }, } @@ -112,8 +99,8 @@ func Test_newSecure_sslForceHost(t *testing.T) { mid.ServeHTTP(rw, req) - assert.Equal(t, test.expected.statusCode, rw.Result().StatusCode) - assert.Equal(t, test.expected.location, rw.Header().Get("Location")) + assert.Equal(t, test.expectedStatusCode, rw.Result().StatusCode) + assert.Equal(t, test.expectedLocation, rw.Header().Get("Location")) }) } } diff --git a/pkg/provider/acme/challenge_tls.go b/pkg/provider/acme/challenge_tls.go index c550ed0901..cc939d1d65 100644 --- a/pkg/provider/acme/challenge_tls.go +++ b/pkg/provider/acme/challenge_tls.go @@ -152,11 +152,9 @@ func createMessage(certs map[string]*Certificate) dynamic.Message { for _, cert := range certs { certConf := &traefiktls.CertAndStores{ - Certificate: traefiktls.Certificate{ - CertFile: traefiktls.FileOrContent(cert.Certificate), - KeyFile: traefiktls.FileOrContent(cert.Key), - }, - Stores: []string{tlsalpn01.ACMETLS1Protocol}, + CertFile: traefiktls.FileOrContent(cert.Certificate), + KeyFile: traefiktls.FileOrContent(cert.Key), + Stores: []string{tlsalpn01.ACMETLS1Protocol}, } conf.Configuration.TLS.Certificates = append(conf.Configuration.TLS.Certificates, certConf) } diff --git a/pkg/provider/acme/provider.go b/pkg/provider/acme/provider.go index f8cde1e365..d3deaca6e2 100644 --- a/pkg/provider/acme/provider.go +++ b/pkg/provider/acme/provider.go @@ -775,11 +775,9 @@ func (p *Provider) buildMessage() dynamic.Message { for _, cert := range p.certificates { certConf := &traefiktls.CertAndStores{ - Certificate: traefiktls.Certificate{ - CertFile: traefiktls.FileOrContent(cert.Certificate.Certificate), - KeyFile: traefiktls.FileOrContent(cert.Key), - }, - Stores: []string{cert.Store}, + CertFile: traefiktls.FileOrContent(cert.Certificate.Certificate), + KeyFile: traefiktls.FileOrContent(cert.Key), + Stores: []string{cert.Store}, } conf.Configuration.TLS.Certificates = append(conf.Configuration.TLS.Certificates, certConf) } diff --git a/pkg/provider/acme/provider_test.go b/pkg/provider/acme/provider_test.go index 246525eb69..330a125a77 100644 --- a/pkg/provider/acme/provider_test.go +++ b/pkg/provider/acme/provider_test.go @@ -49,9 +49,7 @@ func TestGetUncheckedCertificates(t *testing.T) { domains: []string{"*.traefik.wtf"}, acmeCertificates: []*CertAndStore{ { - Certificate: Certificate{ - Domain: types.Domain{Main: "*.traefik.wtf"}, - }, + Domain: types.Domain{Main: "*.traefik.wtf"}, }, }, expectedDomains: nil, @@ -72,9 +70,7 @@ func TestGetUncheckedCertificates(t *testing.T) { domains: []string{"traefik.wtf", "foo.traefik.wtf"}, acmeCertificates: []*CertAndStore{ { - Certificate: Certificate{ - Domain: types.Domain{Main: "traefik.wtf"}, - }, + Domain: types.Domain{Main: "traefik.wtf"}, }, }, expectedDomains: []string{"foo.traefik.wtf"}, @@ -90,9 +86,7 @@ func TestGetUncheckedCertificates(t *testing.T) { domains: []string{"traefik.wtf"}, acmeCertificates: []*CertAndStore{ { - Certificate: Certificate{ - Domain: types.Domain{Main: "traefik.wtf"}, - }, + Domain: types.Domain{Main: "traefik.wtf"}, }, }, expectedDomains: nil, @@ -108,9 +102,7 @@ func TestGetUncheckedCertificates(t *testing.T) { domains: []string{"who.traefik.wtf", "foo.traefik.wtf"}, acmeCertificates: []*CertAndStore{ { - Certificate: Certificate{ - Domain: types.Domain{Main: "*.traefik.wtf"}, - }, + Domain: types.Domain{Main: "*.traefik.wtf"}, }, }, expectedDomains: nil, @@ -120,9 +112,7 @@ func TestGetUncheckedCertificates(t *testing.T) { domains: []string{"traefik.wtf", "foo.traefik.wtf"}, acmeCertificates: []*CertAndStore{ { - Certificate: Certificate{ - Domain: types.Domain{Main: "*.traefik.wtf"}, - }, + Domain: types.Domain{Main: "*.traefik.wtf"}, }, }, expectedDomains: []string{"traefik.wtf"}, diff --git a/pkg/provider/consulcatalog/config_test.go b/pkg/provider/consulcatalog/config_test.go index 58f41021be..315f6a4a07 100644 --- a/pkg/provider/consulcatalog/config_test.go +++ b/pkg/provider/consulcatalog/config_test.go @@ -276,10 +276,8 @@ func TestDefaultRule(t *testing.T) { t.Parallel() p := Provider{ - Configuration: Configuration{ - ExposedByDefault: true, - DefaultRule: test.defaultRule, - }, + ExposedByDefault: true, + DefaultRule: test.defaultRule, } err := p.Init() @@ -3160,12 +3158,10 @@ func Test_buildConfiguration(t *testing.T) { t.Parallel() p := Provider{ - Configuration: Configuration{ - ExposedByDefault: true, - DefaultRule: "Host(`{{ normalize .Name }}.traefik.wtf`)", - ConnectAware: test.ConnectAware, - Constraints: test.constraints, - }, + ExposedByDefault: true, + DefaultRule: "Host(`{{ normalize .Name }}.traefik.wtf`)", + ConnectAware: test.ConnectAware, + Constraints: test.constraints, } err := p.Init() diff --git a/pkg/provider/docker/swarm_test.go b/pkg/provider/docker/swarm_test.go index 5dcda99f6e..ecfcdf71ea 100644 --- a/pkg/provider/docker/swarm_test.go +++ b/pkg/provider/docker/swarm_test.go @@ -199,23 +199,21 @@ func TestListServices(t *testing.T) { dockerVersion: "1.30", networks: []networktypes.Summary{ { - Network: networktypes.Network{ - Name: "network_name", - ID: "yk6l57rfwizjzxxzftn4amaot", - Created: time.Now(), - Scope: "swarm", - Driver: "overlay", - EnableIPv6: false, - Internal: true, - Ingress: false, - ConfigOnly: false, - Options: map[string]string{ - "com.docker.networktypes.driver.overlay.vxlanid_list": "4098", - "com.docker.networktypes.enable_ipv6": "false", - }, - Labels: map[string]string{ - "com.docker.stack.namespace": "test", - }, + Name: "network_name", + ID: "yk6l57rfwizjzxxzftn4amaot", + Created: time.Now(), + Scope: "swarm", + Driver: "overlay", + EnableIPv6: false, + Internal: true, + Ingress: false, + ConfigOnly: false, + Options: map[string]string{ + "com.docker.networktypes.driver.overlay.vxlanid_list": "4098", + "com.docker.networktypes.enable_ipv6": "false", + }, + Labels: map[string]string{ + "com.docker.stack.namespace": "test", }, }, }, @@ -256,23 +254,21 @@ func TestListServices(t *testing.T) { dockerVersion: "1.30", networks: []networktypes.Summary{ { - Network: networktypes.Network{ - Name: "network_name", - ID: "yk6l57rfwizjzxxzftn4amaot", - Created: time.Now(), - Scope: "swarm", - Driver: "overlay", - EnableIPv6: false, - Internal: true, - Ingress: false, - ConfigOnly: false, - Options: map[string]string{ - "com.docker.networktypes.driver.overlay.vxlanid_list": "4098", - "com.docker.networktypes.enable_ipv6": "false", - }, - Labels: map[string]string{ - "com.docker.stack.namespace": "test", - }, + Name: "network_name", + ID: "yk6l57rfwizjzxxzftn4amaot", + Created: time.Now(), + Scope: "swarm", + Driver: "overlay", + EnableIPv6: false, + Internal: true, + Ingress: false, + ConfigOnly: false, + Options: map[string]string{ + "com.docker.networktypes.driver.overlay.vxlanid_list": "4098", + "com.docker.networktypes.enable_ipv6": "false", + }, + Labels: map[string]string{ + "com.docker.stack.namespace": "test", }, }, }, diff --git a/pkg/provider/kubernetes/crd/client_test.go b/pkg/provider/kubernetes/crd/client_test.go index 8d2685f5e9..c826661021 100644 --- a/pkg/provider/kubernetes/crd/client_test.go +++ b/pkg/provider/kubernetes/crd/client_test.go @@ -8,24 +8,19 @@ import ( "github.com/stretchr/testify/require" traefikcrdfake "github.com/traefik/traefik/v2/pkg/provider/kubernetes/crd/generated/clientset/versioned/fake" corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" kubefake "k8s.io/client-go/kubernetes/fake" ) func TestClientIgnoresHelmOwnedSecrets(t *testing.T) { secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "secret", - }, + Namespace: "default", + Name: "secret", } helmSecret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "helm-secret", - Labels: map[string]string{ - "owner": "helm", - }, + Namespace: "default", + Name: "helm-secret", + Labels: map[string]string{ + "owner": "helm", }, } diff --git a/pkg/provider/kubernetes/crd/kubernetes.go b/pkg/provider/kubernetes/crd/kubernetes.go index 5712ac0ad6..649bd1a53a 100644 --- a/pkg/provider/kubernetes/crd/kubernetes.go +++ b/pkg/provider/kubernetes/crd/kubernetes.go @@ -1127,10 +1127,8 @@ func getTLS(k8sClient Client, secretName, namespace string) (*tls.CertAndStores, } return &tls.CertAndStores{ - Certificate: tls.Certificate{ - CertFile: tls.FileOrContent(cert), - KeyFile: tls.FileOrContent(key), - }, + CertFile: tls.FileOrContent(cert), + KeyFile: tls.FileOrContent(key), }, nil } diff --git a/pkg/provider/kubernetes/crd/kubernetes_test.go b/pkg/provider/kubernetes/crd/kubernetes_test.go index f2a866e97f..74f932021b 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_test.go +++ b/pkg/provider/kubernetes/crd/kubernetes_test.go @@ -720,10 +720,8 @@ func TestLoadIngressRouteTCPs(t *testing.T) { TLS: &dynamic.TLSConfiguration{ Certificates: []*tls.CertAndStores{ { - Certificate: tls.Certificate{ - CertFile: tls.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), - KeyFile: tls.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), - }, + CertFile: tls.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), + KeyFile: tls.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), }, }, }, @@ -3647,10 +3645,8 @@ func TestLoadIngressRoutes(t *testing.T) { TLS: &dynamic.TLSConfiguration{ Certificates: []*tls.CertAndStores{ { - Certificate: tls.Certificate{ - CertFile: tls.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), - KeyFile: tls.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), - }, + CertFile: tls.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), + KeyFile: tls.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), }, }, }, @@ -5070,11 +5066,9 @@ func TestLoadIngressRoutes(t *testing.T) { TLS: &dynamic.TLSConfiguration{ Certificates: []*tls.CertAndStores{ { - Certificate: tls.Certificate{ - CertFile: tls.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), - KeyFile: tls.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), - }, - Stores: []string{"default"}, + CertFile: tls.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), + KeyFile: tls.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), + Stores: []string{"default"}, }, }, Stores: map[string]tls.Store{ diff --git a/pkg/provider/kubernetes/gateway/kubernetes.go b/pkg/provider/kubernetes/gateway/kubernetes.go index 673599a922..0d5397872b 100644 --- a/pkg/provider/kubernetes/gateway/kubernetes.go +++ b/pkg/provider/kubernetes/gateway/kubernetes.go @@ -1387,10 +1387,8 @@ func getTLS(k8sClient Client, secretName gatev1alpha2.ObjectName, namespace stri } return &tls.CertAndStores{ - Certificate: tls.Certificate{ - CertFile: tls.FileOrContent(cert), - KeyFile: tls.FileOrContent(key), - }, + CertFile: tls.FileOrContent(cert), + KeyFile: tls.FileOrContent(key), }, nil } diff --git a/pkg/provider/kubernetes/gateway/kubernetes_test.go b/pkg/provider/kubernetes/gateway/kubernetes_test.go index 43bc9d53ff..6f15a27ae6 100644 --- a/pkg/provider/kubernetes/gateway/kubernetes_test.go +++ b/pkg/provider/kubernetes/gateway/kubernetes_test.go @@ -8,7 +8,6 @@ import ( "github.com/traefik/traefik/v2/pkg/config/dynamic" "github.com/traefik/traefik/v2/pkg/provider" "github.com/traefik/traefik/v2/pkg/tls" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/utils/ptr" gatev1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" ) @@ -470,10 +469,8 @@ func TestLoadHTTPRoutes(t *testing.T) { TLS: &dynamic.TLSConfiguration{ Certificates: []*tls.CertAndStores{ { - Certificate: tls.Certificate{ - CertFile: tls.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), - KeyFile: tls.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), - }, + CertFile: tls.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), + KeyFile: tls.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), }, }, }, @@ -705,10 +702,8 @@ func TestLoadHTTPRoutes(t *testing.T) { TLS: &dynamic.TLSConfiguration{ Certificates: []*tls.CertAndStores{ { - Certificate: tls.Certificate{ - CertFile: tls.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), - KeyFile: tls.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), - }, + CertFile: tls.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), + KeyFile: tls.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), }, }, }, @@ -1110,10 +1105,8 @@ func TestLoadHTTPRoutes(t *testing.T) { TLS: &dynamic.TLSConfiguration{ Certificates: []*tls.CertAndStores{ { - Certificate: tls.Certificate{ - CertFile: tls.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), - KeyFile: tls.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), - }, + CertFile: tls.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), + KeyFile: tls.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), }, }, }, @@ -1195,10 +1188,8 @@ func TestLoadHTTPRoutes(t *testing.T) { TLS: &dynamic.TLSConfiguration{ Certificates: []*tls.CertAndStores{ { - Certificate: tls.Certificate{ - CertFile: tls.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), - KeyFile: tls.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), - }, + CertFile: tls.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), + KeyFile: tls.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), }, }, }, @@ -2003,10 +1994,8 @@ func TestLoadTCPRoutes(t *testing.T) { TLS: &dynamic.TLSConfiguration{ Certificates: []*tls.CertAndStores{ { - Certificate: tls.Certificate{ - CertFile: tls.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), - KeyFile: tls.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), - }, + CertFile: tls.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), + KeyFile: tls.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), }, }, }, @@ -2485,10 +2474,8 @@ func TestLoadTLSRoutes(t *testing.T) { TLS: &dynamic.TLSConfiguration{ Certificates: []*tls.CertAndStores{ { - Certificate: tls.Certificate{ - CertFile: tls.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), - KeyFile: tls.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), - }, + CertFile: tls.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), + KeyFile: tls.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), }, }, }, @@ -2694,10 +2681,8 @@ func TestLoadTLSRoutes(t *testing.T) { TLS: &dynamic.TLSConfiguration{ Certificates: []*tls.CertAndStores{ { - Certificate: tls.Certificate{ - CertFile: tls.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), - KeyFile: tls.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), - }, + CertFile: tls.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), + KeyFile: tls.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), }, }, }, @@ -2762,10 +2747,8 @@ func TestLoadTLSRoutes(t *testing.T) { TLS: &dynamic.TLSConfiguration{ Certificates: []*tls.CertAndStores{ { - Certificate: tls.Certificate{ - CertFile: tls.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), - KeyFile: tls.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), - }, + CertFile: tls.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), + KeyFile: tls.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), }, }, }, @@ -3552,10 +3535,8 @@ func TestLoadMixedRoutes(t *testing.T) { TLS: &dynamic.TLSConfiguration{ Certificates: []*tls.CertAndStores{ { - Certificate: tls.Certificate{ - CertFile: tls.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), - KeyFile: tls.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), - }, + CertFile: tls.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), + KeyFile: tls.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), }, }, }, @@ -3726,10 +3707,8 @@ func TestLoadMixedRoutes(t *testing.T) { TLS: &dynamic.TLSConfiguration{ Certificates: []*tls.CertAndStores{ { - Certificate: tls.Certificate{ - CertFile: tls.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), - KeyFile: tls.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), - }, + CertFile: tls.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), + KeyFile: tls.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), }, }, }, @@ -3960,10 +3939,8 @@ func TestLoadMixedRoutes(t *testing.T) { TLS: &dynamic.TLSConfiguration{ Certificates: []*tls.CertAndStores{ { - Certificate: tls.Certificate{ - CertFile: tls.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), - KeyFile: tls.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), - }, + CertFile: tls.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), + KeyFile: tls.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), }, }, }, @@ -4107,10 +4084,8 @@ func TestLoadMixedRoutes(t *testing.T) { TLS: &dynamic.TLSConfiguration{ Certificates: []*tls.CertAndStores{ { - Certificate: tls.Certificate{ - CertFile: tls.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), - KeyFile: tls.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), - }, + CertFile: tls.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), + KeyFile: tls.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), }, }, }, @@ -4235,10 +4210,8 @@ func TestLoadMixedRoutes(t *testing.T) { TLS: &dynamic.TLSConfiguration{ Certificates: []*tls.CertAndStores{ { - Certificate: tls.Certificate{ - CertFile: tls.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), - KeyFile: tls.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), - }, + CertFile: tls.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), + KeyFile: tls.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), }, }, }, @@ -4708,10 +4681,8 @@ func Test_shouldAttach(t *testing.T) { { desc: "No ParentRefs", gateway: &gatev1alpha2.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "gateway", - Namespace: "default", - }, + Name: "gateway", + Namespace: "default", }, listener: gatev1alpha2.Listener{ Name: "foo", @@ -4725,10 +4696,8 @@ func Test_shouldAttach(t *testing.T) { { desc: "Unsupported Kind", gateway: &gatev1alpha2.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "gateway", - Namespace: "default", - }, + Name: "gateway", + Namespace: "default", }, listener: gatev1alpha2.Listener{ Name: "foo", @@ -4750,10 +4719,8 @@ func Test_shouldAttach(t *testing.T) { { desc: "Unsupported Group", gateway: &gatev1alpha2.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "gateway", - Namespace: "default", - }, + Name: "gateway", + Namespace: "default", }, listener: gatev1alpha2.Listener{ Name: "foo", @@ -4775,10 +4742,8 @@ func Test_shouldAttach(t *testing.T) { { desc: "Kind is nil", gateway: &gatev1alpha2.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "gateway", - Namespace: "default", - }, + Name: "gateway", + Namespace: "default", }, listener: gatev1alpha2.Listener{ Name: "foo", @@ -4799,10 +4764,8 @@ func Test_shouldAttach(t *testing.T) { { desc: "Group is nil", gateway: &gatev1alpha2.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "gateway", - Namespace: "default", - }, + Name: "gateway", + Namespace: "default", }, listener: gatev1alpha2.Listener{ Name: "foo", @@ -4823,10 +4786,8 @@ func Test_shouldAttach(t *testing.T) { { desc: "SectionName does not match a listener desc", gateway: &gatev1alpha2.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "gateway", - Namespace: "default", - }, + Name: "gateway", + Namespace: "default", }, listener: gatev1alpha2.Listener{ Name: "foo", @@ -4848,10 +4809,8 @@ func Test_shouldAttach(t *testing.T) { { desc: "Namespace does not match the Gateway namespace", gateway: &gatev1alpha2.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "gateway", - Namespace: "default", - }, + Name: "gateway", + Namespace: "default", }, listener: gatev1alpha2.Listener{ Name: "foo", @@ -4873,10 +4832,8 @@ func Test_shouldAttach(t *testing.T) { { desc: "Route namespace does not match the Gateway namespace", gateway: &gatev1alpha2.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "gateway", - Namespace: "default", - }, + Name: "gateway", + Namespace: "default", }, listener: gatev1alpha2.Listener{ Name: "foo", @@ -4897,10 +4854,8 @@ func Test_shouldAttach(t *testing.T) { { desc: "Unsupported Kind", gateway: &gatev1alpha2.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "gateway", - Namespace: "default", - }, + Name: "gateway", + Namespace: "default", }, listener: gatev1alpha2.Listener{ Name: "foo", @@ -4922,10 +4877,8 @@ func Test_shouldAttach(t *testing.T) { { desc: "Route namespace matches the Gateway namespace", gateway: &gatev1alpha2.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "gateway", - Namespace: "default", - }, + Name: "gateway", + Namespace: "default", }, listener: gatev1alpha2.Listener{ Name: "foo", @@ -4946,10 +4899,8 @@ func Test_shouldAttach(t *testing.T) { { desc: "Namespace matches the Gateway namespace", gateway: &gatev1alpha2.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "gateway", - Namespace: "default", - }, + Name: "gateway", + Namespace: "default", }, listener: gatev1alpha2.Listener{ Name: "foo", @@ -4971,10 +4922,8 @@ func Test_shouldAttach(t *testing.T) { { desc: "Only one ParentRef matches the Gateway", gateway: &gatev1alpha2.Gateway{ - ObjectMeta: metav1.ObjectMeta{ - Name: "gateway", - Namespace: "default", - }, + Name: "gateway", + Namespace: "default", }, listener: gatev1alpha2.Listener{ Name: "foo", diff --git a/pkg/provider/kubernetes/ingress/client_test.go b/pkg/provider/kubernetes/ingress/client_test.go index 3752dd68ab..455368e5fe 100644 --- a/pkg/provider/kubernetes/ingress/client_test.go +++ b/pkg/provider/kubernetes/ingress/client_test.go @@ -134,18 +134,14 @@ func TestIsLoadBalancerIngressEquals(t *testing.T) { func TestClientIgnoresHelmOwnedSecrets(t *testing.T) { secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "secret", - }, + Namespace: "default", + Name: "secret", } helmSecret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "helm-secret", - Labels: map[string]string{ - "owner": "helm", - }, + Namespace: "default", + Name: "helm-secret", + Labels: map[string]string{ + "owner": "helm", }, } @@ -190,22 +186,18 @@ func TestClientIgnoresHelmOwnedSecrets(t *testing.T) { func TestClientIgnoresEmptyEndpointUpdates(t *testing.T) { emptyEndpoint := &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - Name: "empty-endpoint", - Namespace: "test", - ResourceVersion: "1244", - Annotations: map[string]string{ - "test-annotation": "_", - }, + Name: "empty-endpoint", + Namespace: "test", + ResourceVersion: "1244", + Annotations: map[string]string{ + "test-annotation": "_", }, } filledEndpoint := &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - Name: "filled-endpoint", - Namespace: "test", - ResourceVersion: "1234", - }, + Name: "filled-endpoint", + Namespace: "test", + ResourceVersion: "1234", Subsets: []corev1.EndpointSubset{{ Addresses: []corev1.EndpointAddress{{ IP: "10.13.37.1", @@ -289,17 +281,13 @@ func TestClientIgnoresEmptyEndpointUpdates(t *testing.T) { func TestClientUsesCorrectServerVersion(t *testing.T) { ingressV1Beta := &netv1beta1.Ingress{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "ingress-v1beta", - }, + Namespace: "default", + Name: "ingress-v1beta", } ingressV1 := &netv1.Ingress{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "ingress-v1", - }, + Namespace: "default", + Name: "ingress-v1", } kubeClient := kubefake.NewClientset(ingressV1Beta, ingressV1) diff --git a/pkg/provider/kubernetes/ingress/kubernetes.go b/pkg/provider/kubernetes/ingress/kubernetes.go index bf62999d9d..f707f0c4dd 100644 --- a/pkg/provider/kubernetes/ingress/kubernetes.go +++ b/pkg/provider/kubernetes/ingress/kubernetes.go @@ -483,10 +483,8 @@ func getCertificates(ctx context.Context, ingress *netv1.Ingress, k8sClient Clie } tlsConfigs[configKey] = &tls.CertAndStores{ - Certificate: tls.Certificate{ - CertFile: tls.FileOrContent(cert), - KeyFile: tls.FileOrContent(key), - }, + CertFile: tls.FileOrContent(cert), + KeyFile: tls.FileOrContent(key), } } } diff --git a/pkg/provider/kubernetes/ingress/kubernetes_test.go b/pkg/provider/kubernetes/ingress/kubernetes_test.go index 94d6625ba7..c5556ce674 100644 --- a/pkg/provider/kubernetes/ingress/kubernetes_test.go +++ b/pkg/provider/kubernetes/ingress/kubernetes_test.go @@ -15,7 +15,6 @@ import ( "github.com/traefik/traefik/v2/pkg/types" corev1 "k8s.io/api/core/v1" netv1 "k8s.io/api/networking/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) var _ provider.Provider = (*Provider)(nil) @@ -824,10 +823,8 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { TLS: &dynamic.TLSConfiguration{ Certificates: []*tls.CertAndStores{ { - Certificate: tls.Certificate{ - CertFile: tls.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), - KeyFile: tls.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), - }, + CertFile: tls.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), + KeyFile: tls.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), }, }, }, @@ -2135,10 +2132,8 @@ func TestGetCertificates(t *testing.T) { client: clientMock{ secrets: []*corev1.Secret{ { - ObjectMeta: metav1.ObjectMeta{ - Name: "test-secret", - Namespace: "testing", - }, + Name: "test-secret", + Namespace: "testing", Data: map[string][]byte{ "tls.key": []byte("tls-key"), }, @@ -2153,10 +2148,8 @@ func TestGetCertificates(t *testing.T) { client: clientMock{ secrets: []*corev1.Secret{ { - ObjectMeta: metav1.ObjectMeta{ - Name: "test-secret", - Namespace: "testing", - }, + Name: "test-secret", + Namespace: "testing", Data: map[string][]byte{ "tls.crt": []byte("tls-crt"), }, @@ -2171,11 +2164,9 @@ func TestGetCertificates(t *testing.T) { client: clientMock{ secrets: []*corev1.Secret{ { - ObjectMeta: metav1.ObjectMeta{ - Name: "test-secret", - Namespace: "testing", - }, - Data: map[string][]byte{}, + Name: "test-secret", + Namespace: "testing", + Data: map[string][]byte{}, }, }, }, @@ -2198,20 +2189,16 @@ func TestGetCertificates(t *testing.T) { client: clientMock{ secrets: []*corev1.Secret{ { - ObjectMeta: metav1.ObjectMeta{ - Name: "test-secret2", - Namespace: "testing", - }, + Name: "test-secret2", + Namespace: "testing", Data: map[string][]byte{ "tls.crt": []byte("tls-crt"), "tls.key": []byte("tls-key"), }, }, { - ObjectMeta: metav1.ObjectMeta{ - Name: "test-secret", - Namespace: "testing", - }, + Name: "test-secret", + Namespace: "testing", Data: map[string][]byte{ "tls.crt": []byte("tls-crt"), "tls.key": []byte("tls-key"), diff --git a/pkg/provider/kubernetes/k8s/event_handler_test.go b/pkg/provider/kubernetes/k8s/event_handler_test.go index 4d88adb77b..e7a2c443fe 100644 --- a/pkg/provider/kubernetes/k8s/event_handler_test.go +++ b/pkg/provider/kubernetes/k8s/event_handler_test.go @@ -6,7 +6,6 @@ import ( "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" netv1 "k8s.io/api/networking/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func Test_detectChanges(t *testing.T) { @@ -38,139 +37,103 @@ func Test_detectChanges(t *testing.T) { { name: "With same version", oldObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "1", - }, + ResourceVersion: "1", }, newObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "1", - }, + ResourceVersion: "1", }, }, { name: "With different version", oldObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "1", - }, + ResourceVersion: "1", }, newObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "2", - }, + ResourceVersion: "2", }, }, { name: "Ingress With same version", oldObj: &netv1.Ingress{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "1", - }, + ResourceVersion: "1", }, newObj: &netv1.Ingress{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "1", - }, + ResourceVersion: "1", }, }, { name: "Ingress With different version", oldObj: &netv1.Ingress{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "1", - }, + ResourceVersion: "1", }, newObj: &netv1.Ingress{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "2", - }, + ResourceVersion: "2", }, want: true, }, { name: "With same annotations", oldObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "1", - Annotations: map[string]string{ - "test-annotation": "_", - }, + ResourceVersion: "1", + Annotations: map[string]string{ + "test-annotation": "_", }, }, newObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "2", - Annotations: map[string]string{ - "test-annotation": "_", - }, + ResourceVersion: "2", + Annotations: map[string]string{ + "test-annotation": "_", }, }, }, { name: "With different annotations", oldObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "1", - Annotations: map[string]string{ - "test-annotation": "V", - }, + ResourceVersion: "1", + Annotations: map[string]string{ + "test-annotation": "V", }, }, newObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "2", - Annotations: map[string]string{ - "test-annotation": "X", - }, + ResourceVersion: "2", + Annotations: map[string]string{ + "test-annotation": "X", }, }, }, { name: "With same subsets", oldObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "1", - }, - Subsets: []corev1.EndpointSubset{}, + ResourceVersion: "1", + Subsets: []corev1.EndpointSubset{}, }, newObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "1", - }, - Subsets: []corev1.EndpointSubset{}, + ResourceVersion: "1", + Subsets: []corev1.EndpointSubset{}, }, }, { name: "With different len of subsets", oldObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "1", - }, - Subsets: []corev1.EndpointSubset{}, + ResourceVersion: "1", + Subsets: []corev1.EndpointSubset{}, }, newObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "2", - }, - Subsets: []corev1.EndpointSubset{{}}, + ResourceVersion: "2", + Subsets: []corev1.EndpointSubset{{}}, }, want: true, }, { name: "With same subsets with same len of addresses", oldObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "1", - }, + ResourceVersion: "1", Subsets: []corev1.EndpointSubset{{ Addresses: []corev1.EndpointAddress{}, }}, }, newObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "2", - }, + ResourceVersion: "2", Subsets: []corev1.EndpointSubset{{ Addresses: []corev1.EndpointAddress{}, }}, @@ -179,17 +142,13 @@ func Test_detectChanges(t *testing.T) { { name: "With same subsets with different len of addresses", oldObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "1", - }, + ResourceVersion: "1", Subsets: []corev1.EndpointSubset{{ Addresses: []corev1.EndpointAddress{}, }}, }, newObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "2", - }, + ResourceVersion: "2", Subsets: []corev1.EndpointSubset{{ Addresses: []corev1.EndpointAddress{{}}, }}, @@ -199,17 +158,13 @@ func Test_detectChanges(t *testing.T) { { name: "With same subsets with same len of ports", oldObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "1", - }, + ResourceVersion: "1", Subsets: []corev1.EndpointSubset{{ Ports: []corev1.EndpointPort{}, }}, }, newObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "2", - }, + ResourceVersion: "2", Subsets: []corev1.EndpointSubset{{ Ports: []corev1.EndpointPort{}, }}, @@ -218,17 +173,13 @@ func Test_detectChanges(t *testing.T) { { name: "With same subsets with different len of ports", oldObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "1", - }, + ResourceVersion: "1", Subsets: []corev1.EndpointSubset{{ Ports: []corev1.EndpointPort{}, }}, }, newObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "2", - }, + ResourceVersion: "2", Subsets: []corev1.EndpointSubset{{ Ports: []corev1.EndpointPort{{}}, }}, @@ -238,9 +189,7 @@ func Test_detectChanges(t *testing.T) { { name: "With same subsets with same len of addresses with same ip", oldObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "1", - }, + ResourceVersion: "1", Subsets: []corev1.EndpointSubset{{ Addresses: []corev1.EndpointAddress{{ IP: "10.10.10.10", @@ -248,9 +197,7 @@ func Test_detectChanges(t *testing.T) { }}, }, newObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "2", - }, + ResourceVersion: "2", Subsets: []corev1.EndpointSubset{{ Addresses: []corev1.EndpointAddress{{ IP: "10.10.10.10", @@ -261,9 +208,7 @@ func Test_detectChanges(t *testing.T) { { name: "With same subsets with same len of addresses with different ip", oldObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "1", - }, + ResourceVersion: "1", Subsets: []corev1.EndpointSubset{{ Addresses: []corev1.EndpointAddress{{ IP: "10.10.10.10", @@ -271,9 +216,7 @@ func Test_detectChanges(t *testing.T) { }}, }, newObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "2", - }, + ResourceVersion: "2", Subsets: []corev1.EndpointSubset{{ Addresses: []corev1.EndpointAddress{{ IP: "10.10.10.42", @@ -285,9 +228,7 @@ func Test_detectChanges(t *testing.T) { { name: "With same subsets with same len of addresses with same hostname", oldObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "1", - }, + ResourceVersion: "1", Subsets: []corev1.EndpointSubset{{ Addresses: []corev1.EndpointAddress{{ Hostname: "foo", @@ -295,9 +236,7 @@ func Test_detectChanges(t *testing.T) { }}, }, newObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "2", - }, + ResourceVersion: "2", Subsets: []corev1.EndpointSubset{{ Addresses: []corev1.EndpointAddress{{ Hostname: "foo", @@ -308,9 +247,7 @@ func Test_detectChanges(t *testing.T) { { name: "With same subsets with same len of addresses with same hostname", oldObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "1", - }, + ResourceVersion: "1", Subsets: []corev1.EndpointSubset{{ Addresses: []corev1.EndpointAddress{{ Hostname: "foo", @@ -318,9 +255,7 @@ func Test_detectChanges(t *testing.T) { }}, }, newObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "2", - }, + ResourceVersion: "2", Subsets: []corev1.EndpointSubset{{ Addresses: []corev1.EndpointAddress{{ Hostname: "bar", @@ -332,9 +267,7 @@ func Test_detectChanges(t *testing.T) { { name: "With same subsets with same len of port with same name", oldObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "1", - }, + ResourceVersion: "1", Subsets: []corev1.EndpointSubset{{ Ports: []corev1.EndpointPort{{ Name: "foo", @@ -342,9 +275,7 @@ func Test_detectChanges(t *testing.T) { }}, }, newObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "2", - }, + ResourceVersion: "2", Subsets: []corev1.EndpointSubset{{ Ports: []corev1.EndpointPort{{ Name: "foo", @@ -355,9 +286,7 @@ func Test_detectChanges(t *testing.T) { { name: "With same subsets with same len of port with different name", oldObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "1", - }, + ResourceVersion: "1", Subsets: []corev1.EndpointSubset{{ Ports: []corev1.EndpointPort{{ Name: "foo", @@ -365,9 +294,7 @@ func Test_detectChanges(t *testing.T) { }}, }, newObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "2", - }, + ResourceVersion: "2", Subsets: []corev1.EndpointSubset{{ Ports: []corev1.EndpointPort{{ Name: "bar", @@ -379,9 +306,7 @@ func Test_detectChanges(t *testing.T) { { name: "With same subsets with same len of port with same port", oldObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "1", - }, + ResourceVersion: "1", Subsets: []corev1.EndpointSubset{{ Ports: []corev1.EndpointPort{{ Port: 4242, @@ -389,9 +314,7 @@ func Test_detectChanges(t *testing.T) { }}, }, newObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "2", - }, + ResourceVersion: "2", Subsets: []corev1.EndpointSubset{{ Ports: []corev1.EndpointPort{{ Port: 4242, @@ -402,9 +325,7 @@ func Test_detectChanges(t *testing.T) { { name: "With same subsets with same len of port with different port", oldObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "1", - }, + ResourceVersion: "1", Subsets: []corev1.EndpointSubset{{ Ports: []corev1.EndpointPort{{ Port: 4242, @@ -412,9 +333,7 @@ func Test_detectChanges(t *testing.T) { }}, }, newObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "2", - }, + ResourceVersion: "2", Subsets: []corev1.EndpointSubset{{ Ports: []corev1.EndpointPort{{ Port: 6969, @@ -426,9 +345,7 @@ func Test_detectChanges(t *testing.T) { { name: "With same subsets with same len of port with same protocol", oldObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "1", - }, + ResourceVersion: "1", Subsets: []corev1.EndpointSubset{{ Ports: []corev1.EndpointPort{{ Protocol: "HTTP", @@ -436,9 +353,7 @@ func Test_detectChanges(t *testing.T) { }}, }, newObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "2", - }, + ResourceVersion: "2", Subsets: []corev1.EndpointSubset{{ Ports: []corev1.EndpointPort{{ Protocol: "HTTP", @@ -449,9 +364,7 @@ func Test_detectChanges(t *testing.T) { { name: "With same subsets with same len of port with different protocol", oldObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "1", - }, + ResourceVersion: "1", Subsets: []corev1.EndpointSubset{{ Ports: []corev1.EndpointPort{{ Protocol: "HTTP", @@ -459,9 +372,7 @@ func Test_detectChanges(t *testing.T) { }}, }, newObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "2", - }, + ResourceVersion: "2", Subsets: []corev1.EndpointSubset{{ Ports: []corev1.EndpointPort{{ Protocol: "TCP", @@ -473,9 +384,7 @@ func Test_detectChanges(t *testing.T) { { name: "With same subsets with same subset", oldObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "1", - }, + ResourceVersion: "1", Subsets: []corev1.EndpointSubset{{ Addresses: []corev1.EndpointAddress{{ IP: "10.10.10.10", @@ -489,9 +398,7 @@ func Test_detectChanges(t *testing.T) { }}, }, newObj: &corev1.Endpoints{ - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: "2", - }, + ResourceVersion: "2", Subsets: []corev1.EndpointSubset{{ Addresses: []corev1.EndpointAddress{{ IP: "10.10.10.10", diff --git a/pkg/provider/kv/kv_test.go b/pkg/provider/kv/kv_test.go index be693ed9e5..8d1ffc72e7 100644 --- a/pkg/provider/kv/kv_test.go +++ b/pkg/provider/kv/kv_test.go @@ -825,20 +825,16 @@ func Test_buildConfiguration(t *testing.T) { TLS: &dynamic.TLSConfiguration{ Certificates: []*tls.CertAndStores{ { - Certificate: tls.Certificate{ - CertFile: tls.FileOrContent("foobar"), - KeyFile: tls.FileOrContent("foobar"), - }, + CertFile: tls.FileOrContent("foobar"), + KeyFile: tls.FileOrContent("foobar"), Stores: []string{ "foobar", "foobar", }, }, { - Certificate: tls.Certificate{ - CertFile: tls.FileOrContent("foobar"), - KeyFile: tls.FileOrContent("foobar"), - }, + CertFile: tls.FileOrContent("foobar"), + KeyFile: tls.FileOrContent("foobar"), Stores: []string{ "foobar", "foobar", diff --git a/pkg/provider/nomad/nomad_test.go b/pkg/provider/nomad/nomad_test.go index e6a11c60c8..547e742c44 100644 --- a/pkg/provider/nomad/nomad_test.go +++ b/pkg/provider/nomad/nomad_test.go @@ -66,10 +66,8 @@ func Test_globalConfig(t *testing.T) { for _, test := range cases { t.Run(test.Name, func(t *testing.T) { p := Provider{ - Configuration: Configuration{ - ExposedByDefault: test.ExposedByDefault, - Prefix: test.Prefix, - }, + ExposedByDefault: test.ExposedByDefault, + Prefix: test.Prefix, } result := p.getExtraConf(test.Tags) require.Equal(t, test.exp, result) diff --git a/pkg/redactor/redactor_config_test.go b/pkg/redactor/redactor_config_test.go index a8df514d37..fd15d776d8 100644 --- a/pkg/redactor/redactor_config_test.go +++ b/pkg/redactor/redactor_config_test.go @@ -23,7 +23,6 @@ import ( "github.com/traefik/traefik/v2/pkg/provider/kubernetes/crd" "github.com/traefik/traefik/v2/pkg/provider/kubernetes/gateway" "github.com/traefik/traefik/v2/pkg/provider/kubernetes/ingress" - "github.com/traefik/traefik/v2/pkg/provider/kv" "github.com/traefik/traefik/v2/pkg/provider/kv/consul" "github.com/traefik/traefik/v2/pkg/provider/kv/etcd" "github.com/traefik/traefik/v2/pkg/provider/kv/redis" @@ -448,11 +447,9 @@ func init() { }, Certificates: []*traefiktls.CertAndStores{ { - Certificate: traefiktls.Certificate{ - CertFile: "cert.pem", - KeyFile: "key.pem", - }, - Stores: []string{"foo"}, + CertFile: "cert.pem", + KeyFile: "key.pem", + Stores: []string{"foo"}, }, }, Stores: map[string]traefiktls.Store{ @@ -683,36 +680,34 @@ func TestDo_staticConfiguration(t *testing.T) { } config.Providers.ConsulCatalog = &consulcatalog.ProviderBuilder{ - Configuration: consulcatalog.Configuration{ - Constraints: `Label("foo", "bar")`, - Endpoint: &consulcatalog.EndpointConfig{ - Address: "MyAddress", - Scheme: "MyScheme", - DataCenter: "MyDatacenter", - Token: "MyToken", - TLS: &types.ClientTLS{ - CA: "myCa", - CAOptional: true, - Cert: "mycert.pem", - Key: "mycert.key", - InsecureSkipVerify: true, - }, - HTTPAuth: &consulcatalog.EndpointHTTPAuthConfig{ - Username: "MyUsername", - Password: "MyPassword", - }, - EndpointWaitTime: 42, + Constraints: `Label("foo", "bar")`, + Endpoint: &consulcatalog.EndpointConfig{ + Address: "MyAddress", + Scheme: "MyScheme", + DataCenter: "MyDatacenter", + Token: "MyToken", + TLS: &types.ClientTLS{ + CA: "myCa", + CAOptional: true, + Cert: "mycert.pem", + Key: "mycert.key", + InsecureSkipVerify: true, }, - Prefix: "MyPrefix", - RefreshInterval: 42, - RequireConsistent: true, - Stale: true, - Cache: true, - ExposedByDefault: true, - DefaultRule: "PathPrefix(`/`)", + HTTPAuth: &consulcatalog.EndpointHTTPAuthConfig{ + Username: "MyUsername", + Password: "MyPassword", + }, + EndpointWaitTime: 42, }, - Namespace: "ns", - Namespaces: []string{"ns1", "ns2"}, + Prefix: "MyPrefix", + RefreshInterval: 42, + RequireConsistent: true, + Stale: true, + Cache: true, + ExposedByDefault: true, + DefaultRule: "PathPrefix(`/`)", + Namespace: "ns", + Namespaces: []string{"ns1", "ns2"}, } config.Providers.Ecs = &ecs.Provider{ @@ -729,11 +724,9 @@ func TestDo_staticConfiguration(t *testing.T) { } config.Providers.Consul = &consul.ProviderBuilder{ - Provider: kv.Provider{ - RootKey: "RootKey", - Endpoints: nil, - }, - Token: "secret", + RootKey: "RootKey", + Endpoints: nil, + Token: "secret", TLS: &types.ClientTLS{ CA: "myCa", CAOptional: true, @@ -746,12 +739,10 @@ func TestDo_staticConfiguration(t *testing.T) { } config.Providers.Etcd = &etcd.Provider{ - Provider: kv.Provider{ - RootKey: "RootKey", - Endpoints: nil, - }, - Username: "username", - Password: "password", + RootKey: "RootKey", + Endpoints: nil, + Username: "username", + Password: "password", TLS: &types.ClientTLS{ CA: "myCa", CAOptional: true, @@ -762,21 +753,17 @@ func TestDo_staticConfiguration(t *testing.T) { } config.Providers.ZooKeeper = &zk.Provider{ - Provider: kv.Provider{ - RootKey: "RootKey", - Endpoints: nil, - }, - Username: "username", - Password: "password", + RootKey: "RootKey", + Endpoints: nil, + Username: "username", + Password: "password", } config.Providers.Redis = &redis.Provider{ - Provider: kv.Provider{ - RootKey: "RootKey", - Endpoints: nil, - }, - Username: "username", - Password: "password", + RootKey: "RootKey", + Endpoints: nil, + Username: "username", + Password: "password", TLS: &types.ClientTLS{ CA: "myCa", CAOptional: true, diff --git a/pkg/server/aggregator_test.go b/pkg/server/aggregator_test.go index c546df1b46..0daa811762 100644 --- a/pkg/server/aggregator_test.go +++ b/pkg/server/aggregator_test.go @@ -149,15 +149,17 @@ func Test_mergeConfiguration_tlsCertificates(t *testing.T) { "tlsalpn.acme": &dynamic.Configuration{ TLS: &dynamic.TLSConfiguration{ Certificates: []*tls.CertAndStores{{ - Certificate: tls.Certificate{CertFile: "foo", KeyFile: "bar"}, - Stores: []string{tlsalpn01.ACMETLS1Protocol}, + CertFile: "foo", + KeyFile: "bar", + Stores: []string{tlsalpn01.ACMETLS1Protocol}, }}, }, }, }, expected: []*tls.CertAndStores{{ - Certificate: tls.Certificate{CertFile: "foo", KeyFile: "bar"}, - Stores: []string{tlsalpn01.ACMETLS1Protocol}, + CertFile: "foo", + KeyFile: "bar", + Stores: []string{tlsalpn01.ACMETLS1Protocol}, }}, }, } diff --git a/pkg/server/router/tcp/router_test.go b/pkg/server/router/tcp/router_test.go index 8e25d9a5cc..e4ab6b4dfa 100644 --- a/pkg/server/router/tcp/router_test.go +++ b/pkg/server/router/tcp/router_test.go @@ -192,8 +192,8 @@ func Test_Routing(t *testing.T) { }, }, []*traefiktls.CertAndStores{{ - Certificate: traefiktls.Certificate{CertFile: traefiktls.FileOrContent(certPEM), KeyFile: traefiktls.FileOrContent(keyPEM)}, - Stores: []string{tlsalpn01.ACMETLS1Protocol}, + CertFile: traefiktls.FileOrContent(certPEM), KeyFile: traefiktls.FileOrContent(keyPEM), + Stores: []string{tlsalpn01.ACMETLS1Protocol}, }}) middlewaresBuilder := tcpmiddleware.NewBuilder(conf.TCPMiddlewares) diff --git a/pkg/server/service/roundtripper.go b/pkg/server/service/roundtripper.go index c783c6e905..bf81faa402 100644 --- a/pkg/server/service/roundtripper.go +++ b/pkg/server/service/roundtripper.go @@ -17,18 +17,8 @@ import ( "github.com/traefik/traefik/v2/pkg/config/dynamic" "github.com/traefik/traefik/v2/pkg/log" traefiktls "github.com/traefik/traefik/v2/pkg/tls" - "golang.org/x/net/http2" ) -type h2cTransportWrapper struct { - *http2.Transport -} - -func (t *h2cTransportWrapper) RoundTrip(req *http.Request) (*http.Response, error) { - req.URL.Scheme = "http" - return t.Transport.RoundTrip(req) -} - // RoundTripperManager handles roundtripper for the reverse proxy. type RoundTripperManager struct { rtLock sync.RWMutex @@ -133,6 +123,16 @@ func createRoundTripper(cfg *dynamic.ServersTransport) (http.RoundTripper, error if cfg.ForwardingTimeouts != nil { transport.ResponseHeaderTimeout = time.Duration(cfg.ForwardingTimeouts.ResponseHeaderTimeout) transport.IdleConnTimeout = time.Duration(cfg.ForwardingTimeouts.IdleConnTimeout) + // The forwarding timeout names come from the x/net/http2.Transport fields (ReadIdleTimeout/PingTimeout), + // which were used to configure the HTTP/2 health checks before the net/http native support (Go 1.24). + // HTTP2Config.SendPingTimeout carries the same semantics as ReadIdleTimeout: + // the delay without any frame received on a connection after which a ping health check is sent. + // The field was renamed when the HTTP/2 configuration moved to net/http, see https://go.dev/issue/67813. + // The HTTP2 config does not enable HTTP2 protocol. + transport.HTTP2 = &http.HTTP2Config{ + SendPingTimeout: time.Duration(cfg.ForwardingTimeouts.ReadIdleTimeout), + PingTimeout: time.Duration(cfg.ForwardingTimeouts.PingTimeout), + } } if cfg.InsecureSkipVerify || len(cfg.RootCAs) > 0 || len(cfg.ServerName) > 0 || len(cfg.Certificates) > 0 || cfg.PeerCertURI != "" { @@ -160,10 +160,8 @@ func createRoundTripper(cfg *dynamic.ServersTransport) (http.RoundTripper, error }, nil } - rt, err := newSmartRoundTripper(transport, cfg.ForwardingTimeouts) - if err != nil { - return nil, err - } + rt := newSmartRoundTripper(transport) + return &KerberosRoundTripper{ OriginalRoundTripper: rt, new: func() http.RoundTripper { diff --git a/pkg/server/service/roundtripper_test.go b/pkg/server/service/roundtripper_test.go index 189db81880..a1875dd7ac 100644 --- a/pkg/server/service/roundtripper_test.go +++ b/pkg/server/service/roundtripper_test.go @@ -1,19 +1,26 @@ package service import ( + "bytes" "crypto/tls" "crypto/x509" + "io" "net" "net/http" "net/http/httptest" + "net/url" "sync" "sync/atomic" "testing" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + ptypes "github.com/traefik/paerser/types" "github.com/traefik/traefik/v2/pkg/config/dynamic" traefiktls "github.com/traefik/traefik/v2/pkg/tls" + "golang.org/x/net/http2" + "golang.org/x/net/http2/hpack" ) // LocalhostCert is a PEM-encoded TLS cert @@ -261,6 +268,7 @@ func TestDisableHTTP2(t *testing.T) { srv := httptest.NewUnstartedServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { rw.WriteHeader(http.StatusOK) })) + defer srv.Close() srv.EnableHTTP2 = test.serverHTTP2 srv.StartTLS() @@ -290,6 +298,188 @@ func TestDisableHTTP2(t *testing.T) { } } +func TestHTTP2HealthCheckTimeouts(t *testing.T) { + rt, err := createRoundTripper(&dynamic.ServersTransport{ + ForwardingTimeouts: &dynamic.ForwardingTimeouts{ + ReadIdleTimeout: ptypes.Duration(10 * time.Second), + PingTimeout: ptypes.Duration(15 * time.Second), + }, + }) + require.NoError(t, err) + + krt, ok := rt.(*KerberosRoundTripper) + require.True(t, ok) + + srt, ok := krt.OriginalRoundTripper.(*smartRoundTripper) + require.True(t, ok) + + expected := &http.HTTP2Config{ + SendPingTimeout: 10 * time.Second, + PingTimeout: 15 * time.Second, + } + assert.Equal(t, expected, srt.http.HTTP2) + assert.Equal(t, expected, srt.http2.HTTP2) + assert.Equal(t, expected, srt.h2c.HTTP2) +} + +func TestProxyHTTP2BackendScheme(t *testing.T) { + testCases := []struct { + desc string + tls bool + expectedScheme string + }{ + { + desc: "h2c backend", + expectedScheme: "http", + }, + { + desc: "https backend", + tls: true, + expectedScheme: "https", + }, + } + + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + backendURL, schemes := startHTTP2FramerBackend(t, test.tls) + + rtManager := NewRoundTripperManager() + rtManager.Update(map[string]*dynamic.ServersTransport{ + "test": { + InsecureSkipVerify: true, + }, + }) + + rt, err := rtManager.Get("test") + require.NoError(t, err) + + proxy, err := buildProxy(new(true), nil, rt, newBufferPool()) + require.NoError(t, err) + + // The load balancer sets the backend server on the request URL before the proxy runs. + handler := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + req.URL.Scheme = backendURL.Scheme + req.URL.Host = backendURL.Host + proxy.ServeHTTP(rw, req) + }) + + srv := httptest.NewServer(handler) + t.Cleanup(srv.Close) + + req, err := http.NewRequestWithContext(t.Context(), http.MethodGet, srv.URL, nil) + require.NoError(t, err) + + res, err := http.DefaultClient.Do(req) + require.NoError(t, err) + t.Cleanup(func() { _ = res.Body.Close() }) + + assert.Equal(t, http.StatusOK, res.StatusCode) + + select { + case scheme := <-schemes: + assert.Equal(t, test.expectedScheme, scheme) + default: + t.Error("backend did not receive an HTTP/2 request") + } + }) + } +} + +// startHTTP2FramerBackend starts a backend speaking raw HTTP/2 frames, +// as the standard library server does not expose the :scheme pseudo-header. +func startHTTP2FramerBackend(t *testing.T, useTLS bool) (*url.URL, <-chan string) { + t.Helper() + + ln, err := net.Listen("tcp", "127.0.0.1:0") + require.NoError(t, err) + + scheme := "h2c" + if useTLS { + cert, err := tls.X509KeyPair(LocalhostCert, LocalhostKey) + require.NoError(t, err) + + ln = tls.NewListener(ln, &tls.Config{ + Certificates: []tls.Certificate{cert}, + NextProtos: []string{"h2"}, + }) + scheme = "https" + } + t.Cleanup(func() { _ = ln.Close() }) + + schemes := make(chan string, 1) + go func() { + for { + conn, err := ln.Accept() + if err != nil { + return + } + + go serveHTTP2Frames(conn, schemes) + } + }() + + return &url.URL{Scheme: scheme, Host: ln.Addr().String()}, schemes +} + +func serveHTTP2Frames(conn net.Conn, schemes chan<- string) { + defer func() { _ = conn.Close() }() + + _ = conn.SetDeadline(time.Now().Add(5 * time.Second)) + + preface := make([]byte, len(http2.ClientPreface)) + if _, err := io.ReadFull(conn, preface); err != nil || string(preface) != http2.ClientPreface { + return + } + + framer := http2.NewFramer(conn, conn) + framer.ReadMetaHeaders = hpack.NewDecoder(4096, nil) + + if err := framer.WriteSettings(); err != nil { + return + } + + for { + frame, err := framer.ReadFrame() + if err != nil { + return + } + + switch f := frame.(type) { + case *http2.SettingsFrame: + if f.IsAck() { + continue + } + + if err := framer.WriteSettingsAck(); err != nil { + return + } + + case *http2.MetaHeadersFrame: + select { + case schemes <- f.PseudoValue("scheme"): + default: + } + + var headers bytes.Buffer + if err := hpack.NewEncoder(&headers).WriteField(hpack.HeaderField{Name: ":status", Value: "200"}); err != nil { + return + } + + err := framer.WriteHeaders(http2.HeadersFrameParam{ + StreamID: f.StreamID, + BlockFragment: headers.Bytes(), + EndHeaders: true, + EndStream: true, + }) + if err != nil { + return + } + } + } +} + type roundTripperFn func(req *http.Request) (*http.Response, error) func (r roundTripperFn) RoundTrip(request *http.Request) (*http.Response, error) { diff --git a/pkg/server/service/smart_roundtripper.go b/pkg/server/service/smart_roundtripper.go index 0d6c280bc8..782fee6f35 100644 --- a/pkg/server/service/smart_roundtripper.go +++ b/pkg/server/service/smart_roundtripper.go @@ -1,49 +1,33 @@ package service import ( - "crypto/tls" - "net" "net/http" - "time" - "github.com/traefik/traefik/v2/pkg/config/dynamic" "golang.org/x/net/http/httpguts" - "golang.org/x/net/http2" ) -func newSmartRoundTripper(transport *http.Transport, forwardingTimeouts *dynamic.ForwardingTimeouts) (*smartRoundTripper, error) { +func newSmartRoundTripper(transport *http.Transport) *smartRoundTripper { + // HTTP/1 only transport for requests with a Connection: Upgrade header. transportHTTP1 := transport.Clone() + transportHTTP1.Protocols = new(http.Protocols) + transportHTTP1.Protocols.SetHTTP1(true) - transportHTTP2, err := http2.ConfigureTransports(transport) - if err != nil { - return nil, err - } + // Transport switching automatically to HTTP/2 with TLS ALPN. + transportHTTP2 := transport.Clone() + transportHTTP2.Protocols = new(http.Protocols) + transportHTTP2.Protocols.SetHTTP1(true) + transportHTTP2.Protocols.SetHTTP2(true) - if forwardingTimeouts != nil { - transportHTTP2.ReadIdleTimeout = time.Duration(forwardingTimeouts.ReadIdleTimeout) - transportHTTP2.PingTimeout = time.Duration(forwardingTimeouts.PingTimeout) - } - - transportH2C := &h2cTransportWrapper{ - Transport: &http2.Transport{ - DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) { - return net.Dial(network, addr) - }, - AllowHTTP: true, - }, - } - - if forwardingTimeouts != nil { - transportH2C.ReadIdleTimeout = time.Duration(forwardingTimeouts.ReadIdleTimeout) - transportH2C.PingTimeout = time.Duration(forwardingTimeouts.PingTimeout) - } - - transport.RegisterProtocol("h2c", transportH2C) + // Transport speaking HTTP/2 with prior knowledge on unencrypted connections. + transportH2C := transport.Clone() + transportH2C.Protocols = new(http.Protocols) + transportH2C.Protocols.SetUnencryptedHTTP2(true) return &smartRoundTripper{ - http2: transport, + http2: transportHTTP2, http: transportHTTP1, - }, nil + h2c: transportH2C, + } } // smartRoundTripper implements RoundTrip while making sure that HTTP/2 is not used @@ -51,19 +35,31 @@ func newSmartRoundTripper(transport *http.Transport, forwardingTimeouts *dynamic type smartRoundTripper struct { http2 *http.Transport http *http.Transport + h2c *http.Transport } func (m *smartRoundTripper) Clone() http.RoundTripper { - h := m.http.Clone() - h2 := m.http2.Clone() - return &smartRoundTripper{http: h, http2: h2} + return &smartRoundTripper{ + http2: m.http2.Clone(), + http: m.http.Clone(), + h2c: m.h2c.Clone(), + } } func (m *smartRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { - // If we have a connection upgrade, we don't use HTTP/2 + h2c := req.URL.Scheme == "h2c" + if h2c { + req.URL.Scheme = "http" + } + + // Connection upgrades cannot be carried over HTTP/2, they always use HTTP/1. if httpguts.HeaderValuesContainsToken(req.Header["Connection"], "Upgrade") { return m.http.RoundTrip(req) } + if h2c { + return m.h2c.RoundTrip(req) + } + return m.http2.RoundTrip(req) } diff --git a/pkg/tls/tlsmanager_test.go b/pkg/tls/tlsmanager_test.go index 48490a3651..86025d2eb5 100644 --- a/pkg/tls/tlsmanager_test.go +++ b/pkg/tls/tlsmanager_test.go @@ -69,10 +69,8 @@ ZboOWVe3icTy64BT3OQhmg== func TestTLSInStore(t *testing.T) { dynamicConfigs := []*CertAndStores{{ - Certificate: Certificate{ - CertFile: localhostCert, - KeyFile: localhostKey, - }, + CertFile: localhostCert, + KeyFile: localhostKey, }} tlsManager := NewManager() @@ -86,10 +84,8 @@ func TestTLSInStore(t *testing.T) { func TestTLSInvalidStore(t *testing.T) { dynamicConfigs := []*CertAndStores{{ - Certificate: Certificate{ - CertFile: localhostCert, - KeyFile: localhostKey, - }, + CertFile: localhostCert, + KeyFile: localhostKey, }} tlsManager := NewManager() @@ -111,10 +107,8 @@ func TestTLSInvalidStore(t *testing.T) { func TestManager_Get(t *testing.T) { dynamicConfigs := []*CertAndStores{{ - Certificate: Certificate{ - CertFile: localhostCert, - KeyFile: localhostKey, - }, + CertFile: localhostCert, + KeyFile: localhostKey, }} tlsConfigs := map[string]Options{ From c0501112e1506d384942a30bd3887d9d9fe1befe Mon Sep 17 00:00:00 2001 From: Julien Salleyron Date: Wed, 21 Oct 2026 14:44:06 +0200 Subject: [PATCH 2/2] Add an entry point option to limit the number of request header values --- docs/content/migration/v2.md | 2 +- .../reference/static-configuration/cli-ref.md | 3 ++ .../reference/static-configuration/env-ref.md | 3 ++ .../reference/static-configuration/file.toml | 1 + .../reference/static-configuration/file.yaml | 1 + docs/content/routing/entrypoints.md | 34 ++++++++++++++ .../simple_max_header_value_count.toml | 25 +++++++++++ integration/simple_test.go | 45 +++++++++++++++++++ pkg/config/static/entrypoints.go | 2 + pkg/server/server_entrypoint_tcp.go | 15 ++++--- 10 files changed, 123 insertions(+), 8 deletions(-) create mode 100644 integration/fixtures/simple_max_header_value_count.toml diff --git a/docs/content/migration/v2.md b/docs/content/migration/v2.md index ad5493d1f4..c3b2b624f3 100644 --- a/docs/content/migration/v2.md +++ b/docs/content/migration/v2.md @@ -1222,7 +1222,7 @@ Go 1.27 limits the number of header values a request can carry to 500. A request exceeding this limit is rejected with a `431 Request Header Fields Too Large` response, for HTTP/1 and HTTP/2. -This limit is not configurable. +This limit can be changed with the new [`maxHeaderValueCount`](../routing/entrypoints.md#maxheadervaluecount) entry point option. As the rejection happens before the routing, the rejected requests are not reported in the access logs. diff --git a/docs/content/reference/static-configuration/cli-ref.md b/docs/content/reference/static-configuration/cli-ref.md index 217f381089..a233fa9b3d 100644 --- a/docs/content/reference/static-configuration/cli-ref.md +++ b/docs/content/reference/static-configuration/cli-ref.md @@ -156,6 +156,9 @@ Defines whether request query semicolons should be URLEncoded. (Default: ```fals `--entrypoints..http.maxheaderbytes`: Maximum size of request headers in bytes. (Default: ```1048576```) +`--entrypoints..http.maxheadervaluecount`: +Maximum number of request header values. (Default: ```500```) + `--entrypoints..http.middlewares`: Default middlewares for the routers linked to the entry point. diff --git a/docs/content/reference/static-configuration/env-ref.md b/docs/content/reference/static-configuration/env-ref.md index 4c9a934c63..ab63579284 100644 --- a/docs/content/reference/static-configuration/env-ref.md +++ b/docs/content/reference/static-configuration/env-ref.md @@ -165,6 +165,9 @@ Defines whether request query semicolons should be URLEncoded. (Default: ```fals `TRAEFIK_ENTRYPOINTS__HTTP_MAXHEADERBYTES`: Maximum size of request headers in bytes. (Default: ```1048576```) +`TRAEFIK_ENTRYPOINTS__HTTP_MAXHEADERVALUECOUNT`: +Maximum number of request header values. (Default: ```500```) + `TRAEFIK_ENTRYPOINTS__HTTP_MIDDLEWARES`: Default middlewares for the routers linked to the entry point. diff --git a/docs/content/reference/static-configuration/file.toml b/docs/content/reference/static-configuration/file.toml index 7204e7773d..3fd3bf5b9f 100644 --- a/docs/content/reference/static-configuration/file.toml +++ b/docs/content/reference/static-configuration/file.toml @@ -42,6 +42,7 @@ encodeQuerySemicolons = true sanitizePath = true maxHeaderBytes = 42 + maxHeaderValueCount = 42 aliasHeadersStrategy = "foobar" underscoreHeadersStrategy = "foobar" [entryPoints.EntryPoint0.http.redirections] diff --git a/docs/content/reference/static-configuration/file.yaml b/docs/content/reference/static-configuration/file.yaml index a5ac6a8b22..ba4ff89049 100644 --- a/docs/content/reference/static-configuration/file.yaml +++ b/docs/content/reference/static-configuration/file.yaml @@ -75,6 +75,7 @@ entryPoints: encodeQuerySemicolons: true sanitizePath: true maxHeaderBytes: 42 + maxHeaderValueCount: 42 aliasHeadersStrategy: foobar underscoreHeadersStrategy: foobar http2: diff --git a/docs/content/routing/entrypoints.md b/docs/content/routing/entrypoints.md index 86c72f4231..97457b2777 100644 --- a/docs/content/routing/entrypoints.md +++ b/docs/content/routing/entrypoints.md @@ -1347,6 +1347,40 @@ entryPoints: --entryPoints.websecure.http.maxHeaderBytes=524288 ``` +### MaxHeaderValueCount + +_Optional, Default=500_ + +The `maxHeaderValueCount` option defines the maximum number of request header values Traefik reads for each request. +A request exceeding this limit is rejected with a `431 Request Header Fields Too Large` status. + +Comma-separated values in a single header line count as one value, while values sent on multiple header lines count once per line. + +!!! note "HTTP/3" + + This option applies to HTTP/1 and HTTP/2 only; it has no effect on HTTP/3 requests. + +```yaml tab="File (YAML)" +entryPoints: + websecure: + address: ':443' + http: + maxHeaderValueCount: 100 +``` + +```toml tab="File (TOML)" +[entryPoints.websecure] + address = ":443" + + [entryPoints.websecure.http] + maxHeaderValueCount = 100 +``` + +```bash tab="CLI" +--entryPoints.websecure.address=:443 +--entryPoints.websecure.http.maxHeaderValueCount=100 +``` + ### AliasHeadersStrategy _Optional, Default=keep_ diff --git a/integration/fixtures/simple_max_header_value_count.toml b/integration/fixtures/simple_max_header_value_count.toml new file mode 100644 index 0000000000..c7444b71ed --- /dev/null +++ b/integration/fixtures/simple_max_header_value_count.toml @@ -0,0 +1,25 @@ +[global] + checkNewVersion = false + sendAnonymousUsage = false + +[entryPoints] + [entryPoints.web] + address = ":8000" + [entryPoints.web.http] + maxHeaderValueCount = 10 + +[providers.file] + filename = "{{ .SelfFilename }}" + +## dynamic configuration ## + +[http.routers] + [http.routers.test-router] + entryPoints = ["web"] + service = "test-service" + rule = "Host(`127.0.0.1`)" + +[http.services] + [http.services.test-service] + [[http.services.test-service.loadBalancer.servers]] + url = "{{ .TestServer }}" diff --git a/integration/simple_test.go b/integration/simple_test.go index fa255df114..684e54d29b 100644 --- a/integration/simple_test.go +++ b/integration/simple_test.go @@ -12,6 +12,7 @@ import ( "net/http/httptest" "os" "regexp" + "strconv" "strings" "sync/atomic" "syscall" @@ -1668,6 +1669,50 @@ func (s *SimpleSuite) TestMaxHeaderBytes() { } } +func (s *SimpleSuite) TestMaxHeaderValueCount() { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + })) + defer ts.Close() + + file := s.adaptFile("fixtures/simple_max_header_value_count.toml", struct { + TestServer string + }{ts.URL}) + + s.traefikCmd(withConfigFile(file)) + + testCases := []struct { + name string + headerCount int + expectedStatus int + }{ + { + name: "5 headers", + headerCount: 5, + expectedStatus: http.StatusOK, + }, + { + name: "20 headers", + headerCount: 20, + expectedStatus: http.StatusRequestHeaderFieldsTooLarge, + }, + } + + for _, test := range testCases { + s.Run(test.name, func() { + req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000", nil) + require.NoError(s.T(), err) + + for i := range test.headerCount { + req.Header.Add("X-Header", strconv.Itoa(i)) + } + + err = try.Request(req, 2*time.Second, try.StatusCodeIs(test.expectedStatus)) + require.NoError(s.T(), err) + }) + } +} + func (s *SimpleSuite) TestUnderscoreHeadersStrategy() { handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if _, ok := r.Header["X_auth_user"]; ok { diff --git a/pkg/config/static/entrypoints.go b/pkg/config/static/entrypoints.go index 6d565fc89d..7edee15bc5 100644 --- a/pkg/config/static/entrypoints.go +++ b/pkg/config/static/entrypoints.go @@ -98,6 +98,7 @@ type HTTPConfig struct { EncodeQuerySemicolons bool `description:"Defines whether request query semicolons should be URLEncoded." json:"encodeQuerySemicolons,omitempty" toml:"encodeQuerySemicolons,omitempty" yaml:"encodeQuerySemicolons,omitempty" export:"true"` SanitizePath *bool `description:"Defines whether to enable request path sanitization (removal of /./, /../ and multiple slash sequences)." json:"sanitizePath,omitempty" toml:"sanitizePath,omitempty" yaml:"sanitizePath,omitempty" export:"true"` MaxHeaderBytes int `description:"Maximum size of request headers in bytes." json:"maxHeaderBytes,omitempty" toml:"maxHeaderBytes,omitempty" yaml:"maxHeaderBytes,omitempty" export:"true"` + MaxHeaderValueCount int `description:"Maximum number of request header values." json:"maxHeaderValueCount,omitempty" toml:"maxHeaderValueCount,omitempty" yaml:"maxHeaderValueCount,omitempty" export:"true"` AliasHeadersStrategy string `description:"Defines the strategy to handle the requests carrying a header whose name aliases another header name (keep, delete, and reject)." json:"aliasHeadersStrategy,omitempty" toml:"aliasHeadersStrategy,omitempty" yaml:"aliasHeadersStrategy,omitempty" export:"true"` // UnderscoreHeadersStrategy is the strategy to handle request headers with underscores in their names. @@ -111,6 +112,7 @@ func (h *HTTPConfig) SetDefaults() { sanitizePath := true h.SanitizePath = &sanitizePath h.MaxHeaderBytes = http.DefaultMaxHeaderBytes + h.MaxHeaderValueCount = http.DefaultMaxHeaderValueCount } // EncodedCharacters configures which encoded characters are allowed in the request path. diff --git a/pkg/server/server_entrypoint_tcp.go b/pkg/server/server_entrypoint_tcp.go index 517fd9ff5e..a606e0865f 100644 --- a/pkg/server/server_entrypoint_tcp.go +++ b/pkg/server/server_entrypoint_tcp.go @@ -698,13 +698,14 @@ func createHTTPServer(ctx context.Context, ln net.Listener, configuration *stati } serverHTTP := &http.Server{ - Protocols: &protocols, - Handler: handler, - ErrorLog: httpServerLogger, - ReadTimeout: time.Duration(configuration.Transport.RespondingTimeouts.ReadTimeout), - WriteTimeout: time.Duration(configuration.Transport.RespondingTimeouts.WriteTimeout), - IdleTimeout: time.Duration(configuration.Transport.RespondingTimeouts.IdleTimeout), - MaxHeaderBytes: configuration.HTTP.MaxHeaderBytes, + Protocols: &protocols, + Handler: handler, + ErrorLog: httpServerLogger, + ReadTimeout: time.Duration(configuration.Transport.RespondingTimeouts.ReadTimeout), + WriteTimeout: time.Duration(configuration.Transport.RespondingTimeouts.WriteTimeout), + IdleTimeout: time.Duration(configuration.Transport.RespondingTimeouts.IdleTimeout), + MaxHeaderBytes: configuration.HTTP.MaxHeaderBytes, + MaxHeaderValueCount: configuration.HTTP.MaxHeaderValueCount, HTTP2: &http.HTTP2Config{ MaxConcurrentStreams: int(configuration.HTTP2.MaxConcurrentStreams), },