1package tls
2
3import (
4	"os"
5	"path/filepath"
6	"testing"
7
8	"github.com/stretchr/testify/assert"
9	"github.com/stretchr/testify/require"
10)
11
12// certPEM and keyPEM are copied from the golang crypto/tls library
13// https://github.com/golang/go/blob/7eb5941b95a588a23f18fa4c22fe42ff0119c311/src/crypto/tls/example_test.go#L127
14const certPEM = `-----BEGIN CERTIFICATE-----
15MIIBhTCCASugAwIBAgIQIRi6zePL6mKjOipn+dNuaTAKBggqhkjOPQQDAjASMRAw
16DgYDVQQKEwdBY21lIENvMB4XDTE3MTAyMDE5NDMwNloXDTE4MTAyMDE5NDMwNlow
17EjEQMA4GA1UEChMHQWNtZSBDbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABD0d
187VNhbWvZLWPuj/RtHFjvtJBEwOkhbN/BnnE8rnZR8+sbwnc/KhCk3FhnpHZnQz7B
195aETbbIgmuvewdjvSBSjYzBhMA4GA1UdDwEB/wQEAwICpDATBgNVHSUEDDAKBggr
20BgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MCkGA1UdEQQiMCCCDmxvY2FsaG9zdDo1
21NDUzgg4xMjcuMC4wLjE6NTQ1MzAKBggqhkjOPQQDAgNIADBFAiEA2zpJEPQyz6/l
22Wf86aX6PepsntZv2GYlA5UpabfT2EZICICpJ5h/iI+i341gBmLiAFQOyTDT+/wQc
236MF9+Yw1Yy0t
24-----END CERTIFICATE-----`
25const keyPEM = `-----BEGIN EC PRIVATE KEY-----
26MHcCAQEEIIrYSSNQFaA2Hwf1duRSxKtLYX5CB04fSeQ6tF1aY/PuoAoGCCqGSM49
27AwEHoUQDQgAEPR3tU2Fta9ktY+6P9G0cWO+0kETA6SFs38GecTyudlHz6xvCdz8q
28EKTcWGekdmdDPsHloRNtsiCa697B2O9IFA==
29-----END EC PRIVATE KEY-----`
30
31// caPEM is CA certificate of Let's Encrypt
32// https://letsencrypt.org/certs/isrgrootx1.pem.txt
33const caPEM = `-----BEGIN CERTIFICATE-----
34MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
35TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
36cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
37WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
38ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
39MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
40h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
410TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
42A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
43T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
44B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
45B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
46KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
47OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
48jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
49qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
50rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
51HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
52hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
53ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
543BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
55NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
56ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
57TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
58jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
59oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
604RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
61mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
62emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
63-----END CERTIFICATE-----`
64
65type x509Paths struct {
66	cert string
67	key  string
68	ca   string
69}
70
71func newTestX509Files(t *testing.T, cert, key, ca []byte) x509Paths {
72	t.Helper()
73
74	certsPath := t.TempDir()
75
76	paths := x509Paths{
77		cert: filepath.Join(certsPath, "cert.pem"),
78		key:  filepath.Join(certsPath, "key.pem"),
79		ca:   filepath.Join(certsPath, "ca.pem"),
80	}
81
82	if cert != nil {
83		err := os.WriteFile(paths.cert, cert, 0600)
84		require.NoError(t, err)
85	}
86
87	if key != nil {
88		err := os.WriteFile(paths.key, key, 0600)
89		require.NoError(t, err)
90	}
91
92	if ca != nil {
93		err := os.WriteFile(paths.ca, ca, 0600)
94		require.NoError(t, err)
95	}
96
97	return paths
98}
99
100func TestGetTLSConfig_ClientCerts(t *testing.T) {
101	paths := newTestX509Files(t, []byte(certPEM), []byte(keyPEM), nil)
102
103	// test working certificate passed
104	c := &ClientConfig{
105		CertPath: paths.cert,
106		KeyPath:  paths.key,
107	}
108	tlsConfig, err := c.GetTLSConfig()
109	assert.NoError(t, err)
110	assert.Equal(t, false, tlsConfig.InsecureSkipVerify, "make sure we default to not skip verification")
111	assert.Equal(t, 1, len(tlsConfig.Certificates), "ensure a certificate is returned")
112
113	// expect error with key and cert swapped passed along
114	c = &ClientConfig{
115		CertPath: paths.key,
116		KeyPath:  paths.cert,
117	}
118	_, err = c.GetTLSConfig()
119	assert.Error(t, err)
120	assert.Contains(t, err.Error(), "failed to find certificate PEM data in certificate input, but did find a private key")
121
122	// expect error with only key passed along
123	c = &ClientConfig{
124		KeyPath: paths.key,
125	}
126	_, err = c.GetTLSConfig()
127	assert.EqualError(t, err, errCertMissing.Error())
128
129	// expect error with only cert passed along
130	c = &ClientConfig{
131		CertPath: paths.cert,
132	}
133	_, err = c.GetTLSConfig()
134	assert.EqualError(t, err, errKeyMissing.Error())
135}
136
137func TestGetTLSConfig_CA(t *testing.T) {
138	paths := newTestX509Files(t, nil, nil, []byte(certPEM))
139
140	// test single ca passed
141	c := &ClientConfig{
142		CAPath: paths.ca,
143	}
144	tlsConfig, err := c.GetTLSConfig()
145	assert.NoError(t, err)
146	assert.Equal(t, 1, len(tlsConfig.RootCAs.Subjects()), "ensure one CA is returned")
147	assert.Equal(t, false, tlsConfig.InsecureSkipVerify, "make sure we default to not skip verification")
148
149	// test two cas passed
150	paths = newTestX509Files(t, nil, nil, []byte(certPEM+"\n"+caPEM))
151	c = &ClientConfig{
152		CAPath: paths.ca,
153	}
154	tlsConfig, err = c.GetTLSConfig()
155	assert.NoError(t, err)
156	assert.Equal(t, 2, len(tlsConfig.RootCAs.Subjects()), "ensure two CAs are returned")
157	assert.False(t, tlsConfig.InsecureSkipVerify, "make sure we default to not skip verification")
158
159	// expect errors to be passed
160	c = &ClientConfig{
161		CAPath: paths.ca + "not-existing",
162	}
163	_, err = c.GetTLSConfig()
164	assert.Error(t, err)
165	assert.Contains(t, err.Error(), "error loading ca cert")
166}
167
168func TestGetTLSConfig_InsecureSkipVerify(t *testing.T) {
169	c := &ClientConfig{
170		InsecureSkipVerify: true,
171	}
172	tlsConfig, err := c.GetTLSConfig()
173	assert.NoError(t, err)
174	assert.True(t, tlsConfig.InsecureSkipVerify)
175}
176
177func TestGetTLSConfig_ServerName(t *testing.T) {
178	c := &ClientConfig{
179		ServerName: "myserver.com",
180	}
181	tlsConfig, err := c.GetTLSConfig()
182	assert.NoError(t, err)
183	assert.Equal(t, "myserver.com", tlsConfig.ServerName)
184}
185