From 393651f5e2f8a351dc36a6c70a41f8516458749e Mon Sep 17 00:00:00 2001 From: Fernandez Ludovic Date: Wed, 20 Dec 2017 22:59:57 +0100 Subject: [PATCH] feat(ecs): add error pages and rate limits. --- provider/ecs/config.go | 68 +++++++++++++++++---- templates/ecs.tmpl | 130 ++++++++++++++++++++++++----------------- 2 files changed, 133 insertions(+), 65 deletions(-) diff --git a/provider/ecs/config.go b/provider/ecs/config.go index d2d5a5d95..88a24aad8 100644 --- a/provider/ecs/config.go +++ b/provider/ecs/config.go @@ -36,18 +36,23 @@ func (p *Provider) buildConfiguration(services map[string][]ecsInstance) (*types "getMaxConnExtractorFunc": getFuncFirstStringValue(label.TraefikBackendMaxConnExtractorFunc, label.DefaultBackendMaxconnExtractorFunc), // Frontend functions - "filterFrontends": filterFrontends, - "getFrontendRule": p.getFrontendRule, - "getPassHostHeader": getFuncStringValue(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeader), - "getPassTLSCert": getFuncBoolValue(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert), - "getPriority": getFuncStringValue(label.TraefikFrontendPriority, label.DefaultFrontendPriority), - "getBasicAuth": getFuncSliceString(label.TraefikFrontendAuthBasic), - "getEntryPoints": getFuncSliceString(label.TraefikFrontendEntryPoints), - "getWhitelistSourceRange": getFuncSliceString(label.TraefikFrontendWhitelistSourceRange), - "hasRedirect": hasRedirect, - "getRedirectEntryPoint": getFuncStringValue(label.TraefikFrontendRedirectEntryPoint, label.DefaultFrontendRedirectEntryPoint), - "getRedirectRegex": getFuncStringValue(label.TraefikFrontendRedirectRegex, ""), - "getRedirectReplacement": getFuncStringValue(label.TraefikFrontendRedirectReplacement, ""), + "filterFrontends": filterFrontends, + "getFrontendRule": p.getFrontendRule, + "getPassHostHeader": getFuncStringValue(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeader), + "getPassTLSCert": getFuncBoolValue(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert), + "getPriority": getFuncStringValue(label.TraefikFrontendPriority, label.DefaultFrontendPriority), + "getBasicAuth": getFuncSliceString(label.TraefikFrontendAuthBasic), + "getEntryPoints": getFuncSliceString(label.TraefikFrontendEntryPoints), + "getWhitelistSourceRange": getFuncSliceString(label.TraefikFrontendWhitelistSourceRange), + "hasRedirect": hasRedirect, + "getRedirectEntryPoint": getFuncStringValue(label.TraefikFrontendRedirectEntryPoint, label.DefaultFrontendRedirectEntryPoint), + "getRedirectRegex": getFuncStringValue(label.TraefikFrontendRedirectRegex, ""), + "getRedirectReplacement": getFuncStringValue(label.TraefikFrontendRedirectReplacement, ""), + "hasErrorPages": hasPrefixFuncLabel(label.Prefix + label.BaseFrontendErrorPage), + "getErrorPages": getErrorPages, + "hasRateLimits": hasFuncLabel(label.TraefikFrontendRateLimitExtractorFunc), + "getRateLimitsExtractorFunc": getFuncStringValue(label.TraefikFrontendRateLimitExtractorFunc, ""), + "getRateLimits": getRateLimits, // Headers "hasRequestHeaders": hasFuncLabel(label.TraefikFrontendRequestHeaders), "getRequestHeaders": getFuncMapValue(label.TraefikFrontendRequestHeaders), @@ -154,6 +159,20 @@ func hasRedirect(instance ecsInstance) bool { hasLabel(instance, label.TraefikFrontendRedirectRegex) && hasLabel(instance, label.TraefikFrontendRedirectReplacement) } +func getErrorPages(instance ecsInstance) map[string]*types.ErrorPage { + labels := mapPToMap(instance.containerDefinition.DockerLabels) + + prefix := label.Prefix + label.BaseFrontendErrorPage + return label.ParseErrorPages(labels, prefix, label.RegexpFrontendErrorPage) +} + +func getRateLimits(instance ecsInstance) map[string]*types.Rate { + labels := mapPToMap(instance.containerDefinition.DockerLabels) + + prefix := label.Prefix + label.BaseFrontendRateLimit + return label.ParseRateSets(labels, prefix, label.RegexpFrontendRateLimit) +} + // Label functions func hasFuncLabel(labelName string) func(i ecsInstance) bool { @@ -162,6 +181,12 @@ func hasFuncLabel(labelName string) func(i ecsInstance) bool { } } +func hasPrefixFuncLabel(prefix string) func(i ecsInstance) bool { + return func(i ecsInstance) bool { + return hasPrefix(i, prefix) + } +} + func getFuncStringValue(labelName string, defaultValue string) func(i ecsInstance) string { return func(i ecsInstance) string { return getStringValue(i, labelName, defaultValue) @@ -236,6 +261,15 @@ func hasLabel(i ecsInstance, labelName string) bool { return ok && value != nil && len(*value) > 0 } +func hasPrefix(i ecsInstance, prefix string) bool { + for name, value := range i.containerDefinition.DockerLabels { + if strings.HasPrefix(name, prefix) && value != nil && len(*value) > 0 { + return true + } + } + return false +} + func getStringValue(i ecsInstance, labelName string, defaultValue string) string { if v, ok := i.containerDefinition.DockerLabels[labelName]; ok { if v == nil { @@ -328,6 +362,16 @@ func getFirstStringValue(instances []ecsInstance, labelName string, defaultValue return getStringValue(instances[0], labelName, defaultValue) } +func mapPToMap(src map[string]*string) map[string]string { + result := make(map[string]string) + for key, value := range src { + if value != nil && len(*value) > 0 { + result[key] = *value + } + } + return result +} + func isEnabled(i ecsInstance, exposedByDefault bool) bool { return getBoolValue(i, label.TraefikEnable, exposedByDefault) } diff --git a/templates/ecs.tmpl b/templates/ecs.tmpl index 0fb9778c8..ca735535d 100644 --- a/templates/ecs.tmpl +++ b/templates/ecs.tmpl @@ -39,114 +39,138 @@ [frontends] {{range $serviceName, $instances := .Services}} -{{range filterFrontends $instances}} +{{range $instance := filterFrontends $instances}} [frontends.frontend-{{ $serviceName }}] backend = "backend-{{ $serviceName }}" - priority = {{ getPriority .}} - passHostHeader = {{ getPassHostHeader .}} - passTLSCert = {{ getPassTLSCert .}} + priority = {{ getPriority $instance}} + passHostHeader = {{ getPassHostHeader $instance}} + passTLSCert = {{ getPassTLSCert $instance}} - entryPoints = [{{range getEntryPoints .}} + entryPoints = [{{range getEntryPoints $instance}} "{{.}}", {{end}}] - {{if getWhitelistSourceRange .}} - whitelistSourceRange = [{{range getWhitelistSourceRange .}} + {{if getWhitelistSourceRange $instance}} + whitelistSourceRange = [{{range getWhitelistSourceRange $instance}} "{{.}}", {{end}}] {{end}} - basicAuth = [{{range getBasicAuth .}} + basicAuth = [{{range getBasicAuth $instance}} "{{.}}", {{end}}] - {{if hasRedirect .}} + {{if hasRedirect $instance}} [frontends."frontend-{{ $serviceName }}".redirect] - entryPoint = "{{getRedirectEntryPoint .}}" - regex = "{{getRedirectRegex .}}" - replacement = "{{getRedirectReplacement .}}" + entryPoint = "{{getRedirectEntryPoint $instance}}" + regex = "{{getRedirectRegex $instance}}" + replacement = "{{getRedirectReplacement $instance}}" + {{end}} + + {{ if hasErrorPages $instance }} + [frontends."frontend-{{ $serviceName }}".errors] + {{ range $pageName, $page := getErrorPages $instance }} + [frontends."frontend-{{ $serviceName }}".errors.{{ $pageName }}] + status = [{{range $page.Status}} + "{{.}}", + {{end}}] + backend = "{{$page.Backend}}" + query = "{{$page.Query}}" + {{end}} + {{end}} + + {{ if hasRateLimits $instance }} + [frontends."frontend-{{ $serviceName }}".rateLimit] + extractorFunc = "{{ getRateLimitsExtractorFunc $instance }}" + [frontends."frontend-{{ $serviceName }}".rateLimit.rateSet] + {{ range $limitName, $rateLimit := getRateLimits $instance }} + [frontends."frontend-{{ $serviceName }}".rateLimit.rateSet.{{ $limitName }}] + period = "{{ $rateLimit.Period }}" + average = {{ $rateLimit.Average }} + burst = {{ $rateLimit.Burst }} + {{end}} {{end}} [frontends."frontend-{{ $serviceName }}".headers] - {{if hasSSLRedirectHeaders .}} - SSLRedirect = {{getSSLRedirectHeaders .}} + {{if hasSSLRedirectHeaders $instance}} + SSLRedirect = {{getSSLRedirectHeaders $instance}} {{end}} - {{if hasSSLTemporaryRedirectHeaders .}} - SSLTemporaryRedirect = {{getSSLTemporaryRedirectHeaders .}} + {{if hasSSLTemporaryRedirectHeaders $instance}} + SSLTemporaryRedirect = {{getSSLTemporaryRedirectHeaders $instance}} {{end}} - {{if hasSSLHostHeaders .}} - SSLHost = "{{getSSLHostHeaders .}}" + {{if hasSSLHostHeaders $instance}} + SSLHost = "{{getSSLHostHeaders $instance}}" {{end}} - {{if hasSTSSecondsHeaders .}} - STSSeconds = {{getSTSSecondsHeaders .}} + {{if hasSTSSecondsHeaders $instance}} + STSSeconds = {{getSTSSecondsHeaders $instance}} {{end}} - {{if hasSTSIncludeSubdomainsHeaders .}} - STSIncludeSubdomains = {{getSTSIncludeSubdomainsHeaders .}} + {{if hasSTSIncludeSubdomainsHeaders $instance}} + STSIncludeSubdomains = {{getSTSIncludeSubdomainsHeaders $instance}} {{end}} - {{if hasSTSPreloadHeaders .}} - STSPreload = {{getSTSPreloadHeaders .}} + {{if hasSTSPreloadHeaders $instance}} + STSPreload = {{getSTSPreloadHeaders $instance}} {{end}} - {{if hasForceSTSHeaderHeaders .}} - ForceSTSHeader = {{getForceSTSHeaderHeaders .}} + {{if hasForceSTSHeaderHeaders $instance}} + ForceSTSHeader = {{getForceSTSHeaderHeaders $instance}} {{end}} - {{if hasFrameDenyHeaders .}} - FrameDeny = {{getFrameDenyHeaders .}} + {{if hasFrameDenyHeaders $instance}} + FrameDeny = {{getFrameDenyHeaders $instance}} {{end}} - {{if hasCustomFrameOptionsValueHeaders .}} - CustomFrameOptionsValue = "{{getCustomFrameOptionsValueHeaders .}}" + {{if hasCustomFrameOptionsValueHeaders $instance}} + CustomFrameOptionsValue = "{{getCustomFrameOptionsValueHeaders $instance}}" {{end}} - {{if hasContentTypeNosniffHeaders .}} - ContentTypeNosniff = {{getContentTypeNosniffHeaders .}} + {{if hasContentTypeNosniffHeaders $instance}} + ContentTypeNosniff = {{getContentTypeNosniffHeaders $instance}} {{end}} - {{if hasBrowserXSSFilterHeaders .}} - BrowserXSSFilter = {{getBrowserXSSFilterHeaders .}} + {{if hasBrowserXSSFilterHeaders $instance}} + BrowserXSSFilter = {{getBrowserXSSFilterHeaders $instance}} {{end}} - {{if hasContentSecurityPolicyHeaders .}} - ContentSecurityPolicy = "{{getContentSecurityPolicyHeaders .}}" + {{if hasContentSecurityPolicyHeaders $instance}} + ContentSecurityPolicy = "{{getContentSecurityPolicyHeaders $instance}}" {{end}} - {{if hasPublicKeyHeaders .}} - PublicKey = "{{getPublicKeyHeaders .}}" + {{if hasPublicKeyHeaders $instance}} + PublicKey = "{{getPublicKeyHeaders $instance}}" {{end}} - {{if hasReferrerPolicyHeaders .}} - ReferrerPolicy = "{{getReferrerPolicyHeaders .}}" + {{if hasReferrerPolicyHeaders $instance}} + ReferrerPolicy = "{{getReferrerPolicyHeaders $instance}}" {{end}} - {{if hasIsDevelopmentHeaders .}} - IsDevelopment = {{getIsDevelopmentHeaders .}} + {{if hasIsDevelopmentHeaders $instance}} + IsDevelopment = {{getIsDevelopmentHeaders $instance}} {{end}} - {{if hasRequestHeaders .}} + {{if hasRequestHeaders $instance}} [frontends."frontend-{{ $serviceName }}".headers.customRequestHeaders] - {{range $k, $v := getRequestHeaders .}} + {{range $k, $v := getRequestHeaders $instance}} {{$k}} = "{{$v}}" {{end}} {{end}} - {{if hasResponseHeaders .}} + {{if hasResponseHeaders $instance}} [frontends."frontend-{{ $serviceName }}".headers.customResponseHeaders] - {{range $k, $v := getResponseHeaders .}} + {{range $k, $v := getResponseHeaders $instance}} {{$k}} = "{{$v}}" {{end}} {{end}} - {{if hasAllowedHostsHeaders .}} + {{if hasAllowedHostsHeaders $instance}} [frontends."frontend-{{ $serviceName }}".headers.AllowedHosts] - {{range getAllowedHostsHeaders .}} + {{range getAllowedHostsHeaders $instance}} "{{.}}" {{end}} {{end}} - {{if hasHostsProxyHeaders .}} + {{if hasHostsProxyHeaders $instance}} [frontends."frontend-{{ $serviceName }}".headers.HostsProxyHeaders] - {{range getHostsProxyHeaders .}} + {{range getHostsProxyHeaders $instance}} "{{.}}" {{end}} {{end}} - {{if hasSSLProxyHeaders .}} + {{if hasSSLProxyHeaders $instance}} [frontends."frontend-{{ $serviceName }}".headers.SSLProxyHeaders] - {{range $k, $v := getSSLProxyHeaders .}} + {{range $k, $v := getSSLProxyHeaders $instance}} {{$k}} = "{{$v}}" {{end}} {{end}} [frontends.frontend-{{ $serviceName }}.routes.route-frontend-{{ $serviceName }}] - rule = "{{getFrontendRule .}}" + rule = "{{getFrontendRule $instance}}" {{end}} {{end}} \ No newline at end of file