Use the same TLS cert for the server and client

This commit is contained in:
Brian Kassouf 2017-04-19 15:46:07 -07:00
parent af9ff63e9a
commit 2ab159569d
3 changed files with 39 additions and 96 deletions

View File

@ -51,20 +51,20 @@ type PluginRunner struct {
// plugin. // plugin.
func (r *PluginRunner) Run(wrapper Wrapper, pluginMap map[string]plugin.Plugin, hs plugin.HandshakeConfig, env []string) (*plugin.Client, error) { func (r *PluginRunner) Run(wrapper Wrapper, pluginMap map[string]plugin.Plugin, hs plugin.HandshakeConfig, env []string) (*plugin.Client, error) {
// Get a CA TLS Certificate // Get a CA TLS Certificate
CACertBytes, CACert, CAKey, err := GenerateCACert() certBytes, key, err := GenerateCert()
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Use CA to sign a client cert and return a configured TLS config // Use CA to sign a client cert and return a configured TLS config
clientTLSConfig, err := CreateClientTLSConfig(CACert, CAKey) clientTLSConfig, err := CreateClientTLSConfig(certBytes, key)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Use CA to sign a server cert and wrap the values in a response wrapped // Use CA to sign a server cert and wrap the values in a response wrapped
// token. // token.
wrapToken, err := WrapServerConfig(wrapper, CACertBytes, CACert, CAKey) wrapToken, err := WrapServerConfig(wrapper, certBytes, key)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -29,58 +29,19 @@ var (
PluginUnwrapTokenEnv = "VAULT_UNWRAP_TOKEN" PluginUnwrapTokenEnv = "VAULT_UNWRAP_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)
if err != nil {
return nil, nil, nil, err
}
host, err := uuid.GenerateUUID()
if err != nil {
return nil, nil, nil, err
}
host = "localhost"
template := &x509.Certificate{
Subject: pkix.Name{
CommonName: host,
},
DNSNames: []string{host},
ExtKeyUsage: []x509.ExtKeyUsage{
x509.ExtKeyUsageServerAuth,
x509.ExtKeyUsageClientAuth,
},
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment | x509.KeyUsageKeyAgreement | x509.KeyUsageCertSign,
SerialNumber: big.NewInt(mathrand.Int63()),
NotBefore: time.Now().Add(-30 * time.Second),
// 30 years of single-active uptime ought to be enough for anybody
NotAfter: time.Now().Add(262980 * time.Hour),
BasicConstraintsValid: true,
IsCA: true,
}
certBytes, err := x509.CreateCertificate(rand.Reader, template, template, key.Public(), key)
if err != nil {
return nil, nil, nil, fmt.Errorf("unable to generate replicated cluster certificate: %v", err)
}
caCert, err := x509.ParseCertificate(certBytes)
if err != nil {
return nil, nil, nil, fmt.Errorf("error parsing generated replication certificate: %v", err)
}
return certBytes, caCert, key, nil
}
// generateSignedCert is used internally to create certificates for the plugin // generateSignedCert is used internally to create certificates for the plugin
// client and server. These certs are signed by the given CA Cert and Key. // 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) { func GenerateCert() ([]byte, *ecdsa.PrivateKey, error) {
key, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
if err != nil {
return nil, nil, err
}
host, err := uuid.GenerateUUID() host, err := uuid.GenerateUUID()
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, err
} }
host = "localhost"
template := &x509.Certificate{ template := &x509.Certificate{
Subject: pkix.Name{ Subject: pkix.Name{
CommonName: host, CommonName: host,
@ -94,48 +55,38 @@ func generateSignedCert(CACert *x509.Certificate, CAKey *ecdsa.PrivateKey) ([]by
SerialNumber: big.NewInt(mathrand.Int63()), SerialNumber: big.NewInt(mathrand.Int63()),
NotBefore: time.Now().Add(-30 * time.Second), NotBefore: time.Now().Add(-30 * time.Second),
NotAfter: time.Now().Add(262980 * time.Hour), NotAfter: time.Now().Add(262980 * time.Hour),
IsCA: true,
} }
clientKey, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader) certBytes, err := x509.CreateCertificate(rand.Reader, template, template, key.Public(), key)
if err != nil { if err != nil {
return nil, nil, nil, errwrap.Wrapf("error generating client key: {{err}}", err) return nil, nil, errwrap.Wrapf("unable to generate client certificate: {{err}}", err)
} }
certBytes, err := x509.CreateCertificate(rand.Reader, template, CACert, clientKey.Public(), CAKey) return certBytes, key, nil
if err != nil {
return nil, nil, nil, errwrap.Wrapf("unable to generate client certificate: {{err}}", err)
}
clientCert, err := x509.ParseCertificate(certBytes)
if err != nil {
return nil, nil, nil, fmt.Errorf("error parsing generated replication certificate: %v", err)
}
return certBytes, clientCert, clientKey, nil
} }
// CreateClientTLSConfig creates a signed certificate and returns a configured // CreateClientTLSConfig creates a signed certificate and returns a configured
// TLS config. // TLS config.
func CreateClientTLSConfig(CACert *x509.Certificate, CAKey *ecdsa.PrivateKey) (*tls.Config, error) { func CreateClientTLSConfig(certBytes []byte, key *ecdsa.PrivateKey) (*tls.Config, error) {
clientCertBytes, clientCert, clientKey, err := generateSignedCert(CACert, CAKey) clientCert, err := x509.ParseCertificate(certBytes)
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("error parsing generated plugin certificate: %v", err)
} }
cert := tls.Certificate{ cert := tls.Certificate{
Certificate: [][]byte{clientCertBytes}, Certificate: [][]byte{certBytes},
PrivateKey: clientKey, PrivateKey: key,
Leaf: clientCert, Leaf: clientCert,
} }
clientCertPool := x509.NewCertPool() clientCertPool := x509.NewCertPool()
clientCertPool.AddCert(CACert) clientCertPool.AddCert(clientCert)
tlsConfig := &tls.Config{ tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cert}, Certificates: []tls.Certificate{cert},
RootCAs: clientCertPool, RootCAs: clientCertPool,
ClientCAs: clientCertPool, ServerName: clientCert.Subject.CommonName,
ServerName: CACert.Subject.CommonName,
MinVersion: tls.VersionTLS12, MinVersion: tls.VersionTLS12,
} }
@ -146,19 +97,14 @@ func CreateClientTLSConfig(CACert *x509.Certificate, CAKey *ecdsa.PrivateKey) (*
// WrapServerConfig is used to create a server certificate and private key, then // WrapServerConfig is used to create a server certificate and private key, then
// wrap them in an unwrap token for later retrieval by the plugin. // wrap them in an unwrap token for later retrieval by the plugin.
func WrapServerConfig(sys Wrapper, CACertBytes []byte, CACert *x509.Certificate, CAKey *ecdsa.PrivateKey) (string, error) { func WrapServerConfig(sys Wrapper, certBytes []byte, key *ecdsa.PrivateKey) (string, error) {
serverCertBytes, _, serverKey, err := generateSignedCert(CACert, CAKey) rawKey, err := x509.MarshalECPrivateKey(key)
if err != nil {
return "", err
}
rawKey, err := x509.MarshalECPrivateKey(serverKey)
if err != nil { if err != nil {
return "", err return "", err
} }
wrapToken, err := sys.ResponseWrapData(map[string]interface{}{ wrapToken, err := sys.ResponseWrapData(map[string]interface{}{
"CACert": CACertBytes, "ServerCert": certBytes,
"ServerCert": serverCertBytes,
"ServerKey": rawKey, "ServerKey": rawKey,
}, time.Second*10, true) }, time.Second*10, true)
@ -217,22 +163,6 @@ func VaultPluginTLSProvider() (*tls.Config, error) {
return nil, errors.New("error during token unwrap request secret is nil") return nil, errors.New("error during token unwrap request secret is nil")
} }
// Retrieve and parse the CA Certificate
CABytesRaw, ok := secret.Data["CACert"].(string)
if !ok {
return nil, errors.New("error unmarshalling CA certificate")
}
CABytes, err := base64.StdEncoding.DecodeString(CABytesRaw)
if err != nil {
return nil, fmt.Errorf("error parsing certificate: %v", err)
}
CACert, err := x509.ParseCertificate(CABytes)
if err != nil {
return nil, fmt.Errorf("error parsing certificate: %v", err)
}
// Retrieve and parse the server's certificate // Retrieve and parse the server's certificate
serverCertBytesRaw, ok := secret.Data["ServerCert"].(string) serverCertBytesRaw, ok := secret.Data["ServerCert"].(string)
if !ok { if !ok {
@ -267,7 +197,7 @@ func VaultPluginTLSProvider() (*tls.Config, error) {
// Add CA cert to the cert pool // Add CA cert to the cert pool
caCertPool := x509.NewCertPool() caCertPool := x509.NewCertPool()
caCertPool.AddCert(CACert) caCertPool.AddCert(serverCert)
// Build a certificate object out of the server's cert and private key. // Build a certificate object out of the server's cert and private key.
cert := tls.Certificate{ cert := tls.Certificate{

View File

@ -29,6 +29,19 @@ func StrListSubset(super, sub []string) bool {
return true return true
} }
// Parses a comma separated list of strings into a slice of strings.
// The return slice will be sorted and will not contain duplicate or
// empty items.
func ParseDedupAndSortStrings(input string, sep string) []string {
input = strings.TrimSpace(input)
parsed := []string{}
if input == "" {
// Don't return nil
return parsed
}
return RemoveDuplicates(strings.Split(input, sep), false)
}
// Parses a comma separated list of strings into a slice of strings. // Parses a comma separated list of strings into a slice of strings.
// The return slice will be sorted and will not contain duplicate or // The return slice will be sorted and will not contain duplicate or
// empty items. The values will be converted to lower case. // empty items. The values will be converted to lower case.