Merge branch v2.4 into master
This commit is contained in:
commit
2461e36ed4
14 changed files with 214 additions and 56 deletions
|
@ -1,5 +1,5 @@
|
|||
|
||||
FROM alpine:3.10 as alpine
|
||||
FROM alpine:3.13 as alpine
|
||||
|
||||
RUN apk --no-cache --no-progress add \
|
||||
libcurl \
|
||||
|
|
|
@ -324,10 +324,12 @@ For complete details, refer to your provider's _Additional configuration_ link.
|
|||
| [IIJ](https://www.iij.ad.jp/) | `iij` | `IIJ_API_ACCESS_KEY`, `IIJ_API_SECRET_KEY`, `IIJ_DO_SERVICE_CODE` | [Additional configuration](https://go-acme.github.io/lego/dns/iij) |
|
||||
| [Infomaniak](https://www.infomaniak.com) | `infomaniak` | `INFOMANIAK_ACCESS_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/infomaniak) |
|
||||
| [INWX](https://www.inwx.de/en) | `inwx` | `INWX_USERNAME`, `INWX_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/inwx) |
|
||||
| [ionos](https://ionos.com/) | `ionos` | `IONOS_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/ionos) |
|
||||
| [Joker.com](https://joker.com) | `joker` | `JOKER_API_MODE` with `JOKER_API_KEY` or `JOKER_USERNAME`, `JOKER_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/joker) |
|
||||
| [Lightsail](https://aws.amazon.com/lightsail/) | `lightsail` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `DNS_ZONE` | [Additional configuration](https://go-acme.github.io/lego/dns/lightsail) |
|
||||
| [Linode v4](https://www.linode.com) | `linode` | `LINODE_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/linode) |
|
||||
| [Liquid Web](https://www.liquidweb.com/) | `liquidweb` | `LIQUID_WEB_PASSWORD`, `LIQUID_WEB_USERNAME`, `LIQUID_WEB_ZONE` | [Additional configuration](https://go-acme.github.io/lego/dns/liquidweb) |
|
||||
| [Loopia](https://loopia.com/) | `loopia` | `LOOPIA_API_PASSWORD`, `LOOPIA_API_USER` | [Additional configuration](https://go-acme.github.io/lego/dns/loopia) |
|
||||
| [LuaDNS](https://luadns.com) | `luadns` | `LUADNS_API_USERNAME`, `LUADNS_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/luadns) |
|
||||
| manual | `manual` | none, but you need to run Traefik interactively [^4], turn on debug log to see instructions and press <kbd>Enter</kbd>. | |
|
||||
| [MyDNS.jp](https://www.mydns.jp/) | `mydnsjp` | `MYDNSJP_MASTER_ID`, `MYDNSJP_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/mydnsjp) |
|
||||
|
|
|
@ -145,7 +145,7 @@ _Optional, Default=empty_
|
|||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
kubernetesGateway:
|
||||
token = "mytoken"
|
||||
token: "mytoken"
|
||||
# ...
|
||||
```
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
FROM alpine:3.12
|
||||
FROM alpine:3.13
|
||||
|
||||
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/.local/bin
|
||||
|
||||
|
|
2
go.mod
2
go.mod
|
@ -30,7 +30,7 @@ require (
|
|||
github.com/elazarl/go-bindata-assetfs v1.0.0
|
||||
github.com/fatih/structs v1.1.0
|
||||
github.com/gambol99/go-marathon v0.0.0-20180614232016-99a156b96fb2
|
||||
github.com/go-acme/lego/v4 v4.1.3
|
||||
github.com/go-acme/lego/v4 v4.2.0
|
||||
github.com/go-check/check v0.0.0-00010101000000-000000000000
|
||||
github.com/go-kit/kit v0.10.1-0.20200915143503-439c4d2ed3ea
|
||||
github.com/golang/protobuf v1.4.3
|
||||
|
|
16
go.sum
16
go.sum
|
@ -227,8 +227,8 @@ github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfc
|
|||
github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cpu/goacmedns v0.0.3 h1:QOeMpIEsIdm1LSASSswjaTf8CXmzcrgy5OeCfHjppA4=
|
||||
github.com/cpu/goacmedns v0.0.3/go.mod h1:4MipLkI+qScwqtVxcNO6okBhbgRrr7/tKXUSgSL0teQ=
|
||||
github.com/cpu/goacmedns v0.1.1 h1:DM3H2NiN2oam7QljgGY5ygy4yDXhK5Z4JUnqaugs2C4=
|
||||
github.com/cpu/goacmedns v0.1.1/go.mod h1:MuaouqEhPAHxsbqjgnck5zeghuwBP1dLnPoobeGqugQ=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
|
@ -319,8 +319,8 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME
|
|||
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
github.com/go-acme/lego/v4 v4.1.3 h1:D8nnzrijQFUAqdNPwnbvm6tJ3AJAzQAlnROeecUNG/4=
|
||||
github.com/go-acme/lego/v4 v4.1.3/go.mod h1:pIFm5tWkXSgiAEfJ/XQCQIvX1cEvHFwbgLZyx8OVSUE=
|
||||
github.com/go-acme/lego/v4 v4.2.0 h1:zEvpcDLqvzOlNUGBMA0MCKPpb9UBbnBzgWwCIbTEt2g=
|
||||
github.com/go-acme/lego/v4 v4.2.0/go.mod h1:jmhqxBaangB8txXZKjRLTPXFXUwPCTU2fU8S9/eQzBI=
|
||||
github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s=
|
||||
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
|
||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||
|
@ -529,8 +529,8 @@ github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:
|
|||
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.7 h1:8/CAEZt/+F7kR7GevNHulKkUjLht3CPmn7egmhieNKo=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.7/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||
github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI=
|
||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||
github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs=
|
||||
|
@ -974,8 +974,8 @@ github.com/vulcand/oxy v1.1.0 h1:DbBijGo1+6cFqR9jarkMxasdj0lgWwrrFtue6ijek4Q=
|
|||
github.com/vulcand/oxy v1.1.0/go.mod h1:ADiMYHi8gkGl2987yQIzDRoXZilANF4WtKaQ92OppKY=
|
||||
github.com/vulcand/predicate v1.1.0 h1:Gq/uWopa4rx/tnZu2opOSBqHK63Yqlou/SzrbwdJiNg=
|
||||
github.com/vulcand/predicate v1.1.0/go.mod h1:mlccC5IRBoc2cIFmCB8ZM62I3VDb6p2GXESMHa3CnZg=
|
||||
github.com/vultr/govultr v0.5.0 h1:iQzYhzbokmpDARbvIkvTkoyS7WMH82zVTKAL1PZ4JOA=
|
||||
github.com/vultr/govultr v0.5.0/go.mod h1:wZZXZbYbqyY1n3AldoeYNZK4Wnmmoq6dNFkvd5TV3ss=
|
||||
github.com/vultr/govultr/v2 v2.0.0 h1:+lAtqfWy3g9VwL7tT2Fpyad8Vv4MxOhT/NU8O5dk+EQ=
|
||||
github.com/vultr/govultr/v2 v2.0.0/go.mod h1:2PsEeg+gs3p/Fo5Pw8F9mv+DUBEOlrNZ8GmCTGmhOhs=
|
||||
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
|
||||
github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
|
||||
assetfs "github.com/elazarl/go-bindata-assetfs"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/traefik/traefik/v2/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v2/pkg/config/runtime"
|
||||
"github.com/traefik/traefik/v2/pkg/config/static"
|
||||
"github.com/traefik/traefik/v2/pkg/log"
|
||||
|
@ -157,6 +158,13 @@ func extractType(element interface{}) string {
|
|||
v := reflect.ValueOf(element).Elem()
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
field := v.Field(i)
|
||||
|
||||
if field.Kind() == reflect.Map && field.Type().Elem() == reflect.TypeOf(dynamic.PluginConf{}) {
|
||||
if keys := field.MapKeys(); len(keys) == 1 {
|
||||
return keys[0].String()
|
||||
}
|
||||
}
|
||||
|
||||
if field.Kind() == reflect.Ptr && field.Elem().Kind() == reflect.Struct {
|
||||
if !field.IsNil() {
|
||||
return v.Type().Field(i).Name
|
||||
|
|
|
@ -171,3 +171,112 @@ func TestHandler_RawData(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandler_GetMiddleware(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
middlewareName string
|
||||
conf runtime.Configuration
|
||||
expectedStatus int
|
||||
expected interface{}
|
||||
}{
|
||||
{
|
||||
desc: "Middleware not found",
|
||||
middlewareName: "auth@myprovider",
|
||||
conf: runtime.Configuration{
|
||||
Middlewares: map[string]*runtime.MiddlewareInfo{},
|
||||
},
|
||||
expectedStatus: http.StatusNotFound,
|
||||
},
|
||||
{
|
||||
desc: "Get middleware",
|
||||
middlewareName: "auth@myprovider",
|
||||
conf: runtime.Configuration{
|
||||
Middlewares: map[string]*runtime.MiddlewareInfo{
|
||||
"auth@myprovider": {
|
||||
Middleware: &dynamic.Middleware{
|
||||
BasicAuth: &dynamic.BasicAuth{
|
||||
Users: []string{"admin:admin"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedStatus: http.StatusOK,
|
||||
expected: middlewareRepresentation{
|
||||
MiddlewareInfo: &runtime.MiddlewareInfo{
|
||||
Middleware: &dynamic.Middleware{
|
||||
BasicAuth: &dynamic.BasicAuth{
|
||||
Users: []string{"admin:admin"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Name: "auth@myprovider",
|
||||
Provider: "myprovider",
|
||||
Type: "basicauth",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Get plugin middleware",
|
||||
middlewareName: "myplugin@myprovider",
|
||||
conf: runtime.Configuration{
|
||||
Middlewares: map[string]*runtime.MiddlewareInfo{
|
||||
"myplugin@myprovider": {
|
||||
Middleware: &dynamic.Middleware{
|
||||
Plugin: map[string]dynamic.PluginConf{
|
||||
"mysuperplugin": {
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedStatus: http.StatusOK,
|
||||
expected: middlewareRepresentation{
|
||||
MiddlewareInfo: &runtime.MiddlewareInfo{
|
||||
Middleware: &dynamic.Middleware{
|
||||
Plugin: map[string]dynamic.PluginConf{
|
||||
"mysuperplugin": {
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Name: "myplugin@myprovider",
|
||||
Provider: "myprovider",
|
||||
Type: "mysuperplugin",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
handler := New(static.Configuration{API: &static.API{}, Global: &static.Global{}}, &test.conf)
|
||||
server := httptest.NewServer(handler.createRouter())
|
||||
|
||||
resp, err := http.DefaultClient.Get(server.URL + "/api/http/middlewares/" + test.middlewareName)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, test.expectedStatus, resp.StatusCode)
|
||||
|
||||
if test.expected == nil {
|
||||
return
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = resp.Body.Close()
|
||||
require.NoError(t, err)
|
||||
|
||||
expected, err := json.Marshal(test.expected)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.JSONEq(t, string(expected), string(data))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,18 @@ const (
|
|||
forwardedTypeName = "ForwardedAuthType"
|
||||
)
|
||||
|
||||
// hopHeaders Hop-by-hop headers to be removed in the authentication request.
|
||||
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html
|
||||
// Proxy-Authorization header is forwarded to the authentication server (see https://tools.ietf.org/html/rfc7235#section-4.4).
|
||||
var hopHeaders = []string{
|
||||
forward.Connection,
|
||||
forward.KeepAlive,
|
||||
forward.Te, // canonicalized version of "TE"
|
||||
forward.Trailers,
|
||||
forward.TransferEncoding,
|
||||
forward.Upgrade,
|
||||
}
|
||||
|
||||
type forwardAuth struct {
|
||||
address string
|
||||
authResponseHeaders []string
|
||||
|
@ -131,7 +143,7 @@ func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
|||
logger.Debugf("Remote error %s. StatusCode: %d", fa.address, forwardResponse.StatusCode)
|
||||
|
||||
utils.CopyHeaders(rw.Header(), forwardResponse.Header)
|
||||
utils.RemoveHeaders(rw.Header(), forward.HopHeaders...)
|
||||
utils.RemoveHeaders(rw.Header(), hopHeaders...)
|
||||
|
||||
// Grab the location header, if any.
|
||||
redirectURL, err := forwardResponse.Location()
|
||||
|
@ -187,7 +199,7 @@ func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
|||
|
||||
func writeHeader(req, forwardReq *http.Request, trustForwardHeader bool, allowedHeaders []string) {
|
||||
utils.CopyHeaders(forwardReq.Header, req.Header)
|
||||
utils.RemoveHeaders(forwardReq.Header, forward.HopHeaders...)
|
||||
utils.RemoveHeaders(forwardReq.Header, hopHeaders...)
|
||||
|
||||
forwardReq.Header = filterForwardRequestHeaders(forwardReq.Header, allowedHeaders)
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ func TestForwardAuthFail(t *testing.T) {
|
|||
})
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set(forward.ProxyAuthenticate, "test")
|
||||
http.Error(w, "Forbidden", http.StatusForbidden)
|
||||
}))
|
||||
t.Cleanup(server.Close)
|
||||
|
@ -48,6 +49,7 @@ func TestForwardAuthFail(t *testing.T) {
|
|||
err = res.Body.Close()
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "test", res.Header.Get(forward.ProxyAuthenticate))
|
||||
assert.Equal(t, "Forbidden\n", string(body))
|
||||
}
|
||||
|
||||
|
@ -142,7 +144,7 @@ func TestForwardAuthRedirect(t *testing.T) {
|
|||
func TestForwardAuthRemoveHopByHopHeaders(t *testing.T) {
|
||||
authTs := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
headers := w.Header()
|
||||
for _, header := range forward.HopHeaders {
|
||||
for _, header := range hopHeaders {
|
||||
if header == forward.TransferEncoding {
|
||||
headers.Set(header, "chunked")
|
||||
} else {
|
||||
|
@ -367,11 +369,13 @@ func Test_writeHeader(t *testing.T) {
|
|||
},
|
||||
trustForwardHeader: false,
|
||||
expectedHeaders: map[string]string{
|
||||
"X-CustomHeader": "CustomHeader",
|
||||
"X-Forwarded-Proto": "http",
|
||||
"X-Forwarded-Host": "foo.bar",
|
||||
"X-Forwarded-Uri": "/path?q=1",
|
||||
"X-Forwarded-Method": "GET",
|
||||
"X-CustomHeader": "CustomHeader",
|
||||
"X-Forwarded-Proto": "http",
|
||||
"X-Forwarded-Host": "foo.bar",
|
||||
"X-Forwarded-Uri": "/path?q=1",
|
||||
"X-Forwarded-Method": "GET",
|
||||
forward.ProxyAuthenticate: "ProxyAuthenticate",
|
||||
forward.ProxyAuthorization: "ProxyAuthorization",
|
||||
},
|
||||
checkForUnexpectedHeaders: true,
|
||||
},
|
||||
|
|
|
@ -84,19 +84,28 @@ func (x *XForwarded) isTrustedIP(ip string) bool {
|
|||
// removeIPv6Zone removes the zone if the given IP is an ipv6 address and it has {zone} information in it,
|
||||
// like "[fe80::d806:a55d:eb1b:49cc%vEthernet (vmxnet3 Ethernet Adapter - Virtual Switch)]:64692".
|
||||
func removeIPv6Zone(clientIP string) string {
|
||||
return strings.Split(clientIP, "%")[0]
|
||||
if idx := strings.Index(clientIP, "%"); idx != -1 {
|
||||
return clientIP[:idx]
|
||||
}
|
||||
return clientIP
|
||||
}
|
||||
|
||||
// isWebsocketRequest returns whether the specified HTTP request is a websocket handshake request.
|
||||
func isWebsocketRequest(req *http.Request) bool {
|
||||
containsHeader := func(name, value string) bool {
|
||||
items := strings.Split(req.Header.Get(name), ",")
|
||||
for _, item := range items {
|
||||
if value == strings.ToLower(strings.TrimSpace(item)) {
|
||||
h := unsafeHeader(req.Header).Get(name)
|
||||
for {
|
||||
pos := strings.Index(h, ",")
|
||||
if pos == -1 {
|
||||
return strings.EqualFold(value, strings.TrimSpace(h))
|
||||
}
|
||||
|
||||
if strings.EqualFold(value, strings.TrimSpace(h[:pos])) {
|
||||
return true
|
||||
}
|
||||
|
||||
h = h[pos:]
|
||||
}
|
||||
return false
|
||||
}
|
||||
return containsHeader(connection, "upgrade") && containsHeader(upgrade, "websocket")
|
||||
}
|
||||
|
@ -110,7 +119,7 @@ func forwardedPort(req *http.Request) string {
|
|||
return port
|
||||
}
|
||||
|
||||
if req.Header.Get(xForwardedProto) == "https" || req.Header.Get(xForwardedProto) == "wss" {
|
||||
if unsafeHeader(req.Header).Get(xForwardedProto) == "https" || unsafeHeader(req.Header).Get(xForwardedProto) == "wss" {
|
||||
return "443"
|
||||
}
|
||||
|
||||
|
@ -125,38 +134,38 @@ func (x *XForwarded) rewrite(outreq *http.Request) {
|
|||
if clientIP, _, err := net.SplitHostPort(outreq.RemoteAddr); err == nil {
|
||||
clientIP = removeIPv6Zone(clientIP)
|
||||
|
||||
if outreq.Header.Get(xRealIP) == "" {
|
||||
outreq.Header.Set(xRealIP, clientIP)
|
||||
if unsafeHeader(outreq.Header).Get(xRealIP) == "" {
|
||||
unsafeHeader(outreq.Header).Set(xRealIP, clientIP)
|
||||
}
|
||||
}
|
||||
|
||||
xfProto := outreq.Header.Get(xForwardedProto)
|
||||
xfProto := unsafeHeader(outreq.Header).Get(xForwardedProto)
|
||||
if xfProto == "" {
|
||||
if isWebsocketRequest(outreq) {
|
||||
if outreq.TLS != nil {
|
||||
outreq.Header.Set(xForwardedProto, "wss")
|
||||
unsafeHeader(outreq.Header).Set(xForwardedProto, "wss")
|
||||
} else {
|
||||
outreq.Header.Set(xForwardedProto, "ws")
|
||||
unsafeHeader(outreq.Header).Set(xForwardedProto, "ws")
|
||||
}
|
||||
} else {
|
||||
if outreq.TLS != nil {
|
||||
outreq.Header.Set(xForwardedProto, "https")
|
||||
unsafeHeader(outreq.Header).Set(xForwardedProto, "https")
|
||||
} else {
|
||||
outreq.Header.Set(xForwardedProto, "http")
|
||||
unsafeHeader(outreq.Header).Set(xForwardedProto, "http")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if xfPort := outreq.Header.Get(xForwardedPort); xfPort == "" {
|
||||
outreq.Header.Set(xForwardedPort, forwardedPort(outreq))
|
||||
if xfPort := unsafeHeader(outreq.Header).Get(xForwardedPort); xfPort == "" {
|
||||
unsafeHeader(outreq.Header).Set(xForwardedPort, forwardedPort(outreq))
|
||||
}
|
||||
|
||||
if xfHost := outreq.Header.Get(xForwardedHost); xfHost == "" && outreq.Host != "" {
|
||||
outreq.Header.Set(xForwardedHost, outreq.Host)
|
||||
if xfHost := unsafeHeader(outreq.Header).Get(xForwardedHost); xfHost == "" && outreq.Host != "" {
|
||||
unsafeHeader(outreq.Header).Set(xForwardedHost, outreq.Host)
|
||||
}
|
||||
|
||||
if x.hostname != "" {
|
||||
outreq.Header.Set(xForwardedServer, x.hostname)
|
||||
unsafeHeader(outreq.Header).Set(xForwardedServer, x.hostname)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -164,7 +173,7 @@ func (x *XForwarded) rewrite(outreq *http.Request) {
|
|||
func (x *XForwarded) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if !x.insecure && !x.isTrustedIP(r.RemoteAddr) {
|
||||
for _, h := range xHeaders {
|
||||
r.Header.Del(h)
|
||||
unsafeHeader(r.Header).Del(h)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -172,3 +181,22 @@ func (x *XForwarded) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
x.next.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
// unsafeHeader allows to manage Header values.
|
||||
// Must be used only when the header name is already a canonical key.
|
||||
type unsafeHeader map[string][]string
|
||||
|
||||
func (h unsafeHeader) Set(key, value string) {
|
||||
h[key] = []string{value}
|
||||
}
|
||||
|
||||
func (h unsafeHeader) Get(key string) string {
|
||||
if len(h[key]) == 0 {
|
||||
return ""
|
||||
}
|
||||
return h[key][0]
|
||||
}
|
||||
|
||||
func (h unsafeHeader) Del(key string) {
|
||||
delete(h, key)
|
||||
}
|
||||
|
|
|
@ -10,42 +10,41 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
typeName = "Recovery"
|
||||
typeName = "Recovery"
|
||||
middlewareName = "traefik-internal-recovery"
|
||||
)
|
||||
|
||||
type recovery struct {
|
||||
next http.Handler
|
||||
name string
|
||||
}
|
||||
|
||||
// New creates recovery middleware.
|
||||
func New(ctx context.Context, next http.Handler, name string) (http.Handler, error) {
|
||||
log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName)).Debug("Creating middleware")
|
||||
func New(ctx context.Context, next http.Handler) (http.Handler, error) {
|
||||
log.FromContext(middlewares.GetLoggerCtx(ctx, middlewareName, typeName)).Debug("Creating middleware")
|
||||
|
||||
return &recovery{
|
||||
next: next,
|
||||
name: name,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (re *recovery) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
defer recoverFunc(middlewares.GetLoggerCtx(req.Context(), re.name, typeName), rw, req)
|
||||
defer recoverFunc(rw, req)
|
||||
re.next.ServeHTTP(rw, req)
|
||||
}
|
||||
|
||||
func recoverFunc(ctx context.Context, rw http.ResponseWriter, r *http.Request) {
|
||||
func recoverFunc(rw http.ResponseWriter, r *http.Request) {
|
||||
if err := recover(); err != nil {
|
||||
logger := log.FromContext(middlewares.GetLoggerCtx(r.Context(), middlewareName, typeName))
|
||||
if !shouldLogPanic(err) {
|
||||
log.FromContext(ctx).Debugf("Request has been aborted [%s - %s]: %v", r.RemoteAddr, r.URL, err)
|
||||
logger.Debugf("Request has been aborted [%s - %s]: %v", r.RemoteAddr, r.URL, err)
|
||||
return
|
||||
}
|
||||
|
||||
log.FromContext(ctx).Errorf("Recovered from panic in HTTP handler [%s - %s]: %+v", r.RemoteAddr, r.URL, err)
|
||||
|
||||
logger.Errorf("Recovered from panic in HTTP handler [%s - %s]: %+v", r.RemoteAddr, r.URL, err)
|
||||
const size = 64 << 10
|
||||
buf := make([]byte, size)
|
||||
buf = buf[:runtime.Stack(buf, false)]
|
||||
log.FromContext(ctx).Errorf("Stack: %s", buf)
|
||||
logger.Errorf("Stack: %s", buf)
|
||||
|
||||
http.Error(rw, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ func TestRecoverHandler(t *testing.T) {
|
|||
fn := func(w http.ResponseWriter, r *http.Request) {
|
||||
panic("I love panicing!")
|
||||
}
|
||||
recovery, err := New(context.Background(), http.HandlerFunc(fn), "foo-recovery")
|
||||
recovery, err := New(context.Background(), http.HandlerFunc(fn))
|
||||
require.NoError(t, err)
|
||||
|
||||
server := httptest.NewServer(recovery)
|
||||
|
|
|
@ -16,10 +16,6 @@ import (
|
|||
"github.com/traefik/traefik/v2/pkg/server/provider"
|
||||
)
|
||||
|
||||
const (
|
||||
recoveryMiddlewareName = "traefik-internal-recovery"
|
||||
)
|
||||
|
||||
type middlewareBuilder interface {
|
||||
BuildChain(ctx context.Context, names []string) *alice.Chain
|
||||
}
|
||||
|
@ -130,7 +126,7 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
|
|||
|
||||
chain := alice.New()
|
||||
chain = chain.Append(func(next http.Handler) (http.Handler, error) {
|
||||
return recovery.New(ctx, next, recoveryMiddlewareName)
|
||||
return recovery.New(ctx, next)
|
||||
})
|
||||
|
||||
return chain.Then(router)
|
||||
|
|
Loading…
Reference in a new issue