package types import ( "context" "crypto/tls" "crypto/x509" "errors" "fmt" "os" "github.com/traefik/traefik/v2/pkg/log" ) // +k8s:deepcopy-gen=true // ClientTLS holds TLS specific configurations as client // CA, Cert and Key can be either path or file contents. type ClientTLS struct { CA string `description:"TLS CA" json:"ca,omitempty" toml:"ca,omitempty" yaml:"ca,omitempty"` // Deprecated: TLS client authentication is a server side option (see https://github.com/golang/go/blob/740a490f71d026bb7d2d13cb8fa2d6d6e0572b70/src/crypto/tls/common.go#L634). CAOptional bool `description:"TLS CA.Optional" json:"caOptional,omitempty" toml:"caOptional,omitempty" yaml:"caOptional,omitempty" export:"true"` Cert string `description:"TLS cert" json:"cert,omitempty" toml:"cert,omitempty" yaml:"cert,omitempty"` Key string `description:"TLS key" json:"key,omitempty" toml:"key,omitempty" yaml:"key,omitempty" loggable:"false"` InsecureSkipVerify bool `description:"TLS insecure skip verify" json:"insecureSkipVerify,omitempty" toml:"insecureSkipVerify,omitempty" yaml:"insecureSkipVerify,omitempty" export:"true"` } // CreateTLSConfig creates a TLS config from ClientTLS structures. func (clientTLS *ClientTLS) CreateTLSConfig(ctx context.Context) (*tls.Config, error) { if clientTLS == nil { log.FromContext(ctx).Warnf("clientTLS is nil") return nil, nil } if clientTLS.CAOptional { log.FromContext(ctx).Warn("CAOptional is deprecated, TLS client authentication is a server side option.") } // Not initialized, to rely on system bundle. var caPool *x509.CertPool if clientTLS.CA != "" { var ca []byte if _, errCA := os.Stat(clientTLS.CA); errCA == nil { var err error ca, err = os.ReadFile(clientTLS.CA) if err != nil { return nil, fmt.Errorf("failed to read CA. %w", err) } } else { ca = []byte(clientTLS.CA) } caPool = x509.NewCertPool() if !caPool.AppendCertsFromPEM(ca) { return nil, errors.New("failed to parse CA") } } hasCert := len(clientTLS.Cert) > 0 hasKey := len(clientTLS.Key) > 0 if hasCert != hasKey { return nil, errors.New("both TLS cert and key must be defined") } if !hasCert || !hasKey { return &tls.Config{ RootCAs: caPool, InsecureSkipVerify: clientTLS.InsecureSkipVerify, }, nil } cert, err := loadKeyPair(clientTLS.Cert, clientTLS.Key) if err != nil { return nil, err } return &tls.Config{ Certificates: []tls.Certificate{cert}, RootCAs: caPool, InsecureSkipVerify: clientTLS.InsecureSkipVerify, }, nil } func loadKeyPair(cert, key string) (tls.Certificate, error) { keyPair, err := tls.X509KeyPair([]byte(cert), []byte(key)) if err == nil { return keyPair, nil } _, err = os.Stat(cert) if err != nil { return tls.Certificate{}, errors.New("cert file does not exist") } _, err = os.Stat(key) if err != nil { return tls.Certificate{}, errors.New("key file does not exist") } keyPair, err = tls.LoadX509KeyPair(cert, key) if err != nil { return tls.Certificate{}, err } return keyPair, nil }