github.com/thanos-io/thanos@v0.32.5/pkg/exthttp/tlsconfig.go (about) 1 // Copyright (c) The Thanos Authors. 2 // Licensed under the Apache License 2.0. 3 4 package exthttp 5 6 import ( 7 "crypto/tls" 8 "crypto/x509" 9 "fmt" 10 "os" 11 ) 12 13 // TLSConfig configures the options for TLS connections. 14 type TLSConfig struct { 15 // The CA cert to use for the targets. 16 CAFile string `yaml:"ca_file"` 17 // The client cert file for the targets. 18 CertFile string `yaml:"cert_file"` 19 // The client key file for the targets. 20 KeyFile string `yaml:"key_file"` 21 // Used to verify the hostname for the targets. 22 ServerName string `yaml:"server_name"` 23 // Disable target certificate validation. 24 InsecureSkipVerify bool `yaml:"insecure_skip_verify"` 25 } 26 27 // NewTLSConfig creates a new tls.Config from the given TLSConfig. 28 func NewTLSConfig(cfg *TLSConfig) (*tls.Config, error) { 29 tlsConfig := &tls.Config{InsecureSkipVerify: cfg.InsecureSkipVerify} 30 31 // If a CA cert is provided then let's read it in. 32 if len(cfg.CAFile) > 0 { 33 b, err := readCAFile(cfg.CAFile) 34 if err != nil { 35 return nil, err 36 } 37 if !updateRootCA(tlsConfig, b) { 38 return nil, fmt.Errorf("unable to use specified CA cert %s", cfg.CAFile) 39 } 40 } 41 42 if len(cfg.ServerName) > 0 { 43 tlsConfig.ServerName = cfg.ServerName 44 } 45 // If a client cert & key is provided then configure TLS config accordingly. 46 if len(cfg.CertFile) > 0 && len(cfg.KeyFile) == 0 { 47 return nil, fmt.Errorf("client cert file %q specified without client key file", cfg.CertFile) 48 } else if len(cfg.KeyFile) > 0 && len(cfg.CertFile) == 0 { 49 return nil, fmt.Errorf("client key file %q specified without client cert file", cfg.KeyFile) 50 } else if len(cfg.CertFile) > 0 && len(cfg.KeyFile) > 0 { 51 // Verify that client cert and key are valid. 52 if _, err := cfg.getClientCertificate(nil); err != nil { 53 return nil, err 54 } 55 tlsConfig.GetClientCertificate = cfg.getClientCertificate 56 } 57 58 return tlsConfig, nil 59 } 60 61 // readCAFile reads the CA cert file from disk. 62 func readCAFile(f string) ([]byte, error) { 63 data, err := os.ReadFile(f) 64 if err != nil { 65 return nil, fmt.Errorf("unable to load specified CA cert %s: %s", f, err) 66 } 67 return data, nil 68 } 69 70 // updateRootCA parses the given byte slice as a series of PEM encoded certificates and updates tls.Config.RootCAs. 71 func updateRootCA(cfg *tls.Config, b []byte) bool { 72 caCertPool := x509.NewCertPool() 73 if !caCertPool.AppendCertsFromPEM(b) { 74 return false 75 } 76 cfg.RootCAs = caCertPool 77 return true 78 } 79 80 // getClientCertificate reads the pair of client cert and key from disk and returns a tls.Certificate. 81 func (c *TLSConfig) getClientCertificate(*tls.CertificateRequestInfo) (*tls.Certificate, error) { 82 cert, err := tls.LoadX509KeyPair(c.CertFile, c.KeyFile) 83 if err != nil { 84 return nil, fmt.Errorf("unable to use specified client cert (%s) & key (%s): %s", c.CertFile, c.KeyFile, err) 85 } 86 return &cert, nil 87 }