-
Notifications
You must be signed in to change notification settings - Fork 2.8k
/
server.go
127 lines (115 loc) · 4.24 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
// SPDX-License-Identifier: Apache-2.0
// Copyright Authors of Cilium
package certloader
import (
"crypto/tls"
"errors"
"github.com/sirupsen/logrus"
)
// ServerConfigBuilder creates tls.Config to be used as TLS server.
type ServerConfigBuilder interface {
IsMutualTLS() bool
ServerConfig(base *tls.Config) *tls.Config
}
// WatchedServerConfig is a ServerConfigBuilder backed up by files to be
// watched for changes. The tls.Config created will use the latest CA and
// keypair on each TLS handshake, allowing for smooth TLS configuration
// rotation.
type WatchedServerConfig struct {
*Watcher
log logrus.FieldLogger
}
var (
// ErrMissingCertFile is returned when the certificate file is missing.
ErrMissingCertFile = errors.New("certificate file path is required")
// ErrMissingPrivkeyFile is returned when the private key file is missing.
ErrMissingPrivkeyFile = errors.New("private key file path is required")
)
// NewWatchedServerConfig returns a WatchedServerConfig configured with the
// provided files. both certFile and privkeyFile must be provided. To configure
// a mTLS capable ServerConfigBuilder, caFiles must contains at least one file
// path.
func NewWatchedServerConfig(log logrus.FieldLogger, caFiles []string, certFile, privkeyFile string) (*WatchedServerConfig, error) {
if certFile == "" {
return nil, ErrMissingCertFile
}
if privkeyFile == "" {
return nil, ErrMissingPrivkeyFile
}
w, err := NewWatcher(log, caFiles, certFile, privkeyFile)
if err != nil {
return nil, err
}
c := &WatchedServerConfig{
Watcher: w,
log: log,
}
return c, nil
}
// FutureWatchedServerConfig returns a channel where exactly one
// WatchedServerConfig will be sent once the given files are ready and loaded.
// This can be useful when the file paths are well-known, but the files
// themselves don't exist yet. both certFile and privkeyFile must be provided.
// To configure a mTLS capable ServerConfigBuilder, caFiles must contains at
// least one file path.
func FutureWatchedServerConfig(log logrus.FieldLogger, caFiles []string, certFile, privkeyFile string) (<-chan *WatchedServerConfig, error) {
if certFile == "" {
return nil, ErrMissingCertFile
}
if privkeyFile == "" {
return nil, ErrMissingPrivkeyFile
}
ew, err := FutureWatcher(log, caFiles, certFile, privkeyFile)
if err != nil {
return nil, err
}
res := make(chan *WatchedServerConfig)
go func(res chan<- *WatchedServerConfig) {
defer close(res)
if watcher, ok := <-ew; ok {
res <- &WatchedServerConfig{
Watcher: watcher,
log: log,
}
}
}(res)
return res, nil
}
// IsMutualTLS implement ServerConfigBuilder.
func (c *WatchedServerConfig) IsMutualTLS() bool {
return c.HasCustomCA()
}
// ServerConfig implement ServerConfigBuilder.
func (c *WatchedServerConfig) ServerConfig(base *tls.Config) *tls.Config {
// We return a tls.Config having only the GetConfigForClient member set.
// When a client initialize a TLS handshake, this function will be called
// and the tls.Config returned by GetConfigForClient will be used. This
// mechanism allow us to reload the certificates transparently between two
// clients connections without having to restart the server.
// See also the discussion at https://github.com/golang/go/issues/16066.
return &tls.Config{
GetConfigForClient: func(_ *tls.ClientHelloInfo) (*tls.Config, error) {
keypair, caCertPool := c.KeypairAndCACertPool()
tlsConfig := base.Clone()
tlsConfig.Certificates = []tls.Certificate{*keypair}
if c.IsMutualTLS() {
// We've been configured to serve mTLS, so setup the ClientCAs
// accordingly.
tlsConfig.ClientCAs = caCertPool
// The caller may have its own desire about the handshake
// ClientAuthType. We honor it unless its tls.NoClientCert (the
// default zero value) as we are configured to serve mTLS.
if tlsConfig.ClientAuth == tls.NoClientCert {
tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
}
}
c.log.WithField("keypair-sn", keypairId(keypair)).
Debug("Server tls handshake")
return tlsConfig, nil
},
// NOTE: this MinVersion is not used as this tls.Config will be
// overridden by the one returned by GetConfigForClient. The effective
// MinVersion must be set by the provided base TLS configuration.
MinVersion: tls.VersionTLS13,
}
}