Fix entry point redirect behavior
This commit is contained in:
parent
63d7ed74f1
commit
44221fba49
5 changed files with 184 additions and 38 deletions
|
@ -575,20 +575,23 @@ This whole section is dedicated to options, keyed by entry point, that will appl
|
||||||
|
|
||||||
#### `entryPoint`
|
#### `entryPoint`
|
||||||
|
|
||||||
This section is a convenience to enable (permanent) redirecting of all incoming requests on an entry point (e.g. port `80`) to another entry point (e.g. port `443`).
|
This section is a convenience to enable (permanent) redirecting of all incoming requests on an entry point (e.g. port `80`) to another entry point (e.g. port `443`) or an explicit port (`:443`).
|
||||||
|
|
||||||
??? info "`entryPoint.to`"
|
??? info "`entryPoint.to`"
|
||||||
|
|
||||||
_Required_
|
_Required_
|
||||||
|
|
||||||
The target entry point.
|
The target element, it can be:
|
||||||
|
|
||||||
|
- an entry point name (ex: `websecure`)
|
||||||
|
- a port (`:443`)
|
||||||
|
|
||||||
```toml tab="File (TOML)"
|
```toml tab="File (TOML)"
|
||||||
[entryPoints.foo]
|
[entryPoints.foo]
|
||||||
# ...
|
# ...
|
||||||
[entryPoints.foo.http.redirections]
|
[entryPoints.foo.http.redirections]
|
||||||
[entryPoints.foo.http.redirections.entryPoint]
|
[entryPoints.foo.http.redirections.entryPoint]
|
||||||
to = "bar"
|
to = "websecure"
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
```yaml tab="File (YAML)"
|
||||||
|
@ -598,7 +601,7 @@ This section is a convenience to enable (permanent) redirecting of all incoming
|
||||||
http:
|
http:
|
||||||
redirections:
|
redirections:
|
||||||
entryPoint:
|
entryPoint:
|
||||||
to: bar
|
to: websecure
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
|
@ -607,7 +610,7 @@ This section is a convenience to enable (permanent) redirecting of all incoming
|
||||||
|
|
||||||
??? info "`entryPoint.scheme`"
|
??? info "`entryPoint.scheme`"
|
||||||
|
|
||||||
_Optional, Default="http"_
|
_Optional, Default="https"_
|
||||||
|
|
||||||
The redirection target scheme.
|
The redirection target scheme.
|
||||||
|
|
||||||
|
@ -635,6 +638,66 @@ This section is a convenience to enable (permanent) redirecting of all incoming
|
||||||
--entrypoints.foo.http.redirections.entryPoint.scheme=https
|
--entrypoints.foo.http.redirections.entryPoint.scheme=https
|
||||||
```
|
```
|
||||||
|
|
||||||
|
??? info "`entryPoint.permanent`"
|
||||||
|
|
||||||
|
_Optional, Default=true_
|
||||||
|
|
||||||
|
To apply a permanent redirection.
|
||||||
|
|
||||||
|
```toml tab="File (TOML)"
|
||||||
|
[entryPoints.foo]
|
||||||
|
# ...
|
||||||
|
[entryPoints.foo.http.redirections]
|
||||||
|
[entryPoints.foo.http.redirections.entryPoint]
|
||||||
|
# ...
|
||||||
|
permanent = true
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="File (YAML)"
|
||||||
|
entryPoints:
|
||||||
|
foo:
|
||||||
|
# ...
|
||||||
|
http:
|
||||||
|
redirections:
|
||||||
|
entryPoint:
|
||||||
|
# ...
|
||||||
|
permanent: true
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash tab="CLI"
|
||||||
|
--entrypoints.foo.http.redirections.entrypoint.permanent=true
|
||||||
|
```
|
||||||
|
|
||||||
|
??? info "`entryPoint.priority`"
|
||||||
|
|
||||||
|
_Optional, Default=1_
|
||||||
|
|
||||||
|
Priority of the generated router.
|
||||||
|
|
||||||
|
```toml tab="File (TOML)"
|
||||||
|
[entryPoints.foo]
|
||||||
|
# ...
|
||||||
|
[entryPoints.foo.http.redirections]
|
||||||
|
[entryPoints.foo.http.redirections.entryPoint]
|
||||||
|
# ...
|
||||||
|
priority = 10
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="File (YAML)"
|
||||||
|
entryPoints:
|
||||||
|
foo:
|
||||||
|
# ...
|
||||||
|
http:
|
||||||
|
redirections:
|
||||||
|
entryPoint:
|
||||||
|
# ...
|
||||||
|
priority: 10
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash tab="CLI"
|
||||||
|
--entrypoints.foo.http.redirections.entrypoint.priority=10
|
||||||
|
```
|
||||||
|
|
||||||
### Middlewares
|
### Middlewares
|
||||||
|
|
||||||
The list of middlewares that are prepended by default to the list of middlewares of each router associated to the named entry point.
|
The list of middlewares that are prepended by default to the list of middlewares of each router associated to the named entry point.
|
||||||
|
|
|
@ -60,13 +60,17 @@ type Redirections struct {
|
||||||
|
|
||||||
// RedirectEntryPoint is the definition of an entry point redirection.
|
// RedirectEntryPoint is the definition of an entry point redirection.
|
||||||
type RedirectEntryPoint struct {
|
type RedirectEntryPoint struct {
|
||||||
To string `description:"Targeted entry point of the redirection." json:"to,omitempty" toml:"to,omitempty" yaml:"to,omitempty"`
|
To string `description:"Targeted entry point of the redirection." json:"to,omitempty" toml:"to,omitempty" yaml:"to,omitempty"`
|
||||||
Scheme string `description:"Scheme used for the redirection. Defaults to https." json:"https,omitempty" toml:"https,omitempty" yaml:"https,omitempty"`
|
Scheme string `description:"Scheme used for the redirection. Defaults to https." json:"https,omitempty" toml:"https,omitempty" yaml:"https,omitempty"`
|
||||||
|
Permanent bool `description:"Applied a permanent redirection. Defaults to true." json:"permanent,omitempty" toml:"permanent,omitempty" yaml:"permanent,omitempty"`
|
||||||
|
Priority int `description:"Priority of the generated router. Defaults to 1." json:"priority,omitempty" toml:"priority,omitempty" yaml:"priority,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetDefaults sets the default values.
|
// SetDefaults sets the default values.
|
||||||
func (r *RedirectEntryPoint) SetDefaults() {
|
func (r *RedirectEntryPoint) SetDefaults() {
|
||||||
r.Scheme = "https"
|
r.Scheme = "https"
|
||||||
|
r.Permanent = true
|
||||||
|
r.Priority = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// TLSConfig is the default TLS configuration for all the routers associated to the concerned entry point.
|
// TLSConfig is the default TLS configuration for all the routers associated to the concerned entry point.
|
||||||
|
|
30
pkg/provider/traefik/fixtures/redirection_port.json
Normal file
30
pkg/provider/traefik/fixtures/redirection_port.json
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
"http": {
|
||||||
|
"routers": {
|
||||||
|
"web-to-443": {
|
||||||
|
"entryPoints": [
|
||||||
|
"web"
|
||||||
|
],
|
||||||
|
"middlewares": [
|
||||||
|
"redirect-web-to-443"
|
||||||
|
],
|
||||||
|
"service": "noop@internal",
|
||||||
|
"rule": "HostRegexp(`{host:.+}`)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"middlewares": {
|
||||||
|
"redirect-web-to-443": {
|
||||||
|
"redirectScheme": {
|
||||||
|
"scheme": "https",
|
||||||
|
"port": "443",
|
||||||
|
"permanent": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"services": {
|
||||||
|
"noop": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tcp": {},
|
||||||
|
"tls": {}
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"net"
|
"net"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
"github.com/containous/traefik/v2/pkg/config/dynamic"
|
"github.com/containous/traefik/v2/pkg/config/dynamic"
|
||||||
"github.com/containous/traefik/v2/pkg/config/static"
|
"github.com/containous/traefik/v2/pkg/config/static"
|
||||||
|
@ -30,9 +31,11 @@ func New(staticCfg static.Configuration) *Provider {
|
||||||
|
|
||||||
// Provide allows the provider to provide configurations to traefik using the given configuration channel.
|
// Provide allows the provider to provide configurations to traefik using the given configuration channel.
|
||||||
func (i *Provider) Provide(configurationChan chan<- dynamic.Message, _ *safe.Pool) error {
|
func (i *Provider) Provide(configurationChan chan<- dynamic.Message, _ *safe.Pool) error {
|
||||||
|
ctx := log.With(context.Background(), log.Str(log.ProviderName, "internal"))
|
||||||
|
|
||||||
configurationChan <- dynamic.Message{
|
configurationChan <- dynamic.Message{
|
||||||
ProviderName: "internal",
|
ProviderName: "internal",
|
||||||
Configuration: i.createConfiguration(),
|
Configuration: i.createConfiguration(ctx),
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -43,7 +46,7 @@ func (i *Provider) Init() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Provider) createConfiguration() *dynamic.Configuration {
|
func (i *Provider) createConfiguration(ctx context.Context) *dynamic.Configuration {
|
||||||
cfg := &dynamic.Configuration{
|
cfg := &dynamic.Configuration{
|
||||||
HTTP: &dynamic.HTTPConfiguration{
|
HTTP: &dynamic.HTTPConfiguration{
|
||||||
Routers: make(map[string]*dynamic.Router),
|
Routers: make(map[string]*dynamic.Router),
|
||||||
|
@ -66,20 +69,33 @@ func (i *Provider) createConfiguration() *dynamic.Configuration {
|
||||||
i.restConfiguration(cfg)
|
i.restConfiguration(cfg)
|
||||||
i.prometheusConfiguration(cfg)
|
i.prometheusConfiguration(cfg)
|
||||||
i.entryPointModels(cfg)
|
i.entryPointModels(cfg)
|
||||||
i.redirection(cfg)
|
i.redirection(ctx, cfg)
|
||||||
|
|
||||||
cfg.HTTP.Services["noop"] = &dynamic.Service{}
|
cfg.HTTP.Services["noop"] = &dynamic.Service{}
|
||||||
|
|
||||||
return cfg
|
return cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Provider) redirection(cfg *dynamic.Configuration) {
|
func (i *Provider) redirection(ctx context.Context, cfg *dynamic.Configuration) {
|
||||||
for name, ep := range i.staticCfg.EntryPoints {
|
for name, ep := range i.staticCfg.EntryPoints {
|
||||||
if ep.HTTP.Redirections == nil || ep.HTTP.Redirections.EntryPoint == nil {
|
if ep.HTTP.Redirections == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger := log.FromContext(log.With(ctx, log.Str(log.EntryPointName, name)))
|
||||||
|
|
||||||
def := ep.HTTP.Redirections
|
def := ep.HTTP.Redirections
|
||||||
|
if def.EntryPoint == nil || def.EntryPoint.To == "" {
|
||||||
|
logger.Error("Unable to create redirection: the entry point or the port is missing")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
port, err := i.getRedirectPort(name, def)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
rtName := provider.Normalize(name + "-to-" + def.EntryPoint.To)
|
rtName := provider.Normalize(name + "-to-" + def.EntryPoint.To)
|
||||||
mdName := "redirect-" + rtName
|
mdName := "redirect-" + rtName
|
||||||
|
|
||||||
|
@ -88,12 +104,7 @@ func (i *Provider) redirection(cfg *dynamic.Configuration) {
|
||||||
EntryPoints: []string{name},
|
EntryPoints: []string{name},
|
||||||
Middlewares: []string{mdName},
|
Middlewares: []string{mdName},
|
||||||
Service: "noop@internal",
|
Service: "noop@internal",
|
||||||
}
|
Priority: def.EntryPoint.Priority,
|
||||||
|
|
||||||
port, err := i.getEntryPointPort(name, def)
|
|
||||||
if err != nil {
|
|
||||||
log.FromContext(context.Background()).WithField(log.EntryPointName, name).Error(err)
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg.HTTP.Routers[rtName] = rt
|
cfg.HTTP.Routers[rtName] = rt
|
||||||
|
@ -102,7 +113,7 @@ func (i *Provider) redirection(cfg *dynamic.Configuration) {
|
||||||
RedirectScheme: &dynamic.RedirectScheme{
|
RedirectScheme: &dynamic.RedirectScheme{
|
||||||
Scheme: def.EntryPoint.Scheme,
|
Scheme: def.EntryPoint.Scheme,
|
||||||
Port: port,
|
Port: port,
|
||||||
Permanent: true,
|
Permanent: def.EntryPoint.Permanent,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,6 +121,36 @@ func (i *Provider) redirection(cfg *dynamic.Configuration) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *Provider) getRedirectPort(name string, def *static.Redirections) (string, error) {
|
||||||
|
exp := regexp.MustCompile(`^:(\d+)$`)
|
||||||
|
|
||||||
|
if exp.MatchString(def.EntryPoint.To) {
|
||||||
|
_, port, err := net.SplitHostPort(def.EntryPoint.To)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("invalid port value: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return port, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return i.getEntryPointPort(name, def)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Provider) getEntryPointPort(name string, def *static.Redirections) (string, error) {
|
||||||
|
dst, ok := i.staticCfg.EntryPoints[def.EntryPoint.To]
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("'to' entry point field references a non-existing entry point: %s", def.EntryPoint.To)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, port, err := net.SplitHostPort(dst.Address)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("invalid entry point %q address %q: %v",
|
||||||
|
name, i.staticCfg.EntryPoints[def.EntryPoint.To].Address, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return port, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (i *Provider) entryPointModels(cfg *dynamic.Configuration) {
|
func (i *Provider) entryPointModels(cfg *dynamic.Configuration) {
|
||||||
for name, ep := range i.staticCfg.EntryPoints {
|
for name, ep := range i.staticCfg.EntryPoints {
|
||||||
if len(ep.HTTP.Middlewares) == 0 && ep.HTTP.TLS == nil {
|
if len(ep.HTTP.Middlewares) == 0 && ep.HTTP.TLS == nil {
|
||||||
|
@ -233,18 +274,3 @@ func (i *Provider) prometheusConfiguration(cfg *dynamic.Configuration) {
|
||||||
|
|
||||||
cfg.HTTP.Services["prometheus"] = &dynamic.Service{}
|
cfg.HTTP.Services["prometheus"] = &dynamic.Service{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Provider) getEntryPointPort(name string, def *static.Redirections) (string, error) {
|
|
||||||
dst, ok := i.staticCfg.EntryPoints[def.EntryPoint.To]
|
|
||||||
if !ok {
|
|
||||||
return "", fmt.Errorf("'to' entry point field references a non-existing entry point: %s", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, port, err := net.SplitHostPort(dst.Address)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("invalid entry point %q address %q: %v",
|
|
||||||
name, i.staticCfg.EntryPoints[def.EntryPoint.To].Address, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return port, nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package traefik
|
package traefik
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
@ -196,8 +197,30 @@ func Test_createConfiguration(t *testing.T) {
|
||||||
HTTP: static.HTTPConfig{
|
HTTP: static.HTTPConfig{
|
||||||
Redirections: &static.Redirections{
|
Redirections: &static.Redirections{
|
||||||
EntryPoint: &static.RedirectEntryPoint{
|
EntryPoint: &static.RedirectEntryPoint{
|
||||||
To: "websecure",
|
To: "websecure",
|
||||||
Scheme: "https",
|
Scheme: "https",
|
||||||
|
Permanent: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"websecure": {
|
||||||
|
Address: ":443",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
desc: "redirection_port.json",
|
||||||
|
staticCfg: static.Configuration{
|
||||||
|
EntryPoints: map[string]*static.EntryPoint{
|
||||||
|
"web": {
|
||||||
|
Address: ":80",
|
||||||
|
HTTP: static.HTTPConfig{
|
||||||
|
Redirections: &static.Redirections{
|
||||||
|
EntryPoint: &static.RedirectEntryPoint{
|
||||||
|
To: ":443",
|
||||||
|
Scheme: "https",
|
||||||
|
Permanent: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -217,7 +240,7 @@ func Test_createConfiguration(t *testing.T) {
|
||||||
|
|
||||||
provider := Provider{staticCfg: test.staticCfg}
|
provider := Provider{staticCfg: test.staticCfg}
|
||||||
|
|
||||||
cfg := provider.createConfiguration()
|
cfg := provider.createConfiguration(context.Background())
|
||||||
|
|
||||||
filename := filepath.Join("fixtures", test.desc)
|
filename := filepath.Join("fixtures", test.desc)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue