-
Notifications
You must be signed in to change notification settings - Fork 0
/
server.go
328 lines (288 loc) · 9.74 KB
/
server.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
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package comm
import (
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"net"
"sync"
"sync/atomic"
"github.com/grpc-ecosystem/go-grpc-middleware"
"google.golang.org/grpc"
)
type GRPCServer struct {
// Listen address for the server specified as hostname:port
address string
// Listener for handling network requests
listener net.Listener
// GRPC server
server *grpc.Server
// Certificate presented by the server for TLS communication
// stored as an atomic reference
serverCertificate atomic.Value
// Key used by the server for TLS communication
serverKeyPEM []byte
// lock to protect concurrent access to append / remove
lock *sync.Mutex
// Set of PEM-encoded X509 certificate authorities used to populate
// the tlsConfig.ClientCAs indexed by subject
clientRootCAs map[string]*x509.Certificate
// TLS configuration used by the grpc server
tlsConfig *tls.Config
}
// NewGRPCServer creates a new implementation of a GRPCServer given a
// listen address
func NewGRPCServer(address string, serverConfig ServerConfig) (*GRPCServer, error) {
if address == "" {
return nil, errors.New("Missing address parameter")
}
//create our listener
lis, err := net.Listen("tcp", address)
if err != nil {
return nil, err
}
return NewGRPCServerFromListener(lis, serverConfig)
}
// NewGRPCServerFromListener creates a new implementation of a GRPCServer given
// an existing net.Listener instance using default keepalive
func NewGRPCServerFromListener(listener net.Listener, serverConfig ServerConfig) (*GRPCServer, error) {
grpcServer := &GRPCServer{
address: listener.Addr().String(),
listener: listener,
lock: &sync.Mutex{},
}
//set up our server options
var serverOpts []grpc.ServerOption
//check SecOpts
var secureConfig SecureOptions
if serverConfig.SecOpts != nil {
secureConfig = *serverConfig.SecOpts
}
if secureConfig.UseTLS {
//both key and cert are required
if secureConfig.Key != nil && secureConfig.Certificate != nil {
//load server public and private keys
cert, err := tls.X509KeyPair(secureConfig.Certificate, secureConfig.Key)
if err != nil {
return nil, err
}
grpcServer.serverCertificate.Store(cert)
//set up our TLS config
if len(secureConfig.CipherSuites) == 0 {
secureConfig.CipherSuites = DefaultTLSCipherSuites
}
getCert := func(_ *tls.ClientHelloInfo) (*tls.Certificate, error) {
cert := grpcServer.serverCertificate.Load().(tls.Certificate)
return &cert, nil
}
//base server certificate
grpcServer.tlsConfig = &tls.Config{
VerifyPeerCertificate: secureConfig.VerifyCertificate,
GetCertificate: getCert,
SessionTicketsDisabled: true,
CipherSuites: secureConfig.CipherSuites,
}
grpcServer.tlsConfig.ClientAuth = tls.RequestClientCert
//check if client authentication is required
if secureConfig.RequireClientCert {
//require TLS client auth
grpcServer.tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
//if we have client root CAs, create a certPool
if len(secureConfig.ClientRootCAs) > 0 {
grpcServer.clientRootCAs = make(map[string]*x509.Certificate)
grpcServer.tlsConfig.ClientCAs = x509.NewCertPool()
for _, clientRootCA := range secureConfig.ClientRootCAs {
err = grpcServer.appendClientRootCA(clientRootCA)
if err != nil {
return nil, err
}
}
}
}
// create credentials and add to server options
creds := NewServerTransportCredentials(grpcServer.tlsConfig, serverConfig.Logger)
serverOpts = append(serverOpts, grpc.Creds(creds))
} else {
return nil, errors.New("serverConfig.SecOpts must contain both Key and Certificate when UseTLS is true")
}
}
// set max send and recv msg sizes
serverOpts = append(serverOpts, grpc.MaxSendMsgSize(MaxSendMsgSize))
serverOpts = append(serverOpts, grpc.MaxRecvMsgSize(MaxRecvMsgSize))
// set the keepalive options
serverOpts = append(serverOpts, ServerKeepaliveOptions(serverConfig.KaOpts)...)
// set connection timeout
if serverConfig.ConnectionTimeout <= 0 {
serverConfig.ConnectionTimeout = DefaultConnectionTimeout
}
serverOpts = append(
serverOpts,
grpc.ConnectionTimeout(serverConfig.ConnectionTimeout))
// set the interceptors
if len(serverConfig.StreamInterceptors) > 0 {
serverOpts = append(
serverOpts,
grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(serverConfig.StreamInterceptors...)),
)
}
if len(serverConfig.UnaryInterceptors) > 0 {
serverOpts = append(
serverOpts,
grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(serverConfig.UnaryInterceptors...)),
)
}
if serverConfig.MetricsProvider != nil {
sh := NewServerStatsHandler(serverConfig.MetricsProvider)
serverOpts = append(serverOpts, grpc.StatsHandler(sh))
}
grpcServer.server = grpc.NewServer(serverOpts...)
return grpcServer, nil
}
// SetServerCertificate assigns the current TLS certificate to be the peer's server certificate
func (gServer *GRPCServer) SetServerCertificate(cert tls.Certificate) {
gServer.serverCertificate.Store(cert)
}
// Address returns the listen address for this GRPCServer instance
func (gServer *GRPCServer) Address() string {
return gServer.address
}
// Listener returns the net.Listener for the GRPCServer instance
func (gServer *GRPCServer) Listener() net.Listener {
return gServer.listener
}
// Server returns the grpc.Server for the GRPCServer instance
func (gServer *GRPCServer) Server() *grpc.Server {
return gServer.server
}
// ServerCertificate returns the tls.Certificate used by the grpc.Server
func (gServer *GRPCServer) ServerCertificate() tls.Certificate {
return gServer.serverCertificate.Load().(tls.Certificate)
}
// TLSEnabled is a flag indicating whether or not TLS is enabled for the
// GRPCServer instance
func (gServer *GRPCServer) TLSEnabled() bool {
return gServer.tlsConfig != nil
}
// MutualTLSRequired is a flag indicating whether or not client certificates
// are required for this GRPCServer instance
func (gServer *GRPCServer) MutualTLSRequired() bool {
return gServer.tlsConfig != nil &&
gServer.tlsConfig.ClientAuth ==
tls.RequireAndVerifyClientCert
}
// Start starts the underlying grpc.Server
func (gServer *GRPCServer) Start() error {
return gServer.server.Serve(gServer.listener)
}
// Stop stops the underlying grpc.Server
func (gServer *GRPCServer) Stop() {
gServer.server.Stop()
}
// AppendClientRootCAs appends PEM-encoded X509 certificate authorities to
// the list of authorities used to verify client certificates
func (gServer *GRPCServer) AppendClientRootCAs(clientRoots [][]byte) error {
gServer.lock.Lock()
defer gServer.lock.Unlock()
for _, clientRoot := range clientRoots {
err := gServer.appendClientRootCA(clientRoot)
if err != nil {
return err
}
}
return nil
}
// internal function to add a PEM-encoded clientRootCA
func (gServer *GRPCServer) appendClientRootCA(clientRoot []byte) error {
errMsg := "Failed to append client root certificate(s): %s"
//convert to x509
certs, subjects, err := pemToX509Certs(clientRoot)
if err != nil {
return fmt.Errorf(errMsg, err.Error())
}
if len(certs) < 1 {
return fmt.Errorf(errMsg, "No client root certificates found")
}
for i, cert := range certs {
//first add to the ClientCAs
gServer.tlsConfig.ClientCAs.AddCert(cert)
//add it to our clientRootCAs map using subject as key
gServer.clientRootCAs[subjects[i]] = cert
}
return nil
}
// RemoveClientRootCAs removes PEM-encoded X509 certificate authorities from
// the list of authorities used to verify client certificates
func (gServer *GRPCServer) RemoveClientRootCAs(clientRoots [][]byte) error {
gServer.lock.Lock()
defer gServer.lock.Unlock()
//remove from internal map
for _, clientRoot := range clientRoots {
err := gServer.removeClientRootCA(clientRoot)
if err != nil {
return err
}
}
//create a new CertPool and populate with current clientRootCAs
certPool := x509.NewCertPool()
for _, clientRoot := range gServer.clientRootCAs {
certPool.AddCert(clientRoot)
}
//replace the current ClientCAs pool
gServer.tlsConfig.ClientCAs = certPool
return nil
}
// internal function to remove a PEM-encoded clientRootCA
func (gServer *GRPCServer) removeClientRootCA(clientRoot []byte) error {
errMsg := "Failed to remove client root certificate(s): %s"
//convert to x509
certs, subjects, err := pemToX509Certs(clientRoot)
if err != nil {
return fmt.Errorf(errMsg, err.Error())
}
if len(certs) < 1 {
return fmt.Errorf(errMsg, "No client root certificates found")
}
for i, subject := range subjects {
//remove it from our clientRootCAs map using subject as key
//check to see if we have match
if certs[i].Equal(gServer.clientRootCAs[subject]) {
delete(gServer.clientRootCAs, subject)
}
}
return nil
}
// SetClientRootCAs sets the list of authorities used to verify client
// certificates based on a list of PEM-encoded X509 certificate authorities
func (gServer *GRPCServer) SetClientRootCAs(clientRoots [][]byte) error {
gServer.lock.Lock()
defer gServer.lock.Unlock()
errMsg := "Failed to set client root certificate(s): %s"
//create a new map and CertPool
clientRootCAs := make(map[string]*x509.Certificate)
for _, clientRoot := range clientRoots {
certs, subjects, err := pemToX509Certs(clientRoot)
if err != nil {
return fmt.Errorf(errMsg, err.Error())
}
if len(certs) >= 1 {
for i, cert := range certs {
//add it to our clientRootCAs map using subject as key
clientRootCAs[subjects[i]] = cert
}
}
}
//create a new CertPool and populate with the new clientRootCAs
certPool := x509.NewCertPool()
for _, clientRoot := range clientRootCAs {
certPool.AddCert(clientRoot)
}
//replace the internal map
gServer.clientRootCAs = clientRootCAs
//replace the current ClientCAs pool
gServer.tlsConfig.ClientCAs = certPool
return nil
}