From 2ab159569d1ff3725036b42a2346a1e518029423 Mon Sep 17 00:00:00 2001 From: Brian Kassouf Date: Wed, 19 Apr 2017 15:46:07 -0700 Subject: [PATCH] Use the same TLS cert for the server and client --- helper/pluginutil/runner.go | 6 +- helper/pluginutil/tls.go | 116 +++++++----------------------------- helper/strutil/strutil.go | 13 ++++ 3 files changed, 39 insertions(+), 96 deletions(-) diff --git a/helper/pluginutil/runner.go b/helper/pluginutil/runner.go index a57abad0e..bbc5ab99b 100644 --- a/helper/pluginutil/runner.go +++ b/helper/pluginutil/runner.go @@ -51,20 +51,20 @@ type PluginRunner struct { // plugin. func (r *PluginRunner) Run(wrapper Wrapper, pluginMap map[string]plugin.Plugin, hs plugin.HandshakeConfig, env []string) (*plugin.Client, error) { // Get a CA TLS Certificate - CACertBytes, CACert, CAKey, err := GenerateCACert() + certBytes, key, err := GenerateCert() if err != nil { return nil, err } // 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 { return nil, err } // Use CA to sign a server cert and wrap the values in a response wrapped // token. - wrapToken, err := WrapServerConfig(wrapper, CACertBytes, CACert, CAKey) + wrapToken, err := WrapServerConfig(wrapper, certBytes, key) if err != nil { return nil, err } diff --git a/helper/pluginutil/tls.go b/helper/pluginutil/tls.go index c7aa42ee6..d4c0946e4 100644 --- a/helper/pluginutil/tls.go +++ b/helper/pluginutil/tls.go @@ -29,58 +29,19 @@ var ( 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 // 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() if err != nil { - return nil, nil, nil, err + return nil, nil, err } - host = "localhost" + template := &x509.Certificate{ Subject: pkix.Name{ CommonName: host, @@ -94,48 +55,38 @@ func generateSignedCert(CACert *x509.Certificate, CAKey *ecdsa.PrivateKey) ([]by SerialNumber: big.NewInt(mathrand.Int63()), NotBefore: time.Now().Add(-30 * time.Second), 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 { - 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) - 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 + return certBytes, key, 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) +func CreateClientTLSConfig(certBytes []byte, key *ecdsa.PrivateKey) (*tls.Config, error) { + clientCert, err := x509.ParseCertificate(certBytes) if err != nil { - return nil, err + return nil, fmt.Errorf("error parsing generated plugin certificate: %v", err) } cert := tls.Certificate{ - Certificate: [][]byte{clientCertBytes}, - PrivateKey: clientKey, + Certificate: [][]byte{certBytes}, + PrivateKey: key, Leaf: clientCert, } clientCertPool := x509.NewCertPool() - clientCertPool.AddCert(CACert) + clientCertPool.AddCert(clientCert) tlsConfig := &tls.Config{ Certificates: []tls.Certificate{cert}, RootCAs: clientCertPool, - ClientCAs: clientCertPool, - ServerName: CACert.Subject.CommonName, + ServerName: clientCert.Subject.CommonName, 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 // 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) { - serverCertBytes, _, serverKey, err := generateSignedCert(CACert, CAKey) - if err != nil { - return "", err - } - rawKey, err := x509.MarshalECPrivateKey(serverKey) +func WrapServerConfig(sys Wrapper, certBytes []byte, key *ecdsa.PrivateKey) (string, error) { + rawKey, err := x509.MarshalECPrivateKey(key) if err != nil { return "", err } wrapToken, err := sys.ResponseWrapData(map[string]interface{}{ - "CACert": CACertBytes, - "ServerCert": serverCertBytes, + "ServerCert": certBytes, "ServerKey": rawKey, }, 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") } - // 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 serverCertBytesRaw, ok := secret.Data["ServerCert"].(string) if !ok { @@ -267,7 +197,7 @@ func VaultPluginTLSProvider() (*tls.Config, error) { // Add CA cert to the cert pool caCertPool := x509.NewCertPool() - caCertPool.AddCert(CACert) + caCertPool.AddCert(serverCert) // Build a certificate object out of the server's cert and private key. cert := tls.Certificate{ diff --git a/helper/strutil/strutil.go b/helper/strutil/strutil.go index 7c7f64d3d..986928e0e 100644 --- a/helper/strutil/strutil.go +++ b/helper/strutil/strutil.go @@ -29,6 +29,19 @@ func StrListSubset(super, sub []string) bool { 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. // The return slice will be sorted and will not contain duplicate or // empty items. The values will be converted to lower case.