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"
2022-11-21 17:36:05 +00:00
"github.com/rs/zerolog/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.
2022-09-12 15:40:09 +00:00
func ( c * ClientTLS ) CreateTLSConfig ( ctx context . Context ) ( * tls . Config , error ) {
if c == nil {
2022-11-21 17:36:05 +00:00
log . Ctx ( ctx ) . Warn ( ) . Msg ( "clientTLS is nil" )
2019-01-18 14:18:04 +00:00
return nil , nil
}
2022-09-12 15:40:09 +00:00
if c . CAOptional {
2022-11-21 17:36:05 +00:00
log . Ctx ( ctx ) . Warn ( ) . Msg ( "CAOptional is deprecated, TLS client authentication is a server side option." )
2022-04-28 12:58:08 +00:00
}
2021-11-03 16:38:07 +00:00
// Not initialized, to rely on system bundle.
var caPool * x509 . CertPool
2022-09-12 15:40:09 +00:00
if c . CA != "" {
2019-01-18 14:18:04 +00:00
var ca [ ] byte
2022-09-12 15:40:09 +00:00
if _ , errCA := os . Stat ( c . CA ) ; errCA == nil {
2019-01-18 14:18:04 +00:00
var err error
2022-09-12 15:40:09 +00:00
ca , err = os . ReadFile ( c . 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 {
2022-09-12 15:40:09 +00:00
ca = [ ] byte ( c . CA )
2019-01-18 14:18:04 +00:00
}
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
}
}
2022-09-12 15:40:09 +00:00
hasCert := len ( c . Cert ) > 0
hasKey := len ( c . Key ) > 0
2021-10-26 08:54:11 +00:00
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 ,
2022-09-12 15:40:09 +00:00
InsecureSkipVerify : c . InsecureSkipVerify ,
2021-10-26 08:54:11 +00:00
} , nil
}
2022-09-12 15:40:09 +00:00
cert , err := loadKeyPair ( c . Cert , c . Key )
2021-10-26 08:54:11 +00:00
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 ,
2022-09-12 15:40:09 +00:00
InsecureSkipVerify : c . 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
}