Skip to content

Commit

Permalink
raft: use file paths for TLS info in the retry_join block (#8894)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
calvn committed Jun 8, 2020
1 parent c5ca47e commit de0e211
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 25 deletions.
53 changes: 41 additions & 12 deletions physical/raft/raft.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,14 +118,28 @@ type LeaderJoinInfo struct {
// LeaderCACert is the CA cert of the leader node
LeaderCACert string `json:"leader_ca_cert"`

// LeaderClientCert is the client certificate for the follower node to establish
// client authentication during TLS
// LeaderClientCert is the client certificate for the follower node to
// establish client authentication during TLS
LeaderClientCert string `json:"leader_client_cert"`

// LeaderClientKey is the client key for the follower node to establish client
// authentication during TLS
// LeaderClientKey is the client key for the follower node to establish
// client authentication during TLS.
LeaderClientKey string `json:"leader_client_key"`

// LeaderCACertFile is the path on disk to the the CA cert file of the
// leader node. This should only be provided via Vault's configuration file.
LeaderCACertFile string `json:"leader_ca_cert_file"`

// LeaderClientCertFile is the path on disk to the client certificate file
// for the follower node to establish client authentication during TLS. This
// should only be provided via Vault's configuration file.
LeaderClientCertFile string `json:"leader_client_cert_file"`

// LeaderClientKeyFile is the path on disk to the client key file for the
// follower node to establish client authentication during TLS. This should
// only be provided via Vault's configuration file.
LeaderClientKeyFile string `json:"leader_client_key_file"`

// Retry indicates if the join process should automatically be retried
Retry bool `json:"-"`

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

for _, info := range leaderInfos {
info.Retry = true
var tlsConfig *tls.Config
var err error
if len(info.LeaderCACert) != 0 || len(info.LeaderClientCert) != 0 || len(info.LeaderClientKey) != 0 {
tlsConfig, err = tlsutil.ClientTLSConfig([]byte(info.LeaderCACert), []byte(info.LeaderClientCert), []byte(info.LeaderClientKey))
if err != nil {
return nil, errwrap.Wrapf(fmt.Sprintf("failed to create tls config to communicate with leader node %q: {{err}}", info.LeaderAPIAddr), err)
}
info.TLSConfig, err = parseTLSInfo(info)
if err != nil {
return nil, errwrap.Wrapf(fmt.Sprintf("failed to create tls config to communicate with leader node %q: {{err}}", info.LeaderAPIAddr), err)
}
info.TLSConfig = tlsConfig
}

return leaderInfos, nil
}

// parseTLSInfo is a helper for parses the TLS information, preferring file
// paths over raw certificate content.
func parseTLSInfo(leaderInfo *LeaderJoinInfo) (*tls.Config, error) {
var tlsConfig *tls.Config
var err error
if len(leaderInfo.LeaderCACertFile) != 0 || len(leaderInfo.LeaderClientCertFile) != 0 || len(leaderInfo.LeaderClientKeyFile) != 0 {
tlsConfig, err = tlsutil.LoadClientTLSConfig(leaderInfo.LeaderCACertFile, leaderInfo.LeaderClientCertFile, leaderInfo.LeaderClientKeyFile)
if err != nil {
return nil, err
}
} else if len(leaderInfo.LeaderCACert) != 0 || len(leaderInfo.LeaderClientCert) != 0 || len(leaderInfo.LeaderClientKey) != 0 {
tlsConfig, err = tlsutil.ClientTLSConfig([]byte(leaderInfo.LeaderCACert), []byte(leaderInfo.LeaderClientCert), []byte(leaderInfo.LeaderClientKey))
if err != nil {
return nil, err
}
}

return tlsConfig, nil
}

// EnsurePath is used to make sure a path exists
func EnsurePath(path string, dir bool) error {
if !dir {
Expand Down
51 changes: 51 additions & 0 deletions sdk/helper/tlsutil/tlsutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ func GetCipherName(cipher uint16) (string, error) {
return "", fmt.Errorf("unsupported cipher %d", cipher)
}

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

// LoadClientTLSConfig loads and parse the CA certificate, and optionally a
// public/private client certificate key pair. The certificates must be in PEM
// encoded format.
func LoadClientTLSConfig(caCert, clientCert, clientKey string) (*tls.Config, error) {
var tlsConfig *tls.Config
var pool *x509.CertPool

switch {
case len(caCert) != 0:
// Valid
case len(clientCert) != 0 && len(clientKey) != 0:
// Valid
default:
return nil, ErrInvalidCertParams
}

if len(caCert) != 0 {
pool = x509.NewCertPool()

data, err := ioutil.ReadFile(caCert)
if err != nil {
return nil, errwrap.Wrapf("failed to read CA file: {{err}}", err)
}

if !pool.AppendCertsFromPEM(data) {
return nil, fmt.Errorf("failed to parse CA certificate")
}
}

tlsConfig = &tls.Config{
RootCAs: pool,
ClientAuth: tls.RequireAndVerifyClientCert,
MinVersion: tls.VersionTLS12,
}

var cert tls.Certificate
var err error
if len(clientCert) != 0 && len(clientKey) != 0 {
cert, err = tls.LoadX509KeyPair(clientCert, clientKey)
if err != nil {
return nil, err
}
tlsConfig.Certificates = []tls.Certificate{cert}
}
tlsConfig.BuildNameToCertificate()

return tlsConfig, nil
}

func SetupTLSConfig(conf map[string]string, address string) (*tls.Config, error) {
serverName, _, err := net.SplitHostPort(address)
switch {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

42 changes: 29 additions & 13 deletions website/pages/docs/configuration/storage/raft.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,29 @@ set `disable_mlock` to `true`, and to disable memory swapping on the system.

### `retry_join` stanza

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

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

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

- `leader_client_key` `(string: "")` - Client key for the follower node to establish client authentication with the possible leader node
- `leader_client_key_file` `(string: "")` - File path to the client key for the
follower node to establish client authentication with the possible leader node.

- `leader_ca_cert` `(string: "")` - CA cert of the possible leader node.

- `leader_client_cert` `(string: "")` - Client certificate for the follower node
to establish client authentication with the possible leader node.

- `leader_client_key` `(string: "")` - Client key for the follower node to
establish client authentication with the possible leader node.

Each `retry_join` block may provide TLS certificates via file paths or as a
single-line certificate string value with newlines delimited by `\n`, but not a
combination of both.

Example Configuration:
```
Expand All @@ -111,21 +127,21 @@ storage "raft" {
node_id = "node1"
retry_join {
leader_api_addr = "http://127.0.0.2:8200"
leader_ca_cert = "/path/to/ca1"
leader_client_cert = "/path/to/client/cert1"
leader_client_key = "/path/to/client/key1"
leader_ca_cer_file = "/path/to/ca1"
leader_client_cert_file = "/path/to/client/cert1"
leader_client_key_file = "/path/to/client/key1"
}
retry_join {
leader_api_addr = "http://127.0.0.3:8200"
leader_ca_cert = "/path/to/ca2"
leader_client_cert = "/path/to/client/cert2"
leader_client_key = "/path/to/client/key2"
leader_ca_cert_file = "/path/to/ca2"
leader_client_cert_file = "/path/to/client/cert2"
leader_client_key_file = "/path/to/client/key2"
}
retry_join {
leader_api_addr = "http://127.0.0.4:8200"
leader_ca_cert = "/path/to/ca3"
leader_client_cert = "/path/to/client/cert3"
leader_client_key = "/path/to/client/key3"
leader_ca_cert_file = "/path/to/ca3"
leader_client_cert_file = "/path/to/client/cert3"
leader_client_key_file = "/path/to/client/key3"
}
}
```
Expand Down

0 comments on commit de0e211

Please sign in to comment.