diff --git a/provider/rancher/config.go b/provider/rancher/config.go index 9359fbea1..65c0a4806 100644 --- a/provider/rancher/config.go +++ b/provider/rancher/config.go @@ -45,6 +45,47 @@ func (p *Provider) buildConfiguration(services []rancherData) *types.Configurati "getHealthCheckPath": getFuncString(label.TraefikBackendHealthCheckPath, ""), "getHealthCheckPort": getFuncInt(label.TraefikBackendHealthCheckPort, label.DefaultBackendHealthCheckPort), "getHealthCheckInterval": getFuncString(label.TraefikBackendHealthCheckInterval, ""), + + "hasRequestHeaders": hasFunc(label.TraefikFrontendRequestHeaders), + "getRequestHeaders": getFuncMap(label.TraefikFrontendRequestHeaders), + "hasResponseHeaders": hasFunc(label.TraefikFrontendResponseHeaders), + "getResponseHeaders": getFuncMap(label.TraefikFrontendResponseHeaders), + "hasAllowedHostsHeaders": hasFunc(label.TraefikFrontendAllowedHosts), + "getAllowedHostsHeaders": getFuncSliceString(label.TraefikFrontendAllowedHosts), + "hasHostsProxyHeaders": hasFunc(label.TraefikFrontendHostsProxyHeaders), + "getHostsProxyHeaders": getFuncSliceString(label.TraefikFrontendHostsProxyHeaders), + "hasSSLRedirectHeaders": hasFunc(label.TraefikFrontendSSLRedirect), + "getSSLRedirectHeaders": getFuncBool(label.TraefikFrontendSSLRedirect, false), + "hasSSLTemporaryRedirectHeaders": hasFunc(label.TraefikFrontendSSLTemporaryRedirect), + "getSSLTemporaryRedirectHeaders": getFuncBool(label.TraefikFrontendSSLTemporaryRedirect, false), + "hasSSLHostHeaders": hasFunc(label.TraefikFrontendSSLHost), + "getSSLHostHeaders": getFuncString(label.TraefikFrontendSSLHost, ""), + "hasSSLProxyHeaders": hasFunc(label.TraefikFrontendSSLProxyHeaders), + "getSSLProxyHeaders": getFuncMap(label.TraefikFrontendSSLProxyHeaders), + "hasSTSSecondsHeaders": hasFunc(label.TraefikFrontendSTSSeconds), + "getSTSSecondsHeaders": getFuncInt64(label.TraefikFrontendSTSSeconds, 0), + "hasSTSIncludeSubdomainsHeaders": hasFunc(label.TraefikFrontendSTSIncludeSubdomains), + "getSTSIncludeSubdomainsHeaders": getFuncBool(label.TraefikFrontendSTSIncludeSubdomains, false), + "hasSTSPreloadHeaders": hasFunc(label.TraefikFrontendSTSPreload), + "getSTSPreloadHeaders": getFuncBool(label.TraefikFrontendSTSPreload, false), + "hasForceSTSHeaderHeaders": hasFunc(label.TraefikFrontendForceSTSHeader), + "getForceSTSHeaderHeaders": getFuncBool(label.TraefikFrontendForceSTSHeader, false), + "hasFrameDenyHeaders": hasFunc(label.TraefikFrontendFrameDeny), + "getFrameDenyHeaders": getFuncBool(label.TraefikFrontendFrameDeny, false), + "hasCustomFrameOptionsValueHeaders": hasFunc(label.TraefikFrontendCustomFrameOptionsValue), + "getCustomFrameOptionsValueHeaders": getFuncString(label.TraefikFrontendCustomFrameOptionsValue, ""), + "hasContentTypeNosniffHeaders": hasFunc(label.TraefikFrontendContentTypeNosniff), + "getContentTypeNosniffHeaders": getFuncBool(label.TraefikFrontendContentTypeNosniff, false), + "hasBrowserXSSFilterHeaders": hasFunc(label.TraefikFrontendBrowserXSSFilter), + "getBrowserXSSFilterHeaders": getFuncBool(label.TraefikFrontendBrowserXSSFilter, false), + "hasContentSecurityPolicyHeaders": hasFunc(label.TraefikFrontendContentSecurityPolicy), + "getContentSecurityPolicyHeaders": getFuncString(label.TraefikFrontendContentSecurityPolicy, ""), + "hasPublicKeyHeaders": hasFunc(label.TraefikFrontendPublicKey), + "getPublicKeyHeaders": getFuncString(label.TraefikFrontendPublicKey, ""), + "hasReferrerPolicyHeaders": hasFunc(label.TraefikFrontendReferrerPolicy), + "getReferrerPolicyHeaders": getFuncString(label.TraefikFrontendReferrerPolicy, ""), + "hasIsDevelopmentHeaders": hasFunc(label.TraefikFrontendIsDevelopment), + "getIsDevelopmentHeaders": getFuncBool(label.TraefikFrontendIsDevelopment, false), } // filter services @@ -65,9 +106,9 @@ func (p *Provider) buildConfiguration(services []rancherData) *types.Configurati Backends map[string]rancherData Domain string }{ - frontends, - backends, - p.Domain, + Frontends: frontends, + Backends: backends, + Domain: p.Domain, } configuration, err := p.GetConfiguration("templates/rancher.tmpl", RancherFuncMap, templateObjects) @@ -190,6 +231,12 @@ func getFuncSliceString(labelName string) func(service rancherData) []string { } } +func getFuncMap(labelName string) func(service rancherData) map[string]string { + return func(service rancherData) map[string]string { + return label.GetMapValue(service.Labels, labelName) + } +} + func hasFunc(labelName string) func(service rancherData) bool { return func(service rancherData) bool { return label.Has(service.Labels, labelName) diff --git a/provider/rancher/config_test.go b/provider/rancher/config_test.go index b2cb35017..058a510f6 100644 --- a/provider/rancher/config_test.go +++ b/provider/rancher/config_test.go @@ -9,6 +9,84 @@ import ( "github.com/stretchr/testify/require" ) +func TestProviderBuildConfiguration(t *testing.T) { + provider := &Provider{ + Domain: "rancher.localhost", + ExposedByDefault: true, + } + + testCases := []struct { + desc string + services []rancherData + expectedFrontends map[string]*types.Frontend + expectedBackends map[string]*types.Backend + }{ + { + desc: "without services", + services: []rancherData{}, + expectedFrontends: map[string]*types.Frontend{}, + expectedBackends: map[string]*types.Backend{}, + }, + { + desc: "with services", + services: []rancherData{ + { + Name: "test/service", + Labels: map[string]string{ + label.TraefikPort: "80", + label.TraefikFrontendAuthBasic: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", + label.TraefikFrontendRedirectEntryPoint: "https", + }, + Health: "healthy", + Containers: []string{"127.0.0.1"}, + }, + }, + expectedFrontends: map[string]*types.Frontend{ + "frontend-Host-test-service-rancher-localhost": { + Backend: "backend-test-service", + PassHostHeader: true, + EntryPoints: []string{}, + BasicAuth: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, + Priority: 0, + Redirect: &types.Redirect{ + EntryPoint: "https", + }, + Routes: map[string]types.Route{ + "route-frontend-Host-test-service-rancher-localhost": { + Rule: "Host:test.service.rancher.localhost", + }, + }, + }, + }, + expectedBackends: map[string]*types.Backend{ + "backend-test-service": { + Servers: map[string]types.Server{ + "server-0": { + URL: "http://127.0.0.1:80", + Weight: 0, + }, + }, + CircuitBreaker: nil, + }, + }, + }, + } + + for _, test := range testCases { + test := test + + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + actualConfig := provider.buildConfiguration(test.services) + require.NotNil(t, actualConfig) + + assert.EqualValues(t, test.expectedBackends, actualConfig.Backends) + assert.EqualValues(t, test.expectedFrontends, actualConfig.Frontends) + }) + } +} + func TestProviderServiceFilter(t *testing.T) { provider := &Provider{ Domain: "rancher.localhost", @@ -338,84 +416,6 @@ func TestGetBackend(t *testing.T) { } } -func TestProviderLoadRancherConfig(t *testing.T) { - provider := &Provider{ - Domain: "rancher.localhost", - ExposedByDefault: true, - } - - testCases := []struct { - desc string - services []rancherData - expectedFrontends map[string]*types.Frontend - expectedBackends map[string]*types.Backend - }{ - { - desc: "without services", - services: []rancherData{}, - expectedFrontends: map[string]*types.Frontend{}, - expectedBackends: map[string]*types.Backend{}, - }, - { - desc: "with services", - services: []rancherData{ - { - Name: "test/service", - Labels: map[string]string{ - label.TraefikPort: "80", - label.TraefikFrontendAuthBasic: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", - label.TraefikFrontendRedirectEntryPoint: "https", - }, - Health: "healthy", - Containers: []string{"127.0.0.1"}, - }, - }, - expectedFrontends: map[string]*types.Frontend{ - "frontend-Host-test-service-rancher-localhost": { - Backend: "backend-test-service", - PassHostHeader: true, - EntryPoints: []string{}, - BasicAuth: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, - Priority: 0, - Redirect: &types.Redirect{ - EntryPoint: "https", - }, - Routes: map[string]types.Route{ - "route-frontend-Host-test-service-rancher-localhost": { - Rule: "Host:test.service.rancher.localhost", - }, - }, - }, - }, - expectedBackends: map[string]*types.Backend{ - "backend-test-service": { - Servers: map[string]types.Server{ - "server-0": { - URL: "http://127.0.0.1:80", - Weight: 0, - }, - }, - CircuitBreaker: nil, - }, - }, - }, - } - - for _, test := range testCases { - test := test - - t.Run(test.desc, func(t *testing.T) { - t.Parallel() - - actualConfig := provider.buildConfiguration(test.services) - require.NotNil(t, actualConfig) - - assert.EqualValues(t, test.expectedBackends, actualConfig.Backends) - assert.EqualValues(t, test.expectedFrontends, actualConfig.Frontends) - }) - } -} - func TestHasRedirect(t *testing.T) { testCases := []struct { desc string diff --git a/templates/rancher.tmpl b/templates/rancher.tmpl index 732d4a267..67ba00395 100644 --- a/templates/rancher.tmpl +++ b/templates/rancher.tmpl @@ -68,6 +68,83 @@ replacement = "{{getRedirectReplacement $service}}" {{end}} + [frontends."frontend-{{$frontendName}}".headers] + {{if hasSSLRedirectHeaders $service}} + SSLRedirect = {{getSSLRedirectHeaders $service}} + {{end}} + {{if hasSSLTemporaryRedirectHeaders $service}} + SSLTemporaryRedirect = {{getSSLTemporaryRedirectHeaders $service}} + {{end}} + {{if hasSSLHostHeaders $service}} + SSLHost = "{{getSSLHostHeaders $service}}" + {{end}} + {{if hasSTSSecondsHeaders $service}} + STSSeconds = {{getSTSSecondsHeaders $service}} + {{end}} + {{if hasSTSIncludeSubdomainsHeaders $service}} + STSIncludeSubdomains = {{getSTSIncludeSubdomainsHeaders $service}} + {{end}} + {{if hasSTSPreloadHeaders $service}} + STSPreload = {{getSTSPreloadHeaders $service}} + {{end}} + {{if hasForceSTSHeaderHeaders $service}} + ForceSTSHeader = {{getForceSTSHeaderHeaders $service}} + {{end}} + {{if hasFrameDenyHeaders $service}} + FrameDeny = {{getFrameDenyHeaders $service}} + {{end}} + {{if hasCustomFrameOptionsValueHeaders $service}} + CustomFrameOptionsValue = "{{getCustomFrameOptionsValueHeaders $service}}" + {{end}} + {{if hasContentTypeNosniffHeaders $service}} + ContentTypeNosniff = {{getContentTypeNosniffHeaders $service}} + {{end}} + {{if hasBrowserXSSFilterHeaders $service}} + BrowserXSSFilter = {{getBrowserXSSFilterHeaders $service}} + {{end}} + {{if hasContentSecurityPolicyHeaders $service}} + ContentSecurityPolicy = "{{getContentSecurityPolicyHeaders $service}}" + {{end}} + {{if hasPublicKeyHeaders $service}} + PublicKey = "{{getPublicKeyHeaders $service}}" + {{end}} + {{if hasReferrerPolicyHeaders $service}} + ReferrerPolicy = "{{getReferrerPolicyHeaders $service}}" + {{end}} + {{if hasIsDevelopmentHeaders $service}} + IsDevelopment = {{getIsDevelopmentHeaders $service}} + {{end}} + {{if hasRequestHeaders $service}} + [frontends."frontend-{{$frontendName}}".headers.customRequestHeaders] + {{range $k, $v := getRequestHeaders $service}} + {{$k}} = "{{$v}}" + {{end}} + {{end}} + {{if hasResponseHeaders $service}} + [frontends."frontend-{{$frontendName}}".headers.customResponseHeaders] + {{range $k, $v := getResponseHeaders $service}} + {{$k}} = "{{$v}}" + {{end}} + {{end}} + {{if hasAllowedHostsHeaders $service}} + [frontends."frontend-{{$frontendName}}".headers.AllowedHosts] + {{range getAllowedHostsHeaders $service}} + "{{.}}" + {{end}} + {{end}} + {{if hasHostsProxyHeaders $service}} + [frontends."frontend-{{$frontendName}}".headers.HostsProxyHeaders] + {{range getHostsProxyHeaders $service}} + "{{.}}" + {{end}} + {{end}} + {{if hasSSLProxyHeaders $service}} + [frontends."frontend-{{$frontendName}}".headers.SSLProxyHeaders] + {{range $k, $v := getSSLProxyHeaders $service}} + {{$k}} = "{{$v}}" + {{end}} + {{end}} + [frontends."frontend-{{$frontendName}}".routes."route-frontend-{{$frontendName}}"] rule = "{{getFrontendRule $service}}"