github.com/infraboard/keyauth@v0.8.1/common/tls/tls.go (about)

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