2017-04-17 10:50:02 +00:00
package kubernetes
2016-02-08 20:57:32 +00:00
import (
2017-04-23 14:17:20 +00:00
"bufio"
"bytes"
"errors"
2017-07-03 08:06:32 +00:00
"flag"
2017-03-07 12:09:11 +00:00
"fmt"
"os"
2016-06-22 16:31:14 +00:00
"reflect"
2016-05-18 16:30:42 +00:00
"strconv"
2016-04-25 14:56:06 +00:00
"strings"
2016-02-08 20:57:32 +00:00
"text/template"
"time"
2016-09-12 19:06:21 +00:00
2016-12-30 08:21:13 +00:00
"github.com/cenk/backoff"
"github.com/containous/traefik/job"
2016-11-11 22:50:20 +00:00
"github.com/containous/traefik/log"
2017-04-17 10:50:02 +00:00
"github.com/containous/traefik/provider"
2017-12-02 18:28:11 +00:00
"github.com/containous/traefik/provider/label"
2016-11-11 22:50:20 +00:00
"github.com/containous/traefik/safe"
2018-01-07 23:36:03 +00:00
"github.com/containous/traefik/tls"
2016-11-11 22:50:20 +00:00
"github.com/containous/traefik/types"
2017-04-07 10:49:53 +00:00
"k8s.io/client-go/pkg/api/v1"
2017-04-23 14:17:20 +00:00
"k8s.io/client-go/pkg/apis/extensions/v1beta1"
2017-04-07 10:49:53 +00:00
"k8s.io/client-go/pkg/util/intstr"
2016-02-08 20:57:32 +00:00
)
2017-04-17 10:50:02 +00:00
var _ provider . Provider = ( * Provider ) ( nil )
2016-08-16 17:13:18 +00:00
2017-02-06 23:04:30 +00:00
const (
2017-07-10 14:58:12 +00:00
ruleTypePathPrefix = "PathPrefix"
ruleTypeReplacePath = "ReplacePath"
2017-04-30 09:22:07 +00:00
2017-11-28 12:36:03 +00:00
annotationKubernetesIngressClass = "kubernetes.io/ingress.class"
annotationKubernetesAuthRealm = "ingress.kubernetes.io/auth-realm"
annotationKubernetesAuthType = "ingress.kubernetes.io/auth-type"
annotationKubernetesAuthSecret = "ingress.kubernetes.io/auth-secret"
annotationKubernetesRewriteTarget = "ingress.kubernetes.io/rewrite-target"
annotationKubernetesWhitelistSourceRange = "ingress.kubernetes.io/whitelist-source-range"
annotationKubernetesSSLRedirect = "ingress.kubernetes.io/ssl-redirect"
annotationKubernetesHSTSMaxAge = "ingress.kubernetes.io/hsts-max-age"
annotationKubernetesHSTSIncludeSubdomains = "ingress.kubernetes.io/hsts-include-subdomains"
annotationKubernetesCustomRequestHeaders = "ingress.kubernetes.io/custom-request-headers"
annotationKubernetesCustomResponseHeaders = "ingress.kubernetes.io/custom-response-headers"
annotationKubernetesAllowedHosts = "ingress.kubernetes.io/allowed-hosts"
annotationKubernetesProxyHeaders = "ingress.kubernetes.io/proxy-headers"
annotationKubernetesSSLTemporaryRedirect = "ingress.kubernetes.io/ssl-temporary-redirect"
annotationKubernetesSSLHost = "ingress.kubernetes.io/ssl-host"
annotationKubernetesSSLProxyHeaders = "ingress.kubernetes.io/ssl-proxy-headers"
annotationKubernetesHSTSPreload = "ingress.kubernetes.io/hsts-preload"
annotationKubernetesForceHSTSHeader = "ingress.kubernetes.io/force-hsts"
annotationKubernetesFrameDeny = "ingress.kubernetes.io/frame-deny"
annotationKubernetesCustomFrameOptionsValue = "ingress.kubernetes.io/custom-frame-options-value"
annotationKubernetesContentTypeNosniff = "ingress.kubernetes.io/content-type-nosniff"
annotationKubernetesBrowserXSSFilter = "ingress.kubernetes.io/browser-xss-filter"
annotationKubernetesContentSecurityPolicy = "ingress.kubernetes.io/content-security-policy"
annotationKubernetesPublicKey = "ingress.kubernetes.io/public-key"
annotationKubernetesReferrerPolicy = "ingress.kubernetes.io/referrer-policy"
annotationKubernetesIsDevelopment = "ingress.kubernetes.io/is-development"
2017-02-06 23:04:30 +00:00
)
2017-04-23 14:17:20 +00:00
const traefikDefaultRealm = "traefik"
2017-04-17 10:50:02 +00:00
// Provider holds configurations of the provider.
type Provider struct {
2017-10-02 08:32:02 +00:00
provider . BaseProvider ` mapstructure:",squash" export:"true" `
2017-04-17 10:50:02 +00:00
Endpoint string ` description:"Kubernetes server endpoint (required for external cluster client)" `
Token string ` description:"Kubernetes bearer token (not needed for in-cluster client)" `
CertAuthFilePath string ` description:"Kubernetes certificate authority file path (not needed for in-cluster client)" `
2017-10-02 08:32:02 +00:00
DisablePassHostHeaders bool ` description:"Kubernetes disable PassHost Headers" export:"true" `
2017-11-20 01:12:03 +00:00
EnablePassTLSCert bool ` description:"Kubernetes enable Pass TLS Client Certs" export:"true" `
2017-10-02 08:32:02 +00:00
Namespaces Namespaces ` description:"Kubernetes namespaces" export:"true" `
LabelSelector string ` description:"Kubernetes api label selector to use" export:"true" `
2016-06-22 16:31:14 +00:00
lastConfiguration safe . Safe
2016-02-08 20:57:32 +00:00
}
2017-12-04 10:40:03 +00:00
func ( p * Provider ) newK8sClient ( ) ( Client , error ) {
2017-03-07 12:09:11 +00:00
withEndpoint := ""
2017-04-17 10:50:02 +00:00
if p . Endpoint != "" {
withEndpoint = fmt . Sprintf ( " with endpoint %v" , p . Endpoint )
2016-02-08 20:57:32 +00:00
}
2017-03-07 12:09:11 +00:00
if os . Getenv ( "KUBERNETES_SERVICE_HOST" ) != "" && os . Getenv ( "KUBERNETES_SERVICE_PORT" ) != "" {
2018-01-15 15:04:05 +00:00
log . Infof ( "Creating in-cluster Provider client%s" , withEndpoint )
2017-04-17 10:50:02 +00:00
return NewInClusterClient ( p . Endpoint )
2017-03-07 12:09:11 +00:00
}
2018-01-15 15:04:05 +00:00
log . Infof ( "Creating cluster-external Provider client%s" , withEndpoint )
2017-04-17 10:50:02 +00:00
return NewExternalClusterClient ( p . Endpoint , p . Token , p . CertAuthFilePath )
2016-04-19 17:23:08 +00:00
}
2017-04-17 10:50:02 +00:00
// Provide allows the k8s provider to provide configurations to traefik
2016-04-19 17:23:08 +00:00
// using the given configuration channel.
2017-04-17 10:50:02 +00:00
func ( p * Provider ) Provide ( configurationChan chan <- types . ConfigMessage , pool * safe . Pool , constraints types . Constraints ) error {
2017-07-03 08:06:32 +00:00
// Tell glog (used by client-go) to log into STDERR. Otherwise, we risk
// certain kinds of API errors getting logged into a directory not
// available in a `FROM scratch` Docker container, causing glog to abort
// hard with an exit code > 0.
2017-12-02 18:28:11 +00:00
err := flag . Set ( "logtostderr" , "true" )
if err != nil {
return err
}
2017-07-03 08:06:32 +00:00
2017-04-17 10:50:02 +00:00
k8sClient , err := p . newK8sClient ( )
2016-02-08 20:57:32 +00:00
if err != nil {
return err
}
2017-04-17 10:50:02 +00:00
p . Constraints = append ( p . Constraints , constraints ... )
2016-02-08 20:57:32 +00:00
pool . Go ( func ( stop chan bool ) {
operation := func ( ) error {
for {
2016-12-03 20:20:39 +00:00
stopWatch := make ( chan struct { } , 1 )
2016-05-19 18:09:01 +00:00
defer close ( stopWatch )
2017-04-17 10:50:02 +00:00
log . Debugf ( "Using label selector: '%s'" , p . LabelSelector )
2017-10-10 14:26:03 +00:00
eventsChan , err := k8sClient . WatchAll ( p . Namespaces , p . LabelSelector , stopWatch )
2016-04-25 14:56:06 +00:00
if err != nil {
log . Errorf ( "Error watching kubernetes events: %v" , err )
2016-05-19 18:09:01 +00:00
timer := time . NewTimer ( 1 * time . Second )
select {
case <- timer . C :
return err
case <- stop :
return nil
}
2016-04-25 14:56:06 +00:00
}
for {
select {
case <- stop :
return nil
case event := <- eventsChan :
2017-10-10 14:26:03 +00:00
log . Debugf ( "Received Kubernetes event kind %T" , event )
2017-04-17 10:50:02 +00:00
templateObjects , err := p . loadIngresses ( k8sClient )
2016-04-25 14:56:06 +00:00
if err != nil {
return err
}
2017-04-17 10:50:02 +00:00
if reflect . DeepEqual ( p . lastConfiguration . Get ( ) , templateObjects ) {
2017-10-10 14:26:03 +00:00
log . Debugf ( "Skipping Kubernetes event kind %T" , event )
2016-06-22 16:31:14 +00:00
} else {
2017-04-17 10:50:02 +00:00
p . lastConfiguration . Set ( templateObjects )
2016-06-22 16:31:14 +00:00
configurationChan <- types . ConfigMessage {
ProviderName : "kubernetes" ,
2017-04-17 10:50:02 +00:00
Configuration : p . loadConfig ( * templateObjects ) ,
2016-06-22 16:31:14 +00:00
}
2016-04-25 14:56:06 +00:00
}
2016-02-08 20:57:32 +00:00
}
}
}
}
notify := func ( err error , time time . Duration ) {
2017-06-27 23:32:19 +00:00
log . Errorf ( "Provider connection error: %s; retrying in %s" , err , time )
2016-02-08 20:57:32 +00:00
}
2016-12-08 12:32:12 +00:00
err := backoff . RetryNotify ( safe . OperationWithRecover ( operation ) , job . NewBackOff ( backoff . NewExponentialBackOff ( ) ) , notify )
2016-02-08 20:57:32 +00:00
if err != nil {
2017-06-27 23:32:19 +00:00
log . Errorf ( "Cannot connect to Provider: %s" , err )
2016-02-08 20:57:32 +00:00
}
} )
return nil
}
2017-04-17 10:50:02 +00:00
func ( p * Provider ) loadIngresses ( k8sClient Client ) ( * types . Configuration , error ) {
2017-10-10 14:26:03 +00:00
ingresses := k8sClient . GetIngresses ( )
2016-11-11 22:50:20 +00:00
2016-04-19 17:23:08 +00:00
templateObjects := types . Configuration {
2017-05-26 15:03:14 +00:00
Backends : map [ string ] * types . Backend { } ,
Frontends : map [ string ] * types . Frontend { } ,
2016-04-19 17:23:08 +00:00
}
for _ , i := range ingresses {
2017-05-26 15:03:14 +00:00
ingressClass := i . Annotations [ annotationKubernetesIngressClass ]
2017-03-03 19:30:22 +00:00
if ! shouldProcessIngress ( ingressClass ) {
continue
}
2018-01-24 10:57:06 +00:00
tlsSection , err := getTLS ( i , k8sClient )
2018-01-07 23:36:03 +00:00
if err != nil {
log . Errorf ( "Error configuring TLS for ingress %s/%s: %v" , i . Namespace , i . Name , err )
continue
}
2018-01-24 10:57:06 +00:00
templateObjects . TLS = append ( templateObjects . TLS , tlsSection ... )
2018-01-07 23:36:03 +00:00
2016-04-19 17:23:08 +00:00
for _ , r := range i . Spec . Rules {
2016-12-08 12:32:52 +00:00
if r . HTTP == nil {
2017-05-26 15:03:14 +00:00
log . Warn ( "Error in ingress: HTTP is nil" )
2016-12-08 12:32:52 +00:00
continue
}
2017-07-07 19:27:54 +00:00
2016-04-19 17:23:08 +00:00
for _ , pa := range r . HTTP . Paths {
if _ , exists := templateObjects . Backends [ r . Host + pa . Path ] ; ! exists {
templateObjects . Backends [ r . Host + pa . Path ] = & types . Backend {
Servers : make ( map [ string ] types . Server ) ,
2017-01-25 13:11:00 +00:00
LoadBalancer : & types . LoadBalancer {
Method : "wrr" ,
} ,
2016-04-19 17:23:08 +00:00
}
}
2017-02-10 11:05:59 +00:00
2017-05-26 15:03:14 +00:00
if realm := i . Annotations [ annotationKubernetesAuthRealm ] ; realm != "" && realm != traefikDefaultRealm {
2017-10-12 13:48:03 +00:00
log . Errorf ( "Value for annotation %q on ingress %s/%s invalid: no realm customization supported" , annotationKubernetesAuthRealm , i . ObjectMeta . Namespace , i . ObjectMeta . Name )
delete ( templateObjects . Backends , r . Host + pa . Path )
continue
2017-04-23 14:17:20 +00:00
}
2017-02-10 11:05:59 +00:00
2016-04-19 17:23:08 +00:00
if _ , exists := templateObjects . Frontends [ r . Host + pa . Path ] ; ! exists {
2017-04-23 14:17:20 +00:00
basicAuthCreds , err := handleBasicAuthConfig ( i , k8sClient )
if err != nil {
2017-06-27 23:32:35 +00:00
log . Errorf ( "Failed to retrieve basic auth configuration for ingress %s/%s: %s" , i . ObjectMeta . Namespace , i . ObjectMeta . Name , err )
continue
2017-04-23 14:17:20 +00:00
}
2017-07-29 16:35:23 +00:00
2017-12-21 14:54:27 +00:00
passHostHeader := label . GetBoolValue ( i . Annotations , label . TraefikFrontendPassHostHeader , ! p . DisablePassHostHeaders )
passTLSCert := label . GetBoolValue ( i . Annotations , label . TraefikFrontendPassTLSCert , p . EnablePassTLSCert )
2017-12-02 18:28:11 +00:00
priority := label . GetIntValue ( i . Annotations , label . TraefikFrontendPriority , 0 )
2017-11-28 12:36:03 +00:00
2017-12-21 14:40:07 +00:00
entryPoints := label . GetSliceStringValue ( i . Annotations , label . TraefikFrontendEntryPoints )
whitelistSourceRange := label . GetSliceStringValue ( i . Annotations , annotationKubernetesWhitelistSourceRange )
2017-07-29 16:35:23 +00:00
2016-04-19 17:23:08 +00:00
templateObjects . Frontends [ r . Host + pa . Path ] = & types . Frontend {
2017-04-30 09:22:07 +00:00
Backend : r . Host + pa . Path ,
2017-11-20 01:12:03 +00:00
PassHostHeader : passHostHeader ,
PassTLSCert : passTLSCert ,
2017-04-30 09:22:07 +00:00
Routes : make ( map [ string ] types . Route ) ,
2017-07-29 16:35:23 +00:00
Priority : priority ,
2017-04-30 09:22:07 +00:00
BasicAuth : basicAuthCreds ,
WhitelistSourceRange : whitelistSourceRange ,
2017-12-15 10:48:03 +00:00
Redirect : getFrontendRedirect ( i ) ,
2017-11-20 01:12:03 +00:00
EntryPoints : entryPoints ,
2017-12-21 14:40:07 +00:00
Headers : getHeader ( i ) ,
2016-04-19 17:23:08 +00:00
}
}
2017-01-20 13:16:05 +00:00
2017-12-21 14:40:07 +00:00
if len ( r . Host ) > 0 {
2016-05-25 12:16:19 +00:00
if _ , exists := templateObjects . Frontends [ r . Host + pa . Path ] . Routes [ r . Host ] ; ! exists {
templateObjects . Frontends [ r . Host + pa . Path ] . Routes [ r . Host ] = types . Route {
2017-12-21 14:40:07 +00:00
Rule : getRuleForHost ( r . Host ) ,
2016-05-25 12:16:19 +00:00
}
2016-04-19 17:23:08 +00:00
}
}
2017-02-06 23:04:30 +00:00
2017-12-21 14:40:07 +00:00
if rule := getRuleForPath ( pa , i ) ; rule != "" {
2016-05-17 10:50:06 +00:00
templateObjects . Frontends [ r . Host + pa . Path ] . Routes [ pa . Path ] = types . Route {
2017-07-07 19:27:54 +00:00
Rule : rule ,
2016-04-19 17:23:08 +00:00
}
}
2017-02-06 23:04:30 +00:00
2016-11-11 22:50:20 +00:00
service , exists , err := k8sClient . GetService ( i . ObjectMeta . Namespace , pa . Backend . ServiceName )
2017-03-17 15:34:34 +00:00
if err != nil {
2017-03-22 17:56:14 +00:00
log . Errorf ( "Error while retrieving service information from k8s API %s/%s: %v" , i . ObjectMeta . Namespace , pa . Backend . ServiceName , err )
2017-03-17 15:34:34 +00:00
return nil , err
}
if ! exists {
2017-03-22 17:56:14 +00:00
log . Errorf ( "Service not found for %s/%s" , i . ObjectMeta . Namespace , pa . Backend . ServiceName )
2016-04-25 14:56:06 +00:00
delete ( templateObjects . Frontends , r . Host + pa . Path )
2016-05-25 23:53:51 +00:00
continue
2016-04-25 14:56:06 +00:00
}
2016-11-11 22:50:20 +00:00
2017-12-02 18:28:11 +00:00
if expression := service . Annotations [ label . TraefikBackendCircuitBreaker ] ; expression != "" {
2017-02-03 16:47:48 +00:00
templateObjects . Backends [ r . Host + pa . Path ] . CircuitBreaker = & types . CircuitBreaker {
Expression : expression ,
}
}
2017-07-07 19:27:54 +00:00
2017-12-21 14:40:07 +00:00
templateObjects . Backends [ r . Host + pa . Path ] . LoadBalancer = getLoadBalancer ( service )
2018-01-31 14:32:04 +00:00
templateObjects . Backends [ r . Host + pa . Path ] . Buffering = getBuffering ( service )
2017-12-02 18:28:11 +00:00
protocol := label . DefaultProtocol
2016-05-25 23:53:51 +00:00
for _ , port := range service . Spec . Ports {
if equalPorts ( port , pa . Backend . ServicePort ) {
if port . Port == 443 {
protocol = "https"
}
2017-07-07 19:27:54 +00:00
2017-02-10 01:25:38 +00:00
if service . Spec . Type == "ExternalName" {
url := protocol + "://" + service . Spec . ExternalName
name := url
templateObjects . Backends [ r . Host + pa . Path ] . Servers [ name ] = types . Server {
URL : url ,
2016-05-25 23:53:51 +00:00
Weight : 1 ,
2016-05-20 16:34:57 +00:00
}
2016-05-25 23:53:51 +00:00
} else {
2017-04-11 15:10:46 +00:00
endpoints , exists , err := k8sClient . GetEndpoints ( service . ObjectMeta . Namespace , service . ObjectMeta . Name )
if err != nil {
log . Errorf ( "Error retrieving endpoints %s/%s: %v" , service . ObjectMeta . Namespace , service . ObjectMeta . Name , err )
return nil , err
}
2017-02-14 19:53:35 +00:00
2017-04-11 15:10:46 +00:00
if ! exists {
2017-05-15 21:16:35 +00:00
log . Warnf ( "Endpoints not found for %s/%s" , service . ObjectMeta . Namespace , service . ObjectMeta . Name )
break
2017-03-14 14:59:13 +00:00
}
2017-04-11 15:10:46 +00:00
if len ( endpoints . Subsets ) == 0 {
2017-05-15 21:16:35 +00:00
log . Warnf ( "Endpoints not available for %s/%s" , service . ObjectMeta . Namespace , service . ObjectMeta . Name )
break
}
for _ , subset := range endpoints . Subsets {
for _ , address := range subset . Addresses {
url := protocol + "://" + address . IP + ":" + strconv . Itoa ( endpointPortNumber ( port , subset . Ports ) )
name := url
if address . TargetRef != nil && address . TargetRef . Name != "" {
name = address . TargetRef . Name
}
templateObjects . Backends [ r . Host + pa . Path ] . Servers [ name ] = types . Server {
URL : url ,
Weight : 1 ,
2016-05-20 16:34:57 +00:00
}
}
2016-04-20 11:26:51 +00:00
}
2016-04-19 17:23:08 +00:00
}
2016-05-25 23:53:51 +00:00
break
2016-04-19 17:23:08 +00:00
}
}
}
}
}
return & templateObjects , nil
}
2017-12-04 10:40:03 +00:00
func ( p * Provider ) loadConfig ( templateObjects types . Configuration ) * types . Configuration {
2017-11-28 12:36:03 +00:00
var FuncMap = template . FuncMap { }
configuration , err := p . GetConfiguration ( "templates/kubernetes.tmpl" , FuncMap , templateObjects )
if err != nil {
log . Error ( err )
2017-11-20 01:12:03 +00:00
}
2017-11-28 12:36:03 +00:00
return configuration
2017-11-20 01:12:03 +00:00
}
2017-07-07 19:27:54 +00:00
func getRuleForPath ( pa v1beta1 . HTTPIngressPath , i * v1beta1 . Ingress ) string {
if len ( pa . Path ) == 0 {
return ""
}
2017-12-02 18:28:11 +00:00
ruleType := i . Annotations [ label . TraefikFrontendRuleType ]
2017-07-07 19:27:54 +00:00
if ruleType == "" {
ruleType = ruleTypePathPrefix
}
2017-11-27 10:22:03 +00:00
rules := [ ] string { ruleType + ":" + pa . Path }
2017-07-07 19:27:54 +00:00
if rewriteTarget := i . Annotations [ annotationKubernetesRewriteTarget ] ; rewriteTarget != "" {
2017-11-27 10:22:03 +00:00
rules = append ( rules , ruleTypeReplacePath + ":" + rewriteTarget )
2017-07-07 19:27:54 +00:00
}
2017-11-27 10:22:03 +00:00
return strings . Join ( rules , ";" )
2017-07-07 19:27:54 +00:00
}
2017-12-21 14:40:07 +00:00
func getRuleForHost ( host string ) string {
if strings . Contains ( host , "*" ) {
return "HostRegexp:" + strings . Replace ( host , "*" , "{subdomain:[A-Za-z0-9-_]+}" , 1 )
}
return "Host:" + host
}
2017-04-23 14:17:20 +00:00
func handleBasicAuthConfig ( i * v1beta1 . Ingress , k8sClient Client ) ( [ ] string , error ) {
2017-05-26 15:03:14 +00:00
authType , exists := i . Annotations [ annotationKubernetesAuthType ]
2017-04-23 14:17:20 +00:00
if ! exists {
return nil , nil
}
2017-12-02 18:28:11 +00:00
2017-04-23 14:17:20 +00:00
if strings . ToLower ( authType ) != "basic" {
2017-06-27 23:32:19 +00:00
return nil , fmt . Errorf ( "unsupported auth-type on annotation ingress.kubernetes.io/auth-type: %q" , authType )
2017-04-23 14:17:20 +00:00
}
2017-12-02 18:28:11 +00:00
2017-05-26 15:03:14 +00:00
authSecret := i . Annotations [ annotationKubernetesAuthSecret ]
2017-04-23 14:17:20 +00:00
if authSecret == "" {
2017-06-27 23:32:19 +00:00
return nil , errors . New ( "auth-secret annotation ingress.kubernetes.io/auth-secret must be set" )
2017-04-23 14:17:20 +00:00
}
2017-12-02 18:28:11 +00:00
2017-04-23 14:17:20 +00:00
basicAuthCreds , err := loadAuthCredentials ( i . Namespace , authSecret , k8sClient )
if err != nil {
2017-06-27 23:32:19 +00:00
return nil , fmt . Errorf ( "failed to load auth credentials: %s" , err )
2017-04-23 14:17:20 +00:00
}
2017-12-02 18:28:11 +00:00
2017-04-23 14:17:20 +00:00
return basicAuthCreds , nil
}
func loadAuthCredentials ( namespace , secretName string , k8sClient Client ) ( [ ] string , error ) {
secret , ok , err := k8sClient . GetSecret ( namespace , secretName )
switch { // keep order of case conditions
case err != nil :
return nil , fmt . Errorf ( "failed to fetch secret %q/%q: %s" , namespace , secretName , err )
case ! ok :
return nil , fmt . Errorf ( "secret %q/%q not found" , namespace , secretName )
case secret == nil :
2017-06-27 23:32:19 +00:00
return nil , fmt . Errorf ( "data for secret %q/%q must not be nil" , namespace , secretName )
2017-04-23 14:17:20 +00:00
case len ( secret . Data ) != 1 :
2017-06-27 23:32:19 +00:00
return nil , fmt . Errorf ( "found %d elements for secret %q/%q, must be single element exactly" , len ( secret . Data ) , namespace , secretName )
2017-04-23 14:17:20 +00:00
default :
}
var firstSecret [ ] byte
for _ , v := range secret . Data {
firstSecret = v
break
}
creds := make ( [ ] string , 0 )
scanner := bufio . NewScanner ( bytes . NewReader ( firstSecret ) )
for scanner . Scan ( ) {
if cred := scanner . Text ( ) ; cred != "" {
creds = append ( creds , cred )
}
}
2017-06-27 23:32:19 +00:00
if len ( creds ) == 0 {
return nil , fmt . Errorf ( "secret %q/%q does not contain any credentials" , namespace , secretName )
}
2017-04-23 14:17:20 +00:00
return creds , nil
}
2018-01-24 10:57:06 +00:00
func getTLS ( ingress * v1beta1 . Ingress , k8sClient Client ) ( [ ] * tls . Configuration , error ) {
2018-01-07 23:36:03 +00:00
var tlsConfigs [ ] * tls . Configuration
for _ , t := range ingress . Spec . TLS {
tlsSecret , exists , err := k8sClient . GetSecret ( ingress . Namespace , t . SecretName )
if err != nil {
return nil , fmt . Errorf ( "failed to fetch secret %s/%s: %v" , ingress . Namespace , t . SecretName , err )
}
if ! exists {
return nil , fmt . Errorf ( "secret %s/%s does not exist" , ingress . Namespace , t . SecretName )
}
tlsCrtData , tlsCrtExists := tlsSecret . Data [ "tls.crt" ]
tlsKeyData , tlsKeyExists := tlsSecret . Data [ "tls.key" ]
var missingEntries [ ] string
if ! tlsCrtExists {
missingEntries = append ( missingEntries , "tls.crt" )
}
if ! tlsKeyExists {
missingEntries = append ( missingEntries , "tls.key" )
}
if len ( missingEntries ) > 0 {
return nil , fmt . Errorf ( "secret %s/%s is missing the following TLS data entries: %s" , ingress . Namespace , t . SecretName , strings . Join ( missingEntries , ", " ) )
}
entryPoints := label . GetSliceStringValue ( ingress . Annotations , label . TraefikFrontendEntryPoints )
tlsConfig := & tls . Configuration {
EntryPoints : entryPoints ,
Certificate : & tls . Certificate {
CertFile : tls . FileOrContent ( tlsCrtData ) ,
KeyFile : tls . FileOrContent ( tlsKeyData ) ,
} ,
}
tlsConfigs = append ( tlsConfigs , tlsConfig )
}
return tlsConfigs , nil
}
2016-11-11 22:50:20 +00:00
func endpointPortNumber ( servicePort v1 . ServicePort , endpointPorts [ ] v1 . EndpointPort ) int {
2016-05-20 16:34:57 +00:00
if len ( endpointPorts ) > 0 {
//name is optional if there is only one port
port := endpointPorts [ 0 ]
for _ , endpointPort := range endpointPorts {
if servicePort . Name == endpointPort . Name {
port = endpointPort
}
}
return int ( port . Port )
}
2016-11-11 22:50:20 +00:00
return int ( servicePort . Port )
2016-05-20 16:34:57 +00:00
}
2016-11-11 22:50:20 +00:00
func equalPorts ( servicePort v1 . ServicePort , ingressPort intstr . IntOrString ) bool {
if int ( servicePort . Port ) == ingressPort . IntValue ( ) {
2016-05-18 16:30:42 +00:00
return true
}
if servicePort . Name != "" && servicePort . Name == ingressPort . String ( ) {
return true
}
return false
}
2017-03-03 19:30:22 +00:00
func shouldProcessIngress ( ingressClass string ) bool {
2017-12-02 18:28:11 +00:00
return ingressClass == "" || ingressClass == "traefik"
2017-03-03 19:30:22 +00:00
}
2017-12-15 10:48:03 +00:00
func getFrontendRedirect ( i * v1beta1 . Ingress ) * types . Redirect {
2017-12-15 21:16:48 +00:00
frontendRedirectEntryPoint , ok := i . Annotations [ label . TraefikFrontendRedirectEntryPoint ]
2017-12-15 10:48:03 +00:00
frep := ok && len ( frontendRedirectEntryPoint ) > 0
2017-12-15 21:16:48 +00:00
frontendRedirectRegex , ok := i . Annotations [ label . TraefikFrontendRedirectRegex ]
2017-12-15 10:48:03 +00:00
frrg := ok && len ( frontendRedirectRegex ) > 0
2017-12-15 21:16:48 +00:00
frontendRedirectReplacement , ok := i . Annotations [ label . TraefikFrontendRedirectReplacement ]
2017-12-15 10:48:03 +00:00
frrp := ok && len ( frontendRedirectReplacement ) > 0
if frep || frrg && frrp {
return & types . Redirect {
EntryPoint : frontendRedirectEntryPoint ,
Regex : frontendRedirectRegex ,
Replacement : frontendRedirectReplacement ,
}
}
return nil
}
2018-01-31 14:32:04 +00:00
2017-12-21 14:40:07 +00:00
func getLoadBalancer ( service * v1 . Service ) * types . LoadBalancer {
loadBalancer := & types . LoadBalancer {
Method : "wrr" ,
}
if service . Annotations [ label . TraefikBackendLoadBalancerMethod ] == "drr" {
loadBalancer . Method = "drr"
}
if sticky := service . Annotations [ label . TraefikBackendLoadBalancerSticky ] ; len ( sticky ) > 0 {
log . Warnf ( "Deprecated configuration found: %s. Please use %s." , label . TraefikBackendLoadBalancerSticky , label . TraefikBackendLoadBalancerStickiness )
loadBalancer . Sticky = strings . EqualFold ( strings . TrimSpace ( sticky ) , "true" )
}
if stickiness := getStickiness ( service ) ; stickiness != nil {
loadBalancer . Stickiness = stickiness
}
return loadBalancer
}
func getStickiness ( service * v1 . Service ) * types . Stickiness {
if service . Annotations [ label . TraefikBackendLoadBalancerStickiness ] == "true" {
stickiness := & types . Stickiness { }
if cookieName := service . Annotations [ label . TraefikBackendLoadBalancerStickinessCookieName ] ; len ( cookieName ) > 0 {
stickiness . CookieName = cookieName
}
return stickiness
}
return nil
}
func getHeader ( i * v1beta1 . Ingress ) * types . Headers {
return & types . Headers {
CustomRequestHeaders : label . GetMapValue ( i . Annotations , annotationKubernetesCustomRequestHeaders ) ,
CustomResponseHeaders : label . GetMapValue ( i . Annotations , annotationKubernetesCustomResponseHeaders ) ,
AllowedHosts : label . GetSliceStringValue ( i . Annotations , annotationKubernetesAllowedHosts ) ,
HostsProxyHeaders : label . GetSliceStringValue ( i . Annotations , annotationKubernetesProxyHeaders ) ,
SSLRedirect : label . GetBoolValue ( i . Annotations , annotationKubernetesSSLRedirect , false ) ,
SSLTemporaryRedirect : label . GetBoolValue ( i . Annotations , annotationKubernetesSSLTemporaryRedirect , false ) ,
SSLHost : label . GetStringValue ( i . Annotations , annotationKubernetesSSLHost , "" ) ,
SSLProxyHeaders : label . GetMapValue ( i . Annotations , annotationKubernetesSSLProxyHeaders ) ,
STSSeconds : label . GetInt64Value ( i . Annotations , annotationKubernetesHSTSMaxAge , 0 ) ,
STSIncludeSubdomains : label . GetBoolValue ( i . Annotations , annotationKubernetesHSTSIncludeSubdomains , false ) ,
STSPreload : label . GetBoolValue ( i . Annotations , annotationKubernetesHSTSPreload , false ) ,
ForceSTSHeader : label . GetBoolValue ( i . Annotations , annotationKubernetesForceHSTSHeader , false ) ,
FrameDeny : label . GetBoolValue ( i . Annotations , annotationKubernetesFrameDeny , false ) ,
CustomFrameOptionsValue : label . GetStringValue ( i . Annotations , annotationKubernetesCustomFrameOptionsValue , "" ) ,
ContentTypeNosniff : label . GetBoolValue ( i . Annotations , annotationKubernetesContentTypeNosniff , false ) ,
BrowserXSSFilter : label . GetBoolValue ( i . Annotations , annotationKubernetesBrowserXSSFilter , false ) ,
ContentSecurityPolicy : label . GetStringValue ( i . Annotations , annotationKubernetesContentSecurityPolicy , "" ) ,
PublicKey : label . GetStringValue ( i . Annotations , annotationKubernetesPublicKey , "" ) ,
ReferrerPolicy : label . GetStringValue ( i . Annotations , annotationKubernetesReferrerPolicy , "" ) ,
IsDevelopment : label . GetBoolValue ( i . Annotations , annotationKubernetesIsDevelopment , false ) ,
}
}
2018-01-31 14:32:04 +00:00
func getBuffering ( service * v1 . Service ) * types . Buffering {
if label . HasPrefix ( service . Annotations , label . TraefikBackendBuffering ) {
return & types . Buffering {
MaxRequestBodyBytes : label . GetInt64Value ( service . Annotations , label . TraefikBackendBufferingMaxRequestBodyBytes , 0 ) ,
MemRequestBodyBytes : label . GetInt64Value ( service . Annotations , label . TraefikBackendBufferingMemRequestBodyBytes , 0 ) ,
MaxResponseBodyBytes : label . GetInt64Value ( service . Annotations , label . TraefikBackendBufferingMaxResponseBodyBytes , 0 ) ,
MemResponseBodyBytes : label . GetInt64Value ( service . Annotations , label . TraefikBackendBufferingMemResponseBodyBytes , 0 ) ,
RetryExpression : label . GetStringValue ( service . Annotations , label . TraefikBackendBufferingRetryExpression , "" ) ,
}
}
return nil
}