1package exporter
2
3import (
4	"crypto/tls"
5	"crypto/x509"
6	"io/ioutil"
7
8	log "github.com/sirupsen/logrus"
9)
10
11// CreateClientTLSConfig verifies configured files and return a prepared tls.Config
12func (e *Exporter) CreateClientTLSConfig() (*tls.Config, error) {
13	tlsConfig := tls.Config{
14		InsecureSkipVerify: e.options.SkipTLSVerification,
15	}
16
17	if e.options.ClientCertFile != "" && e.options.ClientKeyFile != "" {
18		cert, err := LoadKeyPair(e.options.ClientCertFile, e.options.ClientKeyFile)
19		if err != nil {
20			return nil, err
21		}
22		tlsConfig.Certificates = []tls.Certificate{*cert}
23	}
24
25	if e.options.CaCertFile != "" {
26		certificates, err := LoadCAFile(e.options.CaCertFile)
27		if err != nil {
28			return nil, err
29		}
30		tlsConfig.RootCAs = certificates
31	}
32
33	return &tlsConfig, nil
34}
35
36// CreateServerTLSConfig verifies configured files and return a prepared tls.Config
37func (e *Exporter) CreateServerTLSConfig(certFile, keyFile, caCertFile string) (*tls.Config, error) {
38	// Verify that the initial key pair is accepted
39	_, err := LoadKeyPair(certFile, keyFile)
40	if err != nil {
41		return nil, err
42	}
43
44	tlsConfig := tls.Config{
45		GetCertificate: GetServerCertificateFunc(certFile, keyFile),
46	}
47
48	if caCertFile != "" {
49		// Verify that the initial CA file is accepted when configured
50		_, err := LoadCAFile(caCertFile)
51		if err != nil {
52			return nil, err
53		}
54		tlsConfig.GetConfigForClient = GetConfigForClientFunc(certFile, keyFile, caCertFile)
55	}
56
57	return &tlsConfig, nil
58}
59
60// GetServerCertificateFunc returns a function for tls.Config.GetCertificate
61func GetServerCertificateFunc(certFile, keyFile string) func(*tls.ClientHelloInfo) (*tls.Certificate, error) {
62	return func(*tls.ClientHelloInfo) (*tls.Certificate, error) {
63		return LoadKeyPair(certFile, keyFile)
64	}
65}
66
67// GetConfigForClientFunc returns a function for tls.Config.GetConfigForClient
68func GetConfigForClientFunc(certFile, keyFile, caCertFile string) func(*tls.ClientHelloInfo) (*tls.Config, error) {
69	return func(*tls.ClientHelloInfo) (*tls.Config, error) {
70		certificates, err := LoadCAFile(caCertFile)
71		if err != nil {
72			return nil, err
73		}
74
75		tlsConfig := tls.Config{
76			ClientAuth:     tls.RequireAndVerifyClientCert,
77			ClientCAs:      certificates,
78			GetCertificate: GetServerCertificateFunc(certFile, keyFile),
79		}
80		return &tlsConfig, nil
81	}
82}
83
84// LoadKeyPair reads and parses a public/private key pair from a pair of files.
85// The files must contain PEM encoded data.
86func LoadKeyPair(certFile, keyFile string) (*tls.Certificate, error) {
87	log.Debugf("Load key pair: %s %s", certFile, keyFile)
88	cert, err := tls.LoadX509KeyPair(certFile, keyFile)
89	if err != nil {
90		return nil, err
91	}
92	return &cert, nil
93}
94
95// LoadCAFile reads and parses CA certificates from a file into a pool.
96// The file must contain PEM encoded data.
97func LoadCAFile(caFile string) (*x509.CertPool, error) {
98	log.Debugf("Load CA cert file: %s", caFile)
99	pemCerts, err := ioutil.ReadFile(caFile)
100	if err != nil {
101		return nil, err
102	}
103	pool := x509.NewCertPool()
104	pool.AppendCertsFromPEM(pemCerts)
105	return pool, nil
106}
107