Merge branch 'v1.5' into master

This commit is contained in:
Fernandez Ludovic 2018-01-02 14:49:11 +01:00
commit c84fb9895e
35 changed files with 462 additions and 124 deletions

View file

@ -1,5 +1,13 @@
# Change Log
## [v1.4.6](https://github.com/containous/traefik/tree/v1.4.6) (2018-01-02)
[All Commits](https://github.com/containous/traefik/compare/v1.4.5...v1.4.6)
**Bug fixes:**
- **[docker]** Normalize serviceName added to the service backend names ([#2631](https://github.com/containous/traefik/pull/2631) by [mmatur](https://github.com/mmatur))
- **[websocket]** Use gorilla readMessage and writeMessage instead of just an io.Copy ([#2640](https://github.com/containous/traefik/pull/2640) by [Juliens](https://github.com/Juliens))
- Fix bug report command ([#2638](https://github.com/containous/traefik/pull/2638) by [ldez](https://github.com/ldez))
## [v1.5.0-rc3](https://github.com/containous/traefik/tree/v1.5.0-rc3) (2017-12-20)
[All Commits](https://github.com/containous/traefik/compare/v1.5.0-rc2...v1.5.0-rc3)

View file

@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2016-2017 Containous SAS
Copyright (c) 2016-2018 Containous SAS
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View file

@ -304,6 +304,7 @@ var _templatesDockerTmpl = []byte(`{{$backendServers := .Servers}}
{{end}}
{{end}}
{{ if hasHeaders $container}}
[frontends."frontend-{{$frontend}}".headers]
{{if hasSSLRedirectHeaders $container}}
SSLRedirect = {{getSSLRedirectHeaders $container}}
@ -383,6 +384,7 @@ var _templatesDockerTmpl = []byte(`{{$backendServers := .Servers}}
{{$k}} = "{{$v}}"
{{end}}
{{end}}
{{end}}
[frontends."frontend-{{$frontend}}".routes."route-frontend-{{$frontend}}"]
rule = "{{getFrontendRule $container}}"
@ -536,6 +538,7 @@ var _templatesKubernetesTmpl = []byte(`[backends]{{range $backendName, $backend
replacement = "{{$frontend.RedirectReplacement}}"
{{end}}
{{if $frontend.Headers }}
[frontends."{{$frontendName}}".headers]
SSLRedirect = {{$frontend.Headers.SSLRedirect}}
SSLTemporaryRedirect = {{$frontend.Headers.SSLTemporaryRedirect}}
@ -579,13 +582,13 @@ var _templatesKubernetesTmpl = []byte(`[backends]{{range $backendName, $backend
{{range $k, $v := $frontend.Headers.SSLProxyHeaders}}
{{$k}} = "{{$v}}"
{{end}}
{{end}}
{{end}}
{{range $routeName, $route := $frontend.Routes}}
[frontends."{{$frontendName}}".routes."{{$routeName}}"]
rule = "{{$route.Rule}}"
{{end}}
{{end}}
`)
{{end}}`)
func templatesKubernetesTmplBytes() ([]byte, error) {
return _templatesKubernetesTmpl, nil
@ -805,6 +808,7 @@ var _templatesMarathonTmpl = []byte(`{{$apps := .Applications}}
{{end}}
{{end}}
{{if hasHeaders $app $serviceName }}
[frontends."{{ getFrontendName $app $serviceName }}".headers]
{{if hasSSLRedirectHeaders $app $serviceName}}
SSLRedirect = {{getSSLRedirectHeaders $app $serviceName}}
@ -884,6 +888,7 @@ var _templatesMarathonTmpl = []byte(`{{$apps := .Applications}}
{{$k}} = "{{$v}}"
{{end}}
{{end}}
{{end}}
[frontends."{{ getFrontendName $app $serviceName }}".routes."route-host{{$app.ID | replace "/" "-"}}{{getServiceNameSuffix $serviceName }}"]
rule = "{{getFrontendRule $app $serviceName}}"

View file

@ -84,7 +84,7 @@ Add more configuration information here.
)
// newBugCmd builds a new Bug command
func newBugCmd(traefikConfiguration interface{}, traefikPointersConfiguration interface{}) *flaeg.Command {
func newBugCmd(traefikConfiguration *TraefikConfiguration, traefikPointersConfiguration *TraefikConfiguration) *flaeg.Command {
//version Command init
return &flaeg.Command{
@ -99,7 +99,7 @@ func newBugCmd(traefikConfiguration interface{}, traefikPointersConfiguration in
}
}
func runBugCmd(traefikConfiguration interface{}) func() error {
func runBugCmd(traefikConfiguration *TraefikConfiguration) func() error {
return func() error {
body, err := createBugReport(traefikConfiguration)
@ -113,7 +113,7 @@ func runBugCmd(traefikConfiguration interface{}) func() error {
}
}
func createBugReport(traefikConfiguration interface{}) (string, error) {
func createBugReport(traefikConfiguration *TraefikConfiguration) (string, error) {
var version bytes.Buffer
if err := getVersionPrint(&version); err != nil {
return "", err
@ -124,7 +124,7 @@ func createBugReport(traefikConfiguration interface{}) (string, error) {
return "", err
}
config, err := anonymize.Do(&traefikConfiguration, true)
config, err := anonymize.Do(traefikConfiguration, true)
if err != nil {
return "", err
}

View file

@ -7,16 +7,27 @@ import (
"github.com/containous/traefik/configuration"
"github.com/containous/traefik/provider/file"
"github.com/containous/traefik/tls"
"github.com/containous/traefik/types"
"github.com/stretchr/testify/assert"
)
func Test_createBugReport(t *testing.T) {
traefikConfiguration := TraefikConfiguration{
traefikConfiguration := &TraefikConfiguration{
ConfigFile: "FOO",
GlobalConfiguration: configuration.GlobalConfiguration{
EntryPoints: configuration.EntryPoints{
"goo": &configuration.EntryPoint{
Address: "hoo.bar",
Auth: &types.Auth{
Basic: &types.Basic{
UsersFile: "foo Basic UsersFile",
Users: types.Users{"foo Basic Users 1", "foo Basic Users 2", "foo Basic Users 3"},
},
Digest: &types.Digest{
UsersFile: "foo Digest UsersFile",
Users: types.Users{"foo Digest Users 1", "foo Digest Users 2", "foo Digest Users 3"},
},
},
},
},
File: &file.Provider{
@ -28,6 +39,11 @@ func Test_createBugReport(t *testing.T) {
report, err := createBugReport(traefikConfiguration)
assert.NoError(t, err, report)
// exported anonymous configuration
assert.NotContains(t, "web Basic Users ", report)
assert.NotContains(t, "foo Digest Users ", report)
assert.NotContains(t, "hoo.bar", report)
}
func Test_anonymize_traefikConfiguration(t *testing.T) {

View file

@ -150,12 +150,15 @@ domain = "marathon.localhost"
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific).
## Labels: overriding default behaviour
### On Containers
Marathon labels may be used to dynamically change the routing and forwarding behaviour.
Labels can be used on containers to override default behaviour:
They may be specified on one of two levels: Application or service.
### Application Level
The following labels can be defined on Marathon applications. They adjust the behaviour for the entire application.
| Label | Description |
|------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
@ -219,9 +222,9 @@ Labels can be used on containers to override default behaviour:
| `traefik.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
| `traefik.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
### On Services
### Service Level
If several ports need to be exposed from a container, the services labels can be used:
For applications that expose multiple ports, specific labels can be used to extract one frontend/backend configuration pair per port. Each such pair is called a _service_. The (freely choosable) name of the service is an integral part of the service label name.
| Label | Description |
|---------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------|

View file

@ -20,7 +20,7 @@
IN THE SOFTWARE.
-->
{% import "partials/language.html" as lang %}
{% import "partials/language.html" as lang with context %}
<!-- Application footer -->
<footer class="md-footer">
@ -97,7 +97,7 @@
<!-- Social links -->
{% block social %}
{% include "partials/social.html" %}
{% include "partials/social.html" %}
{% endblock %}
</div>
</div>

View file

@ -28,6 +28,24 @@ Following is the order by which Traefik tries to identify the port (the first on
1. The port from the application's `portDefinitions` field (possibly indexed through the `traefik.portIndex` label, otherwise the first one).
1. The port from the application's `ipAddressPerTask` field (possibly indexed through the `traefik.portIndex` label, otherwise the first one).
## Applications with multiple ports
Some Marathon applications may expose multiple ports. Traefik supports creating one so-called _service_ per port using [specific labels](/configuration/backends/marathon#service-level).
For instance, assume that a Marathon application exposes a web API on port 80 and an admin interface on port 8080. It would then be possible to make each service available by specifying the following Marathon labels:
```
traefik.web.port=80
```
```
traefik.admin.port=8080
```
(Note that the service names `web` and `admin` can be chosen arbitrarily.)
Technically, Traefik will create one pair of frontend and backend configurations for each service.
## Achieving high availability
### Scenarios

View file

@ -86,7 +86,7 @@ docker $(docker-machine config mhs-demo0) run \
-c /dev/null \
--docker \
--docker.domain=traefik \
--docker.endpoint=tcp://$(docker-machine ip mhs-demo0):3376 \
--docker.endpoint=tcp://$(docker-machine ip mhs-demo0):2376 \
--docker.tls \
--docker.tls.ca=/ssl/ca.pem \
--docker.tls.cert=/ssl/server.pem \
@ -105,7 +105,7 @@ Let's explain this command:
| `-v /var/lib/boot2docker/:/ssl` | mount the ssl keys generated by docker-machine |
| `-c /dev/null` | empty config file |
| `--docker` | enable docker backend |
| `--docker.endpoint=tcp://172.18.0.1:3376` | connect to the swarm master using the docker_gwbridge network |
| `--docker.endpoint=tcp://172.18.0.1:2376` | connect to the swarm master using the docker_gwbridge network |
| `--docker.tls` | enable TLS using the docker-machine keys |
| `--web` | activate the webUI on port 8080 |

View file

@ -577,3 +577,109 @@ func (s *Etcd3Suite) TestSNIDynamicTlsConfig(c *check.C) {
cn = resp.TLS.PeerCertificates[0].Subject.CommonName
c.Assert(cn, checker.Equals, "snitest.org")
}
func (s *Etcd3Suite) TestDeleteSNIDynamicTlsConfig(c *check.C) {
// start Træfik
cmd, display := s.traefikCmd(
withConfigFile("fixtures/etcd/simple_https.toml"),
"--etcd",
"--etcd.endpoint="+ipEtcd+":4001",
"--etcd.useAPIV3=true")
defer display(c)
// prepare to config
snitestComCert, err := ioutil.ReadFile("fixtures/https/snitest.com.cert")
c.Assert(err, checker.IsNil)
snitestComKey, err := ioutil.ReadFile("fixtures/https/snitest.com.key")
c.Assert(err, checker.IsNil)
backend1 := map[string]string{
"/traefik/backends/backend1/circuitbreaker/expression": "NetworkErrorRatio() > 0.5",
"/traefik/backends/backend1/servers/server1/url": "http://" + ipWhoami01 + ":80",
"/traefik/backends/backend1/servers/server1/weight": "1",
"/traefik/backends/backend1/servers/server2/url": "http://" + ipWhoami02 + ":80",
"/traefik/backends/backend1/servers/server2/weight": "1",
}
frontend1 := map[string]string{
"/traefik/frontends/frontend1/backend": "backend1",
"/traefik/frontends/frontend1/entrypoints": "https",
"/traefik/frontends/frontend1/priority": "1",
"/traefik/frontends/frontend1/routes/test_1/rule": "Host:snitest.com",
}
tlsconfigure1 := map[string]string{
"/traefik/tlsconfiguration/snitestcom/entrypoints": "https",
"/traefik/tlsconfiguration/snitestcom/certificate/keyfile": string(snitestComKey),
"/traefik/tlsconfiguration/snitestcom/certificate/certfile": string(snitestComCert),
}
// config backends,frontends and first tls keypair
for key, value := range backend1 {
err := s.kv.Put(key, []byte(value), nil)
c.Assert(err, checker.IsNil)
}
for key, value := range frontend1 {
err := s.kv.Put(key, []byte(value), nil)
c.Assert(err, checker.IsNil)
}
for key, value := range tlsconfigure1 {
err := s.kv.Put(key, []byte(value), nil)
c.Assert(err, checker.IsNil)
}
tr1 := &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
ServerName: "snitest.com",
},
}
// wait for etcd
err = try.Do(60*time.Second, func() error {
_, err := s.kv.Get("/traefik/tlsconfiguration/snitestcom/certificate/keyfile", nil)
return err
})
c.Assert(err, checker.IsNil)
err = cmd.Start()
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
// wait for Træfik
err = try.GetRequest(traefikWebEtcdURL+"api/providers", 60*time.Second, try.BodyContains(string("MIIEpQIBAAKCAQEA1RducBK6EiFDv3TYB8ZcrfKWRVaSfHzWicO3J5WdST9oS7h")))
c.Assert(err, checker.IsNil)
req, err := http.NewRequest(http.MethodGet, "https://127.0.0.1:4443/", nil)
c.Assert(err, checker.IsNil)
client := &http.Client{Transport: tr1}
req.Host = tr1.TLSClientConfig.ServerName
req.Header.Set("Host", tr1.TLSClientConfig.ServerName)
req.Header.Set("Accept", "*/*")
var resp *http.Response
resp, err = client.Do(req)
c.Assert(err, checker.IsNil)
cn := resp.TLS.PeerCertificates[0].Subject.CommonName
c.Assert(cn, checker.Equals, "snitest.com")
// now we delete the tls cert/key pairs,so the endpoint show use default cert/key pair
for key := range tlsconfigure1 {
err := s.kv.Delete(key)
c.Assert(err, checker.IsNil)
}
// waiting for Træfik to pull configuration
err = try.GetRequest(traefikWebEtcdURL+"api/providers", 30*time.Second, try.BodyNotContains("MIIEpQIBAAKCAQEA1RducBK6EiFDv3TYB8ZcrfKWRVaSfHzWicO3J5WdST9oS7h"))
c.Assert(err, checker.IsNil)
req, err = http.NewRequest(http.MethodGet, "https://127.0.0.1:4443/", nil)
c.Assert(err, checker.IsNil)
client = &http.Client{Transport: tr1}
req.Host = tr1.TLSClientConfig.ServerName
req.Header.Set("Host", tr1.TLSClientConfig.ServerName)
req.Header.Set("Accept", "*/*")
resp, err = client.Do(req)
c.Assert(err, checker.IsNil)
cn = resp.TLS.PeerCertificates[0].Subject.CommonName
c.Assert(cn, checker.Equals, "TRAEFIK DEFAULT CERT")
}

View file

@ -6,6 +6,9 @@ defaultEntryPoints = ["https"]
[entryPoints.https]
address = ":4443"
[entryPoints.https.tls]
[entryPoints.https02]
address = ":8443"
[entryPoints.https02.tls]
[web]
address = ":8080"

View file

@ -353,7 +353,7 @@ func startTestServer(port string, statusCode int) (ts *httptest.Server) {
return ts
}
// TestWithSNIConfigRoute involves a client sending HTTPS requests with
// TestWithSNIDynamicConfigRouteWithNoChange involves a client sending HTTPS requests with
// SNI hostnames of "snitest.org" and "snitest.com". The test verifies
// that traefik routes the requests to the expected backends thanks to given certificate if possible
// otherwise thanks to the default one.
@ -424,7 +424,7 @@ func (s *HTTPSSuite) TestWithSNIDynamicConfigRouteWithNoChange(c *check.C) {
c.Assert(resp.StatusCode, checker.Equals, http.StatusNoContent)
}
// TestWithSNIConfigRoute involves a client sending HTTPS requests with
// TestWithSNIDynamicConfigRouteWithChange involves a client sending HTTPS requests with
// SNI hostnames of "snitest.org" and "snitest.com". The test verifies
// that traefik updates its configuration when the HTTPS configuration is modified and
// it routes the requests to the expected backends thanks to given certificate if possible
@ -479,7 +479,7 @@ func (s *HTTPSSuite) TestWithSNIDynamicConfigRouteWithChange(c *check.C) {
req.Header.Set("Accept", "*/*")
// Change certificates configuration file content
modifyCertificateConfFileContent(c, tr1.TLSClientConfig.ServerName, dynamicConfFileName)
modifyCertificateConfFileContent(c, tr1.TLSClientConfig.ServerName, dynamicConfFileName, "https")
var resp *http.Response
err = try.Do(30*time.Second, func() error {
resp, err = client.Do(req)
@ -525,29 +525,121 @@ func (s *HTTPSSuite) TestWithSNIDynamicConfigRouteWithChange(c *check.C) {
c.Assert(resp.StatusCode, checker.Equals, http.StatusNotFound)
}
// modifyCertificateConfFileContent replaces the content of a HTTPS configuration file.
func modifyCertificateConfFileContent(c *check.C, certFileName, confFileName string) {
tlsConf := types.Configuration{
TLSConfiguration: []*traefikTls.Configuration{
{
Certificate: &traefikTls.Certificate{
CertFile: traefikTls.FileOrContent("fixtures/https/" + certFileName + ".cert"),
KeyFile: traefikTls.FileOrContent("fixtures/https/" + certFileName + ".key"),
},
},
// TestWithSNIDynamicConfigRouteWithChangeForEmptyTlsConfiguration involves a client sending HTTPS requests with
// SNI hostnames of "snitest.org" and "snitest.com". The test verifies
// that traefik updates its configuration when the HTTPS configuration is modified, even if it totally deleted, and
// it routes the requests to the expected backends thanks to given certificate if possible
// otherwise thanks to the default one.
func (s *HTTPSSuite) TestWithSNIDynamicConfigRouteWithTlsConfigurationDeletion(c *check.C) {
dynamicConfFileName := s.adaptFile(c, "fixtures/https/dynamic_https.toml", struct{}{})
defer os.Remove(dynamicConfFileName)
confFileName := s.adaptFile(c, "fixtures/https/dynamic_https_sni.toml", struct {
DynamicConfFileName string
}{
DynamicConfFileName: dynamicConfFileName,
})
defer os.Remove(confFileName)
cmd, display := s.traefikCmd(withConfigFile(confFileName))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
tr2 := &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
ServerName: "snitest.org",
},
}
var confBuffer bytes.Buffer
e := toml.NewEncoder(&confBuffer)
err := e.Encode(tlsConf)
// wait for Traefik
err = try.GetRequest("http://127.0.0.1:8080/api/providers", 1*time.Second, try.BodyContains("Host:"+tr2.TLSClientConfig.ServerName))
c.Assert(err, checker.IsNil)
backend2 := startTestServer("9020", http.StatusResetContent)
defer backend2.Close()
err = try.GetRequest(backend2.URL, 500*time.Millisecond, try.StatusCodeIs(http.StatusResetContent))
c.Assert(err, checker.IsNil)
req, err := http.NewRequest(http.MethodGet, "https://127.0.0.1:4443/", nil)
client := &http.Client{Transport: tr2}
req.Host = tr2.TLSClientConfig.ServerName
req.Header.Set("Host", tr2.TLSClientConfig.ServerName)
req.Header.Set("Accept", "*/*")
var resp *http.Response
err = try.Do(30*time.Second, func() error {
resp, err = client.Do(req)
// /!\ If connection is not closed, SSLHandshake will only be done during the first trial /!\
req.Close = true
if err != nil {
return err
}
cn := resp.TLS.PeerCertificates[0].Subject.CommonName
if cn != tr2.TLSClientConfig.ServerName {
return fmt.Errorf("domain %s found in place of %s", cn, tr2.TLSClientConfig.ServerName)
}
return nil
})
c.Assert(err, checker.IsNil)
c.Assert(resp.StatusCode, checker.Equals, http.StatusResetContent)
// Change certificates configuration file content
modifyCertificateConfFileContent(c, "", dynamicConfFileName, "https02")
err = try.Do(60*time.Second, func() error {
resp, err = client.Do(req)
// /!\ If connection is not closed, SSLHandshake will only be done during the first trial /!\
req.Close = true
if err != nil {
return err
}
cn := resp.TLS.PeerCertificates[0].Subject.CommonName
if cn == tr2.TLSClientConfig.ServerName {
return fmt.Errorf("domain %s found in place of default one", tr2.TLSClientConfig.ServerName)
}
return nil
})
c.Assert(err, checker.IsNil)
c.Assert(resp.StatusCode, checker.Equals, http.StatusNotFound)
}
// modifyCertificateConfFileContent replaces the content of a HTTPS configuration file.
func modifyCertificateConfFileContent(c *check.C, certFileName, confFileName, entryPoint string) {
f, err := os.OpenFile("./"+confFileName, os.O_WRONLY, os.ModeExclusive)
c.Assert(err, checker.IsNil)
defer func() {
f.Close()
}()
f.Truncate(0)
_, err = f.Write(confBuffer.Bytes())
c.Assert(err, checker.IsNil)
// If certificate file is not provided, just truncate the configuration file
if len(certFileName) > 0 {
tlsConf := types.Configuration{
TLSConfiguration: []*traefikTls.Configuration{
{
Certificate: &traefikTls.Certificate{
CertFile: traefikTls.FileOrContent("fixtures/https/" + certFileName + ".cert"),
KeyFile: traefikTls.FileOrContent("fixtures/https/" + certFileName + ".key"),
},
EntryPoints: []string{entryPoint},
},
},
}
var confBuffer bytes.Buffer
e := toml.NewEncoder(&confBuffer)
err := e.Encode(tlsConf)
c.Assert(err, checker.IsNil)
_, err = f.Write(confBuffer.Bytes())
c.Assert(err, checker.IsNil)
}
}

View file

@ -34,6 +34,25 @@ func BodyContains(values ...string) ResponseCondition {
}
}
// BodyNotContains returns a retry condition function.
// The condition returns an error if the request body contain one of the given
// strings.
func BodyNotContains(values ...string) ResponseCondition {
return func(res *http.Response) error {
body, err := ioutil.ReadAll(res.Body)
if err != nil {
return fmt.Errorf("failed to read response body: %s", err)
}
for _, value := range values {
if strings.Contains(string(body), value) {
return fmt.Errorf("find '%s' in body '%s'", value, string(body))
}
}
return nil
}
}
// BodyContainsOr returns a retry condition function.
// The condition returns an error if the request body does not contain one of the given
// strings.

View file

@ -24,14 +24,16 @@ type HeaderStruct struct {
}
// NewHeaderFromStruct constructs a new header instance from supplied frontend header struct.
func NewHeaderFromStruct(headers types.Headers) *HeaderStruct {
o := HeaderOptions{
CustomRequestHeaders: headers.CustomRequestHeaders,
CustomResponseHeaders: headers.CustomResponseHeaders,
func NewHeaderFromStruct(headers *types.Headers) *HeaderStruct {
if headers == nil || !headers.HasCustomHeadersDefined() {
return nil
}
return &HeaderStruct{
opt: o,
opt: HeaderOptions{
CustomRequestHeaders: headers.CustomRequestHeaders,
CustomResponseHeaders: headers.CustomResponseHeaders,
},
}
}

View file

@ -17,16 +17,14 @@ var myHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// newHeader constructs a new header instance with supplied options.
func newHeader(options ...HeaderOptions) *HeaderStruct {
var o HeaderOptions
var opt HeaderOptions
if len(options) == 0 {
o = HeaderOptions{}
opt = HeaderOptions{}
} else {
o = options[0]
opt = options[0]
}
return &HeaderStruct{
opt: o,
}
return &HeaderStruct{opt: opt}
}
func TestNoConfig(t *testing.T) {

View file

@ -6,7 +6,11 @@ import (
)
// NewSecure constructs a new Secure instance with supplied options.
func NewSecure(headers types.Headers) *secure.Secure {
func NewSecure(headers *types.Headers) *secure.Secure {
if headers == nil || !headers.HasSecureHeadersDefined() {
return nil
}
opt := secure.Options{
AllowedHosts: headers.AllowedHosts,
HostsProxyHeaders: headers.HostsProxyHeaders,

View file

@ -7,24 +7,14 @@ dev_addr: 0.0.0.0:8000
repo_name: 'GitHub'
repo_url: 'https://github.com/containous/traefik'
# Documentation
docs_dir: 'docs'
#theme: united
#theme: readthedocs
theme: 'material'
#theme: bootstrap
site_favicon: 'img/traefik.icon.png'
copyright: "Copyright &copy; 2016-2017 Containous SAS"
google_analytics:
- 'UA-51880359-3'
- 'docs.traefik.io'
# Options
extra:
theme:
name: 'material'
custom_dir: 'docs/theme'
language: en
include_sidebar: true
favicon: img/traefik.icon.png
logo: img/traefik.logo.png
palette:
primary: 'blue'
@ -37,7 +27,16 @@ extra:
i18n:
prev: 'Previous'
next: 'Next'
copyright: "Copyright &copy; 2016-2018 Containous SAS"
google_analytics:
- 'UA-51880359-3'
- 'docs.traefik.io'
# Options
# Comment because the call of the CDN is very slow.
#extra:
# social:
# - type: 'github'
# link: 'https://github.com/containous/traefik'
@ -48,8 +47,6 @@ extra:
# - type: 'twitter'
# link: 'https://twitter.com/traefikproxy'
theme_dir: docs/theme/
extra_css:
- theme/styles/extra.css
- theme/styles/atom-one-light.css
@ -60,8 +57,8 @@ extra_javascript:
markdown_extensions:
- admonition
# - codehilite(guess_lang=false)
- toc(permalink=true)
- toc:
permalink: true
# Page tree
pages:

View file

@ -21,9 +21,9 @@ func (p *CatalogProvider) buildConfiguration(catalog []catalogUpdate) *types.Con
"getBackendName": getBackendName,
"getBackendAddress": getBackendAddress,
"getBasicAuth": p.getBasicAuth,
"getSticky": getSticky,
"hasStickinessLabel": hasStickinessLabel,
"getStickinessCookieName": getStickinessCookieName,
"getSticky": p.getSticky,
"hasStickinessLabel": p.hasStickinessLabel,
"getStickinessCookieName": p.getStickinessCookieName,
"getAttribute": p.getAttribute,
"getTag": getTag,
"hasTag": hasTag,
@ -147,8 +147,8 @@ func getBackendName(node *api.ServiceEntry, index int) string {
// TODO: Deprecated
// Deprecated replaced by Stickiness
func getSticky(tags []string) string {
stickyTag := getTag(label.TraefikBackendLoadBalancerSticky, tags, "")
func (p *CatalogProvider) getSticky(tags []string) string {
stickyTag := p.getAttribute(label.SuffixBackendLoadBalancerSticky, tags, "")
if len(stickyTag) > 0 {
log.Warnf("Deprecated configuration found: %s. Please use %s.", label.TraefikBackendLoadBalancerSticky, label.TraefikBackendLoadBalancerStickiness)
} else {
@ -157,11 +157,11 @@ func getSticky(tags []string) string {
return stickyTag
}
func hasStickinessLabel(tags []string) bool {
stickinessTag := getTag(label.TraefikBackendLoadBalancerStickiness, tags, "")
func (p *CatalogProvider) hasStickinessLabel(tags []string) bool {
stickinessTag := p.getAttribute(label.SuffixBackendLoadBalancerStickiness, tags, "")
return len(stickinessTag) > 0 && strings.EqualFold(strings.TrimSpace(stickinessTag), "true")
}
func getStickinessCookieName(tags []string) string {
return getTag(label.TraefikBackendLoadBalancerStickinessCookieName, tags, "")
func (p *CatalogProvider) getStickinessCookieName(tags []string) string {
return p.getAttribute(label.SuffixBackendLoadBalancerStickinessCookieName, tags, "")
}

View file

@ -1006,6 +1006,10 @@ func TestGetBasicAuth(t *testing.T) {
}
func TestHasStickinessLabel(t *testing.T) {
p := &CatalogProvider{
Prefix: "traefik",
}
testCases := []struct {
desc string
tags []string
@ -1037,7 +1041,7 @@ func TestHasStickinessLabel(t *testing.T) {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
actual := hasStickinessLabel(test.tags)
actual := p.hasStickinessLabel(test.tags)
assert.Equal(t, test.expected, actual)
})
}

View file

@ -55,6 +55,7 @@ func (p *Provider) buildConfiguration(containersInspected []dockerData) *types.C
"getRateLimitsExtractorFunc": getFuncStringLabel(label.TraefikFrontendRateLimitExtractorFunc, ""),
"getRateLimits": getRateLimits,
// Headers
"hasHeaders": hasHeaders,
"hasRequestHeaders": hasFunc(label.TraefikFrontendRequestHeaders),
"getRequestHeaders": getFuncMapLabel(label.TraefikFrontendRequestHeaders),
"hasResponseHeaders": hasFunc(label.TraefikFrontendResponseHeaders),

View file

@ -187,6 +187,15 @@ func getRateLimits(container dockerData) map[string]*types.Rate {
return label.ParseRateSets(container.Labels, prefix, label.RegexpFrontendRateLimit)
}
func hasHeaders(container dockerData) bool {
for key := range container.Labels {
if strings.HasPrefix(key, label.TraefikFrontendHeaders) {
return true
}
}
return false
}
// Label functions
func getFuncInt64Label(labelName string, defaultValue int64) func(container dockerData) int64 {

View file

@ -75,9 +75,9 @@ func checkServiceLabelPort(container dockerData) error {
// Extract backend from labels for a given service and a given docker container
func getServiceBackend(container dockerData, serviceName string) string {
if value, ok := getServiceLabels(container, serviceName)[label.SuffixFrontendBackend]; ok {
return container.ServiceName + "-" + value
return provider.Normalize(container.ServiceName + "-" + value)
}
return strings.TrimPrefix(container.ServiceName, "/") + "-" + getBackend(container) + "-" + provider.Normalize(serviceName)
return provider.Normalize(container.ServiceName + "-" + getBackend(container) + "-" + serviceName)
}
// Extract port from labels for a given service and a given docker container

View file

@ -402,12 +402,22 @@ func TestDockerGetServiceBackend(t *testing.T) {
})),
expected: "fake-another-backend-myservice",
},
{
container: containerJSON(name("foo.bar")),
expected: "foo-bar-foo-bar-myservice",
},
{
container: containerJSON(labels(map[string]string{
"traefik.myservice.frontend.backend": "custom-backend",
})),
expected: "fake-custom-backend",
},
{
container: containerJSON(labels(map[string]string{
label.TraefikBackend: "another.backend",
})),
expected: "fake-another-backend-myservice",
},
}
for containerID, test := range testCases {

View file

@ -126,7 +126,10 @@ func sendConfigToChannel(configurationChan chan<- types.ConfigMessage, configura
}
func loadFileConfig(filename string) (*types.Configuration, error) {
configuration := new(types.Configuration)
configuration := &types.Configuration{
Frontends: make(map[string]*types.Frontend),
Backends: make(map[string]*types.Backend),
}
if _, err := toml.DecodeFile(filename, configuration); err != nil {
return nil, fmt.Errorf("error reading configuration file: %s", err)
}
@ -142,9 +145,8 @@ func loadFileConfigFromDirectory(directory string, configuration *types.Configur
if configuration == nil {
configuration = &types.Configuration{
Frontends: make(map[string]*types.Frontend),
Backends: make(map[string]*types.Backend),
TLSConfiguration: make([]*tls.Configuration, 0),
Frontends: make(map[string]*types.Frontend),
Backends: make(map[string]*types.Backend),
}
}

View file

@ -152,6 +152,12 @@ func priority(value int) func(*types.Frontend) {
}
}
func headers() func(*types.Frontend) {
return func(f *types.Frontend) {
f.Headers = &types.Headers{}
}
}
func redirectEntryPoint(name string) func(*types.Frontend) {
return func(f *types.Frontend) {
if f.Redirect == nil {

View file

@ -212,7 +212,7 @@ func (p *Provider) loadIngresses(k8sClient Client) (*types.Configuration, error)
priority := label.GetIntValue(i.Annotations, label.TraefikFrontendPriority, 0)
headers := types.Headers{
headers := &types.Headers{
CustomRequestHeaders: label.GetMapValue(i.Annotations, annotationKubernetesCustomRequestHeaders),
CustomResponseHeaders: label.GetMapValue(i.Annotations, annotationKubernetesCustomResponseHeaders),
AllowedHosts: label.GetSliceStringValue(i.Annotations, annotationKubernetesAllowedHosts),

View file

@ -137,18 +137,21 @@ func TestLoadIngresses(t *testing.T) {
),
frontends(
frontend("foo/bar",
headers(),
passHostHeader(),
routes(
route("/bar", "PathPrefix:/bar"),
route("foo", "Host:foo")),
),
frontend("foo/namedthing",
headers(),
passHostHeader(),
routes(
route("/namedthing", "PathPrefix:/namedthing"),
route("foo", "Host:foo")),
),
frontend("bar",
headers(),
passHostHeader(),
routes(route("bar", "Host:bar")),
),
@ -222,6 +225,7 @@ func TestRuleType(t *testing.T) {
require.NoError(t, err, "error loading ingresses")
expected := buildFrontends(frontend("host/path",
headers(),
routes(
route("/path", fmt.Sprintf("%s:/path", test.frontendRuleType)),
route("host", "Host:host")),
@ -267,6 +271,7 @@ func TestGetPassHostHeader(t *testing.T) {
backends(backend("foo/bar", lbMethod("wrr"), servers())),
frontends(
frontend("foo/bar",
headers(),
routes(
route("/bar", "PathPrefix:/bar"),
route("foo", "Host:foo")),
@ -310,6 +315,7 @@ func TestGetPassTLSCert(t *testing.T) {
expected := buildConfiguration(
backends(backend("foo/bar", lbMethod("wrr"), servers())),
frontends(frontend("foo/bar",
headers(),
passHostHeader(),
passTLSCert(),
routes(
@ -364,6 +370,7 @@ func TestOnlyReferencesServicesFromOwnNamespace(t *testing.T) {
expected := buildConfiguration(
backends(backend("foo", lbMethod("wrr"), servers())),
frontends(frontend("foo",
headers(),
passHostHeader(),
routes(route("foo", "Host:foo")),
)),
@ -405,7 +412,9 @@ func TestHostlessIngress(t *testing.T) {
expected := buildConfiguration(
backends(backend("/bar", lbMethod("wrr"), servers())),
frontends(frontend("/bar", routes(route("/bar", "PathPrefix:/bar")))),
frontends(frontend("/bar",
headers(),
routes(route("/bar", "PathPrefix:/bar")))),
)
assert.Equal(t, expected, actual)
@ -503,12 +512,14 @@ func TestServiceAnnotations(t *testing.T) {
),
frontends(
frontend("foo/bar",
headers(),
passHostHeader(),
routes(
route("/bar", "PathPrefix:/bar"),
route("foo", "Host:foo")),
),
frontend("bar",
headers(),
passHostHeader(),
routes(route("bar", "Host:bar"))),
),
@ -712,17 +723,20 @@ func TestIngressAnnotations(t *testing.T) {
),
frontends(
frontend("foo/bar",
headers(),
routes(
route("/bar", "PathPrefix:/bar"),
route("foo", "Host:foo")),
),
frontend("other/stuff",
headers(),
passHostHeader(),
routes(
route("/stuff", "PathPrefix:/stuff"),
route("other", "Host:other")),
),
frontend("other/",
headers(),
passHostHeader(),
entryPoints("http", "https"),
routes(
@ -730,6 +744,7 @@ func TestIngressAnnotations(t *testing.T) {
route("other", "Host:other")),
),
frontend("other/sslstuff",
headers(),
passHostHeader(),
passTLSCert(),
routes(
@ -737,6 +752,7 @@ func TestIngressAnnotations(t *testing.T) {
route("other", "Host:other")),
),
frontend("other/sslstuff",
headers(),
passHostHeader(),
passTLSCert(),
routes(
@ -744,6 +760,7 @@ func TestIngressAnnotations(t *testing.T) {
route("other", "Host:other")),
),
frontend("basic/auth",
headers(),
passHostHeader(),
basicAuth("myUser:myEncodedPW"),
routes(
@ -751,6 +768,7 @@ func TestIngressAnnotations(t *testing.T) {
route("basic", "Host:basic")),
),
frontend("redirect/https",
headers(),
passHostHeader(),
redirectEntryPoint("https"),
routes(
@ -758,6 +776,7 @@ func TestIngressAnnotations(t *testing.T) {
route("redirect", "Host:redirect")),
),
frontend("test/whitelist-source-range",
headers(),
passHostHeader(),
whitelistSourceRange("1.1.1.1/24", "1234:abcd::42/32"),
routes(
@ -765,6 +784,7 @@ func TestIngressAnnotations(t *testing.T) {
route("test", "Host:test")),
),
frontend("rewrite/api",
headers(),
passHostHeader(),
routes(
route("/api", "PathPrefix:/api;ReplacePath:/"),
@ -824,6 +844,7 @@ func TestPriorityHeaderValue(t *testing.T) {
),
frontends(
frontend("foo/bar",
headers(),
passHostHeader(),
priority(1337),
routes(
@ -882,6 +903,7 @@ func TestInvalidPassTLSCertValue(t *testing.T) {
),
frontends(
frontend("foo/bar",
headers(),
passHostHeader(),
routes(
route("/bar", "PathPrefix:/bar"),
@ -939,6 +961,7 @@ func TestInvalidPassHostHeaderValue(t *testing.T) {
),
frontends(
frontend("foo/bar",
headers(),
passHostHeader(),
routes(
route("/bar", "PathPrefix:/bar"),
@ -1112,14 +1135,17 @@ func TestMissingResources(t *testing.T) {
),
frontends(
frontend("fully_working",
headers(),
passHostHeader(),
routes(route("fully_working", "Host:fully_working")),
),
frontend("missing_endpoints",
headers(),
passHostHeader(),
routes(route("missing_endpoints", "Host:missing_endpoints")),
),
frontend("missing_endpoint_subsets",
headers(),
passHostHeader(),
routes(route("missing_endpoint_subsets", "Host:missing_endpoint_subsets")),
),

View file

@ -27,26 +27,27 @@ const (
SuffixFrontendAuthBasic = "frontend.auth.basic"
SuffixFrontendBackend = "frontend.backend"
SuffixFrontendEntryPoints = "frontend.entryPoints"
SuffixFrontendRequestHeaders = "frontend.headers.customRequestHeaders"
SuffixFrontendResponseHeaders = "frontend.headers.customResponseHeaders"
SuffixFrontendHeadersAllowedHosts = "frontend.headers.allowedHosts"
SuffixFrontendHeadersHostsProxyHeaders = "frontend.headers.hostsProxyHeaders"
SuffixFrontendHeadersSSLRedirect = "frontend.headers.SSLRedirect"
SuffixFrontendHeadersSSLTemporaryRedirect = "frontend.headers.SSLTemporaryRedirect"
SuffixFrontendHeadersSSLHost = "frontend.headers.SSLHost"
SuffixFrontendHeadersSSLProxyHeaders = "frontend.headers.SSLProxyHeaders"
SuffixFrontendHeadersSTSSeconds = "frontend.headers.STSSeconds"
SuffixFrontendHeadersSTSIncludeSubdomains = "frontend.headers.STSIncludeSubdomains"
SuffixFrontendHeadersSTSPreload = "frontend.headers.STSPreload"
SuffixFrontendHeadersForceSTSHeader = "frontend.headers.forceSTSHeader"
SuffixFrontendHeadersFrameDeny = "frontend.headers.frameDeny"
SuffixFrontendHeadersCustomFrameOptionsValue = "frontend.headers.customFrameOptionsValue"
SuffixFrontendHeadersContentTypeNosniff = "frontend.headers.contentTypeNosniff"
SuffixFrontendHeadersBrowserXSSFilter = "frontend.headers.browserXSSFilter"
SuffixFrontendHeadersContentSecurityPolicy = "frontend.headers.contentSecurityPolicy"
SuffixFrontendHeadersPublicKey = "frontend.headers.publicKey"
SuffixFrontendHeadersReferrerPolicy = "frontend.headers.referrerPolicy"
SuffixFrontendHeadersIsDevelopment = "frontend.headers.isDevelopment"
SuffixFrontendHeaders = "frontend.headers."
SuffixFrontendRequestHeaders = SuffixFrontendHeaders + "customRequestHeaders"
SuffixFrontendResponseHeaders = SuffixFrontendHeaders + "customResponseHeaders"
SuffixFrontendHeadersAllowedHosts = SuffixFrontendHeaders + "allowedHosts"
SuffixFrontendHeadersHostsProxyHeaders = SuffixFrontendHeaders + "hostsProxyHeaders"
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"
SuffixFrontendHeadersContentSecurityPolicy = SuffixFrontendHeaders + "contentSecurityPolicy"
SuffixFrontendHeadersPublicKey = SuffixFrontendHeaders + "publicKey"
SuffixFrontendHeadersReferrerPolicy = SuffixFrontendHeaders + "referrerPolicy"
SuffixFrontendHeadersIsDevelopment = SuffixFrontendHeaders + "isDevelopment"
SuffixFrontendPassHostHeader = "frontend.passHostHeader"
SuffixFrontendPassTLSCert = "frontend.passTLSCert"
SuffixFrontendPriority = "frontend.priority"
@ -92,6 +93,7 @@ const (
TraefikFrontendRuleType = Prefix + SuffixFrontendRuleType
TraefikFrontendValue = Prefix + SuffixFrontendValue
TraefikFrontendWhitelistSourceRange = Prefix + SuffixFrontendWhitelistSourceRange
TraefikFrontendHeaders = Prefix + SuffixFrontendHeaders
TraefikFrontendRequestHeaders = Prefix + SuffixFrontendRequestHeaders
TraefikFrontendResponseHeaders = Prefix + SuffixFrontendResponseHeaders
TraefikFrontendAllowedHosts = Prefix + SuffixFrontendHeadersAllowedHosts

View file

@ -64,6 +64,7 @@ func (p *Provider) buildConfiguration() *types.Configuration {
"getRateLimitsExtractorFunc": getFuncStringService(label.SuffixFrontendRateLimitExtractorFunc, ""),
"getRateLimits": getRateLimits,
// Headers
"hasHeaders": hasPrefixFuncService(label.TraefikFrontendHeaders),
"hasRequestHeaders": hasFuncService(label.SuffixFrontendRequestHeaders),
"getRequestHeaders": getFuncMapService(label.SuffixFrontendRequestHeaders),
"hasResponseHeaders": hasFuncService(label.SuffixFrontendResponseHeaders),

View file

@ -1,4 +1,4 @@
mkdocs==0.16.3
mkdocs>=0.17.2
pymdown-extensions>=1.4
mkdocs-bootswatch>=0.4.0
mkdocs-material==1.12.2
mkdocs-material>=2.2.6

View file

@ -439,12 +439,12 @@ func (s *Server) loadConfiguration(configMsg types.ConfigMessage) {
if err == nil {
for newServerEntryPointName, newServerEntryPoint := range newServerEntryPoints {
s.serverEntryPoints[newServerEntryPointName].httpRouter.UpdateHandler(newServerEntryPoint.httpRouter.GetHandler())
if newServerEntryPoint.certs.Get() != nil {
if s.globalConfiguration.EntryPoints[newServerEntryPointName].TLS == nil {
if s.globalConfiguration.EntryPoints[newServerEntryPointName].TLS == nil {
if newServerEntryPoint.certs.Get() != nil {
log.Debugf("Certificates not added to non-TLS entryPoint %s.", newServerEntryPointName)
} else {
s.serverEntryPoints[newServerEntryPointName].certs.Set(newServerEntryPoint.certs.Get())
}
} else {
s.serverEntryPoints[newServerEntryPointName].certs.Set(newServerEntryPoint.certs.Get())
}
log.Infof("Server configuration reloaded on %s", s.serverEntryPoints[newServerEntryPointName].httpServer.Addr)
}
@ -980,10 +980,9 @@ func (s *Server) loadConfig(configurations types.Configurations, globalConfigura
continue frontend
}
var headerMiddleware *middlewares.HeaderStruct
headerMiddleware := middlewares.NewHeaderFromStruct(frontend.Headers)
var responseModifier func(res *http.Response) error
if frontend.Headers.HasCustomHeadersDefined() {
headerMiddleware = middlewares.NewHeaderFromStruct(frontend.Headers)
if headerMiddleware != nil {
responseModifier = headerMiddleware.ModifyResponseHeaders
}
@ -1166,8 +1165,9 @@ func (s *Server) loadConfig(configurations types.Configurations, globalConfigura
log.Debugf("Adding header middleware for frontend %s", frontendName)
n.Use(headerMiddleware)
}
if frontend.Headers.HasSecureHeadersDefined() {
secureMiddleware := middlewares.NewSecure(frontend.Headers)
secureMiddleware := middlewares.NewSecure(frontend.Headers)
if secureMiddleware != nil {
log.Debugf("Adding secure middleware for frontend %s", frontendName)
n.UseFunc(secureMiddleware.HandlerFuncWithNext)
}

View file

@ -179,6 +179,7 @@
{{end}}
{{end}}
{{ if hasHeaders $container}}
[frontends."frontend-{{$frontend}}".headers]
{{if hasSSLRedirectHeaders $container}}
SSLRedirect = {{getSSLRedirectHeaders $container}}
@ -258,6 +259,7 @@
{{$k}} = "{{$v}}"
{{end}}
{{end}}
{{end}}
[frontends."frontend-{{$frontend}}".routes."route-frontend-{{$frontend}}"]
rule = "{{getFrontendRule $container}}"

View file

@ -42,6 +42,7 @@
replacement = "{{$frontend.RedirectReplacement}}"
{{end}}
{{if $frontend.Headers }}
[frontends."{{$frontendName}}".headers]
SSLRedirect = {{$frontend.Headers.SSLRedirect}}
SSLTemporaryRedirect = {{$frontend.Headers.SSLTemporaryRedirect}}
@ -85,9 +86,10 @@
{{range $k, $v := $frontend.Headers.SSLProxyHeaders}}
{{$k}} = "{{$v}}"
{{end}}
{{end}}
{{end}}
{{range $routeName, $route := $frontend.Routes}}
[frontends."{{$frontendName}}".routes."{{$routeName}}"]
rule = "{{$route.Rule}}"
{{end}}
{{end}}
{{end}}

View file

@ -103,6 +103,7 @@
{{end}}
{{end}}
{{if hasHeaders $app $serviceName }}
[frontends."{{ getFrontendName $app $serviceName }}".headers]
{{if hasSSLRedirectHeaders $app $serviceName}}
SSLRedirect = {{getSSLRedirectHeaders $app $serviceName}}
@ -182,6 +183,7 @@
{{$k}} = "{{$v}}"
{{end}}
{{end}}
{{end}}
[frontends."{{ getFrontendName $app $serviceName }}".routes."route-host{{$app.ID | replace "/" "-"}}{{getServiceNameSuffix $serviceName }}"]
rule = "{{getFrontendRule $app $serviceName}}"

View file

@ -113,14 +113,14 @@ type Headers struct {
}
// HasCustomHeadersDefined checks to see if any of the custom header elements have been set
func (h Headers) HasCustomHeadersDefined() bool {
return len(h.CustomResponseHeaders) != 0 ||
len(h.CustomRequestHeaders) != 0
func (h *Headers) HasCustomHeadersDefined() bool {
return h != nil && (len(h.CustomResponseHeaders) != 0 ||
len(h.CustomRequestHeaders) != 0)
}
// HasSecureHeadersDefined checks to see if any of the secure header elements have been set
func (h Headers) HasSecureHeadersDefined() bool {
return len(h.AllowedHosts) != 0 ||
func (h *Headers) HasSecureHeadersDefined() bool {
return h != nil && (len(h.AllowedHosts) != 0 ||
len(h.HostsProxyHeaders) != 0 ||
h.SSLRedirect ||
h.SSLTemporaryRedirect ||
@ -137,7 +137,7 @@ func (h Headers) HasSecureHeadersDefined() bool {
h.ContentSecurityPolicy != "" ||
h.PublicKey != "" ||
h.ReferrerPolicy != "" ||
h.IsDevelopment
h.IsDevelopment)
}
// Frontend holds frontend configuration.
@ -150,7 +150,7 @@ type Frontend struct {
Priority int `json:"priority"`
BasicAuth []string `json:"basicAuth"`
WhitelistSourceRange []string `json:"whitelistSourceRange,omitempty"`
Headers Headers `json:"headers,omitempty"`
Headers *Headers `json:"headers,omitempty"`
Errors map[string]*ErrorPage `json:"errors,omitempty"`
RateLimit *RateLimit `json:"ratelimit,omitempty"`
Redirect *Redirect `json:"redirect,omitempty"`