-
Notifications
You must be signed in to change notification settings - Fork 0
/
server.go
364 lines (318 loc) · 10.9 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
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package comm
import (
"crypto/tls"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"net"
"sync"
"google.golang.org/grpc"
)
//A SecureServerConfig structure is used to configure security (e.g. TLS) for a
//GRPCServer instance
type SecureServerConfig struct {
//PEM-encoded X509 public key to be used by the server for TLS communication
ServerCertificate []byte
//PEM-encoded private key to be used by the server for TLS communication
ServerKey []byte
//Set of PEM-encoded X509 certificate authorities to optionally send
//as part of the server handshake
ServerRootCAs [][]byte
//Set of PEM-encoded X509 certificate authorities to use when verifying
//client certificates
ClientRootCAs [][]byte
//Whether or not to use TLS for communication
UseTLS bool
//Whether or not TLS client must present certificates for authentication
RequireClientCert bool
}
//GRPCServer defines an interface representing a GRPC-based server
type GRPCServer interface {
//Address returns the listen address for the GRPCServer
Address() string
//Start starts the underlying grpc.Server
Start() error
//Stop stops the underlying grpc.Server
Stop()
//Server returns the grpc.Server instance for the GRPCServer
Server() *grpc.Server
//Listener returns the net.Listener instance for the GRPCServer
Listener() net.Listener
//ServerCertificate returns the tls.Certificate used by the grpc.Server
ServerCertificate() tls.Certificate
//TLSEnabled is a flag indicating whether or not TLS is enabled for this
//GRPCServer instance
TLSEnabled() bool
//AppendClientRootCAs appends PEM-encoded X509 certificate authorities to
//the list of authorities used to verify client certificates
AppendClientRootCAs(clientRoots [][]byte) error
//RemoveClientRootCAs removes PEM-encoded X509 certificate authorities from
//the list of authorities used to verify client certificates
RemoveClientRootCAs(clientRoots [][]byte) error
//SetClientRootCAs sets the list of authorities used to verify client
//certificates based on a list of PEM-encoded X509 certificate authorities
SetClientRootCAs(clientRoots [][]byte) error
}
type grpcServerImpl 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
serverCertificate tls.Certificate
//Key used by the server for TLS communication
serverKeyPEM []byte
//List of certificate authorities to optionally pass to the client during
//the TLS handshake
serverRootCAs []tls.Certificate
//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
//Is TLS enabled?
tlsEnabled bool
}
//NewGRPCServer creates a new implementation of a GRPCServer given a
//listen address.
func NewGRPCServer(address string, secureConfig SecureServerConfig) (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, secureConfig)
}
//NewGRPCServerFromListener creates a new implementation of a GRPCServer given
//an existing net.Listener instance.
func NewGRPCServerFromListener(listener net.Listener, secureConfig SecureServerConfig) (GRPCServer, error) {
grpcServer := &grpcServerImpl{
address: listener.Addr().String(),
listener: listener,
lock: &sync.Mutex{},
}
//set up our server options
var serverOpts []grpc.ServerOption
//check secureConfig
if secureConfig.UseTLS {
//both key and cert are required
if secureConfig.ServerKey != nil && secureConfig.ServerCertificate != nil {
grpcServer.tlsEnabled = true
//load server public and private keys
cert, err := tls.X509KeyPair(secureConfig.ServerCertificate, secureConfig.ServerKey)
if err != nil {
return nil, err
}
grpcServer.serverCertificate = cert
//set up our TLS config
//base server certificate
certificates := []tls.Certificate{grpcServer.serverCertificate}
grpcServer.tlsConfig = &tls.Config{
Certificates: certificates,
SessionTicketsDisabled: true,
}
grpcServer.tlsConfig.ClientAuth = tls.RequestClientCert
//checkif 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)
serverOpts = append(serverOpts, grpc.Creds(creds))
} else {
return nil, errors.New("secureConfig must contain both ServerKey and " +
"ServerCertificate 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()...)
grpcServer.server = grpc.NewServer(serverOpts...)
return grpcServer, nil
}
//Address returns the listen address for this GRPCServer instance
func (gServer *grpcServerImpl) Address() string {
return gServer.address
}
//Listener returns the net.Listener for the GRPCServer instance
func (gServer *grpcServerImpl) Listener() net.Listener {
return gServer.listener
}
//Server returns the grpc.Server for the GRPCServer instance
func (gServer *grpcServerImpl) Server() *grpc.Server {
return gServer.server
}
//ServerCertificate returns the tls.Certificate used by the grpc.Server
func (gServer *grpcServerImpl) ServerCertificate() tls.Certificate {
return gServer.serverCertificate
}
//TLSEnabled is a flag indicating whether or not TLS is enabled for the
//GRPCServer instance
func (gServer *grpcServerImpl) TLSEnabled() bool {
return gServer.tlsEnabled
}
//Start starts the underlying grpc.Server
func (gServer *grpcServerImpl) Start() error {
return gServer.server.Serve(gServer.listener)
}
//Stop stops the underlying grpc.Server
func (gServer *grpcServerImpl) Stop() {
gServer.server.Stop()
}
//AppendClientRootCAs appends PEM-encoded X509 certificate authorities to
//the list of authorities used to verify client certificates
func (gServer *grpcServerImpl) 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 *grpcServerImpl) 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 *grpcServerImpl) 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 *grpcServerImpl) 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 *grpcServerImpl) 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
}
//utility function to parse PEM-encoded certs
func pemToX509Certs(pemCerts []byte) ([]*x509.Certificate, []string, error) {
//it's possible that multiple certs are encoded
certs := []*x509.Certificate{}
subjects := []string{}
for len(pemCerts) > 0 {
var block *pem.Block
block, pemCerts = pem.Decode(pemCerts)
if block == nil {
break
}
/** TODO: check why msp does not add type to PEM header
if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
continue
}
*/
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, subjects, err
} else {
certs = append(certs, cert)
//extract and append the subject
subjects = append(subjects, string(cert.RawSubject))
}
}
return certs, subjects, nil
}