Skip to content

Commit 9f19daa

Browse files
committed
Allow users to create TLS config from arbitrary sources of PEM data
Fix #52 Signed-off-by: Simon Ferquel <[email protected]>
1 parent 7395e3f commit 9f19daa

File tree

2 files changed

+160
-60
lines changed

2 files changed

+160
-60
lines changed

tlsconfig/config.go

+76-18
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,47 @@ import (
1616
"github.com/pkg/errors"
1717
)
1818

19+
// PEMSource represents a provider of some PEM Block
20+
type PEMSource interface {
21+
Data() ([]byte, error)
22+
Name() string
23+
}
24+
25+
// PEMFile is a PEMSource backed by a file
26+
type PEMFile string
27+
28+
// Data returns PEM data from the file
29+
func (f PEMFile) Data() ([]byte, error) {
30+
return ioutil.ReadFile(string(f))
31+
}
32+
33+
// Name returns the name of the file
34+
func (f PEMFile) Name() string {
35+
return string(f)
36+
}
37+
38+
// PEMInMemory is a PEMSource from memory
39+
type PEMInMemory []byte
40+
41+
// Data returns PEM data from memory
42+
func (m PEMInMemory) Data() ([]byte, error) {
43+
return []byte(m), nil
44+
}
45+
46+
// Name returns <in memory>
47+
func (m PEMInMemory) Name() string {
48+
return "<in memory>"
49+
}
50+
1951
// Options represents the information needed to create client and server TLS configurations.
2052
type Options struct {
21-
CAFile string
53+
CA PEMSource
2254

23-
// If either CertFile or KeyFile is empty, Client() will not load them
55+
// If either Cert or Key is nil, Client() will not load them
2456
// preventing the client from authenticating to the server.
2557
// However, Server() requires them and will error out if they are empty.
26-
CertFile string
27-
KeyFile string
58+
Cert PEMSource
59+
Key PEMSource
2860

2961
// client-only option
3062
InsecureSkipVerify bool
@@ -94,7 +126,10 @@ func ClientDefault(ops ...func(*tls.Config)) *tls.Config {
94126
}
95127

96128
// certPool returns an X.509 certificate pool from `caFile`, the certificate file.
97-
func certPool(caFile string, exclusivePool bool) (*x509.CertPool, error) {
129+
func certPool(ca PEMSource, exclusivePool bool) (*x509.CertPool, error) {
130+
if ca == nil {
131+
return nil, fmt.Errorf("no CA PEM data")
132+
}
98133
// If we should verify the server, we need to load a trusted ca
99134
var (
100135
certPool *x509.CertPool
@@ -108,12 +143,12 @@ func certPool(caFile string, exclusivePool bool) (*x509.CertPool, error) {
108143
return nil, fmt.Errorf("failed to read system certificates: %v", err)
109144
}
110145
}
111-
pem, err := ioutil.ReadFile(caFile)
146+
pem, err := ca.Data()
112147
if err != nil {
113-
return nil, fmt.Errorf("could not read CA certificate %q: %v", caFile, err)
148+
return nil, fmt.Errorf("could not read CA certificate %q: %v", ca.Name(), err)
114149
}
115150
if !certPool.AppendCertsFromPEM(pem) {
116-
return nil, fmt.Errorf("failed to append certificates from PEM file: %q", caFile)
151+
return nil, fmt.Errorf("failed to append certificates from PEM file: %q", ca.Name())
117152
}
118153
return certPool, nil
119154
}
@@ -172,18 +207,24 @@ func getPrivateKey(keyBytes []byte, passphrase string) ([]byte, error) {
172207
// if the key is encrypted, the Passphrase in 'options' will be used to
173208
// decrypt it.
174209
func getCert(options Options) ([]tls.Certificate, error) {
175-
if options.CertFile == "" && options.KeyFile == "" {
210+
if options.Cert == nil && options.Key == nil {
176211
return nil, nil
177212
}
213+
if options.Cert == nil {
214+
return nil, errors.New("cert is missing")
215+
}
216+
if options.Key == nil {
217+
return nil, errors.New("key is missing")
218+
}
178219

179220
errMessage := "Could not load X509 key pair"
180221

181-
cert, err := ioutil.ReadFile(options.CertFile)
222+
cert, err := options.Cert.Data()
182223
if err != nil {
183224
return nil, errors.Wrap(err, errMessage)
184225
}
185226

186-
prKeyBytes, err := ioutil.ReadFile(options.KeyFile)
227+
prKeyBytes, err := options.Key.Data()
187228
if err != nil {
188229
return nil, errors.Wrap(err, errMessage)
189230
}
@@ -205,8 +246,8 @@ func getCert(options Options) ([]tls.Certificate, error) {
205246
func Client(options Options) (*tls.Config, error) {
206247
tlsConfig := ClientDefault()
207248
tlsConfig.InsecureSkipVerify = options.InsecureSkipVerify
208-
if !options.InsecureSkipVerify && options.CAFile != "" {
209-
CAs, err := certPool(options.CAFile, options.ExclusiveRootPools)
249+
if !options.InsecureSkipVerify && options.CA != nil {
250+
CAs, err := certPool(options.CA, options.ExclusiveRootPools)
210251
if err != nil {
211252
return nil, err
212253
}
@@ -230,16 +271,33 @@ func Client(options Options) (*tls.Config, error) {
230271
func Server(options Options) (*tls.Config, error) {
231272
tlsConfig := ServerDefault()
232273
tlsConfig.ClientAuth = options.ClientAuth
233-
tlsCert, err := tls.LoadX509KeyPair(options.CertFile, options.KeyFile)
274+
if options.Cert == nil {
275+
return nil, errors.New("cert is missing")
276+
}
277+
if options.Key == nil {
278+
return nil, errors.New("key is missing")
279+
}
280+
cert, err := options.Cert.Data()
234281
if err != nil {
235282
if os.IsNotExist(err) {
236-
return nil, fmt.Errorf("Could not load X509 key pair (cert: %q, key: %q): %v", options.CertFile, options.KeyFile, err)
283+
return nil, fmt.Errorf("Could not load X509 key pair (cert: %q, key: %q): %v", options.Cert.Name(), options.Key.Name(), err)
237284
}
238-
return nil, fmt.Errorf("Error reading X509 key pair (cert: %q, key: %q): %v. Make sure the key is not encrypted.", options.CertFile, options.KeyFile, err)
285+
return nil, fmt.Errorf("could not read cert data: %s", err)
286+
}
287+
key, err := options.Key.Data()
288+
if err != nil {
289+
if os.IsNotExist(err) {
290+
return nil, fmt.Errorf("Could not load X509 key pair (cert: %q, key: %q): %v", options.Cert.Name(), options.Key.Name(), err)
291+
}
292+
return nil, fmt.Errorf("could not read key data: %s", err)
293+
}
294+
tlsCert, err := tls.X509KeyPair(cert, key)
295+
if err != nil {
296+
return nil, fmt.Errorf("Error reading X509 key pair (cert: %q, key: %q): %v. Make sure the key is not encrypted.", options.Cert.Name(), options.Key.Name(), err)
239297
}
240298
tlsConfig.Certificates = []tls.Certificate{tlsCert}
241-
if options.ClientAuth >= tls.VerifyClientCertIfGiven && options.CAFile != "" {
242-
CAs, err := certPool(options.CAFile, options.ExclusiveRootPools)
299+
if options.ClientAuth >= tls.VerifyClientCertIfGiven && options.CA != nil {
300+
CAs, err := certPool(options.CA, options.ExclusiveRootPools)
243301
if err != nil {
244302
return nil, err
245303
}

0 commit comments

Comments
 (0)