forked from hashicorp/nomad
-
Notifications
You must be signed in to change notification settings - Fork 1
/
tls.go
173 lines (144 loc) · 4.91 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
package config
import (
"crypto/tls"
"fmt"
"sync"
)
// TLSConfig provides TLS related configuration
type TLSConfig struct {
// EnableHTTP enabled TLS for http traffic to the Nomad server and clients
EnableHTTP bool `mapstructure:"http"`
// EnableRPC enables TLS for RPC and Raft traffic to the Nomad servers
EnableRPC bool `mapstructure:"rpc"`
// VerifyServerHostname is used to enable hostname verification of servers. This
// ensures that the certificate presented is valid for server.<region>.nomad
// This prevents a compromised client from being restarted as a server, and then
// intercepting request traffic as well as being added as a raft peer. This should be
// enabled by default with VerifyOutgoing, but for legacy reasons we cannot break
// existing clients.
VerifyServerHostname bool `mapstructure:"verify_server_hostname"`
// CAFile is a path to a certificate authority file. This is used with VerifyIncoming
// or VerifyOutgoing to verify the TLS connection.
CAFile string `mapstructure:"ca_file"`
// CertFile is used to provide a TLS certificate that is used for serving TLS connections.
// Must be provided to serve TLS connections.
CertFile string `mapstructure:"cert_file"`
// KeyLoader is a helper to dynamically reload TLS configuration
KeyLoader *KeyLoader
keyloaderLock sync.Mutex
// KeyFile is used to provide a TLS key that is used for serving TLS connections.
// Must be provided to serve TLS connections.
KeyFile string `mapstructure:"key_file"`
// RPCUpgradeMode should be enabled when a cluster is being upgraded
// to TLS. Allows servers to accept both plaintext and TLS connections and
// should only be a temporary state.
RPCUpgradeMode bool `mapstructure:"rpc_upgrade_mode"`
// Verify connections to the HTTPS API
VerifyHTTPSClient bool `mapstructure:"verify_https_client"`
}
type KeyLoader struct {
cacheLock sync.Mutex
certificate *tls.Certificate
}
// LoadKeyPair reloads the TLS certificate based on the specified certificate
// and key file. If successful, stores the certificate for further use.
func (k *KeyLoader) LoadKeyPair(certFile, keyFile string) (*tls.Certificate, error) {
k.cacheLock.Lock()
defer k.cacheLock.Unlock()
// Allow downgrading
if certFile == "" && keyFile == "" {
k.certificate = nil
return nil, nil
}
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return nil, fmt.Errorf("Failed to load cert/key pair: %v", err)
}
k.certificate = &cert
return k.certificate, nil
}
// GetOutgoingCertificate fetches the currently-loaded certificate when
// accepting a TLS connection. This currently does not consider information in
// the ClientHello and only returns the certificate that was last loaded.
func (k *KeyLoader) GetOutgoingCertificate(*tls.ClientHelloInfo) (*tls.Certificate, error) {
k.cacheLock.Lock()
defer k.cacheLock.Unlock()
return k.certificate, nil
}
// GetClientCertificate fetches the currently-loaded certificate when the Server
// requests a certificate from the caller. This currently does not consider
// information in the ClientHello and only returns the certificate that was last
// loaded.
func (k *KeyLoader) GetClientCertificate(*tls.CertificateRequestInfo) (*tls.Certificate, error) {
k.cacheLock.Lock()
defer k.cacheLock.Unlock()
return k.certificate, nil
}
func (k *KeyLoader) Copy() *KeyLoader {
if k == nil {
return nil
}
new := KeyLoader{}
new.certificate = k.certificate
return &new
}
// GetKeyLoader returns the keyloader for a TLSConfig object. If the keyloader
// has not been initialized, it will first do so.
func (t *TLSConfig) GetKeyLoader() *KeyLoader {
t.keyloaderLock.Lock()
defer t.keyloaderLock.Unlock()
// If the keyloader has not yet been initialized, do it here
if t.KeyLoader == nil {
t.KeyLoader = &KeyLoader{}
}
return t.KeyLoader
}
// Copy copies the fields of TLSConfig to another TLSConfig object. Required as
// to not copy mutexes between objects.
func (t *TLSConfig) Copy() *TLSConfig {
if t == nil {
return t
}
new := &TLSConfig{}
new.EnableHTTP = t.EnableHTTP
new.EnableRPC = t.EnableRPC
new.VerifyServerHostname = t.VerifyServerHostname
new.CAFile = t.CAFile
new.CertFile = t.CertFile
t.keyloaderLock.Lock()
new.KeyLoader = t.KeyLoader.Copy()
t.keyloaderLock.Unlock()
new.KeyFile = t.KeyFile
new.RPCUpgradeMode = t.RPCUpgradeMode
new.VerifyHTTPSClient = t.VerifyHTTPSClient
return new
}
// Merge is used to merge two TLS configs together
func (t *TLSConfig) Merge(b *TLSConfig) *TLSConfig {
result := t.Copy()
if b.EnableHTTP {
result.EnableHTTP = true
}
if b.EnableRPC {
result.EnableRPC = true
}
if b.VerifyServerHostname {
result.VerifyServerHostname = true
}
if b.CAFile != "" {
result.CAFile = b.CAFile
}
if b.CertFile != "" {
result.CertFile = b.CertFile
}
if b.KeyFile != "" {
result.KeyFile = b.KeyFile
}
if b.VerifyHTTPSClient {
result.VerifyHTTPSClient = true
}
if b.RPCUpgradeMode {
result.RPCUpgradeMode = true
}
return result
}