2017-12-02 18:26:44 +00:00
|
|
|
package docker
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/containous/traefik/provider"
|
|
|
|
"github.com/containous/traefik/provider/label"
|
2017-12-18 15:26:04 +00:00
|
|
|
"github.com/containous/traefik/types"
|
2017-12-02 18:26:44 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Specific functions
|
|
|
|
|
|
|
|
// Extract rule from labels for a given service and a given docker container
|
|
|
|
func (p Provider) getServiceFrontendRule(container dockerData, serviceName string) string {
|
|
|
|
if value, ok := getServiceLabels(container, serviceName)[label.SuffixFrontendRule]; ok {
|
|
|
|
return value
|
|
|
|
}
|
|
|
|
return p.getFrontendRule(container)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if for the given container, we find labels that are defining services
|
|
|
|
func hasServices(container dockerData) bool {
|
|
|
|
return len(label.ExtractServiceProperties(container.Labels)) > 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// Gets array of service names for a given container
|
|
|
|
func getServiceNames(container dockerData) []string {
|
|
|
|
labelServiceProperties := label.ExtractServiceProperties(container.Labels)
|
|
|
|
keys := make([]string, 0, len(labelServiceProperties))
|
|
|
|
for k := range labelServiceProperties {
|
|
|
|
keys = append(keys, k)
|
|
|
|
}
|
|
|
|
return keys
|
|
|
|
}
|
|
|
|
|
|
|
|
// checkServiceLabelPort checks if all service names have a port service label
|
|
|
|
// or if port container label exists for default value
|
|
|
|
func checkServiceLabelPort(container dockerData) error {
|
|
|
|
// If port container label is present, there is a default values for all ports, use it for the check
|
|
|
|
_, err := strconv.Atoi(container.Labels[label.TraefikPort])
|
|
|
|
if err != nil {
|
|
|
|
serviceLabelPorts := make(map[string]struct{})
|
|
|
|
serviceLabels := make(map[string]struct{})
|
|
|
|
for lbl := range container.Labels {
|
|
|
|
// Get all port service labels
|
2018-01-09 15:26:03 +00:00
|
|
|
portLabel := extractServicePort(lbl)
|
2017-12-02 18:26:44 +00:00
|
|
|
if len(portLabel) > 0 {
|
|
|
|
serviceLabelPorts[portLabel[0]] = struct{}{}
|
|
|
|
}
|
|
|
|
// Get only one instance of all service names from service labels
|
2018-01-09 15:26:03 +00:00
|
|
|
servicesLabelNames := label.FindServiceSubmatch(lbl)
|
2017-12-18 15:26:04 +00:00
|
|
|
|
2018-01-09 15:26:03 +00:00
|
|
|
if len(servicesLabelNames) > 0 {
|
2017-12-02 18:26:44 +00:00
|
|
|
serviceLabels[strings.Split(servicesLabelNames[0], ".")[1]] = struct{}{}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// If the number of service labels is different than the number of port services label
|
|
|
|
// there is an error
|
|
|
|
if len(serviceLabels) == len(serviceLabelPorts) {
|
|
|
|
for labelPort := range serviceLabelPorts {
|
|
|
|
_, err = strconv.Atoi(container.Labels[labelPort])
|
|
|
|
if err != nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
err = errors.New("port service labels missing, please use traefik.port as default value or define all port service labels")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-01-09 15:26:03 +00:00
|
|
|
func extractServicePort(labelName string) []string {
|
|
|
|
if strings.HasPrefix(labelName, label.TraefikFrontend+".") ||
|
|
|
|
strings.HasPrefix(labelName, label.TraefikBackend+".") {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return label.PortRegexp.FindStringSubmatch(labelName)
|
|
|
|
}
|
|
|
|
|
2017-12-02 18:26:44 +00:00
|
|
|
// Extract backend from labels for a given service and a given docker container
|
2018-01-09 15:26:03 +00:00
|
|
|
func getServiceBackendName(container dockerData, serviceName string) string {
|
2017-12-02 18:26:44 +00:00
|
|
|
if value, ok := getServiceLabels(container, serviceName)[label.SuffixFrontendBackend]; ok {
|
2018-01-02 13:49:11 +00:00
|
|
|
return provider.Normalize(container.ServiceName + "-" + value)
|
2017-12-02 18:26:44 +00:00
|
|
|
}
|
2018-01-09 15:26:03 +00:00
|
|
|
return provider.Normalize(container.ServiceName + "-" + getBackendName(container) + "-" + serviceName)
|
2017-12-02 18:26:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Extract port from labels for a given service and a given docker container
|
|
|
|
func getServicePort(container dockerData, serviceName string) string {
|
|
|
|
if value, ok := getServiceLabels(container, serviceName)[label.SuffixPort]; ok {
|
|
|
|
return value
|
|
|
|
}
|
|
|
|
return getPort(container)
|
|
|
|
}
|
|
|
|
|
2018-01-09 15:26:03 +00:00
|
|
|
func getServiceRedirect(container dockerData, serviceName string) *types.Redirect {
|
2017-12-15 21:16:48 +00:00
|
|
|
serviceLabels := getServiceLabels(container, serviceName)
|
2018-01-09 15:26:03 +00:00
|
|
|
|
2018-01-31 18:10:04 +00:00
|
|
|
permanent := getServiceBoolValue(container, serviceLabels, label.SuffixFrontendRedirectPermanent, false)
|
|
|
|
|
2018-01-09 15:26:03 +00:00
|
|
|
if hasStrictServiceLabel(serviceLabels, label.SuffixFrontendRedirectEntryPoint) {
|
|
|
|
return &types.Redirect{
|
|
|
|
EntryPoint: getStrictServiceStringValue(serviceLabels, label.SuffixFrontendRedirectEntryPoint, label.DefaultFrontendRedirectEntryPoint),
|
2018-01-31 18:10:04 +00:00
|
|
|
Permanent: permanent,
|
2018-01-09 15:26:03 +00:00
|
|
|
}
|
2017-12-15 21:16:48 +00:00
|
|
|
}
|
|
|
|
|
2018-01-09 15:26:03 +00:00
|
|
|
if hasStrictServiceLabel(serviceLabels, label.SuffixFrontendRedirectRegex) &&
|
|
|
|
hasStrictServiceLabel(serviceLabels, label.SuffixFrontendRedirectReplacement) {
|
|
|
|
return &types.Redirect{
|
|
|
|
Regex: getStrictServiceStringValue(serviceLabels, label.SuffixFrontendRedirectRegex, ""),
|
|
|
|
Replacement: getStrictServiceStringValue(serviceLabels, label.SuffixFrontendRedirectReplacement, ""),
|
2018-01-31 18:10:04 +00:00
|
|
|
Permanent: permanent,
|
2018-01-09 15:26:03 +00:00
|
|
|
}
|
|
|
|
}
|
2017-12-15 21:16:48 +00:00
|
|
|
|
2018-01-09 15:26:03 +00:00
|
|
|
return getRedirect(container)
|
2017-12-18 15:26:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func getServiceErrorPages(container dockerData, serviceName string) map[string]*types.ErrorPage {
|
|
|
|
serviceLabels := getServiceLabels(container, serviceName)
|
2018-01-09 15:26:03 +00:00
|
|
|
|
|
|
|
if label.HasPrefix(serviceLabels, label.BaseFrontendErrorPage) {
|
|
|
|
return label.ParseErrorPages(serviceLabels, label.BaseFrontendErrorPage, label.RegexpBaseFrontendErrorPage)
|
|
|
|
}
|
|
|
|
|
|
|
|
return getErrorPages(container)
|
2017-12-18 15:26:04 +00:00
|
|
|
}
|
|
|
|
|
2018-01-09 15:26:03 +00:00
|
|
|
func getServiceRateLimit(container dockerData, serviceName string) *types.RateLimit {
|
2017-12-18 17:06:12 +00:00
|
|
|
serviceLabels := getServiceLabels(container, serviceName)
|
2018-01-09 15:26:03 +00:00
|
|
|
|
|
|
|
if hasStrictServiceLabel(serviceLabels, label.SuffixFrontendRateLimitExtractorFunc) {
|
|
|
|
extractorFunc := getStrictServiceStringValue(serviceLabels, label.SuffixFrontendRateLimitExtractorFunc, label.DefaultBackendMaxconnExtractorFunc)
|
|
|
|
return &types.RateLimit{
|
|
|
|
ExtractorFunc: extractorFunc,
|
|
|
|
RateSet: label.ParseRateSets(serviceLabels, label.BaseFrontendRateLimit, label.RegexpBaseFrontendRateLimit),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return getRateLimit(container)
|
2017-12-18 17:06:12 +00:00
|
|
|
}
|
|
|
|
|
2018-01-09 15:26:03 +00:00
|
|
|
func getServiceHeaders(container dockerData, serviceName string) *types.Headers {
|
|
|
|
serviceLabels := getServiceLabels(container, serviceName)
|
|
|
|
|
|
|
|
headers := &types.Headers{
|
|
|
|
CustomRequestHeaders: getServiceMapValue(container, serviceLabels, serviceName, label.SuffixFrontendRequestHeaders),
|
|
|
|
CustomResponseHeaders: getServiceMapValue(container, serviceLabels, serviceName, label.SuffixFrontendResponseHeaders),
|
|
|
|
SSLProxyHeaders: getServiceMapValue(container, serviceLabels, serviceName, label.SuffixFrontendHeadersSSLProxyHeaders),
|
|
|
|
AllowedHosts: getServiceSliceValue(container, serviceLabels, label.SuffixFrontendHeadersAllowedHosts),
|
|
|
|
HostsProxyHeaders: getServiceSliceValue(container, serviceLabels, label.SuffixFrontendHeadersHostsProxyHeaders),
|
|
|
|
STSSeconds: getServiceInt64Value(container, serviceLabels, label.SuffixFrontendHeadersSTSSeconds, 0),
|
|
|
|
SSLRedirect: getServiceBoolValue(container, serviceLabels, label.SuffixFrontendHeadersSSLRedirect, false),
|
|
|
|
SSLTemporaryRedirect: getServiceBoolValue(container, serviceLabels, label.SuffixFrontendHeadersSSLTemporaryRedirect, false),
|
|
|
|
STSIncludeSubdomains: getServiceBoolValue(container, serviceLabels, label.SuffixFrontendHeadersSTSIncludeSubdomains, false),
|
|
|
|
STSPreload: getServiceBoolValue(container, serviceLabels, label.SuffixFrontendHeadersSTSPreload, false),
|
|
|
|
ForceSTSHeader: getServiceBoolValue(container, serviceLabels, label.SuffixFrontendHeadersForceSTSHeader, false),
|
|
|
|
FrameDeny: getServiceBoolValue(container, serviceLabels, label.SuffixFrontendHeadersFrameDeny, false),
|
|
|
|
ContentTypeNosniff: getServiceBoolValue(container, serviceLabels, label.SuffixFrontendHeadersContentTypeNosniff, false),
|
|
|
|
BrowserXSSFilter: getServiceBoolValue(container, serviceLabels, label.SuffixFrontendHeadersBrowserXSSFilter, false),
|
|
|
|
IsDevelopment: getServiceBoolValue(container, serviceLabels, label.SuffixFrontendHeadersIsDevelopment, false),
|
|
|
|
SSLHost: getServiceStringValue(container, serviceLabels, label.SuffixFrontendHeadersSSLHost, ""),
|
|
|
|
CustomFrameOptionsValue: getServiceStringValue(container, serviceLabels, label.SuffixFrontendHeadersCustomFrameOptionsValue, ""),
|
|
|
|
ContentSecurityPolicy: getServiceStringValue(container, serviceLabels, label.SuffixFrontendHeadersContentSecurityPolicy, ""),
|
|
|
|
PublicKey: getServiceStringValue(container, serviceLabels, label.SuffixFrontendHeadersPublicKey, ""),
|
|
|
|
ReferrerPolicy: getServiceStringValue(container, serviceLabels, label.SuffixFrontendHeadersReferrerPolicy, ""),
|
|
|
|
}
|
2017-12-02 18:26:44 +00:00
|
|
|
|
2018-01-09 15:26:03 +00:00
|
|
|
if !headers.HasSecureHeadersDefined() && !headers.HasCustomHeadersDefined() {
|
|
|
|
return nil
|
2017-12-06 21:26:03 +00:00
|
|
|
}
|
2018-01-09 15:26:03 +00:00
|
|
|
|
|
|
|
return headers
|
2017-12-06 21:26:03 +00:00
|
|
|
}
|
|
|
|
|
2018-01-09 15:26:03 +00:00
|
|
|
// Service label functions
|
|
|
|
|
2017-12-02 18:26:44 +00:00
|
|
|
func getFuncServiceSliceStringLabel(labelSuffix string) func(container dockerData, serviceName string) []string {
|
|
|
|
return func(container dockerData, serviceName string) []string {
|
2018-01-09 15:26:03 +00:00
|
|
|
serviceLabels := getServiceLabels(container, serviceName)
|
|
|
|
return getServiceSliceValue(container, serviceLabels, labelSuffix)
|
2017-12-02 18:26:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func getFuncServiceStringLabel(labelSuffix string, defaultValue string) func(container dockerData, serviceName string) string {
|
|
|
|
return func(container dockerData, serviceName string) string {
|
2018-01-09 15:26:03 +00:00
|
|
|
serviceLabels := getServiceLabels(container, serviceName)
|
|
|
|
return getServiceStringValue(container, serviceLabels, labelSuffix, defaultValue)
|
2017-12-02 18:26:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-16 11:42:32 +00:00
|
|
|
func getFuncServiceBoolLabel(labelSuffix string, defaultValue bool) func(container dockerData, serviceName string) bool {
|
|
|
|
return func(container dockerData, serviceName string) bool {
|
2018-01-09 15:26:03 +00:00
|
|
|
serviceLabels := getServiceLabels(container, serviceName)
|
|
|
|
return getServiceBoolValue(container, serviceLabels, labelSuffix, defaultValue)
|
2017-12-16 11:42:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-16 12:24:07 +00:00
|
|
|
func getFuncServiceIntLabel(labelSuffix string, defaultValue int) func(container dockerData, serviceName string) int {
|
|
|
|
return func(container dockerData, serviceName string) int {
|
|
|
|
return getServiceIntLabel(container, serviceName, labelSuffix, defaultValue)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-09 15:26:03 +00:00
|
|
|
func hasStrictServiceLabel(serviceLabels map[string]string, labelSuffix string) bool {
|
|
|
|
value, ok := serviceLabels[labelSuffix]
|
|
|
|
return ok && len(value) > 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func getServiceStringValue(container dockerData, serviceLabels map[string]string, labelSuffix string, defaultValue string) string {
|
|
|
|
if value, ok := serviceLabels[labelSuffix]; ok {
|
|
|
|
return value
|
2017-12-02 18:26:44 +00:00
|
|
|
}
|
2018-01-09 15:26:03 +00:00
|
|
|
return label.GetStringValue(container.Labels, label.Prefix+labelSuffix, defaultValue)
|
2017-12-02 18:26:44 +00:00
|
|
|
}
|
|
|
|
|
2018-01-09 15:26:03 +00:00
|
|
|
func getStrictServiceStringValue(serviceLabels map[string]string, labelSuffix string, defaultValue string) string {
|
|
|
|
if value, ok := serviceLabels[labelSuffix]; ok {
|
|
|
|
return value
|
2017-12-02 18:26:44 +00:00
|
|
|
}
|
2018-01-09 15:26:03 +00:00
|
|
|
return defaultValue
|
2017-12-02 18:26:44 +00:00
|
|
|
}
|
|
|
|
|
2018-01-09 15:26:03 +00:00
|
|
|
func getServiceMapValue(container dockerData, serviceLabels map[string]string, serviceName string, labelSuffix string) map[string]string {
|
|
|
|
if value, ok := serviceLabels[labelSuffix]; ok {
|
2017-12-06 21:26:03 +00:00
|
|
|
lblName := label.GetServiceLabel(labelSuffix, serviceName)
|
|
|
|
return label.ParseMapValue(lblName, value)
|
|
|
|
}
|
|
|
|
return label.GetMapValue(container.Labels, label.Prefix+labelSuffix)
|
|
|
|
}
|
|
|
|
|
2018-01-09 15:26:03 +00:00
|
|
|
func getServiceSliceValue(container dockerData, serviceLabels map[string]string, labelSuffix string) []string {
|
|
|
|
if value, ok := serviceLabels[labelSuffix]; ok {
|
2017-12-02 18:26:44 +00:00
|
|
|
return label.SplitAndTrimString(value, ",")
|
|
|
|
}
|
|
|
|
return label.GetSliceStringValue(container.Labels, label.Prefix+labelSuffix)
|
|
|
|
}
|
|
|
|
|
2018-01-09 15:26:03 +00:00
|
|
|
func getServiceBoolValue(container dockerData, serviceLabels map[string]string, labelSuffix string, defaultValue bool) bool {
|
|
|
|
if rawValue, ok := serviceLabels[labelSuffix]; ok {
|
2017-12-16 11:42:32 +00:00
|
|
|
value, err := strconv.ParseBool(rawValue)
|
|
|
|
if err == nil {
|
|
|
|
return value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return label.GetBoolValue(container.Labels, label.Prefix+labelSuffix, defaultValue)
|
|
|
|
}
|
|
|
|
|
2017-12-16 12:24:07 +00:00
|
|
|
func getServiceIntLabel(container dockerData, serviceName string, labelSuffix string, defaultValue int) int {
|
|
|
|
if rawValue, ok := getServiceLabels(container, serviceName)[labelSuffix]; ok {
|
|
|
|
value, err := strconv.Atoi(rawValue)
|
|
|
|
if err == nil {
|
|
|
|
return value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return label.GetIntValue(container.Labels, label.Prefix+labelSuffix, defaultValue)
|
|
|
|
}
|
|
|
|
|
2018-01-09 15:26:03 +00:00
|
|
|
func getServiceInt64Value(container dockerData, serviceLabels map[string]string, labelSuffix string, defaultValue int64) int64 {
|
|
|
|
if rawValue, ok := serviceLabels[labelSuffix]; ok {
|
|
|
|
value, err := strconv.ParseInt(rawValue, 10, 64)
|
|
|
|
if err == nil {
|
|
|
|
return value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return label.GetInt64Value(container.Labels, label.Prefix+labelSuffix, defaultValue)
|
|
|
|
}
|
|
|
|
|
2017-12-02 18:26:44 +00:00
|
|
|
func getServiceLabels(container dockerData, serviceName string) label.ServicePropertyValues {
|
|
|
|
return label.ExtractServiceProperties(container.Labels)[serviceName]
|
|
|
|
}
|