forked from Ivasoft/traefik
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f482e5e84a | ||
|
|
447c3567b4 | ||
|
|
3c5e6fe7f8 | ||
|
|
bf4a578bbb | ||
|
|
4cabea069d | ||
|
|
c53033a778 | ||
|
|
ea8642e2a1 | ||
|
|
73cea2d303 | ||
|
|
96a3468791 | ||
|
|
2065f4c003 | ||
|
|
9a931e4dc9 | ||
|
|
49ec62c757 | ||
|
|
a371f971fb | ||
|
|
5f9a84fc8b | ||
|
|
1305bf49a5 | ||
|
|
da0a16e122 | ||
|
|
fb10687168 | ||
|
|
f0d78471af | ||
|
|
a90b2a672e |
@@ -61,6 +61,7 @@
|
||||
"exhaustivestruct", # Not relevant
|
||||
"makezero", # not relevant
|
||||
"forbidigo", # not relevant
|
||||
"ifshort", # not relevant
|
||||
]
|
||||
|
||||
[issues]
|
||||
@@ -119,4 +120,4 @@
|
||||
text = "printf-like formatting function 'SetErrorWithEvent' should be named 'SetErrorWithEventf'"
|
||||
[[issues.exclude-rules]]
|
||||
path = "pkg/log/deprecated.go"
|
||||
linters = ["godot"]
|
||||
linters = ["godot"]
|
||||
|
||||
30
CHANGELOG.md
30
CHANGELOG.md
@@ -1,3 +1,33 @@
|
||||
## [v2.4.2](https://github.com/traefik/traefik/tree/v2.4.2) (2021-02-02)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.4.1...v2.4.2)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Fix the redirect entrypoint default priority ([#7851](https://github.com/traefik/traefik/pull/7851) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- **[middleware]** Fix the infinite loop in forwarded header middleware. ([#7847](https://github.com/traefik/traefik/pull/7847) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Documentation:**
|
||||
- Fix the static configuration generation for environment variables ([#7849](https://github.com/traefik/traefik/pull/7849) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
|
||||
## [v2.4.1](https://github.com/traefik/traefik/tree/v2.4.1) (2021-02-01)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.4.0...v2.4.1)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme,provider]** Fix HTTP challenge router unexpected delayed creation ([#7805](https://github.com/traefik/traefik/pull/7805) by [jspdown](https://github.com/jspdown))
|
||||
- **[acme]** Update go-acme/lego to v4.2.0 ([#7793](https://github.com/traefik/traefik/pull/7793) by [ldez](https://github.com/ldez))
|
||||
- **[api,plugins]** Fix plugin type on middleware endpoint response ([#7782](https://github.com/traefik/traefik/pull/7782) by [jspdown](https://github.com/jspdown))
|
||||
- **[authentication,middleware]** Forward Proxy-Authorization header to authentication server ([#7433](https://github.com/traefik/traefik/pull/7433) by [Scapal](https://github.com/Scapal))
|
||||
- **[k8s,k8s/ingress]** Add support for multiple ingress classes ([#7799](https://github.com/traefik/traefik/pull/7799) by [LandryBe](https://github.com/LandryBe))
|
||||
- **[middleware]** Improve forwarded header and recovery middlewares performances ([#7783](https://github.com/traefik/traefik/pull/7783) by [juliens](https://github.com/juliens))
|
||||
- **[pilot]** Reduce pressure of pilot services when errors occurs ([#7824](https://github.com/traefik/traefik/pull/7824) by [darkweaver87](https://github.com/darkweaver87))
|
||||
- **[provider]** Fix aggregator test comment ([#7840](https://github.com/traefik/traefik/pull/7840) by [rtribotte](https://github.com/rtribotte))
|
||||
- **[provider]** Fix servers transport not found ([#7839](https://github.com/traefik/traefik/pull/7839) by [jspdown](https://github.com/jspdown))
|
||||
|
||||
**Documentation:**
|
||||
- **[consulcatalog]** Fix refresh interval option description in consulcatalog provider ([#7810](https://github.com/traefik/traefik/pull/7810) by [GabeL7r](https://github.com/GabeL7r))
|
||||
- **[docker]** Fix missing serverstransport documentation ([#7822](https://github.com/traefik/traefik/pull/7822) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- **[k8s]** Fix YAML syntax in providers docs ([#7787](https://github.com/traefik/traefik/pull/7787) by [4ops](https://github.com/4ops))
|
||||
- **[service]** Fix typo in server transports documentation ([#7797](https://github.com/traefik/traefik/pull/7797) by [obezuk](https://github.com/obezuk))
|
||||
|
||||
## [v2.4.0](https://github.com/traefik/traefik/tree/v2.4.0) (2021-01-19)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.3.0-rc1...v2.4.0)
|
||||
|
||||
|
||||
@@ -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.34.0
|
||||
RUN curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.36.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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
FROM alpine:3.10 as alpine
|
||||
FROM alpine:3.13 as alpine
|
||||
|
||||
RUN apk --no-cache --no-progress add \
|
||||
libcurl \
|
||||
|
||||
@@ -324,10 +324,12 @@ For complete details, refer to your provider's _Additional configuration_ link.
|
||||
| [IIJ](https://www.iij.ad.jp/) | `iij` | `IIJ_API_ACCESS_KEY`, `IIJ_API_SECRET_KEY`, `IIJ_DO_SERVICE_CODE` | [Additional configuration](https://go-acme.github.io/lego/dns/iij) |
|
||||
| [Infomaniak](https://www.infomaniak.com) | `infomaniak` | `INFOMANIAK_ACCESS_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/infomaniak) |
|
||||
| [INWX](https://www.inwx.de/en) | `inwx` | `INWX_USERNAME`, `INWX_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/inwx) |
|
||||
| [ionos](https://ionos.com/) | `ionos` | `IONOS_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/ionos) |
|
||||
| [Joker.com](https://joker.com) | `joker` | `JOKER_API_MODE` with `JOKER_API_KEY` or `JOKER_USERNAME`, `JOKER_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/joker) |
|
||||
| [Lightsail](https://aws.amazon.com/lightsail/) | `lightsail` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `DNS_ZONE` | [Additional configuration](https://go-acme.github.io/lego/dns/lightsail) |
|
||||
| [Linode v4](https://www.linode.com) | `linode` | `LINODE_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/linode) |
|
||||
| [Liquid Web](https://www.liquidweb.com/) | `liquidweb` | `LIQUID_WEB_PASSWORD`, `LIQUID_WEB_USERNAME`, `LIQUID_WEB_ZONE` | [Additional configuration](https://go-acme.github.io/lego/dns/liquidweb) |
|
||||
| [Loopia](https://loopia.com/) | `loopia` | `LOOPIA_API_PASSWORD`, `LOOPIA_API_USER` | [Additional configuration](https://go-acme.github.io/lego/dns/loopia) |
|
||||
| [LuaDNS](https://luadns.com) | `luadns` | `LUADNS_API_USERNAME`, `LUADNS_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/luadns) |
|
||||
| manual | `manual` | none, but you need to run Traefik interactively [^4], turn on debug log to see instructions and press <kbd>Enter</kbd>. | |
|
||||
| [MyDNS.jp](https://www.mydns.jp/) | `mydnsjp` | `MYDNSJP_MASTER_ID`, `MYDNSJP_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/mydnsjp) |
|
||||
|
||||
@@ -145,7 +145,7 @@ _Optional, Default=empty_
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
kubernetesGateway:
|
||||
token = "mytoken"
|
||||
token: "mytoken"
|
||||
# ...
|
||||
```
|
||||
|
||||
|
||||
@@ -159,6 +159,7 @@
|
||||
- "traefik.http.services.service01.loadbalancer.sticky.cookie.secure=true"
|
||||
- "traefik.http.services.service01.loadbalancer.server.port=foobar"
|
||||
- "traefik.http.services.service01.loadbalancer.server.scheme=foobar"
|
||||
- "traefik.http.services.service01.loadbalancer.serverstransport=foobar"
|
||||
- "traefik.tcp.routers.tcprouter0.entrypoints=foobar, foobar"
|
||||
- "traefik.tcp.routers.tcprouter0.rule=foobar"
|
||||
- "traefik.tcp.routers.tcprouter0.service=foobar"
|
||||
|
||||
@@ -96,6 +96,7 @@ spec:
|
||||
strategy: RoundRobin
|
||||
- name: s2
|
||||
port: 433
|
||||
serversTransport: mytransport
|
||||
- match: PathPrefix(`/misc`)
|
||||
services:
|
||||
- name: s3
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
"traefik.http.middlewares.middleware10.headers.accesscontrolallowmethods": "foobar, foobar",
|
||||
"traefik.http.middlewares.middleware10.headers.accesscontrolalloworigin": "foobar",
|
||||
"traefik.http.middlewares.middleware10.headers.accesscontrolalloworiginlist": "foobar, foobar",
|
||||
"traefik.http.middlewares.middleware10.headers.accesscontrolalloworiginlistregex": "foobar, foobar",
|
||||
"traefik.http.middlewares.middleware10.headers.accesscontrolexposeheaders": "foobar, foobar",
|
||||
"traefik.http.middlewares.middleware10.headers.accesscontrolmaxage": "42",
|
||||
"traefik.http.middlewares.middleware10.headers.addvaryheader": "true",
|
||||
@@ -120,6 +121,7 @@
|
||||
"traefik.http.routers.router0.priority": "42",
|
||||
"traefik.http.routers.router0.rule": "foobar",
|
||||
"traefik.http.routers.router0.service": "foobar",
|
||||
"traefik.http.routers.router0.tls": "true",
|
||||
"traefik.http.routers.router0.tls.certresolver": "foobar",
|
||||
"traefik.http.routers.router0.tls.domains[0].main": "foobar",
|
||||
"traefik.http.routers.router0.tls.domains[0].sans": "foobar, foobar",
|
||||
@@ -131,6 +133,7 @@
|
||||
"traefik.http.routers.router1.priority": "42",
|
||||
"traefik.http.routers.router1.rule": "foobar",
|
||||
"traefik.http.routers.router1.service": "foobar",
|
||||
"traefik.http.routers.router1.tls": "true",
|
||||
"traefik.http.routers.router1.tls.certresolver": "foobar",
|
||||
"traefik.http.routers.router1.tls.domains[0].main": "foobar",
|
||||
"traefik.http.routers.router1.tls.domains[0].sans": "foobar, foobar",
|
||||
@@ -156,9 +159,11 @@
|
||||
"traefik.http.services.service01.loadbalancer.sticky.cookie.secure": "true",
|
||||
"traefik.http.services.service01.loadbalancer.server.port": "foobar",
|
||||
"traefik.http.services.service01.loadbalancer.server.scheme": "foobar",
|
||||
"traefik.http.services.service01.loadbalancer.serverstransport": "foobar",
|
||||
"traefik.tcp.routers.tcprouter0.entrypoints": "foobar, foobar",
|
||||
"traefik.tcp.routers.tcprouter0.rule": "foobar",
|
||||
"traefik.tcp.routers.tcprouter0.service": "foobar",
|
||||
"traefik.tcp.routers.tcprouter0.tls": "true",
|
||||
"traefik.tcp.routers.tcprouter0.tls.certresolver": "foobar",
|
||||
"traefik.tcp.routers.tcprouter0.tls.domains[0].main": "foobar",
|
||||
"traefik.tcp.routers.tcprouter0.tls.domains[0].sans": "foobar, foobar",
|
||||
@@ -169,6 +174,7 @@
|
||||
"traefik.tcp.routers.tcprouter1.entrypoints": "foobar, foobar",
|
||||
"traefik.tcp.routers.tcprouter1.rule": "foobar",
|
||||
"traefik.tcp.routers.tcprouter1.service": "foobar",
|
||||
"traefik.tcp.routers.tcprouter1.tls": "true",
|
||||
"traefik.tcp.routers.tcprouter1.tls.certresolver": "foobar",
|
||||
"traefik.tcp.routers.tcprouter1.tls.domains[0].main": "foobar",
|
||||
"traefik.tcp.routers.tcprouter1.tls.domains[0].sans": "foobar, foobar",
|
||||
|
||||
@@ -394,7 +394,7 @@ Expose containers by default. (Default: ```true```)
|
||||
Prefix for consul service tags. Default 'traefik' (Default: ```traefik```)
|
||||
|
||||
`--providers.consulcatalog.refreshinterval`:
|
||||
Interval for check Consul API. Default 100ms (Default: ```15```)
|
||||
Interval for check Consul API. Default 15s (Default: ```15```)
|
||||
|
||||
`--providers.consulcatalog.requireconsistent`:
|
||||
Forces the read to be fully consistent. (Default: ```false```)
|
||||
|
||||
@@ -135,10 +135,10 @@ Default certificate resolver for the routers linked to the entry point.
|
||||
`TRAEFIK_ENTRYPOINTS_<NAME>_HTTP_TLS_DOMAINS`:
|
||||
Default TLS domains for the routers linked to the entry point.
|
||||
|
||||
`TRAEFIK_ENTRYPOINTS_<NAME>_HTTP_TLS_DOMAINS[n]_MAIN`:
|
||||
`TRAEFIK_ENTRYPOINTS_<NAME>_HTTP_TLS_DOMAINS_n_MAIN`:
|
||||
Default subject name.
|
||||
|
||||
`TRAEFIK_ENTRYPOINTS_<NAME>_HTTP_TLS_DOMAINS[n]_SANS`:
|
||||
`TRAEFIK_ENTRYPOINTS_<NAME>_HTTP_TLS_DOMAINS_n_SANS`:
|
||||
Subject alternative names.
|
||||
|
||||
`TRAEFIK_ENTRYPOINTS_<NAME>_HTTP_TLS_OPTIONS`:
|
||||
@@ -367,7 +367,7 @@ Expose containers by default. (Default: ```true```)
|
||||
Prefix for consul service tags. Default 'traefik' (Default: ```traefik```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_CONSULCATALOG_REFRESHINTERVAL`:
|
||||
Interval for check Consul API. Default 100ms (Default: ```15```)
|
||||
Interval for check Consul API. Default 15s (Default: ```15```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_CONSULCATALOG_REQUIRECONSISTENT`:
|
||||
Forces the read to be fully consistent. (Default: ```false```)
|
||||
|
||||
@@ -380,6 +380,7 @@
|
||||
token = "foobar"
|
||||
|
||||
[experimental]
|
||||
kubernetesGateway = true
|
||||
[experimental.plugins]
|
||||
[experimental.plugins.Descriptor0]
|
||||
moduleName = "foobar"
|
||||
@@ -390,4 +391,3 @@
|
||||
[experimental.devPlugin]
|
||||
goPath = "foobar"
|
||||
moduleName = "foobar"
|
||||
kubernetesGateway = true
|
||||
|
||||
@@ -400,6 +400,7 @@ certificatesResolvers:
|
||||
pilot:
|
||||
token: foobar
|
||||
experimental:
|
||||
kubernetesGateway: true
|
||||
plugins:
|
||||
Descriptor0:
|
||||
moduleName: foobar
|
||||
@@ -410,5 +411,4 @@ experimental:
|
||||
devPlugin:
|
||||
goPath: foobar
|
||||
moduleName: foobar
|
||||
kubernetesGateway: true
|
||||
|
||||
|
||||
@@ -285,6 +285,14 @@ you'd add the label `traefik.http.services.<name-of-your-choice>.loadbalancer.pa
|
||||
- "traefik.http.services.myservice.loadbalancer.server.scheme=http"
|
||||
```
|
||||
|
||||
??? info "`traefik.http.services.<service_name>.loadbalancer.serverstransport`"
|
||||
|
||||
See [serverstransport](../services/index.md#serverstransport) for more information.
|
||||
|
||||
```yaml
|
||||
- "traefik.http.services.<service_name>.loadbalancer.serverstransport=foobar"
|
||||
```
|
||||
|
||||
??? info "`traefik.http.services.<service_name>.loadbalancer.passhostheader`"
|
||||
|
||||
See [pass Host header](../services/index.md#pass-host-header) for more information.
|
||||
|
||||
@@ -335,6 +335,7 @@ Register the `IngressRoute` [kind](../../reference/dynamic-configuration/kuberne
|
||||
responseForwarding:
|
||||
flushInterval: 1ms
|
||||
scheme: https
|
||||
serversTransport: transport
|
||||
sticky:
|
||||
cookie:
|
||||
httpOnly: true
|
||||
|
||||
@@ -462,7 +462,7 @@ By default, `passHostHeader` is true.
|
||||
|
||||
#### ServersTransport
|
||||
|
||||
`serversTransport` allows to reference a ServersTransport configuration for the communication between Traefik and your servers.
|
||||
`serversTransport` allows to reference a [ServersTransport](./index.md#serverstransport_1) configuration for the communication between Traefik and your servers.
|
||||
|
||||
??? example "Specify a transport -- Using the [File Provider](../../providers/file.md)"
|
||||
|
||||
@@ -573,7 +573,7 @@ that will be set as client certificates for mTLS.
|
||||
http:
|
||||
serversTransports:
|
||||
mytransport:
|
||||
certficates:
|
||||
certificates:
|
||||
- certFile: foo.crt
|
||||
keyFile: bar.crt
|
||||
```
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM alpine:3.12
|
||||
FROM alpine:3.13
|
||||
|
||||
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/.local/bin
|
||||
|
||||
|
||||
2
go.mod
2
go.mod
@@ -32,7 +32,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/v4 v4.1.3
|
||||
github.com/go-acme/lego/v4 v4.2.0
|
||||
github.com/go-check/check v0.0.0-00010101000000-000000000000
|
||||
github.com/go-kit/kit v0.10.1-0.20200915143503-439c4d2ed3ea
|
||||
github.com/golang/protobuf v1.4.2
|
||||
|
||||
16
go.sum
16
go.sum
@@ -220,8 +220,8 @@ github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfc
|
||||
github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cpu/goacmedns v0.0.3 h1:QOeMpIEsIdm1LSASSswjaTf8CXmzcrgy5OeCfHjppA4=
|
||||
github.com/cpu/goacmedns v0.0.3/go.mod h1:4MipLkI+qScwqtVxcNO6okBhbgRrr7/tKXUSgSL0teQ=
|
||||
github.com/cpu/goacmedns v0.1.1 h1:DM3H2NiN2oam7QljgGY5ygy4yDXhK5Z4JUnqaugs2C4=
|
||||
github.com/cpu/goacmedns v0.1.1/go.mod h1:MuaouqEhPAHxsbqjgnck5zeghuwBP1dLnPoobeGqugQ=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
@@ -310,8 +310,8 @@ github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
github.com/go-acme/lego/v4 v4.1.3 h1:D8nnzrijQFUAqdNPwnbvm6tJ3AJAzQAlnROeecUNG/4=
|
||||
github.com/go-acme/lego/v4 v4.1.3/go.mod h1:pIFm5tWkXSgiAEfJ/XQCQIvX1cEvHFwbgLZyx8OVSUE=
|
||||
github.com/go-acme/lego/v4 v4.2.0 h1:zEvpcDLqvzOlNUGBMA0MCKPpb9UBbnBzgWwCIbTEt2g=
|
||||
github.com/go-acme/lego/v4 v4.2.0/go.mod h1:jmhqxBaangB8txXZKjRLTPXFXUwPCTU2fU8S9/eQzBI=
|
||||
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=
|
||||
@@ -509,8 +509,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.7 h1:8/CAEZt/+F7kR7GevNHulKkUjLht3CPmn7egmhieNKo=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.7/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||
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=
|
||||
@@ -907,8 +907,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.5.0 h1:iQzYhzbokmpDARbvIkvTkoyS7WMH82zVTKAL1PZ4JOA=
|
||||
github.com/vultr/govultr v0.5.0/go.mod h1:wZZXZbYbqyY1n3AldoeYNZK4Wnmmoq6dNFkvd5TV3ss=
|
||||
github.com/vultr/govultr/v2 v2.0.0 h1:+lAtqfWy3g9VwL7tT2Fpyad8Vv4MxOhT/NU8O5dk+EQ=
|
||||
github.com/vultr/govultr/v2 v2.0.0/go.mod h1:2PsEeg+gs3p/Fo5Pw8F9mv+DUBEOlrNZ8GmCTGmhOhs=
|
||||
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=
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
whoami:
|
||||
image: traefik/whoami
|
||||
labels:
|
||||
- traefik.http.routers.route1.rule=PathPrefix(`/`)
|
||||
- traefik.http.routers.route1.rule=PathPrefix(`/foo`)
|
||||
- traefik.http.routers.route1.middlewares=passtls
|
||||
- traefik.http.routers.route1.tls=true
|
||||
- traefik.http.middlewares.passtls.passtlsclientcert.pem=true
|
||||
|
||||
@@ -50,10 +50,10 @@ func (s *TLSClientHeadersSuite) TestTLSClientHeaders(c *check.C) {
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer s.killCmd(cmd)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 2*time.Second, try.BodyContains("PathPrefix(`/`)"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 2*time.Second, try.BodyContains("PathPrefix(`/foo`)"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
request, err := http.NewRequest(http.MethodGet, "https://127.0.0.1:8443", nil)
|
||||
request, err := http.NewRequest(http.MethodGet, "https://127.0.0.1:8443/foo", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
certificate, err := tls.LoadX509KeyPair(certPemPath, certKeyPath)
|
||||
|
||||
@@ -64,7 +64,12 @@ THIS FILE MUST NOT BE EDITED BY HAND
|
||||
continue
|
||||
}
|
||||
|
||||
w.writeln("`" + prefix + strings.ReplaceAll(flat.Name, "[0]", "[n]") + "`: ")
|
||||
if prefix == "" {
|
||||
w.writeln("`" + prefix + strings.ReplaceAll(flat.Name, "[0]", "_n") + "`: ")
|
||||
} else {
|
||||
w.writeln("`" + prefix + strings.ReplaceAll(flat.Name, "[0]", "[n]") + "`: ")
|
||||
}
|
||||
|
||||
if flat.Default == "" {
|
||||
w.writeln(flat.Description)
|
||||
} else {
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
assetfs "github.com/elazarl/go-bindata-assetfs"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/traefik/traefik/v2/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v2/pkg/config/runtime"
|
||||
"github.com/traefik/traefik/v2/pkg/config/static"
|
||||
"github.com/traefik/traefik/v2/pkg/log"
|
||||
@@ -157,6 +158,13 @@ func extractType(element interface{}) string {
|
||||
v := reflect.ValueOf(element).Elem()
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
field := v.Field(i)
|
||||
|
||||
if field.Kind() == reflect.Map && field.Type().Elem() == reflect.TypeOf(dynamic.PluginConf{}) {
|
||||
if keys := field.MapKeys(); len(keys) == 1 {
|
||||
return keys[0].String()
|
||||
}
|
||||
}
|
||||
|
||||
if field.Kind() == reflect.Ptr && field.Elem().Kind() == reflect.Struct {
|
||||
if !field.IsNil() {
|
||||
return v.Type().Field(i).Name
|
||||
|
||||
@@ -171,3 +171,112 @@ func TestHandler_RawData(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandler_GetMiddleware(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
middlewareName string
|
||||
conf runtime.Configuration
|
||||
expectedStatus int
|
||||
expected interface{}
|
||||
}{
|
||||
{
|
||||
desc: "Middleware not found",
|
||||
middlewareName: "auth@myprovider",
|
||||
conf: runtime.Configuration{
|
||||
Middlewares: map[string]*runtime.MiddlewareInfo{},
|
||||
},
|
||||
expectedStatus: http.StatusNotFound,
|
||||
},
|
||||
{
|
||||
desc: "Get middleware",
|
||||
middlewareName: "auth@myprovider",
|
||||
conf: runtime.Configuration{
|
||||
Middlewares: map[string]*runtime.MiddlewareInfo{
|
||||
"auth@myprovider": {
|
||||
Middleware: &dynamic.Middleware{
|
||||
BasicAuth: &dynamic.BasicAuth{
|
||||
Users: []string{"admin:admin"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedStatus: http.StatusOK,
|
||||
expected: middlewareRepresentation{
|
||||
MiddlewareInfo: &runtime.MiddlewareInfo{
|
||||
Middleware: &dynamic.Middleware{
|
||||
BasicAuth: &dynamic.BasicAuth{
|
||||
Users: []string{"admin:admin"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Name: "auth@myprovider",
|
||||
Provider: "myprovider",
|
||||
Type: "basicauth",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Get plugin middleware",
|
||||
middlewareName: "myplugin@myprovider",
|
||||
conf: runtime.Configuration{
|
||||
Middlewares: map[string]*runtime.MiddlewareInfo{
|
||||
"myplugin@myprovider": {
|
||||
Middleware: &dynamic.Middleware{
|
||||
Plugin: map[string]dynamic.PluginConf{
|
||||
"mysuperplugin": {
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedStatus: http.StatusOK,
|
||||
expected: middlewareRepresentation{
|
||||
MiddlewareInfo: &runtime.MiddlewareInfo{
|
||||
Middleware: &dynamic.Middleware{
|
||||
Plugin: map[string]dynamic.PluginConf{
|
||||
"mysuperplugin": {
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Name: "myplugin@myprovider",
|
||||
Provider: "myprovider",
|
||||
Type: "mysuperplugin",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
handler := New(static.Configuration{API: &static.API{}, Global: &static.Global{}}, &test.conf)
|
||||
server := httptest.NewServer(handler.createRouter())
|
||||
|
||||
resp, err := http.DefaultClient.Get(server.URL + "/api/http/middlewares/" + test.middlewareName)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, test.expectedStatus, resp.StatusCode)
|
||||
|
||||
if test.expected == nil {
|
||||
return
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = resp.Body.Close()
|
||||
require.NoError(t, err)
|
||||
|
||||
expected, err := json.Marshal(test.expected)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.JSONEq(t, string(expected), string(data))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ type RedirectEntryPoint struct {
|
||||
func (r *RedirectEntryPoint) SetDefaults() {
|
||||
r.Scheme = "https"
|
||||
r.Permanent = true
|
||||
r.Priority = math.MaxInt32
|
||||
r.Priority = math.MaxInt32 - 1
|
||||
}
|
||||
|
||||
// TLSConfig is the default TLS configuration for all the routers associated to the concerned entry point.
|
||||
|
||||
@@ -379,12 +379,12 @@ func (d *dynamicConfig) hasServerURL(serviceName, serverURL string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func newCollector(metricName string, labels stdprometheus.Labels, c stdprometheus.Collector, delete func()) *collector {
|
||||
func newCollector(metricName string, labels stdprometheus.Labels, c stdprometheus.Collector, deleteFn func()) *collector {
|
||||
return &collector{
|
||||
id: buildMetricID(metricName, labels),
|
||||
labels: labels,
|
||||
collector: c,
|
||||
delete: delete,
|
||||
delete: deleteFn,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,18 @@ const (
|
||||
forwardedTypeName = "ForwardedAuthType"
|
||||
)
|
||||
|
||||
// hopHeaders Hop-by-hop headers to be removed in the authentication request.
|
||||
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html
|
||||
// Proxy-Authorization header is forwarded to the authentication server (see https://tools.ietf.org/html/rfc7235#section-4.4).
|
||||
var hopHeaders = []string{
|
||||
forward.Connection,
|
||||
forward.KeepAlive,
|
||||
forward.Te, // canonicalized version of "TE"
|
||||
forward.Trailers,
|
||||
forward.TransferEncoding,
|
||||
forward.Upgrade,
|
||||
}
|
||||
|
||||
type forwardAuth struct {
|
||||
address string
|
||||
authResponseHeaders []string
|
||||
@@ -131,7 +143,7 @@ func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
logger.Debugf("Remote error %s. StatusCode: %d", fa.address, forwardResponse.StatusCode)
|
||||
|
||||
utils.CopyHeaders(rw.Header(), forwardResponse.Header)
|
||||
utils.RemoveHeaders(rw.Header(), forward.HopHeaders...)
|
||||
utils.RemoveHeaders(rw.Header(), hopHeaders...)
|
||||
|
||||
// Grab the location header, if any.
|
||||
redirectURL, err := forwardResponse.Location()
|
||||
@@ -187,7 +199,7 @@ func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
|
||||
func writeHeader(req, forwardReq *http.Request, trustForwardHeader bool, allowedHeaders []string) {
|
||||
utils.CopyHeaders(forwardReq.Header, req.Header)
|
||||
utils.RemoveHeaders(forwardReq.Header, forward.HopHeaders...)
|
||||
utils.RemoveHeaders(forwardReq.Header, hopHeaders...)
|
||||
|
||||
forwardReq.Header = filterForwardRequestHeaders(forwardReq.Header, allowedHeaders)
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ func TestForwardAuthFail(t *testing.T) {
|
||||
})
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set(forward.ProxyAuthenticate, "test")
|
||||
http.Error(w, "Forbidden", http.StatusForbidden)
|
||||
}))
|
||||
t.Cleanup(server.Close)
|
||||
@@ -48,6 +49,7 @@ func TestForwardAuthFail(t *testing.T) {
|
||||
err = res.Body.Close()
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "test", res.Header.Get(forward.ProxyAuthenticate))
|
||||
assert.Equal(t, "Forbidden\n", string(body))
|
||||
}
|
||||
|
||||
@@ -142,7 +144,7 @@ func TestForwardAuthRedirect(t *testing.T) {
|
||||
func TestForwardAuthRemoveHopByHopHeaders(t *testing.T) {
|
||||
authTs := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
headers := w.Header()
|
||||
for _, header := range forward.HopHeaders {
|
||||
for _, header := range hopHeaders {
|
||||
if header == forward.TransferEncoding {
|
||||
headers.Set(header, "chunked")
|
||||
} else {
|
||||
@@ -367,11 +369,13 @@ func Test_writeHeader(t *testing.T) {
|
||||
},
|
||||
trustForwardHeader: false,
|
||||
expectedHeaders: map[string]string{
|
||||
"X-CustomHeader": "CustomHeader",
|
||||
"X-Forwarded-Proto": "http",
|
||||
"X-Forwarded-Host": "foo.bar",
|
||||
"X-Forwarded-Uri": "/path?q=1",
|
||||
"X-Forwarded-Method": "GET",
|
||||
"X-CustomHeader": "CustomHeader",
|
||||
"X-Forwarded-Proto": "http",
|
||||
"X-Forwarded-Host": "foo.bar",
|
||||
"X-Forwarded-Uri": "/path?q=1",
|
||||
"X-Forwarded-Method": "GET",
|
||||
forward.ProxyAuthenticate: "ProxyAuthenticate",
|
||||
forward.ProxyAuthorization: "ProxyAuthorization",
|
||||
},
|
||||
checkForUnexpectedHeaders: true,
|
||||
},
|
||||
|
||||
@@ -284,9 +284,9 @@ func TestIntegrationShouldCompress(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func generateBytes(len int) []byte {
|
||||
func generateBytes(length int) []byte {
|
||||
var value []byte
|
||||
for i := 0; i < len; i++ {
|
||||
for i := 0; i < length; i++ {
|
||||
value = append(value, 0x61+byte(i))
|
||||
}
|
||||
return value
|
||||
|
||||
@@ -29,6 +29,7 @@ func TestHandler(t *testing.T) {
|
||||
fmt.Fprintln(w, "My error page.")
|
||||
}),
|
||||
validate: func(t *testing.T, recorder *httptest.ResponseRecorder) {
|
||||
t.Helper()
|
||||
assert.Equal(t, http.StatusOK, recorder.Code, "HTTP status")
|
||||
assert.Contains(t, recorder.Body.String(), http.StatusText(http.StatusOK))
|
||||
},
|
||||
@@ -41,6 +42,7 @@ func TestHandler(t *testing.T) {
|
||||
fmt.Fprintln(w, "My error page.")
|
||||
}),
|
||||
validate: func(t *testing.T, recorder *httptest.ResponseRecorder) {
|
||||
t.Helper()
|
||||
assert.Equal(t, http.StatusPartialContent, recorder.Code, "HTTP status")
|
||||
assert.Contains(t, recorder.Body.String(), http.StatusText(http.StatusPartialContent))
|
||||
},
|
||||
@@ -53,6 +55,7 @@ func TestHandler(t *testing.T) {
|
||||
fmt.Fprintln(w, "whatever, should not be called")
|
||||
}),
|
||||
validate: func(t *testing.T, recorder *httptest.ResponseRecorder) {
|
||||
t.Helper()
|
||||
assert.Equal(t, http.StatusNotModified, recorder.Code, "HTTP status")
|
||||
assert.Contains(t, recorder.Body.String(), "")
|
||||
},
|
||||
@@ -65,6 +68,7 @@ func TestHandler(t *testing.T) {
|
||||
fmt.Fprintln(w, "My error page.")
|
||||
}),
|
||||
validate: func(t *testing.T, recorder *httptest.ResponseRecorder) {
|
||||
t.Helper()
|
||||
assert.Equal(t, http.StatusInternalServerError, recorder.Code, "HTTP status")
|
||||
assert.Contains(t, recorder.Body.String(), "My error page.")
|
||||
assert.NotContains(t, recorder.Body.String(), "oops", "Should not return the oops page")
|
||||
@@ -78,6 +82,7 @@ func TestHandler(t *testing.T) {
|
||||
fmt.Fprintln(w, "My error page.")
|
||||
}),
|
||||
validate: func(t *testing.T, recorder *httptest.ResponseRecorder) {
|
||||
t.Helper()
|
||||
assert.Equal(t, http.StatusBadGateway, recorder.Code, "HTTP status")
|
||||
assert.Contains(t, recorder.Body.String(), http.StatusText(http.StatusBadGateway))
|
||||
assert.NotContains(t, recorder.Body.String(), "Test Server", "Should return the oops page since we have not configured the 502 code")
|
||||
@@ -95,6 +100,7 @@ func TestHandler(t *testing.T) {
|
||||
}
|
||||
}),
|
||||
validate: func(t *testing.T, recorder *httptest.ResponseRecorder) {
|
||||
t.Helper()
|
||||
assert.Equal(t, http.StatusServiceUnavailable, recorder.Code, "HTTP status")
|
||||
assert.Contains(t, recorder.Body.String(), "My 503 page.")
|
||||
assert.NotContains(t, recorder.Body.String(), "oops", "Should not return the oops page")
|
||||
@@ -112,6 +118,7 @@ func TestHandler(t *testing.T) {
|
||||
}
|
||||
}),
|
||||
validate: func(t *testing.T, recorder *httptest.ResponseRecorder) {
|
||||
t.Helper()
|
||||
assert.Equal(t, http.StatusServiceUnavailable, recorder.Code, "HTTP status")
|
||||
assert.Contains(t, recorder.Body.String(), "My 503 page.")
|
||||
assert.NotContains(t, recorder.Body.String(), "oops", "Should not return the oops page")
|
||||
|
||||
@@ -84,20 +84,30 @@ func (x *XForwarded) isTrustedIP(ip string) bool {
|
||||
// removeIPv6Zone removes the zone if the given IP is an ipv6 address and it has {zone} information in it,
|
||||
// like "[fe80::d806:a55d:eb1b:49cc%vEthernet (vmxnet3 Ethernet Adapter - Virtual Switch)]:64692".
|
||||
func removeIPv6Zone(clientIP string) string {
|
||||
return strings.Split(clientIP, "%")[0]
|
||||
if idx := strings.Index(clientIP, "%"); idx != -1 {
|
||||
return clientIP[:idx]
|
||||
}
|
||||
return clientIP
|
||||
}
|
||||
|
||||
// isWebsocketRequest returns whether the specified HTTP request is a websocket handshake request.
|
||||
func isWebsocketRequest(req *http.Request) bool {
|
||||
containsHeader := func(name, value string) bool {
|
||||
items := strings.Split(req.Header.Get(name), ",")
|
||||
for _, item := range items {
|
||||
if value == strings.ToLower(strings.TrimSpace(item)) {
|
||||
h := unsafeHeader(req.Header).Get(name)
|
||||
for {
|
||||
pos := strings.Index(h, ",")
|
||||
if pos == -1 {
|
||||
return strings.EqualFold(value, strings.TrimSpace(h))
|
||||
}
|
||||
|
||||
if strings.EqualFold(value, strings.TrimSpace(h[:pos])) {
|
||||
return true
|
||||
}
|
||||
|
||||
h = h[pos+1:]
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
return containsHeader(connection, "upgrade") && containsHeader(upgrade, "websocket")
|
||||
}
|
||||
|
||||
@@ -110,7 +120,7 @@ func forwardedPort(req *http.Request) string {
|
||||
return port
|
||||
}
|
||||
|
||||
if req.Header.Get(xForwardedProto) == "https" || req.Header.Get(xForwardedProto) == "wss" {
|
||||
if unsafeHeader(req.Header).Get(xForwardedProto) == "https" || unsafeHeader(req.Header).Get(xForwardedProto) == "wss" {
|
||||
return "443"
|
||||
}
|
||||
|
||||
@@ -125,38 +135,38 @@ func (x *XForwarded) rewrite(outreq *http.Request) {
|
||||
if clientIP, _, err := net.SplitHostPort(outreq.RemoteAddr); err == nil {
|
||||
clientIP = removeIPv6Zone(clientIP)
|
||||
|
||||
if outreq.Header.Get(xRealIP) == "" {
|
||||
outreq.Header.Set(xRealIP, clientIP)
|
||||
if unsafeHeader(outreq.Header).Get(xRealIP) == "" {
|
||||
unsafeHeader(outreq.Header).Set(xRealIP, clientIP)
|
||||
}
|
||||
}
|
||||
|
||||
xfProto := outreq.Header.Get(xForwardedProto)
|
||||
xfProto := unsafeHeader(outreq.Header).Get(xForwardedProto)
|
||||
if xfProto == "" {
|
||||
if isWebsocketRequest(outreq) {
|
||||
if outreq.TLS != nil {
|
||||
outreq.Header.Set(xForwardedProto, "wss")
|
||||
unsafeHeader(outreq.Header).Set(xForwardedProto, "wss")
|
||||
} else {
|
||||
outreq.Header.Set(xForwardedProto, "ws")
|
||||
unsafeHeader(outreq.Header).Set(xForwardedProto, "ws")
|
||||
}
|
||||
} else {
|
||||
if outreq.TLS != nil {
|
||||
outreq.Header.Set(xForwardedProto, "https")
|
||||
unsafeHeader(outreq.Header).Set(xForwardedProto, "https")
|
||||
} else {
|
||||
outreq.Header.Set(xForwardedProto, "http")
|
||||
unsafeHeader(outreq.Header).Set(xForwardedProto, "http")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if xfPort := outreq.Header.Get(xForwardedPort); xfPort == "" {
|
||||
outreq.Header.Set(xForwardedPort, forwardedPort(outreq))
|
||||
if xfPort := unsafeHeader(outreq.Header).Get(xForwardedPort); xfPort == "" {
|
||||
unsafeHeader(outreq.Header).Set(xForwardedPort, forwardedPort(outreq))
|
||||
}
|
||||
|
||||
if xfHost := outreq.Header.Get(xForwardedHost); xfHost == "" && outreq.Host != "" {
|
||||
outreq.Header.Set(xForwardedHost, outreq.Host)
|
||||
if xfHost := unsafeHeader(outreq.Header).Get(xForwardedHost); xfHost == "" && outreq.Host != "" {
|
||||
unsafeHeader(outreq.Header).Set(xForwardedHost, outreq.Host)
|
||||
}
|
||||
|
||||
if x.hostname != "" {
|
||||
outreq.Header.Set(xForwardedServer, x.hostname)
|
||||
unsafeHeader(outreq.Header).Set(xForwardedServer, x.hostname)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,7 +174,7 @@ func (x *XForwarded) rewrite(outreq *http.Request) {
|
||||
func (x *XForwarded) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if !x.insecure && !x.isTrustedIP(r.RemoteAddr) {
|
||||
for _, h := range xHeaders {
|
||||
r.Header.Del(h)
|
||||
unsafeHeader(r.Header).Del(h)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,3 +182,22 @@ func (x *XForwarded) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
x.next.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
// unsafeHeader allows to manage Header values.
|
||||
// Must be used only when the header name is already a canonical key.
|
||||
type unsafeHeader map[string][]string
|
||||
|
||||
func (h unsafeHeader) Set(key, value string) {
|
||||
h[key] = []string{value}
|
||||
}
|
||||
|
||||
func (h unsafeHeader) Get(key string) string {
|
||||
if len(h[key]) == 0 {
|
||||
return ""
|
||||
}
|
||||
return h[key][0]
|
||||
}
|
||||
|
||||
func (h unsafeHeader) Del(key string) {
|
||||
delete(h, key)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package forwardedheaders
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -299,3 +300,71 @@ func TestServeHTTP(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_isWebsocketRequest(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
connectionHeader string
|
||||
upgradeHeader string
|
||||
assert assert.BoolAssertionFunc
|
||||
}{
|
||||
{
|
||||
desc: "connection Header multiple values middle",
|
||||
connectionHeader: "foo,upgrade,bar",
|
||||
upgradeHeader: "websocket",
|
||||
assert: assert.True,
|
||||
},
|
||||
{
|
||||
desc: "connection Header multiple values end",
|
||||
connectionHeader: "foo,bar,upgrade",
|
||||
upgradeHeader: "websocket",
|
||||
assert: assert.True,
|
||||
},
|
||||
{
|
||||
desc: "connection Header multiple values begin",
|
||||
connectionHeader: "upgrade,foo,bar",
|
||||
upgradeHeader: "websocket",
|
||||
assert: assert.True,
|
||||
},
|
||||
{
|
||||
desc: "connection Header no upgrade",
|
||||
connectionHeader: "foo,bar",
|
||||
upgradeHeader: "websocket",
|
||||
assert: assert.False,
|
||||
},
|
||||
{
|
||||
desc: "connection Header empty",
|
||||
connectionHeader: "",
|
||||
upgradeHeader: "websocket",
|
||||
assert: assert.False,
|
||||
},
|
||||
{
|
||||
desc: "no header values",
|
||||
connectionHeader: "foo,bar",
|
||||
upgradeHeader: "foo,bar",
|
||||
assert: assert.False,
|
||||
},
|
||||
{
|
||||
desc: "upgrade header multiple values",
|
||||
connectionHeader: "upgrade",
|
||||
upgradeHeader: "foo,bar,websocket",
|
||||
assert: assert.True,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "http://localhost", nil)
|
||||
|
||||
req.Header.Set(connection, test.connectionHeader)
|
||||
req.Header.Set(upgrade, test.upgradeHeader)
|
||||
|
||||
ok := isWebsocketRequest(req)
|
||||
|
||||
test.assert(t, ok)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,42 +10,41 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
typeName = "Recovery"
|
||||
typeName = "Recovery"
|
||||
middlewareName = "traefik-internal-recovery"
|
||||
)
|
||||
|
||||
type recovery struct {
|
||||
next http.Handler
|
||||
name string
|
||||
}
|
||||
|
||||
// New creates recovery middleware.
|
||||
func New(ctx context.Context, next http.Handler, name string) (http.Handler, error) {
|
||||
log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName)).Debug("Creating middleware")
|
||||
func New(ctx context.Context, next http.Handler) (http.Handler, error) {
|
||||
log.FromContext(middlewares.GetLoggerCtx(ctx, middlewareName, typeName)).Debug("Creating middleware")
|
||||
|
||||
return &recovery{
|
||||
next: next,
|
||||
name: name,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (re *recovery) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
defer recoverFunc(middlewares.GetLoggerCtx(req.Context(), re.name, typeName), rw, req)
|
||||
defer recoverFunc(rw, req)
|
||||
re.next.ServeHTTP(rw, req)
|
||||
}
|
||||
|
||||
func recoverFunc(ctx context.Context, rw http.ResponseWriter, r *http.Request) {
|
||||
func recoverFunc(rw http.ResponseWriter, r *http.Request) {
|
||||
if err := recover(); err != nil {
|
||||
logger := log.FromContext(middlewares.GetLoggerCtx(r.Context(), middlewareName, typeName))
|
||||
if !shouldLogPanic(err) {
|
||||
log.FromContext(ctx).Debugf("Request has been aborted [%s - %s]: %v", r.RemoteAddr, r.URL, err)
|
||||
logger.Debugf("Request has been aborted [%s - %s]: %v", r.RemoteAddr, r.URL, err)
|
||||
return
|
||||
}
|
||||
|
||||
log.FromContext(ctx).Errorf("Recovered from panic in HTTP handler [%s - %s]: %+v", r.RemoteAddr, r.URL, err)
|
||||
|
||||
logger.Errorf("Recovered from panic in HTTP handler [%s - %s]: %+v", r.RemoteAddr, r.URL, err)
|
||||
const size = 64 << 10
|
||||
buf := make([]byte, size)
|
||||
buf = buf[:runtime.Stack(buf, false)]
|
||||
log.FromContext(ctx).Errorf("Stack: %s", buf)
|
||||
logger.Errorf("Stack: %s", buf)
|
||||
|
||||
http.Error(rw, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ func TestRecoverHandler(t *testing.T) {
|
||||
fn := func(w http.ResponseWriter, r *http.Request) {
|
||||
panic("I love panicing!")
|
||||
}
|
||||
recovery, err := New(context.Background(), http.HandlerFunc(fn), "foo-recovery")
|
||||
recovery, err := New(context.Background(), http.HandlerFunc(fn))
|
||||
require.NoError(t, err)
|
||||
|
||||
server := httptest.NewServer(recovery)
|
||||
|
||||
@@ -33,6 +33,8 @@ const (
|
||||
pilotInstanceInfoTimer = 5 * time.Minute
|
||||
pilotDynConfTimer = 12 * time.Hour
|
||||
maxElapsedTime = 4 * time.Minute
|
||||
initialInterval = 5 * time.Second
|
||||
multiplier = 3
|
||||
)
|
||||
|
||||
type instanceInfo struct {
|
||||
@@ -219,6 +221,8 @@ func (c *client) SendInstanceInfo(ctx context.Context, pilotMetrics []metrics.Pi
|
||||
func (c *client) sendDataRetryable(ctx context.Context, req *http.Request) error {
|
||||
exponentialBackOff := backoff.NewExponentialBackOff()
|
||||
exponentialBackOff.MaxElapsedTime = maxElapsedTime
|
||||
exponentialBackOff.InitialInterval = initialInterval
|
||||
exponentialBackOff.Multiplier = multiplier
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set(tokenHashHeader, c.tokenHash)
|
||||
|
||||
@@ -8,13 +8,15 @@ import (
|
||||
"github.com/traefik/traefik/v2/pkg/log"
|
||||
"github.com/traefik/traefik/v2/pkg/provider"
|
||||
"github.com/traefik/traefik/v2/pkg/provider/file"
|
||||
"github.com/traefik/traefik/v2/pkg/provider/traefik"
|
||||
"github.com/traefik/traefik/v2/pkg/safe"
|
||||
)
|
||||
|
||||
// ProviderAggregator aggregates providers.
|
||||
type ProviderAggregator struct {
|
||||
fileProvider *file.Provider
|
||||
providers []provider.Provider
|
||||
internalProvider provider.Provider
|
||||
fileProvider provider.Provider
|
||||
providers []provider.Provider
|
||||
}
|
||||
|
||||
// NewProviderAggregator returns an aggregate of all the providers configured in the static configuration.
|
||||
@@ -98,11 +100,15 @@ func (p *ProviderAggregator) AddProvider(provider provider.Provider) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if fileProvider, ok := provider.(*file.Provider); ok {
|
||||
p.fileProvider = fileProvider
|
||||
} else {
|
||||
switch provider.(type) {
|
||||
case *file.Provider:
|
||||
p.fileProvider = provider
|
||||
case *traefik.Provider:
|
||||
p.internalProvider = provider
|
||||
default:
|
||||
p.providers = append(p.providers, provider)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -113,6 +119,10 @@ func (p ProviderAggregator) Init() error {
|
||||
|
||||
// Provide calls the provide method of every providers.
|
||||
func (p ProviderAggregator) Provide(configurationChan chan<- dynamic.Message, pool *safe.Pool) error {
|
||||
if p.internalProvider != nil {
|
||||
launchProvider(configurationChan, pool, p.internalProvider)
|
||||
}
|
||||
|
||||
if p.fileProvider != nil {
|
||||
launchProvider(configurationChan, pool, p.fileProvider)
|
||||
}
|
||||
@@ -123,6 +133,7 @@ func (p ProviderAggregator) Provide(configurationChan chan<- dynamic.Message, po
|
||||
launchProvider(configurationChan, pool, prd)
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
79
pkg/provider/aggregator/aggregator_test.go
Normal file
79
pkg/provider/aggregator/aggregator_test.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package aggregator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/traefik/traefik/v2/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v2/pkg/provider"
|
||||
"github.com/traefik/traefik/v2/pkg/safe"
|
||||
)
|
||||
|
||||
func TestProviderAggregator_Provide(t *testing.T) {
|
||||
aggregator := ProviderAggregator{
|
||||
internalProvider: &providerMock{"internal"},
|
||||
fileProvider: &providerMock{"file"},
|
||||
providers: []provider.Provider{
|
||||
&providerMock{"salad"},
|
||||
&providerMock{"tomato"},
|
||||
&providerMock{"onion"},
|
||||
},
|
||||
}
|
||||
|
||||
cfgCh := make(chan dynamic.Message)
|
||||
errCh := make(chan error)
|
||||
pool := safe.NewPool(context.Background())
|
||||
|
||||
t.Cleanup(pool.Stop)
|
||||
|
||||
go func() {
|
||||
errCh <- aggregator.Provide(cfgCh, pool)
|
||||
}()
|
||||
|
||||
// Make sure the internal provider is always called first, followed by the file provider.
|
||||
requireReceivedMessageFromProviders(t, cfgCh, []string{"internal"})
|
||||
requireReceivedMessageFromProviders(t, cfgCh, []string{"file"})
|
||||
|
||||
// Check if all providers have been called, the order doesn't matter.
|
||||
requireReceivedMessageFromProviders(t, cfgCh, []string{"salad", "tomato", "onion"})
|
||||
|
||||
require.NoError(t, <-errCh)
|
||||
}
|
||||
|
||||
// requireReceivedMessageFromProviders makes sure the given providers have emitted a message on the given message channel.
|
||||
// Providers order is not enforced.
|
||||
func requireReceivedMessageFromProviders(t *testing.T, cfgCh <-chan dynamic.Message, names []string) {
|
||||
t.Helper()
|
||||
|
||||
var msg dynamic.Message
|
||||
var receivedMessagesFrom []string
|
||||
|
||||
for range names {
|
||||
select {
|
||||
case <-time.After(10 * time.Millisecond):
|
||||
case msg = <-cfgCh:
|
||||
receivedMessagesFrom = append(receivedMessagesFrom, msg.ProviderName)
|
||||
}
|
||||
}
|
||||
|
||||
require.ElementsMatch(t, names, receivedMessagesFrom)
|
||||
}
|
||||
|
||||
type providerMock struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func (p *providerMock) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *providerMock) Provide(configurationChan chan<- dynamic.Message, pool *safe.Pool) error {
|
||||
configurationChan <- dynamic.Message{
|
||||
ProviderName: p.Name,
|
||||
Configuration: &dynamic.Configuration{},
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -42,7 +42,7 @@ type Provider struct {
|
||||
Constraints string `description:"Constraints is an expression that Traefik matches against the container's labels to determine whether to create any route for that container." json:"constraints,omitempty" toml:"constraints,omitempty" yaml:"constraints,omitempty" export:"true"`
|
||||
Endpoint *EndpointConfig `description:"Consul endpoint settings" json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty" export:"true"`
|
||||
Prefix string `description:"Prefix for consul service tags. Default 'traefik'" json:"prefix,omitempty" toml:"prefix,omitempty" yaml:"prefix,omitempty" export:"true"`
|
||||
RefreshInterval ptypes.Duration `description:"Interval for check Consul API. Default 100ms" json:"refreshInterval,omitempty" toml:"refreshInterval,omitempty" yaml:"refreshInterval,omitempty" export:"true"`
|
||||
RefreshInterval ptypes.Duration `description:"Interval for check Consul API. Default 15s" json:"refreshInterval,omitempty" toml:"refreshInterval,omitempty" yaml:"refreshInterval,omitempty" export:"true"`
|
||||
RequireConsistent bool `description:"Forces the read to be fully consistent." json:"requireConsistent,omitempty" toml:"requireConsistent,omitempty" yaml:"requireConsistent,omitempty" export:"true"`
|
||||
Stale bool `description:"Use stale consistency for catalog reads." json:"stale,omitempty" toml:"stale,omitempty" yaml:"stale,omitempty" export:"true"`
|
||||
Cache bool `description:"Use local agent caching for catalog reads." json:"cache,omitempty" toml:"cache,omitempty" yaml:"cache,omitempty" export:"true"`
|
||||
|
||||
@@ -56,7 +56,7 @@ func (reh *resourceEventHandler) OnDelete(obj interface{}) {
|
||||
type Client interface {
|
||||
WatchAll(namespaces []string, stopCh <-chan struct{}) (<-chan interface{}, error)
|
||||
GetIngresses() []*networkingv1beta1.Ingress
|
||||
GetIngressClass() (*networkingv1beta1.IngressClass, error)
|
||||
GetIngressClasses() ([]*networkingv1beta1.IngressClass, error)
|
||||
GetService(namespace, name string) (*corev1.Service, bool, error)
|
||||
GetSecret(namespace, name string) (*corev1.Secret, bool, error)
|
||||
GetEndpoints(namespace, name string) (*corev1.Endpoints, bool, error)
|
||||
@@ -393,9 +393,9 @@ func (c *clientWrapper) GetSecret(namespace, name string) (*corev1.Secret, bool,
|
||||
return secret, exist, err
|
||||
}
|
||||
|
||||
func (c *clientWrapper) GetIngressClass() (*networkingv1beta1.IngressClass, error) {
|
||||
func (c *clientWrapper) GetIngressClasses() ([]*networkingv1beta1.IngressClass, error) {
|
||||
if c.clusterFactory == nil {
|
||||
return nil, errors.New("failed to find ingressClass: factory not loaded")
|
||||
return nil, errors.New("cluster factory not loaded")
|
||||
}
|
||||
|
||||
ingressClasses, err := c.clusterFactory.Networking().V1beta1().IngressClasses().Lister().List(labels.Everything())
|
||||
@@ -403,13 +403,14 @@ func (c *clientWrapper) GetIngressClass() (*networkingv1beta1.IngressClass, erro
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ics []*networkingv1beta1.IngressClass
|
||||
for _, ic := range ingressClasses {
|
||||
if ic.Spec.Controller == traefikDefaultIngressClassController {
|
||||
return ic, nil
|
||||
ics = append(ics, ic)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
return ics, nil
|
||||
}
|
||||
|
||||
// lookupNamespace returns the lookup namespace key for the given namespace.
|
||||
|
||||
@@ -14,11 +14,11 @@ import (
|
||||
var _ Client = (*clientMock)(nil)
|
||||
|
||||
type clientMock struct {
|
||||
ingresses []*networkingv1beta1.Ingress
|
||||
services []*corev1.Service
|
||||
secrets []*corev1.Secret
|
||||
endpoints []*corev1.Endpoints
|
||||
ingressClass *networkingv1beta1.IngressClass
|
||||
ingresses []*networkingv1beta1.Ingress
|
||||
services []*corev1.Service
|
||||
secrets []*corev1.Secret
|
||||
endpoints []*corev1.Endpoints
|
||||
ingressClasses []*networkingv1beta1.IngressClass
|
||||
|
||||
serverVersion *version.Version
|
||||
|
||||
@@ -59,7 +59,7 @@ func newClientMock(serverVersion string, paths ...string) clientMock {
|
||||
}
|
||||
c.ingresses = append(c.ingresses, ing)
|
||||
case *networkingv1beta1.IngressClass:
|
||||
c.ingressClass = o
|
||||
c.ingressClasses = append(c.ingressClasses, o)
|
||||
default:
|
||||
panic(fmt.Sprintf("Unknown runtime object %+v %T", o, o))
|
||||
}
|
||||
@@ -117,8 +117,8 @@ func (c clientMock) GetSecret(namespace, name string) (*corev1.Secret, bool, err
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
func (c clientMock) GetIngressClass() (*networkingv1beta1.IngressClass, error) {
|
||||
return c.ingressClass, nil
|
||||
func (c clientMock) GetIngressClasses() ([]*networkingv1beta1.IngressClass, error) {
|
||||
return c.ingressClasses, nil
|
||||
}
|
||||
|
||||
func (c clientMock) WatchAll(namespaces []string, stopCh <-chan struct{}) (<-chan interface{}, error) {
|
||||
|
||||
@@ -8,7 +8,7 @@ spec:
|
||||
ports:
|
||||
- name: tchouk
|
||||
port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
---
|
||||
kind: Service
|
||||
@@ -21,4 +21,4 @@ spec:
|
||||
ports:
|
||||
- name: tchouk
|
||||
port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
@@ -7,7 +7,7 @@ metadata:
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
---
|
||||
kind: Service
|
||||
@@ -19,4 +19,4 @@ metadata:
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
@@ -7,4 +7,4 @@ metadata:
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
@@ -7,4 +7,4 @@ metadata:
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
@@ -7,4 +7,4 @@ metadata:
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
@@ -7,4 +7,4 @@ metadata:
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
@@ -8,7 +8,7 @@ spec:
|
||||
ports:
|
||||
- name: http
|
||||
port: 8080
|
||||
clusterIp: "fc00:f853:ccd:e793::1"
|
||||
clusterIP: "fc00:f853:ccd:e793::1"
|
||||
type: ClusterIP
|
||||
|
||||
---
|
||||
|
||||
@@ -7,4 +7,4 @@ metadata:
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
@@ -7,4 +7,4 @@ metadata:
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
@@ -8,4 +8,4 @@ spec:
|
||||
ports:
|
||||
- port: 443
|
||||
targetPort: 8443
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
@@ -9,4 +9,4 @@ spec:
|
||||
- name: https
|
||||
protocol: ""
|
||||
port: 8443
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
@@ -9,4 +9,4 @@ spec:
|
||||
- name: https-foo
|
||||
protocol: ""
|
||||
port: 8443
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
@@ -8,4 +8,4 @@ metadata:
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
@@ -17,4 +17,4 @@ metadata:
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
@@ -7,4 +7,4 @@ metadata:
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
@@ -7,4 +7,4 @@ metadata:
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
@@ -7,4 +7,4 @@ metadata:
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
@@ -7,4 +7,4 @@ metadata:
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
@@ -7,4 +7,4 @@ metadata:
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
@@ -8,5 +8,5 @@ spec:
|
||||
ports:
|
||||
- name: http
|
||||
port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
type: ClusterIP
|
||||
|
||||
@@ -7,4 +7,4 @@ metadata:
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
@@ -7,4 +7,4 @@ metadata:
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
@@ -7,4 +7,4 @@ metadata:
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
@@ -7,4 +7,4 @@ metadata:
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
@@ -10,5 +10,5 @@ spec:
|
||||
port: 8082
|
||||
- name: tchouk
|
||||
port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
|
||||
@@ -10,5 +10,5 @@ spec:
|
||||
port: 8082
|
||||
- name: tchouk
|
||||
port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ metadata:
|
||||
spec:
|
||||
ports:
|
||||
- port: 8080
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
type: ExternalName
|
||||
externalName: traefik.wtf
|
||||
|
||||
|
||||
@@ -7,4 +7,4 @@ metadata:
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
@@ -10,5 +10,5 @@ spec:
|
||||
port: 8082
|
||||
- name: tchouk
|
||||
port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ metadata:
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
---
|
||||
kind: Service
|
||||
@@ -19,4 +19,4 @@ metadata:
|
||||
spec:
|
||||
ports:
|
||||
- port: 8082
|
||||
clusterIp: 10.1.0.1
|
||||
clusterIP: 10.1.0.1
|
||||
|
||||
@@ -7,4 +7,4 @@ metadata:
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
@@ -7,4 +7,4 @@ metadata:
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
@@ -8,4 +8,4 @@ metadata:
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
@@ -10,5 +10,5 @@ spec:
|
||||
port: 8082
|
||||
- name: tchouk
|
||||
port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
|
||||
@@ -7,4 +7,4 @@ metadata:
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
@@ -7,4 +7,4 @@ metadata:
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
@@ -8,7 +8,7 @@ spec:
|
||||
ports:
|
||||
- name: http
|
||||
port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
type: ClusterIP
|
||||
|
||||
---
|
||||
@@ -22,5 +22,5 @@ spec:
|
||||
ports:
|
||||
- name: http
|
||||
port: 80
|
||||
clusterIp: 10.0.0.2
|
||||
clusterIP: 10.0.0.2
|
||||
type: ClusterIP
|
||||
|
||||
@@ -7,4 +7,4 @@ metadata:
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
@@ -7,4 +7,4 @@ metadata:
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
@@ -7,4 +7,4 @@ metadata:
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
@@ -7,4 +7,4 @@ metadata:
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
@@ -7,4 +7,4 @@ metadata:
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
@@ -7,4 +7,4 @@ metadata:
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
kind: Endpoints
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: service1
|
||||
namespace: testing
|
||||
|
||||
subsets:
|
||||
- addresses:
|
||||
- ip: 10.10.0.1
|
||||
ports:
|
||||
- port: 8080
|
||||
@@ -0,0 +1,30 @@
|
||||
kind: Ingress
|
||||
apiVersion: networking.k8s.io/v1beta1
|
||||
metadata:
|
||||
name: ""
|
||||
namespace: testing
|
||||
spec:
|
||||
ingressClassName: traefik-lb
|
||||
rules:
|
||||
- http:
|
||||
paths:
|
||||
- path: /bar
|
||||
backend:
|
||||
serviceName: service1
|
||||
servicePort: 80
|
||||
|
||||
---
|
||||
kind: Ingress
|
||||
apiVersion: networking.k8s.io/v1beta1
|
||||
metadata:
|
||||
name: ""
|
||||
namespace: testing
|
||||
spec:
|
||||
ingressClassName: traefik-lb2
|
||||
rules:
|
||||
- http:
|
||||
paths:
|
||||
- path: /foo
|
||||
backend:
|
||||
serviceName: service1
|
||||
servicePort: 80
|
||||
@@ -0,0 +1,14 @@
|
||||
apiVersion: networking.k8s.io/v1beta1
|
||||
kind: IngressClass
|
||||
metadata:
|
||||
name: traefik-lb2
|
||||
spec:
|
||||
controller: traefik.io/ingress-controller
|
||||
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1beta1
|
||||
kind: IngressClass
|
||||
metadata:
|
||||
name: traefik-lb
|
||||
spec:
|
||||
controller: traefik.io/ingress-controller
|
||||
@@ -0,0 +1,10 @@
|
||||
kind: Service
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: service1
|
||||
namespace: testing
|
||||
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
clusterIP: 10.0.0.1
|
||||
@@ -7,4 +7,4 @@ metadata:
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
@@ -7,4 +7,4 @@ metadata:
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
clusterIp: 10.0.0.1
|
||||
clusterIP: 10.0.0.1
|
||||
|
||||
@@ -190,15 +190,15 @@ func (p *Provider) loadConfigurationFromIngresses(ctx context.Context, client Cl
|
||||
return conf
|
||||
}
|
||||
|
||||
var ingressClass *networkingv1beta1.IngressClass
|
||||
var ingressClasses []*networkingv1beta1.IngressClass
|
||||
|
||||
if supportsIngressClass(serverVersion) {
|
||||
ic, err := client.GetIngressClass()
|
||||
ics, err := client.GetIngressClasses()
|
||||
if err != nil {
|
||||
log.FromContext(ctx).Warnf("Failed to find an ingress class: %v", err)
|
||||
log.FromContext(ctx).Warnf("Failed to list ingress classes: %v", err)
|
||||
}
|
||||
|
||||
ingressClass = ic
|
||||
ingressClasses = ics
|
||||
}
|
||||
|
||||
ingresses := client.GetIngresses()
|
||||
@@ -207,7 +207,7 @@ func (p *Provider) loadConfigurationFromIngresses(ctx context.Context, client Cl
|
||||
for _, ingress := range ingresses {
|
||||
ctx = log.With(ctx, log.Str("ingress", ingress.Name), log.Str("namespace", ingress.Namespace))
|
||||
|
||||
if !p.shouldProcessIngress(p.IngressClass, ingress, ingressClass) {
|
||||
if !p.shouldProcessIngress(ingress, ingressClasses) {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -351,14 +351,20 @@ func (p *Provider) updateIngressStatus(ing *networkingv1beta1.Ingress, k8sClient
|
||||
return k8sClient.UpdateIngressStatus(ing, service.Status.LoadBalancer.Ingress)
|
||||
}
|
||||
|
||||
func (p *Provider) shouldProcessIngress(providerIngressClass string, ingress *networkingv1beta1.Ingress, ingressClass *networkingv1beta1.IngressClass) bool {
|
||||
func (p *Provider) shouldProcessIngress(ingress *networkingv1beta1.Ingress, ingressClasses []*networkingv1beta1.IngressClass) bool {
|
||||
// configuration through the new kubernetes ingressClass
|
||||
if ingress.Spec.IngressClassName != nil {
|
||||
return ingressClass != nil && ingressClass.ObjectMeta.Name == *ingress.Spec.IngressClassName
|
||||
for _, ic := range ingressClasses {
|
||||
if *ingress.Spec.IngressClassName == ic.ObjectMeta.Name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
return providerIngressClass == ingress.Annotations[annotationKubernetesIngressClass] ||
|
||||
len(providerIngressClass) == 0 && ingress.Annotations[annotationKubernetesIngressClass] == traefikDefaultIngressClass
|
||||
return p.IngressClass == ingress.Annotations[annotationKubernetesIngressClass] ||
|
||||
len(p.IngressClass) == 0 && ingress.Annotations[annotationKubernetesIngressClass] == traefikDefaultIngressClass
|
||||
}
|
||||
|
||||
func buildHostRule(host string) string {
|
||||
|
||||
@@ -1061,6 +1061,38 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "v18 Ingress with multiple ingressClasses",
|
||||
serverVersion: "v1.18",
|
||||
expected: &dynamic.Configuration{
|
||||
TCP: &dynamic.TCPConfiguration{},
|
||||
HTTP: &dynamic.HTTPConfiguration{
|
||||
Middlewares: map[string]*dynamic.Middleware{},
|
||||
Routers: map[string]*dynamic.Router{
|
||||
"testing-foo": {
|
||||
Rule: "PathPrefix(`/foo`)",
|
||||
Service: "testing-service1-80",
|
||||
},
|
||||
"testing-bar": {
|
||||
Rule: "PathPrefix(`/bar`)",
|
||||
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",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "v18 Ingress with no pathType",
|
||||
serverVersion: "v1.18",
|
||||
|
||||
@@ -16,10 +16,6 @@ import (
|
||||
"github.com/traefik/traefik/v2/pkg/server/provider"
|
||||
)
|
||||
|
||||
const (
|
||||
recoveryMiddlewareName = "traefik-internal-recovery"
|
||||
)
|
||||
|
||||
type middlewareBuilder interface {
|
||||
BuildChain(ctx context.Context, names []string) *alice.Chain
|
||||
}
|
||||
@@ -130,7 +126,7 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
|
||||
|
||||
chain := alice.New()
|
||||
chain = chain.Append(func(next http.Handler) (http.Handler, error) {
|
||||
return recovery.New(ctx, next, recoveryMiddlewareName)
|
||||
return recovery.New(ctx, next)
|
||||
})
|
||||
|
||||
return chain.Then(router)
|
||||
|
||||
@@ -4,11 +4,11 @@ RepositoryName = "traefik"
|
||||
OutputType = "file"
|
||||
FileName = "traefik_changelog.md"
|
||||
|
||||
# example new bugfix v2.3.7
|
||||
CurrentRef = "v2.3"
|
||||
PreviousRef = "v2.3.6"
|
||||
BaseBranch = "v2.3"
|
||||
FutureCurrentRefName = "v2.3.7"
|
||||
# example new bugfix v2.4.2
|
||||
CurrentRef = "v2.4"
|
||||
PreviousRef = "v2.4.1"
|
||||
BaseBranch = "v2.4"
|
||||
FutureCurrentRefName = "v2.4.2"
|
||||
|
||||
ThresholdPreviousRef = 10
|
||||
ThresholdCurrentRef = 10
|
||||
|
||||
Reference in New Issue
Block a user