2019-03-14 14:56:06 +00:00
package crd
2019-02-21 22:08:05 +00:00
import (
2019-09-05 11:42:04 +00:00
"bufio"
"bytes"
2019-02-21 22:08:05 +00:00
"context"
2021-09-14 13:16:11 +00:00
"crypto/sha1"
2019-03-14 14:56:06 +00:00
"crypto/sha256"
2021-09-14 13:16:11 +00:00
"encoding/base64"
2021-03-03 14:32:04 +00:00
"encoding/json"
2020-03-10 11:46:05 +00:00
"errors"
2019-02-21 22:08:05 +00:00
"fmt"
2023-03-20 15:46:05 +00:00
"net"
2019-02-21 22:08:05 +00:00
"os"
2024-03-25 13:38:04 +00:00
"slices"
2019-02-21 22:08:05 +00:00
"sort"
2023-03-20 15:46:05 +00:00
"strconv"
2019-02-21 22:08:05 +00:00
"strings"
"time"
2020-02-26 09:36:05 +00:00
"github.com/cenkalti/backoff/v4"
2019-10-25 13:46:05 +00:00
"github.com/mitchellh/hashstructure"
2022-11-21 17:36:05 +00:00
"github.com/rs/zerolog/log"
2020-08-17 16:04:03 +00:00
ptypes "github.com/traefik/paerser/types"
2023-02-03 14:24:05 +00:00
"github.com/traefik/traefik/v3/pkg/config/dynamic"
"github.com/traefik/traefik/v3/pkg/job"
"github.com/traefik/traefik/v3/pkg/logs"
"github.com/traefik/traefik/v3/pkg/provider"
2023-04-17 08:56:36 +00:00
traefikv1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1"
2024-03-25 13:38:04 +00:00
"github.com/traefik/traefik/v3/pkg/provider/kubernetes/gateway"
2023-05-17 09:07:09 +00:00
"github.com/traefik/traefik/v3/pkg/provider/kubernetes/k8s"
2023-02-03 14:24:05 +00:00
"github.com/traefik/traefik/v3/pkg/safe"
"github.com/traefik/traefik/v3/pkg/tls"
"github.com/traefik/traefik/v3/pkg/types"
2019-02-21 22:08:05 +00:00
corev1 "k8s.io/api/core/v1"
2021-03-03 14:32:04 +00:00
apiextensionv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
2019-02-21 22:08:05 +00:00
"k8s.io/apimachinery/pkg/labels"
2021-01-15 14:54:04 +00:00
"k8s.io/apimachinery/pkg/util/intstr"
2019-02-21 22:08:05 +00:00
)
const (
annotationKubernetesIngressClass = "kubernetes.io/ingress.class"
traefikDefaultIngressClass = "traefik"
)
2019-11-14 18:28:04 +00:00
const (
providerName = "kubernetescrd"
providerNamespaceSeparator = "@"
)
2019-02-21 22:08:05 +00:00
// Provider holds configurations of the provider.
type Provider struct {
2024-08-01 13:50:04 +00:00
Endpoint string ` description:"Kubernetes server endpoint (required for external cluster client)." json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty" `
Token types . FileOrContent ` description:"Kubernetes bearer token (not needed for in-cluster client). It accepts either a token value or a file path to the token." json:"token,omitempty" toml:"token,omitempty" yaml:"token,omitempty" loggable:"false" `
CertAuthFilePath string ` description:"Kubernetes certificate authority file path (not needed for in-cluster client)." json:"certAuthFilePath,omitempty" toml:"certAuthFilePath,omitempty" yaml:"certAuthFilePath,omitempty" `
Namespaces [ ] string ` description:"Kubernetes namespaces." json:"namespaces,omitempty" toml:"namespaces,omitempty" yaml:"namespaces,omitempty" export:"true" `
AllowCrossNamespace bool ` description:"Allow cross namespace resource reference." json:"allowCrossNamespace,omitempty" toml:"allowCrossNamespace,omitempty" yaml:"allowCrossNamespace,omitempty" export:"true" `
AllowExternalNameServices bool ` description:"Allow ExternalName services." json:"allowExternalNameServices,omitempty" toml:"allowExternalNameServices,omitempty" yaml:"allowExternalNameServices,omitempty" export:"true" `
LabelSelector string ` description:"Kubernetes label selector to use." json:"labelSelector,omitempty" toml:"labelSelector,omitempty" yaml:"labelSelector,omitempty" export:"true" `
IngressClass string ` description:"Value of kubernetes.io/ingress.class annotation to watch for." json:"ingressClass,omitempty" toml:"ingressClass,omitempty" yaml:"ingressClass,omitempty" export:"true" `
ThrottleDuration ptypes . Duration ` description:"Ingress refresh throttle duration" json:"throttleDuration,omitempty" toml:"throttleDuration,omitempty" yaml:"throttleDuration,omitempty" export:"true" `
AllowEmptyServices bool ` description:"Allow the creation of services without endpoints." json:"allowEmptyServices,omitempty" toml:"allowEmptyServices,omitempty" yaml:"allowEmptyServices,omitempty" export:"true" `
NativeLBByDefault bool ` description:"Defines whether to use Native Kubernetes load-balancing mode by default." json:"nativeLBByDefault,omitempty" toml:"nativeLBByDefault,omitempty" yaml:"nativeLBByDefault,omitempty" export:"true" `
DisableClusterScopeResources bool ` description:"Disables the lookup of cluster scope resources (incompatible with IngressClasses and NodePortLB enabled services)." json:"disableClusterScopeResources,omitempty" toml:"disableClusterScopeResources,omitempty" yaml:"disableClusterScopeResources,omitempty" export:"true" `
2023-05-15 14:38:05 +00:00
lastConfiguration safe . Safe
routerTransform k8s . RouterTransform
}
func ( p * Provider ) SetRouterTransform ( routerTransform k8s . RouterTransform ) {
p . routerTransform = routerTransform
}
func ( p * Provider ) applyRouterTransform ( ctx context . Context , rt * dynamic . Router , ingress * traefikv1alpha1 . IngressRoute ) {
if p . routerTransform == nil {
return
}
2024-03-15 08:24:03 +00:00
err := p . routerTransform . Apply ( ctx , rt , ingress )
2023-05-15 14:38:05 +00:00
if err != nil {
2023-05-17 09:07:09 +00:00
log . Ctx ( ctx ) . Error ( ) . Err ( err ) . Msg ( "Apply router transform" )
2023-05-15 14:38:05 +00:00
}
2020-12-11 09:58:00 +00:00
}
2020-11-19 23:18:04 +00:00
func ( p * Provider ) newK8sClient ( ctx context . Context ) ( * clientWrapper , error ) {
_ , err := labels . Parse ( p . LabelSelector )
2019-02-21 22:08:05 +00:00
if err != nil {
2020-11-19 23:18:04 +00:00
return nil , fmt . Errorf ( "invalid label selector: %q" , p . LabelSelector )
2019-02-21 22:08:05 +00:00
}
2022-11-21 17:36:05 +00:00
log . Ctx ( ctx ) . Info ( ) . Msgf ( "label selector is: %q" , p . LabelSelector )
2019-02-21 22:08:05 +00:00
withEndpoint := ""
if p . Endpoint != "" {
2019-11-14 18:28:04 +00:00
withEndpoint = fmt . Sprintf ( " with endpoint %s" , p . Endpoint )
2019-02-21 22:08:05 +00:00
}
2019-03-14 14:56:06 +00:00
var client * clientWrapper
2019-03-11 13:54:05 +00:00
switch {
case os . Getenv ( "KUBERNETES_SERVICE_HOST" ) != "" && os . Getenv ( "KUBERNETES_SERVICE_PORT" ) != "" :
2022-11-21 17:36:05 +00:00
log . Ctx ( ctx ) . Info ( ) . Msgf ( "Creating in-cluster Provider client%s" , withEndpoint )
2019-03-14 14:56:06 +00:00
client , err = newInClusterClient ( p . Endpoint )
2019-03-11 13:54:05 +00:00
case os . Getenv ( "KUBECONFIG" ) != "" :
2022-11-21 17:36:05 +00:00
log . Ctx ( ctx ) . Info ( ) . Msgf ( "Creating cluster-external Provider client from KUBECONFIG %s" , os . Getenv ( "KUBECONFIG" ) )
2019-03-14 14:56:06 +00:00
client , err = newExternalClusterClientFromFile ( os . Getenv ( "KUBECONFIG" ) )
2019-03-11 13:54:05 +00:00
default :
2022-11-21 17:36:05 +00:00
log . Ctx ( ctx ) . Info ( ) . Msgf ( "Creating cluster-external Provider client%s" , withEndpoint )
2024-01-11 16:06:06 +00:00
client , err = newExternalClusterClient ( p . Endpoint , p . CertAuthFilePath , p . Token )
2019-02-21 22:08:05 +00:00
}
2020-11-19 23:18:04 +00:00
if err != nil {
return nil , err
2019-02-21 22:08:05 +00:00
}
2020-11-19 23:18:04 +00:00
client . labelSelector = p . LabelSelector
2024-08-01 13:50:04 +00:00
client . disableClusterScopeInformer = p . DisableClusterScopeResources
2020-11-19 23:18:04 +00:00
return client , nil
2019-02-21 22:08:05 +00:00
}
// Init the provider.
func ( p * Provider ) Init ( ) error {
2019-03-27 14:02:06 +00:00
return nil
2019-02-21 22:08:05 +00:00
}
// Provide allows the k8s provider to provide configurations to traefik
// using the given configuration channel.
2019-07-10 07:26:04 +00:00
func ( p * Provider ) Provide ( configurationChan chan <- dynamic . Message , pool * safe . Pool ) error {
2022-11-21 17:36:05 +00:00
logger := log . With ( ) . Str ( logs . ProviderName , providerName ) . Logger ( )
ctxLog := logger . WithContext ( context . Background ( ) )
2019-02-21 22:08:05 +00:00
2020-11-19 23:18:04 +00:00
k8sClient , err := p . newK8sClient ( ctxLog )
2019-02-21 22:08:05 +00:00
if err != nil {
return err
}
2021-07-13 08:48:05 +00:00
if p . AllowCrossNamespace {
2022-11-21 17:36:05 +00:00
logger . Warn ( ) . Msg ( "Cross-namespace reference between IngressRoutes and resources is enabled, please ensure that this is expected (see AllowCrossNamespace option)" )
2020-12-10 13:58:04 +00:00
}
2021-07-13 10:54:09 +00:00
if p . AllowExternalNameServices {
2024-05-13 07:06:03 +00:00
logger . Info ( ) . Msg ( "ExternalName service loading is enabled, please ensure that this is expected (see AllowExternalNameServices option)" )
2021-07-13 10:54:09 +00:00
}
2020-02-03 16:56:04 +00:00
pool . GoCtx ( func ( ctxPool context . Context ) {
2019-02-21 22:08:05 +00:00
operation := func ( ) error {
2020-02-03 16:56:04 +00:00
eventsChan , err := k8sClient . WatchAll ( p . Namespaces , ctxPool . Done ( ) )
2019-02-21 22:08:05 +00:00
if err != nil {
2022-11-21 17:36:05 +00:00
logger . Error ( ) . Err ( err ) . Msg ( "Error watching kubernetes events" )
2019-02-21 22:08:05 +00:00
timer := time . NewTimer ( 1 * time . Second )
select {
case <- timer . C :
return err
2020-02-03 16:56:04 +00:00
case <- ctxPool . Done ( ) :
2019-02-21 22:08:05 +00:00
return nil
}
}
2019-08-30 10:16:04 +00:00
throttleDuration := time . Duration ( p . ThrottleDuration )
2020-02-03 16:56:04 +00:00
throttledChan := throttleEvents ( ctxLog , throttleDuration , pool , eventsChan )
2019-08-31 12:10:04 +00:00
if throttledChan != nil {
eventsChan = throttledChan
}
2019-08-30 10:16:04 +00:00
2019-02-21 22:08:05 +00:00
for {
select {
2020-02-03 16:56:04 +00:00
case <- ctxPool . Done ( ) :
2019-02-21 22:08:05 +00:00
return nil
2019-08-31 12:10:04 +00:00
case event := <- eventsChan :
2019-11-14 18:28:04 +00:00
// Note that event is the *first* event that came in during this throttling interval -- if we're hitting our throttle, we may have dropped events.
// This is fine, because we don't treat different event types differently.
// But if we do in the future, we'll need to track more information about the dropped events.
2019-06-21 15:18:05 +00:00
conf := p . loadConfigurationFromCRD ( ctxLog , k8sClient )
2019-02-21 22:08:05 +00:00
2019-10-25 13:46:05 +00:00
confHash , err := hashstructure . Hash ( conf , nil )
switch {
case err != nil :
2022-11-21 17:36:05 +00:00
logger . Error ( ) . Err ( err ) . Msg ( "Unable to hash the configuration" )
2019-10-25 13:46:05 +00:00
case p . lastConfiguration . Get ( ) == confHash :
2022-11-21 17:36:05 +00:00
logger . Debug ( ) . Msgf ( "Skipping Kubernetes event kind %T" , event )
2019-10-25 13:46:05 +00:00
default :
p . lastConfiguration . Set ( confHash )
2019-07-10 07:26:04 +00:00
configurationChan <- dynamic . Message {
2019-11-14 18:28:04 +00:00
ProviderName : providerName ,
2019-02-21 22:08:05 +00:00
Configuration : conf ,
}
}
2019-08-30 10:16:04 +00:00
2019-11-14 18:28:04 +00:00
// If we're throttling,
// we sleep here for the throttle duration to enforce that we don't refresh faster than our throttle.
// time.Sleep returns immediately if p.ThrottleDuration is 0 (no throttle).
2019-08-30 10:16:04 +00:00
time . Sleep ( throttleDuration )
2019-02-21 22:08:05 +00:00
}
}
}
notify := func ( err error , time time . Duration ) {
2022-11-30 08:50:05 +00:00
logger . Error ( ) . Err ( err ) . Msgf ( "Provider error, retrying in %s" , time )
2019-02-21 22:08:05 +00:00
}
2020-02-03 16:56:04 +00:00
err := backoff . RetryNotify ( safe . OperationWithRecover ( operation ) , backoff . WithContext ( job . NewBackOff ( backoff . NewExponentialBackOff ( ) ) , ctxPool ) , notify )
2019-02-21 22:08:05 +00:00
if err != nil {
2022-11-30 08:50:05 +00:00
logger . Error ( ) . Err ( err ) . Msg ( "Cannot retrieve data" )
2019-02-21 22:08:05 +00:00
}
} )
return nil
}
2019-08-26 08:30:05 +00:00
func ( p * Provider ) loadConfigurationFromCRD ( ctx context . Context , client Client ) * dynamic . Configuration {
2022-05-19 14:42:09 +00:00
stores , tlsConfigs := buildTLSStores ( ctx , client )
if tlsConfigs == nil {
tlsConfigs = make ( map [ string ] * tls . CertAndStores )
}
2019-08-26 08:30:05 +00:00
conf := & dynamic . Configuration {
2022-05-19 14:42:09 +00:00
// TODO: choose between mutating and returning tlsConfigs
2019-08-26 08:30:05 +00:00
HTTP : p . loadIngressRouteConfiguration ( ctx , client , tlsConfigs ) ,
TCP : p . loadIngressRouteTCPConfiguration ( ctx , client , tlsConfigs ) ,
2020-02-26 11:28:05 +00:00
UDP : p . loadIngressRouteUDPConfiguration ( ctx , client ) ,
2019-08-26 08:30:05 +00:00
TLS : & dynamic . TLSConfiguration {
2022-05-19 14:42:09 +00:00
Options : buildTLSOptions ( ctx , client ) ,
Stores : stores ,
2019-08-26 08:30:05 +00:00
} ,
2019-02-21 22:08:05 +00:00
}
2022-05-19 14:42:09 +00:00
// Done after because tlsConfigs is mutated by the others above.
conf . TLS . Certificates = getTLSConfig ( tlsConfigs )
2019-08-26 08:30:05 +00:00
for _ , middleware := range client . GetMiddlewares ( ) {
2019-11-14 18:28:04 +00:00
id := provider . Normalize ( makeID ( middleware . Namespace , middleware . Name ) )
2022-11-21 17:36:05 +00:00
logger := log . Ctx ( ctx ) . With ( ) . Str ( logs . MiddlewareName , id ) . Logger ( )
ctxMid := logger . WithContext ( ctx )
2019-09-05 11:42:04 +00:00
basicAuth , err := createBasicAuthMiddleware ( client , middleware . Namespace , middleware . Spec . BasicAuth )
if err != nil {
2022-11-21 17:36:05 +00:00
logger . Error ( ) . Err ( err ) . Msg ( "Error while reading basic auth middleware" )
2019-09-05 11:42:04 +00:00
continue
}
digestAuth , err := createDigestAuthMiddleware ( client , middleware . Namespace , middleware . Spec . DigestAuth )
if err != nil {
2022-11-21 17:36:05 +00:00
logger . Error ( ) . Err ( err ) . Msg ( "Error while reading digest auth middleware" )
2019-09-05 11:42:04 +00:00
continue
}
forwardAuth , err := createForwardAuthMiddleware ( client , middleware . Namespace , middleware . Spec . ForwardAuth )
if err != nil {
2022-11-21 17:36:05 +00:00
logger . Error ( ) . Err ( err ) . Msg ( "Error while reading forward auth middleware" )
2019-09-05 11:42:04 +00:00
continue
}
2020-12-10 13:58:04 +00:00
errorPage , errorPageService , err := p . createErrorPageMiddleware ( client , middleware . Namespace , middleware . Spec . Errors )
2019-09-10 15:24:03 +00:00
if err != nil {
2022-11-21 17:36:05 +00:00
logger . Error ( ) . Err ( err ) . Msg ( "Error while reading error page middleware" )
2019-09-10 15:24:03 +00:00
continue
}
if errorPage != nil && errorPageService != nil {
serviceName := id + "-errorpage-service"
errorPage . Service = serviceName
conf . HTTP . Services [ serviceName ] = errorPageService
}
2022-06-20 13:44:08 +00:00
plugin , err := createPluginMiddleware ( client , middleware . Namespace , middleware . Spec . Plugin )
2021-03-03 14:32:04 +00:00
if err != nil {
2022-11-21 17:36:05 +00:00
logger . Error ( ) . Err ( err ) . Msg ( "Error while reading plugins middleware" )
2021-03-03 14:32:04 +00:00
continue
}
rateLimit , err := createRateLimitMiddleware ( middleware . Spec . RateLimit )
if err != nil {
2022-11-21 17:36:05 +00:00
logger . Error ( ) . Err ( err ) . Msg ( "Error while reading rateLimit middleware" )
2021-03-03 14:32:04 +00:00
continue
}
retry , err := createRetryMiddleware ( middleware . Spec . Retry )
if err != nil {
2022-11-21 17:36:05 +00:00
logger . Error ( ) . Err ( err ) . Msg ( "Error while reading retry middleware" )
2021-03-03 14:32:04 +00:00
continue
}
2022-04-05 10:30:08 +00:00
circuitBreaker , err := createCircuitBreakerMiddleware ( middleware . Spec . CircuitBreaker )
if err != nil {
2022-11-21 17:36:05 +00:00
logger . Error ( ) . Err ( err ) . Msg ( "Error while reading circuit breaker middleware" )
2022-04-05 10:30:08 +00:00
continue
}
2019-09-03 17:20:04 +00:00
conf . HTTP . Middlewares [ id ] = & dynamic . Middleware {
AddPrefix : middleware . Spec . AddPrefix ,
StripPrefix : middleware . Spec . StripPrefix ,
StripPrefixRegex : middleware . Spec . StripPrefixRegex ,
ReplacePath : middleware . Spec . ReplacePath ,
ReplacePathRegex : middleware . Spec . ReplacePathRegex ,
Chain : createChainMiddleware ( ctxMid , middleware . Namespace , middleware . Spec . Chain ) ,
2024-01-11 09:40:06 +00:00
IPWhiteList : middleware . Spec . IPWhiteList ,
2022-10-26 15:16:05 +00:00
IPAllowList : middleware . Spec . IPAllowList ,
2019-09-03 17:20:04 +00:00
Headers : middleware . Spec . Headers ,
2019-09-10 15:24:03 +00:00
Errors : errorPage ,
2021-03-03 14:32:04 +00:00
RateLimit : rateLimit ,
2019-09-03 17:20:04 +00:00
RedirectRegex : middleware . Spec . RedirectRegex ,
RedirectScheme : middleware . Spec . RedirectScheme ,
2019-09-05 11:42:04 +00:00
BasicAuth : basicAuth ,
DigestAuth : digestAuth ,
ForwardAuth : forwardAuth ,
2019-09-03 17:20:04 +00:00
InFlightReq : middleware . Spec . InFlightReq ,
Buffering : middleware . Spec . Buffering ,
2022-04-05 10:30:08 +00:00
CircuitBreaker : circuitBreaker ,
2024-08-07 14:20:04 +00:00
Compress : createCompressMiddleware ( middleware . Spec . Compress ) ,
2019-09-03 17:20:04 +00:00
PassTLSClientCert : middleware . Spec . PassTLSClientCert ,
2021-03-03 14:32:04 +00:00
Retry : retry ,
2020-07-13 10:30:03 +00:00
ContentType : middleware . Spec . ContentType ,
2022-10-27 15:34:06 +00:00
GrpcWeb : middleware . Spec . GrpcWeb ,
2021-03-03 14:32:04 +00:00
Plugin : plugin ,
2019-09-03 17:20:04 +00:00
}
2019-02-21 22:08:05 +00:00
}
2021-06-11 13:30:05 +00:00
for _ , middlewareTCP := range client . GetMiddlewareTCPs ( ) {
id := provider . Normalize ( makeID ( middlewareTCP . Namespace , middlewareTCP . Name ) )
conf . TCP . Middlewares [ id ] = & dynamic . TCPMiddleware {
2021-11-29 16:12:06 +00:00
InFlightConn : middlewareTCP . Spec . InFlightConn ,
2024-01-11 09:40:06 +00:00
IPWhiteList : middlewareTCP . Spec . IPWhiteList ,
2022-10-26 15:16:05 +00:00
IPAllowList : middlewareTCP . Spec . IPAllowList ,
2021-06-11 13:30:05 +00:00
}
}
2022-11-22 09:18:04 +00:00
cb := configBuilder {
client : client ,
allowCrossNamespace : p . AllowCrossNamespace ,
allowExternalNameServices : p . AllowExternalNameServices ,
allowEmptyServices : p . AllowEmptyServices ,
}
2020-09-11 13:40:03 +00:00
2019-11-14 18:28:04 +00:00
for _ , service := range client . GetTraefikServices ( ) {
err := cb . buildTraefikService ( ctx , service , conf . HTTP . Services )
if err != nil {
2022-11-21 17:36:05 +00:00
log . Ctx ( ctx ) . Error ( ) . Str ( logs . ServiceName , service . Name ) . Err ( err ) .
Msg ( "Error while building TraefikService" )
2019-11-14 18:28:04 +00:00
continue
}
}
2020-09-11 13:40:03 +00:00
for _ , serversTransport := range client . GetServersTransports ( ) {
2022-11-21 17:36:05 +00:00
logger := log . Ctx ( ctx ) . With ( ) . Str ( logs . ServersTransportName , serversTransport . Name ) . Logger ( )
2020-09-11 13:40:03 +00:00
2024-01-11 16:06:06 +00:00
var rootCAs [ ] types . FileOrContent
2020-09-11 13:40:03 +00:00
for _ , secret := range serversTransport . Spec . RootCAsSecrets {
caSecret , err := loadCASecret ( serversTransport . Namespace , secret , client )
if err != nil {
2022-11-21 17:36:05 +00:00
logger . Error ( ) . Err ( err ) . Msgf ( "Error while loading rootCAs %s" , secret )
2020-09-11 13:40:03 +00:00
continue
}
2024-01-11 16:06:06 +00:00
rootCAs = append ( rootCAs , types . FileOrContent ( caSecret ) )
2020-09-11 13:40:03 +00:00
}
var certs tls . Certificates
for _ , secret := range serversTransport . Spec . CertificatesSecrets {
tlsSecret , tlsKey , err := loadAuthTLSSecret ( serversTransport . Namespace , secret , client )
if err != nil {
2022-11-21 17:36:05 +00:00
logger . Error ( ) . Err ( err ) . Msgf ( "Error while loading certificates %s" , secret )
2020-09-11 13:40:03 +00:00
continue
}
certs = append ( certs , tls . Certificate {
2024-01-11 16:06:06 +00:00
CertFile : types . FileOrContent ( tlsSecret ) ,
KeyFile : types . FileOrContent ( tlsKey ) ,
2021-08-23 08:13:31 +00:00
OCSP : tls . OCSPConfig {
DisableStapling : false ,
} ,
2020-09-11 13:40:03 +00:00
} )
}
forwardingTimeout := & dynamic . ForwardingTimeouts { }
forwardingTimeout . SetDefaults ( )
if serversTransport . Spec . ForwardingTimeouts != nil {
if serversTransport . Spec . ForwardingTimeouts . DialTimeout != nil {
err := forwardingTimeout . DialTimeout . Set ( serversTransport . Spec . ForwardingTimeouts . DialTimeout . String ( ) )
if err != nil {
2022-11-21 17:36:05 +00:00
logger . Error ( ) . Err ( err ) . Msg ( "Error while reading DialTimeout" )
2020-09-11 13:40:03 +00:00
}
}
if serversTransport . Spec . ForwardingTimeouts . ResponseHeaderTimeout != nil {
err := forwardingTimeout . ResponseHeaderTimeout . Set ( serversTransport . Spec . ForwardingTimeouts . ResponseHeaderTimeout . String ( ) )
if err != nil {
2022-11-21 17:36:05 +00:00
logger . Error ( ) . Err ( err ) . Msg ( "Error while reading ResponseHeaderTimeout" )
2020-09-11 13:40:03 +00:00
}
}
if serversTransport . Spec . ForwardingTimeouts . IdleConnTimeout != nil {
err := forwardingTimeout . IdleConnTimeout . Set ( serversTransport . Spec . ForwardingTimeouts . IdleConnTimeout . String ( ) )
if err != nil {
2022-11-21 17:36:05 +00:00
logger . Error ( ) . Err ( err ) . Msg ( "Error while reading IdleConnTimeout" )
2020-09-11 13:40:03 +00:00
}
}
2021-11-09 11:16:08 +00:00
if serversTransport . Spec . ForwardingTimeouts . ReadIdleTimeout != nil {
err := forwardingTimeout . ReadIdleTimeout . Set ( serversTransport . Spec . ForwardingTimeouts . ReadIdleTimeout . String ( ) )
if err != nil {
2022-11-21 17:36:05 +00:00
logger . Error ( ) . Err ( err ) . Msg ( "Error while reading ReadIdleTimeout" )
2021-11-09 11:16:08 +00:00
}
}
if serversTransport . Spec . ForwardingTimeouts . PingTimeout != nil {
err := forwardingTimeout . PingTimeout . Set ( serversTransport . Spec . ForwardingTimeouts . PingTimeout . String ( ) )
if err != nil {
2022-11-21 17:36:05 +00:00
logger . Error ( ) . Err ( err ) . Msg ( "Error while reading PingTimeout" )
2021-11-09 11:16:08 +00:00
}
}
2020-09-11 13:40:03 +00:00
}
2021-09-16 13:12:13 +00:00
id := provider . Normalize ( makeID ( serversTransport . Namespace , serversTransport . Name ) )
conf . HTTP . ServersTransports [ id ] = & dynamic . ServersTransport {
2020-09-11 13:40:03 +00:00
ServerName : serversTransport . Spec . ServerName ,
InsecureSkipVerify : serversTransport . Spec . InsecureSkipVerify ,
RootCAs : rootCAs ,
Certificates : certs ,
2021-09-16 10:18:08 +00:00
DisableHTTP2 : serversTransport . Spec . DisableHTTP2 ,
2020-09-11 13:40:03 +00:00
MaxIdleConnsPerHost : serversTransport . Spec . MaxIdleConnsPerHost ,
ForwardingTimeouts : forwardingTimeout ,
2021-09-17 06:56:07 +00:00
PeerCertURI : serversTransport . Spec . PeerCertURI ,
2022-10-14 15:16:08 +00:00
Spiffe : serversTransport . Spec . Spiffe ,
2020-09-11 13:40:03 +00:00
}
}
2022-12-09 08:58:05 +00:00
for _ , serversTransportTCP := range client . GetServersTransportTCPs ( ) {
logger := log . Ctx ( ctx ) . With ( ) . Str ( logs . ServersTransportName , serversTransportTCP . Name ) . Logger ( )
var tcpServerTransport dynamic . TCPServersTransport
tcpServerTransport . SetDefaults ( )
if serversTransportTCP . Spec . DialTimeout != nil {
err := tcpServerTransport . DialTimeout . Set ( serversTransportTCP . Spec . DialTimeout . String ( ) )
if err != nil {
logger . Error ( ) . Err ( err ) . Msg ( "Error while reading DialTimeout" )
}
}
if serversTransportTCP . Spec . DialKeepAlive != nil {
err := tcpServerTransport . DialKeepAlive . Set ( serversTransportTCP . Spec . DialKeepAlive . String ( ) )
if err != nil {
logger . Error ( ) . Err ( err ) . Msg ( "Error while reading DialKeepAlive" )
}
}
if serversTransportTCP . Spec . TerminationDelay != nil {
err := tcpServerTransport . TerminationDelay . Set ( serversTransportTCP . Spec . TerminationDelay . String ( ) )
if err != nil {
logger . Error ( ) . Err ( err ) . Msg ( "Error while reading TerminationDelay" )
}
}
if serversTransportTCP . Spec . TLS != nil {
2024-01-11 16:06:06 +00:00
var rootCAs [ ] types . FileOrContent
2022-12-09 08:58:05 +00:00
for _ , secret := range serversTransportTCP . Spec . TLS . RootCAsSecrets {
caSecret , err := loadCASecret ( serversTransportTCP . Namespace , secret , client )
if err != nil {
logger . Error ( ) .
Err ( err ) .
Str ( "rootCAs" , secret ) .
Msg ( "Error while loading rootCAs" )
continue
}
2024-01-11 16:06:06 +00:00
rootCAs = append ( rootCAs , types . FileOrContent ( caSecret ) )
2022-12-09 08:58:05 +00:00
}
var certs tls . Certificates
for _ , secret := range serversTransportTCP . Spec . TLS . CertificatesSecrets {
tlsCert , tlsKey , err := loadAuthTLSSecret ( serversTransportTCP . Namespace , secret , client )
if err != nil {
logger . Error ( ) .
Err ( err ) .
Str ( "certificates" , secret ) .
Msg ( "Error while loading certificates" )
continue
}
certs = append ( certs , tls . Certificate {
2024-01-11 16:06:06 +00:00
CertFile : types . FileOrContent ( tlsCert ) ,
KeyFile : types . FileOrContent ( tlsKey ) ,
2022-12-09 08:58:05 +00:00
} )
}
tcpServerTransport . TLS = & dynamic . TLSClientConfig {
ServerName : serversTransportTCP . Spec . TLS . ServerName ,
InsecureSkipVerify : serversTransportTCP . Spec . TLS . InsecureSkipVerify ,
RootCAs : rootCAs ,
Certificates : certs ,
PeerCertURI : serversTransportTCP . Spec . TLS . PeerCertURI ,
}
tcpServerTransport . TLS . Spiffe = serversTransportTCP . Spec . TLS . Spiffe
}
id := provider . Normalize ( makeID ( serversTransportTCP . Namespace , serversTransportTCP . Name ) )
conf . TCP . ServersTransports [ id ] = & tcpServerTransport
}
2019-08-26 08:30:05 +00:00
return conf
2019-02-21 22:08:05 +00:00
}
2023-03-20 15:46:05 +00:00
// getServicePort always returns a valid port, an error otherwise.
2021-01-15 14:54:04 +00:00
func getServicePort ( svc * corev1 . Service , port intstr . IntOrString ) ( * corev1 . ServicePort , error ) {
2020-03-10 11:46:05 +00:00
if svc == nil {
return nil , errors . New ( "service is not defined" )
}
2021-01-15 14:54:04 +00:00
if ( port . Type == intstr . Int && port . IntVal == 0 ) || ( port . Type == intstr . String && port . StrVal == "" ) {
2020-03-10 11:46:05 +00:00
return nil , errors . New ( "ingressRoute service port not defined" )
}
hasValidPort := false
for _ , p := range svc . Spec . Ports {
2021-01-15 14:54:04 +00:00
if ( port . Type == intstr . Int && port . IntVal == p . Port ) || ( port . Type == intstr . String && port . StrVal == p . Name ) {
2020-03-10 11:46:05 +00:00
return & p , nil
}
if p . Port != 0 {
hasValidPort = true
}
}
2021-01-15 14:54:04 +00:00
if svc . Spec . Type != corev1 . ServiceTypeExternalName || port . Type == intstr . String {
return nil , fmt . Errorf ( "service port not found: %s" , & port )
2020-03-10 11:46:05 +00:00
}
if hasValidPort {
2022-11-21 17:36:05 +00:00
log . Warn ( ) . Msgf ( "The port %s from IngressRoute doesn't match with ports defined in the ExternalName service %s/%s." ,
& port , svc . Namespace , svc . Name )
2020-03-10 11:46:05 +00:00
}
2021-01-15 14:54:04 +00:00
return & corev1 . ServicePort { Port : port . IntVal } , nil
2020-03-10 11:46:05 +00:00
}
2023-03-20 15:46:05 +00:00
func getNativeServiceAddress ( service corev1 . Service , svcPort corev1 . ServicePort ) ( string , error ) {
if service . Spec . ClusterIP == "None" {
return "" , fmt . Errorf ( "no clusterIP on headless service: %s/%s" , service . Namespace , service . Name )
}
if service . Spec . ClusterIP == "" {
return "" , fmt . Errorf ( "no clusterIP found for service: %s/%s" , service . Namespace , service . Name )
}
return net . JoinHostPort ( service . Spec . ClusterIP , strconv . Itoa ( int ( svcPort . Port ) ) ) , nil
}
2022-06-20 13:44:08 +00:00
func createPluginMiddleware ( k8sClient Client , ns string , plugins map [ string ] apiextensionv1 . JSON ) ( map [ string ] dynamic . PluginConf , error ) {
2021-03-03 14:32:04 +00:00
if plugins == nil {
return nil , nil
}
data , err := json . Marshal ( plugins )
if err != nil {
return nil , err
}
2022-06-20 13:44:08 +00:00
pcMap := map [ string ] dynamic . PluginConf { }
if err = json . Unmarshal ( data , & pcMap ) ; err != nil {
2021-03-03 14:32:04 +00:00
return nil , err
}
2022-06-20 13:44:08 +00:00
for _ , pc := range pcMap {
for key := range pc {
if pc [ key ] , err = loadSecretKeys ( k8sClient , ns , pc [ key ] ) ; err != nil {
return nil , err
}
}
}
return pcMap , nil
}
func loadSecretKeys ( k8sClient Client , ns string , i interface { } ) ( interface { } , error ) {
var err error
switch iv := i . ( type ) {
case string :
if ! strings . HasPrefix ( iv , "urn:k8s:secret:" ) {
return iv , nil
}
return getSecretValue ( k8sClient , ns , iv )
case [ ] interface { } :
for i := range iv {
if iv [ i ] , err = loadSecretKeys ( k8sClient , ns , iv [ i ] ) ; err != nil {
return nil , err
}
}
case map [ string ] interface { } :
for k := range iv {
if iv [ k ] , err = loadSecretKeys ( k8sClient , ns , iv [ k ] ) ; err != nil {
return nil , err
}
}
}
return i , nil
}
func getSecretValue ( c Client , ns , urn string ) ( string , error ) {
parts := strings . Split ( urn , ":" )
if len ( parts ) != 5 {
return "" , fmt . Errorf ( "malformed secret URN %q" , urn )
}
secretName := parts [ 3 ]
secret , ok , err := c . GetSecret ( ns , secretName )
if err != nil {
return "" , err
}
if ! ok {
return "" , fmt . Errorf ( "secret %s/%s is not found" , ns , secretName )
}
secretKey := parts [ 4 ]
secretValue , ok := secret . Data [ secretKey ]
if ! ok {
return "" , fmt . Errorf ( "key %q not found in secret %s/%s" , secretKey , ns , secretName )
}
return string ( secretValue ) , nil
2021-03-03 14:32:04 +00:00
}
2023-04-17 08:56:36 +00:00
func createCircuitBreakerMiddleware ( circuitBreaker * traefikv1alpha1 . CircuitBreaker ) ( * dynamic . CircuitBreaker , error ) {
2022-04-05 10:30:08 +00:00
if circuitBreaker == nil {
return nil , nil
}
cb := & dynamic . CircuitBreaker { Expression : circuitBreaker . Expression }
cb . SetDefaults ( )
if circuitBreaker . CheckPeriod != nil {
if err := cb . CheckPeriod . Set ( circuitBreaker . CheckPeriod . String ( ) ) ; err != nil {
return nil , err
}
}
if circuitBreaker . FallbackDuration != nil {
if err := cb . FallbackDuration . Set ( circuitBreaker . FallbackDuration . String ( ) ) ; err != nil {
return nil , err
}
}
if circuitBreaker . RecoveryDuration != nil {
if err := cb . RecoveryDuration . Set ( circuitBreaker . RecoveryDuration . String ( ) ) ; err != nil {
return nil , err
}
}
2024-04-19 09:26:05 +00:00
if circuitBreaker . ResponseCode != 0 {
cb . ResponseCode = circuitBreaker . ResponseCode
}
2022-04-05 10:30:08 +00:00
return cb , nil
}
2024-08-07 14:20:04 +00:00
func createCompressMiddleware ( compress * traefikv1alpha1 . Compress ) * dynamic . Compress {
if compress == nil {
return nil
}
c := & dynamic . Compress { }
c . SetDefaults ( )
if compress . ExcludedContentTypes != nil {
c . ExcludedContentTypes = compress . ExcludedContentTypes
}
if compress . IncludedContentTypes != nil {
c . IncludedContentTypes = compress . IncludedContentTypes
}
if compress . MinResponseBodyBytes != nil {
c . MinResponseBodyBytes = * compress . MinResponseBodyBytes
}
if compress . Encodings != nil {
c . Encodings = compress . Encodings
}
if compress . DefaultEncoding != nil {
c . DefaultEncoding = * compress . DefaultEncoding
}
return c
}
2023-04-17 08:56:36 +00:00
func createRateLimitMiddleware ( rateLimit * traefikv1alpha1 . RateLimit ) ( * dynamic . RateLimit , error ) {
2021-03-03 14:32:04 +00:00
if rateLimit == nil {
return nil , nil
}
2024-08-07 14:20:04 +00:00
rl := & dynamic . RateLimit { }
2021-03-03 14:32:04 +00:00
rl . SetDefaults ( )
2024-08-07 14:20:04 +00:00
if rateLimit . Average != nil {
rl . Average = * rateLimit . Average
}
2021-03-03 14:32:04 +00:00
if rateLimit . Burst != nil {
rl . Burst = * rateLimit . Burst
}
if rateLimit . Period != nil {
err := rl . Period . Set ( rateLimit . Period . String ( ) )
if err != nil {
return nil , err
}
}
2021-11-26 11:10:11 +00:00
if rateLimit . SourceCriterion != nil {
rl . SourceCriterion = rateLimit . SourceCriterion
}
2021-03-03 14:32:04 +00:00
return rl , nil
}
2023-04-17 08:56:36 +00:00
func createRetryMiddleware ( retry * traefikv1alpha1 . Retry ) ( * dynamic . Retry , error ) {
2021-03-03 14:32:04 +00:00
if retry == nil {
return nil , nil
}
r := & dynamic . Retry { Attempts : retry . Attempts }
err := r . InitialInterval . Set ( retry . InitialInterval . String ( ) )
if err != nil {
return nil , err
}
return r , nil
}
2023-04-17 08:56:36 +00:00
func ( p * Provider ) createErrorPageMiddleware ( client Client , namespace string , errorPage * traefikv1alpha1 . ErrorPage ) ( * dynamic . ErrorPage , * dynamic . Service , error ) {
2019-09-10 15:24:03 +00:00
if errorPage == nil {
return nil , nil , nil
}
errorPageMiddleware := & dynamic . ErrorPage {
Status : errorPage . Status ,
Query : errorPage . Query ,
}
2022-11-22 09:18:04 +00:00
cb := configBuilder {
client : client ,
allowCrossNamespace : p . AllowCrossNamespace ,
allowExternalNameServices : p . AllowExternalNameServices ,
allowEmptyServices : p . AllowEmptyServices ,
}
balancerServerHTTP , err := cb . buildServersLB ( namespace , errorPage . Service . LoadBalancerSpec )
2019-09-10 15:24:03 +00:00
if err != nil {
return nil , nil , err
}
return errorPageMiddleware , balancerServerHTTP , nil
}
2024-03-25 13:38:04 +00:00
func ( p * Provider ) FillExtensionBuilderRegistry ( registry gateway . ExtensionBuilderRegistry ) {
registry . RegisterFilterFuncs ( traefikv1alpha1 . GroupName , "Middleware" , func ( name , namespace string ) ( string , * dynamic . Middleware , error ) {
if len ( p . Namespaces ) > 0 && ! slices . Contains ( p . Namespaces , namespace ) {
return "" , nil , fmt . Errorf ( "namespace %q is not allowed" , namespace )
}
return makeID ( namespace , name ) + providerNamespaceSeparator + providerName , nil , nil
} )
registry . RegisterBackendFuncs ( traefikv1alpha1 . GroupName , "TraefikService" , func ( name , namespace string ) ( string , * dynamic . Service , error ) {
if len ( p . Namespaces ) > 0 && ! slices . Contains ( p . Namespaces , namespace ) {
return "" , nil , fmt . Errorf ( "namespace %q is not allowed" , namespace )
}
return makeID ( namespace , name ) + providerNamespaceSeparator + providerName , nil , nil
} )
}
2023-04-17 08:56:36 +00:00
func createForwardAuthMiddleware ( k8sClient Client , namespace string , auth * traefikv1alpha1 . ForwardAuth ) ( * dynamic . ForwardAuth , error ) {
2019-09-05 11:42:04 +00:00
if auth == nil {
return nil , nil
}
if len ( auth . Address ) == 0 {
2024-02-07 16:14:07 +00:00
return nil , errors . New ( "forward authentication requires an address" )
2019-09-05 11:42:04 +00:00
}
forwardAuth := & dynamic . ForwardAuth {
2020-10-29 14:10:04 +00:00
Address : auth . Address ,
TrustForwardHeader : auth . TrustForwardHeader ,
AuthResponseHeaders : auth . AuthResponseHeaders ,
AuthResponseHeadersRegex : auth . AuthResponseHeadersRegex ,
AuthRequestHeaders : auth . AuthRequestHeaders ,
2024-01-15 15:14:05 +00:00
AddAuthCookiesToResponse : auth . AddAuthCookiesToResponse ,
2019-09-05 11:42:04 +00:00
}
if auth . TLS == nil {
return forwardAuth , nil
}
2024-01-29 16:32:05 +00:00
forwardAuth . TLS = & dynamic . ClientTLS {
2019-09-05 11:42:04 +00:00
InsecureSkipVerify : auth . TLS . InsecureSkipVerify ,
}
if len ( auth . TLS . CASecret ) > 0 {
caSecret , err := loadCASecret ( namespace , auth . TLS . CASecret , k8sClient )
if err != nil {
2020-05-11 10:06:07 +00:00
return nil , fmt . Errorf ( "failed to load auth ca secret: %w" , err )
2019-09-05 11:42:04 +00:00
}
forwardAuth . TLS . CA = caSecret
}
if len ( auth . TLS . CertSecret ) > 0 {
authSecretCert , authSecretKey , err := loadAuthTLSSecret ( namespace , auth . TLS . CertSecret , k8sClient )
if err != nil {
2020-05-11 10:06:07 +00:00
return nil , fmt . Errorf ( "failed to load auth secret: %w" , err )
2019-09-05 11:42:04 +00:00
}
forwardAuth . TLS . Cert = authSecretCert
forwardAuth . TLS . Key = authSecretKey
}
2024-01-29 16:32:05 +00:00
forwardAuth . TLS . CAOptional = auth . TLS . CAOptional
2019-09-05 11:42:04 +00:00
return forwardAuth , nil
}
func loadCASecret ( namespace , secretName string , k8sClient Client ) ( string , error ) {
secret , ok , err := k8sClient . GetSecret ( namespace , secretName )
if err != nil {
2020-05-11 10:06:07 +00:00
return "" , fmt . Errorf ( "failed to fetch secret '%s/%s': %w" , namespace , secretName , err )
2019-09-05 11:42:04 +00:00
}
2021-06-14 16:06:10 +00:00
2019-09-05 11:42:04 +00:00
if ! ok {
return "" , fmt . Errorf ( "secret '%s/%s' not found" , namespace , secretName )
}
2021-06-14 16:06:10 +00:00
2019-09-05 11:42:04 +00:00
if secret == nil {
return "" , fmt . Errorf ( "data for secret '%s/%s' must not be nil" , namespace , secretName )
}
2021-06-14 16:06:10 +00:00
tlsCAData , err := getCABlocks ( secret , namespace , secretName )
if err == nil {
return tlsCAData , nil
2019-09-05 11:42:04 +00:00
}
2021-06-14 16:06:10 +00:00
// TODO: remove this behavior in the next major version (v3)
if len ( secret . Data ) == 1 {
// For backwards compatibility, use the only available secret data as CA if both 'ca.crt' and 'tls.ca' are missing.
for _ , v := range secret . Data {
return string ( v ) , nil
}
2019-09-05 11:42:04 +00:00
}
2021-06-14 16:06:10 +00:00
return "" , fmt . Errorf ( "could not find CA block: %w" , err )
2019-09-05 11:42:04 +00:00
}
func loadAuthTLSSecret ( namespace , secretName string , k8sClient Client ) ( string , string , error ) {
secret , exists , err := k8sClient . GetSecret ( namespace , secretName )
if err != nil {
2020-05-11 10:06:07 +00:00
return "" , "" , fmt . Errorf ( "failed to fetch secret '%s/%s': %w" , namespace , secretName , err )
2019-09-05 11:42:04 +00:00
}
2021-06-14 16:06:10 +00:00
2019-09-05 11:42:04 +00:00
if ! exists {
return "" , "" , fmt . Errorf ( "secret '%s/%s' does not exist" , namespace , secretName )
}
2021-06-14 16:06:10 +00:00
2019-09-05 11:42:04 +00:00
if secret == nil {
return "" , "" , fmt . Errorf ( "data for secret '%s/%s' must not be nil" , namespace , secretName )
}
return getCertificateBlocks ( secret , namespace , secretName )
}
2023-04-17 08:56:36 +00:00
func createBasicAuthMiddleware ( client Client , namespace string , basicAuth * traefikv1alpha1 . BasicAuth ) ( * dynamic . BasicAuth , error ) {
2019-09-05 11:42:04 +00:00
if basicAuth == nil {
return nil , nil
}
2021-09-14 13:16:11 +00:00
if basicAuth . Secret == "" {
2024-02-07 16:14:07 +00:00
return nil , errors . New ( "auth secret must be set" )
2021-09-14 13:16:11 +00:00
}
secret , ok , err := client . GetSecret ( namespace , basicAuth . Secret )
2019-09-05 11:42:04 +00:00
if err != nil {
2021-09-14 13:16:11 +00:00
return nil , fmt . Errorf ( "failed to fetch secret '%s/%s': %w" , namespace , basicAuth . Secret , err )
}
if ! ok {
return nil , fmt . Errorf ( "secret '%s/%s' not found" , namespace , basicAuth . Secret )
}
if secret == nil {
return nil , fmt . Errorf ( "data for secret '%s/%s' must not be nil" , namespace , basicAuth . Secret )
}
if secret . Type == corev1 . SecretTypeBasicAuth {
credentials , err := loadBasicAuthCredentials ( secret )
if err != nil {
return nil , fmt . Errorf ( "failed to load basic auth credentials: %w" , err )
}
return & dynamic . BasicAuth {
Users : credentials ,
Realm : basicAuth . Realm ,
RemoveHeader : basicAuth . RemoveHeader ,
HeaderField : basicAuth . HeaderField ,
} , nil
}
credentials , err := loadAuthCredentials ( secret )
if err != nil {
return nil , fmt . Errorf ( "failed to load basic auth credentials: %w" , err )
2019-09-05 11:42:04 +00:00
}
return & dynamic . BasicAuth {
Users : credentials ,
Realm : basicAuth . Realm ,
RemoveHeader : basicAuth . RemoveHeader ,
HeaderField : basicAuth . HeaderField ,
} , nil
}
2023-04-17 08:56:36 +00:00
func createDigestAuthMiddleware ( client Client , namespace string , digestAuth * traefikv1alpha1 . DigestAuth ) ( * dynamic . DigestAuth , error ) {
2019-09-05 11:42:04 +00:00
if digestAuth == nil {
return nil , nil
}
2021-09-14 13:16:11 +00:00
if digestAuth . Secret == "" {
2024-02-07 16:14:07 +00:00
return nil , errors . New ( "auth secret must be set" )
2021-09-14 13:16:11 +00:00
}
secret , ok , err := client . GetSecret ( namespace , digestAuth . Secret )
2019-09-05 11:42:04 +00:00
if err != nil {
2021-09-14 13:16:11 +00:00
return nil , fmt . Errorf ( "failed to fetch secret '%s/%s': %w" , namespace , digestAuth . Secret , err )
}
if ! ok {
return nil , fmt . Errorf ( "secret '%s/%s' not found" , namespace , digestAuth . Secret )
}
if secret == nil {
return nil , fmt . Errorf ( "data for secret '%s/%s' must not be nil" , namespace , digestAuth . Secret )
}
credentials , err := loadAuthCredentials ( secret )
if err != nil {
return nil , fmt . Errorf ( "failed to load digest auth credentials: %w" , err )
2019-09-05 11:42:04 +00:00
}
return & dynamic . DigestAuth {
Users : credentials ,
Realm : digestAuth . Realm ,
RemoveHeader : digestAuth . RemoveHeader ,
HeaderField : digestAuth . HeaderField ,
} , nil
}
2021-09-14 13:16:11 +00:00
func loadBasicAuthCredentials ( secret * corev1 . Secret ) ( [ ] string , error ) {
username , usernameExists := secret . Data [ "username" ]
password , passwordExists := secret . Data [ "password" ]
if ! ( usernameExists && passwordExists ) {
return nil , fmt . Errorf ( "secret '%s/%s' must contain both username and password keys" , secret . Namespace , secret . Name )
2019-09-05 11:42:04 +00:00
}
2021-09-14 13:16:11 +00:00
hash := sha1 . New ( )
hash . Write ( password )
passwordSum := base64 . StdEncoding . EncodeToString ( hash . Sum ( nil ) )
2019-09-05 11:42:04 +00:00
2021-09-14 13:16:11 +00:00
return [ ] string { fmt . Sprintf ( "%s:{SHA}%s" , username , passwordSum ) } , nil
2019-09-05 11:42:04 +00:00
}
2021-09-14 13:16:11 +00:00
func loadAuthCredentials ( secret * corev1 . Secret ) ( [ ] string , error ) {
2019-09-05 11:42:04 +00:00
if len ( secret . Data ) != 1 {
2021-09-14 13:16:11 +00:00
return nil , fmt . Errorf ( "found %d elements for secret '%s/%s', must be single element exactly" , len ( secret . Data ) , secret . Namespace , secret . Name )
2019-09-05 11:42:04 +00:00
}
var firstSecret [ ] byte
for _ , v := range secret . Data {
firstSecret = v
break
}
var credentials [ ] string
scanner := bufio . NewScanner ( bytes . NewReader ( firstSecret ) )
for scanner . Scan ( ) {
if cred := scanner . Text ( ) ; len ( cred ) > 0 {
credentials = append ( credentials , cred )
}
}
if err := scanner . Err ( ) ; err != nil {
2021-09-14 13:16:11 +00:00
return nil , fmt . Errorf ( "error reading secret for %s/%s: %w" , secret . Namespace , secret . Name , err )
2019-09-05 11:42:04 +00:00
}
if len ( credentials ) == 0 {
2021-09-14 13:16:11 +00:00
return nil , fmt . Errorf ( "secret '%s/%s' does not contain any credentials" , secret . Namespace , secret . Name )
2019-09-05 11:42:04 +00:00
}
return credentials , nil
}
2023-04-17 08:56:36 +00:00
func createChainMiddleware ( ctx context . Context , namespace string , chain * traefikv1alpha1 . Chain ) * dynamic . Chain {
2019-09-03 17:20:04 +00:00
if chain == nil {
return nil
}
var mds [ ] string
for _ , mi := range chain . Middlewares {
2019-11-14 18:28:04 +00:00
if strings . Contains ( mi . Name , providerNamespaceSeparator ) {
2019-09-03 17:20:04 +00:00
if len ( mi . Namespace ) > 0 {
2022-11-21 17:36:05 +00:00
log . Ctx ( ctx ) . Warn ( ) . Msgf ( "namespace %q is ignored in cross-provider context" , mi . Namespace )
2019-09-03 17:20:04 +00:00
}
mds = append ( mds , mi . Name )
continue
}
ns := mi . Namespace
if len ( ns ) == 0 {
ns = namespace
}
mds = append ( mds , makeID ( ns , mi . Name ) )
}
return & dynamic . Chain { Middlewares : mds }
}
2019-06-27 21:58:03 +00:00
func buildTLSOptions ( ctx context . Context , client Client ) map [ string ] tls . Options {
2024-07-23 14:30:05 +00:00
tlsOptionsCRDs := client . GetTLSOptions ( )
2019-06-27 21:58:03 +00:00
var tlsOptions map [ string ] tls . Options
2019-06-21 15:18:05 +00:00
2024-07-23 14:30:05 +00:00
if len ( tlsOptionsCRDs ) == 0 {
2019-06-21 15:18:05 +00:00
return tlsOptions
}
2019-06-27 21:58:03 +00:00
tlsOptions = make ( map [ string ] tls . Options )
2020-02-24 16:14:06 +00:00
var nsDefault [ ] string
2019-06-21 15:18:05 +00:00
2024-07-23 14:30:05 +00:00
for _ , tlsOptionsCRD := range tlsOptionsCRDs {
2024-07-30 13:14:29 +00:00
logger := log . Ctx ( ctx ) . With ( ) . Str ( "tlsOption" , tlsOptionsCRD . Name ) . Str ( "namespace" , tlsOptionsCRD . Namespace ) . Logger ( )
2024-01-11 16:06:06 +00:00
var clientCAs [ ] types . FileOrContent
2019-06-21 15:18:05 +00:00
2024-07-23 14:30:05 +00:00
for _ , secretName := range tlsOptionsCRD . Spec . ClientAuth . SecretNames {
secret , exists , err := client . GetSecret ( tlsOptionsCRD . Namespace , secretName )
2019-06-21 15:18:05 +00:00
if err != nil {
2024-07-30 13:14:29 +00:00
logger . Error ( ) . Err ( err ) . Msgf ( "Failed to fetch secret %s/%s" , tlsOptionsCRD . Namespace , secretName )
2019-06-21 15:18:05 +00:00
continue
}
if ! exists {
2024-07-30 13:14:29 +00:00
logger . Warn ( ) . Msgf ( "Secret %s/%s does not exist" , tlsOptionsCRD . Namespace , secretName )
2019-06-21 15:18:05 +00:00
continue
}
2024-07-23 14:30:05 +00:00
cert , err := getCABlocks ( secret , tlsOptionsCRD . Namespace , secretName )
2019-06-21 15:18:05 +00:00
if err != nil {
2024-07-30 13:14:29 +00:00
logger . Error ( ) . Err ( err ) . Msgf ( "Failed to extract CA from secret %s/%s" , tlsOptionsCRD . Namespace , secretName )
2019-06-21 15:18:05 +00:00
continue
}
2024-01-11 16:06:06 +00:00
clientCAs = append ( clientCAs , types . FileOrContent ( cert ) )
2019-06-21 15:18:05 +00:00
}
2024-07-23 14:30:05 +00:00
id := makeID ( tlsOptionsCRD . Namespace , tlsOptionsCRD . Name )
2020-02-24 16:14:06 +00:00
// If the name is default, we override the default config.
2024-07-23 14:30:05 +00:00
if tlsOptionsCRD . Name == tls . DefaultTLSConfigName {
id = tlsOptionsCRD . Name
nsDefault = append ( nsDefault , tlsOptionsCRD . Namespace )
2020-02-24 16:14:06 +00:00
}
2021-08-20 16:20:06 +00:00
2024-07-23 14:30:05 +00:00
tlsOption := tls . Options { }
tlsOption . SetDefaults ( )
tlsOption . MinVersion = tlsOptionsCRD . Spec . MinVersion
tlsOption . MaxVersion = tlsOptionsCRD . Spec . MaxVersion
if tlsOptionsCRD . Spec . CipherSuites != nil {
tlsOption . CipherSuites = tlsOptionsCRD . Spec . CipherSuites
2021-08-20 16:20:06 +00:00
}
2024-07-23 14:30:05 +00:00
tlsOption . CurvePreferences = tlsOptionsCRD . Spec . CurvePreferences
tlsOption . ClientAuth = tls . ClientAuth {
CAFiles : clientCAs ,
ClientAuthType : tlsOptionsCRD . Spec . ClientAuth . ClientAuthType ,
2019-06-21 15:18:05 +00:00
}
2024-07-23 14:30:05 +00:00
tlsOption . SniStrict = tlsOptionsCRD . Spec . SniStrict
if tlsOptionsCRD . Spec . ALPNProtocols != nil {
tlsOption . ALPNProtocols = tlsOptionsCRD . Spec . ALPNProtocols
}
tlsOptions [ id ] = tlsOption
2019-06-21 15:18:05 +00:00
}
2020-02-24 16:14:06 +00:00
if len ( nsDefault ) > 1 {
2021-06-14 08:06:05 +00:00
delete ( tlsOptions , tls . DefaultTLSConfigName )
2022-11-21 17:36:05 +00:00
log . Ctx ( ctx ) . Error ( ) . Msgf ( "Default TLS Options defined in multiple namespaces: %v" , nsDefault )
2020-02-24 16:14:06 +00:00
}
2019-06-21 15:18:05 +00:00
return tlsOptions
}
2022-05-19 14:42:09 +00:00
func buildTLSStores ( ctx context . Context , client Client ) ( map [ string ] tls . Store , map [ string ] * tls . CertAndStores ) {
2020-02-24 16:14:06 +00:00
tlsStoreCRD := client . GetTLSStores ( )
if len ( tlsStoreCRD ) == 0 {
2022-05-19 14:42:09 +00:00
return nil , nil
2020-02-24 16:14:06 +00:00
}
2022-05-19 14:42:09 +00:00
2020-02-24 16:14:06 +00:00
var nsDefault [ ] string
2022-05-19 14:42:09 +00:00
tlsStores := make ( map [ string ] tls . Store )
tlsConfigs := make ( map [ string ] * tls . CertAndStores )
2020-02-24 16:14:06 +00:00
2022-05-19 14:42:09 +00:00
for _ , t := range tlsStoreCRD {
2022-11-21 17:36:05 +00:00
logger := log . Ctx ( ctx ) . With ( ) . Str ( "TLSStore" , t . Name ) . Str ( "namespace" , t . Namespace ) . Logger ( )
2020-02-24 16:14:06 +00:00
2022-05-19 14:42:09 +00:00
id := makeID ( t . Namespace , t . Name )
2020-02-24 16:14:06 +00:00
// If the name is default, we override the default config.
2022-05-19 14:42:09 +00:00
if t . Name == tls . DefaultTLSStoreName {
id = t . Name
nsDefault = append ( nsDefault , t . Namespace )
2020-02-24 16:14:06 +00:00
}
2022-05-19 14:42:09 +00:00
var tlsStore tls . Store
if t . Spec . DefaultCertificate != nil {
secretName := t . Spec . DefaultCertificate . SecretName
secret , exists , err := client . GetSecret ( t . Namespace , secretName )
if err != nil {
2022-11-21 17:36:05 +00:00
logger . Error ( ) . Err ( err ) . Msgf ( "Failed to fetch secret %s/%s" , t . Namespace , secretName )
2022-05-19 14:42:09 +00:00
continue
}
if ! exists {
2022-11-21 17:36:05 +00:00
logger . Error ( ) . Msgf ( "Secret %s/%s does not exist" , t . Namespace , secretName )
2022-05-19 14:42:09 +00:00
continue
}
cert , key , err := getCertificateBlocks ( secret , t . Namespace , secretName )
if err != nil {
2022-11-21 17:36:05 +00:00
logger . Error ( ) . Err ( err ) . Msg ( "Could not get certificate blocks" )
2022-05-19 14:42:09 +00:00
continue
}
tlsStore . DefaultCertificate = & tls . Certificate {
2024-01-11 16:06:06 +00:00
CertFile : types . FileOrContent ( cert ) ,
KeyFile : types . FileOrContent ( key ) ,
2022-05-19 14:42:09 +00:00
}
2020-02-24 16:14:06 +00:00
}
2022-05-19 14:42:09 +00:00
2022-09-13 18:34:08 +00:00
if t . Spec . DefaultGeneratedCert != nil {
tlsStore . DefaultGeneratedCert = & tls . GeneratedCert {
Resolver : t . Spec . DefaultGeneratedCert . Resolver ,
Domain : t . Spec . DefaultGeneratedCert . Domain ,
}
}
2022-05-19 14:42:09 +00:00
if err := buildCertificates ( client , id , t . Namespace , t . Spec . Certificates , tlsConfigs ) ; err != nil {
2022-11-21 17:36:05 +00:00
logger . Error ( ) . Err ( err ) . Msg ( "Failed to load certificates" )
2022-05-19 14:42:09 +00:00
continue
}
tlsStores [ id ] = tlsStore
2020-02-24 16:14:06 +00:00
}
if len ( nsDefault ) > 1 {
2021-06-14 08:06:05 +00:00
delete ( tlsStores , tls . DefaultTLSStoreName )
2022-11-21 17:36:05 +00:00
log . Ctx ( ctx ) . Error ( ) . Msgf ( "Default TLS Stores defined in multiple namespaces: %v" , nsDefault )
2020-02-24 16:14:06 +00:00
}
2022-05-19 14:42:09 +00:00
return tlsStores , tlsConfigs
}
// buildCertificates loads TLSStore certificates from secrets and sets them into tlsConfigs.
2023-04-17 08:56:36 +00:00
func buildCertificates ( client Client , tlsStore , namespace string , certificates [ ] traefikv1alpha1 . Certificate , tlsConfigs map [ string ] * tls . CertAndStores ) error {
2022-05-19 14:42:09 +00:00
for _ , c := range certificates {
configKey := namespace + "/" + c . SecretName
if _ , tlsExists := tlsConfigs [ configKey ] ; ! tlsExists {
certAndStores , err := getTLS ( client , c . SecretName , namespace )
if err != nil {
return fmt . Errorf ( "unable to read secret %s: %w" , configKey , err )
}
certAndStores . Stores = [ ] string { tlsStore }
tlsConfigs [ configKey ] = certAndStores
}
}
return nil
2020-02-24 16:14:06 +00:00
}
2019-06-11 13:12:04 +00:00
func makeServiceKey ( rule , ingressName string ) ( string , error ) {
h := sha256 . New ( )
if _ , err := h . Write ( [ ] byte ( rule ) ) ; err != nil {
return "" , err
}
key := fmt . Sprintf ( "%s-%.10x" , ingressName , h . Sum ( nil ) )
return key , nil
}
2019-03-14 14:56:06 +00:00
func makeID ( namespace , name string ) string {
if namespace == "" {
return name
}
2019-06-11 13:12:04 +00:00
2019-09-13 18:44:04 +00:00
return namespace + "-" + name
2019-03-14 14:56:06 +00:00
}
2020-07-07 12:42:03 +00:00
func shouldProcessIngress ( ingressClass , ingressClassAnnotation string ) bool {
2019-02-21 22:08:05 +00:00
return ingressClass == ingressClassAnnotation ||
( len ( ingressClass ) == 0 && ingressClassAnnotation == traefikDefaultIngressClass )
}
2019-06-27 21:58:03 +00:00
func getTLS ( k8sClient Client , secretName , namespace string ) ( * tls . CertAndStores , error ) {
2019-06-11 13:12:04 +00:00
secret , exists , err := k8sClient . GetSecret ( namespace , secretName )
if err != nil {
2020-05-11 10:06:07 +00:00
return nil , fmt . Errorf ( "failed to fetch secret %s/%s: %w" , namespace , secretName , err )
2019-06-11 13:12:04 +00:00
}
if ! exists {
return nil , fmt . Errorf ( "secret %s/%s does not exist" , namespace , secretName )
}
cert , key , err := getCertificateBlocks ( secret , namespace , secretName )
if err != nil {
return nil , err
}
2019-06-27 21:58:03 +00:00
return & tls . CertAndStores {
Certificate : tls . Certificate {
2024-01-11 16:06:06 +00:00
CertFile : types . FileOrContent ( cert ) ,
KeyFile : types . FileOrContent ( key ) ,
2019-06-11 13:12:04 +00:00
} ,
} , nil
}
2019-06-27 21:58:03 +00:00
func getTLSConfig ( tlsConfigs map [ string ] * tls . CertAndStores ) [ ] * tls . CertAndStores {
2019-02-21 22:08:05 +00:00
var secretNames [ ] string
for secretName := range tlsConfigs {
secretNames = append ( secretNames , secretName )
}
sort . Strings ( secretNames )
2019-06-27 21:58:03 +00:00
var configs [ ] * tls . CertAndStores
2019-02-21 22:08:05 +00:00
for _ , secretName := range secretNames {
configs = append ( configs , tlsConfigs [ secretName ] )
}
return configs
}
func getCertificateBlocks ( secret * corev1 . Secret , namespace , secretName string ) ( string , string , error ) {
var missingEntries [ ] string
tlsCrtData , tlsCrtExists := secret . Data [ "tls.crt" ]
if ! tlsCrtExists {
missingEntries = append ( missingEntries , "tls.crt" )
}
tlsKeyData , tlsKeyExists := secret . Data [ "tls.key" ]
if ! tlsKeyExists {
missingEntries = append ( missingEntries , "tls.key" )
}
if len ( missingEntries ) > 0 {
return "" , "" , fmt . Errorf ( "secret %s/%s is missing the following TLS data entries: %s" ,
namespace , secretName , strings . Join ( missingEntries , ", " ) )
}
cert := string ( tlsCrtData )
if cert == "" {
missingEntries = append ( missingEntries , "tls.crt" )
}
key := string ( tlsKeyData )
if key == "" {
missingEntries = append ( missingEntries , "tls.key" )
}
if len ( missingEntries ) > 0 {
return "" , "" , fmt . Errorf ( "secret %s/%s contains the following empty TLS data entries: %s" ,
namespace , secretName , strings . Join ( missingEntries , ", " ) )
}
return cert , key , nil
}
2019-06-21 15:18:05 +00:00
func getCABlocks ( secret * corev1 . Secret , namespace , secretName string ) ( string , error ) {
tlsCrtData , tlsCrtExists := secret . Data [ "tls.ca" ]
2021-06-14 16:06:10 +00:00
if tlsCrtExists {
return string ( tlsCrtData ) , nil
2019-06-21 15:18:05 +00:00
}
2021-06-14 16:06:10 +00:00
tlsCrtData , tlsCrtExists = secret . Data [ "ca.crt" ]
if tlsCrtExists {
return string ( tlsCrtData ) , nil
2019-06-21 15:18:05 +00:00
}
2021-06-14 16:06:10 +00:00
return "" , fmt . Errorf ( "secret %s/%s contains neither tls.ca nor ca.crt" , namespace , secretName )
2019-06-21 15:18:05 +00:00
}
2019-08-30 10:16:04 +00:00
2020-02-03 16:56:04 +00:00
func throttleEvents ( ctx context . Context , throttleDuration time . Duration , pool * safe . Pool , eventsChan <- chan interface { } ) chan interface { } {
2019-08-30 10:16:04 +00:00
if throttleDuration == 0 {
return nil
}
// Create a buffered channel to hold the pending event (if we're delaying processing the event due to throttling)
eventsChanBuffered := make ( chan interface { } , 1 )
2019-11-14 18:28:04 +00:00
// Run a goroutine that reads events from eventChan and does a non-blocking write to pendingEvent.
// This guarantees that writing to eventChan will never block,
// and that pendingEvent will have something in it if there's been an event since we read from that channel.
2020-02-03 16:56:04 +00:00
pool . GoCtx ( func ( ctxPool context . Context ) {
2019-08-30 10:16:04 +00:00
for {
select {
2020-02-03 16:56:04 +00:00
case <- ctxPool . Done ( ) :
2019-08-30 10:16:04 +00:00
return
case nextEvent := <- eventsChan :
select {
case eventsChanBuffered <- nextEvent :
default :
2019-11-14 18:28:04 +00:00
// We already have an event in eventsChanBuffered, so we'll do a refresh as soon as our throttle allows us to.
// It's fine to drop the event and keep whatever's in the buffer -- we don't do different things for different events
2022-11-21 17:36:05 +00:00
log . Ctx ( ctx ) . Debug ( ) . Msgf ( "Dropping event kind %T due to throttling" , nextEvent )
2019-08-30 10:16:04 +00:00
}
}
}
2020-02-03 16:56:04 +00:00
} )
2019-08-30 10:16:04 +00:00
return eventsChanBuffered
}
2020-12-10 13:58:04 +00:00
2021-07-13 08:48:05 +00:00
func isNamespaceAllowed ( allowCrossNamespace bool , parentNamespace , namespace string ) bool {
2020-12-10 13:58:04 +00:00
// If allowCrossNamespace option is not defined the default behavior is to allow cross namespace references.
2021-07-13 08:48:05 +00:00
return allowCrossNamespace || parentNamespace == namespace
2020-12-10 13:58:04 +00:00
}