diff --git a/.golangci.toml b/.golangci.toml index 66b1b5e32..e0f5ffbe0 100644 --- a/.golangci.toml +++ b/.golangci.toml @@ -52,6 +52,8 @@ "testpackage", # Too strict "goerr113", # Too strict "nestif", # Too many false-positive. + "noctx", # Too strict + "exhaustive", # Too strict ] [issues] diff --git a/CHANGELOG.md b/CHANGELOG.md index 650a05a7f..e4a0ef5ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,55 @@ +## [v2.2.3](https://github.com/containous/traefik/tree/v2.2.3) (2020-07-09) +[All Commits](https://github.com/containous/traefik/compare/v2.2.2...v2.2.3) + +**Bug fixes:** +- **[middleware]** Fix panic when using chain middleware. ([#7016](https://github.com/containous/traefik/pull/7016) by [juliens](https://github.com/juliens)) + +## [v2.2.2](https://github.com/containous/traefik/tree/v2.2.2) (2020-07-08) +[All Commits](https://github.com/containous/traefik/compare/v2.2.1...v2.2.2) + +**Bug fixes:** +- **[acme]** Update go-acme/lego to v3.8.0 ([#6988](https://github.com/containous/traefik/pull/6988) by [ldez](https://github.com/ldez)) +- **[acme]** Fix triggering multiple concurrent requests to ACME ([#6939](https://github.com/containous/traefik/pull/6939) by [ddtmachado](https://github.com/ddtmachado)) +- **[acme]** Update go-acme/lego to v3.7.0 ([#6792](https://github.com/containous/traefik/pull/6792) by [ldez](https://github.com/ldez)) +- **[acme]** added required quotes to domains config ([#6867](https://github.com/containous/traefik/pull/6867) by [tompson](https://github.com/tompson)) +- **[authentication,logs,middleware]** Provide username in log data on auth failure ([#6827](https://github.com/containous/traefik/pull/6827) by [rtribotte](https://github.com/rtribotte)) +- **[docker]** Use specified network for "container" network mode ([#6763](https://github.com/containous/traefik/pull/6763) by [bjeanes](https://github.com/bjeanes)) +- **[k8s,k8s/crd]** Remove checkStringQuoteValidity in loadIngressRouteConf ([#6775](https://github.com/containous/traefik/pull/6775) by [fefe982](https://github.com/fefe982)) +- **[middleware,websocket]** Fix wss in x-forwarded-proto ([#6752](https://github.com/containous/traefik/pull/6752) by [juliens](https://github.com/juliens)) +- **[middleware]** internal handlers: support for response modifiers ([#6750](https://github.com/containous/traefik/pull/6750) by [mpl](https://github.com/mpl)) +- **[middleware]** Fix ipv6 handling in redirect middleware ([#6902](https://github.com/containous/traefik/pull/6902) by [rtribotte](https://github.com/rtribotte)) +- **[middleware]** refactor X-Forwarded-Proto ([#6863](https://github.com/containous/traefik/pull/6863) by [jcgruenhage](https://github.com/jcgruenhage)) +- **[provider]** Fix race condition issues with provided dynamic configuration ([#6979](https://github.com/containous/traefik/pull/6979) by [kevinpollet](https://github.com/kevinpollet)) +- **[rules,server,tls]** Disable domain fronting ([#7008](https://github.com/containous/traefik/pull/7008) by [rtribotte](https://github.com/rtribotte)) +- **[udp]** Fix mem leak on UDP connections ([#6815](https://github.com/containous/traefik/pull/6815) by [ddtmachado](https://github.com/ddtmachado)) +- **[udp]** Avoid overwriting already received UDP messages ([#6797](https://github.com/containous/traefik/pull/6797) by [cbachert](https://github.com/cbachert)) +- **[webui]** Add missing accessControlAllowOrigin list to middleware view ([#6747](https://github.com/containous/traefik/pull/6747) by [barthez](https://github.com/barthez)) + +**Documentation:** +- **[acme]** Fix doc url for Aurora DNS provider ([#6899](https://github.com/containous/traefik/pull/6899) by [rtribotte](https://github.com/rtribotte)) +- **[acme]** Fix acme.md typo ([#6817](https://github.com/containous/traefik/pull/6817) by [juliocc](https://github.com/juliocc)) +- **[acme]** fix certResolver typo ([#6983](https://github.com/containous/traefik/pull/6983) by [DavidBadura](https://github.com/DavidBadura)) +- **[acme]** Fix statement about lego _FILE env var ([#6964](https://github.com/containous/traefik/pull/6964) by [solvaholic](https://github.com/solvaholic)) +- **[acme]** Improve acme CLI options in Let's Encrypt documentation ([#6762](https://github.com/containous/traefik/pull/6762) by [netoax](https://github.com/netoax)) +- **[docker]** fix a broken link on Docker plugins documentation ([#6908](https://github.com/containous/traefik/pull/6908) by [jbdoumenjou](https://github.com/jbdoumenjou)) +- **[docker]** Fix healthcheck.interval in docs ([#6847](https://github.com/containous/traefik/pull/6847) by [OndrejIT](https://github.com/OndrejIT)) +- **[k8s,k8s/ingress]** Remove redundant paragraph in Kubernetes ingress documentation ([#6806](https://github.com/containous/traefik/pull/6806) by [lpfann](https://github.com/lpfann)) +- **[k8s,k8s/ingress]** Fix sticky cookie ingress annotation doc ([#6938](https://github.com/containous/traefik/pull/6938) by [rtribotte](https://github.com/rtribotte)) +- **[k8s]** fixing typo in Provider KubernetesIngress at Routing documentation ([#6845](https://github.com/containous/traefik/pull/6845) by [sw360cab](https://github.com/sw360cab)) +- **[k8s]** Update kubernetes-crd.md ([#6878](https://github.com/containous/traefik/pull/6878) by [rherrick](https://github.com/rherrick)) +- **[logs]** Fixed incorrect logging parameter in documentation ([#6819](https://github.com/containous/traefik/pull/6819) by [cplewnia](https://github.com/cplewnia)) +- **[logs]** Use "headers" instead of "header" in access log docs ([#6836](https://github.com/containous/traefik/pull/6836) by [bradjones1](https://github.com/bradjones1)) +- **[middleware,k8s/crd]** Fix Headers middleware documentation, usage of proper bool ([#6928](https://github.com/containous/traefik/pull/6928) by [rtribotte](https://github.com/rtribotte)) +- **[middleware]** Improve redirectScheme documentation ([#6769](https://github.com/containous/traefik/pull/6769) by [dtomcej](https://github.com/dtomcej)) +- **[middleware]** Update basicauth.md ([#6967](https://github.com/containous/traefik/pull/6967) by [vitalets](https://github.com/vitalets)) +- Update Dashboard examples and move it after 'Router Rule' section ([#6874](https://github.com/containous/traefik/pull/6874) by [ddtmachado](https://github.com/ddtmachado)) +- Fix log field names in documentation ([#6952](https://github.com/containous/traefik/pull/6952) by [gysel](https://github.com/gysel)) +- Minor fix to Go templating documentation ([#6977](https://github.com/containous/traefik/pull/6977) by [PCM2](https://github.com/PCM2)) +- Add rtribotte to maintainers ([#6936](https://github.com/containous/traefik/pull/6936) by [emilevauge](https://github.com/emilevauge)) +- Update Copyright ([#6795](https://github.com/containous/traefik/pull/6795) by [mmatur](https://github.com/mmatur)) +- fix: dead link. ([#6876](https://github.com/containous/traefik/pull/6876) by [ldez](https://github.com/ldez)) +- Fix v1-> v2 migration: unify domain name in documentation example ([#6904](https://github.com/containous/traefik/pull/6904) by [sinacek](https://github.com/sinacek)) + ## [v2.2.1](https://github.com/containous/traefik/tree/v2.2.1) (2020-04-29) [All Commits](https://github.com/containous/traefik/compare/v2.2.0...v2.2.1) diff --git a/build.Dockerfile b/build.Dockerfile index ea5e7893e..497663beb 100644 --- a/build.Dockerfile +++ b/build.Dockerfile @@ -19,7 +19,7 @@ RUN mkdir -p /usr/local/bin \ && chmod +x /usr/local/bin/go-bindata # Download golangci-lint binary to bin folder in $GOPATH -RUN curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.26.0 +RUN curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.28.0 # Download misspell binary to bin folder in $GOPATH RUN curl -sfL https://raw.githubusercontent.com/client9/misspell/master/install-misspell.sh | bash -s -- -b $GOPATH/bin v0.3.4 diff --git a/cmd/traefik/traefik.go b/cmd/traefik/traefik.go index 5ea34c16c..ec114361a 100644 --- a/cmd/traefik/traefik.go +++ b/cmd/traefik/traefik.go @@ -25,6 +25,7 @@ import ( "github.com/containous/traefik/v2/pkg/provider/acme" "github.com/containous/traefik/v2/pkg/provider/aggregator" "github.com/containous/traefik/v2/pkg/provider/traefik" + "github.com/containous/traefik/v2/pkg/rules" "github.com/containous/traefik/v2/pkg/safe" "github.com/containous/traefik/v2/pkg/server" "github.com/containous/traefik/v2/pkg/server/middleware" @@ -161,6 +162,8 @@ func runCmd(staticConfiguration *static.Configuration) error { } func setupServer(staticConfiguration *static.Configuration) (*server.Server, error) { + rules.EnableDomainFronting(staticConfiguration.Global.InsecureSNI) + providerAggregator := aggregator.NewProviderAggregator(*staticConfiguration.Providers) // adds internal provider @@ -403,7 +406,7 @@ func configureLogging(staticConfiguration *static.Configuration) { if len(logFile) > 0 { dir := filepath.Dir(logFile) - if err := os.MkdirAll(dir, 0755); err != nil { + if err := os.MkdirAll(dir, 0o755); err != nil { log.WithoutContext().Errorf("Failed to create log path %s: %s", dir, err) } diff --git a/docs/content/contributing/maintainers.md b/docs/content/contributing/maintainers.md index 076d9ad8b..b95a54663 100644 --- a/docs/content/contributing/maintainers.md +++ b/docs/content/contributing/maintainers.md @@ -16,6 +16,7 @@ * Gérald Croës [@geraldcroes](https://github.com/geraldcroes) * Jean-Baptiste Doumenjou [@jbdoumenjou](https://github.com/jbdoumenjou) * Mathieu Lonjaret [@mpl](https://github.com/mpl) +* Romain Tribotté [@rtribotte](https://github.com/rtribotte) ## Contributions Daily Meeting diff --git a/docs/content/https/acme.md b/docs/content/https/acme.md index ef2a27310..f07cf410d 100644 --- a/docs/content/https/acme.md +++ b/docs/content/https/acme.md @@ -275,13 +275,16 @@ Here is a list of supported `providers`, that can automate the DNS verification, along with the required environment variables and their [wildcard & root domain support](#wildcard-domains). Do not hesitate to complete it. -Every lego environment variable can be overridden by their respective `_FILE` counterpart, which should have a filepath to a file that contains the secret as its value. +Many lego environment variables can be overridden by their respective `_FILE` counterpart, which should have a filepath to a file that contains the secret as its value. For example, `CF_API_EMAIL_FILE=/run/secrets/traefik_cf-api-email` could be used to provide a Cloudflare API email address as a Docker secret named `traefik_cf-api-email`. +For complete details, refer to your provider's _Additional configuration_ link. + | Provider Name | Provider Code | Environment Variables | | |-------------------------------------------------------------|----------------|---------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------| | [ACME DNS](https://github.com/joohoi/acme-dns) | `acme-dns` | `ACME_DNS_API_BASE`, `ACME_DNS_STORAGE_PATH` | [Additional configuration](https://go-acme.github.io/lego/dns/acme-dns) | | [Alibaba Cloud](https://www.alibabacloud.com) | `alidns` | `ALICLOUD_ACCESS_KEY`, `ALICLOUD_SECRET_KEY`, `ALICLOUD_REGION_ID` | [Additional configuration](https://go-acme.github.io/lego/dns/alidns) | +| [ArvanCloud](https://arvancloud.com) | `arvancloud` | `ARVANCLOUD_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/arvancloud) | | [Auroradns](https://www.pcextreme.com/dns-health-checks) | `auroradns` | `AURORA_USER_ID`, `AURORA_KEY`, `AURORA_ENDPOINT` | [Additional configuration](https://go-acme.github.io/lego/dns/auroradns) | | [Autodns](https://www.internetx.com/domains/autodns/) | `autodns` | `AUTODNS_API_USER`, `AUTODNS_API_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/autodns) | | [Azure](https://azure.microsoft.com/services/dns/) | `azure` | `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_SUBSCRIPTION_ID`, `AZURE_TENANT_ID`, `AZURE_RESOURCE_GROUP`, `[AZURE_METADATA_ENDPOINT]` | [Additional configuration](https://go-acme.github.io/lego/dns/azure) | diff --git a/docs/content/https/include-acme-multiple-domains-example.md b/docs/content/https/include-acme-multiple-domains-example.md index 4e9d02773..7fbeb9e06 100644 --- a/docs/content/https/include-acme-multiple-domains-example.md +++ b/docs/content/https/include-acme-multiple-domains-example.md @@ -40,7 +40,7 @@ spec: domains: - main: example.org sans: - - *.example.org + - '*.example.org' ``` ```json tab="Marathon" diff --git a/docs/content/https/include-acme-multiple-domains-from-rule-example.md b/docs/content/https/include-acme-multiple-domains-from-rule-example.md index 96609e40b..4bf9b23c4 100644 --- a/docs/content/https/include-acme-multiple-domains-from-rule-example.md +++ b/docs/content/https/include-acme-multiple-domains-from-rule-example.md @@ -32,7 +32,7 @@ spec: - name: blog port: 8080 tls: - certresolver: myresolver + certResolver: myresolver ``` ```json tab="Marathon" diff --git a/docs/content/https/include-acme-single-domain-example.md b/docs/content/https/include-acme-single-domain-example.md index 91d4a93fa..e3aea2309 100644 --- a/docs/content/https/include-acme-single-domain-example.md +++ b/docs/content/https/include-acme-single-domain-example.md @@ -32,7 +32,7 @@ spec: - name: blog port: 8080 tls: - certresolver: myresolver + certResolver: myresolver ``` ```json tab="Marathon" diff --git a/docs/content/https/tls.md b/docs/content/https/tls.md index 6804636c2..1ba56c62b 100644 --- a/docs/content/https/tls.md +++ b/docs/content/https/tls.md @@ -130,6 +130,20 @@ tls: If no default certificate is provided, Traefik generates and uses a self-signed certificate. +## Domain fronting + +Basically, [domain fronting](https://en.wikipedia.org/wiki/Domain_fronting) is a technique that allows to open a +connection with a specific domain name, thanks to the +[Server Name Indication](https://en.wikipedia.org/wiki/Server_Name_Indication), then access a service with another +domain set in the HTTP `Host` header. + +Since the `v2.2.2`, Traefik avoids (by default) using domain fronting. +As it is valid for advanced use cases, the `HostHeader` and `HostSNI` [rules](../routing/routers/index.md#rule) allow +to fine tune the routing with the `Server Name Indication` and `Host header` value. + +If you encounter routing issues with a previously working configuration, please refer to the +[migration guide](../migration/v2.md) to update your configuration. + ## TLS Options The TLS options allow one to configure some parameters of the TLS connection. @@ -317,7 +331,7 @@ spec: ### Strict SNI Checking With strict SNI checking, Traefik won't allow connections from clients connections -that do not specify a server_name extension. +that do not specify a server_name extension or don't match any certificate configured on the tlsOption. ```toml tab="File (TOML)" # Dynamic configuration diff --git a/docs/content/middlewares/basicauth.md b/docs/content/middlewares/basicauth.md index 7d5b8ebc9..83d2d5b43 100644 --- a/docs/content/middlewares/basicauth.md +++ b/docs/content/middlewares/basicauth.md @@ -12,9 +12,11 @@ The BasicAuth middleware is a quick way to restrict access to your services to k ```yaml tab="Docker" # Declaring the user list # -# Note: all dollar signs in the hash need to be doubled for escaping. +# Note: when used in docker-compose.yml all dollar signs in the hash need to be doubled for escaping. # To create user:password pair, it's possible to use this command: # echo $(htpasswd -nb user password) | sed -e s/\\$/\\$\\$/g +# +# Also note that dollar signs should NOT be doubled when they not evaluated (e.g. Ansible docker_container module). labels: - "traefik.http.middlewares.test-auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0" ``` diff --git a/docs/content/middlewares/headers.md b/docs/content/middlewares/headers.md index c9740d917..70e51cf54 100644 --- a/docs/content/middlewares/headers.md +++ b/docs/content/middlewares/headers.md @@ -151,8 +151,8 @@ metadata: name: testHeader spec: headers: - frameDeny: "true" - sslRedirect: "true" + frameDeny: true + sslRedirect: true ``` ```yaml tab="Consul Catalog" @@ -217,7 +217,7 @@ spec: - "https://foo.bar.org" - "https://example.org" accessControlMaxAge: 100 - addVaryHeader: "true" + addVaryHeader: true ``` ```yaml tab="Consul Catalog" diff --git a/docs/content/migration/v2.md b/docs/content/migration/v2.md index 5239c4a87..6e056ab37 100644 --- a/docs/content/migration/v2.md +++ b/docs/content/migration/v2.md @@ -1,5 +1,117 @@ # Migration: Steps needed between the versions +## v2.x to v2.2.2 + +### Domain fronting + +In `v2.2.2` we introduced the ability to avoid [Domain fronting](https://en.wikipedia.org/wiki/Domain_fronting), +and enabled it by default for [https routers](../routing/routers/index.md#rule) configured with ```Host(`something`)```. + +!!! example "Allow Domain Fronting on a Specific Router" + + !!! info "Before v2.2.2" + + ```yaml tab="Docker" + labels: + - "traefik.http.routers.router0.rule=Host(`test.localhost`)" + ``` + + ```yaml tab="K8s Ingress" + apiVersion: traefik.containo.us/v1alpha1 + kind: IngressRoute + metadata: + name: ingressroutebar + + spec: + entryPoints: + - http + routes: + - match: Host(`test.localhost`) + kind: Rule + services: + - name: server0 + port: 80 + - name: server1 + port: 80 + ``` + + ```toml tab="File (TOML)" + [http.routers.router0] + rule = "Host(`test.localhost`)" + service = "my-service" + ``` + + ```toml tab="File (YAML)" + http: + routers: + router0: + rule: "Host(`test.localhost`)" + service: my-service + ``` + + !!! info "v2.2.2" + + ```yaml tab="Docker" + labels: + - "traefik.http.routers.router0.rule=HostHeader(`test.localhost`)" + ``` + + ```yaml tab="K8s Ingress" + apiVersion: traefik.containo.us/v1alpha1 + kind: IngressRoute + metadata: + name: ingressroutebar + + spec: + entryPoints: + - http + routes: + - match: HostHeader(`test.localhost`) + kind: Rule + services: + - name: server0 + port: 80 + - name: server1 + port: 80 + ``` + + ```toml tab="File (TOML)" + [http.routers.router0] + rule = "HostHeader(`test.localhost`)" + service = "my-service" + ``` + + ```toml tab="File (YAML)" + http: + routers: + router0: + rule: "HostHeader(`test.localhost`)" + service: my-service + ``` + +As a fallback, a new flag is available as a global option: + +!!! example "Enabling Domain Fronting for All Routers" + + ```toml tab="File (TOML)" + # Static configuration + [global] + # Enabling domain fronting + insecureSNI = true + ``` + + ```yaml tab="File (YAML)" + # Static configuration + global: + # Enabling domain fronting + insecureSNI: true + ``` + + ```bash tab="CLI" + # Enabling domain fronting + --global.insecureSNI + ``` + ## v2.0 to v2.1 ### Kubernetes CRD diff --git a/docs/content/observability/access-logs.md b/docs/content/observability/access-logs.md index 0c54d485d..fc9f424dc 100644 --- a/docs/content/observability/access-logs.md +++ b/docs/content/observability/access-logs.md @@ -181,10 +181,10 @@ accessLog: | `StartUTC` | The time at which request processing started. | | `StartLocal` | The local time at which request processing started. | | `Duration` | The total time taken (in nanoseconds) by processing the response, including the origin server's time but not the log writing time. | - | `FrontendName` | The name of the Traefik frontend. | - | `BackendName` | The name of the Traefik backend. | - | `BackendURL` | The URL of the Traefik backend. | - | `BackendAddr` | The IP:port of the Traefik backend (extracted from `BackendURL`) | + | `RouterName` | The name of the Traefik router. | + | `ServiceName` | The name of the Traefik backend. | + | `ServiceURL` | The URL of the Traefik backend. | + | `ServiceAddr` | The IP:port of the Traefik backend (extracted from `ServiceURL`) | | `ClientAddr` | The remote address in its original form (usually IP:port). | | `ClientHost` | The remote IP address from which the client request was received. | | `ClientPort` | The remote TCP port from which the client request was received. | diff --git a/docs/content/operations/dashboard.md b/docs/content/operations/dashboard.md index 9c84d0497..879ae1e06 100644 --- a/docs/content/operations/dashboard.md +++ b/docs/content/operations/dashboard.md @@ -72,9 +72,6 @@ to allow defining: - A [router rule](#dashboard-router-rule) for accessing the dashboard, through Traefik itself (sometimes referred as "Traefik-ception"). -??? example "Dashboard Dynamic Configuration Examples" - --8<-- "content/operations/include-api-examples.md" - ### Dashboard Router Rule As underlined in the [documentation for the `api.dashboard` option](./api.md#dashboard), @@ -99,6 +96,9 @@ rule = "PathPrefix(`/api`) || PathPrefix(`/dashboard`)" rule = "Host(`traefik.example.com`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))" ``` +??? example "Dashboard Dynamic Configuration Examples" + --8<-- "content/operations/include-dashboard-examples.md" + ## Insecure Mode This mode is not recommended because it does not allow the use of security features. diff --git a/docs/content/operations/include-dashboard-examples.md b/docs/content/operations/include-dashboard-examples.md new file mode 100644 index 000000000..8e3201482 --- /dev/null +++ b/docs/content/operations/include-dashboard-examples.md @@ -0,0 +1,101 @@ +```yaml tab="Docker" +# Dynamic Configuration +labels: + - "traefik.http.routers.dashboard.rule=Host(`traefik.example.com`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))" + - "traefik.http.routers.dashboard.service=api@internal" + - "traefik.http.routers.dashboard.middlewares=auth" + - "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0" +``` + +```yaml tab="Docker (Swarm)" +# Dynamic Configuration +deploy: + labels: + - "traefik.http.routers.dashboard.rule=Host(`traefik.example.com`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))" + - "traefik.http.routers.dashboard.service=api@internal" + - "traefik.http.routers.dashboard.middlewares=auth" + - "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0" + # Dummy service for Swarm port detection. The port can be any valid integer value. + - "traefik.http.services.dummy-svc.loadbalancer.server.port=9999" +``` + +```yaml tab="Kubernetes CRD" +apiVersion: traefik.containo.us/v1alpha1 +kind: IngressRoute +metadata: + name: traefik-dashboard +spec: + routes: + - match: Host(`traefik.example.com`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`)) + kind: Rule + services: + - name: api@internal + kind: TraefikService + middlewares: + - name: auth +--- +apiVersion: traefik.containo.us/v1alpha1 +kind: Middleware +metadata: + name: auth +spec: + basicAuth: + secret: secretName # Kubernetes secret named "secretName" +``` + +```yaml tab="Consul Catalog" +# Dynamic Configuration +- "traefik.http.routers.dashboard.rule=Host(`traefik.example.com`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))" +- "traefik.http.routers.dashboard.service=api@internal" +- "traefik.http.routers.dashboard.middlewares=auth" +- "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0" +``` + +```json tab="Marathon" +"labels": { + "traefik.http.routers.dashboard.rule": "Host(`traefik.example.com`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))", + "traefik.http.routers.dashboard.service": "api@internal", + "traefik.http.routers.dashboard.middlewares": "auth", + "traefik.http.middlewares.auth.basicauth.users": "test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0" +} +``` + +```yaml tab="Rancher" +# Dynamic Configuration +labels: + - "traefik.http.routers.dashboard.rule=Host(`traefik.example.com`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))" + - "traefik.http.routers.dashboard.service=api@internal" + - "traefik.http.routers.dashboard.middlewares=auth" + - "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0" +``` + +```toml tab="File (TOML)" +# Dynamic Configuration +[http.routers.my-api] + rule = "Host(`traefik.example.com`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))" + service = "api@internal" + middlewares = ["auth"] + +[http.middlewares.auth.basicAuth] + users = [ + "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", + "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", + ] +``` + +```yaml tab="File (YAML)" +# Dynamic Configuration +http: + routers: + dashboard: + rule: Host(`traefik.example.com`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`)) + service: api@internal + middlewares: + - auth + middlewares: + auth: + basicAuth: + users: + - "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/" + - "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0" +``` diff --git a/docs/content/providers/file.md b/docs/content/providers/file.md index cf13f53b1..368dbdfc1 100644 --- a/docs/content/providers/file.md +++ b/docs/content/providers/file.md @@ -191,14 +191,14 @@ providers: ### Go Templating !!! warning - Go Templating only works along with dedicated dynamic configuration files. + Go Templating only works with dedicated dynamic configuration files. Templating does not work in the Traefik main static configuration file. -Traefik allows using Go templating, -it must be a valid [Go template](https://golang.org/pkg/text/template/), -augmented with the [sprig template functions](http://masterminds.github.io/sprig/). +Traefik supports using Go templating to automatically generate repetitive portions of configuration files. +These sections must be valid [Go templates](https://golang.org/pkg/text/template/), +augmented with the [Sprig template functions](http://masterminds.github.io/sprig/). -Thus, it's possible to define easily lot of routers, services and TLS certificates as described in the following examples: +To illustrate, it's possible to easily define multiple routers, services, and TLS certificates as described in the following examples: ??? example "Configuring Using Templating" diff --git a/docs/content/reference/static-configuration/cli-ref.md b/docs/content/reference/static-configuration/cli-ref.md index 7afe56d75..9f14b83c4 100644 --- a/docs/content/reference/static-configuration/cli-ref.md +++ b/docs/content/reference/static-configuration/cli-ref.md @@ -162,6 +162,9 @@ WriteTimeout is the maximum duration before timing out writes of the response. I `--global.checknewversion`: Periodically check if a new version has been released. (Default: ```false```) +`--global.insecuresni`: +Allow domain fronting. If the option is not specified, it will be disabled by default. (Default: ```false```) + `--global.sendanonymoususage`: Periodically send anonymous usage statistics. If the option is not specified, it will be enabled by default. (Default: ```false```) diff --git a/docs/content/reference/static-configuration/env-ref.md b/docs/content/reference/static-configuration/env-ref.md index bafef49ad..b497abd99 100644 --- a/docs/content/reference/static-configuration/env-ref.md +++ b/docs/content/reference/static-configuration/env-ref.md @@ -162,6 +162,9 @@ WriteTimeout is the maximum duration before timing out writes of the response. I `TRAEFIK_GLOBAL_CHECKNEWVERSION`: Periodically check if a new version has been released. (Default: ```false```) +`TRAEFIK_GLOBAL_INSECURESNI`: +Allow domain fronting. If the option is not specified, it will be disabled by default. (Default: ```false```) + `TRAEFIK_GLOBAL_SENDANONYMOUSUSAGE`: Periodically send anonymous usage statistics. If the option is not specified, it will be enabled by default. (Default: ```false```) diff --git a/docs/content/reference/static-configuration/file.toml b/docs/content/reference/static-configuration/file.toml index fce1dc4d3..c90dc95c5 100644 --- a/docs/content/reference/static-configuration/file.toml +++ b/docs/content/reference/static-configuration/file.toml @@ -1,6 +1,7 @@ [global] checkNewVersion = true sendAnonymousUsage = true + insecureSNI = false [serversTransport] insecureSkipVerify = true diff --git a/docs/content/reference/static-configuration/file.yaml b/docs/content/reference/static-configuration/file.yaml index a8ab3acda..85d0888f5 100644 --- a/docs/content/reference/static-configuration/file.yaml +++ b/docs/content/reference/static-configuration/file.yaml @@ -1,6 +1,8 @@ global: checkNewVersion: true sendAnonymousUsage: true + insecureSNI: false + serversTransport: insecureSkipVerify: true rootCAs: diff --git a/docs/content/routing/providers/kubernetes-ingress.md b/docs/content/routing/providers/kubernetes-ingress.md index 3fcd8af7f..7f6c0e2c6 100644 --- a/docs/content/routing/providers/kubernetes-ingress.md +++ b/docs/content/routing/providers/kubernetes-ingress.md @@ -282,12 +282,12 @@ which in turn will create the resulting routers, services, handlers, etc. traefik.ingress.kubernetes.io/service.passhostheader: "true" ``` -??? info "`traefik.ingress.kubernetes.io/service.sticky`" +??? info "`traefik.ingress.kubernetes.io/service.sticky.cookie`" See [sticky sessions](../services/index.md#sticky-sessions) for more information. ```yaml - traefik.ingress.kubernetes.io/service.sticky: "true" + traefik.ingress.kubernetes.io/service.sticky.cookie: "true" ``` ??? info "`traefik.ingress.kubernetes.io/service.sticky.cookie.name`" diff --git a/docs/content/routing/routers/index.md b/docs/content/routing/routers/index.md index bba559dc9..eb97bf151 100644 --- a/docs/content/routing/routers/index.md +++ b/docs/content/routing/routers/index.md @@ -228,16 +228,18 @@ If the rule is verified, the router becomes active, calls middlewares, and then The table below lists all the available matchers: -| Rule | Description | -|------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------| -| ```Headers(`key`, `value`)``` | Check if there is a key `key`defined in the headers, with the value `value` | -| ```HeadersRegexp(`key`, `regexp`)``` | Check if there is a key `key`defined in the headers, with a value that matches the regular expression `regexp` | -| ```Host(`example.com`, ...)``` | Check if the request domain targets one of the given `domains`. | -| ```HostRegexp(`example.com`, `{subdomain:[a-z]+}.example.com`, ...)``` | Check if the request domain matches the given `regexp`. | -| ```Method(`GET`, ...)``` | Check if the request method is one of the given `methods` (`GET`, `POST`, `PUT`, `DELETE`, `PATCH`) | -| ```Path(`/path`, `/articles/{cat:[a-z]+}/{id:[0-9]+}`, ...)``` | Match exact request path. It accepts a sequence of literal and regular expression paths. | -| ```PathPrefix(`/products/`, `/articles/{cat:[a-z]+}/{id:[0-9]+}`)``` | Match request prefix path. It accepts a sequence of literal and regular expression prefix paths. | -| ```Query(`foo=bar`, `bar=baz`)``` | Match Query String parameters. It accepts a sequence of key=value pairs. | +| Rule | Description | +|------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| ```Headers(`key`, `value`)``` | Check if there is a key `key`defined in the headers, with the value `value` | +| ```HeadersRegexp(`key`, `regexp`)``` | Check if there is a key `key`defined in the headers, with a value that matches the regular expression `regexp` | +| ```Host(`example.com`, ...)``` | By default, is equivalent to `HostHeader` **AND** `HostSNI` rules. See [Domain Fronting](../../https/tls.md#domain-fronting) and the [migration guide](../../migration/v2.md#domain-fronting) for more details. | +| ```HostHeader(`example.com`, ...)``` | Check if the request domain (host header value) targets one of the given `domains`. | +| ```HostSNI(`example.com`, ...)``` | Check if the [Server Name Indication](https://en.wikipedia.org/wiki/Server_Name_Indication) corresponds to the given `domains`. | +| ```HostRegexp(`example.com`, `{subdomain:[a-z]+}.example.com`, ...)``` | Check if the request domain matches the given `regexp`. | +| ```Method(`GET`, ...)``` | Check if the request method is one of the given `methods` (`GET`, `POST`, `PUT`, `DELETE`, `PATCH`) | +| ```Path(`/path`, `/articles/{cat:[a-z]+}/{id:[0-9]+}`, ...)``` | Match exact request path. It accepts a sequence of literal and regular expression paths. | +| ```PathPrefix(`/products/`, `/articles/{cat:[a-z]+}/{id:[0-9]+}`)``` | Match request prefix path. It accepts a sequence of literal and regular expression prefix paths. | +| ```Query(`foo=bar`, `bar=baz`)``` | Match Query String parameters. It accepts a sequence of key=value pairs. | !!! important "Regexp Syntax" diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 35cbe239a..986dc19a8 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -28,10 +28,6 @@ theme: copyright: "Copyright © 2016-2020 Containous" -google_analytics: - - 'UA-51880359-3' - - 'docs.traefik.io' - extra_css: - assets/styles/extra.css # Our custom styles - assets/styles/atom-one-light.css # HightlightJS's CSS theme diff --git a/docs/theme/main.html b/docs/theme/main.html index a8e704e40..5b1a18c99 100644 --- a/docs/theme/main.html +++ b/docs/theme/main.html @@ -1,5 +1,15 @@ {% extends "base.html" %} +{% block analytics %} + + + +{% endblock %} + {% block footer %} {% import "partials/language.html" as lang with context %} diff --git a/go.mod b/go.mod index 6a00e47a2..9ab2f801b 100644 --- a/go.mod +++ b/go.mod @@ -36,7 +36,7 @@ require ( github.com/fatih/structs v1.1.0 github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect github.com/gambol99/go-marathon v0.0.0-20180614232016-99a156b96fb2 - github.com/go-acme/lego/v3 v3.7.0 + github.com/go-acme/lego/v3 v3.8.0 github.com/go-check/check v0.0.0-00010101000000-000000000000 github.com/go-kit/kit v0.9.0 github.com/golang/protobuf v1.3.4 diff --git a/go.sum b/go.sum index c809573b2..3c773ca3d 100644 --- a/go.sum +++ b/go.sum @@ -117,6 +117,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI= +github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/c0va23/go-proxyprotocol v0.9.1 h1:5BCkp0fDJOhzzH1lhjUgHhmZz9VvRMMif1U2D31hb34= github.com/c0va23/go-proxyprotocol v0.9.1/go.mod h1:TNjUV+llvk8TvWJxlPYAeAYZgSzT/iicNr3nWBWX320= github.com/cenkalti/backoff/v4 v4.0.0 h1:6VeaLF9aI+MAUQ95106HwWzYZgJJpZ4stumjj6RFYAU= @@ -247,8 +249,8 @@ github.com/gambol99/go-marathon v0.0.0-20180614232016-99a156b96fb2/go.mod h1:GLy github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-acme/lego/v3 v3.7.0 h1:qC5/8/CbltyAE8fGLE6bGlqucj7pXc/vBxiLwLOsmAQ= -github.com/go-acme/lego/v3 v3.7.0/go.mod h1:4eDjjYkAsDXyNcwN8IhhZAwxz9Ltiks1Zmpv0q20J7A= +github.com/go-acme/lego/v3 v3.8.0 h1:9OOEn54eZvEPRRdM7xiC5f7EBW0MlEeChr+kzlIhdN8= +github.com/go-acme/lego/v3 v3.8.0/go.mod h1:kYiHYgSRzb1l2NQPWvWvkVG5etNCusGFsZc2MTak3m0= github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= @@ -378,6 +380,8 @@ github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brv github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI= +github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= @@ -387,6 +391,8 @@ github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1: github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM= +github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs= @@ -445,8 +451,8 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kolo/xmlrpc v0.0.0-20190717152603-07c4ee3fd181 h1:TrxPzApUukas24OMMVDUMlCs1XCExJtnGaDEiIAR4oQ= -github.com/kolo/xmlrpc v0.0.0-20190717152603-07c4ee3fd181/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ= +github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b h1:DzHy0GlWeF0KAglaTMY7Q+khIFoG8toHP+wLFBVBQJc= +github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -512,6 +518,8 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.3.1 h1:cCBH2gTD2K0OtLlv/Y5H01VQCqmlDxz30kS5Y5bqfLA= +github.com/mitchellh/mapstructure v1.3.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -533,8 +541,8 @@ github.com/nrdcg/auroradns v1.0.1 h1:m/kBq83Xvy3cU261MOknd8BdnOk12q4lAWM+kOdsC2Y github.com/nrdcg/auroradns v1.0.1/go.mod h1:y4pc0i9QXYlFCWrhWrUSIETnZgrf4KuwjDIWmmXo3JI= github.com/nrdcg/dnspod-go v0.4.0 h1:c/jn1mLZNKF3/osJ6mz3QPxTudvPArXTjpkmYj0uK6U= github.com/nrdcg/dnspod-go v0.4.0/go.mod h1:vZSoFSFeQVm2gWLMkyX61LZ8HI3BaqtHZWgPTGKr6KQ= -github.com/nrdcg/goinwx v0.6.1 h1:AJnjoWPELyCtofhGcmzzcEMFd9YdF2JB/LgutWsWt/s= -github.com/nrdcg/goinwx v0.6.1/go.mod h1:XPiut7enlbEdntAqalBIqcYcTEVhpv/dKWgDCX2SwKQ= +github.com/nrdcg/goinwx v0.7.0 h1:j6JlOp0nNwtvaP09TvKqc9pktjH81nOad0+Gx9S1t9U= +github.com/nrdcg/goinwx v0.7.0/go.mod h1:4tKJOCi/1lTxuw9/yB2Ez0aojwtUCSkckjc22eALpqE= github.com/nrdcg/namesilo v0.2.1 h1:kLjCjsufdW/IlC+iSfAqj0iQGgKjlbUUeDJio5Y6eMg= github.com/nrdcg/namesilo v0.2.1/go.mod h1:lwMvfQTyYq+BbjJd30ylEG4GPSS6PII0Tia4rRpRiyw= github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= @@ -596,6 +604,8 @@ github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6J github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/pquerna/otp v1.2.0 h1:/A3+Jn+cagqayeR3iHs/L62m5ue7710D35zl1zJ1kok= +github.com/pquerna/otp v1.2.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= @@ -694,8 +704,8 @@ github.com/vulcand/oxy v1.1.0 h1:DbBijGo1+6cFqR9jarkMxasdj0lgWwrrFtue6ijek4Q= github.com/vulcand/oxy v1.1.0/go.mod h1:ADiMYHi8gkGl2987yQIzDRoXZilANF4WtKaQ92OppKY= github.com/vulcand/predicate v1.1.0 h1:Gq/uWopa4rx/tnZu2opOSBqHK63Yqlou/SzrbwdJiNg= github.com/vulcand/predicate v1.1.0/go.mod h1:mlccC5IRBoc2cIFmCB8ZM62I3VDb6p2GXESMHa3CnZg= -github.com/vultr/govultr v0.1.4 h1:UnNMixYFVO0p80itc8PcweoVENyo1PasfvwKhoasR9U= -github.com/vultr/govultr v0.1.4/go.mod h1:9H008Uxr/C4vFNGLqKx232C206GL0PBHzOP0809bGNA= +github.com/vultr/govultr v0.4.2 h1:9i8xKZ+xp6vwZ9raqHoBLzhB4wCnMj7nOQTj5YIRLWY= +github.com/vultr/govultr v0.4.2/go.mod h1:TUuUizMOFc7z+PNMssb6iGjKjQfpw5arIaOLfocVudQ= github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= diff --git a/integration/access_log_test.go b/integration/access_log_test.go index 31e52e306..17d1420ae 100644 --- a/integration/access_log_test.go +++ b/integration/access_log_test.go @@ -111,6 +111,20 @@ func (s *AccessLogSuite) TestAccessLogAuthFrontend(c *check.C) { routerName: "rt-authFrontend", serviceURL: "-", }, + { + formatOnly: false, + code: "401", + user: "test", + routerName: "rt-authFrontend", + serviceURL: "-", + }, + { + formatOnly: false, + code: "200", + user: "test", + routerName: "rt-authFrontend", + serviceURL: "http://172.17.0", + }, } // Start Traefik @@ -130,7 +144,7 @@ func (s *AccessLogSuite) TestAccessLogAuthFrontend(c *check.C) { // Verify Traefik started OK checkTraefikStarted(c) - // Test auth frontend + // Test auth entrypoint req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8006/", nil) c.Assert(err, checker.IsNil) req.Host = "frontend.auth.docker.local" @@ -138,6 +152,16 @@ func (s *AccessLogSuite) TestAccessLogAuthFrontend(c *check.C) { err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusUnauthorized), try.HasBody()) c.Assert(err, checker.IsNil) + req.SetBasicAuth("test", "") + + err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusUnauthorized), try.HasBody()) + c.Assert(err, checker.IsNil) + + req.SetBasicAuth("test", "test") + + err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody()) + c.Assert(err, checker.IsNil) + // Verify access.log output as expected count := checkAccessLogExactValuesOutput(c, expected) @@ -158,6 +182,13 @@ func (s *AccessLogSuite) TestAccessLogDigestAuthMiddleware(c *check.C) { routerName: "rt-digestAuthMiddleware", serviceURL: "-", }, + { + formatOnly: false, + code: "401", + user: "test", + routerName: "rt-digestAuthMiddleware", + serviceURL: "-", + }, { formatOnly: false, code: "200", @@ -192,15 +223,22 @@ func (s *AccessLogSuite) TestAccessLogDigestAuthMiddleware(c *check.C) { resp, err := try.ResponseUntilStatusCode(req, 500*time.Millisecond, http.StatusUnauthorized) c.Assert(err, checker.IsNil) - digestParts := digestParts(resp) - digestParts["uri"] = "/" - digestParts["method"] = http.MethodGet - digestParts["username"] = "test" - digestParts["password"] = "test" + digest := digestParts(resp) + digest["uri"] = "/" + digest["method"] = http.MethodGet + digest["username"] = "test" + digest["password"] = "wrong" - req.Header.Set("Authorization", getDigestAuthorization(digestParts)) + req.Header.Set("Authorization", getDigestAuthorization(digest)) req.Header.Set("Content-Type", "application/json") + err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusUnauthorized), try.HasBody()) + c.Assert(err, checker.IsNil) + + digest["password"] = "test" + + req.Header.Set("Authorization", getDigestAuthorization(digest)) + err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody()) c.Assert(err, checker.IsNil) diff --git a/integration/consul_test.go b/integration/consul_test.go index d0496b9ca..248c73828 100644 --- a/integration/consul_test.go +++ b/integration/consul_test.go @@ -138,7 +138,7 @@ func (s *ConsulSuite) TestSimpleConfiguration(c *check.C) { expectedJSON := filepath.FromSlash("testdata/rawdata-consul.json") if *updateExpected { - err = ioutil.WriteFile(expectedJSON, got, 0666) + err = ioutil.WriteFile(expectedJSON, got, 0o666) c.Assert(err, checker.IsNil) } diff --git a/integration/docker_compose_test.go b/integration/docker_compose_test.go index 7e1022ac8..3eea49890 100644 --- a/integration/docker_compose_test.go +++ b/integration/docker_compose_test.go @@ -36,8 +36,8 @@ func (s *DockerComposeSuite) TearDownSuite(c *check.C) { } func (s *DockerComposeSuite) TestComposeScale(c *check.C) { - var serviceCount = 2 - var composeService = "whoami1" + serviceCount := 2 + composeService := "whoami1" s.composeProject.Scale(c, composeService, serviceCount) diff --git a/integration/docker_test.go b/integration/docker_test.go index 2116cebaa..eb7ce57bc 100644 --- a/integration/docker_test.go +++ b/integration/docker_test.go @@ -43,7 +43,7 @@ func (s *DockerSuite) startContainerWithLabels(c *check.C, image string, labels }) } -func (s *DockerSuite) startContainerWithNameAndLabels(c *check.C, name string, image string, labels map[string]string, args ...string) string { +func (s *DockerSuite) startContainerWithNameAndLabels(c *check.C, name, image string, labels map[string]string, args ...string) string { return s.startContainerWithConfig(c, image, d.ContainerConfig{ Name: name, Cmd: args, diff --git a/integration/etcd_test.go b/integration/etcd_test.go index b0bd07902..7be0dfdfe 100644 --- a/integration/etcd_test.go +++ b/integration/etcd_test.go @@ -138,7 +138,7 @@ func (s *EtcdSuite) TestSimpleConfiguration(c *check.C) { expectedJSON := filepath.FromSlash("testdata/rawdata-etcd.json") if *updateExpected { - err = ioutil.WriteFile(expectedJSON, got, 0666) + err = ioutil.WriteFile(expectedJSON, got, 0o666) c.Assert(err, checker.IsNil) } diff --git a/integration/fixtures/grpc/config.toml b/integration/fixtures/grpc/config.toml index 9d448ae5b..c75bbebd4 100644 --- a/integration/fixtures/grpc/config.toml +++ b/integration/fixtures/grpc/config.toml @@ -22,7 +22,7 @@ [http.routers] [http.routers.router1] - rule = "Host(`127.0.0.1`)" + rule = "Host(`localhost`)" service = "service1" [http.routers.router1.tls] diff --git a/integration/fixtures/grpc/config_h2c_termination.toml b/integration/fixtures/grpc/config_h2c_termination.toml index a51bcc21b..f9ecc99d6 100644 --- a/integration/fixtures/grpc/config_h2c_termination.toml +++ b/integration/fixtures/grpc/config_h2c_termination.toml @@ -19,7 +19,7 @@ [http.routers] [http.routers.router1] - rule = "Host(`127.0.0.1`)" + rule = "Host(`localhost`)" service = "service1" [http.routers.router1.tls] diff --git a/integration/fixtures/grpc/config_insecure.toml b/integration/fixtures/grpc/config_insecure.toml index 264360ad5..ecd179e5a 100644 --- a/integration/fixtures/grpc/config_insecure.toml +++ b/integration/fixtures/grpc/config_insecure.toml @@ -22,7 +22,7 @@ [http.routers] [http.routers.router1] - rule = "Host(`127.0.0.1`)" + rule = "Host(`localhost`)" service = "service1" [http.routers.router1.tls] diff --git a/integration/fixtures/grpc/config_retry.toml b/integration/fixtures/grpc/config_retry.toml index 6f7a3a96a..b984f6ddf 100644 --- a/integration/fixtures/grpc/config_retry.toml +++ b/integration/fixtures/grpc/config_retry.toml @@ -22,7 +22,7 @@ [http.routers] [http.routers.router1] - rule = "Host(`127.0.0.1`)" + rule = "Host(`localhost`)" service = "service1" middlewares = ["retryer"] [http.routers.router1.tls] diff --git a/integration/fixtures/headers/secure.toml b/integration/fixtures/headers/secure.toml index d04e5a9c8..08a12319b 100644 --- a/integration/fixtures/headers/secure.toml +++ b/integration/fixtures/headers/secure.toml @@ -2,6 +2,9 @@ checkNewVersion = false sendAnonymousUsage = false +[api] + insecure = true + [log] level = "DEBUG" @@ -24,6 +27,11 @@ rule = "Host(`test2.localhost`)" service = "service1" + [http.routers.router3] + rule = "Host(`internal.localhost`)" + middlewares = ["secure"] + service = "api@internal" + [http.middlewares] [http.middlewares.secure.headers] featurePolicy = "vibrate 'none';" diff --git a/integration/grpc_test.go b/integration/grpc_test.go index 1a25931a8..f881fb66e 100644 --- a/integration/grpc_test.go +++ b/integration/grpc_test.go @@ -19,8 +19,10 @@ import ( "google.golang.org/grpc/credentials" ) -var LocalhostCert []byte -var LocalhostKey []byte +var ( + LocalhostCert []byte + LocalhostKey []byte +) const randCharset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890" @@ -84,7 +86,7 @@ func starth2cGRPCServer(lis net.Listener, server *myserver) error { func getHelloClientGRPC() (helloworld.GreeterClient, func() error, error) { roots := x509.NewCertPool() roots.AppendCertsFromPEM(LocalhostCert) - credsClient := credentials.NewClientTLSFromCert(roots, "") + credsClient := credentials.NewClientTLSFromCert(roots, "localhost") conn, err := grpc.Dial("127.0.0.1:4443", grpc.WithTransportCredentials(credsClient)) if err != nil { return nil, func() error { return nil }, err @@ -165,7 +167,7 @@ func (s *GRPCSuite) TestGRPC(c *check.C) { defer cmd.Process.Kill() // wait for Traefik - err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)")) + err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`localhost`)")) c.Assert(err, check.IsNil) var response string @@ -245,7 +247,7 @@ func (s *GRPCSuite) TestGRPCh2cTermination(c *check.C) { defer cmd.Process.Kill() // wait for Traefik - err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)")) + err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`localhost`)")) c.Assert(err, check.IsNil) var response string @@ -287,7 +289,7 @@ func (s *GRPCSuite) TestGRPCInsecure(c *check.C) { defer cmd.Process.Kill() // wait for Traefik - err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)")) + err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`localhost`)")) c.Assert(err, check.IsNil) var response string @@ -334,7 +336,7 @@ func (s *GRPCSuite) TestGRPCBuffer(c *check.C) { defer cmd.Process.Kill() // wait for Traefik - err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)")) + err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`localhost`)")) c.Assert(err, check.IsNil) var client helloworld.Greeter_StreamExampleClient client, closer, err := callStreamExampleClientGRPC() @@ -393,7 +395,7 @@ func (s *GRPCSuite) TestGRPCBufferWithFlushInterval(c *check.C) { defer cmd.Process.Kill() // wait for Traefik - err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)")) + err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`localhost`)")) c.Assert(err, check.IsNil) var client helloworld.Greeter_StreamExampleClient @@ -451,7 +453,7 @@ func (s *GRPCSuite) TestGRPCWithRetry(c *check.C) { defer cmd.Process.Kill() // wait for Traefik - err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)")) + err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`localhost`)")) c.Assert(err, check.IsNil) var response string diff --git a/integration/headers_test.go b/integration/headers_test.go index 06c208ef7..b2e34beff 100644 --- a/integration/headers_test.go +++ b/integration/headers_test.go @@ -131,16 +131,18 @@ func (s *HeadersSuite) TestSecureHeadersResponses(c *check.C) { c.Assert(err, checker.IsNil) testCase := []struct { - desc string - expected http.Header - reqHost string + desc string + expected http.Header + reqHost string + internalReqHost string }{ { desc: "Feature-Policy Set", expected: http.Header{ "Feature-Policy": {"vibrate 'none';"}, }, - reqHost: "test.localhost", + reqHost: "test.localhost", + internalReqHost: "internal.localhost", }, } @@ -149,7 +151,14 @@ func (s *HeadersSuite) TestSecureHeadersResponses(c *check.C) { c.Assert(err, checker.IsNil) req.Host = test.reqHost - err = try.Request(req, 500*time.Millisecond, try.HasHeaderStruct(test.expected)) + err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasHeaderStruct(test.expected)) + c.Assert(err, checker.IsNil) + + req, err = http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/api/rawdata", nil) + c.Assert(err, checker.IsNil) + req.Host = test.internalReqHost + + err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasHeaderStruct(test.expected)) c.Assert(err, checker.IsNil) } } diff --git a/integration/helloworld/helloworld.pb.go b/integration/helloworld/helloworld.pb.go index bfdd5f9dc..96320f02a 100644 --- a/integration/helloworld/helloworld.pb.go +++ b/integration/helloworld/helloworld.pb.go @@ -15,9 +15,12 @@ It has these top-level messages: */ package helloworld -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" +import ( + fmt "fmt" + math "math" + + proto "github.com/golang/protobuf/proto" +) import ( context "context" @@ -26,9 +29,11 @@ import ( ) // Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf +var ( + _ = proto.Marshal + _ = fmt.Errorf + _ = math.Inf +) // This is a compile-time assertion to ensure that this generated file // is compatible with the proto package it is being compiled against. @@ -102,8 +107,10 @@ func init() { } // Reference imports to suppress errors if they are not otherwise used. -var _ context.Context -var _ grpc.ClientConn +var ( + _ context.Context + _ grpc.ClientConn +) // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. diff --git a/integration/https_test.go b/integration/https_test.go index bf19cb30c..9ac854eba 100644 --- a/integration/https_test.go +++ b/integration/https_test.go @@ -956,11 +956,13 @@ func modifyCertificateConfFileContent(c *check.C, certFileName, confFileName str if len(certFileName) > 0 { tlsConf := dynamic.Configuration{ TLS: &dynamic.TLSConfiguration{ - Certificates: []*traefiktls.CertAndStores{{ - Certificate: traefiktls.Certificate{ - CertFile: traefiktls.FileOrContent("fixtures/https/" + certFileName + ".cert"), - KeyFile: traefiktls.FileOrContent("fixtures/https/" + certFileName + ".key"), - }}, + Certificates: []*traefiktls.CertAndStores{ + { + Certificate: traefiktls.Certificate{ + CertFile: traefiktls.FileOrContent("fixtures/https/" + certFileName + ".cert"), + KeyFile: traefiktls.FileOrContent("fixtures/https/" + certFileName + ".key"), + }, + }, }, }, } diff --git a/integration/integration_test.go b/integration/integration_test.go index 65158cb65..32309349b 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -21,10 +21,12 @@ import ( checker "github.com/vdemeester/shakers" ) -var integration = flag.Bool("integration", false, "run integration tests") -var container = flag.Bool("container", false, "run container integration tests") -var host = flag.Bool("host", false, "run host integration tests") -var showLog = flag.Bool("tlog", false, "always show Traefik logs") +var ( + integration = flag.Bool("integration", false, "run integration tests") + container = flag.Bool("container", false, "run container integration tests") + host = flag.Bool("host", false, "run host integration tests") + showLog = flag.Bool("tlog", false, "always show Traefik logs") +) func Test(t *testing.T) { if !*integration { diff --git a/integration/k8s_test.go b/integration/k8s_test.go index 36cd3e54e..c0fda58b7 100644 --- a/integration/k8s_test.go +++ b/integration/k8s_test.go @@ -119,7 +119,7 @@ func testConfiguration(c *check.C, path, apiPort string) { newJSON, err := json.MarshalIndent(rtRepr, "", "\t") c.Assert(err, checker.IsNil) - err = ioutil.WriteFile(expectedJSON, newJSON, 0644) + err = ioutil.WriteFile(expectedJSON, newJSON, 0o644) c.Assert(err, checker.IsNil) c.Errorf("We do not want a passing test in file update mode") } diff --git a/integration/marathon15_test.go b/integration/marathon15_test.go index fb454f071..6daef217b 100644 --- a/integration/marathon15_test.go +++ b/integration/marathon15_test.go @@ -55,7 +55,7 @@ func (s *MarathonSuite15) extendDockerHostsFile(host, ipAddr string) error { // (See also https://groups.google.com/d/topic/docker-user/JOGE7AnJ3Gw/discussion.) if os.Getenv("CONTAINER") == "DOCKER" { // We are running inside a container -- extend the hosts file. - file, err := os.OpenFile(hostsFile, os.O_APPEND|os.O_WRONLY, 0600) + file, err := os.OpenFile(hostsFile, os.O_APPEND|os.O_WRONLY, 0o600) if err != nil { return err } diff --git a/integration/marathon_test.go b/integration/marathon_test.go index 62f4d0bfa..b44c34f24 100644 --- a/integration/marathon_test.go +++ b/integration/marathon_test.go @@ -60,7 +60,7 @@ func (s *MarathonSuite) extendDockerHostsFile(host, ipAddr string) error { // (See also https://groups.google.com/d/topic/docker-user/JOGE7AnJ3Gw/discussion.) if os.Getenv("CONTAINER") == "DOCKER" { // We are running inside a container -- extend the hosts file. - file, err := os.OpenFile(hostsFile, os.O_APPEND|os.O_WRONLY, 0600) + file, err := os.OpenFile(hostsFile, os.O_APPEND|os.O_WRONLY, 0o600) if err != nil { return err } diff --git a/integration/redis_test.go b/integration/redis_test.go index 91022cdad..1b551d9c3 100644 --- a/integration/redis_test.go +++ b/integration/redis_test.go @@ -138,7 +138,7 @@ func (s *RedisSuite) TestSimpleConfiguration(c *check.C) { expectedJSON := filepath.FromSlash("testdata/rawdata-redis.json") if *updateExpected { - err = ioutil.WriteFile(expectedJSON, got, 0666) + err = ioutil.WriteFile(expectedJSON, got, 0o666) c.Assert(err, checker.IsNil) } diff --git a/integration/testdata/rawdata-consul.json b/integration/testdata/rawdata-consul.json index 8f70cc3b4..0bf110eac 100644 --- a/integration/testdata/rawdata-consul.json +++ b/integration/testdata/rawdata-consul.json @@ -86,7 +86,7 @@ }, "dashboard_redirect@internal": { "redirectRegex": { - "regex": "^(http:\\/\\/[^:\\/]+(:\\d+)?)\\/$", + "regex": "^(http:\\/\\/(\\[[\\w:.]+\\]|[\\w\\._-]+)(:\\d+)?)\\/$", "replacement": "${1}/dashboard/", "permanent": true }, diff --git a/integration/testdata/rawdata-etcd.json b/integration/testdata/rawdata-etcd.json index d8e844220..92b4aa2b5 100644 --- a/integration/testdata/rawdata-etcd.json +++ b/integration/testdata/rawdata-etcd.json @@ -86,7 +86,7 @@ }, "dashboard_redirect@internal": { "redirectRegex": { - "regex": "^(http:\\/\\/[^:\\/]+(:\\d+)?)\\/$", + "regex": "^(http:\\/\\/(\\[[\\w:.]+\\]|[\\w\\._-]+)(:\\d+)?)\\/$", "replacement": "${1}/dashboard/", "permanent": true }, diff --git a/integration/testdata/rawdata-ingress.json b/integration/testdata/rawdata-ingress.json index 6848403f4..dbbfc239e 100644 --- a/integration/testdata/rawdata-ingress.json +++ b/integration/testdata/rawdata-ingress.json @@ -54,7 +54,7 @@ "middlewares": { "dashboard_redirect@internal": { "redirectRegex": { - "regex": "^(http:\\/\\/[^:\\/]+(:\\d+)?)\\/$", + "regex": "^(http:\\/\\/(\\[[\\w:.]+\\]|[\\w\\._-]+)(:\\d+)?)\\/$", "replacement": "${1}/dashboard/", "permanent": true }, diff --git a/integration/testdata/rawdata-redis.json b/integration/testdata/rawdata-redis.json index 15a7a0396..1dda1cebe 100644 --- a/integration/testdata/rawdata-redis.json +++ b/integration/testdata/rawdata-redis.json @@ -86,7 +86,7 @@ }, "dashboard_redirect@internal": { "redirectRegex": { - "regex": "^(http:\\/\\/[^:\\/]+(:\\d+)?)\\/$", + "regex": "^(http:\\/\\/(\\[[\\w:.]+\\]|[\\w\\._-]+)(:\\d+)?)\\/$", "replacement": "${1}/dashboard/", "permanent": true }, diff --git a/integration/testdata/rawdata-zk.json b/integration/testdata/rawdata-zk.json index 494833760..67d5c47f9 100644 --- a/integration/testdata/rawdata-zk.json +++ b/integration/testdata/rawdata-zk.json @@ -86,7 +86,7 @@ }, "dashboard_redirect@internal": { "redirectRegex": { - "regex": "^(http:\\/\\/[^:\\/]+(:\\d+)?)\\/$", + "regex": "^(http:\\/\\/(\\[[\\w:.]+\\]|[\\w\\._-]+)(:\\d+)?)\\/$", "replacement": "${1}/dashboard/", "permanent": true }, diff --git a/integration/websocket_test.go b/integration/websocket_test.go index bfdfea3f9..d1ef860ac 100644 --- a/integration/websocket_test.go +++ b/integration/websocket_test.go @@ -22,7 +22,7 @@ import ( type WebsocketSuite struct{ BaseSuite } func (s *WebsocketSuite) TestBase(c *check.C) { - var upgrader = gorillawebsocket.Upgrader{} // use default options + upgrader := gorillawebsocket.Upgrader{} // use default options srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { c, err := upgrader.Upgrade(w, r, nil) @@ -72,7 +72,7 @@ func (s *WebsocketSuite) TestBase(c *check.C) { } func (s *WebsocketSuite) TestWrongOrigin(c *check.C) { - var upgrader = gorillawebsocket.Upgrader{} // use default options + upgrader := gorillawebsocket.Upgrader{} // use default options srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { c, err := upgrader.Upgrade(w, r, nil) @@ -122,7 +122,7 @@ func (s *WebsocketSuite) TestWrongOrigin(c *check.C) { func (s *WebsocketSuite) TestOrigin(c *check.C) { // use default options - var upgrader = gorillawebsocket.Upgrader{} + upgrader := gorillawebsocket.Upgrader{} srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { c, err := upgrader.Upgrade(w, r, nil) @@ -180,7 +180,7 @@ func (s *WebsocketSuite) TestOrigin(c *check.C) { } func (s *WebsocketSuite) TestWrongOriginIgnoredByServer(c *check.C) { - var upgrader = gorillawebsocket.Upgrader{CheckOrigin: func(r *http.Request) bool { + upgrader := gorillawebsocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }} @@ -240,7 +240,7 @@ func (s *WebsocketSuite) TestWrongOriginIgnoredByServer(c *check.C) { } func (s *WebsocketSuite) TestSSLTermination(c *check.C) { - var upgrader = gorillawebsocket.Upgrader{} // use default options + upgrader := gorillawebsocket.Upgrader{} // use default options srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { c, err := upgrader.Upgrade(w, r, nil) @@ -297,11 +297,10 @@ func (s *WebsocketSuite) TestSSLTermination(c *check.C) { } func (s *WebsocketSuite) TestBasicAuth(c *check.C) { - var upgrader = gorillawebsocket.Upgrader{} // use default options + upgrader := gorillawebsocket.Upgrader{} // use default options srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { conn, err := upgrader.Upgrade(w, r, nil) - if err != nil { return } @@ -390,7 +389,7 @@ func (s *WebsocketSuite) TestSpecificResponseFromBackend(c *check.C) { } func (s *WebsocketSuite) TestURLWithURLEncodedChar(c *check.C) { - var upgrader = gorillawebsocket.Upgrader{} // use default options + upgrader := gorillawebsocket.Upgrader{} // use default options srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { c.Assert(r.URL.EscapedPath(), check.Equals, "/ws/http%3A%2F%2Ftest") @@ -441,7 +440,7 @@ func (s *WebsocketSuite) TestURLWithURLEncodedChar(c *check.C) { } func (s *WebsocketSuite) TestSSLhttp2(c *check.C) { - var upgrader = gorillawebsocket.Upgrader{} // use default options + upgrader := gorillawebsocket.Upgrader{} // use default options ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { c, err := upgrader.Upgrade(w, r, nil) @@ -504,7 +503,7 @@ func (s *WebsocketSuite) TestSSLhttp2(c *check.C) { } func (s *WebsocketSuite) TestHeaderAreForwared(c *check.C) { - var upgrader = gorillawebsocket.Upgrader{} // use default options + upgrader := gorillawebsocket.Upgrader{} // use default options srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { c.Assert(r.Header.Get("X-Token"), check.Equals, "my-token") diff --git a/integration/zk_test.go b/integration/zk_test.go index bc9ecc9f3..b648d8cc8 100644 --- a/integration/zk_test.go +++ b/integration/zk_test.go @@ -138,7 +138,7 @@ func (s *ZookeeperSuite) TestSimpleConfiguration(c *check.C) { expectedJSON := filepath.FromSlash("testdata/rawdata-zk.json") if *updateExpected { - err = ioutil.WriteFile(expectedJSON, got, 0666) + err = ioutil.WriteFile(expectedJSON, got, 0o666) c.Assert(err, checker.IsNil) } diff --git a/internal/gendoc.go b/internal/gendoc.go index 505e98192..2228c1348 100644 --- a/internal/gendoc.go +++ b/internal/gendoc.go @@ -24,7 +24,7 @@ func main() { genKVDynConfDoc("./docs/content/reference/dynamic-configuration/kv-ref.md") } -func genStaticConfDoc(outputFile string, prefix string, encodeFn func(interface{}) ([]parser.Flat, error)) { +func genStaticConfDoc(outputFile, prefix string, encodeFn func(interface{}) ([]parser.Flat, error)) { logger := log.WithoutContext().WithField("file", outputFile) element := &static.Configuration{} @@ -41,7 +41,7 @@ func genStaticConfDoc(outputFile string, prefix string, encodeFn func(interface{ logger.Fatal(err) } - file, err := os.OpenFile(outputFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666) + file, err := os.OpenFile(outputFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o666) if err != nil { logger.Fatal(err) } diff --git a/pkg/anonymize/anonymize_doOnStruct_test.go b/pkg/anonymize/anonymize_doOnStruct_test.go index 3d9529682..676f3bfa3 100644 --- a/pkg/anonymize/anonymize_doOnStruct_test.go +++ b/pkg/anonymize/anonymize_doOnStruct_test.go @@ -11,6 +11,7 @@ type Courgette struct { Ji string Ho string } + type Tomate struct { Ji string Ho string diff --git a/pkg/api/handler_entrypoint_test.go b/pkg/api/handler_entrypoint_test.go index 350734ec8..b9f97f367 100644 --- a/pkg/api/handler_entrypoint_test.go +++ b/pkg/api/handler_entrypoint_test.go @@ -226,7 +226,7 @@ func TestHandler_EntryPoints(t *testing.T) { newJSON, err := json.MarshalIndent(results, "", "\t") require.NoError(t, err) - err = ioutil.WriteFile(test.expected.jsonFile, newJSON, 0644) + err = ioutil.WriteFile(test.expected.jsonFile, newJSON, 0o644) require.NoError(t, err) } diff --git a/pkg/api/handler_http_test.go b/pkg/api/handler_http_test.go index 0d114855a..92723adc0 100644 --- a/pkg/api/handler_http_test.go +++ b/pkg/api/handler_http_test.go @@ -840,7 +840,7 @@ func TestHandler_HTTP(t *testing.T) { newJSON, err := json.MarshalIndent(results, "", "\t") require.NoError(t, err) - err = ioutil.WriteFile(test.expected.jsonFile, newJSON, 0644) + err = ioutil.WriteFile(test.expected.jsonFile, newJSON, 0o644) require.NoError(t, err) } diff --git a/pkg/api/handler_overview_test.go b/pkg/api/handler_overview_test.go index d6e3629bf..2c92f4c0c 100644 --- a/pkg/api/handler_overview_test.go +++ b/pkg/api/handler_overview_test.go @@ -277,7 +277,7 @@ func TestHandler_Overview(t *testing.T) { newJSON, err := json.MarshalIndent(results, "", "\t") require.NoError(t, err) - err = ioutil.WriteFile(test.expected.jsonFile, newJSON, 0644) + err = ioutil.WriteFile(test.expected.jsonFile, newJSON, 0o644) require.NoError(t, err) } diff --git a/pkg/api/handler_tcp_test.go b/pkg/api/handler_tcp_test.go index a7957cd25..e3f189f04 100644 --- a/pkg/api/handler_tcp_test.go +++ b/pkg/api/handler_tcp_test.go @@ -548,7 +548,7 @@ func TestHandler_TCP(t *testing.T) { newJSON, err := json.MarshalIndent(results, "", "\t") require.NoError(t, err) - err = ioutil.WriteFile(test.expected.jsonFile, newJSON, 0644) + err = ioutil.WriteFile(test.expected.jsonFile, newJSON, 0o644) require.NoError(t, err) } diff --git a/pkg/api/handler_test.go b/pkg/api/handler_test.go index 683e162f7..5a726febe 100644 --- a/pkg/api/handler_test.go +++ b/pkg/api/handler_test.go @@ -161,7 +161,7 @@ func TestHandler_RawData(t *testing.T) { newJSON, err := json.MarshalIndent(rtRepr, "", "\t") require.NoError(t, err) - err = ioutil.WriteFile(test.expected.json, newJSON, 0644) + err = ioutil.WriteFile(test.expected.json, newJSON, 0o644) require.NoError(t, err) } diff --git a/pkg/api/handler_udp_test.go b/pkg/api/handler_udp_test.go index a716300cc..e328be8d7 100644 --- a/pkg/api/handler_udp_test.go +++ b/pkg/api/handler_udp_test.go @@ -525,7 +525,7 @@ func TestHandler_UDP(t *testing.T) { newJSON, err := json.MarshalIndent(results, "", "\t") require.NoError(t, err) - err = ioutil.WriteFile(test.expected.jsonFile, newJSON, 0644) + err = ioutil.WriteFile(test.expected.jsonFile, newJSON, 0o644) require.NoError(t, err) } diff --git a/pkg/config/file/file.go b/pkg/config/file/file.go index f55ec2fe3..9024a23a8 100644 --- a/pkg/config/file/file.go +++ b/pkg/config/file/file.go @@ -40,7 +40,7 @@ func Decode(filePath string, element interface{}) error { // file contents -> tree of untyped nodes // untyped nodes -> nodes augmented with metadata such as kind (inferred from element) // "typed" nodes -> typed element. -func DecodeContent(content string, extension string, element interface{}) error { +func DecodeContent(content, extension string, element interface{}) error { data := make(map[string]interface{}) switch extension { diff --git a/pkg/config/file/file_node_test.go b/pkg/config/file/file_node_test.go index e301047a1..927e3828f 100644 --- a/pkg/config/file/file_node_test.go +++ b/pkg/config/file/file_node_test.go @@ -100,25 +100,34 @@ func Test_decodeFileToNode_Toml(t *testing.T) { {Name: "defaultMode", Value: "foobar"}, {Name: "names", Children: []*parser.Node{ {Name: "name0", Value: "foobar"}, - {Name: "name1", Value: "foobar"}}}}}, + {Name: "name1", Value: "foobar"}, + }}, + }}, {Name: "names", Children: []*parser.Node{ {Name: "name0", Value: "foobar"}, - {Name: "name1", Value: "foobar"}}}}}, + {Name: "name1", Value: "foobar"}, + }}, + }}, {Name: "filePath", Value: "foobar"}, {Name: "filters", Children: []*parser.Node{ {Name: "minDuration", Value: "42"}, {Name: "retryAttempts", Value: "true"}, - {Name: "statusCodes", Value: "foobar,foobar"}}}, - {Name: "format", Value: "foobar"}}}, + {Name: "statusCodes", Value: "foobar,foobar"}, + }}, + {Name: "format", Value: "foobar"}, + }}, {Name: "api", Children: []*parser.Node{ {Name: "dashboard", Value: "true"}, {Name: "entryPoint", Value: "foobar"}, {Name: "middlewares", Value: "foobar,foobar"}, {Name: "statistics", Children: []*parser.Node{ - {Name: "recentErrors", Value: "42"}}}}}, + {Name: "recentErrors", Value: "42"}, + }}, + }}, {Name: "certificatesResolvers", Children: []*parser.Node{ {Name: "default", Children: []*parser.Node{ - {Name: "acme", + { + Name: "acme", Children: []*parser.Node{ {Name: "acmeLogging", Value: "true"}, {Name: "caServer", Value: "foobar"}, @@ -131,7 +140,8 @@ func Test_decodeFileToNode_Toml(t *testing.T) { {Name: "email", Value: "foobar"}, {Name: "entryPoint", Value: "foobar"}, {Name: "httpChallenge", Children: []*parser.Node{ - {Name: "entryPoint", Value: "foobar"}}}, + {Name: "entryPoint", Value: "foobar"}, + }}, {Name: "keyType", Value: "foobar"}, {Name: "storage", Value: "foobar"}, {Name: "tlsChallenge"}, @@ -144,33 +154,44 @@ func Test_decodeFileToNode_Toml(t *testing.T) { {Name: "address", Value: "foobar"}, {Name: "forwardedHeaders", Children: []*parser.Node{ {Name: "insecure", Value: "true"}, - {Name: "trustedIPs", Value: "foobar,foobar"}}}, + {Name: "trustedIPs", Value: "foobar,foobar"}, + }}, {Name: "proxyProtocol", Children: []*parser.Node{ {Name: "insecure", Value: "true"}, - {Name: "trustedIPs", Value: "foobar,foobar"}}}, + {Name: "trustedIPs", Value: "foobar,foobar"}, + }}, {Name: "transport", Children: []*parser.Node{ {Name: "lifeCycle", Children: []*parser.Node{ {Name: "graceTimeOut", Value: "42"}, - {Name: "requestAcceptGraceTimeout", Value: "42"}}}, + {Name: "requestAcceptGraceTimeout", Value: "42"}, + }}, {Name: "respondingTimeouts", Children: []*parser.Node{ {Name: "idleTimeout", Value: "42"}, {Name: "readTimeout", Value: "42"}, - {Name: "writeTimeout", Value: "42"}}}}}}}}}, + {Name: "writeTimeout", Value: "42"}, + }}, + }}, + }}, + }}, {Name: "global", Children: []*parser.Node{ {Name: "checkNewVersion", Value: "true"}, - {Name: "sendAnonymousUsage", Value: "true"}}}, + {Name: "sendAnonymousUsage", Value: "true"}, + }}, {Name: "hostResolver", Children: []*parser.Node{ {Name: "cnameFlattening", Value: "true"}, {Name: "resolvConfig", Value: "foobar"}, - {Name: "resolvDepth", Value: "42"}}}, + {Name: "resolvDepth", Value: "42"}, + }}, {Name: "log", Children: []*parser.Node{ {Name: "filePath", Value: "foobar"}, {Name: "format", Value: "foobar"}, - {Name: "level", Value: "foobar"}}}, + {Name: "level", Value: "foobar"}, + }}, {Name: "metrics", Children: []*parser.Node{ {Name: "datadog", Children: []*parser.Node{ {Name: "address", Value: "foobar"}, - {Name: "pushInterval", Value: "10s"}}}, + {Name: "pushInterval", Value: "10s"}, + }}, {Name: "influxDB", Children: []*parser.Node{ {Name: "address", Value: "foobar"}, {Name: "database", Value: "foobar"}, @@ -178,17 +199,22 @@ func Test_decodeFileToNode_Toml(t *testing.T) { {Name: "protocol", Value: "foobar"}, {Name: "pushInterval", Value: "10s"}, {Name: "retentionPolicy", Value: "foobar"}, - {Name: "username", Value: "foobar"}}}, + {Name: "username", Value: "foobar"}, + }}, {Name: "prometheus", Children: []*parser.Node{ {Name: "buckets", Value: "42,42"}, {Name: "entryPoint", Value: "foobar"}, - {Name: "middlewares", Value: "foobar,foobar"}}}, + {Name: "middlewares", Value: "foobar,foobar"}, + }}, {Name: "statsD", Children: []*parser.Node{ {Name: "address", Value: "foobar"}, - {Name: "pushInterval", Value: "10s"}}}}}, + {Name: "pushInterval", Value: "10s"}, + }}, + }}, {Name: "ping", Children: []*parser.Node{ {Name: "entryPoint", Value: "foobar"}, - {Name: "middlewares", Value: "foobar,foobar"}}}, + {Name: "middlewares", Value: "foobar,foobar"}, + }}, {Name: "providers", Children: []*parser.Node{ {Name: "docker", Children: []*parser.Node{ {Name: "constraints", Value: "foobar"}, @@ -203,15 +229,19 @@ func Test_decodeFileToNode_Toml(t *testing.T) { {Name: "caOptional", Value: "true"}, {Name: "cert", Value: "foobar"}, {Name: "insecureSkipVerify", Value: "true"}, - {Name: "key", Value: "foobar"}}}, + {Name: "key", Value: "foobar"}, + }}, {Name: "useBindPortIP", Value: "true"}, - {Name: "watch", Value: "true"}}}, + {Name: "watch", Value: "true"}, + }}, {Name: "file", Children: []*parser.Node{ {Name: "debugLogGeneratedTemplate", Value: "true"}, {Name: "directory", Value: "foobar"}, {Name: "filename", Value: "foobar"}, - {Name: "watch", Value: "true"}}}, - {Name: "kubernetesCRD", + {Name: "watch", Value: "true"}, + }}, + { + Name: "kubernetesCRD", Children: []*parser.Node{ {Name: "certAuthFilePath", Value: "foobar"}, {Name: "disablePassHostHeaders", Value: "true"}, @@ -219,7 +249,9 @@ func Test_decodeFileToNode_Toml(t *testing.T) { {Name: "ingressClass", Value: "foobar"}, {Name: "labelSelector", Value: "foobar"}, {Name: "namespaces", Value: "foobar,foobar"}, - {Name: "token", Value: "foobar"}}}, + {Name: "token", Value: "foobar"}, + }, + }, {Name: "kubernetesIngress", Children: []*parser.Node{ {Name: "certAuthFilePath", Value: "foobar"}, {Name: "disablePassHostHeaders", Value: "true"}, @@ -228,14 +260,17 @@ func Test_decodeFileToNode_Toml(t *testing.T) { {Name: "ingressEndpoint", Children: []*parser.Node{ {Name: "hostname", Value: "foobar"}, {Name: "ip", Value: "foobar"}, - {Name: "publishedService", Value: "foobar"}}}, + {Name: "publishedService", Value: "foobar"}, + }}, {Name: "labelSelector", Value: "foobar"}, {Name: "namespaces", Value: "foobar,foobar"}, - {Name: "token", Value: "foobar"}}}, + {Name: "token", Value: "foobar"}, + }}, {Name: "marathon", Children: []*parser.Node{ {Name: "basic", Children: []*parser.Node{ {Name: "httpBasicAuthUser", Value: "foobar"}, - {Name: "httpBasicPassword", Value: "foobar"}}}, + {Name: "httpBasicPassword", Value: "foobar"}, + }}, {Name: "constraints", Value: "foobar"}, {Name: "dcosToken", Value: "foobar"}, {Name: "defaultRule", Value: "foobar"}, @@ -251,10 +286,12 @@ func Test_decodeFileToNode_Toml(t *testing.T) { {Name: "caOptional", Value: "true"}, {Name: "cert", Value: "foobar"}, {Name: "insecureSkipVerify", Value: "true"}, - {Name: "key", Value: "foobar"}}}, + {Name: "key", Value: "foobar"}, + }}, {Name: "tlsHandshakeTimeout", Value: "42"}, {Name: "trace", Value: "true"}, - {Name: "watch", Value: "true"}}}, + {Name: "watch", Value: "true"}, + }}, {Name: "providersThrottleDuration", Value: "42"}, {Name: "rancher", Children: []*parser.Node{ {Name: "constraints", Value: "foobar"}, @@ -264,17 +301,22 @@ func Test_decodeFileToNode_Toml(t *testing.T) { {Name: "intervalPoll", Value: "true"}, {Name: "prefix", Value: "foobar"}, {Name: "refreshSeconds", Value: "42"}, - {Name: "watch", Value: "true"}}}, + {Name: "watch", Value: "true"}, + }}, {Name: "rest", Children: []*parser.Node{ - {Name: "entryPoint", Value: "foobar"}}}}}, + {Name: "entryPoint", Value: "foobar"}, + }}, + }}, {Name: "serversTransport", Children: []*parser.Node{ {Name: "forwardingTimeouts", Children: []*parser.Node{ {Name: "dialTimeout", Value: "42"}, {Name: "idleConnTimeout", Value: "42"}, - {Name: "responseHeaderTimeout", Value: "42"}}}, + {Name: "responseHeaderTimeout", Value: "42"}, + }}, {Name: "insecureSkipVerify", Value: "true"}, {Name: "maxIdleConnsPerHost", Value: "42"}, - {Name: "rootCAs", Value: "foobar,foobar"}}}, + {Name: "rootCAs", Value: "foobar,foobar"}, + }}, {Name: "tracing", Children: []*parser.Node{ {Name: "datadog", Children: []*parser.Node{ {Name: "bagagePrefixHeaderName", Value: "foobar"}, @@ -284,18 +326,21 @@ func Test_decodeFileToNode_Toml(t *testing.T) { {Name: "parentIDHeaderName", Value: "foobar"}, {Name: "prioritySampling", Value: "true"}, {Name: "samplingPriorityHeaderName", Value: "foobar"}, - {Name: "traceIDHeaderName", Value: "foobar"}}}, + {Name: "traceIDHeaderName", Value: "foobar"}, + }}, {Name: "haystack", Children: []*parser.Node{ {Name: "globalTag", Value: "foobar"}, {Name: "localAgentHost", Value: "foobar"}, {Name: "localAgentPort", Value: "42"}, {Name: "parentIDHeaderName", Value: "foobar"}, {Name: "spanIDHeaderName", Value: "foobar"}, - {Name: "traceIDHeaderName", Value: "foobar"}}}, + {Name: "traceIDHeaderName", Value: "foobar"}, + }}, {Name: "instana", Children: []*parser.Node{ {Name: "localAgentHost", Value: "foobar"}, {Name: "localAgentPort", Value: "42"}, - {Name: "logLevel", Value: "foobar"}}}, + {Name: "logLevel", Value: "foobar"}, + }}, {Name: "jaeger", Children: []*parser.Node{ {Name: "gen128Bit", Value: "true"}, {Name: "localAgentHostPort", Value: "foobar"}, @@ -303,14 +348,17 @@ func Test_decodeFileToNode_Toml(t *testing.T) { {Name: "samplingParam", Value: "42"}, {Name: "samplingServerURL", Value: "foobar"}, {Name: "samplingType", Value: "foobar"}, - {Name: "traceContextHeaderName", Value: "foobar"}}}, + {Name: "traceContextHeaderName", Value: "foobar"}, + }}, {Name: "serviceName", Value: "foobar"}, {Name: "spanNameLimit", Value: "42"}, {Name: "zipkin", Children: []*parser.Node{ {Name: "httpEndpoint", Value: "foobar"}, {Name: "id128Bit", Value: "true"}, {Name: "sameSpan", Value: "true"}, - {Name: "sampleRate", Value: "42"}}}}}, + {Name: "sampleRate", Value: "42"}, + }}, + }}, }, } @@ -332,25 +380,34 @@ func Test_decodeFileToNode_Yaml(t *testing.T) { {Name: "defaultMode", Value: "foobar"}, {Name: "names", Children: []*parser.Node{ {Name: "name0", Value: "foobar"}, - {Name: "name1", Value: "foobar"}}}}}, + {Name: "name1", Value: "foobar"}, + }}, + }}, {Name: "names", Children: []*parser.Node{ {Name: "name0", Value: "foobar"}, - {Name: "name1", Value: "foobar"}}}}}, + {Name: "name1", Value: "foobar"}, + }}, + }}, {Name: "filePath", Value: "foobar"}, {Name: "filters", Children: []*parser.Node{ {Name: "minDuration", Value: "42"}, {Name: "retryAttempts", Value: "true"}, - {Name: "statusCodes", Value: "foobar,foobar"}}}, - {Name: "format", Value: "foobar"}}}, + {Name: "statusCodes", Value: "foobar,foobar"}, + }}, + {Name: "format", Value: "foobar"}, + }}, {Name: "api", Children: []*parser.Node{ {Name: "dashboard", Value: "true"}, {Name: "entryPoint", Value: "foobar"}, {Name: "middlewares", Value: "foobar,foobar"}, {Name: "statistics", Children: []*parser.Node{ - {Name: "recentErrors", Value: "42"}}}}}, + {Name: "recentErrors", Value: "42"}, + }}, + }}, {Name: "certificatesResolvers", Children: []*parser.Node{ {Name: "default", Children: []*parser.Node{ - {Name: "acme", + { + Name: "acme", Children: []*parser.Node{ {Name: "acmeLogging", Value: "true"}, {Name: "caServer", Value: "foobar"}, @@ -363,7 +420,8 @@ func Test_decodeFileToNode_Yaml(t *testing.T) { {Name: "email", Value: "foobar"}, {Name: "entryPoint", Value: "foobar"}, {Name: "httpChallenge", Children: []*parser.Node{ - {Name: "entryPoint", Value: "foobar"}}}, + {Name: "entryPoint", Value: "foobar"}, + }}, {Name: "keyType", Value: "foobar"}, {Name: "storage", Value: "foobar"}, {Name: "tlsChallenge"}, @@ -376,33 +434,44 @@ func Test_decodeFileToNode_Yaml(t *testing.T) { {Name: "address", Value: "foobar"}, {Name: "forwardedHeaders", Children: []*parser.Node{ {Name: "insecure", Value: "true"}, - {Name: "trustedIPs", Value: "foobar,foobar"}}}, + {Name: "trustedIPs", Value: "foobar,foobar"}, + }}, {Name: "proxyProtocol", Children: []*parser.Node{ {Name: "insecure", Value: "true"}, - {Name: "trustedIPs", Value: "foobar,foobar"}}}, + {Name: "trustedIPs", Value: "foobar,foobar"}, + }}, {Name: "transport", Children: []*parser.Node{ {Name: "lifeCycle", Children: []*parser.Node{ {Name: "graceTimeOut", Value: "42"}, - {Name: "requestAcceptGraceTimeout", Value: "42"}}}, + {Name: "requestAcceptGraceTimeout", Value: "42"}, + }}, {Name: "respondingTimeouts", Children: []*parser.Node{ {Name: "idleTimeout", Value: "42"}, {Name: "readTimeout", Value: "42"}, - {Name: "writeTimeout", Value: "42"}}}}}}}}}, + {Name: "writeTimeout", Value: "42"}, + }}, + }}, + }}, + }}, {Name: "global", Children: []*parser.Node{ {Name: "checkNewVersion", Value: "true"}, - {Name: "sendAnonymousUsage", Value: "true"}}}, + {Name: "sendAnonymousUsage", Value: "true"}, + }}, {Name: "hostResolver", Children: []*parser.Node{ {Name: "cnameFlattening", Value: "true"}, {Name: "resolvConfig", Value: "foobar"}, - {Name: "resolvDepth", Value: "42"}}}, + {Name: "resolvDepth", Value: "42"}, + }}, {Name: "log", Children: []*parser.Node{ {Name: "filePath", Value: "foobar"}, {Name: "format", Value: "foobar"}, - {Name: "level", Value: "foobar"}}}, + {Name: "level", Value: "foobar"}, + }}, {Name: "metrics", Children: []*parser.Node{ {Name: "datadog", Children: []*parser.Node{ {Name: "address", Value: "foobar"}, - {Name: "pushInterval", Value: "10s"}}}, + {Name: "pushInterval", Value: "10s"}, + }}, {Name: "influxDB", Children: []*parser.Node{ {Name: "address", Value: "foobar"}, {Name: "database", Value: "foobar"}, @@ -410,17 +479,22 @@ func Test_decodeFileToNode_Yaml(t *testing.T) { {Name: "protocol", Value: "foobar"}, {Name: "pushInterval", Value: "10s"}, {Name: "retentionPolicy", Value: "foobar"}, - {Name: "username", Value: "foobar"}}}, + {Name: "username", Value: "foobar"}, + }}, {Name: "prometheus", Children: []*parser.Node{ {Name: "buckets", Value: "42,42"}, {Name: "entryPoint", Value: "foobar"}, - {Name: "middlewares", Value: "foobar,foobar"}}}, + {Name: "middlewares", Value: "foobar,foobar"}, + }}, {Name: "statsD", Children: []*parser.Node{ {Name: "address", Value: "foobar"}, - {Name: "pushInterval", Value: "10s"}}}}}, + {Name: "pushInterval", Value: "10s"}, + }}, + }}, {Name: "ping", Children: []*parser.Node{ {Name: "entryPoint", Value: "foobar"}, - {Name: "middlewares", Value: "foobar,foobar"}}}, + {Name: "middlewares", Value: "foobar,foobar"}, + }}, {Name: "providers", Children: []*parser.Node{ {Name: "docker", Children: []*parser.Node{ {Name: "constraints", Value: "foobar"}, @@ -435,15 +509,19 @@ func Test_decodeFileToNode_Yaml(t *testing.T) { {Name: "caOptional", Value: "true"}, {Name: "cert", Value: "foobar"}, {Name: "insecureSkipVerify", Value: "true"}, - {Name: "key", Value: "foobar"}}}, + {Name: "key", Value: "foobar"}, + }}, {Name: "useBindPortIP", Value: "true"}, - {Name: "watch", Value: "true"}}}, + {Name: "watch", Value: "true"}, + }}, {Name: "file", Children: []*parser.Node{ {Name: "debugLogGeneratedTemplate", Value: "true"}, {Name: "directory", Value: "foobar"}, {Name: "filename", Value: "foobar"}, - {Name: "watch", Value: "true"}}}, - {Name: "kubernetesCRD", + {Name: "watch", Value: "true"}, + }}, + { + Name: "kubernetesCRD", Children: []*parser.Node{ {Name: "certAuthFilePath", Value: "foobar"}, {Name: "disablePassHostHeaders", Value: "true"}, @@ -451,7 +529,9 @@ func Test_decodeFileToNode_Yaml(t *testing.T) { {Name: "ingressClass", Value: "foobar"}, {Name: "labelSelector", Value: "foobar"}, {Name: "namespaces", Value: "foobar,foobar"}, - {Name: "token", Value: "foobar"}}}, + {Name: "token", Value: "foobar"}, + }, + }, {Name: "kubernetesIngress", Children: []*parser.Node{ {Name: "certAuthFilePath", Value: "foobar"}, {Name: "disablePassHostHeaders", Value: "true"}, @@ -460,14 +540,17 @@ func Test_decodeFileToNode_Yaml(t *testing.T) { {Name: "ingressEndpoint", Children: []*parser.Node{ {Name: "hostname", Value: "foobar"}, {Name: "ip", Value: "foobar"}, - {Name: "publishedService", Value: "foobar"}}}, + {Name: "publishedService", Value: "foobar"}, + }}, {Name: "labelSelector", Value: "foobar"}, {Name: "namespaces", Value: "foobar,foobar"}, - {Name: "token", Value: "foobar"}}}, + {Name: "token", Value: "foobar"}, + }}, {Name: "marathon", Children: []*parser.Node{ {Name: "basic", Children: []*parser.Node{ {Name: "httpBasicAuthUser", Value: "foobar"}, - {Name: "httpBasicPassword", Value: "foobar"}}}, + {Name: "httpBasicPassword", Value: "foobar"}, + }}, {Name: "constraints", Value: "foobar"}, {Name: "dcosToken", Value: "foobar"}, {Name: "defaultRule", Value: "foobar"}, @@ -483,10 +566,12 @@ func Test_decodeFileToNode_Yaml(t *testing.T) { {Name: "caOptional", Value: "true"}, {Name: "cert", Value: "foobar"}, {Name: "insecureSkipVerify", Value: "true"}, - {Name: "key", Value: "foobar"}}}, + {Name: "key", Value: "foobar"}, + }}, {Name: "tlsHandshakeTimeout", Value: "42"}, {Name: "trace", Value: "true"}, - {Name: "watch", Value: "true"}}}, + {Name: "watch", Value: "true"}, + }}, {Name: "providersThrottleDuration", Value: "42"}, {Name: "rancher", Children: []*parser.Node{ {Name: "constraints", Value: "foobar"}, @@ -496,17 +581,22 @@ func Test_decodeFileToNode_Yaml(t *testing.T) { {Name: "intervalPoll", Value: "true"}, {Name: "prefix", Value: "foobar"}, {Name: "refreshSeconds", Value: "42"}, - {Name: "watch", Value: "true"}}}, + {Name: "watch", Value: "true"}, + }}, {Name: "rest", Children: []*parser.Node{ - {Name: "entryPoint", Value: "foobar"}}}}}, + {Name: "entryPoint", Value: "foobar"}, + }}, + }}, {Name: "serversTransport", Children: []*parser.Node{ {Name: "forwardingTimeouts", Children: []*parser.Node{ {Name: "dialTimeout", Value: "42"}, {Name: "idleConnTimeout", Value: "42"}, - {Name: "responseHeaderTimeout", Value: "42"}}}, + {Name: "responseHeaderTimeout", Value: "42"}, + }}, {Name: "insecureSkipVerify", Value: "true"}, {Name: "maxIdleConnsPerHost", Value: "42"}, - {Name: "rootCAs", Value: "foobar,foobar"}}}, + {Name: "rootCAs", Value: "foobar,foobar"}, + }}, {Name: "tracing", Children: []*parser.Node{ {Name: "datadog", Children: []*parser.Node{ {Name: "bagagePrefixHeaderName", Value: "foobar"}, @@ -516,18 +606,21 @@ func Test_decodeFileToNode_Yaml(t *testing.T) { {Name: "parentIDHeaderName", Value: "foobar"}, {Name: "prioritySampling", Value: "true"}, {Name: "samplingPriorityHeaderName", Value: "foobar"}, - {Name: "traceIDHeaderName", Value: "foobar"}}}, + {Name: "traceIDHeaderName", Value: "foobar"}, + }}, {Name: "haystack", Children: []*parser.Node{ {Name: "globalTag", Value: "foobar"}, {Name: "localAgentHost", Value: "foobar"}, {Name: "localAgentPort", Value: "42"}, {Name: "parentIDHeaderName", Value: "foobar"}, {Name: "spanIDHeaderName", Value: "foobar"}, - {Name: "traceIDHeaderName", Value: "foobar"}}}, + {Name: "traceIDHeaderName", Value: "foobar"}, + }}, {Name: "instana", Children: []*parser.Node{ {Name: "localAgentHost", Value: "foobar"}, {Name: "localAgentPort", Value: "42"}, - {Name: "logLevel", Value: "foobar"}}}, + {Name: "logLevel", Value: "foobar"}, + }}, {Name: "jaeger", Children: []*parser.Node{ {Name: "gen128Bit", Value: "true"}, {Name: "localAgentHostPort", Value: "foobar"}, @@ -535,14 +628,17 @@ func Test_decodeFileToNode_Yaml(t *testing.T) { {Name: "samplingParam", Value: "42"}, {Name: "samplingServerURL", Value: "foobar"}, {Name: "samplingType", Value: "foobar"}, - {Name: "traceContextHeaderName", Value: "foobar"}}}, + {Name: "traceContextHeaderName", Value: "foobar"}, + }}, {Name: "serviceName", Value: "foobar"}, {Name: "spanNameLimit", Value: "42"}, {Name: "zipkin", Children: []*parser.Node{ {Name: "httpEndpoint", Value: "foobar"}, {Name: "id128Bit", Value: "true"}, {Name: "sameSpan", Value: "true"}, - {Name: "sampleRate", Value: "42"}}}}}, + {Name: "sampleRate", Value: "42"}, + }}, + }}, }, } diff --git a/pkg/config/flag/flagparser.go b/pkg/config/flag/flagparser.go index 5a66ef89a..4571bed44 100644 --- a/pkg/config/flag/flagparser.go +++ b/pkg/config/flag/flagparser.go @@ -101,7 +101,7 @@ func (f *flagSet) parseOne() (bool, error) { return true, nil } -func (f *flagSet) setValue(name string, value string) { +func (f *flagSet) setValue(name, value string) { srcKey := parser.DefaultRootName + "." + name neutralKey := strings.ToLower(srcKey) diff --git a/pkg/config/kv/kv_node.go b/pkg/config/kv/kv_node.go index e9178d4bc..3cb4f6c30 100644 --- a/pkg/config/kv/kv_node.go +++ b/pkg/config/kv/kv_node.go @@ -81,8 +81,8 @@ func filterPairs(pairs []*store.KVPair, filters []string) []*store.KVPair { return pairs[i].Key < pairs[j].Key }) - var simplePairs = map[string]*store.KVPair{} - var slicePairs = map[string][]string{} + simplePairs := map[string]*store.KVPair{} + slicePairs := map[string][]string{} for _, pair := range pairs { if len(filters) == 0 { diff --git a/pkg/config/parser/element_fill_test.go b/pkg/config/parser/element_fill_test.go index 5843702ee..968d8edc8 100644 --- a/pkg/config/parser/element_fill_test.go +++ b/pkg/config/parser/element_fill_test.go @@ -498,27 +498,31 @@ func TestFill(t *testing.T) { Children: []*Node{ {Name: "Fii", FieldName: "Fii", Value: "huu", Kind: reflect.String}, {Name: "Fuu", FieldName: "Fuu", Value: "6", Kind: reflect.Int}, - }}, - }}, + }, + }, + }, + }, element: &struct { Foo struct { Fii string Fuu int } }{}, - expected: expected{element: &struct { - Foo struct { - Fii string - Fuu int - } - }{ - Foo: struct { - Fii string - Fuu int + expected: expected{ + element: &struct { + Foo struct { + Fii string + Fuu int + } }{ - Fii: "huu", - Fuu: 6, - }}, + Foo: struct { + Fii string + Fuu int + }{ + Fii: "huu", + Fuu: 6, + }, + }, }, }, { @@ -534,27 +538,31 @@ func TestFill(t *testing.T) { Children: []*Node{ {Name: "Fii", FieldName: "Fii", Value: "huu", Kind: reflect.String}, {Name: "Fuu", FieldName: "Fuu", Value: "6", Kind: reflect.Int}, - }}, - }}, + }, + }, + }, + }, element: &struct { Foo *struct { Fii string Fuu int } }{}, - expected: expected{element: &struct { - Foo *struct { - Fii string - Fuu int - } - }{ - Foo: &struct { - Fii string - Fuu int + expected: expected{ + element: &struct { + Foo *struct { + Fii string + Fuu int + } }{ - Fii: "huu", - Fuu: 6, - }}, + Foo: &struct { + Fii string + Fuu int + }{ + Fii: "huu", + Fuu: 6, + }, + }, }, }, { @@ -568,23 +576,26 @@ func TestFill(t *testing.T) { FieldName: "Foo", Kind: reflect.Ptr, }, - }}, + }, + }, element: &struct { Foo *struct { Fii string Fuu int } `label:"allowEmpty"` }{}, - expected: expected{element: &struct { - Foo *struct { - Fii string - Fuu int - } `label:"allowEmpty"` - }{ - Foo: &struct { - Fii string - Fuu int - }{}}, + expected: expected{ + element: &struct { + Foo *struct { + Fii string + Fuu int + } `label:"allowEmpty"` + }{ + Foo: &struct { + Fii string + Fuu int + }{}, + }, }, }, { @@ -599,19 +610,21 @@ func TestFill(t *testing.T) { Kind: reflect.Ptr, Disabled: true, }, - }}, + }, + }, element: &struct { Foo *struct { Fii string Fuu int } `label:"allowEmpty"` }{}, - expected: expected{element: &struct { - Foo *struct { - Fii string - Fuu int - } `label:"allowEmpty"` - }{}, + expected: expected{ + element: &struct { + Foo *struct { + Fii string + Fuu int + } `label:"allowEmpty"` + }{}, }, }, { @@ -628,20 +641,23 @@ func TestFill(t *testing.T) { Children: []*Node{ {Name: "Fii", FieldName: "Fii", Value: "huu", Kind: reflect.String}, {Name: "Fuu", FieldName: "Fuu", Value: "6", Kind: reflect.Int}, - }}, - }}, + }, + }, + }, + }, element: &struct { Foo *struct { Fii string Fuu int } `label:"allowEmpty"` }{}, - expected: expected{element: &struct { - Foo *struct { - Fii string - Fuu int - } `label:"allowEmpty"` - }{}, + expected: expected{ + element: &struct { + Foo *struct { + Fii string + Fuu int + } `label:"allowEmpty"` + }{}, }, }, { @@ -657,18 +673,22 @@ func TestFill(t *testing.T) { Children: []*Node{ {Name: "name1", Value: "hii", Kind: reflect.String}, {Name: "name2", Value: "huu", Kind: reflect.String}, - }}, - }}, + }, + }, + }, + }, element: &struct { Foo map[string]string }{}, - expected: expected{element: &struct { - Foo map[string]string - }{ - Foo: map[string]string{ - "name1": "hii", - "name2": "huu", - }}, + expected: expected{ + element: &struct { + Foo map[string]string + }{ + Foo: map[string]string{ + "name1": "hii", + "name2": "huu", + }, + }, }, }, { @@ -696,18 +716,22 @@ func TestFill(t *testing.T) { {Name: "Fii", FieldName: "Fii", Kind: reflect.String, Value: "huu"}, }, }, - }}, - }}, + }, + }, + }, + }, element: &struct { Foo map[string]struct{ Fii string } }{}, - expected: expected{element: &struct { - Foo map[string]struct{ Fii string } - }{ - Foo: map[string]struct{ Fii string }{ - "name1": {Fii: "hii"}, - "name2": {Fii: "huu"}, - }}, + expected: expected{ + element: &struct { + Foo map[string]struct{ Fii string } + }{ + Foo: map[string]struct{ Fii string }{ + "name1": {Fii: "hii"}, + "name2": {Fii: "huu"}, + }, + }, }, }, { @@ -1186,8 +1210,10 @@ func TestFill(t *testing.T) { Kind: reflect.Struct, Children: []*Node{ {Name: "Fuu", FieldName: "Fuu", Value: "huu", Kind: reflect.String}, - }}, - }}, + }, + }, + }, + }, element: &struct { Foo *InitializedFoo }{}, @@ -1212,8 +1238,10 @@ func TestFill(t *testing.T) { Kind: reflect.Struct, Children: []*Node{ {Name: "Fuu", FieldName: "Fuu", Value: "huu", Kind: reflect.String}, - }}, - }}, + }, + }, + }, + }, element: &struct { Foo *wrongInitialledFoo }{}, @@ -1273,8 +1301,10 @@ func TestFill(t *testing.T) { Kind: reflect.Struct, Children: []*Node{ {Name: "Fuu", FieldName: "Fuu", Value: "huu", Kind: reflect.String}, - }}, - }}, + }, + }, + }, + }, element: &struct { Foo struct { FiiFoo @@ -1401,8 +1431,10 @@ func TestFill(t *testing.T) { } } -type NamedType string -type NamedTypeInt int +type ( + NamedType string + NamedTypeInt int +) type InitializedFoo struct { Fii string diff --git a/pkg/config/parser/element_nodes_test.go b/pkg/config/parser/element_nodes_test.go index c90063f3a..f809f46e3 100644 --- a/pkg/config/parser/element_nodes_test.go +++ b/pkg/config/parser/element_nodes_test.go @@ -23,9 +23,10 @@ func TestEncodeToNode(t *testing.T) { element: struct { Foo string `description:"text"` }{Foo: "bar"}, - expected: expected{node: &Node{Name: "traefik", Children: []*Node{ - {Name: "Foo", FieldName: "Foo", Value: "bar", Description: "text"}, - }}, + expected: expected{ + node: &Node{Name: "traefik", Children: []*Node{ + {Name: "Foo", FieldName: "Foo", Value: "bar", Description: "text"}, + }}, }, }, { @@ -33,9 +34,10 @@ func TestEncodeToNode(t *testing.T) { element: struct { Foo string }{Foo: "bar"}, - expected: expected{node: &Node{Name: "traefik", Children: []*Node{ - {Name: "Foo", FieldName: "Foo", Value: "bar"}, - }}, + expected: expected{ + node: &Node{Name: "traefik", Children: []*Node{ + {Name: "Foo", FieldName: "Foo", Value: "bar"}, + }}, }, }, { @@ -44,10 +46,11 @@ func TestEncodeToNode(t *testing.T) { Foo string Fii string }{Foo: "bar", Fii: "hii"}, - expected: expected{node: &Node{Name: "traefik", Children: []*Node{ - {Name: "Foo", FieldName: "Foo", Value: "bar"}, - {Name: "Fii", FieldName: "Fii", Value: "hii"}, - }}, + expected: expected{ + node: &Node{Name: "traefik", Children: []*Node{ + {Name: "Foo", FieldName: "Foo", Value: "bar"}, + {Name: "Fii", FieldName: "Fii", Value: "hii"}, + }}, }, }, { @@ -55,9 +58,10 @@ func TestEncodeToNode(t *testing.T) { element: struct { Foo int }{Foo: 1}, - expected: expected{node: &Node{Name: "traefik", Children: []*Node{ - {Name: "Foo", FieldName: "Foo", Value: "1"}, - }}, + expected: expected{ + node: &Node{Name: "traefik", Children: []*Node{ + {Name: "Foo", FieldName: "Foo", Value: "1"}, + }}, }, }, { @@ -65,9 +69,10 @@ func TestEncodeToNode(t *testing.T) { element: struct { Foo int8 }{Foo: 2}, - expected: expected{node: &Node{Name: "traefik", Children: []*Node{ - {Name: "Foo", FieldName: "Foo", Value: "2"}, - }}, + expected: expected{ + node: &Node{Name: "traefik", Children: []*Node{ + {Name: "Foo", FieldName: "Foo", Value: "2"}, + }}, }, }, { @@ -75,9 +80,10 @@ func TestEncodeToNode(t *testing.T) { element: struct { Foo int16 }{Foo: 2}, - expected: expected{node: &Node{Name: "traefik", Children: []*Node{ - {Name: "Foo", FieldName: "Foo", Value: "2"}, - }}, + expected: expected{ + node: &Node{Name: "traefik", Children: []*Node{ + {Name: "Foo", FieldName: "Foo", Value: "2"}, + }}, }, }, { @@ -85,9 +91,10 @@ func TestEncodeToNode(t *testing.T) { element: struct { Foo int32 }{Foo: 2}, - expected: expected{node: &Node{Name: "traefik", Children: []*Node{ - {Name: "Foo", FieldName: "Foo", Value: "2"}, - }}, + expected: expected{ + node: &Node{Name: "traefik", Children: []*Node{ + {Name: "Foo", FieldName: "Foo", Value: "2"}, + }}, }, }, { @@ -95,9 +102,10 @@ func TestEncodeToNode(t *testing.T) { element: struct { Foo int64 }{Foo: 2}, - expected: expected{node: &Node{Name: "traefik", Children: []*Node{ - {Name: "Foo", FieldName: "Foo", Value: "2"}, - }}, + expected: expected{ + node: &Node{Name: "traefik", Children: []*Node{ + {Name: "Foo", FieldName: "Foo", Value: "2"}, + }}, }, }, { @@ -105,9 +113,10 @@ func TestEncodeToNode(t *testing.T) { element: struct { Foo uint }{Foo: 1}, - expected: expected{node: &Node{Name: "traefik", Children: []*Node{ - {Name: "Foo", FieldName: "Foo", Value: "1"}, - }}, + expected: expected{ + node: &Node{Name: "traefik", Children: []*Node{ + {Name: "Foo", FieldName: "Foo", Value: "1"}, + }}, }, }, { @@ -115,9 +124,10 @@ func TestEncodeToNode(t *testing.T) { element: struct { Foo uint8 }{Foo: 2}, - expected: expected{node: &Node{Name: "traefik", Children: []*Node{ - {Name: "Foo", FieldName: "Foo", Value: "2"}, - }}, + expected: expected{ + node: &Node{Name: "traefik", Children: []*Node{ + {Name: "Foo", FieldName: "Foo", Value: "2"}, + }}, }, }, { @@ -125,9 +135,10 @@ func TestEncodeToNode(t *testing.T) { element: struct { Foo uint16 }{Foo: 2}, - expected: expected{node: &Node{Name: "traefik", Children: []*Node{ - {Name: "Foo", FieldName: "Foo", Value: "2"}, - }}, + expected: expected{ + node: &Node{Name: "traefik", Children: []*Node{ + {Name: "Foo", FieldName: "Foo", Value: "2"}, + }}, }, }, { @@ -135,9 +146,10 @@ func TestEncodeToNode(t *testing.T) { element: struct { Foo uint32 }{Foo: 2}, - expected: expected{node: &Node{Name: "traefik", Children: []*Node{ - {Name: "Foo", FieldName: "Foo", Value: "2"}, - }}, + expected: expected{ + node: &Node{Name: "traefik", Children: []*Node{ + {Name: "Foo", FieldName: "Foo", Value: "2"}, + }}, }, }, { @@ -145,9 +157,10 @@ func TestEncodeToNode(t *testing.T) { element: struct { Foo uint64 }{Foo: 2}, - expected: expected{node: &Node{Name: "traefik", Children: []*Node{ - {Name: "Foo", FieldName: "Foo", Value: "2"}, - }}, + expected: expected{ + node: &Node{Name: "traefik", Children: []*Node{ + {Name: "Foo", FieldName: "Foo", Value: "2"}, + }}, }, }, { @@ -155,9 +168,10 @@ func TestEncodeToNode(t *testing.T) { element: struct { Foo float32 }{Foo: 1.12}, - expected: expected{node: &Node{Name: "traefik", Children: []*Node{ - {Name: "Foo", FieldName: "Foo", Value: "1.120000"}, - }}, + expected: expected{ + node: &Node{Name: "traefik", Children: []*Node{ + {Name: "Foo", FieldName: "Foo", Value: "1.120000"}, + }}, }, }, { @@ -165,9 +179,10 @@ func TestEncodeToNode(t *testing.T) { element: struct { Foo float64 }{Foo: 1.12}, - expected: expected{node: &Node{Name: "traefik", Children: []*Node{ - {Name: "Foo", FieldName: "Foo", Value: "1.120000"}, - }}, + expected: expected{ + node: &Node{Name: "traefik", Children: []*Node{ + {Name: "Foo", FieldName: "Foo", Value: "1.120000"}, + }}, }, }, { @@ -175,9 +190,10 @@ func TestEncodeToNode(t *testing.T) { element: struct { Foo bool }{Foo: true}, - expected: expected{node: &Node{Name: "traefik", Children: []*Node{ - {Name: "Foo", FieldName: "Foo", Value: "true"}, - }}, + expected: expected{ + node: &Node{Name: "traefik", Children: []*Node{ + {Name: "Foo", FieldName: "Foo", Value: "true"}, + }}, }, }, { @@ -196,12 +212,13 @@ func TestEncodeToNode(t *testing.T) { Fuu: "huu", }, }, - expected: expected{node: &Node{Name: "traefik", Children: []*Node{ - {Name: "Foo", FieldName: "Foo", Children: []*Node{ - {Name: "Fii", FieldName: "Fii", Value: "hii"}, - {Name: "Fuu", FieldName: "Fuu", Value: "huu"}, + expected: expected{ + node: &Node{Name: "traefik", Children: []*Node{ + {Name: "Foo", FieldName: "Foo", Children: []*Node{ + {Name: "Fii", FieldName: "Fii", Value: "hii"}, + {Name: "Fuu", FieldName: "Fuu", Value: "huu"}, + }}, }}, - }}, }, }, { @@ -220,11 +237,12 @@ func TestEncodeToNode(t *testing.T) { fuu: "huu", }, }, - expected: expected{node: &Node{Name: "traefik", Children: []*Node{ - {Name: "Foo", FieldName: "Foo", Children: []*Node{ - {Name: "Fii", FieldName: "Fii", Value: "hii"}, + expected: expected{ + node: &Node{Name: "traefik", Children: []*Node{ + {Name: "Foo", FieldName: "Foo", Children: []*Node{ + {Name: "Fii", FieldName: "Fii", Value: "hii"}, + }}, }}, - }}, }, }, { @@ -243,12 +261,13 @@ func TestEncodeToNode(t *testing.T) { Fuu: "huu", }, }, - expected: expected{node: &Node{Name: "traefik", Children: []*Node{ - {Name: "Foo", FieldName: "Foo", Children: []*Node{ - {Name: "Fii", FieldName: "Fii", Value: "hii"}, - {Name: "Fuu", FieldName: "Fuu", Value: "huu"}, + expected: expected{ + node: &Node{Name: "traefik", Children: []*Node{ + {Name: "Foo", FieldName: "Foo", Children: []*Node{ + {Name: "Fii", FieldName: "Fii", Value: "hii"}, + {Name: "Fuu", FieldName: "Fuu", Value: "huu"}, + }}, }}, - }}, }, }, { @@ -267,12 +286,13 @@ func TestEncodeToNode(t *testing.T) { Fuu: "huu", }, }, - expected: expected{node: &Node{Name: "traefik", Children: []*Node{ - {Name: "Foo", FieldName: "Foo", Children: []*Node{ - {Name: "Fii", FieldName: "Fii", Value: "hii"}, - {Name: "Fuu", FieldName: "Fuu", Value: "huu"}, + expected: expected{ + node: &Node{Name: "traefik", Children: []*Node{ + {Name: "Foo", FieldName: "Foo", Children: []*Node{ + {Name: "Fii", FieldName: "Fii", Value: "hii"}, + {Name: "Fuu", FieldName: "Fuu", Value: "huu"}, + }}, }}, - }}, }, }, { @@ -291,11 +311,12 @@ func TestEncodeToNode(t *testing.T) { Fuu: "huu", }, }, - expected: expected{node: &Node{Name: "traefik", Children: []*Node{ - {Name: "Foo", FieldName: "Foo", Children: []*Node{ - {Name: "Fuu", FieldName: "Fuu", Value: "huu"}, + expected: expected{ + node: &Node{Name: "traefik", Children: []*Node{ + {Name: "Foo", FieldName: "Foo", Children: []*Node{ + {Name: "Fuu", FieldName: "Fuu", Value: "huu"}, + }}, }}, - }}, }, }, { @@ -314,12 +335,13 @@ func TestEncodeToNode(t *testing.T) { Fuu: 4, }, }, - expected: expected{node: &Node{Name: "traefik", Children: []*Node{ - {Name: "Foo", FieldName: "Foo", Children: []*Node{ - {Name: "Fii", FieldName: "Fii", Value: "6"}, - {Name: "Fuu", FieldName: "Fuu", Value: "4"}, + expected: expected{ + node: &Node{Name: "traefik", Children: []*Node{ + {Name: "Foo", FieldName: "Foo", Children: []*Node{ + {Name: "Fii", FieldName: "Fii", Value: "6"}, + {Name: "Fuu", FieldName: "Fuu", Value: "4"}, + }}, }}, - }}, }, }, { @@ -338,12 +360,13 @@ func TestEncodeToNode(t *testing.T) { Fuu: true, }, }, - expected: expected{node: &Node{Name: "traefik", Children: []*Node{ - {Name: "Foo", FieldName: "Foo", Children: []*Node{ - {Name: "Fii", FieldName: "Fii", Value: "true"}, - {Name: "Fuu", FieldName: "Fuu", Value: "true"}, + expected: expected{ + node: &Node{Name: "traefik", Children: []*Node{ + {Name: "Foo", FieldName: "Foo", Children: []*Node{ + {Name: "Fii", FieldName: "Fii", Value: "true"}, + {Name: "Fuu", FieldName: "Fuu", Value: "true"}, + }}, }}, - }}, }, }, { @@ -386,9 +409,10 @@ func TestEncodeToNode(t *testing.T) { Fuu string }{}, }, - expected: expected{node: &Node{Name: "traefik", Children: []*Node{ - {Name: "Foo", FieldName: "Foo", Value: "true"}, - }}, + expected: expected{ + node: &Node{Name: "traefik", Children: []*Node{ + {Name: "Foo", FieldName: "Foo", Value: "true"}, + }}, }, }, { @@ -452,113 +476,127 @@ func TestEncodeToNode(t *testing.T) { { desc: "slice of string", element: struct{ Bar []string }{Bar: []string{"huu", "hii"}}, - expected: expected{node: &Node{Name: "traefik", Children: []*Node{ - {Name: "Bar", FieldName: "Bar", Value: "huu, hii"}, - }}, + expected: expected{ + node: &Node{Name: "traefik", Children: []*Node{ + {Name: "Bar", FieldName: "Bar", Value: "huu, hii"}, + }}, }, }, { desc: "slice of int", element: struct{ Bar []int }{Bar: []int{4, 2, 3}}, - expected: expected{node: &Node{Name: "traefik", Children: []*Node{ - {Name: "Bar", FieldName: "Bar", Value: "4, 2, 3"}, - }}, + expected: expected{ + node: &Node{Name: "traefik", Children: []*Node{ + {Name: "Bar", FieldName: "Bar", Value: "4, 2, 3"}, + }}, }, }, { desc: "slice of int8", element: struct{ Bar []int8 }{Bar: []int8{4, 2, 3}}, - expected: expected{node: &Node{Name: "traefik", Children: []*Node{ - {Name: "Bar", FieldName: "Bar", Value: "4, 2, 3"}, - }}, + expected: expected{ + node: &Node{Name: "traefik", Children: []*Node{ + {Name: "Bar", FieldName: "Bar", Value: "4, 2, 3"}, + }}, }, }, { desc: "slice of int16", element: struct{ Bar []int16 }{Bar: []int16{4, 2, 3}}, - expected: expected{node: &Node{Name: "traefik", Children: []*Node{ - {Name: "Bar", FieldName: "Bar", Value: "4, 2, 3"}, - }}, + expected: expected{ + node: &Node{Name: "traefik", Children: []*Node{ + {Name: "Bar", FieldName: "Bar", Value: "4, 2, 3"}, + }}, }, }, { desc: "slice of int32", element: struct{ Bar []int32 }{Bar: []int32{4, 2, 3}}, - expected: expected{node: &Node{Name: "traefik", Children: []*Node{ - {Name: "Bar", FieldName: "Bar", Value: "4, 2, 3"}, - }}, + expected: expected{ + node: &Node{Name: "traefik", Children: []*Node{ + {Name: "Bar", FieldName: "Bar", Value: "4, 2, 3"}, + }}, }, }, { desc: "slice of int64", element: struct{ Bar []int64 }{Bar: []int64{4, 2, 3}}, - expected: expected{node: &Node{Name: "traefik", Children: []*Node{ - {Name: "Bar", FieldName: "Bar", Value: "4, 2, 3"}, - }}, + expected: expected{ + node: &Node{Name: "traefik", Children: []*Node{ + {Name: "Bar", FieldName: "Bar", Value: "4, 2, 3"}, + }}, }, }, { desc: "slice of uint", element: struct{ Bar []uint }{Bar: []uint{4, 2, 3}}, - expected: expected{node: &Node{Name: "traefik", Children: []*Node{ - {Name: "Bar", FieldName: "Bar", Value: "4, 2, 3"}, - }}, + expected: expected{ + node: &Node{Name: "traefik", Children: []*Node{ + {Name: "Bar", FieldName: "Bar", Value: "4, 2, 3"}, + }}, }, }, { desc: "slice of uint8", element: struct{ Bar []uint8 }{Bar: []uint8{4, 2, 3}}, - expected: expected{node: &Node{Name: "traefik", Children: []*Node{ - {Name: "Bar", FieldName: "Bar", Value: "4, 2, 3"}, - }}, + expected: expected{ + node: &Node{Name: "traefik", Children: []*Node{ + {Name: "Bar", FieldName: "Bar", Value: "4, 2, 3"}, + }}, }, }, { desc: "slice of uint16", element: struct{ Bar []uint16 }{Bar: []uint16{4, 2, 3}}, - expected: expected{node: &Node{Name: "traefik", Children: []*Node{ - {Name: "Bar", FieldName: "Bar", Value: "4, 2, 3"}, - }}, + expected: expected{ + node: &Node{Name: "traefik", Children: []*Node{ + {Name: "Bar", FieldName: "Bar", Value: "4, 2, 3"}, + }}, }, }, { desc: "slice of uint32", element: struct{ Bar []uint32 }{Bar: []uint32{4, 2, 3}}, - expected: expected{node: &Node{Name: "traefik", Children: []*Node{ - {Name: "Bar", FieldName: "Bar", Value: "4, 2, 3"}, - }}, + expected: expected{ + node: &Node{Name: "traefik", Children: []*Node{ + {Name: "Bar", FieldName: "Bar", Value: "4, 2, 3"}, + }}, }, }, { desc: "slice of uint64", element: struct{ Bar []uint64 }{Bar: []uint64{4, 2, 3}}, - expected: expected{node: &Node{Name: "traefik", Children: []*Node{ - {Name: "Bar", FieldName: "Bar", Value: "4, 2, 3"}, - }}, + expected: expected{ + node: &Node{Name: "traefik", Children: []*Node{ + {Name: "Bar", FieldName: "Bar", Value: "4, 2, 3"}, + }}, }, }, { desc: "slice of float32", element: struct{ Bar []float32 }{Bar: []float32{4.1, 2, 3.2}}, - expected: expected{node: &Node{Name: "traefik", Children: []*Node{ - {Name: "Bar", FieldName: "Bar", Value: "4.100000, 2.000000, 3.200000"}, - }}, + expected: expected{ + node: &Node{Name: "traefik", Children: []*Node{ + {Name: "Bar", FieldName: "Bar", Value: "4.100000, 2.000000, 3.200000"}, + }}, }, }, { desc: "slice of float64", element: struct{ Bar []float64 }{Bar: []float64{4.1, 2, 3.2}}, - expected: expected{node: &Node{Name: "traefik", Children: []*Node{ - {Name: "Bar", FieldName: "Bar", Value: "4.100000, 2.000000, 3.200000"}, - }}, + expected: expected{ + node: &Node{Name: "traefik", Children: []*Node{ + {Name: "Bar", FieldName: "Bar", Value: "4.100000, 2.000000, 3.200000"}, + }}, }, }, { desc: "slice of bool", element: struct{ Bar []bool }{Bar: []bool{true, false, true}}, - expected: expected{node: &Node{Name: "traefik", Children: []*Node{ - {Name: "Bar", FieldName: "Bar", Value: "true, false, true"}, - }}, + expected: expected{ + node: &Node{Name: "traefik", Children: []*Node{ + {Name: "Bar", FieldName: "Bar", Value: "true, false, true"}, + }}, }, }, { @@ -708,12 +746,13 @@ func TestEncodeToNode(t *testing.T) { }, }, }, - expected: expected{node: &Node{Name: "traefik", Children: []*Node{ - {Name: "Foo", FieldName: "Foo", Children: []*Node{ - {Name: "Fii", FieldName: "Fii", Value: "hii"}, - {Name: "Fuu", FieldName: "Fuu", Value: "huu"}, + expected: expected{ + node: &Node{Name: "traefik", Children: []*Node{ + {Name: "Foo", FieldName: "Foo", Children: []*Node{ + {Name: "Fii", FieldName: "Fii", Value: "hii"}, + {Name: "Fuu", FieldName: "Fuu", Value: "huu"}, + }}, }}, - }}, }, }, } diff --git a/pkg/config/parser/nodes_metadata_test.go b/pkg/config/parser/nodes_metadata_test.go index c3ab1aa5f..419b9eafe 100644 --- a/pkg/config/parser/nodes_metadata_test.go +++ b/pkg/config/parser/nodes_metadata_test.go @@ -682,7 +682,8 @@ func TestAddMetadata(t *testing.T) { Children: []*Node{ {Name: "Bar", FieldName: "Bar", Value: "bir", Kind: reflect.String}, {Name: "Bur", FieldName: "Bur", Value: "fuu", Kind: reflect.String}, - }}, + }, + }, }, }, }, @@ -728,8 +729,10 @@ func TestAddMetadata(t *testing.T) { Kind: reflect.Struct, Children: []*Node{ {Name: "Bur", FieldName: "Bur", Value: "fuu", Kind: reflect.String}, - }}, - }}, + }, + }, + }, + }, }, }, }, @@ -797,22 +800,26 @@ func TestAddMetadata(t *testing.T) { Children: []*Node{ {Name: "Fii", FieldName: "Fii", Kind: reflect.String, Value: "fii"}, {Name: "Fee", FieldName: "Fee", Kind: reflect.Int, Value: "1"}, - }}, + }, + }, { Name: "Bur", FieldName: "Bur", Kind: reflect.Struct, Children: []*Node{ {Name: "Faa", FieldName: "Faa", Kind: reflect.String, Value: "faa"}, - }}, - }}, + }, + }, + }, + }, { Name: "Fii", FieldName: "Fii", Kind: reflect.Struct, Children: []*Node{ {Name: "FiiBar", FieldName: "FiiBar", Kind: reflect.String, Value: "fiiBar"}, - }}, + }, + }, }, }, }, diff --git a/pkg/config/runtime/runtime_http.go b/pkg/config/runtime/runtime_http.go index bfb48820e..a43ee4483 100644 --- a/pkg/config/runtime/runtime_http.go +++ b/pkg/config/runtime/runtime_http.go @@ -168,7 +168,7 @@ func (s *ServiceInfo) AddError(err error, critical bool) { // UpdateServerStatus sets the status of the server in the ServiceInfo. // It is the responsibility of the caller to check that s is not nil. -func (s *ServiceInfo) UpdateServerStatus(server string, status string) { +func (s *ServiceInfo) UpdateServerStatus(server, status string) { s.serverStatusMu.Lock() defer s.serverStatusMu.Unlock() diff --git a/pkg/config/static/static_config.go b/pkg/config/static/static_config.go index 91f5e68c6..31a0dfb50 100644 --- a/pkg/config/static/static_config.go +++ b/pkg/config/static/static_config.go @@ -79,6 +79,7 @@ type CertificateResolver struct { type Global struct { CheckNewVersion bool `description:"Periodically check if a new version has been released." json:"checkNewVersion,omitempty" toml:"checkNewVersion,omitempty" yaml:"checkNewVersion,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` SendAnonymousUsage bool `description:"Periodically send anonymous usage statistics. If the option is not specified, it will be enabled by default." json:"sendAnonymousUsage,omitempty" toml:"sendAnonymousUsage,omitempty" yaml:"sendAnonymousUsage,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` + InsecureSNI bool `description:"Allow domain fronting. If the option is not specified, it will be disabled by default." json:"insecureSNI,omitempty" toml:"insecureSNI,omitempty" yaml:"insecureSNI,omitempty" label:"allowEmpty" export:"true"` } // ServersTransport options to configure communication between Traefik and the servers. diff --git a/pkg/healthcheck/healthcheck.go b/pkg/healthcheck/healthcheck.go index 738cd40b2..fec055128 100644 --- a/pkg/healthcheck/healthcheck.go +++ b/pkg/healthcheck/healthcheck.go @@ -22,8 +22,10 @@ const ( serverDown = "DOWN" ) -var singleton *HealthCheck -var once sync.Once +var ( + singleton *HealthCheck + once sync.Once +) // Balancer is the set of operations required to manage the list of servers in a load-balancer. type Balancer interface { diff --git a/pkg/healthcheck/healthcheck_test.go b/pkg/healthcheck/healthcheck_test.go index e9f6092ad..a1fe3f265 100644 --- a/pkg/healthcheck/healthcheck_test.go +++ b/pkg/healthcheck/healthcheck_test.go @@ -16,8 +16,10 @@ import ( "github.com/vulcand/oxy/roundrobin" ) -const healthCheckInterval = 200 * time.Millisecond -const healthCheckTimeout = 100 * time.Millisecond +const ( + healthCheckInterval = 200 * time.Millisecond + healthCheckTimeout = 100 * time.Millisecond +) type testHandler struct { done func() @@ -148,7 +150,7 @@ func TestSetBackendsConfiguration(t *testing.T) { assert.Equal(t, test.expectedNumRemovedServers, lb.numRemovedServers, "removed servers") assert.Equal(t, test.expectedNumUpsertedServers, lb.numUpsertedServers, "upserted servers") // FIXME re add metrics - //assert.Equal(t, test.expectedGaugeValue, collectingMetrics.Gauge.GaugeValue, "ServerUp Gauge") + // assert.Equal(t, test.expectedGaugeValue, collectingMetrics.Gauge.GaugeValue, "ServerUp Gauge") }) } } diff --git a/pkg/job/job.go b/pkg/job/job.go index faa440e03..752f7b905 100644 --- a/pkg/job/job.go +++ b/pkg/job/job.go @@ -6,9 +6,7 @@ import ( "github.com/cenkalti/backoff/v4" ) -var ( - _ backoff.BackOff = (*BackOff)(nil) -) +var _ backoff.BackOff = (*BackOff)(nil) const ( defaultMinJobInterval = 30 * time.Second diff --git a/pkg/job/job_test.go b/pkg/job/job_test.go index 8be9a7d4b..eaa3f8cf5 100644 --- a/pkg/job/job_test.go +++ b/pkg/job/job_test.go @@ -24,21 +24,21 @@ func TestJobBackOff(t *testing.T) { exp.MinJobInterval = testMinJobInterval exp.Reset() - var expectedResults = []time.Duration{500, 500, 500, 1000, 2000, 4000, 5000, 5000, 500, 1000, 2000, 4000, 5000, 5000} + expectedResults := []time.Duration{500, 500, 500, 1000, 2000, 4000, 5000, 5000, 500, 1000, 2000, 4000, 5000, 5000} for i, d := range expectedResults { expectedResults[i] = d * time.Millisecond } for i, expected := range expectedResults { // Assert that the next backoff falls in the expected range. - var minInterval = expected - time.Duration(testRandomizationFactor*float64(expected)) - var maxInterval = expected + time.Duration(testRandomizationFactor*float64(expected)) + minInterval := expected - time.Duration(testRandomizationFactor*float64(expected)) + maxInterval := expected + time.Duration(testRandomizationFactor*float64(expected)) if i < 3 || i == 8 { time.Sleep(2 * time.Second) } - var actualInterval = exp.NextBackOff() + actualInterval := exp.NextBackOff() if !(minInterval <= actualInterval && actualInterval <= maxInterval) { t.Error("error") } diff --git a/pkg/log/log.go b/pkg/log/log.go index eb45c0050..dddb80a99 100644 --- a/pkg/log/log.go +++ b/pkg/log/log.go @@ -101,7 +101,7 @@ func OpenFile(path string) error { logFilePath = path var err error - logFile, err = os.OpenFile(logFilePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) + logFile, err = os.OpenFile(logFilePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0o666) if err != nil { return err } diff --git a/pkg/metrics/statsd.go b/pkg/metrics/statsd.go index b0fd36839..4e16b27c7 100644 --- a/pkg/metrics/statsd.go +++ b/pkg/metrics/statsd.go @@ -11,8 +11,10 @@ import ( "github.com/go-kit/kit/metrics/statsd" ) -var statsdClient *statsd.Statsd -var statsdTicker *time.Ticker +var ( + statsdClient *statsd.Statsd + statsdTicker *time.Ticker +) const ( statsdMetricsServiceReqsName = "service.request.total" diff --git a/pkg/middlewares/accesslog/capture_response_writer.go b/pkg/middlewares/accesslog/capture_response_writer.go index dd22cd7d0..6c5dd2b3f 100644 --- a/pkg/middlewares/accesslog/capture_response_writer.go +++ b/pkg/middlewares/accesslog/capture_response_writer.go @@ -9,9 +9,7 @@ import ( "github.com/containous/traefik/v2/pkg/middlewares" ) -var ( - _ middlewares.Stateful = &captureResponseWriterWithCloseNotify{} -) +var _ middlewares.Stateful = &captureResponseWriterWithCloseNotify{} type capturer interface { http.ResponseWriter diff --git a/pkg/middlewares/accesslog/field_middleware.go b/pkg/middlewares/accesslog/field_middleware.go index 7a2aa9944..c65dcc052 100644 --- a/pkg/middlewares/accesslog/field_middleware.go +++ b/pkg/middlewares/accesslog/field_middleware.go @@ -19,7 +19,7 @@ type FieldHandler struct { } // NewFieldHandler creates a Field handler. -func NewFieldHandler(next http.Handler, name string, value string, applyFn FieldApply) http.Handler { +func NewFieldHandler(next http.Handler, name, value string, applyFn FieldApply) http.Handler { return &FieldHandler{next: next, name: name, value: value, applyFn: applyFn} } diff --git a/pkg/middlewares/accesslog/logger.go b/pkg/middlewares/accesslog/logger.go index dbe17e1c6..c876efcb3 100644 --- a/pkg/middlewares/accesslog/logger.go +++ b/pkg/middlewares/accesslog/logger.go @@ -131,11 +131,11 @@ func NewHandler(config *types.AccessLog) (*Handler, error) { func openAccessLogFile(filePath string) (*os.File, error) { dir := filepath.Dir(filePath) - if err := os.MkdirAll(dir, 0755); err != nil { + if err := os.MkdirAll(dir, 0o755); err != nil { return nil, fmt.Errorf("failed to create log path %s: %w", dir, err) } - file, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0664) + file, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0o664) if err != nil { return nil, fmt.Errorf("error opening file %s: %w", filePath, err) } @@ -249,7 +249,7 @@ func (h *Handler) Rotate() error { } var err error - h.file, err = os.OpenFile(h.config.FilePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0664) + h.file, err = os.OpenFile(h.config.FilePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0o664) if err != nil { return err } @@ -259,7 +259,7 @@ func (h *Handler) Rotate() error { return nil } -func silentSplitHostPort(value string) (host string, port string) { +func silentSplitHostPort(value string) (host, port string) { host, port, err := net.SplitHostPort(value) if err != nil { return value, "-" diff --git a/pkg/middlewares/accesslog/logger_formatters.go b/pkg/middlewares/accesslog/logger_formatters.go index 4c559a648..5bda8eef9 100644 --- a/pkg/middlewares/accesslog/logger_formatters.go +++ b/pkg/middlewares/accesslog/logger_formatters.go @@ -21,7 +21,7 @@ type CommonLogFormatter struct{} func (f *CommonLogFormatter) Format(entry *logrus.Entry) ([]byte, error) { b := &bytes.Buffer{} - var timestamp = defaultValue + timestamp := defaultValue if v, ok := entry.Data[StartUTC]; ok { timestamp = v.(time.Time).Format(commonLogTimeFormat) } else if v, ok := entry.Data[StartLocal]; ok { @@ -52,7 +52,7 @@ func (f *CommonLogFormatter) Format(entry *logrus.Entry) ([]byte, error) { return b.Bytes(), err } -func toLog(fields logrus.Fields, key string, defaultValue string, quoted bool) interface{} { +func toLog(fields logrus.Fields, key, defaultValue string, quoted bool) interface{} { if v, ok := fields[key]; ok { if v == nil { return defaultValue @@ -73,7 +73,7 @@ func toLog(fields logrus.Fields, key string, defaultValue string, quoted bool) i return defaultValue } -func toLogEntry(s string, defaultValue string, quote bool) string { +func toLogEntry(s, defaultValue string, quote bool) string { if len(s) == 0 { return defaultValue } diff --git a/pkg/middlewares/auth/basic_auth.go b/pkg/middlewares/auth/basic_auth.go index e03fb3f6a..196f3bd30 100644 --- a/pkg/middlewares/auth/basic_auth.go +++ b/pkg/middlewares/auth/basic_auth.go @@ -62,29 +62,39 @@ func (b *basicAuth) GetTracingInformation() (string, ext.SpanKindEnum) { func (b *basicAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) { logger := log.FromContext(middlewares.GetLoggerCtx(req.Context(), b.name, basicTypeName)) - if username := b.auth.CheckAuth(req); username == "" { + user, password, ok := req.BasicAuth() + if ok { + secret := b.auth.Secrets(user, b.auth.Realm) + if secret == "" || !goauth.CheckSecret(password, secret) { + ok = false + } + } + + logData := accesslog.GetLogData(req) + if logData != nil { + logData.Core[accesslog.ClientUsername] = user + } + + if !ok { logger.Debug("Authentication failed") tracing.SetErrorWithEvent(req, "Authentication failed") + b.auth.RequireAuth(rw, req) - } else { - logger.Debug("Authentication succeeded") - req.URL.User = url.User(username) - - logData := accesslog.GetLogData(req) - if logData != nil { - logData.Core[accesslog.ClientUsername] = username - } - - if b.headerField != "" { - req.Header[b.headerField] = []string{username} - } - - if b.removeHeader { - logger.Debug("Removing authorization header") - req.Header.Del(authorizationHeader) - } - b.next.ServeHTTP(rw, req) + return } + + logger.Debug("Authentication succeeded") + req.URL.User = url.User(user) + + if b.headerField != "" { + req.Header[b.headerField] = []string{user} + } + + if b.removeHeader { + logger.Debug("Removing authorization header") + req.Header.Del(authorizationHeader) + } + b.next.ServeHTTP(rw, req) } func (b *basicAuth) secretBasic(user, realm string) string { diff --git a/pkg/middlewares/auth/digest_auth.go b/pkg/middlewares/auth/digest_auth.go index 70d1306ef..f16189469 100644 --- a/pkg/middlewares/auth/digest_auth.go +++ b/pkg/middlewares/auth/digest_auth.go @@ -63,6 +63,19 @@ func (d *digestAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) { username, authinfo := d.auth.CheckAuth(req) if username == "" { + headerField := d.headerField + if d.headerField == "" { + headerField = "Authorization" + } + + auth := goauth.DigestAuthParams(req.Header.Get(headerField)) + if auth["username"] != "" { + logData := accesslog.GetLogData(req) + if logData != nil { + logData.Core[accesslog.ClientUsername] = auth["username"] + } + } + if authinfo != nil && *authinfo == "stale" { logger.Debug("Digest authentication failed, possibly because out of order requests") tracing.SetErrorWithEvent(req, "Digest authentication failed, possibly because out of order requests") diff --git a/pkg/middlewares/auth/forward.go b/pkg/middlewares/auth/forward.go index a39abbea4..9b410db2b 100644 --- a/pkg/middlewares/auth/forward.go +++ b/pkg/middlewares/auth/forward.go @@ -158,7 +158,7 @@ func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) { fa.next.ServeHTTP(rw, req) } -func writeHeader(req *http.Request, forwardReq *http.Request, trustForwardHeader bool) { +func writeHeader(req, forwardReq *http.Request, trustForwardHeader bool) { utils.CopyHeaders(forwardReq.Header, req.Header) utils.RemoveHeaders(forwardReq.Header, forward.HopHeaders...) diff --git a/pkg/middlewares/auth/forward_test.go b/pkg/middlewares/auth/forward_test.go index 707e4f4ed..7674a1384 100644 --- a/pkg/middlewares/auth/forward_test.go +++ b/pkg/middlewares/auth/forward_test.go @@ -325,7 +325,8 @@ func Test_writeHeader(t *testing.T) { "X-Forwarded-Host": "foo.bar", "X-Forwarded-Uri": "/path?q=1", }, - }, { + }, + { name: "trust Forward Header with forwarded request Method", headers: map[string]string{ "X-Forwarded-Method": "OPTIONS", diff --git a/pkg/middlewares/forwardedheaders/forwarded_header_test.go b/pkg/middlewares/forwardedheaders/forwarded_header_test.go index 96e36442b..8fc290f63 100644 --- a/pkg/middlewares/forwardedheaders/forwarded_header_test.go +++ b/pkg/middlewares/forwardedheaders/forwarded_header_test.go @@ -246,7 +246,8 @@ func TestServeHTTP(t *testing.T) { expectedHeaders: map[string]string{ xForwardedHost: "foo.com:8080", }, - }, { + }, + { desc: "xForwardedServer from req XForwarded", host: "foo.com:8080", expectedHeaders: map[string]string{ diff --git a/pkg/middlewares/headers/headers.go b/pkg/middlewares/headers/headers.go index 83529da43..81d410a94 100644 --- a/pkg/middlewares/headers/headers.go +++ b/pkg/middlewares/headers/headers.go @@ -54,13 +54,13 @@ func New(ctx context.Context, next http.Handler, cfg dynamic.Headers, name strin nextHandler := next if hasSecureHeaders { - logger.Debug("Setting up secureHeaders from %v", cfg) + logger.Debugf("Setting up secureHeaders from %v", cfg) handler = newSecure(next, cfg, name) nextHandler = handler } if hasCustomHeaders || hasCorsHeaders { - logger.Debug("Setting up customHeaders/Cors from %v", cfg) + logger.Debugf("Setting up customHeaders/Cors from %v", cfg) handler = NewHeader(nextHandler, cfg) } diff --git a/pkg/middlewares/headers/headers_test.go b/pkg/middlewares/headers/headers_test.go index 18a7d2389..2482bb233 100644 --- a/pkg/middlewares/headers/headers_test.go +++ b/pkg/middlewares/headers/headers_test.go @@ -552,7 +552,8 @@ func TestCORSResponses(t *testing.T) { expected: map[string][]string{ "Access-Control-Allow-Origin": {"*"}, }, - }, { + }, + { desc: "Test Simple CustomRequestHeaders Not Hijacked by CORS", header: NewHeader(emptyHandler, dynamic.Headers{ CustomRequestHeaders: map[string]string{"foo": "bar"}, diff --git a/pkg/middlewares/middleware.go b/pkg/middlewares/middleware.go index c340151cf..1ab3db379 100644 --- a/pkg/middlewares/middleware.go +++ b/pkg/middlewares/middleware.go @@ -7,6 +7,6 @@ import ( ) // GetLoggerCtx creates a logger context with the middleware fields. -func GetLoggerCtx(ctx context.Context, middleware string, middlewareType string) context.Context { +func GetLoggerCtx(ctx context.Context, middleware, middlewareType string) context.Context { return log.With(ctx, log.Str(log.MiddlewareName, middleware), log.Str(log.MiddlewareType, middlewareType)) } diff --git a/pkg/middlewares/passtlsclientcert/pass_tls_client_cert.go b/pkg/middlewares/passtlsclientcert/pass_tls_client_cert.go index 40c9bc186..da4c9d972 100644 --- a/pkg/middlewares/passtlsclientcert/pass_tls_client_cert.go +++ b/pkg/middlewares/passtlsclientcert/pass_tls_client_cert.go @@ -235,7 +235,7 @@ func writeParts(ctx context.Context, content io.StringWriter, entries []string, } } -func writePart(ctx context.Context, content io.StringWriter, entry string, prefix string) { +func writePart(ctx context.Context, content io.StringWriter, entry, prefix string) { if len(entry) > 0 { _, err := content.WriteString(fmt.Sprintf("%s=%s%s", prefix, entry, subFieldSeparator)) if err != nil { diff --git a/pkg/middlewares/redirect/redirect.go b/pkg/middlewares/redirect/redirect.go index 9224a3252..e4449b93b 100644 --- a/pkg/middlewares/redirect/redirect.go +++ b/pkg/middlewares/redirect/redirect.go @@ -24,7 +24,7 @@ type redirect struct { } // New creates a Redirect middleware. -func newRedirect(next http.Handler, regex string, replacement string, permanent bool, name string) (http.Handler, error) { +func newRedirect(next http.Handler, regex, replacement string, permanent bool, name string) (http.Handler, error) { re, err := regexp.Compile(regex) if err != nil { return nil, err @@ -115,7 +115,7 @@ func rawURL(req *http.Request) string { port := "" uri := req.RequestURI - schemeRegex := `^(https?):\/\/([\w\._-]+)(:\d+)?(.*)$` + schemeRegex := `^(https?):\/\/(\[[\w:.]+\]|[\w\._-]+)?(:\d+)?(.*)$` re, _ := regexp.Compile(schemeRegex) if re.Match([]byte(req.RequestURI)) { match := re.FindStringSubmatch(req.RequestURI) diff --git a/pkg/middlewares/redirect/redirect_scheme.go b/pkg/middlewares/redirect/redirect_scheme.go index 92523c70e..749c3f594 100644 --- a/pkg/middlewares/redirect/redirect_scheme.go +++ b/pkg/middlewares/redirect/redirect_scheme.go @@ -12,7 +12,7 @@ import ( const ( typeSchemeName = "RedirectScheme" - schemeRedirectRegex = `^(https?:\/\/)?([\w\._-]+)(:\d+)?(.*)$` + schemeRedirectRegex = `^(https?:\/\/)?(\[[\w:.]+\]|[\w\._-]+)?(:\d+)?(.*)$` ) // NewRedirectScheme creates a new RedirectScheme middleware. diff --git a/pkg/middlewares/redirect/redirect_scheme_test.go b/pkg/middlewares/redirect/redirect_scheme_test.go index b0694cd57..e1e6fbacb 100644 --- a/pkg/middlewares/redirect/redirect_scheme_test.go +++ b/pkg/middlewares/redirect/redirect_scheme_test.go @@ -186,6 +186,44 @@ func TestRedirectSchemeHandler(t *testing.T) { expectedURL: "https://foo", expectedStatus: http.StatusFound, }, + { + desc: "IPV6 HTTP to HTTPS redirection without port", + config: dynamic.RedirectScheme{ + Scheme: "https", + }, + url: "http://[::1]", + expectedURL: "https://[::1]", + expectedStatus: http.StatusFound, + }, + { + desc: "IPV6 HTTP to HTTPS redirection with port", + config: dynamic.RedirectScheme{ + Scheme: "https", + Port: "8443", + }, + url: "http://[::1]", + expectedURL: "https://[::1]:8443", + expectedStatus: http.StatusFound, + }, + { + desc: "IPV6 HTTP with port 80 to HTTPS redirection without port", + config: dynamic.RedirectScheme{ + Scheme: "https", + }, + url: "http://[::1]:80", + expectedURL: "https://[::1]", + expectedStatus: http.StatusFound, + }, + { + desc: "IPV6 HTTP with port 80 to HTTPS redirection with port", + config: dynamic.RedirectScheme{ + Scheme: "https", + Port: "8443", + }, + url: "http://[::1]:80", + expectedURL: "https://[::1]:8443", + expectedStatus: http.StatusFound, + }, } for _, test := range testCases { @@ -235,7 +273,7 @@ func TestRedirectSchemeHandler(t *testing.T) { require.Errorf(t, err, "Location %v", location) } - schemeRegex := `^(https?):\/\/([\w\._-]+)(:\d+)?(.*)$` + schemeRegex := `^(https?):\/\/(\[[\w:.]+\]|[\w\._-]+)?(:\d+)?(.*)$` re, _ := regexp.Compile(schemeRegex) if re.Match([]byte(test.url)) { diff --git a/pkg/middlewares/requestdecorator/hostresolver.go b/pkg/middlewares/requestdecorator/hostresolver.go index 91d2e7238..f79a50c3c 100644 --- a/pkg/middlewares/requestdecorator/hostresolver.go +++ b/pkg/middlewares/requestdecorator/hostresolver.go @@ -47,7 +47,7 @@ func (hr *Resolver) CNAMEFlatten(ctx context.Context, host string) string { } logger := log.FromContext(ctx) - var cacheDuration = 0 * time.Second + cacheDuration := 0 * time.Second for depth := 0; depth < hr.ResolvDepth; depth++ { resolv, err := cnameResolve(ctx, request, hr.ResolvConfig) if err != nil { @@ -73,7 +73,7 @@ func (hr *Resolver) CNAMEFlatten(ctx context.Context, host string) string { } // cnameResolve resolves CNAME if exists, and return with the highest TTL. -func cnameResolve(ctx context.Context, host string, resolvPath string) (*cnameResolv, error) { +func cnameResolve(ctx context.Context, host, resolvPath string) (*cnameResolv, error) { config, err := dns.ClientConfigFromFile(resolvPath) if err != nil { return nil, fmt.Errorf("invalid resolver configuration file: %s", resolvPath) @@ -102,7 +102,7 @@ func cnameResolve(ctx context.Context, host string, resolvPath string) (*cnameRe return result[0], nil } -func getRecord(client *dns.Client, msg *dns.Msg, server string, port string) (*cnameResolv, error) { +func getRecord(client *dns.Client, msg *dns.Msg, server, port string) (*cnameResolv, error) { resp, _, err := client.Exchange(msg, net.JoinHostPort(server, port)) if err != nil { return nil, fmt.Errorf("exchange error for server %s: %w", server, err) diff --git a/pkg/middlewares/tracing/forwarder_test.go b/pkg/middlewares/tracing/forwarder_test.go index 073df8ad4..f5872b649 100644 --- a/pkg/middlewares/tracing/forwarder_test.go +++ b/pkg/middlewares/tracing/forwarder_test.go @@ -45,7 +45,8 @@ func TestNewForwarder(t *testing.T) { }, OperationName: "forward some-service.domain.tld/some-service.domain.tld", }, - }, { + }, + { desc: "Simple Forward Tracer with truncation and hashing", spanNameLimit: 101, tracing: &trackingBackenMock{ diff --git a/pkg/middlewares/tracing/mock_tracing_test.go b/pkg/middlewares/tracing/mock_tracing_test.go index f07e8921d..af469ee52 100644 --- a/pkg/middlewares/tracing/mock_tracing_test.go +++ b/pkg/middlewares/tracing/mock_tracing_test.go @@ -18,12 +18,12 @@ func (n MockTracer) StartSpan(operationName string, opts ...opentracing.StartSpa } // Inject belongs to the Tracer interface. -func (n MockTracer) Inject(sp opentracing.SpanContext, format interface{}, carrier interface{}) error { +func (n MockTracer) Inject(sp opentracing.SpanContext, format, carrier interface{}) error { return nil } // Extract belongs to the Tracer interface. -func (n MockTracer) Extract(format interface{}, carrier interface{}) (opentracing.SpanContext, error) { +func (n MockTracer) Extract(format, carrier interface{}) (opentracing.SpanContext, error) { return nil, opentracing.ErrSpanContextNotFound } diff --git a/pkg/provider/acme/account.go b/pkg/provider/acme/account.go index 40b2c05a3..bf6fb654a 100644 --- a/pkg/provider/acme/account.go +++ b/pkg/provider/acme/account.go @@ -26,7 +26,7 @@ const ( ) // NewAccount creates an account. -func NewAccount(ctx context.Context, email string, keyTypeValue string) (*Account, error) { +func NewAccount(ctx context.Context, email, keyTypeValue string) (*Account, error) { keyType := GetKeyType(ctx, keyTypeValue) // Create a user. New accounts need an email and private key to start diff --git a/pkg/provider/acme/local_store.go b/pkg/provider/acme/local_store.go index f92bd0810..8edad959d 100644 --- a/pkg/provider/acme/local_store.go +++ b/pkg/provider/acme/local_store.go @@ -103,7 +103,7 @@ func (s *LocalStore) listenSaveAction() { logger.Error(err) } - err = ioutil.WriteFile(s.filename, data, 0600) + err = ioutil.WriteFile(s.filename, data, 0o600) if err != nil { logger.Error(err) } diff --git a/pkg/provider/acme/local_store_unix.go b/pkg/provider/acme/local_store_unix.go index edcd200a3..35c3d24bd 100644 --- a/pkg/provider/acme/local_store_unix.go +++ b/pkg/provider/acme/local_store_unix.go @@ -16,7 +16,7 @@ func CheckFile(name string) (bool, error) { if err != nil { return false, err } - return false, f.Chmod(0600) + return false, f.Chmod(0o600) } return false, err } @@ -27,7 +27,7 @@ func CheckFile(name string) (bool, error) { return false, err } - if fi.Mode().Perm()&0077 != 0 { + if fi.Mode().Perm()&0o077 != 0 { return false, fmt.Errorf("permissions %o for %s are too open, please use 600", fi.Mode().Perm(), name) } diff --git a/pkg/provider/acme/local_store_windows.go b/pkg/provider/acme/local_store_windows.go index 1804578a3..747618eb8 100644 --- a/pkg/provider/acme/local_store_windows.go +++ b/pkg/provider/acme/local_store_windows.go @@ -12,7 +12,7 @@ func CheckFile(name string) (bool, error) { if err != nil { return false, err } - return false, f.Chmod(0600) + return false, f.Chmod(0o600) } return false, err } diff --git a/pkg/provider/acme/provider.go b/pkg/provider/acme/provider.go index 14309375f..d95cbd0c0 100644 --- a/pkg/provider/acme/provider.go +++ b/pkg/provider/acme/provider.go @@ -27,10 +27,8 @@ import ( "github.com/go-acme/lego/v3/registration" ) -var ( - // oscpMustStaple enables OSCP stapling as from https://github.com/go-acme/lego/issues/270. - oscpMustStaple = false -) +// oscpMustStaple enables OSCP stapling as from https://github.com/go-acme/lego/issues/270. +var oscpMustStaple = false // Configuration holds ACME configuration provided by users. type Configuration struct { @@ -145,7 +143,7 @@ func (p *Provider) Init() error { return nil } -func isAccountMatchingCaServer(ctx context.Context, accountURI string, serverURI string) bool { +func isAccountMatchingCaServer(ctx context.Context, accountURI, serverURI string) bool { logger := log.FromContext(ctx) aru, err := url.Parse(accountURI) @@ -428,13 +426,11 @@ func (p *Provider) resolveCertificate(ctx context.Context, domain types.Domain, return nil, err } - // Check provided certificates + // Check if provided certificates are not already in progress and lock them if needed uncheckedDomains := p.getUncheckedDomains(ctx, domains, tlsStore) if len(uncheckedDomains) == 0 { return nil, nil } - - p.addResolvingDomains(uncheckedDomains) defer p.removeResolvingDomains(uncheckedDomains) logger := log.FromContext(ctx) @@ -483,16 +479,7 @@ func (p *Provider) removeResolvingDomains(resolvingDomains []string) { } } -func (p *Provider) addResolvingDomains(resolvingDomains []string) { - p.resolvingDomainsMutex.Lock() - defer p.resolvingDomainsMutex.Unlock() - - for _, domain := range resolvingDomains { - p.resolvingDomains[domain] = struct{}{} - } -} - -func (p *Provider) addCertificateForDomain(domain types.Domain, certificate []byte, key []byte, tlsStore string) { +func (p *Provider) addCertificateForDomain(domain types.Domain, certificate, key []byte, tlsStore string) { p.certsChan <- &CertAndStore{Certificate: Certificate{Certificate: certificate, Key: key, Domain: domain}, Store: tlsStore} } @@ -640,7 +627,6 @@ func (p *Provider) renewCertificates(ctx context.Context) { PrivateKey: cert.Key, Certificate: cert.Certificate.Certificate, }, true, oscpMustStaple) - if err != nil { logger.Errorf("Error renewing certificate from LE: %v, %v", cert.Domain, err) continue @@ -659,8 +645,8 @@ func (p *Provider) renewCertificates(ctx context.Context) { // Get provided certificate which check a domains list (Main and SANs) // from static and dynamic provided certificates. func (p *Provider) getUncheckedDomains(ctx context.Context, domainsToCheck []string, tlsStore string) []string { - p.resolvingDomainsMutex.RLock() - defer p.resolvingDomainsMutex.RUnlock() + p.resolvingDomainsMutex.Lock() + defer p.resolvingDomainsMutex.Unlock() log.FromContext(ctx).Debugf("Looking for provided certificate(s) to validate %q...", domainsToCheck) @@ -676,10 +662,17 @@ func (p *Provider) getUncheckedDomains(ctx context.Context, domainsToCheck []str allDomains = append(allDomains, domain) } - return searchUncheckedDomains(ctx, domainsToCheck, allDomains) + uncheckedDomains := searchUncheckedDomains(ctx, domainsToCheck, allDomains) + + // Lock domains that will be resolved by this routine + for _, domain := range uncheckedDomains { + p.resolvingDomains[domain] = struct{}{} + } + + return uncheckedDomains } -func searchUncheckedDomains(ctx context.Context, domainsToCheck []string, existentDomains []string) []string { +func searchUncheckedDomains(ctx context.Context, domainsToCheck, existentDomains []string) []string { var uncheckedDomains []string for _, domainToCheck := range domainsToCheck { if !isDomainAlreadyChecked(domainToCheck, existentDomains) { diff --git a/pkg/provider/docker/builder_test.go b/pkg/provider/docker/builder_test.go index ddb98b390..28b8d225c 100644 --- a/pkg/provider/docker/builder_test.go +++ b/pkg/provider/docker/builder_test.go @@ -95,7 +95,7 @@ func taskSlot(slot int) func(*swarm.Task) { } } -func taskNetworkAttachment(id string, name string, driver string, addresses []string) func(*swarm.Task) { +func taskNetworkAttachment(id, name, driver string, addresses []string) func(*swarm.Task) { return func(task *swarm.Task) { task.NetworksAttachments = append(task.NetworksAttachments, swarm.NetworkAttachment{ Network: swarm.Network{ diff --git a/pkg/provider/docker/config.go b/pkg/provider/docker/config.go index 1f35e7897..3fb4e3f7c 100644 --- a/pkg/provider/docker/config.go +++ b/pkg/provider/docker/config.go @@ -350,7 +350,6 @@ func (p Provider) getIPAddress(ctx context.Context, container dockerData) string // the network specified on the current container. containerParsed := parseContainer(containerInspected) extraConf, err := p.getConfiguration(containerParsed) - if err != nil { logger.Warnf("Unable to get IP address for container %s : failed to get extra configuration for container %s: %s", container.Name, containerInspected.Name, err) return "" diff --git a/pkg/provider/docker/label.go b/pkg/provider/docker/label.go index 34b044803..c83b90f72 100644 --- a/pkg/provider/docker/label.go +++ b/pkg/provider/docker/label.go @@ -55,7 +55,7 @@ func getStringMultipleStrict(labels map[string]string, labelNames ...string) (ma } // getStringValue get string value associated to a label. -func getStringValue(labels map[string]string, labelName string, defaultValue string) string { +func getStringValue(labels map[string]string, labelName, defaultValue string) string { if value, ok := labels[labelName]; ok && len(value) > 0 { return value } diff --git a/pkg/provider/file/file.go b/pkg/provider/file/file.go index e7e33bd75..35bd0c75c 100644 --- a/pkg/provider/file/file.go +++ b/pkg/provider/file/file.go @@ -48,7 +48,6 @@ func (p *Provider) Init() error { // using the given configuration channel. func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.Pool) error { configuration, err := p.BuildConfiguration() - if err != nil { return err } @@ -356,7 +355,7 @@ func (p *Provider) CreateConfiguration(ctx context.Context, filename string, fun return nil, fmt.Errorf("error reading configuration file: %s - %w", filename, err) } - var defaultFuncMap = sprig.TxtFuncMap() + defaultFuncMap := sprig.TxtFuncMap() defaultFuncMap["normalize"] = provider.Normalize defaultFuncMap["split"] = strings.Split for funcID, funcElement := range funcMap { @@ -376,7 +375,7 @@ func (p *Provider) CreateConfiguration(ctx context.Context, filename string, fun return nil, err } - var renderedTemplate = buffer.String() + renderedTemplate := buffer.String() if p.DebugLogGeneratedTemplate { logger := log.FromContext(ctx) logger.Debugf("Template content: %s", tmplContent) @@ -396,7 +395,7 @@ func (p *Provider) DecodeConfiguration(filename string) (*dynamic.Configuration, return p.decodeConfiguration(filename, content) } -func (p *Provider) decodeConfiguration(filePath string, content string) (*dynamic.Configuration, error) { +func (p *Provider) decodeConfiguration(filePath, content string) (*dynamic.Configuration, error) { configuration := &dynamic.Configuration{ HTTP: &dynamic.HTTPConfiguration{ Routers: make(map[string]*dynamic.Router), diff --git a/pkg/provider/file/file_test.go b/pkg/provider/file/file_test.go index 7581564f4..7ec7e3494 100644 --- a/pkg/provider/file/file_test.go +++ b/pkg/provider/file/file_test.go @@ -293,7 +293,7 @@ func createTempDir(t *testing.T, dir string) string { } func copyFile(srcPath, dstPath string) error { - dst, err := os.OpenFile(dstPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666) + dst, err := os.OpenFile(dstPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o666) if err != nil { return err } diff --git a/pkg/provider/kubernetes/crd/fixtures/tcp/with_bad_host_rule.yml b/pkg/provider/kubernetes/crd/fixtures/tcp/with_bad_host_rule.yml deleted file mode 100644 index 084da9bf7..000000000 --- a/pkg/provider/kubernetes/crd/fixtures/tcp/with_bad_host_rule.yml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: traefik.containo.us/v1alpha1 -kind: IngressRouteTCP -metadata: - name: test.route - namespace: default - -spec: - entryPoints: - - web - - routes: - - match: HostSNI(`foo.com"0"`) - services: - - name: whoamitcp - port: 8000 diff --git a/pkg/provider/kubernetes/crd/fixtures/with_bad_host_rule.yml b/pkg/provider/kubernetes/crd/fixtures/with_bad_host_rule.yml deleted file mode 100644 index 2a722c8f6..000000000 --- a/pkg/provider/kubernetes/crd/fixtures/with_bad_host_rule.yml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: traefik.containo.us/v1alpha1 -kind: IngressRoute -metadata: - name: test.route - namespace: default - -spec: - entryPoints: - - web - - routes: - - match: Host(`foo.com"0"`) && PathPrefix(`/bar`) - kind: Rule - priority: 12 - services: - - name: whoami - port: 80 diff --git a/pkg/provider/kubernetes/crd/generated/clientset/versioned/fake/register.go b/pkg/provider/kubernetes/crd/generated/clientset/versioned/fake/register.go index 05425a4a2..18d2c5ea6 100644 --- a/pkg/provider/kubernetes/crd/generated/clientset/versioned/fake/register.go +++ b/pkg/provider/kubernetes/crd/generated/clientset/versioned/fake/register.go @@ -35,12 +35,14 @@ import ( utilruntime "k8s.io/apimachinery/pkg/util/runtime" ) -var scheme = runtime.NewScheme() -var codecs = serializer.NewCodecFactory(scheme) -var parameterCodec = runtime.NewParameterCodec(scheme) -var localSchemeBuilder = runtime.SchemeBuilder{ - traefikv1alpha1.AddToScheme, -} +var ( + scheme = runtime.NewScheme() + codecs = serializer.NewCodecFactory(scheme) + parameterCodec = runtime.NewParameterCodec(scheme) + localSchemeBuilder = runtime.SchemeBuilder{ + traefikv1alpha1.AddToScheme, + } +) // AddToScheme adds all types of this clientset into the given scheme. This allows composition // of clientsets, like in: diff --git a/pkg/provider/kubernetes/crd/generated/clientset/versioned/scheme/register.go b/pkg/provider/kubernetes/crd/generated/clientset/versioned/scheme/register.go index 154438378..3452dc7c3 100644 --- a/pkg/provider/kubernetes/crd/generated/clientset/versioned/scheme/register.go +++ b/pkg/provider/kubernetes/crd/generated/clientset/versioned/scheme/register.go @@ -35,12 +35,14 @@ import ( utilruntime "k8s.io/apimachinery/pkg/util/runtime" ) -var Scheme = runtime.NewScheme() -var Codecs = serializer.NewCodecFactory(Scheme) -var ParameterCodec = runtime.NewParameterCodec(Scheme) -var localSchemeBuilder = runtime.SchemeBuilder{ - traefikv1alpha1.AddToScheme, -} +var ( + Scheme = runtime.NewScheme() + Codecs = serializer.NewCodecFactory(Scheme) + ParameterCodec = runtime.NewParameterCodec(Scheme) + localSchemeBuilder = runtime.SchemeBuilder{ + traefikv1alpha1.AddToScheme, + } +) // AddToScheme adds all types of this clientset into the given scheme. This allows composition // of clientsets, like in: diff --git a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefik/v1alpha1/fake/fake_ingressroute.go b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefik/v1alpha1/fake/fake_ingressroute.go index 7ebc6b1a7..9b59b3913 100644 --- a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefik/v1alpha1/fake/fake_ingressroute.go +++ b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefik/v1alpha1/fake/fake_ingressroute.go @@ -85,7 +85,6 @@ func (c *FakeIngressRoutes) List(ctx context.Context, opts v1.ListOptions) (resu func (c *FakeIngressRoutes) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { return c.Fake. InvokesWatch(testing.NewWatchAction(ingressroutesResource, c.ns, opts)) - } // Create takes the representation of a ingressRoute and creates it. Returns the server's representation of the ingressRoute, and an error, if there is any. diff --git a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefik/v1alpha1/fake/fake_ingressroutetcp.go b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefik/v1alpha1/fake/fake_ingressroutetcp.go index 3866e10c3..423819c4c 100644 --- a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefik/v1alpha1/fake/fake_ingressroutetcp.go +++ b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefik/v1alpha1/fake/fake_ingressroutetcp.go @@ -85,7 +85,6 @@ func (c *FakeIngressRouteTCPs) List(ctx context.Context, opts v1.ListOptions) (r func (c *FakeIngressRouteTCPs) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { return c.Fake. InvokesWatch(testing.NewWatchAction(ingressroutetcpsResource, c.ns, opts)) - } // Create takes the representation of a ingressRouteTCP and creates it. Returns the server's representation of the ingressRouteTCP, and an error, if there is any. diff --git a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefik/v1alpha1/fake/fake_ingressrouteudp.go b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefik/v1alpha1/fake/fake_ingressrouteudp.go index 61517973d..7eb8fd1d7 100644 --- a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefik/v1alpha1/fake/fake_ingressrouteudp.go +++ b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefik/v1alpha1/fake/fake_ingressrouteudp.go @@ -85,7 +85,6 @@ func (c *FakeIngressRouteUDPs) List(ctx context.Context, opts v1.ListOptions) (r func (c *FakeIngressRouteUDPs) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { return c.Fake. InvokesWatch(testing.NewWatchAction(ingressrouteudpsResource, c.ns, opts)) - } // Create takes the representation of a ingressRouteUDP and creates it. Returns the server's representation of the ingressRouteUDP, and an error, if there is any. diff --git a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefik/v1alpha1/fake/fake_middleware.go b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefik/v1alpha1/fake/fake_middleware.go index fd0ab0481..82bb6a8b3 100644 --- a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefik/v1alpha1/fake/fake_middleware.go +++ b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefik/v1alpha1/fake/fake_middleware.go @@ -85,7 +85,6 @@ func (c *FakeMiddlewares) List(ctx context.Context, opts v1.ListOptions) (result func (c *FakeMiddlewares) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { return c.Fake. InvokesWatch(testing.NewWatchAction(middlewaresResource, c.ns, opts)) - } // Create takes the representation of a middleware and creates it. Returns the server's representation of the middleware, and an error, if there is any. diff --git a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefik/v1alpha1/fake/fake_tlsoption.go b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefik/v1alpha1/fake/fake_tlsoption.go index 78bd634cb..ca523a715 100644 --- a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefik/v1alpha1/fake/fake_tlsoption.go +++ b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefik/v1alpha1/fake/fake_tlsoption.go @@ -85,7 +85,6 @@ func (c *FakeTLSOptions) List(ctx context.Context, opts v1.ListOptions) (result func (c *FakeTLSOptions) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { return c.Fake. InvokesWatch(testing.NewWatchAction(tlsoptionsResource, c.ns, opts)) - } // Create takes the representation of a tLSOption and creates it. Returns the server's representation of the tLSOption, and an error, if there is any. diff --git a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefik/v1alpha1/fake/fake_tlsstore.go b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefik/v1alpha1/fake/fake_tlsstore.go index 1a26fc934..f2d7b5e2b 100644 --- a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefik/v1alpha1/fake/fake_tlsstore.go +++ b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefik/v1alpha1/fake/fake_tlsstore.go @@ -85,7 +85,6 @@ func (c *FakeTLSStores) List(ctx context.Context, opts v1.ListOptions) (result * func (c *FakeTLSStores) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { return c.Fake. InvokesWatch(testing.NewWatchAction(tlsstoresResource, c.ns, opts)) - } // Create takes the representation of a tLSStore and creates it. Returns the server's representation of the tLSStore, and an error, if there is any. diff --git a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefik/v1alpha1/fake/fake_traefikservice.go b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefik/v1alpha1/fake/fake_traefikservice.go index deaecc27a..b9d8d3842 100644 --- a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefik/v1alpha1/fake/fake_traefikservice.go +++ b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefik/v1alpha1/fake/fake_traefikservice.go @@ -85,7 +85,6 @@ func (c *FakeTraefikServices) List(ctx context.Context, opts v1.ListOptions) (re func (c *FakeTraefikServices) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { return c.Fake. InvokesWatch(testing.NewWatchAction(traefikservicesResource, c.ns, opts)) - } // Create takes the representation of a traefikService and creates it. Returns the server's representation of the traefikService, and an error, if there is any. diff --git a/pkg/provider/kubernetes/crd/kubernetes.go b/pkg/provider/kubernetes/crd/kubernetes.go index ddd7f4a98..21ce74ea0 100644 --- a/pkg/provider/kubernetes/crd/kubernetes.go +++ b/pkg/provider/kubernetes/crd/kubernetes.go @@ -9,7 +9,6 @@ import ( "fmt" "os" "sort" - "strconv" "strings" "time" @@ -102,7 +101,6 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe. pool.GoCtx(func(ctxPool context.Context) { operation := func() error { eventsChan, err := k8sClient.WatchAll(p.Namespaces, ctxPool.Done()) - if err != nil { logger.Errorf("Error watching kubernetes events: %v", err) timer := time.NewTimer(1 * time.Second) @@ -609,11 +607,6 @@ func buildTLSStores(ctx context.Context, client Client) map[string]tls.Store { return tlsStores } -func checkStringQuoteValidity(value string) error { - _, err := strconv.Unquote(`"` + value + `"`) - return err -} - func makeServiceKey(rule, ingressName string) (string, error) { h := sha256.New() if _, err := h.Write([]byte(rule)); err != nil { @@ -633,7 +626,7 @@ func makeID(namespace, name string) string { return namespace + "-" + name } -func shouldProcessIngress(ingressClass string, ingressClassAnnotation string) bool { +func shouldProcessIngress(ingressClass, ingressClassAnnotation string) bool { return ingressClass == ingressClassAnnotation || (len(ingressClass) == 0 && ingressClassAnnotation == traefikDefaultIngressClass) } diff --git a/pkg/provider/kubernetes/crd/kubernetes_http.go b/pkg/provider/kubernetes/crd/kubernetes_http.go index 776e7e4fb..26d024dc3 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_http.go +++ b/pkg/provider/kubernetes/crd/kubernetes_http.go @@ -58,11 +58,6 @@ func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Cli continue } - if err := checkStringQuoteValidity(route.Match); err != nil { - logger.Errorf("Invalid syntax for match rule: %s", route.Match) - continue - } - serviceKey, err := makeServiceKey(route.Match, ingressName) if err != nil { logger.Error(err) @@ -437,7 +432,7 @@ func getTLSHTTP(ctx context.Context, ingressRoute *v1alpha1.IngressRoute, k8sCli // parseServiceProtocol parses the scheme, port name, and number to determine the correct protocol. // an error is returned if the scheme provided is invalid. -func parseServiceProtocol(providedScheme string, portName string, portNumber int32) (string, error) { +func parseServiceProtocol(providedScheme, portName string, portNumber int32) (string, error) { switch providedScheme { case httpProtocol, httpsProtocol, "h2c": return providedScheme, nil diff --git a/pkg/provider/kubernetes/crd/kubernetes_tcp.go b/pkg/provider/kubernetes/crd/kubernetes_tcp.go index 9229cc4ce..54f363401 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_tcp.go +++ b/pkg/provider/kubernetes/crd/kubernetes_tcp.go @@ -44,11 +44,6 @@ func (p *Provider) loadIngressRouteTCPConfiguration(ctx context.Context, client continue } - if err := checkStringQuoteValidity(route.Match); err != nil { - logger.Errorf("Invalid syntax for match rule: %s", route.Match) - continue - } - key, err := makeServiceKey(route.Match, ingressName) if err != nil { logger.Error(err) diff --git a/pkg/provider/kubernetes/crd/kubernetes_test.go b/pkg/provider/kubernetes/crd/kubernetes_test.go index 6aa982a0f..eba2ad1b3 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_test.go +++ b/pkg/provider/kubernetes/crd/kubernetes_test.go @@ -372,26 +372,6 @@ func TestLoadIngressRouteTCPs(t *testing.T) { TLS: &dynamic.TLSConfiguration{}, }, }, - { - desc: "check rule quoting validity", - paths: []string{"tcp/services.yml", "tcp/with_bad_host_rule.yml"}, - expected: &dynamic.Configuration{ - UDP: &dynamic.UDPConfiguration{ - Routers: map[string]*dynamic.UDPRouter{}, - Services: map[string]*dynamic.UDPService{}, - }, - TCP: &dynamic.TCPConfiguration{ - Routers: map[string]*dynamic.TCPRouter{}, - Services: map[string]*dynamic.TCPService{}, - }, - HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, - }, - TLS: &dynamic.TLSConfiguration{}, - }, - }, { desc: "TLS", paths: []string{"tcp/services.yml", "tcp/with_tls.yml"}, @@ -2149,26 +2129,6 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, }, - { - desc: "check rule quoting validity", - paths: []string{"services.yml", "with_bad_host_rule.yml"}, - expected: &dynamic.Configuration{ - UDP: &dynamic.UDPConfiguration{ - Routers: map[string]*dynamic.UDPRouter{}, - Services: map[string]*dynamic.UDPService{}, - }, - TLS: &dynamic.TLSConfiguration{}, - TCP: &dynamic.TCPConfiguration{ - Routers: map[string]*dynamic.TCPRouter{}, - Services: map[string]*dynamic.TCPService{}, - }, - HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, - }, - }, - }, { desc: "TLS", paths: []string{"services.yml", "with_tls.yml"}, @@ -2980,7 +2940,8 @@ func TestLoadIngressRoutes(t *testing.T) { EntryPoints: []string{"foo"}, Service: "default-test-route-6f97418635c7e18853da", Rule: "Host(`foo.com`)", - }}, + }, + }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ "default-test-route-6f97418635c7e18853da": { @@ -3016,7 +2977,8 @@ func TestLoadIngressRoutes(t *testing.T) { EntryPoints: []string{"foo"}, Service: "default-test-route-6f97418635c7e18853da", Rule: "Host(`foo.com`)", - }}, + }, + }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ "default-test-route-6f97418635c7e18853da": { @@ -3052,7 +3014,8 @@ func TestLoadIngressRoutes(t *testing.T) { EntryPoints: []string{"foo"}, Service: "default-test-route-6f97418635c7e18853da", Rule: "Host(`foo.com`)", - }}, + }, + }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ "default-test-route-6f97418635c7e18853da": { diff --git a/pkg/provider/kubernetes/crd/kubernetes_udp.go b/pkg/provider/kubernetes/crd/kubernetes_udp.go index 9ff4706ec..611000c5a 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_udp.go +++ b/pkg/provider/kubernetes/crd/kubernetes_udp.go @@ -107,6 +107,7 @@ func loadUDPServers(client Client, namespace string, svc v1alpha1.ServiceUDP) ([ var portSpec *corev1.ServicePort for _, p := range service.Spec.Ports { + p := p if svc.Port == p.Port { portSpec = &p break diff --git a/pkg/provider/kubernetes/ingress/annotations_test.go b/pkg/provider/kubernetes/ingress/annotations_test.go index 9a461a720..c62642c8d 100644 --- a/pkg/provider/kubernetes/ingress/annotations_test.go +++ b/pkg/provider/kubernetes/ingress/annotations_test.go @@ -105,7 +105,7 @@ func Test_parseServiceConfig(t *testing.T) { "traefik.ingress.kubernetes.io/foo": "bar", "traefik.ingress.kubernetes.io/service.serversscheme": "protocol", "traefik.ingress.kubernetes.io/service.passhostheader": "true", - "traefik.ingress.kubernetes.io/service.sticky": "true", + "traefik.ingress.kubernetes.io/service.sticky.cookie": "true", "traefik.ingress.kubernetes.io/service.sticky.cookie.httponly": "true", "traefik.ingress.kubernetes.io/service.sticky.cookie.name": "foobar", "traefik.ingress.kubernetes.io/service.sticky.cookie.secure": "true", @@ -129,11 +129,11 @@ func Test_parseServiceConfig(t *testing.T) { { desc: "simple sticky annotation", annotations: map[string]string{ - "traefik.ingress.kubernetes.io/service.sticky": "true", + "traefik.ingress.kubernetes.io/service.sticky.cookie": "true", }, expected: &ServiceConfig{ Service: &ServiceIng{ - Sticky: &dynamic.Sticky{}, + Sticky: &dynamic.Sticky{Cookie: &dynamic.Cookie{}}, PassHostHeader: Bool(true), }, }, @@ -206,7 +206,7 @@ func Test_convertAnnotations(t *testing.T) { annotations: map[string]string{ "traefik.ingress.kubernetes.io/service.serversscheme": "protocol", "traefik.ingress.kubernetes.io/service.passhostheader": "true", - "traefik.ingress.kubernetes.io/service.sticky": "true", + "traefik.ingress.kubernetes.io/service.sticky.cookie": "true", "traefik.ingress.kubernetes.io/service.sticky.cookie.httponly": "true", "traefik.ingress.kubernetes.io/service.sticky.cookie.name": "foobar", "traefik.ingress.kubernetes.io/service.sticky.cookie.secure": "true", @@ -214,7 +214,7 @@ func Test_convertAnnotations(t *testing.T) { expected: map[string]string{ "traefik.service.passhostheader": "true", "traefik.service.serversscheme": "protocol", - "traefik.service.sticky": "true", + "traefik.service.sticky.cookie": "true", "traefik.service.sticky.cookie.httponly": "true", "traefik.service.sticky.cookie.name": "foobar", "traefik.service.sticky.cookie.secure": "true", diff --git a/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-a-bad-host-syntax_ingress.yml b/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-a-bad-host-syntax_ingress.yml deleted file mode 100644 index 3f85b878a..000000000 --- a/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-a-bad-host-syntax_ingress.yml +++ /dev/null @@ -1,22 +0,0 @@ -kind: Ingress -apiVersion: networking.k8s.io/v1beta1 -metadata: - name: "" - namespace: testing - -spec: - rules: - - host: traefik.tchouk"0" - http: - paths: - - path: /foo - backend: - serviceName: service1 - servicePort: 80 - - host: traefik.courgette - http: - paths: - - path: /carotte - backend: - serviceName: service1 - servicePort: 80 diff --git a/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-a-bad-path-syntax_ingress.yml b/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-a-bad-path-syntax_ingress.yml deleted file mode 100644 index d50177fca..000000000 --- a/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-a-bad-path-syntax_ingress.yml +++ /dev/null @@ -1,23 +0,0 @@ -kind: Ingress -apiVersion: networking.k8s.io/v1beta1 -metadata: - name: "" - namespace: testing - -spec: - rules: - - host: "" - http: - paths: - - path: /foo - backend: - serviceName: service1 - servicePort: 80 - - path: /bar-"0" - backend: - serviceName: service1 - servicePort: 80 - - path: /bar - backend: - serviceName: service1 - servicePort: 80 diff --git a/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-only-a-bad-host-syntax_ingress.yml b/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-only-a-bad-host-syntax_ingress.yml deleted file mode 100644 index 55878efbe..000000000 --- a/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-only-a-bad-host-syntax_ingress.yml +++ /dev/null @@ -1,15 +0,0 @@ -kind: Ingress -apiVersion: networking.k8s.io/v1beta1 -metadata: - name: "" - namespace: testing - -spec: - rules: - - host: traefik.tchouk"0" - http: - paths: - - path: /foo - backend: - serviceName: service1 - servicePort: 80 diff --git a/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-only-a-bad-path-syntax_ingress.yml b/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-only-a-bad-path-syntax_ingress.yml deleted file mode 100644 index 100ff267a..000000000 --- a/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-only-a-bad-path-syntax_ingress.yml +++ /dev/null @@ -1,14 +0,0 @@ -kind: Ingress -apiVersion: networking.k8s.io/v1beta1 -metadata: - name: "" - namespace: testing - -spec: - rules: - - http: - paths: - - path: /bar-"0" - backend: - serviceName: service1 - servicePort: 80 diff --git a/pkg/provider/kubernetes/ingress/kubernetes.go b/pkg/provider/kubernetes/ingress/kubernetes.go index 149ab47e3..fa19f4909 100644 --- a/pkg/provider/kubernetes/ingress/kubernetes.go +++ b/pkg/provider/kubernetes/ingress/kubernetes.go @@ -7,7 +7,6 @@ import ( "math" "os" "sort" - "strconv" "strings" "time" @@ -235,11 +234,6 @@ func (p *Provider) loadConfigurationFromIngresses(ctx context.Context, client Cl } for _, rule := range ingress.Spec.Rules { - if err := checkStringQuoteValidity(rule.Host); err != nil { - log.FromContext(ctx).Errorf("Invalid syntax for host: %s", rule.Host) - continue - } - if err := p.updateIngressStatus(ingress, client); err != nil { log.FromContext(ctx).Errorf("Error while updating ingress status: %v", err) } @@ -249,11 +243,6 @@ func (p *Provider) loadConfigurationFromIngresses(ctx context.Context, client Cl } for _, pa := range rule.HTTP.Paths { - if err = checkStringQuoteValidity(pa.Path); err != nil { - log.FromContext(ctx).Errorf("Invalid syntax for path: %s", pa.Path) - continue - } - service, err := loadService(client, ingress.Namespace, pa.Backend) if err != nil { log.FromContext(ctx). @@ -330,7 +319,7 @@ func buildHostRule(host string) string { return "Host(`" + host + "`)" } -func shouldProcessIngress(ingressClass string, ingressClassAnnotation string) bool { +func shouldProcessIngress(ingressClass, ingressClassAnnotation string) bool { return ingressClass == ingressClassAnnotation || (len(ingressClass) == 0 && ingressClassAnnotation == traefikDefaultIngressClass) } @@ -559,11 +548,6 @@ func loadRouter(rule v1beta1.IngressRule, pa v1beta1.HTTPIngressPath, rtConfig * return rt } -func checkStringQuoteValidity(value string) error { - _, err := strconv.Unquote(`"` + value + `"`) - return err -} - func throttleEvents(ctx context.Context, throttleDuration time.Duration, pool *safe.Pool, eventsChan <-chan interface{}) chan interface{} { if throttleDuration == 0 { return nil diff --git a/pkg/provider/kubernetes/ingress/kubernetes_test.go b/pkg/provider/kubernetes/ingress/kubernetes_test.go index d3e945fd4..0ba4d2857 100644 --- a/pkg/provider/kubernetes/ingress/kubernetes_test.go +++ b/pkg/provider/kubernetes/ingress/kubernetes_test.go @@ -327,92 +327,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { }, }, }, - { - desc: "Ingress with a bad path syntax", - expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, - HTTP: &dynamic.HTTPConfiguration{ - Middlewares: map[string]*dynamic.Middleware{}, - Routers: map[string]*dynamic.Router{ - "testing-bar": { - Rule: "PathPrefix(`/bar`)", - Service: "testing-service1-80", - }, - "testing-foo": { - Rule: "PathPrefix(`/foo`)", - Service: "testing-service1-80", - }, - }, - Services: map[string]*dynamic.Service{ - "testing-service1-80": { - LoadBalancer: &dynamic.ServersLoadBalancer{ - PassHostHeader: Bool(true), - Servers: []dynamic.Server{ - { - URL: "http://10.10.0.1:8080", - }, - { - URL: "http://10.21.0.1:8080", - }, - }, - }, - }, - }, - }, - }, - }, - { - desc: "Ingress with only a bad path syntax", - expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, - HTTP: &dynamic.HTTPConfiguration{ - Middlewares: map[string]*dynamic.Middleware{}, - Routers: map[string]*dynamic.Router{}, - Services: map[string]*dynamic.Service{}, - }, - }, - }, - { - desc: "Ingress with a bad host syntax", - expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, - HTTP: &dynamic.HTTPConfiguration{ - Middlewares: map[string]*dynamic.Middleware{}, - Routers: map[string]*dynamic.Router{ - "testing-traefik-courgette-carotte": { - Rule: "Host(`traefik.courgette`) && PathPrefix(`/carotte`)", - Service: "testing-service1-80", - }, - }, - Services: map[string]*dynamic.Service{ - "testing-service1-80": { - LoadBalancer: &dynamic.ServersLoadBalancer{ - PassHostHeader: Bool(true), - Servers: []dynamic.Server{ - { - URL: "http://10.10.0.1:8080", - }, - { - URL: "http://10.21.0.1:8080", - }, - }, - }, - }, - }, - }, - }, - }, - { - desc: "Ingress with only a bad host syntax", - expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{}, - HTTP: &dynamic.HTTPConfiguration{ - Middlewares: map[string]*dynamic.Middleware{}, - Routers: map[string]*dynamic.Router{}, - Services: map[string]*dynamic.Service{}, - }, - }, - }, { desc: "Ingress with two services", expected: &dynamic.Configuration{ diff --git a/pkg/provider/marathon/mocks/Marathon.go b/pkg/provider/marathon/mocks/Marathon.go index a4cc12178..d2506bc79 100644 --- a/pkg/provider/marathon/mocks/Marathon.go +++ b/pkg/provider/marathon/mocks/Marathon.go @@ -129,7 +129,7 @@ func (_m *Marathon) ApplicationBy(name string, opts *marathon.GetAppOpts) (*mara } // ApplicationByVersion provides a mock function with given fields: name, version -func (_m *Marathon) ApplicationByVersion(name string, version string) (*marathon.Application, error) { +func (_m *Marathon) ApplicationByVersion(name, version string) (*marathon.Application, error) { ret := _m.Called(name, version) var r0 *marathon.Application @@ -394,7 +394,7 @@ func (_m *Marathon) DeletePod(name string, force bool) (*marathon.DeploymentID, } // DeletePodInstance provides a mock function with given fields: name, instance -func (_m *Marathon) DeletePodInstance(name string, instance string) (*marathon.PodInstance, error) { +func (_m *Marathon) DeletePodInstance(name, instance string) (*marathon.PodInstance, error) { ret := _m.Called(name, instance) var r0 *marathon.PodInstance @@ -583,7 +583,7 @@ func (_m *Marathon) GroupsBy(opts *marathon.GetGroupOpts) (*marathon.Groups, err } // HasApplicationVersion provides a mock function with given fields: name, version -func (_m *Marathon) HasApplicationVersion(name string, version string) (bool, error) { +func (_m *Marathon) HasApplicationVersion(name, version string) (bool, error) { ret := _m.Called(name, version) var r0 bool @@ -817,7 +817,7 @@ func (_m *Marathon) Pod(name string) (*marathon.Pod, error) { } // PodByVersion provides a mock function with given fields: name, version -func (_m *Marathon) PodByVersion(name string, version string) (*marathon.Pod, error) { +func (_m *Marathon) PodByVersion(name, version string) (*marathon.Pod, error) { ret := _m.Called(name, version) var r0 *marathon.Pod diff --git a/pkg/provider/traefik/fixtures/api_insecure_with_dashboard.json b/pkg/provider/traefik/fixtures/api_insecure_with_dashboard.json index c4a306889..992a447d1 100644 --- a/pkg/provider/traefik/fixtures/api_insecure_with_dashboard.json +++ b/pkg/provider/traefik/fixtures/api_insecure_with_dashboard.json @@ -25,7 +25,7 @@ "middlewares": { "dashboard_redirect": { "redirectRegex": { - "regex": "^(http:\\/\\/[^:\\/]+(:\\d+)?)\\/$", + "regex": "^(http:\\/\\/(\\[[\\w:.]+\\]|[\\w\\._-]+)(:\\d+)?)\\/$", "replacement": "${1}/dashboard/", "permanent": true } diff --git a/pkg/provider/traefik/fixtures/full_configuration.json b/pkg/provider/traefik/fixtures/full_configuration.json index bd08c9f0b..f09614e2e 100644 --- a/pkg/provider/traefik/fixtures/full_configuration.json +++ b/pkg/provider/traefik/fixtures/full_configuration.json @@ -57,7 +57,7 @@ "middlewares": { "dashboard_redirect": { "redirectRegex": { - "regex": "^(http:\\/\\/[^:\\/]+(:\\d+)?)\\/$", + "regex": "^(http:\\/\\/(\\[[\\w:.]+\\]|[\\w\\._-]+)(:\\d+)?)\\/$", "replacement": "${1}/dashboard/", "permanent": true } diff --git a/pkg/provider/traefik/internal.go b/pkg/provider/traefik/internal.go index 0c25420c5..a63a2f49b 100644 --- a/pkg/provider/traefik/internal.go +++ b/pkg/provider/traefik/internal.go @@ -197,7 +197,7 @@ func (i *Provider) apiConfiguration(cfg *dynamic.Configuration) { cfg.HTTP.Middlewares["dashboard_redirect"] = &dynamic.Middleware{ RedirectRegex: &dynamic.RedirectRegex{ - Regex: `^(http:\/\/[^:\/]+(:\d+)?)\/$`, + Regex: `^(http:\/\/(\[[\w:.]+\]|[\w\._-]+)(:\d+)?)\/$`, Replacement: "${1}/dashboard/", Permanent: true, }, diff --git a/pkg/provider/traefik/internal_test.go b/pkg/provider/traefik/internal_test.go index ab7c33bf8..dd6ef8c6a 100644 --- a/pkg/provider/traefik/internal_test.go +++ b/pkg/provider/traefik/internal_test.go @@ -209,7 +209,8 @@ func Test_createConfiguration(t *testing.T) { }, }, }, - }, { + }, + { desc: "redirection_port.json", staticCfg: static.Configuration{ EntryPoints: map[string]*static.EntryPoint{ @@ -248,7 +249,7 @@ func Test_createConfiguration(t *testing.T) { newJSON, err := json.MarshalIndent(cfg, "", " ") require.NoError(t, err) - err = ioutil.WriteFile(filename, newJSON, 0644) + err = ioutil.WriteFile(filename, newJSON, 0o644) require.NoError(t, err) } diff --git a/pkg/responsemodifiers/log.go b/pkg/responsemodifiers/log.go index b9ae3639f..43eac2a1b 100644 --- a/pkg/responsemodifiers/log.go +++ b/pkg/responsemodifiers/log.go @@ -8,6 +8,6 @@ import ( ) // getLogger creates a logger configured with the middleware fields. -func getLogger(ctx context.Context, middleware string, middlewareType string) logrus.FieldLogger { +func getLogger(ctx context.Context, middleware, middlewareType string) logrus.FieldLogger { return log.FromContext(ctx).WithField(log.MiddlewareName, middleware).WithField(log.MiddlewareType, middlewareType) } diff --git a/pkg/responsemodifiers/response_modifier.go b/pkg/responsemodifiers/response_modifier.go index 92b99d566..a49cb8cea 100644 --- a/pkg/responsemodifiers/response_modifier.go +++ b/pkg/responsemodifiers/response_modifier.go @@ -19,6 +19,7 @@ type Builder struct { } // Build Builds the response modifier. +// It returns nil if there is no modifier to apply. func (f *Builder) Build(ctx context.Context, names []string) func(*http.Response) error { var modifiers []func(*http.Response) error @@ -44,7 +45,10 @@ func (f *Builder) Build(ctx context.Context, names []string) func(*http.Response for _, name := range conf.Chain.Middlewares { qualifiedNames = append(qualifiedNames, provider.GetQualifiedName(chainCtx, name)) } - modifiers = append(modifiers, f.Build(ctx, qualifiedNames)) + + if rm := f.Build(ctx, qualifiedNames); rm != nil { + modifiers = append(modifiers, rm) + } } } @@ -60,5 +64,5 @@ func (f *Builder) Build(ctx context.Context, names []string) func(*http.Response } } - return func(response *http.Response) error { return nil } + return nil } diff --git a/pkg/responsemodifiers/response_modifier_test.go b/pkg/responsemodifiers/response_modifier_test.go index 8a53c92f7..f75f9dfae 100644 --- a/pkg/responsemodifiers/response_modifier_test.go +++ b/pkg/responsemodifiers/response_modifier_test.go @@ -169,6 +169,21 @@ func TestBuilderBuild(t *testing.T) { }, assertResponse: func(t *testing.T, resp *http.Response) {}, }, + + { + desc: "chain without headers", + middlewares: []string{"chain"}, + buildResponse: stubResponse, + conf: map[string]*dynamic.Middleware{ + "foo": {IPWhiteList: &dynamic.IPWhiteList{}}, + "chain": { + Chain: &dynamic.Chain{ + Middlewares: []string{"foo"}, + }, + }, + }, + assertResponse: func(t *testing.T, resp *http.Response) {}, + }, } for _, test := range testCases { @@ -184,6 +199,9 @@ func TestBuilderBuild(t *testing.T) { builder := NewBuilder(rtConf.Middlewares) rm := builder.Build(context.Background(), test.middlewares) + if rm == nil { + return + } resp := test.buildResponse(test.conf) diff --git a/pkg/rules/rules.go b/pkg/rules/rules.go index 797a669eb..04e029a15 100644 --- a/pkg/rules/rules.go +++ b/pkg/rules/rules.go @@ -12,7 +12,9 @@ import ( ) var funcs = map[string]func(*mux.Route, ...string) error{ - "Host": host, + "Host": hostSecure, + "HostHeader": host, + "HostSNI": hostSNI, "HostRegexp": hostRegexp, "Path": path, "PathPrefix": pathPrefix, @@ -22,6 +24,18 @@ var funcs = map[string]func(*mux.Route, ...string) error{ "Query": query, } +// EnableDomainFronting initialize the matcher functions to used on routers. +// InsecureSNI defines if the domain fronting is allowed. +func EnableDomainFronting(ok bool) { + if ok { + log.WithoutContext().Warn("With insecureSNI enabled, router rules do not prevent domain fronting techniques. Please use `HostHeader` and `HostSNI` rules if domain fronting is not desired.") + funcs["Host"] = host + return + } + + funcs["Host"] = hostSecure +} + // Router handle routing with rules. type Router struct { *mux.Router @@ -98,46 +112,125 @@ func host(route *mux.Route, hosts ...string) error { } route.MatcherFunc(func(req *http.Request, _ *mux.RouteMatch) bool { - reqHost := requestdecorator.GetCanonizedHost(req.Context()) - if len(reqHost) == 0 { - log.FromContext(req.Context()).Warnf("Could not retrieve CanonizedHost, rejecting %s", req.Host) - return false - } + return matchHost(req, true, hosts...) + }) + return nil +} - flatH := requestdecorator.GetCNAMEFlatten(req.Context()) - if len(flatH) > 0 { - for _, host := range hosts { - if strings.EqualFold(reqHost, host) || strings.EqualFold(flatH, host) { - return true - } - log.FromContext(req.Context()).Debugf("CNAMEFlattening: request %s which resolved to %s, is not matched to route %s", reqHost, flatH, host) - } - return false - } +func matchHost(req *http.Request, insecureSNI bool, hosts ...string) bool { + logger := log.FromContext(req.Context()) + reqHost := requestdecorator.GetCanonizedHost(req.Context()) + if len(reqHost) == 0 { + logger.Warnf("Could not retrieve CanonizedHost, rejecting %s", req.Host) + return false + } + + flatH := requestdecorator.GetCNAMEFlatten(req.Context()) + if len(flatH) > 0 { for _, host := range hosts { - if reqHost == host { + if strings.EqualFold(reqHost, host) || strings.EqualFold(flatH, host) { return true } - - // Check for match on trailing period on host - if last := len(host) - 1; last >= 0 && host[last] == '.' { - h := host[:last] - if reqHost == h { - return true - } - } - - // Check for match on trailing period on request - if last := len(reqHost) - 1; last >= 0 && reqHost[last] == '.' { - h := reqHost[:last] - if h == host { - return true - } - } + logger.Debugf("CNAMEFlattening: request %s which resolved to %s, is not matched to route %s", reqHost, flatH, host) } return false + } + + for _, host := range hosts { + if reqHost == host { + logHostSNI(insecureSNI, req, reqHost) + return true + } + + // Check for match on trailing period on host + if last := len(host) - 1; last >= 0 && host[last] == '.' { + h := host[:last] + if reqHost == h { + logHostSNI(insecureSNI, req, reqHost) + return true + } + } + + // Check for match on trailing period on request + if last := len(reqHost) - 1; last >= 0 && reqHost[last] == '.' { + h := reqHost[:last] + if h == host { + logHostSNI(insecureSNI, req, reqHost) + return true + } + } + } + return false +} + +func logHostSNI(insecureSNI bool, req *http.Request, reqHost string) { + if insecureSNI && req.TLS != nil && !strings.EqualFold(reqHost, req.TLS.ServerName) { + log.FromContext(req.Context()).Debugf("Router reached with Host(%q) different from SNI(%q)", reqHost, req.TLS.ServerName) + } +} + +func hostSNI(route *mux.Route, hosts ...string) error { + for i, host := range hosts { + hosts[i] = strings.ToLower(host) + } + + route.MatcherFunc(func(req *http.Request, _ *mux.RouteMatch) bool { + return matchSNI(req, hosts...) }) + + return nil +} + +func matchSNI(req *http.Request, hosts ...string) bool { + if req.TLS == nil { + return true + } + + if req.TLS.ServerName == "" { + return false + } + + for _, host := range hosts { + if strings.EqualFold(req.TLS.ServerName, host) { + return true + } + + // Check for match on trailing period on host + if last := len(host) - 1; last >= 0 && host[last] == '.' { + h := host[:last] + if strings.EqualFold(req.TLS.ServerName, h) { + return true + } + } + + // Check for match on trailing period on request + if last := len(req.TLS.ServerName) - 1; last >= 0 && req.TLS.ServerName[last] == '.' { + h := req.TLS.ServerName[:last] + if strings.EqualFold(h, host) { + return true + } + } + } + + return false +} + +func hostSecure(route *mux.Route, hosts ...string) error { + for i, host := range hosts { + hosts[i] = strings.ToLower(host) + } + + route.MatcherFunc(func(req *http.Request, _ *mux.RouteMatch) bool { + for _, host := range hosts { + if matchSNI(req, host) && matchHost(req, false, host) { + return true + } + } + + return false + }) + return nil } diff --git a/pkg/rules/rules_test.go b/pkg/rules/rules_test.go index 3b44a4cc9..099779e40 100644 --- a/pkg/rules/rules_test.go +++ b/pkg/rules/rules_test.go @@ -681,6 +681,18 @@ func TestParseDomains(t *testing.T) { domain: []string{"foo.bar", "test.bar"}, errorExpected: false, }, + { + description: "Many host rules upper", + expression: "HOST(`foo.bar`,`test.bar`)", + domain: []string{"foo.bar", "test.bar"}, + errorExpected: false, + }, + { + description: "Many host rules lower", + expression: "host(`foo.bar`,`test.bar`)", + domain: []string{"foo.bar", "test.bar"}, + errorExpected: false, + }, { description: "No host rule", expression: "Path(`/test`)", diff --git a/pkg/server/configurationwatcher.go b/pkg/server/configurationwatcher.go index eb3f56f67..0fcd1c204 100644 --- a/pkg/server/configurationwatcher.go +++ b/pkg/server/configurationwatcher.go @@ -224,8 +224,8 @@ func (c *ConfigurationWatcher) throttleProviderConfigReload(ctx context.Context, logger.Info("Skipping same configuration") continue } - previousConfig = nextConfig - ring.In() <- nextConfig + previousConfig = *nextConfig.DeepCopy() + ring.In() <- *nextConfig.DeepCopy() } } } diff --git a/pkg/server/configurationwatcher_test.go b/pkg/server/configurationwatcher_test.go index 78bab9769..2078cc032 100644 --- a/pkg/server/configurationwatcher_test.go +++ b/pkg/server/configurationwatcher_test.go @@ -301,3 +301,96 @@ func TestListenProvidersPublishesConfigForEachProvider(t *testing.T) { assert.Equal(t, expected, publishedProviderConfig) } + +func TestPublishConfigUpdatedByProvider(t *testing.T) { + routinesPool := safe.NewPool(context.Background()) + + pvdConfiguration := dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{ + "foo": {}, + }, + }, + } + + pvd := &mockProvider{ + wait: 10 * time.Millisecond, + messages: []dynamic.Message{ + { + ProviderName: "mock", + Configuration: &pvdConfiguration, + }, + { + ProviderName: "mock", + Configuration: &pvdConfiguration, + }, + }, + } + + watcher := NewConfigurationWatcher(routinesPool, pvd, 30*time.Millisecond, []string{}) + + publishedConfigCount := 0 + watcher.AddListener(func(configuration dynamic.Configuration) { + publishedConfigCount++ + + // Update the provider configuration published in next dynamic Message which should trigger a new publish. + pvdConfiguration.TCP.Routers["bar"] = &dynamic.TCPRouter{} + }) + + watcher.Start() + defer watcher.Stop() + + // give some time so that the configuration can be processed. + time.Sleep(100 * time.Millisecond) + + assert.Equal(t, 2, publishedConfigCount) +} + +func TestPublishConfigUpdatedByConfigWatcherListener(t *testing.T) { + routinesPool := safe.NewPool(context.Background()) + + pvd := &mockProvider{ + wait: 10 * time.Millisecond, + messages: []dynamic.Message{ + { + ProviderName: "mock", + Configuration: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{ + "foo": {}, + }, + }, + }, + }, + { + ProviderName: "mock", + Configuration: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{ + "foo": {}, + }, + }, + }, + }, + }, + } + + watcher := NewConfigurationWatcher(routinesPool, pvd, 30*time.Millisecond, []string{}) + + publishedConfigCount := 0 + watcher.AddListener(func(configuration dynamic.Configuration) { + publishedConfigCount++ + + // Modify the provided configuration. This should not modify the configuration stored in the configuration + // watcher and cause a new publish. + configuration.TCP.Routers["foo@mock"].Rule = "bar" + }) + + watcher.Start() + defer watcher.Stop() + + // give some time so that the configuration can be processed. + time.Sleep(100 * time.Millisecond) + + assert.Equal(t, 1, publishedConfigCount) +} diff --git a/pkg/server/cookie/cookie.go b/pkg/server/cookie/cookie.go index 6b95c41b5..9c85c73f4 100644 --- a/pkg/server/cookie/cookie.go +++ b/pkg/server/cookie/cookie.go @@ -11,7 +11,7 @@ import ( const cookieNameLength = 6 // GetName of a cookie. -func GetName(cookieName string, backendName string) string { +func GetName(cookieName, backendName string) string { if len(cookieName) != 0 { return sanitizeName(cookieName) } diff --git a/pkg/server/provider/provider.go b/pkg/server/provider/provider.go index e98063ce3..fdf66d7d1 100644 --- a/pkg/server/provider/provider.go +++ b/pkg/server/provider/provider.go @@ -40,6 +40,6 @@ func GetQualifiedName(ctx context.Context, elementName string) string { } // MakeQualifiedName Creates a qualified name for an element. -func MakeQualifiedName(providerName string, elementName string) string { +func MakeQualifiedName(providerName, elementName string) string { return elementName + "@" + providerName } diff --git a/pkg/server/router/router_test.go b/pkg/server/router/router_test.go index f7b17164c..294d55fdc 100644 --- a/pkg/server/router/router_test.go +++ b/pkg/server/router/router_test.go @@ -2,6 +2,7 @@ package router import ( "context" + "crypto/tls" "io/ioutil" "net/http" "net/http/httptest" @@ -14,6 +15,7 @@ import ( "github.com/containous/traefik/v2/pkg/middlewares/accesslog" "github.com/containous/traefik/v2/pkg/middlewares/requestdecorator" "github.com/containous/traefik/v2/pkg/responsemodifiers" + "github.com/containous/traefik/v2/pkg/rules" "github.com/containous/traefik/v2/pkg/server/middleware" "github.com/containous/traefik/v2/pkg/server/service" "github.com/containous/traefik/v2/pkg/testhelpers" @@ -25,6 +27,8 @@ import ( func TestRouterManager_Get(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) + t.Cleanup(func() { server.Close() }) + type expectedResult struct { StatusCode int RequestHeaders map[string]string @@ -310,9 +314,230 @@ func TestRouterManager_Get(t *testing.T) { } } +func TestRouterManager_SNI(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) + + t.Cleanup(func() { server.Close() }) + + type expectedResult struct { + StatusCode int + RequestHeaders map[string]string + } + + testCases := []struct { + desc string + routersConfig map[string]*dynamic.Router + serviceConfig map[string]*dynamic.Service + middlewaresConfig map[string]*dynamic.Middleware + entryPoint string + sni string + insecureSNI bool + expected expectedResult + }{ + { + desc: "Insecure SNI without TLS", + routersConfig: map[string]*dynamic.Router{ + "foo@provider-1": { + EntryPoints: []string{"web"}, + Service: "foo-service", + Rule: "Host(`foo.bar`)", + Priority: 0, + }, + }, + serviceConfig: map[string]*dynamic.Service{ + "foo-service@provider-1": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: server.URL, + }, + }, + }, + }, + }, + insecureSNI: true, + entryPoint: "web", + expected: expectedResult{StatusCode: http.StatusOK}, + }, + { + desc: "Secure SNI without TLS", + routersConfig: map[string]*dynamic.Router{ + "foo@provider-1": { + EntryPoints: []string{"web"}, + Service: "foo-service", + Rule: "Host(`foo.bar`)", + Priority: 0, + }, + }, + serviceConfig: map[string]*dynamic.Service{ + "foo-service@provider-1": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: server.URL, + }, + }, + }, + }, + }, + entryPoint: "web", + expected: expectedResult{StatusCode: http.StatusOK}, + }, + { + desc: "Secure SNI with TLS without sni", + routersConfig: map[string]*dynamic.Router{ + "foo@provider-1": { + EntryPoints: []string{"websecure"}, + Service: "foo-service", + Rule: "Host(`foo.bar`)", + Priority: 0, + TLS: &dynamic.RouterTLSConfig{}, + }, + }, + serviceConfig: map[string]*dynamic.Service{ + "foo-service@provider-1": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: server.URL, + }, + }, + }, + }, + }, + entryPoint: "websecure", + expected: expectedResult{StatusCode: http.StatusNotFound}, + }, + { + desc: "Secure SNI with TLS with sni request", + routersConfig: map[string]*dynamic.Router{ + "foo@provider-1": { + EntryPoints: []string{"websecure"}, + Service: "foo-service", + Rule: "Host(`foo.bar`)", + Priority: 0, + TLS: &dynamic.RouterTLSConfig{}, + }, + }, + serviceConfig: map[string]*dynamic.Service{ + "foo-service@provider-1": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: server.URL, + }, + }, + }, + }, + }, + entryPoint: "websecure", + sni: "foo.bar", + expected: expectedResult{StatusCode: http.StatusOK}, + }, + { + desc: "Insecure SNI with TLS without sni", + routersConfig: map[string]*dynamic.Router{ + "foo@provider-1": { + EntryPoints: []string{"websecure"}, + Service: "foo-service", + Rule: "Host(`foo.bar`)", + Priority: 0, + TLS: &dynamic.RouterTLSConfig{}, + }, + }, + serviceConfig: map[string]*dynamic.Service{ + "foo-service@provider-1": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: server.URL, + }, + }, + }, + }, + }, + entryPoint: "websecure", + insecureSNI: true, + expected: expectedResult{StatusCode: http.StatusOK}, + }, + { + desc: "Secure SNI with TLS with sni uppercase", + routersConfig: map[string]*dynamic.Router{ + "foo@provider-1": { + EntryPoints: []string{"websecure"}, + Service: "foo-service", + Rule: "Host(`Foo.bar`)", + Priority: 0, + TLS: &dynamic.RouterTLSConfig{}, + }, + }, + serviceConfig: map[string]*dynamic.Service{ + "foo-service@provider-1": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: server.URL, + }, + }, + }, + }, + }, + entryPoint: "websecure", + sni: "Foo.bar", + expected: expectedResult{StatusCode: http.StatusOK}, + }, + } + + for _, test := range testCases { + test := test + + t.Run(test.desc, func(t *testing.T) { + rtConf := runtime.NewConfig(dynamic.Configuration{ + HTTP: &dynamic.HTTPConfiguration{ + Services: test.serviceConfig, + Routers: test.routersConfig, + Middlewares: test.middlewaresConfig, + }, + }) + + rules.EnableDomainFronting(test.insecureSNI) + serviceManager := service.NewManager(rtConf.Services, http.DefaultTransport, nil, nil) + middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager) + responseModifierFactory := responsemodifiers.NewBuilder(rtConf.Middlewares) + chainBuilder := middleware.NewChainBuilder(static.Configuration{}, nil, nil) + + routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, responseModifierFactory, chainBuilder) + + handlers := routerManager.BuildHandlers(context.Background(), []string{test.entryPoint}, test.entryPoint == "websecure") + + w := httptest.NewRecorder() + req := testhelpers.MustNewRequest(http.MethodGet, "https://foo.bar/", nil) + + if test.entryPoint == "websecure" { + req.TLS = &tls.ConnectionState{} + + if test.sni != "" { + req.TLS.ServerName = test.sni + } + } + + reqHost := requestdecorator.New(nil) + reqHost.ServeHTTP(w, req, handlers[test.entryPoint].ServeHTTP) + + assert.Equal(t, test.expected.StatusCode, w.Code) + + for key, value := range test.expected.RequestHeaders { + assert.Equal(t, value, req.Header.Get(key)) + } + }) + } +} + func TestAccessLog(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) + t.Cleanup(func() { server.Close() }) + testCases := []struct { desc string routersConfig map[string]*dynamic.Router @@ -683,7 +908,6 @@ func TestRuntimeConfiguration(t *testing.T) { chainBuilder := middleware.NewChainBuilder(static.Configuration{}, nil, nil) routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, responseModifierFactory, chainBuilder) - _ = routerManager.BuildHandlers(context.Background(), entryPoints, false) // even though rtConf was passed by argument to the manager builders above, @@ -778,13 +1002,15 @@ type staticTransport struct { res *http.Response } -func (t *staticTransport) RoundTrip(r *http.Request) (*http.Response, error) { +func (t *staticTransport) RoundTrip(_ *http.Request) (*http.Response, error) { return t.res, nil } func BenchmarkRouterServe(b *testing.B) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) + b.Cleanup(func() { server.Close() }) + res := &http.Response{ StatusCode: 200, Body: ioutil.NopCloser(strings.NewReader("")), diff --git a/pkg/server/router/tcp/router.go b/pkg/server/router/tcp/router.go index 11a44aa88..e5cee5065 100644 --- a/pkg/server/router/tcp/router.go +++ b/pkg/server/router/tcp/router.go @@ -90,7 +90,7 @@ type nameAndConfig struct { TLSConfig *tls.Config } -func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string]*runtime.TCPRouterInfo, configsHTTP map[string]*runtime.RouterInfo, handlerHTTP http.Handler, handlerHTTPS http.Handler) (*tcp.Router, error) { +func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string]*runtime.TCPRouterInfo, configsHTTP map[string]*runtime.RouterInfo, handlerHTTP, handlerHTTPS http.Handler) (*tcp.Router, error) { router := &tcp.Router{} router.HTTPHandler(handlerHTTP) diff --git a/pkg/server/server_entrypoint_tcp.go b/pkg/server/server_entrypoint_tcp.go index a498d6d6d..bf8a5315b 100644 --- a/pkg/server/server_entrypoint_tcp.go +++ b/pkg/server/server_entrypoint_tcp.go @@ -379,7 +379,6 @@ func buildProxyProtocolListener(ctx context.Context, entryPoint *static.EntryPoi func buildListener(ctx context.Context, entryPoint *static.EntryPoint) (net.Listener, error) { listener, err := net.Listen("tcp", entryPoint.GetAddress()) - if err != nil { return nil, fmt.Errorf("error opening listener: %w", err) } diff --git a/pkg/server/service/internalhandler.go b/pkg/server/service/internalhandler.go index 2beb0578e..6ff7e0a1e 100644 --- a/pkg/server/service/internalhandler.go +++ b/pkg/server/service/internalhandler.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/containous/traefik/v2/pkg/config/runtime" + "github.com/containous/traefik/v2/pkg/log" ) type serviceManager interface { @@ -26,7 +27,7 @@ type InternalHandlers struct { } // NewInternalHandlers creates a new InternalHandlers. -func NewInternalHandlers(api func(configuration *runtime.Configuration) http.Handler, configuration *runtime.Configuration, rest http.Handler, metricsHandler http.Handler, pingHandler http.Handler, dashboard http.Handler, next serviceManager) *InternalHandlers { +func NewInternalHandlers(api func(configuration *runtime.Configuration) http.Handler, configuration *runtime.Configuration, rest, metricsHandler, pingHandler, dashboard http.Handler, next serviceManager) *InternalHandlers { var apiHandler http.Handler if api != nil { apiHandler = api(configuration) @@ -42,13 +43,87 @@ func NewInternalHandlers(api func(configuration *runtime.Configuration) http.Han } } -// BuildHTTP builds an HTTP handler. -func (m *InternalHandlers) BuildHTTP(rootCtx context.Context, serviceName string, responseModifier func(*http.Response) error) (http.Handler, error) { - if strings.HasSuffix(serviceName, "@internal") { - return m.get(serviceName) +type responseModifier struct { + r *http.Request + w http.ResponseWriter + + headersSent bool // whether headers have already been sent + code int // status code, must default to 200 + + modifier func(*http.Response) error // can be nil + modified bool // whether modifier has already been called for the current request + modifierErr error // returned by modifier call +} + +// modifier can be nil. +func newResponseModifier(w http.ResponseWriter, r *http.Request, modifier func(*http.Response) error) *responseModifier { + return &responseModifier{ + r: r, + w: w, + modifier: modifier, + code: http.StatusOK, + } +} + +func (w *responseModifier) WriteHeader(code int) { + if w.headersSent { + return + } + defer func() { + w.code = code + w.headersSent = true + }() + + if w.modifier == nil || w.modified { + w.w.WriteHeader(code) + return } - return m.serviceManager.BuildHTTP(rootCtx, serviceName, responseModifier) + resp := http.Response{ + Header: w.w.Header(), + Request: w.r, + } + + if err := w.modifier(&resp); err != nil { + w.modifierErr = err + // we are propagating when we are called in Write, but we're logging anyway, + // because we could be called from another place which does not take care of + // checking w.modifierErr. + log.Errorf("Error when applying response modifier: %v", err) + w.w.WriteHeader(http.StatusInternalServerError) + return + } + + w.modified = true + w.w.WriteHeader(code) +} + +func (w *responseModifier) Header() http.Header { + return w.w.Header() +} + +func (w *responseModifier) Write(b []byte) (int, error) { + w.WriteHeader(w.code) + if w.modifierErr != nil { + return 0, w.modifierErr + } + + return w.w.Write(b) +} + +// BuildHTTP builds an HTTP handler. +func (m *InternalHandlers) BuildHTTP(rootCtx context.Context, serviceName string, respModifier func(*http.Response) error) (http.Handler, error) { + if !strings.HasSuffix(serviceName, "@internal") { + return m.serviceManager.BuildHTTP(rootCtx, serviceName, respModifier) + } + + internalHandler, err := m.get(serviceName) + if err != nil { + return nil, err + } + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + internalHandler.ServeHTTP(newResponseModifier(w, r, respModifier), r) + }), nil } func (m *InternalHandlers) get(serviceName string) (http.Handler, error) { diff --git a/pkg/server/service/proxy_websocket_test.go b/pkg/server/service/proxy_websocket_test.go index bbb0d1196..d309e5379 100644 --- a/pkg/server/service/proxy_websocket_test.go +++ b/pkg/server/service/proxy_websocket_test.go @@ -63,7 +63,7 @@ func TestWebSocketPingPong(t *testing.T) { require.NoError(t, err) - var upgrader = gorillawebsocket.Upgrader{ + upgrader := gorillawebsocket.Upgrader{ HandshakeTimeout: 10 * time.Second, CheckOrigin: func(*http.Request) bool { return true @@ -670,7 +670,7 @@ func (w *websocketRequest) send() (string, error) { if _, err := conn.Write([]byte(w.Data)); err != nil { return "", err } - var msg = make([]byte, 512) + msg := make([]byte, 512) var n int n, err = conn.Read(msg) if err != nil { diff --git a/pkg/tcp/router.go b/pkg/tcp/router.go index c66e987ce..90675eed6 100644 --- a/pkg/tcp/router.go +++ b/pkg/tcp/router.go @@ -102,7 +102,7 @@ func (r *Router) AddRouteTLS(sniHost string, target Handler, config *tls.Config) }) } -// AddRouteHTTPTLS defines a handler for a given sniHost and sets the matching tlsConfig. +// AddRouteHTTPTLS defines the matching tlsConfig for a given sniHost. func (r *Router) AddRouteHTTPTLS(sniHost string, config *tls.Config) { if r.hostHTTPTLSConfig == nil { r.hostHTTPTLSConfig = map[string]*tls.Config{} diff --git a/pkg/tls/certificate_store.go b/pkg/tls/certificate_store.go index 7f6116257..9958e8641 100644 --- a/pkg/tls/certificate_store.go +++ b/pkg/tls/certificate_store.go @@ -118,7 +118,7 @@ func (c CertificateStore) ResetCache() { } // MatchDomain return true if a domain match the cert domain. -func MatchDomain(domain string, certDomain string) bool { +func MatchDomain(domain, certDomain string) bool { if domain == certDomain { return true } diff --git a/pkg/tls/tlsmanager.go b/pkg/tls/tlsmanager.go index 767b4331e..bae3e623a 100644 --- a/pkg/tls/tlsmanager.go +++ b/pkg/tls/tlsmanager.go @@ -81,7 +81,7 @@ func (m *Manager) UpdateConfigs(ctx context.Context, stores map[string]Store, co } // Get gets the TLS configuration to use for a given store / configuration. -func (m *Manager) Get(storeName string, configName string) (*tls.Config, error) { +func (m *Manager) Get(storeName, configName string) (*tls.Config, error) { m.lock.RLock() defer m.lock.RUnlock() diff --git a/pkg/tracing/tracing.go b/pkg/tracing/tracing.go index 33005124a..a70ee54cb 100644 --- a/pkg/tracing/tracing.go +++ b/pkg/tracing/tracing.go @@ -80,12 +80,12 @@ func (t *Tracing) StartSpanf(r *http.Request, spanKind ext.SpanKindEnum, opPrefi } // Inject delegates to opentracing.Tracer. -func (t *Tracing) Inject(sm opentracing.SpanContext, format interface{}, carrier interface{}) error { +func (t *Tracing) Inject(sm opentracing.SpanContext, format, carrier interface{}) error { return t.tracer.Inject(sm, format, carrier) } // Extract delegates to opentracing.Tracer. -func (t *Tracing) Extract(format interface{}, carrier interface{}) (opentracing.SpanContext, error) { +func (t *Tracing) Extract(format, carrier interface{}) (opentracing.SpanContext, error) { return t.tracer.Extract(format, carrier) } diff --git a/pkg/types/domains.go b/pkg/types/domains.go index 101fb584a..b13fbbd2f 100644 --- a/pkg/types/domains.go +++ b/pkg/types/domains.go @@ -30,7 +30,7 @@ func (d *Domain) Set(domains []string) { } // MatchDomain returns true if a domain match the cert domain. -func MatchDomain(domain string, certDomain string) bool { +func MatchDomain(domain, certDomain string) bool { if domain == certDomain { return true } diff --git a/pkg/types/logs.go b/pkg/types/logs.go index 7843079f5..b84ec77f1 100644 --- a/pkg/types/logs.go +++ b/pkg/types/logs.go @@ -113,7 +113,7 @@ func checkFieldValue(value string, defaultKeep bool) bool { } } -func checkFieldHeaderValue(value string, defaultValue string) string { +func checkFieldHeaderValue(value, defaultValue string) string { if value == AccessLogKeep || value == AccessLogDrop || value == AccessLogRedact { return value } diff --git a/pkg/version/version.go b/pkg/version/version.go index 2f02c16d2..e01f38ec4 100644 --- a/pkg/version/version.go +++ b/pkg/version/version.go @@ -24,11 +24,9 @@ var ( // Handler expose version routes. type Handler struct{} -var ( - templatesRenderer = render.New(render.Options{ - Directory: "nowhere", - }) -) +var templatesRenderer = render.New(render.Options{ + Directory: "nowhere", +}) // Append adds version routes on a router. func (v Handler) Append(router *mux.Router) { diff --git a/traefik.sample.toml b/traefik.sample.toml index cb2cb4c40..0658a766f 100644 --- a/traefik.sample.toml +++ b/traefik.sample.toml @@ -12,6 +12,8 @@ [global] checkNewVersion = true sendAnonymousUsage = true + # Enabling domain fronting + # insecureSNI = true ################################################################ # Entrypoints configuration diff --git a/webui/src/components/_commons/PanelMiddlewares.vue b/webui/src/components/_commons/PanelMiddlewares.vue index 7eb5ca148..daab41a76 100644 --- a/webui/src/components/_commons/PanelMiddlewares.vue +++ b/webui/src/components/_commons/PanelMiddlewares.vue @@ -371,15 +371,16 @@ - +
Access Control Allow Origin
- {{ exData(middleware).accessControlAllowOrigin }} + {{ val }}