Skip to content

Commit bef6eae

Browse files
authored
raft: use file paths for TLS info in the retry_join block (#8894)
* raft: use file paths for TLS info in the retry_join stanza * raft: maintain backward compat for existing tls params * docs: update raft docs with new file-based TLS params * Update godoc comment, fix docs
1 parent 338a466 commit bef6eae

File tree

4 files changed

+172
-25
lines changed
  • physical/raft
  • sdk/helper/tlsutil
  • vendor/github.com/hashicorp/vault/sdk/helper/tlsutil
  • website/pages/docs/configuration/storage

4 files changed

+172
-25
lines changed

physical/raft/raft.go

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -118,14 +118,28 @@ type LeaderJoinInfo struct {
118118
// LeaderCACert is the CA cert of the leader node
119119
LeaderCACert string `json:"leader_ca_cert"`
120120

121-
// LeaderClientCert is the client certificate for the follower node to establish
122-
// client authentication during TLS
121+
// LeaderClientCert is the client certificate for the follower node to
122+
// establish client authentication during TLS
123123
LeaderClientCert string `json:"leader_client_cert"`
124124

125-
// LeaderClientKey is the client key for the follower node to establish client
126-
// authentication during TLS
125+
// LeaderClientKey is the client key for the follower node to establish
126+
// client authentication during TLS.
127127
LeaderClientKey string `json:"leader_client_key"`
128128

129+
// LeaderCACertFile is the path on disk to the the CA cert file of the
130+
// leader node. This should only be provided via Vault's configuration file.
131+
LeaderCACertFile string `json:"leader_ca_cert_file"`
132+
133+
// LeaderClientCertFile is the path on disk to the client certificate file
134+
// for the follower node to establish client authentication during TLS. This
135+
// should only be provided via Vault's configuration file.
136+
LeaderClientCertFile string `json:"leader_client_cert_file"`
137+
138+
// LeaderClientKeyFile is the path on disk to the client key file for the
139+
// follower node to establish client authentication during TLS. This should
140+
// only be provided via Vault's configuration file.
141+
LeaderClientKeyFile string `json:"leader_client_key_file"`
142+
129143
// Retry indicates if the join process should automatically be retried
130144
Retry bool `json:"-"`
131145

@@ -153,20 +167,35 @@ func (b *RaftBackend) JoinConfig() ([]*LeaderJoinInfo, error) {
153167

154168
for _, info := range leaderInfos {
155169
info.Retry = true
156-
var tlsConfig *tls.Config
157-
var err error
158-
if len(info.LeaderCACert) != 0 || len(info.LeaderClientCert) != 0 || len(info.LeaderClientKey) != 0 {
159-
tlsConfig, err = tlsutil.ClientTLSConfig([]byte(info.LeaderCACert), []byte(info.LeaderClientCert), []byte(info.LeaderClientKey))
160-
if err != nil {
161-
return nil, errwrap.Wrapf(fmt.Sprintf("failed to create tls config to communicate with leader node %q: {{err}}", info.LeaderAPIAddr), err)
162-
}
170+
info.TLSConfig, err = parseTLSInfo(info)
171+
if err != nil {
172+
return nil, errwrap.Wrapf(fmt.Sprintf("failed to create tls config to communicate with leader node %q: {{err}}", info.LeaderAPIAddr), err)
163173
}
164-
info.TLSConfig = tlsConfig
165174
}
166175

167176
return leaderInfos, nil
168177
}
169178

179+
// parseTLSInfo is a helper for parses the TLS information, preferring file
180+
// paths over raw certificate content.
181+
func parseTLSInfo(leaderInfo *LeaderJoinInfo) (*tls.Config, error) {
182+
var tlsConfig *tls.Config
183+
var err error
184+
if len(leaderInfo.LeaderCACertFile) != 0 || len(leaderInfo.LeaderClientCertFile) != 0 || len(leaderInfo.LeaderClientKeyFile) != 0 {
185+
tlsConfig, err = tlsutil.LoadClientTLSConfig(leaderInfo.LeaderCACertFile, leaderInfo.LeaderClientCertFile, leaderInfo.LeaderClientKeyFile)
186+
if err != nil {
187+
return nil, err
188+
}
189+
} else if len(leaderInfo.LeaderCACert) != 0 || len(leaderInfo.LeaderClientCert) != 0 || len(leaderInfo.LeaderClientKey) != 0 {
190+
tlsConfig, err = tlsutil.ClientTLSConfig([]byte(leaderInfo.LeaderCACert), []byte(leaderInfo.LeaderClientCert), []byte(leaderInfo.LeaderClientKey))
191+
if err != nil {
192+
return nil, err
193+
}
194+
}
195+
196+
return tlsConfig, nil
197+
}
198+
170199
// EnsurePath is used to make sure a path exists
171200
func EnsurePath(path string, dir bool) error {
172201
if !dir {

sdk/helper/tlsutil/tlsutil.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ func GetCipherName(cipher uint16) (string, error) {
7979
return "", fmt.Errorf("unsupported cipher %d", cipher)
8080
}
8181

82+
// ClientTLSConfig parses the CA certificate, and optionally a public/private
83+
// client certificate key pair. The certificates must be in PEM encoded format.
8284
func ClientTLSConfig(caCert []byte, clientCert []byte, clientKey []byte) (*tls.Config, error) {
8385
var tlsConfig *tls.Config
8486
var pool *x509.CertPool
@@ -117,6 +119,55 @@ func ClientTLSConfig(caCert []byte, clientCert []byte, clientKey []byte) (*tls.C
117119
return tlsConfig, nil
118120
}
119121

122+
// LoadClientTLSConfig loads and parse the CA certificate, and optionally a
123+
// public/private client certificate key pair. The certificates must be in PEM
124+
// encoded format.
125+
func LoadClientTLSConfig(caCert, clientCert, clientKey string) (*tls.Config, error) {
126+
var tlsConfig *tls.Config
127+
var pool *x509.CertPool
128+
129+
switch {
130+
case len(caCert) != 0:
131+
// Valid
132+
case len(clientCert) != 0 && len(clientKey) != 0:
133+
// Valid
134+
default:
135+
return nil, ErrInvalidCertParams
136+
}
137+
138+
if len(caCert) != 0 {
139+
pool = x509.NewCertPool()
140+
141+
data, err := ioutil.ReadFile(caCert)
142+
if err != nil {
143+
return nil, errwrap.Wrapf("failed to read CA file: {{err}}", err)
144+
}
145+
146+
if !pool.AppendCertsFromPEM(data) {
147+
return nil, fmt.Errorf("failed to parse CA certificate")
148+
}
149+
}
150+
151+
tlsConfig = &tls.Config{
152+
RootCAs: pool,
153+
ClientAuth: tls.RequireAndVerifyClientCert,
154+
MinVersion: tls.VersionTLS12,
155+
}
156+
157+
var cert tls.Certificate
158+
var err error
159+
if len(clientCert) != 0 && len(clientKey) != 0 {
160+
cert, err = tls.LoadX509KeyPair(clientCert, clientKey)
161+
if err != nil {
162+
return nil, err
163+
}
164+
tlsConfig.Certificates = []tls.Certificate{cert}
165+
}
166+
tlsConfig.BuildNameToCertificate()
167+
168+
return tlsConfig, nil
169+
}
170+
120171
func SetupTLSConfig(conf map[string]string, address string) (*tls.Config, error) {
121172
serverName, _, err := net.SplitHostPort(address)
122173
switch {

vendor/github.com/hashicorp/vault/sdk/helper/tlsutil/tlsutil.go

Lines changed: 51 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

website/pages/docs/configuration/storage/raft.mdx

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -96,13 +96,29 @@ time. To use Raft for HA coordination users must also use Raft for storage.
9696

9797
### `retry_join` stanza
9898

99-
- `leader_api_addr` `(string: "")` - Address of a possible leader node
99+
- `leader_api_addr` `(string: "")` - Address of a possible leader node.
100100

101-
- `leader_ca_cert` `(string: "")` - CA cert of the possible leader node
101+
- `leader_ca_cert_file` `(string: "")` - File path to the CA cert of the
102+
possible leader node.
102103

103-
- `leader_client_cert` `(string: "")` - Client certificate for the follower node to establish client authentication with the possible leader node
104+
- `leader_client_cert_file` `(string: "")` - File path to the client certificate
105+
for the follower node to establish client authentication with the possible
106+
leader node.
104107

105-
- `leader_client_key` `(string: "")` - Client key for the follower node to establish client authentication with the possible leader node
108+
- `leader_client_key_file` `(string: "")` - File path to the client key for the
109+
follower node to establish client authentication with the possible leader node.
110+
111+
- `leader_ca_cert` `(string: "")` - CA cert of the possible leader node.
112+
113+
- `leader_client_cert` `(string: "")` - Client certificate for the follower node
114+
to establish client authentication with the possible leader node.
115+
116+
- `leader_client_key` `(string: "")` - Client key for the follower node to
117+
establish client authentication with the possible leader node.
118+
119+
Each `retry_join` block may provide TLS certificates via file paths or as a
120+
single-line certificate string value with newlines delimited by `\n`, but not a
121+
combination of both.
106122

107123
Example Configuration:
108124
```
@@ -111,21 +127,21 @@ storage "raft" {
111127
node_id = "node1"
112128
retry_join {
113129
leader_api_addr = "http://127.0.0.2:8200"
114-
leader_ca_cert = "/path/to/ca1"
115-
leader_client_cert = "/path/to/client/cert1"
116-
leader_client_key = "/path/to/client/key1"
130+
leader_ca_cer_file = "/path/to/ca1"
131+
leader_client_cert_file = "/path/to/client/cert1"
132+
leader_client_key_file = "/path/to/client/key1"
117133
}
118134
retry_join {
119135
leader_api_addr = "http://127.0.0.3:8200"
120-
leader_ca_cert = "/path/to/ca2"
121-
leader_client_cert = "/path/to/client/cert2"
122-
leader_client_key = "/path/to/client/key2"
136+
leader_ca_cert_file = "/path/to/ca2"
137+
leader_client_cert_file = "/path/to/client/cert2"
138+
leader_client_key_file = "/path/to/client/key2"
123139
}
124140
retry_join {
125141
leader_api_addr = "http://127.0.0.4:8200"
126-
leader_ca_cert = "/path/to/ca3"
127-
leader_client_cert = "/path/to/client/cert3"
128-
leader_client_key = "/path/to/client/key3"
142+
leader_ca_cert_file = "/path/to/ca3"
143+
leader_client_cert_file = "/path/to/client/cert3"
144+
leader_client_key_file = "/path/to/client/key3"
129145
}
130146
}
131147
```

0 commit comments

Comments
 (0)