forked from SW/traefik
Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5afa78e18e | ||
|
|
cfa04c300c | ||
|
|
cb6b94ff40 | ||
|
|
0b2a6cecd1 | ||
|
|
fee89273a3 | ||
|
|
463e2285d6 | ||
|
|
c65277a6f0 | ||
|
|
bb14c3e812 | ||
|
|
85580dde3c | ||
|
|
4bb6d1bf4f | ||
|
|
459cabca8e | ||
|
|
dcca37fe29 | ||
|
|
a610f0b2a1 | ||
|
|
ded285be29 | ||
|
|
4b31d3306b | ||
|
|
5a6652fc20 | ||
|
|
91b0e2e4dc | ||
|
|
554bceb75b | ||
|
|
ce76212605 | ||
|
|
d474c87cde | ||
|
|
862772230c | ||
|
|
09fa1e6546 | ||
|
|
c3125104d9 | ||
|
|
31786f7163 | ||
|
|
d7ed42d614 | ||
|
|
4c709c2fcd | ||
|
|
8dac076711 | ||
|
|
0b039499c6 | ||
|
|
2124975432 | ||
|
|
e4725619ea | ||
|
|
d24f78222c | ||
|
|
3f1925e20e |
@@ -2,19 +2,19 @@
|
||||
|
||||
set -e
|
||||
|
||||
curl -O https://dl.google.com/go/go1.12.linux-amd64.tar.gz
|
||||
curl -O https://dl.google.com/go/go1.14.linux-amd64.tar.gz
|
||||
|
||||
tar -xvf go1.12.linux-amd64.tar.gz
|
||||
rm -rf go1.12.linux-amd64.tar.gz
|
||||
tar -xvf go1.14.linux-amd64.tar.gz
|
||||
rm -rf go1.14.linux-amd64.tar.gz
|
||||
|
||||
sudo mkdir -p /usr/local/golang/1.12/go
|
||||
sudo mv go /usr/local/golang/1.12/
|
||||
sudo mkdir -p /usr/local/golang/1.14/go
|
||||
sudo mv go /usr/local/golang/1.14/
|
||||
|
||||
sudo rm /usr/local/bin/go
|
||||
sudo chmod +x /usr/local/golang/1.12/go/bin/go
|
||||
sudo ln -s /usr/local/golang/1.12/go/bin/go /usr/local/bin/go
|
||||
sudo chmod +x /usr/local/golang/1.14/go/bin/go
|
||||
sudo ln -s /usr/local/golang/1.14/go/bin/go /usr/local/bin/go
|
||||
|
||||
export GOROOT="/usr/local/golang/1.12/go"
|
||||
export GOTOOLDIR="/usr/local/golang/1.12/go/pkg/tool/linux_amd64"
|
||||
export GOROOT="/usr/local/golang/1.14/go"
|
||||
export GOTOOLDIR="/usr/local/golang/1.14/go/pkg/tool/linux_amd64"
|
||||
|
||||
go version
|
||||
|
||||
62
CHANGELOG.md
62
CHANGELOG.md
@@ -1,5 +1,67 @@
|
||||
# Change Log
|
||||
|
||||
## [v1.7.26](https://github.com/containous/traefik/tree/v1.7.26) (2020-07-28)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.7.25...v1.7.26)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[webui]** fix: clean X-Forwarded-Prefix header for the dashboard. ([#7108](https://github.com/containous/traefik/pull/7108) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Documentation:**
|
||||
- **[k8s,k8s/ingress]** Change apiVersion to apps/v1 in examples/k8s/cheese-deployments.yaml ([#7090](https://github.com/containous/traefik/pull/7090) by [claflico](https://github.com/claflico))
|
||||
|
||||
## [v1.7.25](https://github.com/containous/traefik/tree/v1.7.25) (2020-07-15)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.7.24...v1.7.25)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[middleware]** Fix ipv6 handling in redirect middleware ([#6901](https://github.com/containous/traefik/pull/6901) by [rtribotte](https://github.com/rtribotte))
|
||||
- **[provider]** Backport "Same Configuration Check" from master ([#6631](https://github.com/containous/traefik/pull/6631) by [rkojedzinszky](https://github.com/rkojedzinszky))
|
||||
|
||||
**Documentation:**
|
||||
- **[k8s]** Add ingress setup for minikube in docs ([#6552](https://github.com/containous/traefik/pull/6552) by [kobayashi](https://github.com/kobayashi))
|
||||
- Doc improvement on entrypoints whitelist for v1.7 ([#6571](https://github.com/containous/traefik/pull/6571) by [ddtmachado](https://github.com/ddtmachado))
|
||||
|
||||
## [v1.7.24](https://github.com/containous/traefik/tree/v1.7.24) (2020-03-25)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.7.23...v1.7.24)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[k8s/ingress]** fix: stickiness annotations support. ([#6576](https://github.com/containous/traefik/pull/6576) by [ldez](https://github.com/ldez))
|
||||
|
||||
## [v1.7.23](https://github.com/containous/traefik/tree/v1.7.23) (2020-03-23)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.7.22...v1.7.23)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[consul,consulcatalog,docker,ecs,k8s,marathon,mesos,rancher,sticky-session]** Fix sameSite ([#6537](https://github.com/containous/traefik/pull/6537) by [ldez](https://github.com/ldez))
|
||||
- **[server]** Force http/1.1 for upgrade ([#6553](https://github.com/containous/traefik/pull/6553) by [juliens](https://github.com/juliens))
|
||||
- **[tls]** fix: max TLS version. ([#6531](https://github.com/containous/traefik/pull/6531) by [ldez](https://github.com/ldez))
|
||||
|
||||
## [v1.7.22](https://github.com/containous/traefik/tree/v1.7.22) (2020-03-09)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.7.21...v1.7.22)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[provider]** Skip redirection with invalid regex syntax. ([#6446](https://github.com/containous/traefik/pull/6446) by [ldez](https://github.com/ldez))
|
||||
- **[server]** Clear closed hijacked h2c connections ([#6357](https://github.com/containous/traefik/pull/6357) by [omerkay](https://github.com/omerkay))
|
||||
|
||||
**Documentation:**
|
||||
- **[acme]** fix: manual provider code name. ([#6456](https://github.com/containous/traefik/pull/6456) by [ldez](https://github.com/ldez))
|
||||
- **[docker]** Fix typo in user guide. ([#6386](https://github.com/containous/traefik/pull/6386) by [ldez](https://github.com/ldez))
|
||||
- **[k8s]** Complete TLS example for Kubernetes Ingress in user guide ([#6457](https://github.com/containous/traefik/pull/6457) by [rtribotte](https://github.com/rtribotte))
|
||||
- **[k8s]** Updated rbac and Daemonset example bloc ([#6375](https://github.com/containous/traefik/pull/6375) by [bjthomas1](https://github.com/bjthomas1))
|
||||
|
||||
## [v1.7.21](https://github.com/containous/traefik/tree/v1.7.21) (2020-02-20)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.7.20...v1.7.21)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Fix dnspod update. ([#6240](https://github.com/containous/traefik/pull/6240) by [iineva](https://github.com/iineva))
|
||||
- **[acme]** Fix finding proper provided certificate when ACME is enabled ([#5873](https://github.com/containous/traefik/pull/5873) by [yazd](https://github.com/yazd))
|
||||
- **[authentication,middleware]** don't create http client in each request in forward auth ([#6273](https://github.com/containous/traefik/pull/6273) by [juliens](https://github.com/juliens))
|
||||
- **[ecs]** fix: skip ECS container when information are missing instead of panic. ([#6071](https://github.com/containous/traefik/pull/6071) by [ldez](https://github.com/ldez))
|
||||
- **[k8s]** Add edge case for root path with rewrite-target ([#6005](https://github.com/containous/traefik/pull/6005) by [dtomcej](https://github.com/dtomcej))
|
||||
|
||||
**Documentation:**
|
||||
- **[k8s,k8s/ingress]** Update the k8s api version in the documentation ([#6162](https://github.com/containous/traefik/pull/6162) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- **[middleware]** Improve rate-limiting doc ([#6277](https://github.com/containous/traefik/pull/6277) by [mpl](https://github.com/mpl))
|
||||
- **[provider]** Fix sample for ssl-header in docs ([#6337](https://github.com/containous/traefik/pull/6337) by [pierresteiner](https://github.com/pierresteiner))
|
||||
|
||||
## [v1.7.20](https://github.com/containous/traefik/tree/v1.7.20) (2019-12-09)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.7.19...v1.7.20)
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ You need to run the `binary` target. This will create binaries for Linux platfor
|
||||
$ make binary
|
||||
docker build -t "traefik-dev:no-more-godep-ever" -f build.Dockerfile .
|
||||
Sending build context to Docker daemon 295.3 MB
|
||||
Step 0 : FROM golang:1.12-alpine
|
||||
Step 0 : FROM golang:1.14-alpine
|
||||
---> 8c6473912976
|
||||
Step 1 : RUN go get github.com/golang/dep/cmd/dep
|
||||
[...]
|
||||
|
||||
11
Gopkg.lock
generated
11
Gopkg.lock
generated
@@ -477,12 +477,11 @@
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:fa62421bd924623ac10a160686cc55d529f7274b2caedf7d2c607d14bc50c118"
|
||||
digest = "1:295f7aed734ab28daff7c0df7db42bcccf7a043d4e0a5daf5768129346b25f23"
|
||||
name = "github.com/decker502/dnspod-go"
|
||||
packages = ["."]
|
||||
pruneopts = "NUT"
|
||||
revision = "71fbbdbdf1a7eeac949586de15bf96d416d3dd63"
|
||||
version = "v0.2.0"
|
||||
revision = "071171b22a9b65e4f544b61c143befd54a08a64e"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:7a6852b35eb5bbc184561443762d225116ae630c26a7c4d90546619f1e7d2ad2"
|
||||
@@ -1938,8 +1937,7 @@
|
||||
version = "v0.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:86f14aadf288fe3ad8ac060bcb2b5083cec3829dd883803486ec834d031060c9"
|
||||
digest = "1:a31155114d2977ef48d2cca6810c4e055545d676bf13b604c2debd42de44cad0"
|
||||
name = "github.com/vulcand/oxy"
|
||||
packages = [
|
||||
"buffer",
|
||||
@@ -1952,7 +1950,8 @@
|
||||
"utils",
|
||||
]
|
||||
pruneopts = "NUT"
|
||||
revision = "0d102f45103cf49a95b5c6e810e092973cbcb68c"
|
||||
revision = "9dbd22c030f2187f590fea61ebe84ab78a8146a2"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:ca6bac407fedc14fbeeba861dd33a821ba3a1624c10126ec6003b0a28d4139c5"
|
||||
|
||||
@@ -175,7 +175,7 @@
|
||||
version = "0.1.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
version = "v1.1.0"
|
||||
name = "github.com/vulcand/oxy"
|
||||
|
||||
[[constraint]]
|
||||
@@ -272,3 +272,7 @@
|
||||
[[override]]
|
||||
name = "contrib.go.opencensus.io/exporter/ocagent"
|
||||
version = "0.4.12"
|
||||
|
||||
[[override]]
|
||||
name = "github.com/decker502/dnspod-go"
|
||||
revision = "071171b22a9b65e4f544b61c143befd54a08a64e"
|
||||
|
||||
@@ -2,6 +2,7 @@ package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/containous/mux"
|
||||
"github.com/containous/traefik/log"
|
||||
@@ -23,17 +24,35 @@ func (g DashboardHandler) AddRoutes(router *mux.Router) {
|
||||
// Expose dashboard
|
||||
router.Methods(http.MethodGet).
|
||||
Path("/").
|
||||
HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
|
||||
http.Redirect(response, request, request.Header.Get("X-Forwarded-Prefix")+"/dashboard/", 302)
|
||||
HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||
http.Redirect(resp, req, safePrefix(req)+"/dashboard/", 302)
|
||||
})
|
||||
|
||||
router.Methods(http.MethodGet).
|
||||
Path("/dashboard/status").
|
||||
HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
|
||||
http.Redirect(response, request, "/dashboard/", 302)
|
||||
HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||
http.Redirect(resp, req, "/dashboard/", 302)
|
||||
})
|
||||
|
||||
router.Methods(http.MethodGet).
|
||||
PathPrefix("/dashboard/").
|
||||
Handler(http.StripPrefix("/dashboard/", http.FileServer(g.Assets)))
|
||||
}
|
||||
|
||||
func safePrefix(req *http.Request) string {
|
||||
prefix := req.Header.Get("X-Forwarded-Prefix")
|
||||
if prefix == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
parse, err := url.Parse(prefix)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if parse.Host != "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
return parse.Path
|
||||
}
|
||||
|
||||
54
api/dashboard_test.go
Normal file
54
api/dashboard_test.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_safePrefix(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
value string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "host",
|
||||
value: "https://example.com",
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
desc: "host with path",
|
||||
value: "https://example.com/foo/bar?test",
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
desc: "path",
|
||||
value: "/foo/bar",
|
||||
expected: "/foo/bar",
|
||||
},
|
||||
{
|
||||
desc: "path without leading slash",
|
||||
value: "foo/bar",
|
||||
expected: "foo/bar",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "http://localhost", nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
req.Header.Set("X-Forwarded-Prefix", test.value)
|
||||
|
||||
prefix := safePrefix(req)
|
||||
|
||||
assert.Equal(t, test.expected, prefix)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -159,6 +159,9 @@ var _templatesConsul_catalogTmpl = []byte(`[backends]
|
||||
{{if $loadBalancer.Stickiness }}
|
||||
[backends."backend-{{ $backendName }}".loadBalancer.stickiness]
|
||||
cookieName = "{{ $loadBalancer.Stickiness.CookieName }}"
|
||||
secure = {{ $loadBalancer.Stickiness.Secure }}
|
||||
httpOnly = {{ $loadBalancer.Stickiness.HTTPOnly }}
|
||||
sameSite = "{{ $loadBalancer.Stickiness.SameSite }}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
@@ -654,6 +657,9 @@ var _templatesDockerTmpl = []byte(`{{$backendServers := .Servers}}
|
||||
{{if $loadBalancer.Stickiness }}
|
||||
[backends."backend-{{ $backendName }}".loadBalancer.stickiness]
|
||||
cookieName = "{{ $loadBalancer.Stickiness.CookieName }}"
|
||||
secure = {{ $loadBalancer.Stickiness.Secure }}
|
||||
httpOnly = {{ $loadBalancer.Stickiness.HTTPOnly }}
|
||||
sameSite = "{{ $loadBalancer.Stickiness.SameSite }}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
@@ -1000,6 +1006,9 @@ var _templatesEcsTmpl = []byte(`[backends]
|
||||
{{if $loadBalancer.Stickiness }}
|
||||
[backends."backend-{{ $serviceName }}".loadBalancer.stickiness]
|
||||
cookieName = "{{ $loadBalancer.Stickiness.CookieName }}"
|
||||
secure = {{ $loadBalancer.Stickiness.Secure }}
|
||||
httpOnly = {{ $loadBalancer.Stickiness.HTTPOnly }}
|
||||
sameSite = "{{ $loadBalancer.Stickiness.SameSite }}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
@@ -1325,6 +1334,9 @@ var _templatesKubernetesTmpl = []byte(`[backends]
|
||||
{{if $backend.LoadBalancer.Stickiness }}
|
||||
[backends."{{ $backendName }}".loadBalancer.stickiness]
|
||||
cookieName = "{{ $backend.LoadBalancer.Stickiness.CookieName }}"
|
||||
secure = {{ $backend.LoadBalancer.Stickiness.Secure }}
|
||||
httpOnly = {{ $backend.LoadBalancer.Stickiness.HTTPOnly }}
|
||||
sameSite = "{{ $backend.LoadBalancer.Stickiness.SameSite }}"
|
||||
{{end}}
|
||||
|
||||
{{if $backend.MaxConn }}
|
||||
@@ -1582,6 +1594,9 @@ var _templatesKvTmpl = []byte(`[backends]
|
||||
{{if $loadBalancer.Stickiness }}
|
||||
[backends."{{ $backendName }}".loadBalancer.stickiness]
|
||||
cookieName = "{{ $loadBalancer.Stickiness.CookieName }}"
|
||||
secure = {{ $loadBalancer.Stickiness.Secure }}
|
||||
httpOnly = {{ $loadBalancer.Stickiness.HTTPOnly }}
|
||||
sameSite = "{{ $loadBalancer.Stickiness.SameSite }}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
@@ -1970,6 +1985,9 @@ var _templatesMarathonTmpl = []byte(`{{ $apps := .Applications }}
|
||||
{{if $loadBalancer.Stickiness }}
|
||||
[backends."{{ $backendName }}".loadBalancer.stickiness]
|
||||
cookieName = "{{ $loadBalancer.Stickiness.CookieName }}"
|
||||
secure = {{ $loadBalancer.Stickiness.Secure }}
|
||||
httpOnly = {{ $loadBalancer.Stickiness.HTTPOnly }}
|
||||
sameSite = "{{ $loadBalancer.Stickiness.SameSite }}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
@@ -2302,6 +2320,9 @@ var _templatesMesosTmpl = []byte(`[backends]
|
||||
{{if $loadBalancer.Stickiness }}
|
||||
[backends."backend-{{ $backendName }}".loadBalancer.stickiness]
|
||||
cookieName = "{{ $loadBalancer.Stickiness.CookieName }}"
|
||||
secure = {{ $loadBalancer.Stickiness.Secure }}
|
||||
httpOnly = {{ $loadBalancer.Stickiness.HTTPOnly }}
|
||||
sameSite = "{{ $loadBalancer.Stickiness.SameSite }}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
@@ -2688,6 +2709,9 @@ var _templatesRancherTmpl = []byte(`{{ $backendServers := .Backends }}
|
||||
{{if $loadBalancer.Stickiness }}
|
||||
[backends."backend-{{ $backendName }}".loadBalancer.stickiness]
|
||||
cookieName = "{{ $loadBalancer.Stickiness.CookieName }}"
|
||||
secure = {{ $loadBalancer.Stickiness.Secure }}
|
||||
httpOnly = {{ $loadBalancer.Stickiness.HTTPOnly }}
|
||||
sameSite = "{{ $loadBalancer.Stickiness.SameSite }}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.12-alpine
|
||||
FROM golang:1.14-alpine
|
||||
|
||||
RUN apk --update upgrade \
|
||||
&& apk --no-cache --no-progress add git mercurial bash gcc musl-dev curl tar ca-certificates tzdata \
|
||||
|
||||
@@ -212,12 +212,6 @@ func (gc *GlobalConfiguration) SetEffectiveConfiguration(configFile string) {
|
||||
}
|
||||
}
|
||||
|
||||
// Thanks to SSLv3 being enabled by mistake in golang 1.12,
|
||||
// If no minVersion is set, apply TLS1.0 as the minimum.
|
||||
if entryPoint.TLS != nil && len(entryPoint.TLS.MinVersion) == 0 {
|
||||
entryPoint.TLS.MinVersion = "VersionTLS10"
|
||||
}
|
||||
|
||||
if entryPoint.TLS != nil && entryPoint.TLS.DefaultCertificate == nil && len(entryPoint.TLS.Certificates) > 0 {
|
||||
log.Infof("No tls.defaultCertificate given for %s: using the first item in tls.certificates as a fallback.", entryPointName)
|
||||
entryPoint.TLS.DefaultCertificate = &entryPoint.TLS.Certificates[0]
|
||||
|
||||
@@ -312,9 +312,7 @@ func TestSetEffectiveConfigurationTLSMinVersion(t *testing.T) {
|
||||
expected: EntryPoint{
|
||||
Address: ":443",
|
||||
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||
TLS: &tls.TLS{
|
||||
MinVersion: "VersionTLS10",
|
||||
},
|
||||
TLS: &tls.TLS{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -452,6 +452,27 @@ If not, a new backend will be assigned.
|
||||
# Default: a sha1 (6 chars)
|
||||
#
|
||||
# cookieName = "my_cookie"
|
||||
|
||||
# Customize secure option
|
||||
#
|
||||
# Optional
|
||||
# Default: false
|
||||
#
|
||||
# secure = true
|
||||
|
||||
# Customize http only option
|
||||
#
|
||||
# Optional
|
||||
# Default: false
|
||||
#
|
||||
# httpOnly = true
|
||||
|
||||
# Customize same site option.
|
||||
# Can be: "none", "lax", "strict"
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
# sameSite = "none"
|
||||
```
|
||||
|
||||
The deprecated way:
|
||||
|
||||
@@ -313,7 +313,7 @@ For example, `CF_API_EMAIL_FILE=/run/secrets/traefik_cf-api-email` could be used
|
||||
| [Lightsail](https://aws.amazon.com/lightsail/) | `lightsail` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `DNS_ZONE` | Not tested yet |
|
||||
| [Linode](https://www.linode.com) | `linode` | `LINODE_API_KEY` | Not tested yet |
|
||||
| [Linode v4](https://www.linode.com) | `linodev4` | `LINODE_TOKEN` | Not tested yet |
|
||||
| manual | - | none, but you need to run Traefik interactively, turn on `acmeLogging` to see instructions and press <kbd>Enter</kbd>. | YES |
|
||||
| manual | `manual` | none, but you need to run Traefik interactively, turn on `acmeLogging` to see instructions and press <kbd>Enter</kbd>. | YES |
|
||||
| [MyDNS.jp](https://www.mydns.jp/) | `mydnsjp` | `MYDNSJP_MASTER_ID`, `MYDNSJP_PASSWORD` | YES |
|
||||
| [Namecheap](https://www.namecheap.com) | `namecheap` | `NAMECHEAP_API_USER`, `NAMECHEAP_API_KEY` | YES |
|
||||
| [Namesilo](https://www.namesilo.com/) | `namesilo` | `NAMESILO_API_KEY` | YES |
|
||||
|
||||
@@ -124,6 +124,9 @@ Additional settings can be defined using Consul Catalog tags.
|
||||
| `<prefix>.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm. |
|
||||
| `<prefix>.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions. |
|
||||
| `<prefix>.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie name manually for sticky sessions. |
|
||||
| `<prefix>.backend.loadbalancer.stickiness.secure=true` | Sets secure cookie option for sticky sessions. |
|
||||
| `<prefix>.backend.loadbalancer.stickiness.httpOnly=true` | Sets http only cookie option for sticky sessions. |
|
||||
| `<prefix>.backend.loadbalancer.stickiness.sameSite=none` | Sets same site cookie option for sticky sessions. (`none`, `lax`, `strict`) |
|
||||
| `<prefix>.backend.loadbalancer.sticky=true` | Enables backend sticky sessions. (DEPRECATED) |
|
||||
| `<prefix>.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
|
||||
| `<prefix>.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
|
||||
@@ -224,7 +227,7 @@ If you need to support multiple frontends for a service, for example when having
|
||||
| `<prefix>.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
|
||||
| `<prefix>.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
|
||||
| `<prefix>.frontend.headers.SSLForceHost=true` | If `SSLForceHost` is `true` and `SSLHost` is set, requests will be forced to use `SSLHost` even the ones that are already using SSL. Default is false. |
|
||||
| `<prefix>.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`).<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `<prefix>.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-Proto:https`).<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `<prefix>.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
|
||||
| `<prefix>.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
|
||||
| `<prefix>.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
|
||||
|
||||
@@ -313,6 +313,9 @@ Labels can be used on containers to override default behavior.
|
||||
| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm |
|
||||
| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions |
|
||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie name manually for sticky sessions |
|
||||
| `traefik.backend.loadbalancer.stickiness.secure=true` | Sets secure cookie option for sticky sessions. |
|
||||
| `traefik.backend.loadbalancer.stickiness.httpOnly=true` | Sets http only cookie option for sticky sessions. |
|
||||
| `traefik.backend.loadbalancer.stickiness.sameSite=none` | Sets same site cookie option for sticky sessions. (`none`, `lax`, `strict`) |
|
||||
| `traefik.backend.loadbalancer.sticky=true` | Enables backend sticky sessions (DEPRECATED) |
|
||||
| `traefik.backend.loadbalancer.swarm=true` | Uses Swarm's inbuilt load balancer (only relevant under Swarm Mode) [3]. |
|
||||
| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
|
||||
@@ -412,7 +415,7 @@ It also means that Traefik will manipulate only one backend, not one backend per
|
||||
| `traefik.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
|
||||
| `traefik.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
|
||||
| `traefik.frontend.headers.SSLForceHost=true` | If `SSLForceHost` is `true` and `SSLHost` is set, requests will be forced to use `SSLHost` even the ones that are already using SSL. Default is false. |
|
||||
| `traefik.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`).<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-Proto:https`).<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
|
||||
| `traefik.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
|
||||
| `traefik.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
|
||||
|
||||
@@ -160,6 +160,9 @@ Labels can be used on task containers to override default behaviour:
|
||||
| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm |
|
||||
| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions |
|
||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie manually name for sticky sessions |
|
||||
| `traefik.backend.loadbalancer.stickiness.secure=true` | Sets secure cookie option for sticky sessions. |
|
||||
| `traefik.backend.loadbalancer.stickiness.httpOnly=true` | Sets http only cookie option for sticky sessions. |
|
||||
| `traefik.backend.loadbalancer.stickiness.sameSite=none` | Sets same site cookie option for sticky sessions. (`none`, `lax`, `strict`) |
|
||||
| `traefik.backend.loadbalancer.sticky=true` | Enables backend sticky sessions (DEPRECATED) |
|
||||
| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
|
||||
| `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
|
||||
|
||||
@@ -31,6 +31,9 @@ Traefik can be configured with a file.
|
||||
method = "drr"
|
||||
[backends.backend1.loadBalancer.stickiness]
|
||||
cookieName = "foobar"
|
||||
secure = true
|
||||
httpOnly = true
|
||||
sameSite = "foobar"
|
||||
|
||||
[backends.backend1.maxConn]
|
||||
amount = 10
|
||||
|
||||
@@ -341,7 +341,7 @@ The following security annotations are applicable on the Ingress object:
|
||||
| `ingress.kubernetes.io/ssl-temporary-redirect: "true"` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
|
||||
| `ingress.kubernetes.io/ssl-host: HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
|
||||
| `ingress.kubernetes.io/ssl-force-host: "true"` | If `SSLForceHost` is `true` and `SSLHost` is set, requests will be forced to use `SSLHost` even the ones that are already using SSL. Default is false. |
|
||||
| `ingress.kubernetes.io/ssl-proxy-headers: EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`). Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `ingress.kubernetes.io/ssl-proxy-headers: EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-Proto:https`). Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
|
||||
### Authentication
|
||||
|
||||
|
||||
@@ -218,6 +218,9 @@ The following labels can be defined on Marathon applications. They adjust the be
|
||||
| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm |
|
||||
| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions |
|
||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie name manually for sticky sessions |
|
||||
| `traefik.backend.loadbalancer.stickiness.secure=true` | Sets secure cookie option for sticky sessions. |
|
||||
| `traefik.backend.loadbalancer.stickiness.httpOnly=true` | Sets http only cookie option for sticky sessions. |
|
||||
| `traefik.backend.loadbalancer.stickiness.sameSite=none` | Sets same site cookie option for sticky sessions. (`none`, `lax`, `strict`) |
|
||||
| `traefik.backend.loadbalancer.sticky=true` | Enables backend sticky sessions (DEPRECATED) |
|
||||
| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
|
||||
| `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
|
||||
@@ -303,7 +306,7 @@ The following labels can be defined on Marathon applications. They adjust the be
|
||||
| `traefik.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
|
||||
| `traefik.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
|
||||
| `traefik.frontend.headers.SSLForceHost=true` | If `SSLForceHost` is `true` and `SSLHost` is set, requests will be forced to use `SSLHost` even the ones that are already using SSL. Default is false. |
|
||||
| `traefik.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`).<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-Proto:https`).<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
|
||||
| `traefik.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
|
||||
| `traefik.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
|
||||
|
||||
@@ -132,6 +132,9 @@ The following labels can be defined on Mesos tasks. They adjust the behavior for
|
||||
| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm |
|
||||
| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions |
|
||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie manually name for sticky sessions |
|
||||
| `traefik.backend.loadbalancer.stickiness.secure=true` | Sets secure cookie option for sticky sessions. |
|
||||
| `traefik.backend.loadbalancer.stickiness.httpOnly=true` | Sets http only cookie option for sticky sessions. |
|
||||
| `traefik.backend.loadbalancer.stickiness.sameSite=none` | Sets same site cookie option for sticky sessions. (`none`, `lax`, `strict`) |
|
||||
| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
|
||||
| `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
|
||||
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). |
|
||||
@@ -215,7 +218,7 @@ The following labels can be defined on Mesos tasks. They adjust the behavior for
|
||||
| `traefik.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
|
||||
| `traefik.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
|
||||
| `traefik.frontend.headers.SSLForceHost=true` | If `SSLForceHost` is `true` and `SSLHost` is set, requests will be forced to use `SSLHost` even the ones that are already using SSL. Default is false. |
|
||||
| `traefik.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`).<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-Proto:https`).<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
|
||||
| `traefik.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
|
||||
| `traefik.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
|
||||
|
||||
@@ -167,6 +167,9 @@ Labels can be used on task containers to override default behavior:
|
||||
| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm |
|
||||
| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions |
|
||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie name manually for sticky sessions |
|
||||
| `traefik.backend.loadbalancer.stickiness.secure=true` | Sets secure cookie option for sticky sessions. |
|
||||
| `traefik.backend.loadbalancer.stickiness.httpOnly=true` | Sets http only cookie option for sticky sessions. |
|
||||
| `traefik.backend.loadbalancer.stickiness.sameSite=none` | Sets same site cookie option for sticky sessions. (`none`, `lax`, `strict`) |
|
||||
| `traefik.backend.loadbalancer.sticky=true` | Enables backend sticky sessions (DEPRECATED) |
|
||||
| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
|
||||
| `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
|
||||
@@ -250,7 +253,7 @@ Labels can be used on task containers to override default behavior:
|
||||
| `traefik.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
|
||||
| `traefik.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
|
||||
| `traefik.frontend.headers.SSLForceHost=true` | If `SSLForceHost` is `true` and `SSLHost` is set, requests will be forced to use `SSLHost` even the ones that are already using SSL. Default is false. |
|
||||
| `traefik.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`).<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-Proto:https`).<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
|
||||
| `traefik.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
|
||||
| `traefik.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
|
||||
|
||||
@@ -145,7 +145,7 @@ Labels, set through extensions or the property manager, can be used on services
|
||||
| `traefik.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
|
||||
| `traefik.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
|
||||
| `traefik.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
|
||||
| `traefik.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`).<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-Proto:https`).<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
|
||||
| `traefik.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
|
||||
| `traefik.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
|
||||
|
||||
@@ -249,8 +249,14 @@ Multiple sets of rates can be added to each frontend, but the time periods must
|
||||
```
|
||||
|
||||
In the above example, frontend1 is configured to limit requests by the client's ip address.
|
||||
An average of 100 requests every 10 seconds is allowed and an average of 5 requests every 3 seconds.
|
||||
These can "burst" up to 200 and 10 in each period respectively.
|
||||
A sustained rate of 100 requests every 10 seconds (10 req/s) is allowed for rateset1, and 5 requests every 3 seconds (~1.67 req/s) for rateset2.
|
||||
In addition, these can "burst" up to 200 and 10 in each period respectively.
|
||||
|
||||
Another way to describe the above parameters, is to use the [leaky bucket](https://en.wikipedia.org/wiki/Leaky_bucket) analogy:
|
||||
for rateset1, the size of the bucket is 200 drops, and it is leaking at a rate of 10 drop/s.
|
||||
If the incoming rate of drops falling into the bucket gets high enough that the bucket gets filled,
|
||||
any subsequent drop overflows out of the bucket (i.e. the request is discarded).
|
||||
This situation holds until the incoming rate gets low enough again, and remains that way, for the water level in the bucket to go down.
|
||||
|
||||
Valid values for `extractorfunc` are:
|
||||
* `client.ip`
|
||||
|
||||
@@ -485,6 +485,13 @@ To enable IP white listing at the entry point level.
|
||||
# useXForwardedFor = true
|
||||
```
|
||||
|
||||
By setting the `useXForwardedFor` option, the `sourceRange` addresses will be matched against the request header `X-Forwarded-For` address list, from left to right.
|
||||
|
||||
!!! danger
|
||||
When using Traefik behind another load-balancer, its own internal address will be appended in the `X-Forwarded-For` header.
|
||||
Be sure to carefully configure the `sourceRange` as adding the internal network CIDR,
|
||||
or the load-balancer address directly, will cause all requests coming from it to pass through.
|
||||
|
||||
## ProxyProtocol
|
||||
|
||||
To enable [ProxyProtocol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) support.
|
||||
|
||||
7
docs/theme/partials/integrations/analytics.html
vendored
Normal file
7
docs/theme/partials/integrations/analytics.html
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
<!-- Google Tag Manager -->
|
||||
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
|
||||
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
|
||||
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
|
||||
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
|
||||
})(window,document,'script','dataLayer','GTM-NMWC63S');</script>
|
||||
<!-- End Google Tag Manager -->
|
||||
@@ -235,13 +235,11 @@ Let's take a look at the labels themselves for the `app` service, which is a HTT
|
||||
- "traefik.admin.port=9443"
|
||||
```
|
||||
|
||||
We use both `container labels` and `service labels`.
|
||||
We use both `container labels` and `segment labels`.
|
||||
|
||||
#### Container labels
|
||||
|
||||
First, we specify the `backend` name which corresponds to the actual service we're routing **to**.
|
||||
|
||||
We also tell Traefik to use the `web` network to route HTTP traffic to this container.
|
||||
We tell Traefik to use the `web` network to route HTTP traffic to this container.
|
||||
With the `traefik.enable` label, we tell Traefik to include this container in its internal configuration.
|
||||
|
||||
With the `frontend.rule` label, we tell Traefik that we want to route to this container if the incoming HTTP request contains the `Host` `app.my-awesome-app.org`.
|
||||
@@ -249,20 +247,20 @@ Essentially, this is the actual rule used for Layer-7 load balancing.
|
||||
|
||||
Finally but not unimportantly, we tell Traefik to route **to** port `9000`, since that is the actual TCP/IP port the container actually listens on.
|
||||
|
||||
### Service labels
|
||||
#### Segment labels
|
||||
|
||||
`Service labels` allow managing many routes for the same container.
|
||||
`Segment labels` allow managing many routes for the same container.
|
||||
|
||||
When both `container labels` and `service labels` are defined, `container labels` are just used as default values for missing `service labels` but no frontend/backend are going to be defined only with these labels.
|
||||
Obviously, labels `traefik.frontend.rule` and `traefik.port` described above, will only be used to complete information set in `service labels` during the container frontends/backends creation.
|
||||
When both `container labels` and `segment labels` are defined, `container labels` are just used as default values for missing `segment labels` but no frontend/backend are going to be defined only with these labels.
|
||||
Obviously, labels `traefik.frontend.rule` and `traefik.port` described above, will only be used to complete information set in `segment labels` during the container frontends/backends creation.
|
||||
|
||||
In the example, two service names are defined : `basic` and `admin`.
|
||||
In the example, two segment names are defined : `basic` and `admin`.
|
||||
They allow creating two frontends and two backends.
|
||||
|
||||
- `basic` has only one `service label` : `traefik.basic.protocol`.
|
||||
- `basic` has only one `segment label` : `traefik.basic.protocol`.
|
||||
Traefik will use values set in `traefik.frontend.rule` and `traefik.port` to create the `basic` frontend and backend.
|
||||
The frontend listens to incoming HTTP requests which contain the `Host` `app.my-awesome-app.org` and redirect them in `HTTP` to the port `9000` of the backend.
|
||||
- `admin` has all the `services labels` needed to create the `admin` frontend and backend (`traefik.admin.frontend.rule`, `traefik.admin.protocol`, `traefik.admin.port`).
|
||||
- `admin` has all the `segment labels` needed to create the `admin` frontend and backend (`traefik.admin.frontend.rule`, `traefik.admin.protocol`, `traefik.admin.port`).
|
||||
Traefik will create a frontend to listen to incoming HTTP requests which contain the `Host` `admin-app.my-awesome-app.org` and redirect them in `HTTPS` to the port `9443` of the backend.
|
||||
|
||||
#### Gotchas and tips
|
||||
|
||||
@@ -10,10 +10,14 @@ The config files used in this guide can be found in the [examples directory](htt
|
||||
|
||||
1. A working Kubernetes cluster. If you want to follow along with this guide, you should setup [minikube](https://kubernetes.io/docs/getting-started-guides/minikube/) on your machine, as it is the quickest way to get a local Kubernetes cluster setup for experimentation and development.
|
||||
|
||||
2. Setup ingress as an add-on. It can be enabled by the following command:
|
||||
|
||||
`minikube addons enable ingress`
|
||||
|
||||
!!! note
|
||||
The guide is likely not fully adequate for a production-ready setup.
|
||||
|
||||
2. The `kubectl` binary should be [installed on your workstation](https://kubernetes.io/docs/getting-started-guides/minikube/#download-kubectl).
|
||||
3. The `kubectl` binary should be [installed on your workstation](https://kubernetes.io/docs/getting-started-guides/minikube/#download-kubectl).
|
||||
|
||||
### Role Based Access Control configuration (Kubernetes 1.6+ only)
|
||||
|
||||
@@ -53,6 +57,12 @@ rules:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- extensions
|
||||
resources:
|
||||
- ingresses/status
|
||||
verbs:
|
||||
- update
|
||||
---
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
@@ -98,7 +108,7 @@ metadata:
|
||||
namespace: kube-system
|
||||
---
|
||||
kind: Deployment
|
||||
apiVersion: extensions/v1beta1
|
||||
apiVersion: apps/v1
|
||||
metadata:
|
||||
name: traefik-ingress-controller
|
||||
namespace: kube-system
|
||||
@@ -164,13 +174,17 @@ metadata:
|
||||
namespace: kube-system
|
||||
---
|
||||
kind: DaemonSet
|
||||
apiVersion: extensions/v1beta1
|
||||
apiVersion: apps/v1
|
||||
metadata:
|
||||
name: traefik-ingress-controller
|
||||
namespace: kube-system
|
||||
labels:
|
||||
k8s-app: traefik-ingress-lb
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
k8s-app: traefik-ingress-lb
|
||||
name: traefik-ingress-lb
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
@@ -188,6 +202,7 @@ spec:
|
||||
hostPort: 80
|
||||
- name: admin
|
||||
containerPort: 8080
|
||||
hostPort: 8080
|
||||
securityContext:
|
||||
capabilities:
|
||||
drop:
|
||||
@@ -358,7 +373,7 @@ spec:
|
||||
kubectl apply -f https://raw.githubusercontent.com/containous/traefik/v1.7/examples/k8s/ui.yaml
|
||||
```
|
||||
|
||||
Now lets setup an entry in our `/etc/hosts` file to route `traefik-ui.minikube` to our cluster.
|
||||
Now let's setup an entry in our `/etc/hosts` file to route `traefik-ui.minikube` to our cluster.
|
||||
|
||||
In production you would want to set up real DNS entries.
|
||||
You can get the IP address of your minikube instance by running `minikube ip`:
|
||||
@@ -382,6 +397,23 @@ You can add a TLS entrypoint by adding the following `args` to the container spe
|
||||
--entrypoints=Name:https Address::443 TLS
|
||||
--entrypoints=Name:http Address::80
|
||||
```
|
||||
|
||||
Now let's add the TLS port either to the deployment:
|
||||
|
||||
```
|
||||
ports:
|
||||
- name: https
|
||||
containerPort: 443
|
||||
```
|
||||
|
||||
or to the daemon set:
|
||||
|
||||
```
|
||||
ports:
|
||||
- name: https
|
||||
containerPort: 443
|
||||
hostPort: 443
|
||||
```
|
||||
|
||||
To setup an HTTPS-protected ingress, you can leverage the TLS feature of the ingress resource.
|
||||
|
||||
@@ -503,7 +535,7 @@ First lets start by launching the pods for the cheese websites.
|
||||
```yaml
|
||||
---
|
||||
kind: Deployment
|
||||
apiVersion: extensions/v1beta1
|
||||
apiVersion: apps/v1
|
||||
metadata:
|
||||
name: stilton
|
||||
labels:
|
||||
@@ -529,7 +561,7 @@ spec:
|
||||
- containerPort: 80
|
||||
---
|
||||
kind: Deployment
|
||||
apiVersion: extensions/v1beta1
|
||||
apiVersion: apps/v1
|
||||
metadata:
|
||||
name: cheddar
|
||||
labels:
|
||||
@@ -555,7 +587,7 @@ spec:
|
||||
- containerPort: 80
|
||||
---
|
||||
kind: Deployment
|
||||
apiVersion: extensions/v1beta1
|
||||
apiVersion: apps/v1
|
||||
metadata:
|
||||
name: wensleydale
|
||||
labels:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
kind: Deployment
|
||||
apiVersion: extensions/v1beta1
|
||||
apiVersion: apps/v1
|
||||
metadata:
|
||||
name: stilton
|
||||
labels:
|
||||
@@ -33,7 +33,7 @@ spec:
|
||||
- containerPort: 80
|
||||
---
|
||||
kind: Deployment
|
||||
apiVersion: extensions/v1beta1
|
||||
apiVersion: apps/v1
|
||||
metadata:
|
||||
name: cheddar
|
||||
labels:
|
||||
@@ -66,7 +66,7 @@ spec:
|
||||
- containerPort: 80
|
||||
---
|
||||
kind: Deployment
|
||||
apiVersion: extensions/v1beta1
|
||||
apiVersion: apps/v1
|
||||
metadata:
|
||||
name: wensleydale
|
||||
labels:
|
||||
|
||||
@@ -6,7 +6,7 @@ metadata:
|
||||
namespace: kube-system
|
||||
---
|
||||
kind: Deployment
|
||||
apiVersion: extensions/v1beta1
|
||||
apiVersion: apps/v1
|
||||
metadata:
|
||||
name: traefik-ingress-controller
|
||||
namespace: kube-system
|
||||
|
||||
@@ -12,7 +12,7 @@ RUN yarn install
|
||||
RUN npm run build
|
||||
|
||||
# BUILD
|
||||
FROM golang:1.12-alpine as gobuild
|
||||
FROM golang:1.14-alpine as gobuild
|
||||
|
||||
RUN apk --update upgrade \
|
||||
&& apk --no-cache --no-progress add git mercurial bash gcc musl-dev curl tar ca-certificates tzdata \
|
||||
|
||||
28
h2c/h2c.go
28
h2c/h2c.go
@@ -57,20 +57,20 @@ func (s Server) Serve(l net.Listener) error {
|
||||
if http2VerboseLogs {
|
||||
log.Debugf("Attempting h2c with prior knowledge.")
|
||||
}
|
||||
conn, err := initH2CWithPriorKnowledge(w)
|
||||
conn, err := initH2CWithPriorKnowledge(w, s.closeHijackedConnQuietly)
|
||||
if err != nil {
|
||||
if http2VerboseLogs {
|
||||
log.Debugf("Error h2c with prior knowledge: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
defer s.closeHijackedConnQuietly(conn)
|
||||
h2cSrv := &http2.Server{}
|
||||
h2cSrv.ServeConn(conn, &http2.ServeConnOpts{Handler: originalHandler})
|
||||
return
|
||||
}
|
||||
if conn, err := h2cUpgrade(w, r); err == nil {
|
||||
defer conn.Close()
|
||||
if conn, err := h2cUpgrade(w, r, s.closeHijackedConnQuietly); err == nil {
|
||||
defer s.closeHijackedConnQuietly(conn)
|
||||
h2cSrv := &http2.Server{}
|
||||
h2cSrv.ServeConn(conn, &http2.ServeConnOpts{Handler: originalHandler})
|
||||
return
|
||||
@@ -80,12 +80,24 @@ func (s Server) Serve(l net.Listener) error {
|
||||
return s.Server.Serve(l)
|
||||
}
|
||||
|
||||
func (s Server) closeHijackedConnQuietly(conn net.Conn) {
|
||||
connStateKey := conn
|
||||
if rwConn, ok := conn.(*rwConn); ok {
|
||||
connStateKey = rwConn.Conn
|
||||
}
|
||||
|
||||
s.ConnState(connStateKey, http.StateClosed)
|
||||
if err := conn.Close(); err != nil {
|
||||
log.Debugf("Error closing hijacked connection: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// initH2CWithPriorKnowledge implements creating a h2c connection with prior
|
||||
// knowledge (Section 3.4) and creates a net.Conn suitable for http2.ServeConn.
|
||||
// All we have to do is look for the client preface that is suppose to be part
|
||||
// of the body, and reforward the client preface on the net.Conn this function
|
||||
// creates.
|
||||
func initH2CWithPriorKnowledge(w http.ResponseWriter) (net.Conn, error) {
|
||||
func initH2CWithPriorKnowledge(w http.ResponseWriter, onFailureAfterHijack func(conn net.Conn)) (net.Conn, error) {
|
||||
hijacker, ok := w.(http.Hijacker)
|
||||
if !ok {
|
||||
return nil, errors.New("hijack not supported")
|
||||
@@ -100,6 +112,7 @@ func initH2CWithPriorKnowledge(w http.ResponseWriter) (net.Conn, error) {
|
||||
buf := make([]byte, len(expectedBody))
|
||||
n, err := io.ReadFull(rw, buf)
|
||||
if err != nil {
|
||||
onFailureAfterHijack(conn)
|
||||
return nil, fmt.Errorf("fail to read body: %v", err)
|
||||
}
|
||||
|
||||
@@ -112,7 +125,7 @@ func initH2CWithPriorKnowledge(w http.ResponseWriter) (net.Conn, error) {
|
||||
return c, nil
|
||||
}
|
||||
|
||||
conn.Close()
|
||||
onFailureAfterHijack(conn)
|
||||
if http2VerboseLogs {
|
||||
log.Printf(
|
||||
"Missing the request body portion of the client preface. Wanted: %v Got: %v",
|
||||
@@ -139,7 +152,7 @@ func drainClientPreface(r io.Reader) error {
|
||||
}
|
||||
|
||||
// h2cUpgrade establishes a h2c connection using the HTTP/1 upgrade (Section 3.2).
|
||||
func h2cUpgrade(w http.ResponseWriter, r *http.Request) (net.Conn, error) {
|
||||
func h2cUpgrade(w http.ResponseWriter, r *http.Request, onFailureAfterHijack func(conn net.Conn)) (net.Conn, error) {
|
||||
if !isH2CUpgrade(r.Header) {
|
||||
return nil, errors.New("non-conforming h2c headers")
|
||||
}
|
||||
@@ -167,6 +180,7 @@ func h2cUpgrade(w http.ResponseWriter, r *http.Request) (net.Conn, error) {
|
||||
// A conforming client will now send an H2 client preface which need to drain
|
||||
// since we already sent this.
|
||||
if err := drainClientPreface(rw); err != nil {
|
||||
onFailureAfterHijack(conn)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,10 @@ package auth
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
goauth "github.com/abbot/go-http-auth"
|
||||
"github.com/containous/traefik/log"
|
||||
@@ -61,7 +63,10 @@ func NewAuthenticator(authConfig *types.Auth, tracingMiddleware *tracing.Tracing
|
||||
tracingAuth.name = "Auth Digest"
|
||||
tracingAuth.clientSpanKind = false
|
||||
} else if authConfig.Forward != nil {
|
||||
tracingAuth.handler = createAuthForwardHandler(authConfig)
|
||||
tracingAuth.handler, err = createAuthForwardHandler(authConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tracingAuth.name = "Auth Forward"
|
||||
tracingAuth.clientSpanKind = true
|
||||
}
|
||||
@@ -74,10 +79,38 @@ func NewAuthenticator(authConfig *types.Auth, tracingMiddleware *tracing.Tracing
|
||||
return authenticator, nil
|
||||
}
|
||||
|
||||
func createAuthForwardHandler(authConfig *types.Auth) negroni.HandlerFunc {
|
||||
func createAuthForwardHandler(authConfig *types.Auth) (negroni.HandlerFunc, error) {
|
||||
// Ensure our request client does not follow redirects
|
||||
client := http.Client{
|
||||
CheckRedirect: func(r *http.Request, via []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
},
|
||||
Timeout: 30 * time.Second,
|
||||
}
|
||||
|
||||
if authConfig.Forward.TLS != nil {
|
||||
tlsConfig, err := authConfig.Forward.TLS.CreateTLSConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client.Transport = &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
DualStack: true,
|
||||
}).DialContext,
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
TLSClientConfig: tlsConfig,
|
||||
}
|
||||
}
|
||||
return negroni.HandlerFunc(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||
Forward(authConfig.Forward, w, r, next)
|
||||
})
|
||||
Forward(authConfig.Forward, client, w, r, next)
|
||||
}), nil
|
||||
}
|
||||
func createAuthDigestHandler(digestAuth *goauth.DigestAuth, authConfig *types.Auth) negroni.HandlerFunc {
|
||||
return negroni.HandlerFunc(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||
|
||||
@@ -18,28 +18,8 @@ const (
|
||||
xForwardedMethod = "X-Forwarded-Method"
|
||||
)
|
||||
|
||||
// Forward the authentication to a external server
|
||||
func Forward(config *types.Forward, w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||
// Ensure our request client does not follow redirects
|
||||
httpClient := http.Client{
|
||||
CheckRedirect: func(r *http.Request, via []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
},
|
||||
}
|
||||
|
||||
if config.TLS != nil {
|
||||
tlsConfig, err := config.TLS.CreateTLSConfig()
|
||||
if err != nil {
|
||||
tracing.SetErrorAndDebugLog(r, "Unable to configure TLS to call %s. Cause %s", config.Address, err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
httpClient.Transport = &http.Transport{
|
||||
TLSClientConfig: tlsConfig,
|
||||
}
|
||||
}
|
||||
|
||||
// Forward the authentication to an external server
|
||||
func Forward(config *types.Forward, httpClient http.Client, w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||
forwardReq, err := http.NewRequest(http.MethodGet, config.Address, http.NoBody)
|
||||
tracing.LogRequest(tracing.GetSpan(r), forwardReq)
|
||||
if err != nil {
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
defaultRedirectRegex = `^(?:https?:\/\/)?([\w\._-]+)(?::\d+)?(.*)$`
|
||||
defaultRedirectRegex = `^(?:https?:\/\/)?(\[[\w:.]+\]|[\w\._-]+)(?::\d+)?(.*)$`
|
||||
)
|
||||
|
||||
// NewEntryPointHandler create a new redirection handler base on entry point
|
||||
|
||||
@@ -84,6 +84,34 @@ func TestNewEntryPointHandler(t *testing.T) {
|
||||
url: "http://foo:80",
|
||||
errorExpected: true,
|
||||
},
|
||||
{
|
||||
desc: "IPV6 HTTP to HTTP",
|
||||
entryPoint: &configuration.EntryPoint{Address: ":8080"},
|
||||
url: "http://[::1]",
|
||||
expectedURL: "http://[::1]:8080",
|
||||
expectedStatus: http.StatusFound,
|
||||
},
|
||||
{
|
||||
desc: "IPV6 HTTP to HTTPS",
|
||||
entryPoint: &configuration.EntryPoint{Address: ":443", TLS: &tls.TLS{}},
|
||||
url: "http://[::1]",
|
||||
expectedURL: "https://[::1]:443",
|
||||
expectedStatus: http.StatusFound,
|
||||
},
|
||||
{
|
||||
desc: "IPV6 HTTP with port 80 to HTTP",
|
||||
entryPoint: &configuration.EntryPoint{Address: ":8080"},
|
||||
url: "http://[::1]:80",
|
||||
expectedURL: "http://[::1]:8080",
|
||||
expectedStatus: http.StatusFound,
|
||||
},
|
||||
{
|
||||
desc: "IPV6 HTTP with port 80 to HTTPS",
|
||||
entryPoint: &configuration.EntryPoint{Address: ":443", TLS: &tls.TLS{}},
|
||||
url: "http://[::1]:80",
|
||||
expectedURL: "https://[::1]:443",
|
||||
expectedStatus: http.StatusFound,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
|
||||
@@ -28,6 +28,7 @@ theme:
|
||||
|
||||
copyright: "Copyright © 2016-2019 Containous"
|
||||
|
||||
# only to force the use of the analytics partials
|
||||
google_analytics:
|
||||
- 'UA-51880359-3'
|
||||
- 'docs.traefik.io'
|
||||
|
||||
@@ -254,7 +254,8 @@ func TestProviderBuildConfiguration(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
desc: "Should build config with a digest auth",
|
||||
nodes: []catalogUpdate{
|
||||
{
|
||||
@@ -416,6 +417,9 @@ func TestProviderBuildConfiguration(t *testing.T) {
|
||||
label.TraefikBackendLoadBalancerSticky + "=true",
|
||||
label.TraefikBackendLoadBalancerStickiness + "=true",
|
||||
label.TraefikBackendLoadBalancerStickinessCookieName + "=chocolate",
|
||||
label.TraefikBackendLoadBalancerStickinessSecure + "=true",
|
||||
label.TraefikBackendLoadBalancerStickinessHTTPOnly + "=true",
|
||||
label.TraefikBackendLoadBalancerStickinessSameSite + "=none",
|
||||
label.TraefikBackendMaxConnAmount + "=666",
|
||||
label.TraefikBackendMaxConnExtractorFunc + "=client.ip",
|
||||
label.TraefikBackendBufferingMaxResponseBodyBytes + "=10485760",
|
||||
@@ -700,6 +704,9 @@ func TestProviderBuildConfiguration(t *testing.T) {
|
||||
Sticky: true,
|
||||
Stickiness: &types.Stickiness{
|
||||
CookieName: "chocolate",
|
||||
Secure: true,
|
||||
HTTPOnly: true,
|
||||
SameSite: "none",
|
||||
},
|
||||
},
|
||||
MaxConn: &types.MaxConn{
|
||||
|
||||
@@ -463,6 +463,9 @@ func TestDockerBuildConfiguration(t *testing.T) {
|
||||
label.TraefikBackendLoadBalancerSticky: "true",
|
||||
label.TraefikBackendLoadBalancerStickiness: "true",
|
||||
label.TraefikBackendLoadBalancerStickinessCookieName: "chocolate",
|
||||
label.TraefikBackendLoadBalancerStickinessSecure: "true",
|
||||
label.TraefikBackendLoadBalancerStickinessHTTPOnly: "true",
|
||||
label.TraefikBackendLoadBalancerStickinessSameSite: "none",
|
||||
label.TraefikBackendMaxConnAmount: "666",
|
||||
label.TraefikBackendMaxConnExtractorFunc: "client.ip",
|
||||
label.TraefikBackendBufferingMaxResponseBodyBytes: "10485760",
|
||||
@@ -711,6 +714,9 @@ func TestDockerBuildConfiguration(t *testing.T) {
|
||||
Sticky: true,
|
||||
Stickiness: &types.Stickiness{
|
||||
CookieName: "chocolate",
|
||||
Secure: true,
|
||||
HTTPOnly: true,
|
||||
SameSite: "none",
|
||||
},
|
||||
},
|
||||
MaxConn: &types.MaxConn{
|
||||
|
||||
@@ -412,6 +412,9 @@ func TestSwarmBuildConfiguration(t *testing.T) {
|
||||
label.TraefikBackendLoadBalancerSticky: "true",
|
||||
label.TraefikBackendLoadBalancerStickiness: "true",
|
||||
label.TraefikBackendLoadBalancerStickinessCookieName: "chocolate",
|
||||
label.TraefikBackendLoadBalancerStickinessSecure: "true",
|
||||
label.TraefikBackendLoadBalancerStickinessHTTPOnly: "true",
|
||||
label.TraefikBackendLoadBalancerStickinessSameSite: "none",
|
||||
label.TraefikBackendMaxConnAmount: "666",
|
||||
label.TraefikBackendMaxConnExtractorFunc: "client.ip",
|
||||
label.TraefikBackendBufferingMaxResponseBodyBytes: "10485760",
|
||||
@@ -611,6 +614,9 @@ func TestSwarmBuildConfiguration(t *testing.T) {
|
||||
Sticky: true,
|
||||
Stickiness: &types.Stickiness{
|
||||
CookieName: "chocolate",
|
||||
Secure: true,
|
||||
HTTPOnly: true,
|
||||
SameSite: "none",
|
||||
},
|
||||
},
|
||||
MaxConn: &types.MaxConn{
|
||||
|
||||
@@ -353,6 +353,9 @@ func TestBuildConfiguration(t *testing.T) {
|
||||
label.TraefikBackendLoadBalancerSticky: aws.String("true"),
|
||||
label.TraefikBackendLoadBalancerStickiness: aws.String("true"),
|
||||
label.TraefikBackendLoadBalancerStickinessCookieName: aws.String("chocolate"),
|
||||
label.TraefikBackendLoadBalancerStickinessSecure: aws.String("true"),
|
||||
label.TraefikBackendLoadBalancerStickinessHTTPOnly: aws.String("true"),
|
||||
label.TraefikBackendLoadBalancerStickinessSameSite: aws.String("none"),
|
||||
label.TraefikBackendMaxConnAmount: aws.String("666"),
|
||||
label.TraefikBackendMaxConnExtractorFunc: aws.String("client.ip"),
|
||||
label.TraefikBackendBufferingMaxResponseBodyBytes: aws.String("10485760"),
|
||||
@@ -475,6 +478,9 @@ func TestBuildConfiguration(t *testing.T) {
|
||||
Sticky: true,
|
||||
Stickiness: &types.Stickiness{
|
||||
CookieName: "chocolate",
|
||||
Secure: true,
|
||||
HTTPOnly: true,
|
||||
SameSite: "none",
|
||||
},
|
||||
},
|
||||
MaxConn: &types.MaxConn{
|
||||
@@ -662,6 +668,9 @@ func TestBuildConfiguration(t *testing.T) {
|
||||
label.TraefikBackendLoadBalancerSticky: aws.String("true"),
|
||||
label.TraefikBackendLoadBalancerStickiness: aws.String("true"),
|
||||
label.TraefikBackendLoadBalancerStickinessCookieName: aws.String("chocolate"),
|
||||
label.TraefikBackendLoadBalancerStickinessSecure: aws.String("true"),
|
||||
label.TraefikBackendLoadBalancerStickinessHTTPOnly: aws.String("true"),
|
||||
label.TraefikBackendLoadBalancerStickinessSameSite: aws.String("none"),
|
||||
label.TraefikBackendMaxConnAmount: aws.String("666"),
|
||||
label.TraefikBackendMaxConnExtractorFunc: aws.String("client.ip"),
|
||||
label.TraefikBackendBufferingMaxResponseBodyBytes: aws.String("10485760"),
|
||||
@@ -750,6 +759,9 @@ func TestBuildConfiguration(t *testing.T) {
|
||||
label.TraefikBackendLoadBalancerSticky: aws.String("true"),
|
||||
label.TraefikBackendLoadBalancerStickiness: aws.String("true"),
|
||||
label.TraefikBackendLoadBalancerStickinessCookieName: aws.String("chocolate"),
|
||||
label.TraefikBackendLoadBalancerStickinessSecure: aws.String("true"),
|
||||
label.TraefikBackendLoadBalancerStickinessHTTPOnly: aws.String("true"),
|
||||
label.TraefikBackendLoadBalancerStickinessSameSite: aws.String("none"),
|
||||
label.TraefikBackendMaxConnAmount: aws.String("666"),
|
||||
label.TraefikBackendMaxConnExtractorFunc: aws.String("client.ip"),
|
||||
label.TraefikBackendBufferingMaxResponseBodyBytes: aws.String("10485760"),
|
||||
@@ -839,6 +851,9 @@ func TestBuildConfiguration(t *testing.T) {
|
||||
Sticky: true,
|
||||
Stickiness: &types.Stickiness{
|
||||
CookieName: "chocolate",
|
||||
Secure: true,
|
||||
HTTPOnly: true,
|
||||
SameSite: "none",
|
||||
},
|
||||
},
|
||||
MaxConn: &types.MaxConn{
|
||||
|
||||
@@ -309,6 +309,11 @@ func (p *Provider) listInstances(ctx context.Context, client *awsClient) ([]ecsI
|
||||
state: aws.StringValue(task.LastStatus),
|
||||
}
|
||||
} else {
|
||||
if containerInstance == nil {
|
||||
log.Errorf("Unable to find container instance information for %s", aws.StringValue(container.Name))
|
||||
continue
|
||||
}
|
||||
|
||||
var ports []portMapping
|
||||
for _, mapping := range container.NetworkBindings {
|
||||
if mapping != nil {
|
||||
|
||||
@@ -30,6 +30,9 @@ const (
|
||||
annotationKubernetesLoadBalancerMethod = "ingress.kubernetes.io/load-balancer-method"
|
||||
annotationKubernetesAffinity = "ingress.kubernetes.io/affinity"
|
||||
annotationKubernetesSessionCookieName = "ingress.kubernetes.io/session-cookie-name"
|
||||
annotationKubernetesSessionSecure = "ingress.kubernetes.io/session-secure"
|
||||
annotationKubernetesSessionHTTPOnly = "ingress.kubernetes.io/session-http-only"
|
||||
annotationKubernetesSessionSameSite = "ingress.kubernetes.io/session-same-site"
|
||||
annotationKubernetesRuleType = "ingress.kubernetes.io/rule-type"
|
||||
annotationKubernetesRedirectEntryPoint = "ingress.kubernetes.io/redirect-entry-point"
|
||||
annotationKubernetesRedirectPermanent = "ingress.kubernetes.io/redirect-permanent"
|
||||
|
||||
@@ -161,6 +161,23 @@ spec:
|
||||
servicePort: 80
|
||||
path: /api
|
||||
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
annotations:
|
||||
ingress.kubernetes.io/rewrite-target: /app
|
||||
namespace: testing
|
||||
spec:
|
||||
rules:
|
||||
- host: rewritetargetrootpath
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: service1
|
||||
servicePort: 80
|
||||
path: /
|
||||
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Ingress
|
||||
|
||||
@@ -674,6 +674,12 @@ func getRuleForPath(pa extensionsv1beta1.HTTPIngressPath, i *extensionsv1beta1.I
|
||||
return "", fmt.Errorf("rewrite-target must not be used together with annotation %q", annotationKubernetesRuleType)
|
||||
}
|
||||
rewriteTargetRule := fmt.Sprintf("ReplacePathRegex: ^%s(.*) %s$1", pa.Path, strings.TrimRight(rewriteTarget, "/"))
|
||||
if pa.Path == "/" {
|
||||
// If path = /, then just append the cap group, as if we don't cap the path as part of the regex,
|
||||
// then when we strip the right / from the rewrite target, it ends up being missed, as removed but never returned
|
||||
// this only happens when path = / because it is the only case where TrimRight will catch a leading /.
|
||||
rewriteTargetRule = fmt.Sprintf("ReplacePathRegex: ^(.*) %s$1", strings.TrimRight(rewriteTarget, "/"))
|
||||
}
|
||||
rules = append(rules, rewriteTargetRule)
|
||||
}
|
||||
|
||||
@@ -1145,14 +1151,16 @@ func getLoadBalancer(service *corev1.Service) *types.LoadBalancer {
|
||||
}
|
||||
|
||||
func getStickiness(service *corev1.Service) *types.Stickiness {
|
||||
if getBoolValue(service.Annotations, annotationKubernetesAffinity, false) {
|
||||
stickiness := &types.Stickiness{}
|
||||
if cookieName := getStringValue(service.Annotations, annotationKubernetesSessionCookieName, ""); len(cookieName) > 0 {
|
||||
stickiness.CookieName = cookieName
|
||||
}
|
||||
return stickiness
|
||||
if !getBoolValue(service.Annotations, annotationKubernetesAffinity, false) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &types.Stickiness{
|
||||
CookieName: getStringValue(service.Annotations, annotationKubernetesSessionCookieName, ""),
|
||||
Secure: getBoolValue(service.Annotations, annotationKubernetesSessionSecure, false),
|
||||
HTTPOnly: getBoolValue(service.Annotations, annotationKubernetesSessionHTTPOnly, false),
|
||||
SameSite: getStringValue(service.Annotations, annotationKubernetesSessionSameSite, ""),
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getHeader(i *extensionsv1beta1.Ingress) *types.Headers {
|
||||
|
||||
@@ -415,6 +415,12 @@ func TestProvider_loadIngresses(t *testing.T) {
|
||||
server("http://example.com", weight(1))),
|
||||
lbMethod("wrr"),
|
||||
),
|
||||
backend("rewritetargetrootpath/",
|
||||
servers(
|
||||
server("http://example.com", weight(1)),
|
||||
server("http://example.com", weight(1))),
|
||||
lbMethod("wrr"),
|
||||
),
|
||||
backend("error-pages/errorpages",
|
||||
servers(
|
||||
server("http://example.com", weight(1)),
|
||||
@@ -523,6 +529,12 @@ func TestProvider_loadIngresses(t *testing.T) {
|
||||
route("/whitelist-source-range", "PathPrefix:/whitelist-source-range"),
|
||||
route("test", "Host:test")),
|
||||
),
|
||||
frontend("rewritetargetrootpath/",
|
||||
passHostHeader(),
|
||||
routes(
|
||||
route("/", "PathPrefix:/;ReplacePathRegex: ^(.*) /app$1"),
|
||||
route("rewritetargetrootpath", "Host:rewritetargetrootpath")),
|
||||
),
|
||||
frontend("rewrite/api",
|
||||
passHostHeader(),
|
||||
routes(
|
||||
@@ -596,7 +608,7 @@ func TestProvider_loadIngresses(t *testing.T) {
|
||||
passHostHeader(),
|
||||
redirectRegex("root2/$", "root2/root2"),
|
||||
routes(
|
||||
route("/", "PathPrefix:/;ReplacePathRegex: ^/(.*) /abc$1"),
|
||||
route("/", "PathPrefix:/;ReplacePathRegex: ^(.*) /abc$1"),
|
||||
route("root2", "Host:root2"),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -14,6 +14,9 @@ const (
|
||||
pathBackendLoadBalancerSticky = "/loadbalancer/sticky"
|
||||
pathBackendLoadBalancerStickiness = "/loadbalancer/stickiness"
|
||||
pathBackendLoadBalancerStickinessCookieName = "/loadbalancer/stickiness/cookiename"
|
||||
pathBackendLoadBalancerStickinessSecure = "/loadbalancer/stickiness/secure"
|
||||
pathBackendLoadBalancerStickinessHTTPOnly = "/loadbalancer/stickiness/httponly"
|
||||
pathBackendLoadBalancerStickinessSameSite = "/loadbalancer/stickiness/samesite"
|
||||
pathBackendMaxConnAmount = "/maxconn/amount"
|
||||
pathBackendMaxConnExtractorFunc = "/maxconn/extractorfunc"
|
||||
pathBackendServers = "/servers/"
|
||||
|
||||
@@ -276,6 +276,9 @@ func (p *Provider) getLoadBalancer(rootPath string) *types.LoadBalancer {
|
||||
if p.getBool(false, rootPath, pathBackendLoadBalancerStickiness) {
|
||||
lb.Stickiness = &types.Stickiness{
|
||||
CookieName: p.get("", rootPath, pathBackendLoadBalancerStickinessCookieName),
|
||||
Secure: p.getBool(false, rootPath, pathBackendLoadBalancerStickinessSecure),
|
||||
HTTPOnly: p.getBool(false, rootPath, pathBackendLoadBalancerStickinessHTTPOnly),
|
||||
SameSite: p.get("", rootPath, pathBackendLoadBalancerStickinessSameSite),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -260,6 +260,9 @@ func TestProviderBuildConfiguration(t *testing.T) {
|
||||
withPair(pathBackendLoadBalancerSticky, "true"),
|
||||
withPair(pathBackendLoadBalancerStickiness, "true"),
|
||||
withPair(pathBackendLoadBalancerStickinessCookieName, "tomate"),
|
||||
withPair(pathBackendLoadBalancerStickinessSecure, "true"),
|
||||
withPair(pathBackendLoadBalancerStickinessHTTPOnly, "true"),
|
||||
withPair(pathBackendLoadBalancerStickinessSameSite, "none"),
|
||||
withPair(pathBackendHealthCheckScheme, "http"),
|
||||
withPair(pathBackendHealthCheckPath, "/health"),
|
||||
withPair(pathBackendHealthCheckPort, "80"),
|
||||
@@ -389,6 +392,9 @@ func TestProviderBuildConfiguration(t *testing.T) {
|
||||
Sticky: true,
|
||||
Stickiness: &types.Stickiness{
|
||||
CookieName: "tomate",
|
||||
Secure: true,
|
||||
HTTPOnly: true,
|
||||
SameSite: "none",
|
||||
},
|
||||
},
|
||||
MaxConn: &types.MaxConn{
|
||||
@@ -1949,12 +1955,18 @@ func TestProviderGetLoadBalancer(t *testing.T) {
|
||||
withPair(pathBackendLoadBalancerMethod, "drr"),
|
||||
withPair(pathBackendLoadBalancerSticky, "true"),
|
||||
withPair(pathBackendLoadBalancerStickiness, "true"),
|
||||
withPair(pathBackendLoadBalancerStickinessCookieName, "aubergine"))),
|
||||
withPair(pathBackendLoadBalancerStickinessCookieName, "aubergine"),
|
||||
withPair(pathBackendLoadBalancerStickinessSecure, "true"),
|
||||
withPair(pathBackendLoadBalancerStickinessHTTPOnly, "true"),
|
||||
withPair(pathBackendLoadBalancerStickinessSameSite, "none"))),
|
||||
expected: &types.LoadBalancer{
|
||||
Method: "drr",
|
||||
Sticky: true,
|
||||
Stickiness: &types.Stickiness{
|
||||
CookieName: "aubergine",
|
||||
Secure: true,
|
||||
HTTPOnly: true,
|
||||
SameSite: "none",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -26,6 +26,9 @@ const (
|
||||
DefaultBackendLoadBalancerMethod = "wrr"
|
||||
DefaultBackendMaxconnExtractorFunc = "request.host"
|
||||
DefaultBackendLoadbalancerStickinessCookieName = ""
|
||||
DefaultBackendLoadbalancerStickinessSecure = false
|
||||
DefaultBackendLoadbalancerStickinessHTTPOnly = false
|
||||
DefaultBackendLoadbalancerStickinessSameSite = ""
|
||||
DefaultBackendHealthCheckPort = 0
|
||||
)
|
||||
|
||||
@@ -45,6 +48,21 @@ func GetStringValue(labels map[string]string, labelName string, defaultValue str
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// GetStringSafeValue get string value associated to a label and check if the content is "quotable".
|
||||
func GetStringSafeValue(labels map[string]string, labelName string, defaultValue string) (string, error) {
|
||||
value, ok := labels[labelName]
|
||||
if !ok || len(value) <= 0 {
|
||||
return defaultValue, nil
|
||||
}
|
||||
|
||||
_, err := strconv.Unquote(`"` + value + `"`)
|
||||
if err != nil {
|
||||
return value, err
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// GetBoolValue get bool value associated to a label
|
||||
func GetBoolValue(labels map[string]string, labelName string, defaultValue bool) bool {
|
||||
rawValue, ok := labels[labelName]
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSplitAndTrimString(t *testing.T) {
|
||||
@@ -106,6 +107,78 @@ func TestGetStringValue(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetStringSafeValue(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
labels map[string]string
|
||||
labelName string
|
||||
defaultValue string
|
||||
expected string
|
||||
expectedError bool
|
||||
}{
|
||||
{
|
||||
desc: "empty labels map",
|
||||
labelName: "foo",
|
||||
defaultValue: "default",
|
||||
expected: "default",
|
||||
},
|
||||
{
|
||||
desc: "unescaped value",
|
||||
labels: map[string]string{
|
||||
"foo": `^https?://(test\.beta\.redacted\.org|redacted\.com)/(.*)`,
|
||||
},
|
||||
labelName: "foo",
|
||||
defaultValue: "default",
|
||||
expected: `^https?://(test\.beta\.redacted\.org|redacted\.com)/(.*)`,
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
desc: "escaped value",
|
||||
labels: map[string]string{
|
||||
"foo": `^https?://(test\\.beta\\.redacted\\.org|redacted\\.com)/(.*)`,
|
||||
},
|
||||
labelName: "foo",
|
||||
defaultValue: "default",
|
||||
expected: `^https?://(test\\.beta\\.redacted\\.org|redacted\\.com)/(.*)`,
|
||||
},
|
||||
{
|
||||
desc: "empty value",
|
||||
labels: map[string]string{
|
||||
"foo": "",
|
||||
},
|
||||
labelName: "foo",
|
||||
defaultValue: "default",
|
||||
expected: "default",
|
||||
},
|
||||
{
|
||||
desc: "non existing label",
|
||||
labels: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
labelName: "fii",
|
||||
defaultValue: "default",
|
||||
expected: "default",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := GetStringSafeValue(test.labels, test.labelName, test.defaultValue)
|
||||
|
||||
if test.expectedError {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
assert.Equal(t, test.expected, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetBoolValue(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
|
||||
@@ -2,142 +2,150 @@ package label
|
||||
|
||||
// Traefik labels
|
||||
const (
|
||||
Prefix = "traefik."
|
||||
SuffixBackend = "backend"
|
||||
SuffixDomain = "domain"
|
||||
SuffixEnable = "enable"
|
||||
SuffixPort = "port"
|
||||
SuffixPortName = "portName"
|
||||
SuffixPortIndex = "portIndex"
|
||||
SuffixProtocol = "protocol"
|
||||
SuffixTags = "tags"
|
||||
SuffixWeight = "weight"
|
||||
SuffixBackendID = "backend.id"
|
||||
SuffixBackendCircuitBreaker = "backend.circuitbreaker"
|
||||
SuffixBackendCircuitBreakerExpression = "backend.circuitbreaker.expression"
|
||||
SuffixBackendHealthCheckScheme = "backend.healthcheck.scheme"
|
||||
SuffixBackendHealthCheckPath = "backend.healthcheck.path"
|
||||
SuffixBackendHealthCheckPort = "backend.healthcheck.port"
|
||||
SuffixBackendHealthCheckInterval = "backend.healthcheck.interval"
|
||||
SuffixBackendHealthCheckHostname = "backend.healthcheck.hostname"
|
||||
SuffixBackendHealthCheckHeaders = "backend.healthcheck.headers"
|
||||
SuffixBackendLoadBalancer = "backend.loadbalancer"
|
||||
SuffixBackendLoadBalancerMethod = SuffixBackendLoadBalancer + ".method"
|
||||
SuffixBackendLoadBalancerSticky = SuffixBackendLoadBalancer + ".sticky"
|
||||
SuffixBackendLoadBalancerStickiness = SuffixBackendLoadBalancer + ".stickiness"
|
||||
SuffixBackendLoadBalancerStickinessCookieName = SuffixBackendLoadBalancer + ".stickiness.cookieName"
|
||||
SuffixBackendMaxConnAmount = "backend.maxconn.amount"
|
||||
SuffixBackendMaxConnExtractorFunc = "backend.maxconn.extractorfunc"
|
||||
SuffixBackendBuffering = "backend.buffering"
|
||||
SuffixBackendResponseForwardingFlushInterval = "backend.responseForwarding.flushInterval"
|
||||
SuffixBackendBufferingMaxRequestBodyBytes = SuffixBackendBuffering + ".maxRequestBodyBytes"
|
||||
SuffixBackendBufferingMemRequestBodyBytes = SuffixBackendBuffering + ".memRequestBodyBytes"
|
||||
SuffixBackendBufferingMaxResponseBodyBytes = SuffixBackendBuffering + ".maxResponseBodyBytes"
|
||||
SuffixBackendBufferingMemResponseBodyBytes = SuffixBackendBuffering + ".memResponseBodyBytes"
|
||||
SuffixBackendBufferingRetryExpression = SuffixBackendBuffering + ".retryExpression"
|
||||
SuffixFrontend = "frontend"
|
||||
SuffixFrontendAuth = SuffixFrontend + ".auth"
|
||||
SuffixFrontendAuthBasic = SuffixFrontendAuth + ".basic"
|
||||
SuffixFrontendAuthBasicRemoveHeader = SuffixFrontendAuthBasic + ".removeHeader"
|
||||
SuffixFrontendAuthBasicUsers = SuffixFrontendAuthBasic + ".users"
|
||||
SuffixFrontendAuthBasicUsersFile = SuffixFrontendAuthBasic + ".usersFile"
|
||||
SuffixFrontendAuthDigest = SuffixFrontendAuth + ".digest"
|
||||
SuffixFrontendAuthDigestRemoveHeader = SuffixFrontendAuthDigest + ".removeHeader"
|
||||
SuffixFrontendAuthDigestUsers = SuffixFrontendAuthDigest + ".users"
|
||||
SuffixFrontendAuthDigestUsersFile = SuffixFrontendAuthDigest + ".usersFile"
|
||||
SuffixFrontendAuthForward = SuffixFrontendAuth + ".forward"
|
||||
SuffixFrontendAuthForwardAddress = SuffixFrontendAuthForward + ".address"
|
||||
SuffixFrontendAuthForwardAuthResponseHeaders = SuffixFrontendAuthForward + ".authResponseHeaders"
|
||||
SuffixFrontendAuthForwardTLS = SuffixFrontendAuthForward + ".tls"
|
||||
SuffixFrontendAuthForwardTLSCa = SuffixFrontendAuthForwardTLS + ".ca"
|
||||
SuffixFrontendAuthForwardTLSCaOptional = SuffixFrontendAuthForwardTLS + ".caOptional"
|
||||
SuffixFrontendAuthForwardTLSCert = SuffixFrontendAuthForwardTLS + ".cert"
|
||||
SuffixFrontendAuthForwardTLSInsecureSkipVerify = SuffixFrontendAuthForwardTLS + ".insecureSkipVerify"
|
||||
SuffixFrontendAuthForwardTLSKey = SuffixFrontendAuthForwardTLS + ".key"
|
||||
SuffixFrontendAuthForwardTrustForwardHeader = SuffixFrontendAuthForward + ".trustForwardHeader"
|
||||
SuffixFrontendAuthHeaderField = SuffixFrontendAuth + ".headerField"
|
||||
SuffixFrontendEntryPoints = "frontend.entryPoints"
|
||||
SuffixFrontendHeaders = "frontend.headers."
|
||||
SuffixFrontendRequestHeaders = SuffixFrontendHeaders + "customRequestHeaders"
|
||||
SuffixFrontendResponseHeaders = SuffixFrontendHeaders + "customResponseHeaders"
|
||||
SuffixFrontendHeadersAllowedHosts = SuffixFrontendHeaders + "allowedHosts"
|
||||
SuffixFrontendHeadersHostsProxyHeaders = SuffixFrontendHeaders + "hostsProxyHeaders"
|
||||
SuffixFrontendHeadersSSLForceHost = SuffixFrontendHeaders + "SSLForceHost"
|
||||
SuffixFrontendHeadersSSLRedirect = SuffixFrontendHeaders + "SSLRedirect"
|
||||
SuffixFrontendHeadersSSLTemporaryRedirect = SuffixFrontendHeaders + "SSLTemporaryRedirect"
|
||||
SuffixFrontendHeadersSSLHost = SuffixFrontendHeaders + "SSLHost"
|
||||
SuffixFrontendHeadersSSLProxyHeaders = SuffixFrontendHeaders + "SSLProxyHeaders"
|
||||
SuffixFrontendHeadersSTSSeconds = SuffixFrontendHeaders + "STSSeconds"
|
||||
SuffixFrontendHeadersSTSIncludeSubdomains = SuffixFrontendHeaders + "STSIncludeSubdomains"
|
||||
SuffixFrontendHeadersSTSPreload = SuffixFrontendHeaders + "STSPreload"
|
||||
SuffixFrontendHeadersForceSTSHeader = SuffixFrontendHeaders + "forceSTSHeader"
|
||||
SuffixFrontendHeadersFrameDeny = SuffixFrontendHeaders + "frameDeny"
|
||||
SuffixFrontendHeadersCustomFrameOptionsValue = SuffixFrontendHeaders + "customFrameOptionsValue"
|
||||
SuffixFrontendHeadersContentTypeNosniff = SuffixFrontendHeaders + "contentTypeNosniff"
|
||||
SuffixFrontendHeadersBrowserXSSFilter = SuffixFrontendHeaders + "browserXSSFilter"
|
||||
SuffixFrontendHeadersCustomBrowserXSSValue = SuffixFrontendHeaders + "customBrowserXSSValue"
|
||||
SuffixFrontendHeadersContentSecurityPolicy = SuffixFrontendHeaders + "contentSecurityPolicy"
|
||||
SuffixFrontendHeadersPublicKey = SuffixFrontendHeaders + "publicKey"
|
||||
SuffixFrontendHeadersReferrerPolicy = SuffixFrontendHeaders + "referrerPolicy"
|
||||
SuffixFrontendHeadersIsDevelopment = SuffixFrontendHeaders + "isDevelopment"
|
||||
SuffixFrontendPassHostHeader = "frontend.passHostHeader"
|
||||
SuffixFrontendPassTLSClientCert = "frontend.passTLSClientCert"
|
||||
SuffixFrontendPassTLSClientCertPem = SuffixFrontendPassTLSClientCert + ".pem"
|
||||
SuffixFrontendPassTLSClientCertInfos = SuffixFrontendPassTLSClientCert + ".infos"
|
||||
SuffixFrontendPassTLSClientCertInfosIssuer = SuffixFrontendPassTLSClientCertInfos + ".issuer"
|
||||
SuffixFrontendPassTLSClientCertInfosIssuerCommonName = SuffixFrontendPassTLSClientCertInfosIssuer + ".commonName"
|
||||
SuffixFrontendPassTLSClientCertInfosIssuerCountry = SuffixFrontendPassTLSClientCertInfosIssuer + ".country"
|
||||
SuffixFrontendPassTLSClientCertInfosIssuerDomainComponent = SuffixFrontendPassTLSClientCertInfosIssuer + ".domainComponent"
|
||||
SuffixFrontendPassTLSClientCertInfosIssuerLocality = SuffixFrontendPassTLSClientCertInfosIssuer + ".locality"
|
||||
SuffixFrontendPassTLSClientCertInfosIssuerOrganization = SuffixFrontendPassTLSClientCertInfosIssuer + ".organization"
|
||||
SuffixFrontendPassTLSClientCertInfosIssuerProvince = SuffixFrontendPassTLSClientCertInfosIssuer + ".province"
|
||||
SuffixFrontendPassTLSClientCertInfosIssuerSerialNumber = SuffixFrontendPassTLSClientCertInfosIssuer + ".serialNumber"
|
||||
SuffixFrontendPassTLSClientCertInfosSubject = SuffixFrontendPassTLSClientCertInfos + ".subject"
|
||||
SuffixFrontendPassTLSClientCertInfosNotAfter = SuffixFrontendPassTLSClientCertInfos + ".notAfter"
|
||||
SuffixFrontendPassTLSClientCertInfosNotBefore = SuffixFrontendPassTLSClientCertInfos + ".notBefore"
|
||||
SuffixFrontendPassTLSClientCertInfosSans = SuffixFrontendPassTLSClientCertInfos + ".sans"
|
||||
SuffixFrontendPassTLSClientCertInfosSubjectCommonName = SuffixFrontendPassTLSClientCertInfosSubject + ".commonName"
|
||||
SuffixFrontendPassTLSClientCertInfosSubjectCountry = SuffixFrontendPassTLSClientCertInfosSubject + ".country"
|
||||
SuffixFrontendPassTLSClientCertInfosSubjectDomainComponent = SuffixFrontendPassTLSClientCertInfosSubject + ".domainComponent"
|
||||
SuffixFrontendPassTLSClientCertInfosSubjectLocality = SuffixFrontendPassTLSClientCertInfosSubject + ".locality"
|
||||
SuffixFrontendPassTLSClientCertInfosSubjectOrganization = SuffixFrontendPassTLSClientCertInfosSubject + ".organization"
|
||||
SuffixFrontendPassTLSClientCertInfosSubjectProvince = SuffixFrontendPassTLSClientCertInfosSubject + ".province"
|
||||
SuffixFrontendPassTLSClientCertInfosSubjectSerialNumber = SuffixFrontendPassTLSClientCertInfosSubject + ".serialNumber"
|
||||
SuffixFrontendPassTLSCert = "frontend.passTLSCert" // Deprecated
|
||||
SuffixFrontendPriority = "frontend.priority"
|
||||
SuffixFrontendRateLimitExtractorFunc = "frontend.rateLimit.extractorFunc"
|
||||
SuffixFrontendRedirectEntryPoint = "frontend.redirect.entryPoint"
|
||||
SuffixFrontendRedirectRegex = "frontend.redirect.regex"
|
||||
SuffixFrontendRedirectReplacement = "frontend.redirect.replacement"
|
||||
SuffixFrontendRedirectPermanent = "frontend.redirect.permanent"
|
||||
SuffixFrontendRule = "frontend.rule"
|
||||
SuffixFrontendWhitelistSourceRange = "frontend.whitelistSourceRange" // Deprecated
|
||||
SuffixFrontendWhiteList = "frontend.whiteList."
|
||||
SuffixFrontendWhiteListSourceRange = SuffixFrontendWhiteList + "sourceRange"
|
||||
SuffixFrontendWhiteListUseXForwardedFor = SuffixFrontendWhiteList + "useXForwardedFor"
|
||||
TraefikDomain = Prefix + SuffixDomain
|
||||
TraefikEnable = Prefix + SuffixEnable
|
||||
TraefikPort = Prefix + SuffixPort
|
||||
TraefikPortName = Prefix + SuffixPortName
|
||||
TraefikPortIndex = Prefix + SuffixPortIndex
|
||||
TraefikProtocol = Prefix + SuffixProtocol
|
||||
TraefikTags = Prefix + SuffixTags
|
||||
TraefikWeight = Prefix + SuffixWeight
|
||||
TraefikBackend = Prefix + SuffixBackend
|
||||
TraefikBackendID = Prefix + SuffixBackendID
|
||||
TraefikBackendCircuitBreaker = Prefix + SuffixBackendCircuitBreaker
|
||||
TraefikBackendCircuitBreakerExpression = Prefix + SuffixBackendCircuitBreakerExpression
|
||||
TraefikBackendHealthCheckScheme = Prefix + SuffixBackendHealthCheckScheme
|
||||
TraefikBackendHealthCheckPath = Prefix + SuffixBackendHealthCheckPath
|
||||
TraefikBackendHealthCheckPort = Prefix + SuffixBackendHealthCheckPort
|
||||
TraefikBackendHealthCheckInterval = Prefix + SuffixBackendHealthCheckInterval
|
||||
TraefikBackendHealthCheckHostname = Prefix + SuffixBackendHealthCheckHostname
|
||||
TraefikBackendHealthCheckHeaders = Prefix + SuffixBackendHealthCheckHeaders
|
||||
TraefikBackendLoadBalancer = Prefix + SuffixBackendLoadBalancer
|
||||
TraefikBackendLoadBalancerMethod = Prefix + SuffixBackendLoadBalancerMethod
|
||||
TraefikBackendLoadBalancerSticky = Prefix + SuffixBackendLoadBalancerSticky
|
||||
TraefikBackendLoadBalancerStickiness = Prefix + SuffixBackendLoadBalancerStickiness
|
||||
TraefikBackendLoadBalancerStickinessCookieName = Prefix + SuffixBackendLoadBalancerStickinessCookieName
|
||||
Prefix = "traefik."
|
||||
SuffixBackend = "backend"
|
||||
SuffixDomain = "domain"
|
||||
SuffixEnable = "enable"
|
||||
SuffixPort = "port"
|
||||
SuffixPortName = "portName"
|
||||
SuffixPortIndex = "portIndex"
|
||||
SuffixProtocol = "protocol"
|
||||
SuffixTags = "tags"
|
||||
SuffixWeight = "weight"
|
||||
SuffixBackendID = "backend.id"
|
||||
SuffixBackendCircuitBreaker = "backend.circuitbreaker"
|
||||
SuffixBackendCircuitBreakerExpression = "backend.circuitbreaker.expression"
|
||||
SuffixBackendHealthCheckScheme = "backend.healthcheck.scheme"
|
||||
SuffixBackendHealthCheckPath = "backend.healthcheck.path"
|
||||
SuffixBackendHealthCheckPort = "backend.healthcheck.port"
|
||||
SuffixBackendHealthCheckInterval = "backend.healthcheck.interval"
|
||||
SuffixBackendHealthCheckHostname = "backend.healthcheck.hostname"
|
||||
SuffixBackendHealthCheckHeaders = "backend.healthcheck.headers"
|
||||
SuffixBackendLoadBalancer = "backend.loadbalancer"
|
||||
SuffixBackendLoadBalancerMethod = SuffixBackendLoadBalancer + ".method"
|
||||
SuffixBackendLoadBalancerSticky = SuffixBackendLoadBalancer + ".sticky"
|
||||
SuffixBackendLoadBalancerStickiness = SuffixBackendLoadBalancer + ".stickiness"
|
||||
SuffixBackendLoadBalancerStickinessCookieName = SuffixBackendLoadBalancer + ".stickiness.cookieName"
|
||||
SuffixBackendLoadBalancerStickinessSecure = SuffixBackendLoadBalancer + ".stickiness.secure"
|
||||
SuffixBackendLoadBalancerStickinessCHTTPOnly = SuffixBackendLoadBalancer + ".stickiness.httpOnly"
|
||||
SuffixBackendLoadBalancerStickinessSameSite = SuffixBackendLoadBalancer + ".stickiness.sameSite"
|
||||
SuffixBackendMaxConnAmount = "backend.maxconn.amount"
|
||||
SuffixBackendMaxConnExtractorFunc = "backend.maxconn.extractorfunc"
|
||||
SuffixBackendBuffering = "backend.buffering"
|
||||
SuffixBackendResponseForwardingFlushInterval = "backend.responseForwarding.flushInterval"
|
||||
SuffixBackendBufferingMaxRequestBodyBytes = SuffixBackendBuffering + ".maxRequestBodyBytes"
|
||||
SuffixBackendBufferingMemRequestBodyBytes = SuffixBackendBuffering + ".memRequestBodyBytes"
|
||||
SuffixBackendBufferingMaxResponseBodyBytes = SuffixBackendBuffering + ".maxResponseBodyBytes"
|
||||
SuffixBackendBufferingMemResponseBodyBytes = SuffixBackendBuffering + ".memResponseBodyBytes"
|
||||
SuffixBackendBufferingRetryExpression = SuffixBackendBuffering + ".retryExpression"
|
||||
SuffixFrontend = "frontend"
|
||||
SuffixFrontendAuth = SuffixFrontend + ".auth"
|
||||
SuffixFrontendAuthBasic = SuffixFrontendAuth + ".basic"
|
||||
SuffixFrontendAuthBasicRemoveHeader = SuffixFrontendAuthBasic + ".removeHeader"
|
||||
SuffixFrontendAuthBasicUsers = SuffixFrontendAuthBasic + ".users"
|
||||
SuffixFrontendAuthBasicUsersFile = SuffixFrontendAuthBasic + ".usersFile"
|
||||
SuffixFrontendAuthDigest = SuffixFrontendAuth + ".digest"
|
||||
SuffixFrontendAuthDigestRemoveHeader = SuffixFrontendAuthDigest + ".removeHeader"
|
||||
SuffixFrontendAuthDigestUsers = SuffixFrontendAuthDigest + ".users"
|
||||
SuffixFrontendAuthDigestUsersFile = SuffixFrontendAuthDigest + ".usersFile"
|
||||
SuffixFrontendAuthForward = SuffixFrontendAuth + ".forward"
|
||||
SuffixFrontendAuthForwardAddress = SuffixFrontendAuthForward + ".address"
|
||||
SuffixFrontendAuthForwardAuthResponseHeaders = SuffixFrontendAuthForward + ".authResponseHeaders"
|
||||
SuffixFrontendAuthForwardTLS = SuffixFrontendAuthForward + ".tls"
|
||||
SuffixFrontendAuthForwardTLSCa = SuffixFrontendAuthForwardTLS + ".ca"
|
||||
SuffixFrontendAuthForwardTLSCaOptional = SuffixFrontendAuthForwardTLS + ".caOptional"
|
||||
SuffixFrontendAuthForwardTLSCert = SuffixFrontendAuthForwardTLS + ".cert"
|
||||
SuffixFrontendAuthForwardTLSInsecureSkipVerify = SuffixFrontendAuthForwardTLS + ".insecureSkipVerify"
|
||||
SuffixFrontendAuthForwardTLSKey = SuffixFrontendAuthForwardTLS + ".key"
|
||||
SuffixFrontendAuthForwardTrustForwardHeader = SuffixFrontendAuthForward + ".trustForwardHeader"
|
||||
SuffixFrontendAuthHeaderField = SuffixFrontendAuth + ".headerField"
|
||||
SuffixFrontendEntryPoints = "frontend.entryPoints"
|
||||
SuffixFrontendHeaders = "frontend.headers."
|
||||
SuffixFrontendRequestHeaders = SuffixFrontendHeaders + "customRequestHeaders"
|
||||
SuffixFrontendResponseHeaders = SuffixFrontendHeaders + "customResponseHeaders"
|
||||
SuffixFrontendHeadersAllowedHosts = SuffixFrontendHeaders + "allowedHosts"
|
||||
SuffixFrontendHeadersHostsProxyHeaders = SuffixFrontendHeaders + "hostsProxyHeaders"
|
||||
SuffixFrontendHeadersSSLForceHost = SuffixFrontendHeaders + "SSLForceHost"
|
||||
SuffixFrontendHeadersSSLRedirect = SuffixFrontendHeaders + "SSLRedirect"
|
||||
SuffixFrontendHeadersSSLTemporaryRedirect = SuffixFrontendHeaders + "SSLTemporaryRedirect"
|
||||
SuffixFrontendHeadersSSLHost = SuffixFrontendHeaders + "SSLHost"
|
||||
SuffixFrontendHeadersSSLProxyHeaders = SuffixFrontendHeaders + "SSLProxyHeaders"
|
||||
SuffixFrontendHeadersSTSSeconds = SuffixFrontendHeaders + "STSSeconds"
|
||||
SuffixFrontendHeadersSTSIncludeSubdomains = SuffixFrontendHeaders + "STSIncludeSubdomains"
|
||||
SuffixFrontendHeadersSTSPreload = SuffixFrontendHeaders + "STSPreload"
|
||||
SuffixFrontendHeadersForceSTSHeader = SuffixFrontendHeaders + "forceSTSHeader"
|
||||
SuffixFrontendHeadersFrameDeny = SuffixFrontendHeaders + "frameDeny"
|
||||
SuffixFrontendHeadersCustomFrameOptionsValue = SuffixFrontendHeaders + "customFrameOptionsValue"
|
||||
SuffixFrontendHeadersContentTypeNosniff = SuffixFrontendHeaders + "contentTypeNosniff"
|
||||
SuffixFrontendHeadersBrowserXSSFilter = SuffixFrontendHeaders + "browserXSSFilter"
|
||||
SuffixFrontendHeadersCustomBrowserXSSValue = SuffixFrontendHeaders + "customBrowserXSSValue"
|
||||
SuffixFrontendHeadersContentSecurityPolicy = SuffixFrontendHeaders + "contentSecurityPolicy"
|
||||
SuffixFrontendHeadersPublicKey = SuffixFrontendHeaders + "publicKey"
|
||||
SuffixFrontendHeadersReferrerPolicy = SuffixFrontendHeaders + "referrerPolicy"
|
||||
SuffixFrontendHeadersIsDevelopment = SuffixFrontendHeaders + "isDevelopment"
|
||||
SuffixFrontendPassHostHeader = "frontend.passHostHeader"
|
||||
SuffixFrontendPassTLSClientCert = "frontend.passTLSClientCert"
|
||||
SuffixFrontendPassTLSClientCertPem = SuffixFrontendPassTLSClientCert + ".pem"
|
||||
SuffixFrontendPassTLSClientCertInfos = SuffixFrontendPassTLSClientCert + ".infos"
|
||||
SuffixFrontendPassTLSClientCertInfosIssuer = SuffixFrontendPassTLSClientCertInfos + ".issuer"
|
||||
SuffixFrontendPassTLSClientCertInfosIssuerCommonName = SuffixFrontendPassTLSClientCertInfosIssuer + ".commonName"
|
||||
SuffixFrontendPassTLSClientCertInfosIssuerCountry = SuffixFrontendPassTLSClientCertInfosIssuer + ".country"
|
||||
SuffixFrontendPassTLSClientCertInfosIssuerDomainComponent = SuffixFrontendPassTLSClientCertInfosIssuer + ".domainComponent"
|
||||
SuffixFrontendPassTLSClientCertInfosIssuerLocality = SuffixFrontendPassTLSClientCertInfosIssuer + ".locality"
|
||||
SuffixFrontendPassTLSClientCertInfosIssuerOrganization = SuffixFrontendPassTLSClientCertInfosIssuer + ".organization"
|
||||
SuffixFrontendPassTLSClientCertInfosIssuerProvince = SuffixFrontendPassTLSClientCertInfosIssuer + ".province"
|
||||
SuffixFrontendPassTLSClientCertInfosIssuerSerialNumber = SuffixFrontendPassTLSClientCertInfosIssuer + ".serialNumber"
|
||||
SuffixFrontendPassTLSClientCertInfosSubject = SuffixFrontendPassTLSClientCertInfos + ".subject"
|
||||
SuffixFrontendPassTLSClientCertInfosNotAfter = SuffixFrontendPassTLSClientCertInfos + ".notAfter"
|
||||
SuffixFrontendPassTLSClientCertInfosNotBefore = SuffixFrontendPassTLSClientCertInfos + ".notBefore"
|
||||
SuffixFrontendPassTLSClientCertInfosSans = SuffixFrontendPassTLSClientCertInfos + ".sans"
|
||||
SuffixFrontendPassTLSClientCertInfosSubjectCommonName = SuffixFrontendPassTLSClientCertInfosSubject + ".commonName"
|
||||
SuffixFrontendPassTLSClientCertInfosSubjectCountry = SuffixFrontendPassTLSClientCertInfosSubject + ".country"
|
||||
SuffixFrontendPassTLSClientCertInfosSubjectDomainComponent = SuffixFrontendPassTLSClientCertInfosSubject + ".domainComponent"
|
||||
SuffixFrontendPassTLSClientCertInfosSubjectLocality = SuffixFrontendPassTLSClientCertInfosSubject + ".locality"
|
||||
SuffixFrontendPassTLSClientCertInfosSubjectOrganization = SuffixFrontendPassTLSClientCertInfosSubject + ".organization"
|
||||
SuffixFrontendPassTLSClientCertInfosSubjectProvince = SuffixFrontendPassTLSClientCertInfosSubject + ".province"
|
||||
SuffixFrontendPassTLSClientCertInfosSubjectSerialNumber = SuffixFrontendPassTLSClientCertInfosSubject + ".serialNumber"
|
||||
SuffixFrontendPassTLSCert = "frontend.passTLSCert" // Deprecated
|
||||
SuffixFrontendPriority = "frontend.priority"
|
||||
SuffixFrontendRateLimitExtractorFunc = "frontend.rateLimit.extractorFunc"
|
||||
SuffixFrontendRedirectEntryPoint = "frontend.redirect.entryPoint"
|
||||
SuffixFrontendRedirectRegex = "frontend.redirect.regex"
|
||||
SuffixFrontendRedirectReplacement = "frontend.redirect.replacement"
|
||||
SuffixFrontendRedirectPermanent = "frontend.redirect.permanent"
|
||||
SuffixFrontendRule = "frontend.rule"
|
||||
SuffixFrontendWhitelistSourceRange = "frontend.whitelistSourceRange" // Deprecated
|
||||
SuffixFrontendWhiteList = "frontend.whiteList."
|
||||
SuffixFrontendWhiteListSourceRange = SuffixFrontendWhiteList + "sourceRange"
|
||||
SuffixFrontendWhiteListUseXForwardedFor = SuffixFrontendWhiteList + "useXForwardedFor"
|
||||
TraefikDomain = Prefix + SuffixDomain
|
||||
TraefikEnable = Prefix + SuffixEnable
|
||||
TraefikPort = Prefix + SuffixPort
|
||||
TraefikPortName = Prefix + SuffixPortName
|
||||
TraefikPortIndex = Prefix + SuffixPortIndex
|
||||
TraefikProtocol = Prefix + SuffixProtocol
|
||||
TraefikTags = Prefix + SuffixTags
|
||||
TraefikWeight = Prefix + SuffixWeight
|
||||
TraefikBackend = Prefix + SuffixBackend
|
||||
TraefikBackendID = Prefix + SuffixBackendID
|
||||
TraefikBackendCircuitBreaker = Prefix + SuffixBackendCircuitBreaker
|
||||
TraefikBackendCircuitBreakerExpression = Prefix + SuffixBackendCircuitBreakerExpression
|
||||
TraefikBackendHealthCheckScheme = Prefix + SuffixBackendHealthCheckScheme
|
||||
TraefikBackendHealthCheckPath = Prefix + SuffixBackendHealthCheckPath
|
||||
TraefikBackendHealthCheckPort = Prefix + SuffixBackendHealthCheckPort
|
||||
TraefikBackendHealthCheckInterval = Prefix + SuffixBackendHealthCheckInterval
|
||||
TraefikBackendHealthCheckHostname = Prefix + SuffixBackendHealthCheckHostname
|
||||
TraefikBackendHealthCheckHeaders = Prefix + SuffixBackendHealthCheckHeaders
|
||||
TraefikBackendLoadBalancer = Prefix + SuffixBackendLoadBalancer
|
||||
TraefikBackendLoadBalancerMethod = Prefix + SuffixBackendLoadBalancerMethod
|
||||
TraefikBackendLoadBalancerSticky = Prefix + SuffixBackendLoadBalancerSticky
|
||||
TraefikBackendLoadBalancerStickiness = Prefix + SuffixBackendLoadBalancerStickiness
|
||||
TraefikBackendLoadBalancerStickinessCookieName = Prefix + SuffixBackendLoadBalancerStickinessCookieName
|
||||
// FIXME
|
||||
TraefikBackendLoadBalancerStickinessSecure = Prefix + SuffixBackendLoadBalancerStickinessSecure
|
||||
TraefikBackendLoadBalancerStickinessHTTPOnly = Prefix + SuffixBackendLoadBalancerStickinessCHTTPOnly
|
||||
TraefikBackendLoadBalancerStickinessSameSite = Prefix + SuffixBackendLoadBalancerStickinessSameSite
|
||||
|
||||
TraefikBackendMaxConnAmount = Prefix + SuffixBackendMaxConnAmount
|
||||
TraefikBackendMaxConnExtractorFunc = Prefix + SuffixBackendMaxConnExtractorFunc
|
||||
TraefikBackendBuffering = Prefix + SuffixBackendBuffering
|
||||
|
||||
@@ -50,8 +50,13 @@ func GetRedirect(labels map[string]string) *types.Redirect {
|
||||
|
||||
if Has(labels, TraefikFrontendRedirectRegex) &&
|
||||
Has(labels, TraefikFrontendRedirectReplacement) {
|
||||
value, err := GetStringSafeValue(labels, TraefikFrontendRedirectRegex, "")
|
||||
if err != nil {
|
||||
log.Errorf("Invalid regex syntax: %s", value)
|
||||
return nil
|
||||
}
|
||||
return &types.Redirect{
|
||||
Regex: GetStringValue(labels, TraefikFrontendRedirectRegex, ""),
|
||||
Regex: value,
|
||||
Replacement: GetStringValue(labels, TraefikFrontendRedirectReplacement, ""),
|
||||
Permanent: permanent,
|
||||
}
|
||||
@@ -421,8 +426,12 @@ func GetLoadBalancer(labels map[string]string) *types.LoadBalancer {
|
||||
}
|
||||
|
||||
if GetBoolValue(labels, TraefikBackendLoadBalancerStickiness, false) {
|
||||
cookieName := GetStringValue(labels, TraefikBackendLoadBalancerStickinessCookieName, DefaultBackendLoadbalancerStickinessCookieName)
|
||||
lb.Stickiness = &types.Stickiness{CookieName: cookieName}
|
||||
lb.Stickiness = &types.Stickiness{
|
||||
CookieName: GetStringValue(labels, TraefikBackendLoadBalancerStickinessCookieName, DefaultBackendLoadbalancerStickinessCookieName),
|
||||
Secure: GetBoolValue(labels, TraefikBackendLoadBalancerStickinessSecure, DefaultBackendLoadbalancerStickinessSecure),
|
||||
HTTPOnly: GetBoolValue(labels, TraefikBackendLoadBalancerStickinessHTTPOnly, DefaultBackendLoadbalancerStickinessHTTPOnly),
|
||||
SameSite: GetStringValue(labels, TraefikBackendLoadBalancerStickinessSameSite, DefaultBackendLoadbalancerStickinessSameSite),
|
||||
}
|
||||
}
|
||||
|
||||
return lb
|
||||
|
||||
@@ -257,12 +257,18 @@ func TestGetLoadBalancer(t *testing.T) {
|
||||
TraefikBackendLoadBalancerSticky: "true",
|
||||
TraefikBackendLoadBalancerStickiness: "true",
|
||||
TraefikBackendLoadBalancerStickinessCookieName: "foo",
|
||||
TraefikBackendLoadBalancerStickinessSecure: "true",
|
||||
TraefikBackendLoadBalancerStickinessHTTPOnly: "true",
|
||||
TraefikBackendLoadBalancerStickinessSameSite: "none",
|
||||
},
|
||||
expected: &types.LoadBalancer{
|
||||
Method: "drr",
|
||||
Sticky: true,
|
||||
Stickiness: &types.Stickiness{
|
||||
CookieName: "foo",
|
||||
Secure: true,
|
||||
HTTPOnly: true,
|
||||
SameSite: "none",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -449,12 +455,19 @@ func TestGetRedirect(t *testing.T) {
|
||||
labels map[string]string
|
||||
expected *types.Redirect
|
||||
}{
|
||||
|
||||
{
|
||||
desc: "should return nil when no redirect labels",
|
||||
labels: map[string]string{},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should return nil when regex syntax is invalid",
|
||||
labels: map[string]string{
|
||||
TraefikFrontendRedirectRegex: `^https?://(test\.beta\.redacted\.org|redacted\.com)/(.*)`,
|
||||
TraefikFrontendRedirectReplacement: "$1",
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
desc: "should use only entry point tag when mix regex redirect and entry point redirect",
|
||||
labels: map[string]string{
|
||||
@@ -805,6 +818,7 @@ func TestGetAuth(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPassTLSClientCert(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
|
||||
@@ -101,6 +101,9 @@ func TestBuildConfigurationSegments(t *testing.T) {
|
||||
withLabel(label.TraefikBackendLoadBalancerSticky, "true"),
|
||||
withLabel(label.TraefikBackendLoadBalancerStickiness, "true"),
|
||||
withLabel(label.TraefikBackendLoadBalancerStickinessCookieName, "chocolate"),
|
||||
withLabel(label.TraefikBackendLoadBalancerStickinessSecure, "true"),
|
||||
withLabel(label.TraefikBackendLoadBalancerStickinessHTTPOnly, "true"),
|
||||
withLabel(label.TraefikBackendLoadBalancerStickinessSameSite, "none"),
|
||||
withLabel(label.TraefikBackendMaxConnAmount, "666"),
|
||||
withLabel(label.TraefikBackendMaxConnExtractorFunc, "client.ip"),
|
||||
withLabel(label.TraefikBackendBufferingMaxResponseBodyBytes, "10485760"),
|
||||
@@ -347,6 +350,9 @@ func TestBuildConfigurationSegments(t *testing.T) {
|
||||
Sticky: true,
|
||||
Stickiness: &types.Stickiness{
|
||||
CookieName: "chocolate",
|
||||
Secure: true,
|
||||
HTTPOnly: true,
|
||||
SameSite: "none",
|
||||
},
|
||||
},
|
||||
MaxConn: &types.MaxConn{
|
||||
|
||||
@@ -369,6 +369,9 @@ func TestBuildConfiguration(t *testing.T) {
|
||||
withLabel(label.TraefikBackendLoadBalancerSticky, "true"),
|
||||
withLabel(label.TraefikBackendLoadBalancerStickiness, "true"),
|
||||
withLabel(label.TraefikBackendLoadBalancerStickinessCookieName, "chocolate"),
|
||||
withLabel(label.TraefikBackendLoadBalancerStickinessSecure, "true"),
|
||||
withLabel(label.TraefikBackendLoadBalancerStickinessHTTPOnly, "true"),
|
||||
withLabel(label.TraefikBackendLoadBalancerStickinessSameSite, "none"),
|
||||
withLabel(label.TraefikBackendMaxConnAmount, "666"),
|
||||
withLabel(label.TraefikBackendMaxConnExtractorFunc, "client.ip"),
|
||||
withLabel(label.TraefikBackendBufferingMaxResponseBodyBytes, "10485760"),
|
||||
@@ -613,6 +616,9 @@ func TestBuildConfiguration(t *testing.T) {
|
||||
Sticky: true,
|
||||
Stickiness: &types.Stickiness{
|
||||
CookieName: "chocolate",
|
||||
Secure: true,
|
||||
HTTPOnly: true,
|
||||
SameSite: "none",
|
||||
},
|
||||
},
|
||||
MaxConn: &types.MaxConn{
|
||||
|
||||
@@ -119,6 +119,9 @@ func TestBuildConfigurationSegments(t *testing.T) {
|
||||
withLabel(label.TraefikBackendLoadBalancerSticky, "true"),
|
||||
withLabel(label.TraefikBackendLoadBalancerStickiness, "true"),
|
||||
withLabel(label.TraefikBackendLoadBalancerStickinessCookieName, "chocolate"),
|
||||
withLabel(label.TraefikBackendLoadBalancerStickinessSecure, "true"),
|
||||
withLabel(label.TraefikBackendLoadBalancerStickinessHTTPOnly, "true"),
|
||||
withLabel(label.TraefikBackendLoadBalancerStickinessSameSite, "none"),
|
||||
withLabel(label.TraefikBackendMaxConnAmount, "666"),
|
||||
withLabel(label.TraefikBackendMaxConnExtractorFunc, "client.ip"),
|
||||
withLabel(label.TraefikBackendBufferingMaxResponseBodyBytes, "10485760"),
|
||||
@@ -368,6 +371,9 @@ func TestBuildConfigurationSegments(t *testing.T) {
|
||||
Sticky: true,
|
||||
Stickiness: &types.Stickiness{
|
||||
CookieName: "chocolate",
|
||||
Secure: true,
|
||||
HTTPOnly: true,
|
||||
SameSite: "none",
|
||||
},
|
||||
},
|
||||
MaxConn: &types.MaxConn{
|
||||
|
||||
@@ -325,6 +325,9 @@ func TestBuildConfiguration(t *testing.T) {
|
||||
withLabel(label.TraefikBackendLoadBalancerMethod, "drr"),
|
||||
withLabel(label.TraefikBackendLoadBalancerStickiness, "true"),
|
||||
withLabel(label.TraefikBackendLoadBalancerStickinessCookieName, "chocolate"),
|
||||
withLabel(label.TraefikBackendLoadBalancerStickinessSecure, "true"),
|
||||
withLabel(label.TraefikBackendLoadBalancerStickinessHTTPOnly, "true"),
|
||||
withLabel(label.TraefikBackendLoadBalancerStickinessSameSite, "none"),
|
||||
withLabel(label.TraefikBackendMaxConnAmount, "666"),
|
||||
withLabel(label.TraefikBackendMaxConnExtractorFunc, "client.ip"),
|
||||
withLabel(label.TraefikBackendBufferingMaxResponseBodyBytes, "10485760"),
|
||||
@@ -572,6 +575,9 @@ func TestBuildConfiguration(t *testing.T) {
|
||||
Method: "drr",
|
||||
Stickiness: &types.Stickiness{
|
||||
CookieName: "chocolate",
|
||||
Secure: true,
|
||||
HTTPOnly: true,
|
||||
SameSite: "none",
|
||||
},
|
||||
},
|
||||
MaxConn: &types.MaxConn{
|
||||
|
||||
@@ -52,6 +52,9 @@ func TestProviderBuildConfiguration(t *testing.T) {
|
||||
label.TraefikBackendLoadBalancerSticky: "true",
|
||||
label.TraefikBackendLoadBalancerStickiness: "true",
|
||||
label.TraefikBackendLoadBalancerStickinessCookieName: "chocolate",
|
||||
label.TraefikBackendLoadBalancerStickinessSecure: "true",
|
||||
label.TraefikBackendLoadBalancerStickinessHTTPOnly: "true",
|
||||
label.TraefikBackendLoadBalancerStickinessSameSite: "none",
|
||||
label.TraefikBackendMaxConnAmount: "666",
|
||||
label.TraefikBackendMaxConnExtractorFunc: "client.ip",
|
||||
label.TraefikBackendBufferingMaxResponseBodyBytes: "10485760",
|
||||
@@ -304,6 +307,9 @@ func TestProviderBuildConfiguration(t *testing.T) {
|
||||
Sticky: true,
|
||||
Stickiness: &types.Stickiness{
|
||||
CookieName: "chocolate",
|
||||
Secure: true,
|
||||
HTTPOnly: true,
|
||||
SameSite: "none",
|
||||
},
|
||||
},
|
||||
MaxConn: &types.MaxConn{
|
||||
|
||||
@@ -218,7 +218,10 @@ func NewServer(globalConfiguration configuration.GlobalConfiguration, provider p
|
||||
log.Errorf("failed to create HTTP transport: %v", err)
|
||||
}
|
||||
|
||||
server.defaultForwardingRoundTripper = transport
|
||||
server.defaultForwardingRoundTripper, err = newSmartRoundTripper(transport)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to create HTTP transport: %v", err)
|
||||
}
|
||||
|
||||
server.tracingMiddleware = globalConfiguration.Tracing
|
||||
if server.tracingMiddleware != nil && server.tracingMiddleware.Backend != "" {
|
||||
@@ -497,6 +500,12 @@ func (s *Server) createTLSConfig(entryPointName string, tlsOption *traefiktls.TL
|
||||
config.Certificates = []tls.Certificate{}
|
||||
}
|
||||
|
||||
// workaround for users who used GODEBUG to activate TLS1.3
|
||||
config.MaxVersion = tls.VersionTLS12
|
||||
if strings.Contains(os.Getenv("GODEBUG"), "tls13=1") {
|
||||
config.MaxVersion = tls.VersionTLS13
|
||||
}
|
||||
|
||||
// Set the minimum TLS version if set in the config TOML
|
||||
if minConst, exists := traefiktls.MinVersion[s.entryPoints[entryPointName].Configuration.TLS.MinVersion]; exists {
|
||||
config.PreferServerCipherSuites = true
|
||||
@@ -505,7 +514,7 @@ func (s *Server) createTLSConfig(entryPointName string, tlsOption *traefiktls.TL
|
||||
|
||||
// Set the list of CipherSuites if set in the config TOML
|
||||
if s.entryPoints[entryPointName].Configuration.TLS.CipherSuites != nil {
|
||||
// if our list of CipherSuites is defined in the entrypoint config, we can re-initilize the suites list as empty
|
||||
// if our list of CipherSuites is defined in the entrypoint config, we can re-initialize the suites list as empty
|
||||
config.CipherSuites = make([]uint16, 0)
|
||||
for _, cipher := range s.entryPoints[entryPointName].Configuration.TLS.CipherSuites {
|
||||
if cipherConst, exists := traefiktls.CipherSuites[cipher]; exists {
|
||||
|
||||
@@ -237,11 +237,17 @@ func (s *Server) buildForwarder(entryPointName string, entryPoint *configuration
|
||||
}
|
||||
}
|
||||
|
||||
var tlsConfig *tls.Config
|
||||
if smartRt, ok := roundTripper.(*smartRoundTripper); ok {
|
||||
tlsConfig = smartRt.GetTLSClientConfig()
|
||||
}
|
||||
|
||||
var fwd http.Handler
|
||||
fwd, err = forward.New(
|
||||
forward.Stream(true),
|
||||
forward.PassHostHeader(frontend.PassHostHeader),
|
||||
forward.RoundTripper(roundTripper),
|
||||
forward.WebsocketTLSClientConfig(tlsConfig),
|
||||
forward.Rewriter(rewriter),
|
||||
forward.ResponseModifier(responseModifier),
|
||||
forward.BufferPool(s.bufferPool),
|
||||
@@ -303,7 +309,6 @@ func buildServerRoute(serverEntryPoint *serverEntryPoint, frontendName string, f
|
||||
func (s *Server) preLoadConfiguration(configMsg types.ConfigMessage) {
|
||||
providersThrottleDuration := time.Duration(s.globalConfiguration.ProvidersThrottleDuration)
|
||||
s.defaultConfigurationValues(configMsg.Configuration)
|
||||
currentConfigurations := s.currentConfigurations.Get().(types.Configurations)
|
||||
|
||||
if log.GetLevel() == logrus.DebugLevel {
|
||||
jsonConf, _ := json.Marshal(configMsg.Configuration)
|
||||
@@ -315,11 +320,6 @@ func (s *Server) preLoadConfiguration(configMsg types.ConfigMessage) {
|
||||
return
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(currentConfigurations[configMsg.ProviderName], configMsg.Configuration) {
|
||||
log.Infof("Skipping same configuration for provider %s", configMsg.ProviderName)
|
||||
return
|
||||
}
|
||||
|
||||
providerConfigUpdateCh, ok := s.providerConfigUpdateMap[configMsg.ProviderName]
|
||||
if !ok {
|
||||
providerConfigUpdateCh = make(chan types.ConfigMessage)
|
||||
@@ -455,11 +455,17 @@ func (s *Server) throttleProviderConfigReload(throttle time.Duration, publish ch
|
||||
}
|
||||
})
|
||||
|
||||
var previousConfig types.ConfigMessage
|
||||
for {
|
||||
select {
|
||||
case <-stop:
|
||||
return
|
||||
case nextConfig := <-in:
|
||||
if reflect.DeepEqual(previousConfig, nextConfig) {
|
||||
log.Infof("Skipping same configuration for provider %s", nextConfig.ProviderName)
|
||||
continue
|
||||
}
|
||||
previousConfig = nextConfig
|
||||
ring.In() <- nextConfig
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,9 +304,11 @@ func TestThrottleProviderConfigReload(t *testing.T) {
|
||||
}
|
||||
}()
|
||||
|
||||
// publish 5 new configs, one new config each 10 milliseconds
|
||||
// publish 5 new and different configs, one new config each 10 milliseconds
|
||||
for i := 0; i < 5; i++ {
|
||||
providerConfig <- types.ConfigMessage{}
|
||||
providerConfig <- types.ConfigMessage{
|
||||
ProviderName: fmt.Sprintf("test-%d", i),
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
|
||||
|
||||
@@ -123,13 +123,6 @@ func (s *Server) buildLoadBalancer(frontendName string, backendName string, back
|
||||
rr, _ = roundrobin.New(fwd)
|
||||
}
|
||||
|
||||
var stickySession *roundrobin.StickySession
|
||||
var cookieName string
|
||||
if stickiness := backend.LoadBalancer.Stickiness; stickiness != nil {
|
||||
cookieName = cookie.GetName(stickiness.CookieName, backendName)
|
||||
stickySession = roundrobin.NewStickySession(cookieName)
|
||||
}
|
||||
|
||||
lbMethod, err := types.NewLoadBalancerMethod(backend.LoadBalancer)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error loading load balancer method '%+v' for frontend %s: %v", backend.LoadBalancer, frontendName, err)
|
||||
@@ -141,8 +134,10 @@ func (s *Server) buildLoadBalancer(frontendName string, backendName string, back
|
||||
case types.Drr:
|
||||
log.Debug("Creating load-balancer drr")
|
||||
|
||||
if stickySession != nil {
|
||||
log.Debugf("Sticky session with cookie %v", cookieName)
|
||||
if backend.LoadBalancer.Stickiness != nil {
|
||||
cookieName := cookie.GetName(backend.LoadBalancer.Stickiness.CookieName, backendName)
|
||||
|
||||
stickySession := newStickySession(cookieName, backend.LoadBalancer.Stickiness)
|
||||
|
||||
lb, err = roundrobin.NewRebalancer(rr, roundrobin.RebalancerStickySession(stickySession))
|
||||
if err != nil {
|
||||
@@ -157,16 +152,19 @@ func (s *Server) buildLoadBalancer(frontendName string, backendName string, back
|
||||
case types.Wrr:
|
||||
log.Debug("Creating load-balancer wrr")
|
||||
|
||||
if stickySession != nil {
|
||||
log.Debugf("Sticky session with cookie %v", cookieName)
|
||||
if backend.LoadBalancer.Stickiness != nil {
|
||||
cookieName := cookie.GetName(backend.LoadBalancer.Stickiness.CookieName, backendName)
|
||||
|
||||
stickySession := newStickySession(cookieName, backend.LoadBalancer.Stickiness)
|
||||
|
||||
option := roundrobin.EnableStickySession(stickySession)
|
||||
if s.accessLoggerMiddleware != nil {
|
||||
lb, err = roundrobin.New(saveFrontend, roundrobin.EnableStickySession(stickySession))
|
||||
lb, err = roundrobin.New(saveFrontend, option)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
lb, err = roundrobin.New(fwd, roundrobin.EnableStickySession(stickySession))
|
||||
lb, err = roundrobin.New(fwd, option)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -185,6 +183,30 @@ func (s *Server) buildLoadBalancer(frontendName string, backendName string, back
|
||||
return lb, nil
|
||||
}
|
||||
|
||||
func newStickySession(cookieName string, stickiness *types.Stickiness) *roundrobin.StickySession {
|
||||
log.Debugf("Sticky session with cookie %v", cookieName)
|
||||
|
||||
opts := roundrobin.CookieOptions{
|
||||
HTTPOnly: stickiness.HTTPOnly,
|
||||
Secure: stickiness.Secure,
|
||||
SameSite: convertSameSite(stickiness.SameSite),
|
||||
}
|
||||
|
||||
return roundrobin.NewStickySessionWithOptions(cookieName, opts)
|
||||
}
|
||||
|
||||
func convertSameSite(sameSite string) http.SameSite {
|
||||
switch sameSite {
|
||||
case "none":
|
||||
return http.SameSiteNoneMode
|
||||
case "lax":
|
||||
return http.SameSiteLaxMode
|
||||
case "strict":
|
||||
return http.SameSiteStrictMode
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
func (s *Server) configureLBServers(lb healthcheck.BalancerHandler, backend *types.Backend, backendName string) error {
|
||||
for name, srv := range backend.Servers {
|
||||
u, err := url.Parse(srv.URL)
|
||||
@@ -206,23 +228,29 @@ func (s *Server) configureLBServers(lb healthcheck.BalancerHandler, backend *typ
|
||||
// getRoundTripper will either use server.defaultForwardingRoundTripper or create a new one
|
||||
// given a custom TLS configuration is passed and the passTLSCert option is set to true.
|
||||
func (s *Server) getRoundTripper(entryPointName string, passTLSCert bool, tls *traefiktls.TLS) (http.RoundTripper, error) {
|
||||
if passTLSCert {
|
||||
tlsConfig, err := createClientTLSConfig(entryPointName, tls)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create TLSClientConfig: %v", err)
|
||||
}
|
||||
tlsConfig.InsecureSkipVerify = s.globalConfiguration.InsecureSkipVerify
|
||||
|
||||
transport, err := createHTTPTransport(s.globalConfiguration)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create HTTP transport: %v", err)
|
||||
}
|
||||
|
||||
transport.TLSClientConfig = tlsConfig
|
||||
return transport, nil
|
||||
if !passTLSCert {
|
||||
return s.defaultForwardingRoundTripper, nil
|
||||
}
|
||||
|
||||
return s.defaultForwardingRoundTripper, nil
|
||||
tlsConfig, err := createClientTLSConfig(entryPointName, tls)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create TLSClientConfig: %v", err)
|
||||
}
|
||||
tlsConfig.InsecureSkipVerify = s.globalConfiguration.InsecureSkipVerify
|
||||
|
||||
transport, err := createHTTPTransport(s.globalConfiguration)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create HTTP transport: %v", err)
|
||||
}
|
||||
|
||||
transport.TLSClientConfig = tlsConfig
|
||||
|
||||
smartTransport, err := newSmartRoundTripper(transport)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return smartTransport, nil
|
||||
}
|
||||
|
||||
// createHTTPTransport creates an http.Transport configured with the GlobalConfiguration settings.
|
||||
@@ -263,25 +291,21 @@ func createHTTPTransport(globalConfiguration configuration.GlobalConfiguration)
|
||||
transport.ResponseHeaderTimeout = time.Duration(globalConfiguration.ForwardingTimeouts.ResponseHeaderTimeout)
|
||||
}
|
||||
|
||||
if globalConfiguration.InsecureSkipVerify {
|
||||
transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
|
||||
}
|
||||
|
||||
if len(globalConfiguration.RootCAs) > 0 {
|
||||
if globalConfiguration.InsecureSkipVerify || len(globalConfiguration.RootCAs) > 0 {
|
||||
transport.TLSClientConfig = &tls.Config{
|
||||
RootCAs: createRootCACertPool(globalConfiguration.RootCAs),
|
||||
InsecureSkipVerify: globalConfiguration.InsecureSkipVerify,
|
||||
RootCAs: createRootCACertPool(globalConfiguration.RootCAs),
|
||||
}
|
||||
}
|
||||
|
||||
err := http2.ConfigureTransport(transport)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return transport, nil
|
||||
}
|
||||
|
||||
func createRootCACertPool(rootCAs traefiktls.FilesOrContents) *x509.CertPool {
|
||||
if len(rootCAs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
roots := x509.NewCertPool()
|
||||
|
||||
for _, cert := range rootCAs {
|
||||
|
||||
43
server/smart_roundtripper.go
Normal file
43
server/smart_roundtripper.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net/http"
|
||||
|
||||
"golang.org/x/net/http/httpguts"
|
||||
"golang.org/x/net/http2"
|
||||
)
|
||||
|
||||
func newSmartRoundTripper(transport *http.Transport) (http.RoundTripper, error) {
|
||||
transportHTTP1 := transport.Clone()
|
||||
|
||||
err := http2.ConfigureTransport(transport)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &smartRoundTripper{
|
||||
http2: transport,
|
||||
http: transportHTTP1,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// smartRoundTripper implements RoundTrip while making sure that HTTP/2 is not used
|
||||
// with protocols that start with a Connection Upgrade, such as SPDY or Websocket.
|
||||
type smartRoundTripper struct {
|
||||
http2 *http.Transport
|
||||
http *http.Transport
|
||||
}
|
||||
|
||||
func (m *smartRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
// If we have a connection upgrade, we don't use HTTP2
|
||||
if httpguts.HeaderValuesContainsToken(req.Header["Connection"], "Upgrade") {
|
||||
return m.http.RoundTrip(req)
|
||||
}
|
||||
|
||||
return m.http2.RoundTrip(req)
|
||||
}
|
||||
|
||||
func (m *smartRoundTripper) GetTLSClientConfig() *tls.Config {
|
||||
return m.http2.TLSClientConfig
|
||||
}
|
||||
@@ -24,6 +24,9 @@
|
||||
{{if $loadBalancer.Stickiness }}
|
||||
[backends."backend-{{ $backendName }}".loadBalancer.stickiness]
|
||||
cookieName = "{{ $loadBalancer.Stickiness.CookieName }}"
|
||||
secure = {{ $loadBalancer.Stickiness.Secure }}
|
||||
httpOnly = {{ $loadBalancer.Stickiness.HTTPOnly }}
|
||||
sameSite = "{{ $loadBalancer.Stickiness.SameSite }}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
|
||||
@@ -23,6 +23,9 @@
|
||||
{{if $loadBalancer.Stickiness }}
|
||||
[backends."backend-{{ $backendName }}".loadBalancer.stickiness]
|
||||
cookieName = "{{ $loadBalancer.Stickiness.CookieName }}"
|
||||
secure = {{ $loadBalancer.Stickiness.Secure }}
|
||||
httpOnly = {{ $loadBalancer.Stickiness.HTTPOnly }}
|
||||
sameSite = "{{ $loadBalancer.Stickiness.SameSite }}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
{{if $loadBalancer.Stickiness }}
|
||||
[backends."backend-{{ $serviceName }}".loadBalancer.stickiness]
|
||||
cookieName = "{{ $loadBalancer.Stickiness.CookieName }}"
|
||||
secure = {{ $loadBalancer.Stickiness.Secure }}
|
||||
httpOnly = {{ $loadBalancer.Stickiness.HTTPOnly }}
|
||||
sameSite = "{{ $loadBalancer.Stickiness.SameSite }}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
{{if $backend.LoadBalancer.Stickiness }}
|
||||
[backends."{{ $backendName }}".loadBalancer.stickiness]
|
||||
cookieName = "{{ $backend.LoadBalancer.Stickiness.CookieName }}"
|
||||
secure = {{ $backend.LoadBalancer.Stickiness.Secure }}
|
||||
httpOnly = {{ $backend.LoadBalancer.Stickiness.HTTPOnly }}
|
||||
sameSite = "{{ $backend.LoadBalancer.Stickiness.SameSite }}"
|
||||
{{end}}
|
||||
|
||||
{{if $backend.MaxConn }}
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
{{if $loadBalancer.Stickiness }}
|
||||
[backends."{{ $backendName }}".loadBalancer.stickiness]
|
||||
cookieName = "{{ $loadBalancer.Stickiness.CookieName }}"
|
||||
secure = {{ $loadBalancer.Stickiness.Secure }}
|
||||
httpOnly = {{ $loadBalancer.Stickiness.HTTPOnly }}
|
||||
sameSite = "{{ $loadBalancer.Stickiness.SameSite }}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
{{if $loadBalancer.Stickiness }}
|
||||
[backends."{{ $backendName }}".loadBalancer.stickiness]
|
||||
cookieName = "{{ $loadBalancer.Stickiness.CookieName }}"
|
||||
secure = {{ $loadBalancer.Stickiness.Secure }}
|
||||
httpOnly = {{ $loadBalancer.Stickiness.HTTPOnly }}
|
||||
sameSite = "{{ $loadBalancer.Stickiness.SameSite }}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
{{if $loadBalancer.Stickiness }}
|
||||
[backends."backend-{{ $backendName }}".loadBalancer.stickiness]
|
||||
cookieName = "{{ $loadBalancer.Stickiness.CookieName }}"
|
||||
secure = {{ $loadBalancer.Stickiness.Secure }}
|
||||
httpOnly = {{ $loadBalancer.Stickiness.HTTPOnly }}
|
||||
sameSite = "{{ $loadBalancer.Stickiness.SameSite }}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
{{if $loadBalancer.Stickiness }}
|
||||
[backends."backend-{{ $backendName }}".loadBalancer.stickiness]
|
||||
cookieName = "{{ $loadBalancer.Stickiness.CookieName }}"
|
||||
secure = {{ $loadBalancer.Stickiness.Secure }}
|
||||
httpOnly = {{ $loadBalancer.Stickiness.HTTPOnly }}
|
||||
sameSite = "{{ $loadBalancer.Stickiness.SameSite }}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
|
||||
@@ -25,32 +25,34 @@ var (
|
||||
// CipherSuites Map of TLS CipherSuites from crypto/tls
|
||||
// Available CipherSuites defined at https://golang.org/pkg/crypto/tls/#pkg-constants
|
||||
CipherSuites = map[string]uint16{
|
||||
"TLS_RSA_WITH_RC4_128_SHA": tls.TLS_RSA_WITH_RC4_128_SHA,
|
||||
"TLS_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
"TLS_RSA_WITH_AES_128_CBC_SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
"TLS_RSA_WITH_AES_256_CBC_SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
"TLS_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_RSA_WITH_AES_128_CBC_SHA256,
|
||||
"TLS_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||
"TLS_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||
"TLS_ECDHE_RSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
|
||||
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
||||
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
||||
"TLS_AES_128_GCM_SHA256": tls.TLS_AES_128_GCM_SHA256,
|
||||
"TLS_AES_256_GCM_SHA384": tls.TLS_AES_256_GCM_SHA384,
|
||||
"TLS_CHACHA20_POLY1305_SHA256": tls.TLS_CHACHA20_POLY1305_SHA256,
|
||||
"TLS_FALLBACK_SCSV": tls.TLS_FALLBACK_SCSV,
|
||||
"TLS_RSA_WITH_RC4_128_SHA": tls.TLS_RSA_WITH_RC4_128_SHA,
|
||||
"TLS_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
"TLS_RSA_WITH_AES_128_CBC_SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
"TLS_RSA_WITH_AES_256_CBC_SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
"TLS_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_RSA_WITH_AES_128_CBC_SHA256,
|
||||
"TLS_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||
"TLS_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||
"TLS_ECDHE_RSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
|
||||
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
||||
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
||||
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
"TLS_AES_128_GCM_SHA256": tls.TLS_AES_128_GCM_SHA256,
|
||||
"TLS_AES_256_GCM_SHA384": tls.TLS_AES_256_GCM_SHA384,
|
||||
"TLS_CHACHA20_POLY1305_SHA256": tls.TLS_CHACHA20_POLY1305_SHA256,
|
||||
"TLS_FALLBACK_SCSV": tls.TLS_FALLBACK_SCSV,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -120,6 +122,7 @@ func (c *Certificates) CreateTLSConfig(entryPointName string) (*tls.Config, erro
|
||||
}
|
||||
}
|
||||
}
|
||||
config.BuildNameToCertificate()
|
||||
return config, nil
|
||||
}
|
||||
|
||||
|
||||
102
tls/certificate_test.go
Normal file
102
tls/certificate_test.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package tls
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"math/big"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCertificates_CreateTLSConfig(t *testing.T) {
|
||||
var cert Certificates
|
||||
certificate1, err := generateCertificate("test1.traefik.com")
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, certificate1)
|
||||
|
||||
cert = append(cert, *certificate1)
|
||||
|
||||
certificate2, err := generateCertificate("test2.traefik.com")
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, certificate2)
|
||||
|
||||
cert = append(cert, *certificate2)
|
||||
|
||||
config, err := cert.CreateTLSConfig("http")
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Len(t, config.NameToCertificate, 2)
|
||||
assert.Contains(t, config.NameToCertificate, "test1.traefik.com")
|
||||
assert.Contains(t, config.NameToCertificate, "test2.traefik.com")
|
||||
}
|
||||
|
||||
func generateCertificate(domain string) (*Certificate, error) {
|
||||
certPEM, keyPEM, err := keyPair(domain, time.Time{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Certificate{
|
||||
CertFile: FileOrContent(certPEM),
|
||||
KeyFile: FileOrContent(keyPEM),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// keyPair generates cert and key files
|
||||
func keyPair(domain string, expiration time.Time) ([]byte, []byte, error) {
|
||||
rsaPrivKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(rsaPrivKey)})
|
||||
|
||||
certPEM, err := PemCert(rsaPrivKey, domain, expiration)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return certPEM, keyPEM, nil
|
||||
}
|
||||
|
||||
// PemCert generates PEM cert file
|
||||
func PemCert(privKey *rsa.PrivateKey, domain string, expiration time.Time) ([]byte, error) {
|
||||
derBytes, err := derCert(privKey, expiration, domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}), nil
|
||||
}
|
||||
|
||||
func derCert(privKey *rsa.PrivateKey, expiration time.Time, domain string) ([]byte, error) {
|
||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if expiration.IsZero() {
|
||||
expiration = time.Now().Add(365 * (24 * time.Hour))
|
||||
}
|
||||
|
||||
template := x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
Subject: pkix.Name{
|
||||
CommonName: domain,
|
||||
},
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: expiration,
|
||||
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageKeyAgreement | x509.KeyUsageDataEncipherment,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
BasicConstraintsValid: true,
|
||||
DNSNames: []string{domain},
|
||||
}
|
||||
|
||||
return x509.CreateCertificate(rand.Reader, &template, &template, &privKey.PublicKey, privKey)
|
||||
}
|
||||
@@ -52,6 +52,9 @@ type LoadBalancer struct {
|
||||
// Stickiness holds sticky session configuration.
|
||||
type Stickiness struct {
|
||||
CookieName string `json:"cookieName,omitempty"`
|
||||
Secure bool `json:"secure,omitempty"`
|
||||
HTTPOnly bool `json:"httpOnly,omitempty"`
|
||||
SameSite string `json:"sameSite,omitempty"`
|
||||
}
|
||||
|
||||
// CircuitBreaker holds circuit breaker configuration.
|
||||
|
||||
24
vendor/github.com/decker502/dnspod-go/domains.go
generated
vendored
24
vendor/github.com/decker502/dnspod-go/domains.go
generated
vendored
@@ -16,18 +16,18 @@ type DomainsService struct {
|
||||
}
|
||||
|
||||
type DomainInfo struct {
|
||||
DomainTotal int `json:"domain_total,omitempty"`
|
||||
AllTotal int `json:"all_total,omitempty"`
|
||||
MineTotal int `json:"mine_total,omitempty"`
|
||||
ShareTotal string `json:"share_total,omitempty"`
|
||||
VipTotal int `json:"vip_total,omitempty"`
|
||||
IsMarkTotal int `json:"ismark_total,omitempty"`
|
||||
PauseTotal int `json:"pause_total,omitempty"`
|
||||
ErrorTotal int `json:"error_total,omitempty"`
|
||||
LockTotal int `json:"lock_total,omitempty"`
|
||||
SpamTotal int `json:"spam_total,omitempty"`
|
||||
VipExpire int `json:"vip_expire,omitempty"`
|
||||
ShareOutTotal int `json:"share_out_total,omitempty"`
|
||||
DomainTotal json.Number `json:"domain_total,omitempty"`
|
||||
AllTotal json.Number `json:"all_total,omitempty"`
|
||||
MineTotal json.Number `json:"mine_total,omitempty"`
|
||||
ShareTotal json.Number `json:"share_total,omitempty"`
|
||||
VipTotal json.Number `json:"vip_total,omitempty"`
|
||||
IsMarkTotal json.Number `json:"ismark_total,omitempty"`
|
||||
PauseTotal json.Number `json:"pause_total,omitempty"`
|
||||
ErrorTotal json.Number `json:"error_total,omitempty"`
|
||||
LockTotal json.Number `json:"lock_total,omitempty"`
|
||||
SpamTotal json.Number `json:"spam_total,omitempty"`
|
||||
VipExpire json.Number `json:"vip_expire,omitempty"`
|
||||
ShareOutTotal json.Number `json:"share_out_total,omitempty"`
|
||||
}
|
||||
|
||||
type Domain struct {
|
||||
|
||||
40
vendor/github.com/vulcand/oxy/roundrobin/stickysessions.go
generated
vendored
40
vendor/github.com/vulcand/oxy/roundrobin/stickysessions.go
generated
vendored
@@ -3,11 +3,26 @@ package roundrobin
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
// CookieOptions has all the options one would like to set on the affinity cookie
|
||||
type CookieOptions struct {
|
||||
HTTPOnly bool
|
||||
Secure bool
|
||||
|
||||
Path string
|
||||
Domain string
|
||||
Expires time.Time
|
||||
|
||||
MaxAge int
|
||||
SameSite http.SameSite
|
||||
}
|
||||
|
||||
// StickySession is a mixin for load balancers that implements layer 7 (http cookie) session affinity
|
||||
type StickySession struct {
|
||||
cookieName string
|
||||
options CookieOptions
|
||||
}
|
||||
|
||||
// NewStickySession creates a new StickySession
|
||||
@@ -15,6 +30,12 @@ func NewStickySession(cookieName string) *StickySession {
|
||||
return &StickySession{cookieName: cookieName}
|
||||
}
|
||||
|
||||
// NewStickySessionWithOptions creates a new StickySession whilst allowing for options to
|
||||
// shape its affinity cookie such as "httpOnly" or "secure"
|
||||
func NewStickySessionWithOptions(cookieName string, options CookieOptions) *StickySession {
|
||||
return &StickySession{cookieName: cookieName, options: options}
|
||||
}
|
||||
|
||||
// GetBackend returns the backend URL stored in the sticky cookie, iff the backend is still in the valid list of servers.
|
||||
func (s *StickySession) GetBackend(req *http.Request, servers []*url.URL) (*url.URL, bool, error) {
|
||||
cookie, err := req.Cookie(s.cookieName)
|
||||
@@ -39,7 +60,24 @@ func (s *StickySession) GetBackend(req *http.Request, servers []*url.URL) (*url.
|
||||
|
||||
// StickBackend creates and sets the cookie
|
||||
func (s *StickySession) StickBackend(backend *url.URL, w *http.ResponseWriter) {
|
||||
cookie := &http.Cookie{Name: s.cookieName, Value: backend.String(), Path: "/"}
|
||||
opt := s.options
|
||||
|
||||
cp := "/"
|
||||
if opt.Path != "" {
|
||||
cp = opt.Path
|
||||
}
|
||||
|
||||
cookie := &http.Cookie{
|
||||
Name: s.cookieName,
|
||||
Value: backend.String(),
|
||||
Path: cp,
|
||||
Domain: opt.Domain,
|
||||
Expires: opt.Expires,
|
||||
MaxAge: opt.MaxAge,
|
||||
Secure: opt.Secure,
|
||||
HttpOnly: opt.HTTPOnly,
|
||||
SameSite: opt.SameSite,
|
||||
}
|
||||
http.SetCookie(*w, cookie)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user