forked from elastic/beats
-
Notifications
You must be signed in to change notification settings - Fork 0
/
tls.go
219 lines (187 loc) · 6.03 KB
/
tls.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
package outputs
import (
"crypto/tls"
"crypto/x509"
"errors"
"io/ioutil"
"github.com/elastic/beats/libbeat/logp"
)
var (
// ErrNotACertificate indicates a PEM file to be loaded not being a valid
// PEM file or certificate.
ErrNotACertificate = errors.New("file is not a certificate")
// ErrCertificateNoKey indicate a configuration error with missing key file
ErrCertificateNoKey = errors.New("key file not configured")
// ErrKeyNoCertificate indicate a configuration error with missing certificate file
ErrKeyNoCertificate = errors.New("certificate file not configured")
// ErrInvalidTLSVersion indicates an unknown tls version string given.
ErrInvalidTLSVersion = errors.New("invalid TLS version string")
// ErrUnknownCipherSuite indicates an unknown tls cipher suite being used
ErrUnknownCipherSuite = errors.New("unknown cypher suite")
// ErrUnknownCurveID indicates an unknown curve id has been configured
ErrUnknownCurveID = errors.New("unknown curve id")
)
// TLSConfig defines config file options for TLS clients.
type TLSConfig struct {
Certificate string `config:"certificate"`
CertificateKey string `config:"certificate_key"`
CAs []string `config:"certificate_authorities"`
Insecure bool `config:"insecure"`
CipherSuites []string `config:"cipher_suites"`
MinVersion string `config:"min_version"`
MaxVersion string `config:"max_version"`
CurveTypes []string `config:"curve_types"`
}
func (c *TLSConfig) Validate() error {
hasCertificate := c.Certificate != ""
hasKey := c.CertificateKey != ""
switch {
case hasCertificate && !hasKey:
return ErrCertificateNoKey
case !hasCertificate && hasKey:
return ErrKeyNoCertificate
}
if _, err := parseTLSVersion(c.MinVersion); err != nil {
return err
}
if _, err := parseTLSVersion(c.MaxVersion); err != nil {
return err
}
if _, err := parseTLSCipherSuites(c.CipherSuites); err != nil {
return err
}
if _, err := parseCurveTypes(c.CurveTypes); err != nil {
return err
}
return nil
}
// LoadTLSConfig will load a certificate from config with all TLS based keys
// defined. If Certificate and CertificateKey are configured, client authentication
// will be configured. If no CAs are configured, the host CA will be used by go
// built-in TLS support.
func LoadTLSConfig(config *TLSConfig) (*tls.Config, error) {
if config == nil {
return nil, nil
}
certificate := config.Certificate
key := config.CertificateKey
rootCAs := config.CAs
hasCertificate := certificate != ""
hasKey := key != ""
var certs []tls.Certificate
switch {
case hasCertificate && !hasKey:
return nil, ErrCertificateNoKey
case !hasCertificate && hasKey:
return nil, ErrKeyNoCertificate
case hasCertificate && hasKey:
cert, err := tls.LoadX509KeyPair(certificate, key)
if err != nil {
logp.Critical("Failed loading client certificate", err)
return nil, err
}
certs = []tls.Certificate{cert}
}
var roots *x509.CertPool
if len(rootCAs) > 0 {
roots = x509.NewCertPool()
for _, caFile := range rootCAs {
pemData, err := ioutil.ReadFile(caFile)
if err != nil {
logp.Critical("Failed reading CA certificate: %s", err)
return nil, err
}
if ok := roots.AppendCertsFromPEM(pemData); !ok {
return nil, ErrNotACertificate
}
}
}
minVersion, err := parseTLSVersion(config.MinVersion)
if err != nil {
return nil, err
}
if minVersion == 0 {
// require minimum TLS-1.0 if not configured
minVersion = tls.VersionTLS10
}
maxVersion, err := parseTLSVersion(config.MaxVersion)
if err != nil {
return nil, err
}
cipherSuites, err := parseTLSCipherSuites(config.CipherSuites)
if err != nil {
return nil, err
}
curveIDs, err := parseCurveTypes(config.CurveTypes)
if err != nil {
return nil, err
}
tlsConfig := tls.Config{
MinVersion: minVersion,
MaxVersion: maxVersion,
Certificates: certs,
RootCAs: roots,
InsecureSkipVerify: config.Insecure,
CipherSuites: cipherSuites,
CurvePreferences: curveIDs,
}
return &tlsConfig, nil
}
func parseTLSVersion(s string) (uint16, error) {
versions := map[string]uint16{
"": 0,
"SSL-3.0": tls.VersionSSL30,
"1.0": tls.VersionTLS10,
"1.1": tls.VersionTLS11,
"1.2": tls.VersionTLS12,
}
id, ok := versions[s]
if !ok {
return 0, ErrInvalidTLSVersion
}
return id, nil
}
func parseTLSCipherSuites(names []string) ([]uint16, error) {
suites := map[string]uint16{
"RSA-RC4-128-SHA": tls.TLS_RSA_WITH_RC4_128_SHA,
"RSA-3DES-CBC3-SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
"RSA-AES-128-CBC-SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA,
"RSA-AES-256-CBC-SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA,
"ECDHE-ECDSA-RC4-128-SHA": tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
"ECDHE-ECDSA-AES-128-CBC-SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
"ECDHE-ECDSA-AES-256-CBC-SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
"ECDHE-RSA-RC4-128-SHA": tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
"ECDHE-RSA-3DES-CBC3-SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
"ECDHE-RSA-AES-128-CBC-SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
"ECDHE-RSA-AES-256-CBC-SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
"ECDHE-RSA-AES-128-GCM-SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
"ECDHE-ECDSA-AES-128-GCM-SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
"ECDHE-RSA-AES-256-GCM-SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
"ECDHE-ECDSA-AES-256-GCM-SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
}
var list []uint16
for _, name := range names {
id, ok := suites[name]
if !ok {
return nil, ErrUnknownCipherSuite
}
list = append(list, id)
}
return list, nil
}
func parseCurveTypes(names []string) ([]tls.CurveID, error) {
curveIDs := map[string]tls.CurveID{
"P-256": tls.CurveP256,
"P-384": tls.CurveP384,
"P-521": tls.CurveP521,
}
var list []tls.CurveID
for _, name := range names {
id, ok := curveIDs[name]
if !ok {
return nil, ErrUnknownCurveID
}
list = append(list, id)
}
return list, nil
}