mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-01 02:57:59 +00:00
Move reload and listenerutil into sdk
This commit is contained in:
@@ -22,6 +22,8 @@ require (
|
||||
github.com/hashicorp/go-version v1.1.0
|
||||
github.com/hashicorp/golang-lru v0.5.1
|
||||
github.com/hashicorp/hcl v1.0.0
|
||||
github.com/jefferai/isbadcipher v0.0.0-20190226160619-51d2077c035f
|
||||
github.com/mitchellh/cli v1.0.0
|
||||
github.com/mitchellh/copystructure v1.0.0
|
||||
github.com/mitchellh/go-testing-interface v1.0.0
|
||||
github.com/mitchellh/mapstructure v1.1.2
|
||||
|
||||
@@ -31,6 +31,7 @@ github.com/aws/aws-sdk-go v1.25.37 h1:gBtB/F3dophWpsUQKN/Kni+JzYEH2mGHF4hWNtfED1
|
||||
github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
|
||||
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
||||
@@ -108,6 +109,8 @@ github.com/hashicorp/vault/sdk v0.1.14-0.20191108161836-82f2b5571044/go.mod h1:P
|
||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ=
|
||||
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||
github.com/jefferai/isbadcipher v0.0.0-20190226160619-51d2077c035f h1:E87tDTVS5W65euzixn7clSzK66puSt1H4I5SC0EmHH4=
|
||||
github.com/jefferai/isbadcipher v0.0.0-20190226160619-51d2077c035f/go.mod h1:3J2qVK16Lq8V+wfiL2lPeDZ7UWMxk5LemerHa1p6N00=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
@@ -124,6 +127,7 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
|
||||
github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
|
||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mitchellh/cli v1.0.0 h1:iGBIsUe3+HZ/AD/Vd7DErOt5sU9fa8Uj7A2s1aggv1Y=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
|
||||
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
|
||||
@@ -148,6 +152,7 @@ github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1 h1:ccV59UEOTzVDnDUEFdT95ZzHVZ+5+158q8+SJb2QV5w=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
|
||||
271
sdk/helper/listenerutil/listener.go
Normal file
271
sdk/helper/listenerutil/listener.go
Normal file
@@ -0,0 +1,271 @@
|
||||
package listenerutil
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
osuser "os/user"
|
||||
"strconv"
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/hashicorp/vault/sdk/helper/parseutil"
|
||||
"github.com/hashicorp/vault/sdk/helper/reload"
|
||||
"github.com/hashicorp/vault/sdk/helper/tlsutil"
|
||||
"github.com/jefferai/isbadcipher"
|
||||
"github.com/mitchellh/cli"
|
||||
)
|
||||
|
||||
type UnixSocketsConfig struct {
|
||||
User string `hcl:"user"`
|
||||
Mode string `hcl:"mode"`
|
||||
Group string `hcl:"group"`
|
||||
}
|
||||
|
||||
// rmListener is an implementation of net.Listener that forwards most
|
||||
// calls to the listener but also removes a file as part of the close. We
|
||||
// use this to cleanup the unix domain socket on close.
|
||||
type rmListener struct {
|
||||
net.Listener
|
||||
Path string
|
||||
}
|
||||
|
||||
func (l *rmListener) Close() error {
|
||||
// Close the listener itself
|
||||
if err := l.Listener.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Remove the file
|
||||
return os.Remove(l.Path)
|
||||
}
|
||||
|
||||
func UnixSocketListener(path string, unixSocketsConfig *UnixSocketsConfig) (net.Listener, error) {
|
||||
if err := os.Remove(path); err != nil && !os.IsNotExist(err) {
|
||||
return nil, fmt.Errorf("failed to remove socket file: %v", err)
|
||||
}
|
||||
|
||||
ln, err := net.Listen("unix", path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if unixSocketsConfig != nil {
|
||||
err = setFilePermissions(path, unixSocketsConfig.User, unixSocketsConfig.Group, unixSocketsConfig.Mode)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to set file system permissions on the socket file: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap the listener in rmListener so that the Unix domain socket file is
|
||||
// removed on close.
|
||||
return &rmListener{
|
||||
Listener: ln,
|
||||
Path: path,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func WrapTLS(
|
||||
ln net.Listener,
|
||||
props map[string]string,
|
||||
config map[string]interface{},
|
||||
ui cli.Ui) (net.Listener, map[string]string, reload.ReloadFunc, *tls.Config, error) {
|
||||
props["tls"] = "disabled"
|
||||
|
||||
if v, ok := config["tls_disable"]; ok {
|
||||
disabled, err := parseutil.ParseBool(v)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, errwrap.Wrapf("invalid value for 'tls_disable': {{err}}", err)
|
||||
}
|
||||
if disabled {
|
||||
return ln, props, nil, nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
certFileRaw, ok := config["tls_cert_file"]
|
||||
if !ok {
|
||||
return nil, nil, nil, nil, fmt.Errorf("'tls_cert_file' must be set")
|
||||
}
|
||||
certFile := certFileRaw.(string)
|
||||
keyFileRaw, ok := config["tls_key_file"]
|
||||
if !ok {
|
||||
return nil, nil, nil, nil, fmt.Errorf("'tls_key_file' must be set")
|
||||
}
|
||||
keyFile := keyFileRaw.(string)
|
||||
|
||||
cg := reload.NewCertificateGetter(certFile, keyFile, "")
|
||||
if err := cg.Reload(config); err != nil {
|
||||
// We try the key without a passphrase first and if we get an incorrect
|
||||
// passphrase response, try again after prompting for a passphrase
|
||||
if errwrap.Contains(err, x509.IncorrectPasswordError.Error()) {
|
||||
var passphrase string
|
||||
passphrase, err = ui.AskSecret(fmt.Sprintf("Enter passphrase for %s:", keyFile))
|
||||
if err == nil {
|
||||
cg = reload.NewCertificateGetter(certFile, keyFile, passphrase)
|
||||
if err = cg.Reload(config); err == nil {
|
||||
goto PASSPHRASECORRECT
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, nil, nil, nil, errwrap.Wrapf("error loading TLS cert: {{err}}", err)
|
||||
}
|
||||
|
||||
PASSPHRASECORRECT:
|
||||
var tlsvers string
|
||||
tlsversRaw, ok := config["tls_min_version"]
|
||||
if !ok {
|
||||
tlsvers = "tls12"
|
||||
} else {
|
||||
tlsvers = tlsversRaw.(string)
|
||||
}
|
||||
|
||||
tlsConf := &tls.Config{}
|
||||
tlsConf.GetCertificate = cg.GetCertificate
|
||||
tlsConf.NextProtos = []string{"h2", "http/1.1"}
|
||||
tlsConf.MinVersion, ok = tlsutil.TLSLookup[tlsvers]
|
||||
if !ok {
|
||||
return nil, nil, nil, nil, fmt.Errorf("'tls_min_version' value %q not supported, please specify one of [tls10,tls11,tls12]", tlsvers)
|
||||
}
|
||||
tlsConf.ClientAuth = tls.RequestClientCert
|
||||
|
||||
if v, ok := config["tls_cipher_suites"]; ok {
|
||||
ciphers, err := tlsutil.ParseCiphers(v.(string))
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, errwrap.Wrapf("invalid value for 'tls_cipher_suites': {{err}}", err)
|
||||
}
|
||||
|
||||
// HTTP/2 with TLS 1.2 blacklists several cipher suites.
|
||||
// https://tools.ietf.org/html/rfc7540#appendix-A
|
||||
//
|
||||
// Since the CLI (net/http) automatically uses HTTP/2 with TLS 1.2,
|
||||
// we check here if all or some specified cipher suites are blacklisted.
|
||||
badCiphers := []string{}
|
||||
for _, cipher := range ciphers {
|
||||
if isbadcipher.IsBadCipher(cipher) {
|
||||
// Get the name of the current cipher.
|
||||
cipherStr, err := tlsutil.GetCipherName(cipher)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, errwrap.Wrapf("invalid value for 'tls_cipher_suites': {{err}}", err)
|
||||
}
|
||||
badCiphers = append(badCiphers, cipherStr)
|
||||
}
|
||||
}
|
||||
if len(badCiphers) == len(ciphers) {
|
||||
ui.Warn(`WARNING! All cipher suites defined by 'tls_cipher_suites' are blacklisted by the
|
||||
HTTP/2 specification. HTTP/2 communication with TLS 1.2 will not work as intended
|
||||
and Vault will be unavailable via the CLI.
|
||||
Please see https://tools.ietf.org/html/rfc7540#appendix-A for further information.`)
|
||||
} else if len(badCiphers) > 0 {
|
||||
ui.Warn(fmt.Sprintf(`WARNING! The following cipher suites defined by 'tls_cipher_suites' are
|
||||
blacklisted by the HTTP/2 specification:
|
||||
%v
|
||||
Please see https://tools.ietf.org/html/rfc7540#appendix-A for further information.`, badCiphers))
|
||||
}
|
||||
tlsConf.CipherSuites = ciphers
|
||||
}
|
||||
if v, ok := config["tls_prefer_server_cipher_suites"]; ok {
|
||||
preferServer, err := parseutil.ParseBool(v)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, errwrap.Wrapf("invalid value for 'tls_prefer_server_cipher_suites': {{err}}", err)
|
||||
}
|
||||
tlsConf.PreferServerCipherSuites = preferServer
|
||||
}
|
||||
var requireVerifyCerts bool
|
||||
var err error
|
||||
if v, ok := config["tls_require_and_verify_client_cert"]; ok {
|
||||
requireVerifyCerts, err = parseutil.ParseBool(v)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, errwrap.Wrapf("invalid value for 'tls_require_and_verify_client_cert': {{err}}", err)
|
||||
}
|
||||
if requireVerifyCerts {
|
||||
tlsConf.ClientAuth = tls.RequireAndVerifyClientCert
|
||||
}
|
||||
if tlsClientCaFile, ok := config["tls_client_ca_file"]; ok {
|
||||
caPool := x509.NewCertPool()
|
||||
data, err := ioutil.ReadFile(tlsClientCaFile.(string))
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, errwrap.Wrapf("failed to read tls_client_ca_file: {{err}}", err)
|
||||
}
|
||||
|
||||
if !caPool.AppendCertsFromPEM(data) {
|
||||
return nil, nil, nil, nil, fmt.Errorf("failed to parse CA certificate in tls_client_ca_file")
|
||||
}
|
||||
tlsConf.ClientCAs = caPool
|
||||
}
|
||||
}
|
||||
if v, ok := config["tls_disable_client_certs"]; ok {
|
||||
disableClientCerts, err := parseutil.ParseBool(v)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, errwrap.Wrapf("invalid value for 'tls_disable_client_certs': {{err}}", err)
|
||||
}
|
||||
if disableClientCerts && requireVerifyCerts {
|
||||
return nil, nil, nil, nil, fmt.Errorf("'tls_disable_client_certs' and 'tls_require_and_verify_client_cert' are mutually exclusive")
|
||||
}
|
||||
if disableClientCerts {
|
||||
tlsConf.ClientAuth = tls.NoClientCert
|
||||
}
|
||||
}
|
||||
|
||||
ln = tls.NewListener(ln, tlsConf)
|
||||
props["tls"] = "enabled"
|
||||
return ln, props, cg.Reload, tlsConf, nil
|
||||
}
|
||||
|
||||
// setFilePermissions handles configuring ownership and permissions
|
||||
// settings on a given file. All permission/ownership settings are
|
||||
// optional. If no user or group is specified, the current user/group
|
||||
// will be used. Mode is optional, and has no default (the operation is
|
||||
// not performed if absent). User may be specified by name or ID, but
|
||||
// group may only be specified by ID.
|
||||
func setFilePermissions(path string, user, group, mode string) error {
|
||||
var err error
|
||||
uid, gid := os.Getuid(), os.Getgid()
|
||||
|
||||
if user != "" {
|
||||
if uid, err = strconv.Atoi(user); err == nil {
|
||||
goto GROUP
|
||||
}
|
||||
|
||||
// Try looking up the user by name
|
||||
u, err := osuser.Lookup(user)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to look up user %q: %v", user, err)
|
||||
}
|
||||
uid, _ = strconv.Atoi(u.Uid)
|
||||
}
|
||||
|
||||
GROUP:
|
||||
if group != "" {
|
||||
if gid, err = strconv.Atoi(group); err == nil {
|
||||
goto OWN
|
||||
}
|
||||
|
||||
// Try looking up the user by name
|
||||
g, err := osuser.LookupGroup(group)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to look up group %q: %v", user, err)
|
||||
}
|
||||
gid, _ = strconv.Atoi(g.Gid)
|
||||
}
|
||||
|
||||
OWN:
|
||||
if err := os.Chown(path, uid, gid); err != nil {
|
||||
return fmt.Errorf("failed setting ownership to %d:%d on %q: %v",
|
||||
uid, gid, path, err)
|
||||
}
|
||||
|
||||
if mode != "" {
|
||||
mode, err := strconv.ParseUint(mode, 8, 32)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid mode specified: %v", mode)
|
||||
}
|
||||
if err := os.Chmod(path, os.FileMode(mode)); err != nil {
|
||||
return fmt.Errorf("failed setting permissions to %d on %q: %v",
|
||||
mode, path, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
88
sdk/helper/listenerutil/listener_test.go
Normal file
88
sdk/helper/listenerutil/listener_test.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package listenerutil
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
osuser "os/user"
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestUnixSocketListener(t *testing.T) {
|
||||
t.Run("ids", func(t *testing.T) {
|
||||
socket, err := ioutil.TempFile("", "socket")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(socket.Name())
|
||||
|
||||
uid, gid := os.Getuid(), os.Getgid()
|
||||
|
||||
u, err := osuser.LookupId(strconv.Itoa(uid))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
user := u.Username
|
||||
|
||||
g, err := osuser.LookupGroupId(strconv.Itoa(gid))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
group := g.Name
|
||||
|
||||
l, err := UnixSocketListener(socket.Name(), &UnixSocketsConfig{
|
||||
User: user,
|
||||
Group: group,
|
||||
Mode: "644",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer l.Close()
|
||||
|
||||
fi, err := os.Stat(socket.Name())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
mode, err := strconv.ParseUint("644", 8, 32)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if fi.Mode().Perm() != os.FileMode(mode) {
|
||||
t.Fatalf("failed to set permissions on the socket file")
|
||||
}
|
||||
})
|
||||
t.Run("names", func(t *testing.T) {
|
||||
socket, err := ioutil.TempFile("", "socket")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(socket.Name())
|
||||
|
||||
uid, gid := os.Getuid(), os.Getgid()
|
||||
l, err := UnixSocketListener(socket.Name(), &UnixSocketsConfig{
|
||||
User: strconv.Itoa(uid),
|
||||
Group: strconv.Itoa(gid),
|
||||
Mode: "644",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer l.Close()
|
||||
|
||||
fi, err := os.Stat(socket.Name())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
mode, err := strconv.ParseUint("644", 8, 32)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if fi.Mode().Perm() != os.FileMode(mode) {
|
||||
t.Fatalf("failed to set permissions on the socket file")
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
85
sdk/helper/reload/reload.go
Normal file
85
sdk/helper/reload/reload.go
Normal file
@@ -0,0 +1,85 @@
|
||||
package reload
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"sync"
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
)
|
||||
|
||||
// ReloadFunc are functions that are called when a reload is requested
|
||||
type ReloadFunc func(map[string]interface{}) error
|
||||
|
||||
// CertificateGetter satisfies ReloadFunc and its GetCertificate method
|
||||
// satisfies the tls.GetCertificate function signature. Currently it does not
|
||||
// allow changing paths after the fact.
|
||||
type CertificateGetter struct {
|
||||
sync.RWMutex
|
||||
|
||||
cert *tls.Certificate
|
||||
|
||||
certFile string
|
||||
keyFile string
|
||||
passphrase string
|
||||
}
|
||||
|
||||
func NewCertificateGetter(certFile, keyFile, passphrase string) *CertificateGetter {
|
||||
return &CertificateGetter{
|
||||
certFile: certFile,
|
||||
keyFile: keyFile,
|
||||
passphrase: passphrase,
|
||||
}
|
||||
}
|
||||
|
||||
func (cg *CertificateGetter) Reload(_ map[string]interface{}) error {
|
||||
certPEMBlock, err := ioutil.ReadFile(cg.certFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
keyPEMBlock, err := ioutil.ReadFile(cg.keyFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check for encrypted pem block
|
||||
keyBlock, _ := pem.Decode(keyPEMBlock)
|
||||
if keyBlock == nil {
|
||||
return errors.New("decoded PEM is blank")
|
||||
}
|
||||
|
||||
if x509.IsEncryptedPEMBlock(keyBlock) {
|
||||
keyBlock.Bytes, err = x509.DecryptPEMBlock(keyBlock, []byte(cg.passphrase))
|
||||
if err != nil {
|
||||
return errwrap.Wrapf("Decrypting PEM block failed {{err}}", err)
|
||||
}
|
||||
keyPEMBlock = pem.EncodeToMemory(keyBlock)
|
||||
}
|
||||
|
||||
cert, err := tls.X509KeyPair(certPEMBlock, keyPEMBlock)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cg.Lock()
|
||||
defer cg.Unlock()
|
||||
|
||||
cg.cert = &cert
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cg *CertificateGetter) GetCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||
cg.RLock()
|
||||
defer cg.RUnlock()
|
||||
|
||||
if cg.cert == nil {
|
||||
return nil, fmt.Errorf("nil certificate")
|
||||
}
|
||||
|
||||
return cg.cert, nil
|
||||
}
|
||||
74
sdk/helper/reload/reload_test.go
Normal file
74
sdk/helper/reload/reload_test.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package reload
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
)
|
||||
|
||||
func TestReload_KeyWithPassphrase(t *testing.T) {
|
||||
password := "password"
|
||||
cert := []byte(`-----BEGIN CERTIFICATE-----
|
||||
MIICLzCCAZgCCQCq27CeP4WhlDANBgkqhkiG9w0BAQUFADBcMQswCQYDVQQGEwJV
|
||||
UzELMAkGA1UECAwCQ0ExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xEjAQBgNVBAoM
|
||||
CUhhc2hpQ29ycDEUMBIGA1UEAwwLbXl2YXVsdC5jb20wHhcNMTcxMjEzMjEzNTM3
|
||||
WhcNMTgxMjEzMjEzNTM3WjBcMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExFjAU
|
||||
BgNVBAcMDVNhbiBGcmFuY2lzY28xEjAQBgNVBAoMCUhhc2hpQ29ycDEUMBIGA1UE
|
||||
AwwLbXl2YXVsdC5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMvsz/9l
|
||||
EJIlRG6DOw4fXdB/aJgJk2rR8cU0D8+vECIzb+MdDK0cBHtLiVpZC/RnZMdMzjGn
|
||||
Z++Fp3dEnT6CD0IjKdJcD+qSyZSjHIuYpHjnjrVlM/Le0xST7egoG+fXkSt4myzG
|
||||
ec2WK1jcZefRRGPycvMqx1yUWU76jDdFZSL5AgMBAAEwDQYJKoZIhvcNAQEFBQAD
|
||||
gYEAQfYE26FLZ9SPPU8bHNDxoxDmGrn8yJ78C490Qpix/w6gdLaBtILenrZbhpnB
|
||||
3L3okraM8mplaN2KdAcpnsr4wPv9hbYkam0coxCQEKs8ltHSBaXT6uKRWb00nkGu
|
||||
yAXDRpuPdFRqbXW3ZFC5broUrz4ujxTDKfVeIn0zpPZkv24=
|
||||
-----END CERTIFICATE-----`)
|
||||
key := []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
Proc-Type: 4,ENCRYPTED
|
||||
DEK-Info: DES-EDE3-CBC,64B032D83BD6A6DC
|
||||
|
||||
qVJ+mXEBKMkUPrQ8odHunMpPgChQUny4CX73/dAcm7O9iXIv9eXQSxj2qfgCOloj
|
||||
vthg7jYNwtRb0ydzCEnEud35zWw38K/l19/pe4ULfNXlOddlsk4XIHarBiz+KUaX
|
||||
WTbNk0H+DwdcEwhprPgpTk8gp88lZBiHCnTG/s8v/JNt+wkdqjfAp0Xbm9m+OZ7s
|
||||
hlNxZin1OuBdprBqfKWBltUALZYiIBhspMTmh+jGQSyEKNTAIBejIiRH5+xYWuOy
|
||||
xKencq8UpQMOMPR2ZiSw42dU9j8HHMgldI7KszU2FDIEFXG7aSjcxNyyybeBT+Uz
|
||||
YPoxGxSdUYWqaz50UszvHg/QWR8NlPlQc3nFAUVpGKUF9MEQCIAK8HjcpMP+IAVO
|
||||
ertp4cTa2Rpm9YeoFrY6tabvmXApXlQPw6rBn6o5KpceWG3ceOsDOsT+e3edHu9g
|
||||
SGO4hjggbRpO+dBOuwfw4rMn9X1BbqXKJcREAmrgVVSf9/s942E4YOQ+IGJPdtmY
|
||||
WHAFk8hiJepsVCA2NpwVlAD+QbPPaR2RtvYOtq3IKlWRuVQ+6dpxDsz5FlJhs2L+
|
||||
HsX6XqtwuQM8kk1hO8Gm3VeV7+b64r9kfbO8jCM18GexCYiCtig51mJW6IO42d1K
|
||||
bS1axMx/KeDc/sy7LKEbHnjnYanpGz2Wa2EWhnWAeNXD1nUfUNFPp2SsIGbCMnat
|
||||
mC4O4cO7YRl3+iJg3kHtTPGtgtCjrZcjlyBtxT2VC7SsTcTXZBWovczMIstyr4Ka
|
||||
opM24uvQT3Bc0UM0WNh3tdRFuboxDeBDh7PX/2RIoiaMuCCiRZ3O0A==
|
||||
-----END RSA PRIVATE KEY-----`)
|
||||
tempDir, err := ioutil.TempDir("", "vault-test")
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating temporary directory: %s", err)
|
||||
}
|
||||
keyFile := tempDir + "/server.key"
|
||||
certFile := tempDir + "/server.crt"
|
||||
|
||||
err = ioutil.WriteFile(certFile, cert, 0755)
|
||||
if err != nil {
|
||||
t.Fatalf("Error writing to temp file: %s", err)
|
||||
}
|
||||
err = ioutil.WriteFile(keyFile, key, 0755)
|
||||
if err != nil {
|
||||
t.Fatalf("Error writing to temp file: %s", err)
|
||||
}
|
||||
|
||||
cg := NewCertificateGetter(certFile, keyFile, "")
|
||||
err = cg.Reload(nil)
|
||||
if err == nil {
|
||||
t.Fatal("error expected")
|
||||
}
|
||||
if !errwrap.Contains(err, x509.IncorrectPasswordError.Error()) {
|
||||
t.Fatalf("expected incorrect password error, got %v", err)
|
||||
}
|
||||
|
||||
cg = NewCertificateGetter(certFile, keyFile, password)
|
||||
if err := cg.Reload(nil); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user