Comment and slight refactor of the TLS plugin helper

This commit is contained in:
Brian Kassouf 2017-03-16 14:14:49 -07:00
parent 0a52ea5c69
commit f2df4ef0e7
2 changed files with 89 additions and 45 deletions

View file

@ -1,8 +1,6 @@
package dbs package dbs
import ( import (
"crypto/tls"
"crypto/x509"
"fmt" "fmt"
"net/rpc" "net/rpc"
"os/exec" "os/exec"
@ -56,57 +54,34 @@ func newPluginClient(sys logical.SystemView, command, checksum string) (Database
"database": new(DatabasePlugin), "database": new(DatabasePlugin),
} }
CACertBytes, CACert, CAKey, err := pluginutil.GenerateX509Cert() // Get a CA TLS Certificate
CACertBytes, CACert, CAKey, err := pluginutil.GenerateCACert()
if err != nil { if err != nil {
return nil, err return nil, err
} }
clientCertBytes, clientCert, clientKey, err := pluginutil.GenerateClientCert(CACert, CAKey) // Use CA to sign a client cert and return a configured TLS config
clientTLSConfig, err := pluginutil.CreateClientTLSConfig(CACert, CAKey)
if err != nil { if err != nil {
return nil, err return nil, err
} }
/* serverCert, serverKey, err := generateClientCert(CACert, CAKey) // Use CA to sign a server cert and wrap the values in a response wrapped
if err != nil { // token.
return nil, err wrapToken, err := pluginutil.WrapServerConfig(sys, CACertBytes, CACert, CAKey)
}*/
serverKey, err := x509.MarshalECPrivateKey(CAKey)
if err != nil { if err != nil {
return nil, err return nil, err
} }
cert := tls.Certificate{
Certificate: [][]byte{clientCertBytes, CACertBytes},
PrivateKey: clientKey,
Leaf: clientCert,
}
clientCertPool := x509.NewCertPool()
clientCertPool.AddCert(CACert)
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: clientCertPool,
ClientCAs: clientCertPool,
ServerName: CACert.Subject.CommonName,
MinVersion: tls.VersionTLS12,
}
tlsConfig.BuildNameToCertificate()
wrapToken, err := sys.ResponseWrapData(map[string]interface{}{
"CACert": CACertBytes,
"ServerCert": CACertBytes,
"ServerKey": serverKey,
}, time.Second*10, true)
// Add the response wrap token to the ENV of the plugin
cmd := exec.Command(command) cmd := exec.Command(command)
cmd.Env = append(cmd.Env, fmt.Sprintf("VAULT_WRAP_TOKEN=%s", wrapToken)) cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", pluginutil.PluginUnwrapTokenEnv, wrapToken))
client := plugin.NewClient(&plugin.ClientConfig{ client := plugin.NewClient(&plugin.ClientConfig{
HandshakeConfig: handshakeConfig, HandshakeConfig: handshakeConfig,
Plugins: pluginMap, Plugins: pluginMap,
Cmd: cmd, Cmd: cmd,
TLSConfig: tlsConfig, TLSConfig: clientTLSConfig,
}) })
// Connect via RPC // Connect via RPC

View file

@ -21,9 +21,16 @@ import (
"github.com/hashicorp/errwrap" "github.com/hashicorp/errwrap"
uuid "github.com/hashicorp/go-uuid" uuid "github.com/hashicorp/go-uuid"
"github.com/hashicorp/vault/api" "github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/logical"
) )
func GenerateX509Cert() ([]byte, *x509.Certificate, *ecdsa.PrivateKey, error) { var (
PluginUnwrapTokenEnv = "VAULT_WRAP_TOKEN"
)
// GenerateCACert returns a CA cert used to later sign the certificates for the
// plugin client and server.
func GenerateCACert() ([]byte, *x509.Certificate, *ecdsa.PrivateKey, error) {
key, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader) key, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err
@ -65,7 +72,9 @@ func GenerateX509Cert() ([]byte, *x509.Certificate, *ecdsa.PrivateKey, error) {
return certBytes, caCert, key, nil return certBytes, caCert, key, nil
} }
func GenerateClientCert(CACert *x509.Certificate, CAKey *ecdsa.PrivateKey) ([]byte, *x509.Certificate, []byte, error) { // generateSignedCert is used internally to create certificates for the plugin
// client and server. These certs are signed by the given CA Cert and Key.
func generateSignedCert(CACert *x509.Certificate, CAKey *ecdsa.PrivateKey) ([]byte, *x509.Certificate, *ecdsa.PrivateKey, error) {
host, err := uuid.GenerateUUID() host, err := uuid.GenerateUUID()
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err
@ -101,22 +110,71 @@ func GenerateClientCert(CACert *x509.Certificate, CAKey *ecdsa.PrivateKey) ([]by
return nil, nil, nil, fmt.Errorf("error parsing generated replication certificate: %v", err) return nil, nil, nil, fmt.Errorf("error parsing generated replication certificate: %v", err)
} }
keyBytes, err := x509.MarshalECPrivateKey(clientKey) return certBytes, clientCert, clientKey, nil
}
// CreateClientTLSConfig creates a signed certificate and returns a configured
// TLS config.
func CreateClientTLSConfig(CACert *x509.Certificate, CAKey *ecdsa.PrivateKey) (*tls.Config, error) {
clientCertBytes, clientCert, clientKey, err := generateSignedCert(CACert, CAKey)
if err != nil { if err != nil {
return nil, nil, nil, err return nil, err
} }
return certBytes, clientCert, keyBytes, nil cert := tls.Certificate{
Certificate: [][]byte{clientCertBytes},
PrivateKey: clientKey,
Leaf: clientCert,
}
clientCertPool := x509.NewCertPool()
clientCertPool.AddCert(CACert)
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: clientCertPool,
ClientCAs: clientCertPool,
ServerName: CACert.Subject.CommonName,
MinVersion: tls.VersionTLS12,
}
tlsConfig.BuildNameToCertificate()
return tlsConfig, nil
}
// WrapServerConfig is used to create a server certificate and private key, then
// wrap them in an unwrap token for later retrieval by the plugin.
func WrapServerConfig(sys logical.SystemView, CACertBytes []byte, CACert *x509.Certificate, CAKey *ecdsa.PrivateKey) (string, error) {
serverCertBytes, _, serverKey, err := generateSignedCert(CACert, CAKey)
if err != nil {
return "", err
}
rawKey, err := x509.MarshalECPrivateKey(serverKey)
if err != nil {
return "", err
}
wrapToken, err := sys.ResponseWrapData(map[string]interface{}{
"CACert": CACertBytes,
"ServerCert": serverCertBytes,
"ServerKey": rawKey,
}, time.Second*10, true)
return wrapToken, err
} }
// VaultPluginTLSProvider is run inside a plugin and retrives the response // VaultPluginTLSProvider is run inside a plugin and retrives the response
// wrapped TLS certificate from vault. It returns a configured tlsConfig. // wrapped TLS certificate from vault. It returns a configured TLS Config.
func VaultPluginTLSProvider() (*tls.Config, error) { func VaultPluginTLSProvider() (*tls.Config, error) {
unwrapToken := os.Getenv("VAULT_WRAP_TOKEN") unwrapToken := os.Getenv(PluginUnwrapTokenEnv)
// Ensure unwrap token is a JWT
if strings.Count(unwrapToken, ".") != 2 { if strings.Count(unwrapToken, ".") != 2 {
return nil, errors.New("Could not parse unwraptoken") return nil, errors.New("Could not parse unwraptoken")
} }
// Parse the JWT and retrieve the vault address
wt, err := jws.ParseJWT([]byte(unwrapToken)) wt, err := jws.ParseJWT([]byte(unwrapToken))
if err != nil { if err != nil {
return nil, errors.New(fmt.Sprintf("error decoding token: %s", err)) return nil, errors.New(fmt.Sprintf("error decoding token: %s", err))
@ -142,6 +200,7 @@ func VaultPluginTLSProvider() (*tls.Config, error) {
return nil, errors.New(fmt.Sprintf("error parsing the vault address: %s", err)) return nil, errors.New(fmt.Sprintf("error parsing the vault address: %s", err))
} }
// Unwrap the token
clientConf := api.DefaultConfig() clientConf := api.DefaultConfig()
clientConf.Address = vaultAddr clientConf.Address = vaultAddr
client, err := api.NewClient(clientConf) client, err := api.NewClient(clientConf)
@ -154,9 +213,10 @@ func VaultPluginTLSProvider() (*tls.Config, error) {
return nil, errwrap.Wrapf("error during token unwrap request: {{err}}", err) return nil, errwrap.Wrapf("error during token unwrap request: {{err}}", err)
} }
// Retrieve and parse the CA Certificate
CABytesRaw, ok := secret.Data["CACert"].(string) CABytesRaw, ok := secret.Data["CACert"].(string)
if !ok { if !ok {
return nil, errors.New("error unmarshalling certificate") return nil, errors.New("error unmarshalling CA certificate")
} }
CABytes, err := base64.StdEncoding.DecodeString(CABytesRaw) CABytes, err := base64.StdEncoding.DecodeString(CABytesRaw)
@ -169,6 +229,7 @@ func VaultPluginTLSProvider() (*tls.Config, error) {
return nil, fmt.Errorf("error parsing certificate: %v", err) return nil, fmt.Errorf("error parsing certificate: %v", err)
} }
// Retrieve and parse the server's certificate
serverCertBytesRaw, ok := secret.Data["ServerCert"].(string) serverCertBytesRaw, ok := secret.Data["ServerCert"].(string)
if !ok { if !ok {
return nil, errors.New("error unmarshalling certificate") return nil, errors.New("error unmarshalling certificate")
@ -184,19 +245,27 @@ func VaultPluginTLSProvider() (*tls.Config, error) {
return nil, fmt.Errorf("error parsing certificate: %v", err) return nil, fmt.Errorf("error parsing certificate: %v", err)
} }
serverKeyRaw, ok := secret.Data["ServerKey"].(string) // Retrieve and parse the server's private key
serverKeyB64, ok := secret.Data["ServerKey"].(string)
if !ok { if !ok {
return nil, errors.New("error unmarshalling certificate") return nil, errors.New("error unmarshalling certificate")
} }
serverKey, err := base64.StdEncoding.DecodeString(serverKeyRaw) serverKeyRaw, err := base64.StdEncoding.DecodeString(serverKeyB64)
if err != nil { if err != nil {
return nil, fmt.Errorf("error parsing certificate: %v", err) return nil, fmt.Errorf("error parsing certificate: %v", err)
} }
serverKey, err := x509.ParseECPrivateKey(serverKeyRaw)
if err != nil {
return nil, fmt.Errorf("error parsing certificate: %v", err)
}
// Add CA cert to the cert pool
caCertPool := x509.NewCertPool() caCertPool := x509.NewCertPool()
caCertPool.AddCert(CACert) caCertPool.AddCert(CACert)
// Build a certificate object out of the server's cert and private key.
cert := tls.Certificate{ cert := tls.Certificate{
Certificate: [][]byte{serverCertBytes}, Certificate: [][]byte{serverCertBytes},
PrivateKey: serverKey, PrivateKey: serverKey,