2019-01-18 14:18:04 +00:00
package types
import (
"context"
"crypto/tls"
"crypto/x509"
2021-10-26 08:54:11 +00:00
"errors"
2019-01-18 14:18:04 +00:00
"fmt"
"os"
2020-09-16 13:46:04 +00:00
"github.com/traefik/traefik/v2/pkg/log"
2019-01-18 14:18:04 +00:00
)
2021-10-26 08:54:11 +00:00
// +k8s:deepcopy-gen=true
2019-01-18 14:18:04 +00:00
// ClientTLS holds TLS specific configurations as client
2020-05-11 10:06:07 +00:00
// CA, Cert and Key can be either path or file contents.
2019-01-18 14:18:04 +00:00
type ClientTLS struct {
2022-04-28 12:58:08 +00:00
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).
2020-10-30 11:44:05 +00:00
CAOptional bool ` description:"TLS CA.Optional" json:"caOptional,omitempty" toml:"caOptional,omitempty" yaml:"caOptional,omitempty" export:"true" `
2019-07-01 09:30:05 +00:00
Cert string ` description:"TLS cert" json:"cert,omitempty" toml:"cert,omitempty" yaml:"cert,omitempty" `
2022-01-24 10:08:05 +00:00
Key string ` description:"TLS key" json:"key,omitempty" toml:"key,omitempty" yaml:"key,omitempty" loggable:"false" `
2020-10-30 11:44:05 +00:00
InsecureSkipVerify bool ` description:"TLS insecure skip verify" json:"insecureSkipVerify,omitempty" toml:"insecureSkipVerify,omitempty" yaml:"insecureSkipVerify,omitempty" export:"true" `
2019-01-18 14:18:04 +00:00
}
2020-05-11 10:06:07 +00:00
// CreateTLSConfig creates a TLS config from ClientTLS structures.
2019-01-18 14:18:04 +00:00
func ( clientTLS * ClientTLS ) CreateTLSConfig ( ctx context . Context ) ( * tls . Config , error ) {
if clientTLS == nil {
log . FromContext ( ctx ) . Warnf ( "clientTLS is nil" )
return nil , nil
}
2022-04-28 12:58:08 +00:00
if clientTLS . CAOptional {
log . FromContext ( ctx ) . Warn ( "CAOptional is deprecated, TLS client authentication is a server side option." )
}
2021-11-03 16:38:07 +00:00
// Not initialized, to rely on system bundle.
var caPool * x509 . CertPool
2019-01-18 14:18:04 +00:00
if clientTLS . CA != "" {
var ca [ ] byte
if _ , errCA := os . Stat ( clientTLS . CA ) ; errCA == nil {
var err error
2021-03-04 19:08:03 +00:00
ca , err = os . ReadFile ( clientTLS . CA )
2019-01-18 14:18:04 +00:00
if err != nil {
2020-05-11 10:06:07 +00:00
return nil , fmt . Errorf ( "failed to read CA. %w" , err )
2019-01-18 14:18:04 +00:00
}
} else {
ca = [ ] byte ( clientTLS . CA )
}
2021-11-03 16:38:07 +00:00
caPool = x509 . NewCertPool ( )
2019-01-18 14:18:04 +00:00
if ! caPool . AppendCertsFromPEM ( ca ) {
2021-10-26 08:54:11 +00:00
return nil , errors . New ( "failed to parse CA" )
2019-01-18 14:18:04 +00:00
}
}
2021-10-26 08:54:11 +00:00
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" )
2019-01-18 14:18:04 +00:00
}
2021-10-26 08:54:11 +00:00
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
2019-01-18 14:18:04 +00:00
}
2019-11-14 15:40:05 +00:00
return & tls . Config {
2019-01-18 14:18:04 +00:00
Certificates : [ ] tls . Certificate { cert } ,
RootCAs : caPool ,
InsecureSkipVerify : clientTLS . InsecureSkipVerify ,
2019-11-14 15:40:05 +00:00
} , nil
2019-01-18 14:18:04 +00:00
}
2021-10-26 08:54:11 +00:00
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
}