Skip to content

Commit

Permalink
added clientauth, ciphersuites and curves to TLS options
Browse files Browse the repository at this point in the history
  • Loading branch information
lordvidex committed Jul 25, 2023
1 parent 53b4882 commit 8e26313
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 13 deletions.
118 changes: 106 additions & 12 deletions helper/config/tls.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,58 @@ import (
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"os"
"strings"
)

// https://pkg.go.dev/crypto/tls#ClientAuthType
var supportedClientAuths = map[string]tls.ClientAuthType{
"NoClientCert": tls.NoClientCert,
"RequestClientCert": tls.RequestClientCert,
"RequireAnyClientCert": tls.RequireAnyClientCert,
"VerifyClientCertIfGiven": tls.VerifyClientCertIfGiven,
"RequireAndVerifyClientCert": tls.RequireAndVerifyClientCert,
}

var supportedCurveIDs = map[string]tls.CurveID{
"CurveP256": tls.CurveP256,
"CurveP384": tls.CurveP384,
"CurveP521": tls.CurveP521,
"X25519": tls.X25519,
}

type TLS struct {
Certificates []CertificatePair `toml:"certificates"`
CACertFiles []string `toml:"ca-cert"`
ClientAuth string `toml:"client-auth"`

ServerName string `toml:"server-name"`
MinVersion string `toml:"min-version"` // supported formats: TLS10, TLS11, TLS12, TLS13
MaxVersion string `toml:"max-version"` // supported formats: TLS10, TLS11, TLS12, TLS13
InsecureSkipVerify bool `toml:"insecure-skip-verify"`
ServerName string `toml:"server-name"`
MinVersion string `toml:"min-version"` // supported formats: TLS10, TLS11, TLS12, TLS13
MaxVersion string `toml:"max-version"` // supported formats: TLS10, TLS11, TLS12, TLS13
InsecureSkipVerify bool `toml:"insecure-skip-verify"`
Curves []string `toml:"curves"`
CipherSuites []string `toml:"cipher-suites"`
}

type CertificatePair struct {
KeyFile string `toml:"key"`
CertFile string `toml:"cert"`
}

// ParseClientTLSConfig returns TLS config for HTTPS client's mTLS connections.
func ParseClientTLSConfig(config *TLS) (*tls.Config, error) {
// ParseClientTLSConfig parses TLSConfig as it should be used for HTTPS client mTLS and returns &tls.Config, list of
// warnings or error if parsing has failed.
// At this moment warnings are only about insecure ciphers
func ParseClientTLSConfig(config *TLS) (*tls.Config, []string, error) {
if len(config.Certificates) == 0 {
return nil, errors.New("no tls certificates provided")
return nil, nil, fmt.Errorf("no tls certificates provided")
}

caCertPool := x509.NewCertPool()
for _, caCert := range config.CACertFiles {
cert, err := os.ReadFile(caCert)
if err != nil {
return nil, err
return nil, nil, err
}
caCertPool.AppendCertsFromPEM(cert)
}
Expand All @@ -42,18 +64,31 @@ func ParseClientTLSConfig(config *TLS) (*tls.Config, error) {
for _, it := range config.Certificates {
cert, err := tls.LoadX509KeyPair(it.CertFile, it.KeyFile)
if err != nil {
return nil, err
return nil, nil, err
}
certificates = append(certificates, cert)
}

minVersion, err := ParseTLSVersion(config.MinVersion)
if err != nil {
return nil, err
return nil, nil, err
}
maxVersion, err := ParseTLSVersion(config.MaxVersion)
if err != nil {
return nil, err
return nil, nil, err
}
curves, err := ParseCurves(config.Curves)
if err != nil {
return nil, nil, err
}

ciphers, warns, err := CipherSuitesToUint16(config.CipherSuites)
if err != nil {
return nil, warns, err
}

if config.InsecureSkipVerify {
warns = append(warns, "InsecureSkipVerify is set to true, it's not recommended to use that in production")
}

tlsConfig := &tls.Config{
Expand All @@ -63,8 +98,10 @@ func ParseClientTLSConfig(config *TLS) (*tls.Config, error) {
MinVersion: minVersion,
MaxVersion: maxVersion,
InsecureSkipVerify: config.InsecureSkipVerify,
CipherSuites: ciphers,
CurvePreferences: curves,
}
return tlsConfig, nil
return tlsConfig, warns, nil
}

// ParseTLSVersion converts a TLS version string ("TLS10", "TLS11", "TLS12", "TLS13")
Expand All @@ -88,3 +125,60 @@ func ParseTLSVersion(version string) (uint16, error) {
return 0, errors.New("unknown TLS version")
}
}

// ParseCurves returns list of tls.CurveIDs that can be passed to tls.Config or error if they are not supported
// ParseCurves also deduplicate input list
func ParseCurves(curveNames []string) ([]tls.CurveID, error) {
inputCurveNamesMap := make(map[string]struct{})
for _, name := range curveNames {
inputCurveNamesMap[name] = struct{}{}
}
res := make([]tls.CurveID, 0, len(inputCurveNamesMap))
for name := range inputCurveNamesMap {
if curve, ok := supportedCurveIDs[name]; ok {
res = append(res, curve)
} else {
return nil, fmt.Errorf("invalid curve name specified: %v", name)
}
}
return res, nil
}

func ParseClientAuthType(clientAuth string) (tls.ClientAuthType, error) {
if clientAuth == "" {
return tls.NoClientCert, nil
}
if id, ok := supportedClientAuths[clientAuth]; ok {
return id, nil
} else {
return tls.NoClientCert, fmt.Errorf("invalid auth type specified: %v", clientAuth)
}
}

// CipherSuitesToUint16 for a given list of ciphers returns list of corresponding ids, list of insecure ciphers
// if cipher is unknown, it will return an error
func CipherSuitesToUint16(ciphers []string) ([]uint16, []string, error) {
res := make([]uint16, 0)
insecureCiphers := make([]string, 0)
cipherList := tls.CipherSuites()

cipherNames := make([]string, 0, len(cipherList))
cipherSuites := make(map[string]uint16)

for _, cipher := range cipherList {
cipherSuites[cipher.Name] = cipher.ID
if cipher.Insecure {
insecureCiphers = append(insecureCiphers, cipher.Name)
}
}

for _, c := range ciphers {
if id, ok := cipherSuites[c]; ok {
res = append(res, id)
} else {
return nil, nil, fmt.Errorf("unknown cipher specified: %v, supported ciphers: %+v", c, cipherNames)
}
}

return res, insecureCiphers, nil
}
7 changes: 6 additions & 1 deletion uploader/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,10 +255,15 @@ func (u *Base) insertRowBinary(table string, data io.Reader) error {
transport := &http.Transport{DisableKeepAlives: true}

if u.config.TLS != nil {
tlsConfig, err := config.ParseClientTLSConfig(u.config.TLS)
tlsConfig, warns, err := config.ParseClientTLSConfig(u.config.TLS)
if err != nil {
return err
}
if len(warns) > 0 {
u.logger.Warn("insecure options detected, while parsing HTTP Client TLS Config for uploader",
zap.Strings("warnings", warns),
)
}
transport.TLSClientConfig = tlsConfig
}

Expand Down

0 comments on commit 8e26313

Please sign in to comment.