diff --git a/.travis.yml b/.travis.yml index 54a6b81981a7..d6e934febd06 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ services: - docker go: - - "1.10" + - "1.9" matrix: allow_failures: diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f024426de05..415b7ef4158e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,25 @@ +## 0.9.5 (Soon) + +IMPROVEMENTS: + + * auth: Allow sending default_lease_ttl and max_lease_ttl values when enabling + auth methods. [GH-4019] + * secret/database: Add list functionality to `database/config` endpoint + [GH-4026] + * physical/consul: Allow setting a specific service address [GH-3971] + +BUG FIXES: + + * auth/aws: Update libraries to fix regression verifying PKCS#7 identity + documents [GH-4014] + * listener: Revert to Go 1.9 for now to allow certificates with non-DNS names + in their DNS SANs to be used for Vault's TLS connections [GH-4028] + * replication: Fix issue with a performance secondary/DR primary node losing + its DR primary status when performing an update-primary operation + * replication: Fix issue where performance secondaries could be unable to + automatically connect to a performance primary after that performance + primary has been promoted to a DR primary from a DR secondary + ## 0.9.4 (February 20th, 2018) SECURITY: @@ -63,6 +85,10 @@ BUG FIXES: * auth/token: Token creation via the CLI no longer forces periodic token creation. Passing an explicit zero value for the period no longer create periodic tokens. [GH-3880] + * command: Fix interpreted formatting directives when printing raw fields + [GH-4005] + * command: Correctly format output when using -field and -format flags at the + same time [GH-3987] * command/rekey: Re-add lost `stored-shares` parameter [GH-3974] * command/ssh: Create and reuse the api client [GH-3909] * command/status: Fix panic when status returns 500 from leadership lookup diff --git a/Makefile b/Makefile index 6940a97771e4..65a194db791c 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ EXTERNAL_TOOLS=\ BUILD_TAGS?=vault GOFMT_FILES?=$$(find . -name '*.go' | grep -v vendor) -GO_VERSION_MIN=1.10 +GO_VERSION_MIN=1.9 default: dev diff --git a/README.md b/README.md index 3c50e6c33da1..d3fa4a931dd3 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ Developing Vault -------------------- If you wish to work on Vault itself or any of its built-in systems, you'll -first need [Go](https://www.golang.org) installed on your machine (version 1.10+ +first need [Go](https://www.golang.org) installed on your machine (version 1.9+ is *required*). For local dev first make sure Go is properly installed, including setting up a diff --git a/api/sys_auth.go b/api/sys_auth.go index 7b8674bb8bb9..937b0eafb4b9 100644 --- a/api/sys_auth.go +++ b/api/sys_auth.go @@ -91,7 +91,9 @@ type EnableAuthOptions struct { } type AuthConfigInput struct { - PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"` + DefaultLeaseTTL string `json:"default_lease_ttl" structs:"default_lease_ttl" mapstructure:"default_lease_ttl"` + MaxLeaseTTL string `json:"max_lease_ttl" structs:"max_lease_ttl" mapstructure:"max_lease_ttl"` + PluginName string `json:"plugin_name,omitempty" structs:"plugin_name,omitempty" mapstructure:"plugin_name"` } type AuthMount struct { diff --git a/builtin/credential/aws/backend_test.go b/builtin/credential/aws/backend_test.go index 438de50f472f..23c81d43d6d5 100644 --- a/builtin/credential/aws/backend_test.go +++ b/builtin/credential/aws/backend_test.go @@ -958,18 +958,30 @@ func TestBackend_PathBlacklistRoleTag(t *testing.T) { } } -// This is an acceptance test. -// Requires the following env vars: -// TEST_AWS_EC2_PKCS7 -// TEST_AWS_EC2_AMI_ID -// TEST_AWS_EC2_ACCOUNT_ID -// TEST_AWS_EC2_IAM_ROLE_ARN -// -// If the test is not being run on an EC2 instance that has access to -// credentials using EC2RoleProvider, on top of the above vars, following -// needs to be set: -// TEST_AWS_SECRET_KEY -// TEST_AWS_ACCESS_KEY +/* This is an acceptance test. + Requires the following env vars: + TEST_AWS_EC2_PKCS7 + TEST_AWS_EC2_IDENTITY_DOCUMENT + TEST_AWS_EC2_IDENTITY_DOCUMENT_SIG + TEST_AWS_EC2_AMI_ID + TEST_AWS_EC2_ACCOUNT_ID + TEST_AWS_EC2_IAM_ROLE_ARN + + If this is being run on an EC2 instance, you can set the environment vars using this bash snippet: + + export TEST_AWS_EC2_PKCS7=$(curl -s http://169.254.169.254/latest/dynamic/instance-identity/pkcs7) + export TEST_AWS_EC2_IDENTITY_DOCUMENT=$(curl -s http://169.254.169.254/latest/dynamic/instance-identity/document | base64 -w 0) + export TEST_AWS_EC2_IDENTITY_DOCUMENT_SIG=$(curl -s http://169.254.169.254/latest/dynamic/instance-identity/signature | tr -d '\n') + export TEST_AWS_EC2_AMI_ID=$(curl -s http://169.254.169.254/latest/meta-data/ami-id) + export TEST_AWS_EC2_IAM_ROLE_ARN=$(aws iam get-role --role-name $(curl -q http://169.254.169.254/latest/meta-data/iam/security-credentials/ -S -s) --query Role.Arn --output text) + export TEST_AWS_EC2_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text) + + If the test is not being run on an EC2 instance that has access to + credentials using EC2RoleProvider, on top of the above vars, following + needs to be set: + TEST_AWS_SECRET_KEY + TEST_AWS_ACCESS_KEY +*/ func TestBackendAcc_LoginWithInstanceIdentityDocAndWhitelistIdentity(t *testing.T) { // This test case should be run only when certain env vars are set and // executed as an acceptance test. @@ -983,6 +995,16 @@ func TestBackendAcc_LoginWithInstanceIdentityDocAndWhitelistIdentity(t *testing. t.Fatalf("env var TEST_AWS_EC2_PKCS7 not set") } + identityDoc := os.Getenv("TEST_AWS_EC2_IDENTITY_DOCUMENT") + if identityDoc == "" { + t.Fatalf("env var TEST_AWS_EC2_IDENTITY_DOCUMENT not set") + } + + identityDocSig := os.Getenv("TEST_AWS_EC2_IDENTITY_DOCUMENT_SIG") + if identityDocSig == "" { + t.Fatalf("env var TEST_AWS_EC2_IDENTITY_DOCUMENT_SIG not set") + } + amiID := os.Getenv("TEST_AWS_EC2_AMI_ID") if amiID == "" { t.Fatalf("env var TEST_AWS_EC2_AMI_ID not set") @@ -1131,6 +1153,18 @@ func TestBackendAcc_LoginWithInstanceIdentityDocAndWhitelistIdentity(t *testing. t.Fatalf("bad: failed to login: resp:%#v\nerr:%v", resp, err) } + // Attempt to re-login with the identity signture + delete(loginInput, "pkcs7") + loginInput["identity"] = identityDoc + loginInput["signature"] = identityDocSig + resp, err = b.HandleRequest(context.Background(), loginRequest) + if err != nil { + t.Fatal(err) + } + if resp == nil || resp.Auth == nil || resp.IsError() { + t.Fatalf("bad: failed to login: resp:%#v\nerr:%v", resp, err) + } + // verify the presence of instance_id in the response object. instanceID := resp.Auth.Metadata["instance_id"] if instanceID == "" { diff --git a/builtin/credential/cert/path_login.go b/builtin/credential/cert/path_login.go index f517cbb4cf70..51f7b59fb526 100644 --- a/builtin/credential/cert/path_login.go +++ b/builtin/credential/cert/path_login.go @@ -439,12 +439,28 @@ func validateConnState(roots *x509.CertPool, cs *tls.ConnectionState) ([][]*x509 } } - chains, err := certs[0].Verify(opts) - if err != nil { - if _, ok := err.(x509.UnknownAuthorityError); ok { - return nil, nil + var chains [][]*x509.Certificate + var err error + switch { + case len(certs[0].DNSNames) > 0: + for _, dnsName := range certs[0].DNSNames { + opts.DNSName = dnsName + chains, err = certs[0].Verify(opts) + if err != nil { + if _, ok := err.(x509.UnknownAuthorityError); ok { + return nil, nil + } + return nil, errors.New("failed to verify client's certificate: " + err.Error()) + } + } + default: + chains, err = certs[0].Verify(opts) + if err != nil { + if _, ok := err.(x509.UnknownAuthorityError); ok { + return nil, nil + } + return nil, errors.New("failed to verify client's certificate: " + err.Error()) } - return nil, errors.New("failed to verify client's certificate: " + err.Error()) } return chains, nil diff --git a/builtin/logical/pki/cert_util.go b/builtin/logical/pki/cert_util.go index be083a57e168..ee1d0f970246 100644 --- a/builtin/logical/pki/cert_util.go +++ b/builtin/logical/pki/cert_util.go @@ -1351,7 +1351,7 @@ func convertRespToPKCS8(resp *logical.Response) error { return errwrap.Wrapf("error converting response to pkcs8: error parsing previous key: {{err}}", err) } - keyData, err = x509.MarshalPKCS8PrivateKey(signer) + keyData, err = certutil.MarshalPKCS8PrivateKey(signer) if err != nil { return errwrap.Wrapf("error converting response to pkcs8: error marshaling pkcs8 key: {{err}}", err) } diff --git a/command/auth_enable.go b/command/auth_enable.go index 1cc8dbc1a7df..328524eb81cb 100644 --- a/command/auth_enable.go +++ b/command/auth_enable.go @@ -3,6 +3,7 @@ package command import ( "fmt" "strings" + "time" "github.com/hashicorp/vault/api" "github.com/mitchellh/cli" @@ -15,11 +16,13 @@ var _ cli.CommandAutocomplete = (*AuthEnableCommand)(nil) type AuthEnableCommand struct { *BaseCommand - flagDescription string - flagPath string - flagPluginName string - flagLocal bool - flagSealWrap bool + flagDescription string + flagPath string + flagDefaultLeaseTTL time.Duration + flagMaxLeaseTTL time.Duration + flagPluginName string + flagLocal bool + flagSealWrap bool } func (c *AuthEnableCommand) Synopsis() string { @@ -75,6 +78,24 @@ func (c *AuthEnableCommand) Flags() *FlagSets { "\"/auth/\".", }) + f.DurationVar(&DurationVar{ + Name: "default-lease-ttl", + Target: &c.flagDefaultLeaseTTL, + Completion: complete.PredictAnything, + Usage: "The default lease TTL for this auth method. If unspecified, " + + "this defaults to the Vault server's globally configured default lease " + + "TTL.", + }) + + f.DurationVar(&DurationVar{ + Name: "max-lease-ttl", + Target: &c.flagMaxLeaseTTL, + Completion: complete.PredictAnything, + Usage: "The maximum lease TTL for this auth method. If unspecified, " + + "this defaults to the Vault server's globally configured maximum lease " + + "TTL.", + }) + f.StringVar(&StringVar{ Name: "plugin-name", Target: &c.flagPluginName, @@ -155,7 +176,9 @@ func (c *AuthEnableCommand) Run(args []string) int { Local: c.flagLocal, SealWrap: c.flagSealWrap, Config: api.AuthConfigInput{ - PluginName: c.flagPluginName, + DefaultLeaseTTL: c.flagDefaultLeaseTTL.String(), + MaxLeaseTTL: c.flagMaxLeaseTTL.String(), + PluginName: c.flagPluginName, }, }); err != nil { c.UI.Error(fmt.Sprintf("Error enabling %s auth: %s", authType, err)) diff --git a/command/server.go b/command/server.go index ad9d2573e052..d92cf138a98c 100644 --- a/command/server.go +++ b/command/server.go @@ -86,6 +86,7 @@ type ServerCommand struct { flagDevLeasedKV bool flagDevSkipInit bool flagDevThreeNode bool + flagDevFourCluster bool flagDevTransactional bool flagTestVerifyOnly bool } @@ -237,6 +238,13 @@ func (c *ServerCommand) Flags() *FlagSets { Hidden: true, }) + f.BoolVar(&BoolVar{ + Name: "dev-four-cluster", + Target: &c.flagDevFourCluster, + Default: false, + Hidden: true, + }) + // TODO: should this be a public flag? f.BoolVar(&BoolVar{ Name: "test-verify-only", @@ -295,10 +303,11 @@ func (c *ServerCommand) Run(args []string) int { } switch strings.ToLower(logFormat) { case "vault", "vault_json", "vault-json", "vaultjson", "json", "": - if c.flagDevThreeNode { + if c.flagDevThreeNode || c.flagDevFourCluster { c.logger = logbridge.NewLogger(hclog.New(&hclog.LoggerOptions{ Mutex: &sync.Mutex{}, Output: c.logGate, + Level: hclog.Trace, })).LogxiLogger() } else { c.logger = logformat.NewVaultLoggerWithWriter(c.logGate, level) @@ -313,7 +322,7 @@ func (c *ServerCommand) Run(args []string) int { }) // Automatically enable dev mode if other dev flags are provided. - if c.flagDevHA || c.flagDevTransactional || c.flagDevLeasedKV || c.flagDevThreeNode { + if c.flagDevHA || c.flagDevTransactional || c.flagDevLeasedKV || c.flagDevThreeNode || c.flagDevFourCluster { c.flagDev = true } diff --git a/helper/certutil/certutil_test.go b/helper/certutil/certutil_test.go index d3f9e2083df0..9ee013972f87 100644 --- a/helper/certutil/certutil_test.go +++ b/helper/certutil/certutil_test.go @@ -614,7 +614,7 @@ func setCerts() { Bytes: marshaledKey, } privECKeyPem = string(pem.EncodeToMemory(keyPEMBlock)) - marshaledKey, err = x509.MarshalPKCS8PrivateKey(key) + marshaledKey, err = MarshalPKCS8PrivateKey(key) if err != nil { panic(err) } @@ -680,7 +680,7 @@ func setCerts() { Bytes: marshaledKey, } privRSAKeyPem = string(pem.EncodeToMemory(keyPEMBlock)) - marshaledKey, err = x509.MarshalPKCS8PrivateKey(key) + marshaledKey, err = MarshalPKCS8PrivateKey(key) if err != nil { panic(err) } diff --git a/helper/certutil/pkcs8.go b/helper/certutil/pkcs8.go new file mode 100644 index 000000000000..22585de0ccb4 --- /dev/null +++ b/helper/certutil/pkcs8.go @@ -0,0 +1,119 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package certutil + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "errors" + "fmt" +) + +var ( + oidNamedCurveP224 = asn1.ObjectIdentifier{1, 3, 132, 0, 33} + oidNamedCurveP256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 3, 1, 7} + oidNamedCurveP384 = asn1.ObjectIdentifier{1, 3, 132, 0, 34} + oidNamedCurveP521 = asn1.ObjectIdentifier{1, 3, 132, 0, 35} + + oidPublicKeyRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1} + oidPublicKeyDSA = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1} + oidPublicKeyECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1} +) + +// pkcs8 reflects an ASN.1, PKCS#8 PrivateKey. See +// ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-8/pkcs-8v1_2.asn +// and RFC 5208. +type pkcs8 struct { + Version int + Algo pkix.AlgorithmIdentifier + PrivateKey []byte + // optional attributes omitted. +} + +type ecPrivateKey struct { + Version int + PrivateKey []byte + NamedCurveOID asn1.ObjectIdentifier `asn1:"optional,explicit,tag:0"` + PublicKey asn1.BitString `asn1:"optional,explicit,tag:1"` +} + +// MarshalPKCS8PrivateKey converts a private key to PKCS#8 encoded form. +// The following key types are supported: *rsa.PrivateKey, *ecdsa.PublicKey. +// Unsupported key types result in an error. +// +// See RFC 5208. +func MarshalPKCS8PrivateKey(key interface{}) ([]byte, error) { + var privKey pkcs8 + + switch k := key.(type) { + case *rsa.PrivateKey: + privKey.Algo = pkix.AlgorithmIdentifier{ + Algorithm: oidPublicKeyRSA, + Parameters: asn1.NullRawValue, + } + privKey.PrivateKey = x509.MarshalPKCS1PrivateKey(k) + + case *ecdsa.PrivateKey: + oid, ok := oidFromNamedCurve(k.Curve) + if !ok { + return nil, errors.New("x509: unknown curve while marshalling to PKCS#8") + } + + oidBytes, err := asn1.Marshal(oid) + if err != nil { + return nil, errors.New("x509: failed to marshal curve OID: " + err.Error()) + } + + privKey.Algo = pkix.AlgorithmIdentifier{ + Algorithm: oidPublicKeyECDSA, + Parameters: asn1.RawValue{ + FullBytes: oidBytes, + }, + } + + if privKey.PrivateKey, err = marshalECPrivateKeyWithOID(k, nil); err != nil { + return nil, errors.New("x509: failed to marshal EC private key while building PKCS#8: " + err.Error()) + } + + default: + return nil, fmt.Errorf("x509: unknown key type while marshalling PKCS#8: %T", key) + } + + return asn1.Marshal(privKey) +} + +func oidFromNamedCurve(curve elliptic.Curve) (asn1.ObjectIdentifier, bool) { + switch curve { + case elliptic.P224(): + return oidNamedCurveP224, true + case elliptic.P256(): + return oidNamedCurveP256, true + case elliptic.P384(): + return oidNamedCurveP384, true + case elliptic.P521(): + return oidNamedCurveP521, true + } + + return nil, false +} + +// marshalECPrivateKey marshals an EC private key into ASN.1, DER format and +// sets the curve ID to the given OID, or omits it if OID is nil. +func marshalECPrivateKeyWithOID(key *ecdsa.PrivateKey, oid asn1.ObjectIdentifier) ([]byte, error) { + privateKeyBytes := key.D.Bytes() + paddedPrivateKey := make([]byte, (key.Curve.Params().N.BitLen()+7)/8) + copy(paddedPrivateKey[len(paddedPrivateKey)-len(privateKeyBytes):], privateKeyBytes) + + return asn1.Marshal(ecPrivateKey{ + Version: 1, + PrivateKey: paddedPrivateKey, + NamedCurveOID: oid, + PublicKey: asn1.BitString{Bytes: elliptic.Marshal(key.Curve, key.X, key.Y)}, + }) +} diff --git a/helper/certutil/pkcs8_test.go b/helper/certutil/pkcs8_test.go new file mode 100644 index 000000000000..df350ead4137 --- /dev/null +++ b/helper/certutil/pkcs8_test.go @@ -0,0 +1,110 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package certutil + +import ( + "bytes" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rsa" + "crypto/x509" + "encoding/hex" + "reflect" + "testing" +) + +// Generated using: +// openssl genrsa 1024 | openssl pkcs8 -topk8 -nocrypt +var pkcs8RSAPrivateKeyHex = `30820278020100300d06092a864886f70d0101010500048202623082025e02010002818100cfb1b5bf9685ffa97b4f99df4ff122b70e59ac9b992f3bc2b3dde17d53c1a34928719b02e8fd17839499bfbd515bd6ef99c7a1c47a239718fe36bfd824c0d96060084b5f67f0273443007a24dfaf5634f7772c9346e10eb294c2306671a5a5e719ae24b4de467291bc571014b0e02dec04534d66a9bb171d644b66b091780e8d020301000102818100b595778383c4afdbab95d2bfed12b3f93bb0a73a7ad952f44d7185fd9ec6c34de8f03a48770f2009c8580bcd275e9632714e9a5e3f32f29dc55474b2329ff0ebc08b3ffcb35bc96e6516b483df80a4a59cceb71918cbabf91564e64a39d7e35dce21cb3031824fdbc845dba6458852ec16af5dddf51a8397a8797ae0337b1439024100ea0eb1b914158c70db39031dd8904d6f18f408c85fbbc592d7d20dee7986969efbda081fdf8bc40e1b1336d6b638110c836bfdc3f314560d2e49cd4fbde1e20b024100e32a4e793b574c9c4a94c8803db5152141e72d03de64e54ef2c8ed104988ca780cd11397bc359630d01b97ebd87067c5451ba777cf045ca23f5912f1031308c702406dfcdbbd5a57c9f85abc4edf9e9e29153507b07ce0a7ef6f52e60dcfebe1b8341babd8b789a837485da6c8d55b29bbb142ace3c24a1f5b54b454d01b51e2ad03024100bd6a2b60dee01e1b3bfcef6a2f09ed027c273cdbbaf6ba55a80f6dcc64e4509ee560f84b4f3e076bd03b11e42fe71a3fdd2dffe7e0902c8584f8cad877cdc945024100aa512fa4ada69881f1d8bb8ad6614f192b83200aef5edf4811313d5ef30a86cbd0a90f7b025c71ea06ec6b34db6306c86b1040670fd8654ad7291d066d06d031` + +// Generated using: +// openssl ecparam -genkey -name secp224r1 | openssl pkcs8 -topk8 -nocrypt +var pkcs8P224PrivateKeyHex = `3078020100301006072a8648ce3d020106052b810400210461305f020101041cca3d72b3e88fed2684576dad9b80a9180363a5424986900e3abcab3fa13c033a0004f8f2a6372872a4e61263ed893afb919576a4cacfecd6c081a2cbc76873cf4ba8530703c6042b3a00e2205087e87d2435d2e339e25702fae1` + +// Generated using: +// openssl ecparam -genkey -name secp256r1 | openssl pkcs8 -topk8 -nocrypt +var pkcs8P256PrivateKeyHex = `308187020100301306072a8648ce3d020106082a8648ce3d030107046d306b0201010420dad6b2f49ca774c36d8ae9517e935226f667c929498f0343d2424d0b9b591b43a14403420004b9c9b90095476afe7b860d8bd43568cab7bcb2eed7b8bf2fa0ce1762dd20b04193f859d2d782b1e4cbfd48492f1f533113a6804903f292258513837f07fda735` + +// Generated using: +// openssl ecparam -genkey -name secp384r1 | openssl pkcs8 -topk8 -nocrypt +var pkcs8P384PrivateKeyHex = `3081b6020100301006072a8648ce3d020106052b8104002204819e30819b02010104309bf832f6aaaeacb78ce47ffb15e6fd0fd48683ae79df6eca39bfb8e33829ac94aa29d08911568684c2264a08a4ceb679a164036200049070ad4ed993c7770d700e9f6dc2baa83f63dd165b5507f98e8ff29b5d2e78ccbe05c8ddc955dbf0f7497e8222cfa49314fe4e269459f8e880147f70d785e530f2939e4bf9f838325bb1a80ad4cf59272ae0e5efe9a9dc33d874492596304bd3` + +// Generated using: +// openssl ecparam -genkey -name secp521r1 | openssl pkcs8 -topk8 -nocrypt +// +// Note that OpenSSL will truncate the private key if it can (i.e. it emits it +// like an integer, even though it's an OCTET STRING field). Thus if you +// regenerate this you may, randomly, find that it's a byte shorter than +// expected and the Go test will fail to recreate it exactly. +var pkcs8P521PrivateKeyHex = `3081ee020100301006072a8648ce3d020106052b810400230481d63081d3020101044200cfe0b87113a205cf291bb9a8cd1a74ac6c7b2ebb8199aaa9a5010d8b8012276fa3c22ac913369fa61beec2a3b8b4516bc049bde4fb3b745ac11b56ab23ac52e361a1818903818600040138f75acdd03fbafa4f047a8e4b272ba9d555c667962b76f6f232911a5786a0964e5edea6bd21a6f8725720958de049c6e3e6661c1c91b227cebee916c0319ed6ca003db0a3206d372229baf9dd25d868bf81140a518114803ce40c1855074d68c4e9dab9e65efba7064c703b400f1767f217dac82715ac1f6d88c74baf47a7971de4ea` + +func TestPKCS8(t *testing.T) { + tests := []struct { + name string + keyHex string + keyType reflect.Type + curve elliptic.Curve + }{ + { + name: "RSA private key", + keyHex: pkcs8RSAPrivateKeyHex, + keyType: reflect.TypeOf(&rsa.PrivateKey{}), + }, + { + name: "P-224 private key", + keyHex: pkcs8P224PrivateKeyHex, + keyType: reflect.TypeOf(&ecdsa.PrivateKey{}), + curve: elliptic.P224(), + }, + { + name: "P-256 private key", + keyHex: pkcs8P256PrivateKeyHex, + keyType: reflect.TypeOf(&ecdsa.PrivateKey{}), + curve: elliptic.P256(), + }, + { + name: "P-384 private key", + keyHex: pkcs8P384PrivateKeyHex, + keyType: reflect.TypeOf(&ecdsa.PrivateKey{}), + curve: elliptic.P384(), + }, + { + name: "P-521 private key", + keyHex: pkcs8P521PrivateKeyHex, + keyType: reflect.TypeOf(&ecdsa.PrivateKey{}), + curve: elliptic.P521(), + }, + } + + for _, test := range tests { + derBytes, err := hex.DecodeString(test.keyHex) + if err != nil { + t.Errorf("%s: failed to decode hex: %s", test.name, err) + continue + } + privKey, err := x509.ParsePKCS8PrivateKey(derBytes) + if err != nil { + t.Errorf("%s: failed to decode PKCS#8: %s", test.name, err) + continue + } + if reflect.TypeOf(privKey) != test.keyType { + t.Errorf("%s: decoded PKCS#8 returned unexpected key type: %T", test.name, privKey) + continue + } + if ecKey, isEC := privKey.(*ecdsa.PrivateKey); isEC && ecKey.Curve != test.curve { + t.Errorf("%s: decoded PKCS#8 returned unexpected curve %#v", test.name, ecKey.Curve) + continue + } + reserialised, err := MarshalPKCS8PrivateKey(privKey) + if err != nil { + t.Errorf("%s: failed to marshal into PKCS#8: %s", test.name, err) + continue + } + if !bytes.Equal(derBytes, reserialised) { + t.Errorf("%s: marshalled PKCS#8 didn't match original: got %x, want %x", test.name, reserialised, derBytes) + continue + } + } +} diff --git a/helper/consts/replication.go b/helper/consts/replication.go index aad7fd3e60a7..c109977c5cc3 100644 --- a/helper/consts/replication.go +++ b/helper/consts/replication.go @@ -7,6 +7,9 @@ const ( OldReplicationPrimary OldReplicationSecondary OldReplicationBootstrapping + // Don't add anything here. Adding anything to this Old block would cause + // the rest of the values to change below. This was done originally to + // ensure no overlap between old and new values. ReplicationUnknown ReplicationState = 0 ReplicationPerformancePrimary ReplicationState = 1 << iota diff --git a/http/logical.go b/http/logical.go index 2b09414e54a3..2d09f67e5147 100644 --- a/http/logical.go +++ b/http/logical.go @@ -75,6 +75,30 @@ func buildLogicalRequest(core *vault.Core, w http.ResponseWriter, r *http.Reques } } + // If we are a read operation, try and parse any parameters + if op == logical.ReadOperation { + getData := map[string]interface{}{} + + for k, v := range r.URL.Query() { + // Skip the help key as this is a reserved parameter + if k == "help" { + continue + } + + switch { + case len(v) == 0: + case len(v) == 1: + getData[k] = v[0] + default: + getData[k] = v + } + } + + if len(getData) > 0 { + data = getData + } + } + var err error request_id, err := uuid.GenerateUUID() if err != nil { diff --git a/http/logical_test.go b/http/logical_test.go index 7f3ff66aeb5f..fee8fb07eec3 100644 --- a/http/logical_test.go +++ b/http/logical_test.go @@ -4,7 +4,9 @@ import ( "bytes" "encoding/json" "io" + "io/ioutil" "net/http" + "net/http/httptest" "reflect" "strconv" "strings" @@ -14,6 +16,7 @@ import ( log "github.com/mgutz/logxi/v1" "github.com/hashicorp/vault/helper/logformat" + "github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/physical" "github.com/hashicorp/vault/physical/inmem" "github.com/hashicorp/vault/vault" @@ -300,3 +303,34 @@ func TestLogical_ListSuffix(t *testing.T) { t.Fatal("trailing slash not found on path") } } + +func TestLogical_RespondWithStatusCode(t *testing.T) { + resp := &logical.Response{ + Data: map[string]interface{}{ + "test-data": "foo", + }, + } + + resp404, err := logical.RespondWithStatusCode(resp, &logical.Request{ID: "id"}, http.StatusNotFound) + if err != nil { + t.Fatal(err) + } + + w := httptest.NewRecorder() + respondLogical(w, nil, nil, false, resp404) + + if w.Code != 404 { + t.Fatalf("Bad Status code: %d", w.Code) + } + + bodyRaw, err := ioutil.ReadAll(w.Body) + if err != nil { + t.Fatal(err) + } + + expected := `{"request_id":"id","lease_id":"","renewable":false,"lease_duration":0,"data":{"test-data":"foo"},"wrap_info":null,"warnings":null,"auth":null}` + + if string(bodyRaw[:]) != strings.Trim(expected, "\n") { + t.Fatalf("bad response: %s", string(bodyRaw[:])) + } +} diff --git a/http/plugin_test.go b/http/plugin_test.go index b96e8d484020..b5c978992aa0 100644 --- a/http/plugin_test.go +++ b/http/plugin_test.go @@ -1,8 +1,10 @@ package http import ( + "encoding/json" "io/ioutil" "os" + "reflect" "sync" "testing" @@ -96,7 +98,7 @@ func TestPlugin_MockList(t *testing.T) { defer cluster.Cleanup() _, err := core.Client.Logical().Write("mock/kv/foo", map[string]interface{}{ - "bar": "baz", + "value": "baz", }) if err != nil { t.Fatal(err) @@ -111,7 +113,7 @@ func TestPlugin_MockList(t *testing.T) { } _, err = core.Client.Logical().Write("mock/kv/zoo", map[string]interface{}{ - "bar": "baz", + "value": "baz", }) if err != nil { t.Fatal(err) @@ -151,3 +153,40 @@ func TestPlugin_MockRawResponse(t *testing.T) { } } + +func TestPlugin_GetParams(t *testing.T) { + logger := logbridge.NewLogger(hclog.New(&hclog.LoggerOptions{ + Mutex: &sync.Mutex{}, + })) + cluster, core := getPluginClusterAndCore(t, logger) + defer cluster.Cleanup() + + _, err := core.Client.Logical().Write("mock/kv/foo", map[string]interface{}{ + "value": "baz", + }) + if err != nil { + t.Fatal(err) + } + + r := core.Client.NewRequest("GET", "/v1/mock/kv/foo") + r.Params.Add("version", "12") + resp, err := core.Client.RawRequest(r) + if err != nil { + t.Fatal(err) + } + defer resp.Body.Close() + + secret, err := api.ParseSecret(resp.Body) + if err != nil { + t.Fatal(err) + } + + expected := map[string]interface{}{ + "value": "baz", + "version": json.Number("12"), + } + + if !reflect.DeepEqual(secret.Data, expected) { + t.Fatal(secret.Data) + } +} diff --git a/logical/plugin/mock/path_kv.go b/logical/plugin/mock/path_kv.go index 7efa495a0ccd..3e599621edc0 100644 --- a/logical/plugin/mock/path_kv.go +++ b/logical/plugin/mock/path_kv.go @@ -21,8 +21,9 @@ func kvPaths(b *backend) []*framework.Path { &framework.Path{ Pattern: "kv/" + framework.GenericNameRegex("key"), Fields: map[string]*framework.FieldSchema{ - "key": &framework.FieldSchema{Type: framework.TypeString}, - "value": &framework.FieldSchema{Type: framework.TypeString}, + "key": &framework.FieldSchema{Type: framework.TypeString}, + "value": &framework.FieldSchema{Type: framework.TypeString}, + "version": &framework.FieldSchema{Type: framework.TypeInt}, }, ExistenceCheck: b.pathExistenceCheck, Callbacks: map[logical.Operation]framework.OperationFunc{ @@ -45,6 +46,8 @@ func (b *backend) pathExistenceCheck(ctx context.Context, req *logical.Request, } func (b *backend) pathKVRead(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { + version := data.Get("version").(int) + entry, err := req.Storage.Get(ctx, req.Path) if err != nil { return nil, err @@ -58,11 +61,16 @@ func (b *backend) pathKVRead(ctx context.Context, req *logical.Request, data *fr b.Logger().Info("reading value", "key", req.Path, "value", value) // Return the secret - return &logical.Response{ + resp := &logical.Response{ Data: map[string]interface{}{ - "value": value, + "value": value, + "version": version, }, - }, nil + } + if version != 0 { + resp.Data["version"] = version + } + return resp, nil } func (b *backend) pathKVCreateUpdate(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { diff --git a/logical/response.go b/logical/response.go index ab92fd541b07..acfe63945c91 100644 --- a/logical/response.go +++ b/logical/response.go @@ -1,6 +1,7 @@ package logical import ( + "encoding/json" "errors" "github.com/hashicorp/vault/helper/wrapping" @@ -130,3 +131,23 @@ func ListResponseWithInfo(keys []string, keyInfo map[string]interface{}) *Respon return resp } + +// RespondWithStatusCode takes a response and converts it to a raw response with +// the provided Status Code. +func RespondWithStatusCode(resp *Response, req *Request, code int) (*Response, error) { + httpResp := LogicalResponseToHTTPResponse(resp) + httpResp.RequestID = req.ID + + body, err := json.Marshal(httpResp) + if err != nil { + return nil, err + } + + return &Response{ + Data: map[string]interface{}{ + HTTPContentType: "application/json", + HTTPRawBody: body, + HTTPStatusCode: code, + }, + }, nil +} diff --git a/physical/consul/consul.go b/physical/consul/consul.go index 4609d9eff4bc..f479782baf28 100644 --- a/physical/consul/consul.go +++ b/physical/consul/consul.go @@ -92,6 +92,7 @@ type ConsulBackend struct { redirectPort int64 serviceName string serviceTags []string + serviceAddress *string disableRegistration bool checkTimeout time.Duration consistencyMode string @@ -150,11 +151,20 @@ func NewConsulBackend(conf map[string]string, logger log.Logger) (physical.Backe // Get the additional tags to attach to the registered service name tags := conf["service_tags"] - if logger.IsDebug() { logger.Debug("physical/consul: config service_tags set", "service_tags", tags) } + // Get the service-specific address to override the use of the HA redirect address + var serviceAddr *string + serviceAddrStr, ok := conf["service_address"] + if ok { + serviceAddr = &serviceAddrStr + } + if logger.IsDebug() { + logger.Debug("physical/consul: config service_address set", "service_address", serviceAddr) + } + checkTimeout := defaultCheckTimeout checkTimeoutStr, ok := conf["check_timeout"] if ok { @@ -247,6 +257,7 @@ func NewConsulBackend(conf map[string]string, logger log.Logger) (physical.Backe permitPool: physical.NewPermitPool(maxParInt), serviceName: service, serviceTags: strutil.ParseDedupLowercaseAndSortStrings(tags, ","), + serviceAddress: serviceAddr, checkTimeout: checkTimeout, disableRegistration: disableRegistration, consistencyMode: consistencyMode, @@ -726,12 +737,21 @@ func (c *ConsulBackend) reconcileConsul(registeredServiceID string, activeFunc p return serviceID, nil } + // If service address was set explicitly in configuration, use that + // as the service-specific address instead of the HA redirect address. + var serviceAddress string + if c.serviceAddress == nil { + serviceAddress = c.redirectHost + } else { + serviceAddress = *c.serviceAddress + } + service := &api.AgentServiceRegistration{ ID: serviceID, Name: c.serviceName, Tags: tags, Port: int(c.redirectPort), - Address: c.redirectHost, + Address: serviceAddress, EnableTagOverride: false, } diff --git a/physical/consul/consul_test.go b/physical/consul/consul_test.go index 8be1fe956d21..7b1d1f4dc84c 100644 --- a/physical/consul/consul_test.go +++ b/physical/consul/consul_test.go @@ -117,6 +117,51 @@ func TestConsul_ServiceTags(t *testing.T) { } } +func TestConsul_ServiceAddress(t *testing.T) { + tests := []struct { + consulConfig map[string]string + serviceAddrNil bool + }{ + { + consulConfig: map[string]string{ + "service_address": "", + }, + }, + { + consulConfig: map[string]string{ + "service_address": "vault.example.com", + }, + }, + { + serviceAddrNil: true, + }, + } + + for _, test := range tests { + logger := logformat.NewVaultLogger(log.LevelTrace) + + be, err := NewConsulBackend(test.consulConfig, logger) + if err != nil { + t.Fatalf("expected Consul to initialize: %v", err) + } + + c, ok := be.(*ConsulBackend) + if !ok { + t.Fatalf("Expected ConsulBackend") + } + + if test.serviceAddrNil { + if c.serviceAddress != nil { + t.Fatalf("expected service address to be nil") + } + } else { + if c.serviceAddress == nil { + t.Fatalf("did not expect service address to be nil") + } + } + } +} + func TestConsul_newConsulBackend(t *testing.T) { tests := []struct { name string diff --git a/vault/cluster.go b/vault/cluster.go index c29bcaf84b7e..9e5a9d8eafb7 100644 --- a/vault/cluster.go +++ b/vault/cluster.go @@ -1,7 +1,6 @@ package vault import ( - "bytes" "context" "crypto/ecdsa" "crypto/elliptic" @@ -38,6 +37,10 @@ var ( ErrCannotForward = errors.New("cannot forward request; no connection or address not known") ) +// This is used for enterprise replication information +type ReplicatedClusters struct { +} + // This can be one of a few key types so the different params may or may not be filled type clusterKeyParams struct { Type string `json:"type" structs:"type" mapstructure:"type"` @@ -87,11 +90,9 @@ func (c *Core) Cluster(ctx context.Context) (*Cluster, error) { func (c *Core) loadLocalClusterTLS(adv activeAdvertisement) (retErr error) { defer func() { if retErr != nil { - c.clusterParamsLock.Lock() - c.localClusterCert = nil - c.localClusterPrivateKey = nil - c.localClusterParsedCert = nil - c.clusterParamsLock.Unlock() + c.localClusterCert.Store(([]byte)(nil)) + c.localClusterParsedCert.Store((*x509.Certificate)(nil)) + c.localClusterPrivateKey.Store((*ecdsa.PrivateKey)(nil)) c.requestForwardingConnectionLock.Lock() c.clearForwardingClients() @@ -122,28 +123,26 @@ func (c *Core) loadLocalClusterTLS(adv activeAdvertisement) (retErr error) { } - // Prevent data races with the TLS parameters - c.clusterParamsLock.Lock() - defer c.clusterParamsLock.Unlock() - - c.localClusterPrivateKey = &ecdsa.PrivateKey{ + c.localClusterPrivateKey.Store(&ecdsa.PrivateKey{ PublicKey: ecdsa.PublicKey{ Curve: elliptic.P521(), X: adv.ClusterKeyParams.X, Y: adv.ClusterKeyParams.Y, }, D: adv.ClusterKeyParams.D, - } + }) - c.localClusterCert = adv.ClusterCert + locCert := make([]byte, len(adv.ClusterCert)) + copy(locCert, adv.ClusterCert) + c.localClusterCert.Store(locCert) - cert, err := x509.ParseCertificate(c.localClusterCert) + cert, err := x509.ParseCertificate(adv.ClusterCert) if err != nil { c.logger.Error("core: failed parsing local cluster certificate", "error", err) return fmt.Errorf("error parsing local cluster certificate: %v", err) } - c.localClusterParsedCert = cert + c.localClusterParsedCert.Store(cert) return nil } @@ -206,7 +205,7 @@ func (c *Core) setupCluster(ctx context.Context) error { // If we're using HA, generate server-to-server parameters if c.ha != nil { // Create a private key - if c.localClusterPrivateKey == nil { + if c.localClusterPrivateKey.Load().(*ecdsa.PrivateKey) == nil { c.logger.Trace("core: generating cluster private key") key, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader) if err != nil { @@ -214,11 +213,11 @@ func (c *Core) setupCluster(ctx context.Context) error { return err } - c.localClusterPrivateKey = key + c.localClusterPrivateKey.Store(key) } // Create a certificate - if c.localClusterCert == nil { + if c.localClusterCert.Load().([]byte) == nil { c.logger.Trace("core: generating local cluster certificate") host, err := uuid.GenerateUUID() @@ -244,7 +243,7 @@ func (c *Core) setupCluster(ctx context.Context) error { IsCA: true, } - certBytes, err := x509.CreateCertificate(rand.Reader, template, template, c.localClusterPrivateKey.Public(), c.localClusterPrivateKey) + certBytes, err := x509.CreateCertificate(rand.Reader, template, template, c.localClusterPrivateKey.Load().(*ecdsa.PrivateKey).Public(), c.localClusterPrivateKey.Load().(*ecdsa.PrivateKey)) if err != nil { c.logger.Error("core: error generating self-signed cert", "error", err) return errwrap.Wrapf("unable to generate local cluster certificate: {{err}}", err) @@ -256,8 +255,8 @@ func (c *Core) setupCluster(ctx context.Context) error { return errwrap.Wrapf("error parsing generated certificate: {{err}}", err) } - c.localClusterCert = certBytes - c.localClusterParsedCert = parsedCert + c.localClusterCert.Store(certBytes) + c.localClusterParsedCert.Store(parsedCert) } } @@ -338,31 +337,27 @@ func (c *Core) stopClusterListener() { // ClusterTLSConfig generates a TLS configuration based on the local/replicated // cluster key and cert. -func (c *Core) ClusterTLSConfig(ctx context.Context) (*tls.Config, error) { +func (c *Core) ClusterTLSConfig(ctx context.Context, repClusters *ReplicatedClusters) (*tls.Config, error) { // Using lookup functions allows just-in-time lookup of the current state // of clustering as connections come and go serverLookup := func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) { switch { default: - var localCert bytes.Buffer - - c.clusterParamsLock.RLock() - localCert.Write(c.localClusterCert) - localSigner := c.localClusterPrivateKey - parsedCert := c.localClusterParsedCert - c.clusterParamsLock.RUnlock() - - if localCert.Len() == 0 { + currCert := c.localClusterCert.Load().([]byte) + if len(currCert) == 0 { return nil, fmt.Errorf("got forwarding connection but no local cert") } + localCert := make([]byte, len(currCert)) + copy(localCert, currCert) + //c.logger.Trace("core: performing cert name lookup", "hello_server_name", clientHello.ServerName, "local_cluster_cert_name", parsedCert.Subject.CommonName) return &tls.Certificate{ - Certificate: [][]byte{localCert.Bytes()}, - PrivateKey: localSigner, - Leaf: parsedCert, + Certificate: [][]byte{localCert}, + PrivateKey: c.localClusterPrivateKey.Load().(*ecdsa.PrivateKey), + Leaf: c.localClusterParsedCert.Load().(*x509.Certificate), }, nil } } @@ -373,22 +368,19 @@ func (c *Core) ClusterTLSConfig(ctx context.Context) (*tls.Config, error) { if len(requestInfo.AcceptableCAs) != 1 { return nil, fmt.Errorf("expected only a single acceptable CA") } - var localCert bytes.Buffer - - c.clusterParamsLock.RLock() - localCert.Write(c.localClusterCert) - localSigner := c.localClusterPrivateKey - parsedCert := c.localClusterParsedCert - c.clusterParamsLock.RUnlock() - if localCert.Len() == 0 { + currCert := c.localClusterCert.Load().([]byte) + if len(currCert) == 0 { return nil, fmt.Errorf("forwarding connection client but no local cert") } + localCert := make([]byte, len(currCert)) + copy(localCert, currCert) + return &tls.Certificate{ - Certificate: [][]byte{localCert.Bytes()}, - PrivateKey: localSigner, - Leaf: parsedCert, + Certificate: [][]byte{localCert}, + PrivateKey: c.localClusterPrivateKey.Load().(*ecdsa.PrivateKey), + Leaf: c.localClusterParsedCert.Load().(*x509.Certificate), }, nil } @@ -417,9 +409,7 @@ func (c *Core) ClusterTLSConfig(ctx context.Context) (*tls.Config, error) { switch { default: - c.clusterParamsLock.RLock() - parsedCert := c.localClusterParsedCert - c.clusterParamsLock.RUnlock() + parsedCert := c.localClusterParsedCert.Load().(*x509.Certificate) if parsedCert == nil { return nil, fmt.Errorf("forwarding connection client but no local cert") @@ -440,11 +430,10 @@ func (c *Core) ClusterTLSConfig(ctx context.Context) (*tls.Config, error) { CipherSuites: c.clusterCipherSuites, } - var localCert bytes.Buffer - c.clusterParamsLock.RLock() - localCert.Write(c.localClusterCert) - parsedCert := c.localClusterParsedCert - c.clusterParamsLock.RUnlock() + parsedCert := c.localClusterParsedCert.Load().(*x509.Certificate) + currCert := c.localClusterCert.Load().([]byte) + localCert := make([]byte, len(currCert)) + copy(localCert, currCert) if parsedCert != nil { tlsConfig.ServerName = parsedCert.Subject.CommonName diff --git a/vault/cluster_test.go b/vault/cluster_test.go index 17aad7bd5c8d..40048a3abc8d 100644 --- a/vault/cluster_test.go +++ b/vault/cluster_test.go @@ -108,7 +108,7 @@ func TestCluster_ListenForRequests(t *testing.T) { // Use this to have a valid config after sealing since ClusterTLSConfig returns nil var lastTLSConfig *tls.Config checkListenersFunc := func(expectFail bool) { - tlsConfig, err := cores[0].ClusterTLSConfig(context.Background()) + tlsConfig, err := cores[0].ClusterTLSConfig(context.Background(), nil) if err != nil { if err.Error() != consts.ErrSealed.Error() { t.Fatal(err) @@ -395,7 +395,7 @@ func TestCluster_CustomCipherSuites(t *testing.T) { // Wait for core to become active TestWaitActive(t, core.Core) - tlsConf, err := core.Core.ClusterTLSConfig(context.Background()) + tlsConf, err := core.Core.ClusterTLSConfig(context.Background(), nil) if err != nil { t.Fatal(err) } diff --git a/vault/core.go b/vault/core.go index 40f734db64eb..d40aac37af18 100644 --- a/vault/core.go +++ b/vault/core.go @@ -2,7 +2,6 @@ package vault import ( "context" - "crypto" "crypto/ecdsa" "crypto/subtle" "crypto/x509" @@ -307,11 +306,11 @@ type Core struct { clusterParamsLock sync.RWMutex // The private key stored in the barrier used for establishing // mutually-authenticated connections between Vault cluster members - localClusterPrivateKey crypto.Signer + localClusterPrivateKey *atomic.Value // The local cluster cert - localClusterCert []byte + localClusterCert *atomic.Value // The parsed form of the local cluster cert - localClusterParsedCert *x509.Certificate + localClusterParsedCert *atomic.Value // The TCP addresses we should use for clustering clusterListenerAddrs []*net.TCPAddr // The handler to use for request forwarding @@ -497,10 +496,16 @@ func NewCore(conf *CoreConfig) (*Core, error) { rpcServerActive: new(uint32), atomicPrimaryClusterAddrs: new(atomic.Value), atomicPrimaryFailoverAddrs: new(atomic.Value), + localClusterPrivateKey: new(atomic.Value), + localClusterCert: new(atomic.Value), + localClusterParsedCert: new(atomic.Value), activeNodeReplicationState: new(uint32), } atomic.StoreUint32(c.replicationState, uint32(consts.ReplicationDRDisabled|consts.ReplicationPerformanceDisabled)) + c.localClusterCert.Store(([]byte)(nil)) + c.localClusterParsedCert.Store((*x509.Certificate)(nil)) + c.localClusterPrivateKey.Store((*ecdsa.PrivateKey)(nil)) if conf.ClusterCipherSuites != "" { suites, err := tlsutil.ParseCiphers(conf.ClusterCipherSuites) @@ -1816,11 +1821,9 @@ func (c *Core) runStandby(doneCh, stopCh, manualStepDownCh chan struct{}) { // Clear previous local cluster cert info so we generate new. Since the // UUID will have changed, standbys will know to look for new info - c.clusterParamsLock.Lock() - c.localClusterCert = nil - c.localClusterParsedCert = nil - c.localClusterPrivateKey = nil - c.clusterParamsLock.Unlock() + c.localClusterParsedCert.Store((*x509.Certificate)(nil)) + c.localClusterCert.Store(([]byte)(nil)) + c.localClusterPrivateKey.Store((*ecdsa.PrivateKey)(nil)) if err := c.setupCluster(ctx); err != nil { c.stateLock.Unlock() @@ -2049,12 +2052,12 @@ func (c *Core) advertiseLeader(ctx context.Context, uuid string, leaderLostCh <- go c.cleanLeaderPrefix(ctx, uuid, leaderLostCh) var key *ecdsa.PrivateKey - switch c.localClusterPrivateKey.(type) { + switch c.localClusterPrivateKey.Load().(type) { case *ecdsa.PrivateKey: - key = c.localClusterPrivateKey.(*ecdsa.PrivateKey) + key = c.localClusterPrivateKey.Load().(*ecdsa.PrivateKey) default: - c.logger.Error("core: unknown cluster private key type", "key_type", fmt.Sprintf("%T", c.localClusterPrivateKey)) - return fmt.Errorf("unknown cluster private key type %T", c.localClusterPrivateKey) + c.logger.Error("core: unknown cluster private key type", "key_type", fmt.Sprintf("%T", c.localClusterPrivateKey.Load())) + return fmt.Errorf("unknown cluster private key type %T", c.localClusterPrivateKey.Load()) } keyParams := &clusterKeyParams{ @@ -2064,10 +2067,13 @@ func (c *Core) advertiseLeader(ctx context.Context, uuid string, leaderLostCh <- D: key.D, } + locCert := c.localClusterCert.Load().([]byte) + localCert := make([]byte, len(locCert)) + copy(localCert, locCert) adv := &activeAdvertisement{ RedirectAddr: c.redirectAddr, ClusterAddr: c.clusterAddr, - ClusterCert: c.localClusterCert, + ClusterCert: localCert, ClusterKeyParams: keyParams, } val, err := jsonutil.EncodeJSON(adv) diff --git a/vault/logical_system.go b/vault/logical_system.go index 2f4cd725005d..f6ce8e4dbe06 100644 --- a/vault/logical_system.go +++ b/vault/logical_system.go @@ -1961,6 +1961,44 @@ func (b *SystemBackend) handleEnableAuth(ctx context.Context, req *logical.Reque } } + switch apiConfig.DefaultLeaseTTL { + case "": + case "system": + default: + tmpDef, err := parseutil.ParseDurationSecond(apiConfig.DefaultLeaseTTL) + if err != nil { + return logical.ErrorResponse(fmt.Sprintf( + "unable to parse default TTL of %s: %s", apiConfig.DefaultLeaseTTL, err)), + logical.ErrInvalidRequest + } + config.DefaultLeaseTTL = tmpDef + } + + switch apiConfig.MaxLeaseTTL { + case "": + case "system": + default: + tmpMax, err := parseutil.ParseDurationSecond(apiConfig.MaxLeaseTTL) + if err != nil { + return logical.ErrorResponse(fmt.Sprintf( + "unable to parse max TTL of %s: %s", apiConfig.MaxLeaseTTL, err)), + logical.ErrInvalidRequest + } + config.MaxLeaseTTL = tmpMax + } + + if config.MaxLeaseTTL != 0 && config.DefaultLeaseTTL > config.MaxLeaseTTL { + return logical.ErrorResponse( + "given default lease TTL greater than given max lease TTL"), + logical.ErrInvalidRequest + } + + if config.DefaultLeaseTTL > b.Core.maxLeaseTTL && config.MaxLeaseTTL == 0 { + return logical.ErrorResponse(fmt.Sprintf( + "given default lease TTL greater than system max lease TTL of %d", int(b.Core.maxLeaseTTL.Seconds()))), + logical.ErrInvalidRequest + } + // Only set plugin name if mount is of type plugin, with apiConfig.PluginName // option taking precedence. if logicalType == "plugin" { diff --git a/vault/logical_system_test.go b/vault/logical_system_test.go index 75ceedc42cab..9c0785263796 100644 --- a/vault/logical_system_test.go +++ b/vault/logical_system_test.go @@ -180,6 +180,10 @@ func TestSystemBackend_mount(t *testing.T) { req := logical.TestRequest(t, logical.UpdateOperation, "mounts/prod/secret/") req.Data["type"] = "kv" + req.Data["config"] = map[string]interface{}{ + "default_lease_ttl": "35m", + "max_lease_ttl": "45m", + } req.Data["local"] = true req.Data["seal_wrap"] = true @@ -257,8 +261,8 @@ func TestSystemBackend_mount(t *testing.T) { "type": "kv", "accessor": resp.Data["prod/secret/"].(map[string]interface{})["accessor"], "config": map[string]interface{}{ - "default_lease_ttl": resp.Data["identity/"].(map[string]interface{})["config"].(map[string]interface{})["default_lease_ttl"].(int64), - "max_lease_ttl": resp.Data["identity/"].(map[string]interface{})["config"].(map[string]interface{})["max_lease_ttl"].(int64), + "default_lease_ttl": int64(2100), + "max_lease_ttl": int64(2700), "plugin_name": "", "force_no_cache": false, }, @@ -1244,6 +1248,10 @@ func TestSystemBackend_enableAuth(t *testing.T) { req := logical.TestRequest(t, logical.UpdateOperation, "auth/foo") req.Data["type"] = "noop" + req.Data["config"] = map[string]interface{}{ + "default_lease_ttl": "35m", + "max_lease_ttl": "45m", + } req.Data["local"] = true req.Data["seal_wrap"] = true @@ -1270,8 +1278,8 @@ func TestSystemBackend_enableAuth(t *testing.T) { "description": "", "accessor": resp.Data["foo/"].(map[string]interface{})["accessor"], "config": map[string]interface{}{ - "default_lease_ttl": int64(0), - "max_lease_ttl": int64(0), + "default_lease_ttl": int64(2100), + "max_lease_ttl": int64(2700), }, "local": true, "seal_wrap": true, diff --git a/vault/policy_store.go b/vault/policy_store.go index ac1063bea0d4..98646ad6f732 100644 --- a/vault/policy_store.go +++ b/vault/policy_store.go @@ -137,6 +137,7 @@ var ( // PolicyStore is used to provide durable storage of policy, and to // manage ACLs associated with them. type PolicyStore struct { + core *Core aclView *BarrierView tokenPoliciesLRU *lru.TwoQueueCache // This is used to ensure that writes to the store (acl/rgp) or to the egp @@ -158,11 +159,12 @@ type PolicyEntry struct { // NewPolicyStore creates a new PolicyStore that is backed // using a given view. It used used to durable store and manage named policy. -func NewPolicyStore(ctx context.Context, baseView *BarrierView, system logical.SystemView, logger log.Logger) *PolicyStore { +func NewPolicyStore(ctx context.Context, core *Core, baseView *BarrierView, system logical.SystemView, logger log.Logger) *PolicyStore { ps := &PolicyStore{ aclView: baseView.SubView(policyACLSubPath), modifyLock: new(sync.RWMutex), logger: logger, + core: core, } if !system.CachingDisabled() { cache, _ := lru.New2Q(policyCacheSize) @@ -187,7 +189,7 @@ func NewPolicyStore(ctx context.Context, baseView *BarrierView, system logical.S func (c *Core) setupPolicyStore(ctx context.Context) error { // Create the policy store sysView := &dynamicSystemView{core: c} - c.policyStore = NewPolicyStore(ctx, c.systemBarrierView, sysView, c.logger) + c.policyStore = NewPolicyStore(ctx, c, c.systemBarrierView, sysView, c.logger) if c.ReplicationState().HasState(consts.ReplicationPerformanceSecondary) { // Policies will sync from the primary diff --git a/vault/policy_store_test.go b/vault/policy_store_test.go index 5302c11dbeae..6d16029a8727 100644 --- a/vault/policy_store_test.go +++ b/vault/policy_store_test.go @@ -13,7 +13,7 @@ import ( func mockPolicyStore(t *testing.T) *PolicyStore { _, barrier, _ := mockBarrier(t) view := NewBarrierView(barrier, "foo/") - p := NewPolicyStore(context.Background(), view, logical.TestSystemView(), logformat.NewVaultLogger(log.LevelTrace)) + p := NewPolicyStore(context.Background(), nil, view, logical.TestSystemView(), logformat.NewVaultLogger(log.LevelTrace)) return p } @@ -22,7 +22,7 @@ func mockPolicyStoreNoCache(t *testing.T) *PolicyStore { sysView.CachingDisabledVal = true _, barrier, _ := mockBarrier(t) view := NewBarrierView(barrier, "foo/") - p := NewPolicyStore(context.Background(), view, sysView, logformat.NewVaultLogger(log.LevelTrace)) + p := NewPolicyStore(context.Background(), nil, view, sysView, logformat.NewVaultLogger(log.LevelTrace)) return p } diff --git a/vault/request_forwarding.go b/vault/request_forwarding.go index 687e647763ea..f0f6a4c4bcb3 100644 --- a/vault/request_forwarding.go +++ b/vault/request_forwarding.go @@ -45,7 +45,7 @@ func (c *Core) startForwarding(ctx context.Context) error { ha := c.ha != nil // Get our TLS config - tlsConfig, err := c.ClusterTLSConfig(ctx) + tlsConfig, err := c.ClusterTLSConfig(ctx, nil) if err != nil { c.logger.Error("core: failed to get tls configuration when starting forwarding", "error", err) return err @@ -260,7 +260,7 @@ func (c *Core) refreshRequestForwardingConnection(ctx context.Context, clusterAd // the TLS state. dctx, cancelFunc := context.WithCancel(ctx) c.rpcClientConn, err = grpc.DialContext(dctx, clusterURL.Host, - grpc.WithDialer(c.getGRPCDialer(ctx, requestForwardingALPN, "", nil)), + grpc.WithDialer(c.getGRPCDialer(ctx, requestForwardingALPN, "", nil, nil)), grpc.WithInsecure(), // it's not, we handle it in the dialer grpc.WithKeepaliveParams(keepalive.ClientParameters{ Time: 2 * HeartbeatInterval, @@ -343,9 +343,9 @@ func (c *Core) ForwardRequest(req *http.Request) (int, http.Header, []byte, erro // getGRPCDialer is used to return a dialer that has the correct TLS // configuration. Otherwise gRPC tries to be helpful and stomps all over our // NextProtos. -func (c *Core) getGRPCDialer(ctx context.Context, alpnProto, serverName string, caCert *x509.Certificate) func(string, time.Duration) (net.Conn, error) { +func (c *Core) getGRPCDialer(ctx context.Context, alpnProto, serverName string, caCert *x509.Certificate, repClusters *ReplicatedClusters) func(string, time.Duration) (net.Conn, error) { return func(addr string, timeout time.Duration) (net.Conn, error) { - tlsConfig, err := c.ClusterTLSConfig(ctx) + tlsConfig, err := c.ClusterTLSConfig(ctx, repClusters) if err != nil { c.logger.Error("core: failed to get tls configuration", "error", err) return nil, err diff --git a/vault/testing.go b/vault/testing.go index 8ccc0f5697c8..7d1c19722f40 100644 --- a/vault/testing.go +++ b/vault/testing.go @@ -875,6 +875,8 @@ type TestClusterOptions struct { SealFunc func() Seal RawLogger interface{} TempDir string + CACert []byte + CAKey *ecdsa.PrivateKey } var DefaultNumCores = 3 @@ -896,6 +898,8 @@ type certInfo struct { // shared among cores. NewCore's default behavior is to generate a new DefaultSeal if the // provided Seal in coreConfig (i.e. base.Seal) is nil. func NewTestCluster(t testing.T, base *CoreConfig, opts *TestClusterOptions) *TestCluster { + var err error + var numCores int if opts == nil || opts.NumCores == 0 { numCores = DefaultNumCores @@ -909,7 +913,6 @@ func NewTestCluster(t testing.T, base *CoreConfig, opts *TestClusterOptions) *Te } var baseAddr *net.TCPAddr if opts != nil && opts.BaseListenAddress != "" { - var err error baseAddr, err = net.ResolveTCPAddr("tcp", opts.BaseListenAddress) if err != nil { t.Fatal("could not parse given base IP") @@ -933,27 +936,37 @@ func NewTestCluster(t testing.T, base *CoreConfig, opts *TestClusterOptions) *Te testCluster.TempDir = tempDir } - caKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - if err != nil { - t.Fatal(err) + var caKey *ecdsa.PrivateKey + if opts != nil && opts.CAKey != nil { + caKey = opts.CAKey + } else { + caKey, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatal(err) + } } testCluster.CAKey = caKey - caCertTemplate := &x509.Certificate{ - Subject: pkix.Name{ - CommonName: "localhost", - }, - DNSNames: []string{"localhost"}, - IPAddresses: certIPs, - KeyUsage: x509.KeyUsage(x509.KeyUsageCertSign | x509.KeyUsageCRLSign), - SerialNumber: big.NewInt(mathrand.Int63()), - NotBefore: time.Now().Add(-30 * time.Second), - NotAfter: time.Now().Add(262980 * time.Hour), - BasicConstraintsValid: true, - IsCA: true, - } - caBytes, err := x509.CreateCertificate(rand.Reader, caCertTemplate, caCertTemplate, caKey.Public(), caKey) - if err != nil { - t.Fatal(err) + var caBytes []byte + if opts != nil && len(opts.CACert) > 0 { + caBytes = opts.CACert + } else { + caCertTemplate := &x509.Certificate{ + Subject: pkix.Name{ + CommonName: "localhost", + }, + DNSNames: []string{"localhost"}, + IPAddresses: certIPs, + KeyUsage: x509.KeyUsage(x509.KeyUsageCertSign | x509.KeyUsageCRLSign), + SerialNumber: big.NewInt(mathrand.Int63()), + NotBefore: time.Now().Add(-30 * time.Second), + NotAfter: time.Now().Add(262980 * time.Hour), + BasicConstraintsValid: true, + IsCA: true, + } + caBytes, err = x509.CreateCertificate(rand.Reader, caCertTemplate, caCertTemplate, caKey.Public(), caKey) + if err != nil { + t.Fatal(err) + } } caCert, err := x509.ParseCertificate(caBytes) if err != nil { diff --git a/vendor/github.com/fullsailor/pkcs7/pkcs7.go b/vendor/github.com/fullsailor/pkcs7/pkcs7.go index 8d5af853add0..ac03dd3d10e7 100644 --- a/vendor/github.com/fullsailor/pkcs7/pkcs7.go +++ b/vendor/github.com/fullsailor/pkcs7/pkcs7.go @@ -222,6 +222,10 @@ func (p7 *PKCS7) Verify() (err error) { func verifySignature(p7 *PKCS7, signer signerInfo) error { signedData := p7.Content + hash, err := getHashForOID(signer.DigestAlgorithm.Algorithm) + if err != nil { + return err + } if len(signer.AuthenticatedAttributes) > 0 { // TODO(fullsailor): First check the content type match var digest []byte @@ -229,10 +233,6 @@ func verifySignature(p7 *PKCS7, signer signerInfo) error { if err != nil { return err } - hash, err := getHashForOID(signer.DigestAlgorithm.Algorithm) - if err != nil { - return err - } h := hash.New() h.Write(p7.Content) computed := h.Sum(nil) @@ -254,7 +254,18 @@ func verifySignature(p7 *PKCS7, signer signerInfo) error { return errors.New("pkcs7: No certificate for signer") } - algo := x509.SHA1WithRSA + algo := getSignatureAlgorithmFromAI(signer.DigestEncryptionAlgorithm) + if algo == x509.UnknownSignatureAlgorithm { + // I'm not sure what the spec here is, and the openssl sources were not + // helpful. But, this is what App Store receipts appear to do. + // The DigestEncryptionAlgorithm is just "rsaEncryption (PKCS #1)" + // But we're expecting a digest + encryption algorithm. So... we're going + // to determine an algorithm based on the DigestAlgorithm and this + // encryption algorithm. + if signer.DigestEncryptionAlgorithm.Algorithm.Equal(oidEncryptionAlgorithmRSA) { + algo = getRSASignatureAlgorithmForDigestAlgorithm(hash) + } + } return cert.CheckSignature(algo, signedData, signer.EncryptedDigest) } @@ -294,6 +305,15 @@ func getHashForOID(oid asn1.ObjectIdentifier) (crypto.Hash, error) { return crypto.Hash(0), ErrUnsupportedAlgorithm } +func getRSASignatureAlgorithmForDigestAlgorithm(hash crypto.Hash) x509.SignatureAlgorithm { + for _, details := range signatureAlgorithmDetails { + if details.pubKeyAlgo == x509.RSA && details.hash == hash { + return details.algo + } + } + return x509.UnknownSignatureAlgorithm +} + // GetOnlySigner returns an x509.Certificate for the first signer of the signed // data payload. If there are more or less than one signer, nil is returned func (p7 *PKCS7) GetOnlySigner() *x509.Certificate { @@ -633,7 +653,7 @@ func (sd *SignedData) AddSigner(cert *x509.Certificate, pkey crypto.PrivateKey, signer := signerInfo{ AuthenticatedAttributes: finalAttrs, DigestAlgorithm: pkix.AlgorithmIdentifier{Algorithm: oidDigestAlgorithmSHA1}, - DigestEncryptionAlgorithm: pkix.AlgorithmIdentifier{Algorithm: oidEncryptionAlgorithmRSA}, + DigestEncryptionAlgorithm: pkix.AlgorithmIdentifier{Algorithm: oidSignatureSHA1WithRSA}, IssuerAndSerialNumber: ias, EncryptedDigest: signature, Version: 1, diff --git a/vendor/github.com/fullsailor/pkcs7/x509.go b/vendor/github.com/fullsailor/pkcs7/x509.go new file mode 100644 index 000000000000..195fd0e4bb0b --- /dev/null +++ b/vendor/github.com/fullsailor/pkcs7/x509.go @@ -0,0 +1,133 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the go/golang LICENSE file. + +package pkcs7 + +// These are private constants and functions from the crypto/x509 package that +// are useful when dealing with signatures verified by x509 certificates + +import ( + "bytes" + "crypto" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" +) + +var ( + oidSignatureMD2WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2} + oidSignatureMD5WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4} + oidSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5} + oidSignatureSHA256WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11} + oidSignatureSHA384WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12} + oidSignatureSHA512WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13} + oidSignatureRSAPSS = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 10} + oidSignatureDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3} + oidSignatureDSAWithSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 3, 2} + oidSignatureECDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1} + oidSignatureECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2} + oidSignatureECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3} + oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4} + + oidSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 1} + oidSHA384 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 2} + oidSHA512 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 3} + + oidMGF1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 8} + + // oidISOSignatureSHA1WithRSA means the same as oidSignatureSHA1WithRSA + // but it's specified by ISO. Microsoft's makecert.exe has been known + // to produce certificates with this OID. + oidISOSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 29} +) + +var signatureAlgorithmDetails = []struct { + algo x509.SignatureAlgorithm + name string + oid asn1.ObjectIdentifier + pubKeyAlgo x509.PublicKeyAlgorithm + hash crypto.Hash +}{ + {x509.MD2WithRSA, "MD2-RSA", oidSignatureMD2WithRSA, x509.RSA, crypto.Hash(0) /* no value for MD2 */}, + {x509.MD5WithRSA, "MD5-RSA", oidSignatureMD5WithRSA, x509.RSA, crypto.MD5}, + {x509.SHA1WithRSA, "SHA1-RSA", oidSignatureSHA1WithRSA, x509.RSA, crypto.SHA1}, + {x509.SHA1WithRSA, "SHA1-RSA", oidISOSignatureSHA1WithRSA, x509.RSA, crypto.SHA1}, + {x509.SHA256WithRSA, "SHA256-RSA", oidSignatureSHA256WithRSA, x509.RSA, crypto.SHA256}, + {x509.SHA384WithRSA, "SHA384-RSA", oidSignatureSHA384WithRSA, x509.RSA, crypto.SHA384}, + {x509.SHA512WithRSA, "SHA512-RSA", oidSignatureSHA512WithRSA, x509.RSA, crypto.SHA512}, + {x509.SHA256WithRSAPSS, "SHA256-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA256}, + {x509.SHA384WithRSAPSS, "SHA384-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA384}, + {x509.SHA512WithRSAPSS, "SHA512-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA512}, + {x509.DSAWithSHA1, "DSA-SHA1", oidSignatureDSAWithSHA1, x509.DSA, crypto.SHA1}, + {x509.DSAWithSHA256, "DSA-SHA256", oidSignatureDSAWithSHA256, x509.DSA, crypto.SHA256}, + {x509.ECDSAWithSHA1, "ECDSA-SHA1", oidSignatureECDSAWithSHA1, x509.ECDSA, crypto.SHA1}, + {x509.ECDSAWithSHA256, "ECDSA-SHA256", oidSignatureECDSAWithSHA256, x509.ECDSA, crypto.SHA256}, + {x509.ECDSAWithSHA384, "ECDSA-SHA384", oidSignatureECDSAWithSHA384, x509.ECDSA, crypto.SHA384}, + {x509.ECDSAWithSHA512, "ECDSA-SHA512", oidSignatureECDSAWithSHA512, x509.ECDSA, crypto.SHA512}, +} + +// pssParameters reflects the parameters in an AlgorithmIdentifier that +// specifies RSA PSS. See https://tools.ietf.org/html/rfc3447#appendix-A.2.3 +type pssParameters struct { + // The following three fields are not marked as + // optional because the default values specify SHA-1, + // which is no longer suitable for use in signatures. + Hash pkix.AlgorithmIdentifier `asn1:"explicit,tag:0"` + MGF pkix.AlgorithmIdentifier `asn1:"explicit,tag:1"` + SaltLength int `asn1:"explicit,tag:2"` + TrailerField int `asn1:"optional,explicit,tag:3,default:1"` +} + +// asn1.NullBytes is not available prior to Go 1.9 +var nullBytes = []byte{5, 0} + +func getSignatureAlgorithmFromAI(ai pkix.AlgorithmIdentifier) x509.SignatureAlgorithm { + if !ai.Algorithm.Equal(oidSignatureRSAPSS) { + for _, details := range signatureAlgorithmDetails { + if ai.Algorithm.Equal(details.oid) { + return details.algo + } + } + return x509.UnknownSignatureAlgorithm + } + + // RSA PSS is special because it encodes important parameters + // in the Parameters. + + var params pssParameters + if _, err := asn1.Unmarshal(ai.Parameters.FullBytes, ¶ms); err != nil { + return x509.UnknownSignatureAlgorithm + } + + var mgf1HashFunc pkix.AlgorithmIdentifier + if _, err := asn1.Unmarshal(params.MGF.Parameters.FullBytes, &mgf1HashFunc); err != nil { + return x509.UnknownSignatureAlgorithm + } + + // PSS is greatly overburdened with options. This code forces + // them into three buckets by requiring that the MGF1 hash + // function always match the message hash function (as + // recommended in + // https://tools.ietf.org/html/rfc3447#section-8.1), that the + // salt length matches the hash length, and that the trailer + // field has the default value. + if !bytes.Equal(params.Hash.Parameters.FullBytes, nullBytes) || + !params.MGF.Algorithm.Equal(oidMGF1) || + !mgf1HashFunc.Algorithm.Equal(params.Hash.Algorithm) || + !bytes.Equal(mgf1HashFunc.Parameters.FullBytes, nullBytes) || + params.TrailerField != 1 { + return x509.UnknownSignatureAlgorithm + } + + switch { + case params.Hash.Algorithm.Equal(oidSHA256) && params.SaltLength == 32: + return x509.SHA256WithRSAPSS + case params.Hash.Algorithm.Equal(oidSHA384) && params.SaltLength == 48: + return x509.SHA384WithRSAPSS + case params.Hash.Algorithm.Equal(oidSHA512) && params.SaltLength == 64: + return x509.SHA512WithRSAPSS + } + + return x509.UnknownSignatureAlgorithm +} diff --git a/vendor/vendor.json b/vendor/vendor.json index 65a24f048682..7669156f141b 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -835,10 +835,10 @@ "revisionTime": "2018-02-02T13:34:58Z" }, { - "checksumSHA1": "BjjcPf2i7KfBnVazHZCAe9xn6jY=", + "checksumSHA1": "nCoffb4ZRRyCPy2FgR8gMAkQiFo=", "path": "github.com/fullsailor/pkcs7", - "revision": "a009d8d7de53d9503c797cb8ec66fa3b21eed209", - "revisionTime": "2017-06-13T20:12:21Z" + "revision": "1d5002593acb237433a98512c6343587b03ebe5d", + "revisionTime": "2018-02-23T00:23:17Z" }, { "checksumSHA1": "ImX1uv6O09ggFeBPUJJ2nu7MPSA=", diff --git a/website/redirects.txt b/website/redirects.txt index 9f59114bc6ba..0b541201352e 100644 --- a/website/redirects.txt +++ b/website/redirects.txt @@ -69,7 +69,7 @@ /docs/http/sys-raw.html /api/system/raw.html /docs/http/sys-health.html /api/system/health.html -/docs/guides/generate-root.html /guides/configuration/generate-root.html +/docs/guides/generate-root.html /guides/operations/generate-root.html /docs/guides/index.html /guides/index.html /docs/guides/production.html /guides/operations/production.html /docs/guides/replication.html /guides/operations/replication.html @@ -84,12 +84,12 @@ /docs/guides/upgrading/upgrade-to-0.7.0.html /guides/upgrading/upgrade-to-0.7.0.html /guides/production.html /guides/operations/production.html /guides/replication.html /guides/operations/replication.html -/guides/policies.html /guides/configuration/policies.html -/guides/authentication.html /guides/configuration/authentication.html -/guides/lease.html /guides/configuration/lease.html -/guides/generate-root.html /guides/configuration/generate-root.html -/guides/rekeying-and-rotating.html /guides/configuration/rekeying-and-rotating.html -/guides/plugin-backends.html /guides/configuration/plugin-backends.html +/guides/policies.html /guides/identity/policies.html +/guides/authentication.html /guides/identity/authentication.html +/guides/lease.html /guides/identity/lease.html +/guides/generate-root.html /guides/operations/generate-root.html +/guides/rekeying-and-rotating.html /guides/operations/rekeying-and-rotating.html +/guides/plugin-backends.html /guides/operations/plugin-backends.html /guides/static-secrets.html /guides/secret-mgmt/static-secrets.html /guides/dynamic-secret.html /guides/secret-mgmt/dynamic-secret.html /guides/cubbyhole.html /guides/secret-mgmt/cubbyhole.html @@ -98,6 +98,13 @@ /intro/getting-started/acl.html /intro/getting-started/policies.html /intro/getting-started/secret-backends.html /intro/getting-started/secrets-engines.html +/guides/configuration/policies.html /guides/identity/policies.html +/guides/configuration/authentication.html /guides/identity/authentication.html +/guides/configuration/lease.html /guides/identity/lease.html +/guides/configuration/generate-root.html /guides/operations/generate-root.html +/guides/configuration/rekeying-and-rotating.html /guides/operations/rekeying-and-rotating.html +/guides/configuration/plugin-backends.html /guides/operations/plugin-backends.html + /docs/vault-enterprise/index.html /docs/enterprise/index.html /docs/vault-enterprise/replication/index.html /docs/enterprise/replication/index.html /docs/vault-enterprise/hsm/index.html /docs/enterprise/hsm/index.html diff --git a/website/source/api/secret/databases/index.html.md b/website/source/api/secret/databases/index.html.md index 604715990abd..6c3dcee45212 100644 --- a/website/source/api/secret/databases/index.html.md +++ b/website/source/api/secret/databases/index.html.md @@ -101,6 +101,34 @@ $ curl \ } ``` +## List Connections + +This endpoint returns a list of available connections. Only the connection names +are returned, not any values. + +| Method | Path | Produces | +| :------- | :--------------------------- | :--------------------- | +| `LIST` | `/database/config` | `200 application/json` | + +### Sample Request + +``` +$ curl \ + --header "X-Vault-Token: ..." \ + --request LIST \ + https://vault.rocks/v1/database/config +``` + +### Sample Response + +```json +{ + "data": { + "keys": ["db-one", "db-two"] + } +} +``` + ## Delete Connection This endpoint deletes a connection. diff --git a/website/source/api/system/auth.html.md b/website/source/api/system/auth.html.md index 93cfa871e835..062584cf204c 100644 --- a/website/source/api/system/auth.html.md +++ b/website/source/api/system/auth.html.md @@ -191,6 +191,9 @@ can be achieved without `sudo` via `sys/mounts/auth/[auth-path]/tune`._ - `max_lease_ttl` `(int: 0)` – Specifies the maximum time-to-live. If set on a specific auth path, this overrides the global default. +- `description` `(string: "")` – Specifies the description of the mount. This + overrides the current stored value, if any. + ### Sample Payload ```json diff --git a/website/source/api/system/health.html.md b/website/source/api/system/health.html.md index 0bab141f2bf0..f934e507774d 100644 --- a/website/source/api/system/health.html.md +++ b/website/source/api/system/health.html.md @@ -63,6 +63,7 @@ Note: `replication_perf_mode` and `replication_dr_mode` reflect the state of the active node in the cluster; if you are querying it for a standby that has just come up, it can take a small time for the active node to inform the standby of its status. + ```json { "initialized": true, diff --git a/website/source/api/system/mounts.html.md b/website/source/api/system/mounts.html.md index a472d81e9f12..b71cbfbadac3 100644 --- a/website/source/api/system/mounts.html.md +++ b/website/source/api/system/mounts.html.md @@ -199,6 +199,9 @@ This endpoint tunes configuration parameters for a given mount point. overrides the global default. A value of `0` are equivalent and set to the system max TTL. +- `description` `(string: "")` – Specifies the description of the mount. This + overrides the current stored value, if any. + ### Sample Payload ```json diff --git a/website/source/docs/commands/operator/generate-root.html.md b/website/source/docs/commands/operator/generate-root.html.md index 7965860fa531..160bdc45086f 100644 --- a/website/source/docs/commands/operator/generate-root.html.md +++ b/website/source/docs/commands/operator/generate-root.html.md @@ -26,7 +26,7 @@ An unseal key may be provided directly on the command line as an argument to the command. If key is specified as "-", the command will read from stdin. If a TTY is available, the command will prompt for text. -Please see the [generate root guide](/guides/configuration/generate-root.html) for +Please see the [generate root guide](/guides/operations/generate-root.html) for step-by-step instructions. ## Examples diff --git a/website/source/docs/commands/operator/rekey.html.md b/website/source/docs/commands/operator/rekey.html.md index 6ff0c493bd17..b944aae561ee 100644 --- a/website/source/docs/commands/operator/rekey.html.md +++ b/website/source/docs/commands/operator/rekey.html.md @@ -22,7 +22,7 @@ An unseal key may be provided directly on the command line as an argument to the command. If key is specified as "-", the command will read from stdin. If a TTY is available, the command will prompt for text. -Please see the [rotating and rekeying](/guides/configuration/rekeying-and-rotating.html) for +Please see the [rotating and rekeying](/guides/operations/rekeying-and-rotating.html) for step-by-step instructions. ## Examples diff --git a/website/source/docs/concepts/policies.html.md b/website/source/docs/concepts/policies.html.md index 6f15d630b585..eaffadb2e126 100644 --- a/website/source/docs/concepts/policies.html.md +++ b/website/source/docs/concepts/policies.html.md @@ -113,8 +113,8 @@ path "secret/super-secret" { capabilities = ["deny"] } -# Policies can also specify allowed, disallowed, and required parameters. Here -# the key "secret/restricted" can only contain "foo" (any value) and "bar" (one +# Policies can also specify allowed, disallowed, and required parameters. Here +# the key "secret/restricted" can only contain "foo" (any value) and "bar" (one # of "zip" or "zap"). path "secret/restricted" { capabilities = ["create"] @@ -228,13 +228,13 @@ options are: ```ruby # This requires the user to create "secret/foo" with a parameter named - # "bar" and "baz". + # "bar" and "baz". path "secret/foo" { capabilities = ["create"] required_parameters = ["bar", "baz"] } ``` - + * `allowed_parameters` - Whitelists a list of keys and values that are permitted on the given path. @@ -438,7 +438,7 @@ $ curl \ For more information, please read: - [Production Hardening](/guides/operations/production.html) -- [Generating a Root Token](/guides/configuration/generate-root.html) +- [Generating a Root Token](/guides/operations/generate-root.html) ## Managing Policies diff --git a/website/source/docs/concepts/tokens.html.md b/website/source/docs/concepts/tokens.html.md index 53e3d12e76d2..8c7fd43c1c07 100644 --- a/website/source/docs/concepts/tokens.html.md +++ b/website/source/docs/concepts/tokens.html.md @@ -54,7 +54,7 @@ of version 0.6.1, there are only three ways to create root tokens: expiration 2. By using another root token; a root token with an expiration cannot create a root token that never expires -3. By using `vault generate-root` ([example](/guides/configuration/generate-root.html)) +3. By using `vault generate-root` ([example](/guides/operations/generate-root.html)) with the permission of a quorum of unseal key holders Root tokens are useful in development but should be extremely carefully guarded diff --git a/website/source/docs/configuration/seal/pkcs11.html.md b/website/source/docs/configuration/seal/pkcs11.html.md index 62fc37779bea..cdc6966c8b10 100644 --- a/website/source/docs/configuration/seal/pkcs11.html.md +++ b/website/source/docs/configuration/seal/pkcs11.html.md @@ -25,13 +25,16 @@ HSM key backup strategy requires the key to be exportable, you should generate the key yourself. The list of creation attributes that Vault uses to generate the key are listed at the end of this document. - ## Requirements The following software packages are required for Vault Enterprise HSM: -- PKCS#11 compatible HSM integration library -- The [GNU libltdl library](https://www.gnu.org/software/libtool/manual/html_node/Using-libltdl.html) — ensure that it is installed for the correct architecture of your servers +- PKCS#11 compatible HSM integration library. Vault targets version 2.2 or + higher of PKCS#11. Depending on any given HSM, some functions (such as key + generation) may have to be performed manually. +- The [GNU libltdl + library](https://www.gnu.org/software/libtool/manual/html_node/Using-libltdl.html) + — ensure that it is installed for the correct architecture of your servers ## `pkcs11` Example @@ -74,10 +77,11 @@ These parameters apply to the `seal` stanza in the Vault configuration file: environment variable. - `hmac_key_label` `(string: )`: The label of the key to use for - HMACing. This needs to be a suitable type; a good choice is an AES key marked - as valid for signing and verifying. If the key does not exist and generation - is enabled, this is the label that will be given to the generated key. May - also be specified by the `VAULT_HSM_HMAC_KEY_LABEL` environment variable. + HMACing. This needs to be a suitable type. If Vault tries to create this it + will attempt to use CKK_GENERIC_SECRET_KEY. If the key does not exist and + generation is enabled, this is the label that will be given to the generated + key. May also be specified by the `VAULT_HSM_HMAC_KEY_LABEL` environment + variable. - `mechanism` `(string: "0x1082")`: The encryption/decryption mechanism to use, specified as a decimal or hexadecimal (prefixed by `0x`) string. Currently @@ -95,12 +99,15 @@ These parameters apply to the `seal` stanza in the Vault configuration file: specified by `key_label` can be found at Vault initialization time, instructs Vault to generate a key. This is a boolean expressed as a string (e.g. `"true"`). May also be specified by the `VAULT_HSM_GENERATE_KEY` environment - variable. + variable. Vault may not be able to successfully generate keys in all + circumstances, such as if proprietary vendor extensions are required to + create keys of a suitable type. -- `regenerate_key` `(string: "false")`: At Vault initialization time, force - generation of a new key even if one with the given `key_label` already exists. - This is a boolean expressed as a string (e.g. `"true"`). May also be specified - by the `VAULT_HSM_REGENERATE_KEY` environment variable. +- `regenerate_key` `(string: "false")`: Force generation of a new key even if + one with the given `key_label` and `hmac_key_label` already exists. _**This + will render previous data unrecoverable**_ and is meant for testing scenarios. + This is a boolean expressed as a string (e.g. `"true"`). May also be + specified by the `VAULT_HSM_REGENERATE_KEY` environment variable. ~> **Note:** Although the configuration file allows you to pass in `VAULT_HSM_PIN` as part of the seal's parameters, it is *strongly* reccommended @@ -144,3 +151,20 @@ it uses. These identifiers correspond to official PKCS#11 identifiers. * `CKA_WRAP`: `true` (Key can be used for wrapping) * `CKA_UNWRAP`: `true` (Key can be used for unwrapping) * `CKA_EXTRACTABLE`: `false` (Key cannot be exported) + +If Vault generates the HMAC key for you, the following is the list of +attributes it uses. These identifiers correspond to official PKCS#11 +identifiers. + +* `CKA_CLASS`: `CKO_SECRET_KEY` (It's a secret key) +* `CKA_KEY_TYPE`: `CKK_GENERIC_SECRET_KEY` (Key type is a generic secret key) +* `CKA_VALUE_LEN`: `32` (Key size is 256 bits) +* `CKA_LABEL`: Set to the HMAC key label set in Vault's configuration +* `CKA_ID`: Set to a random 32-bit unsigned integer +* `CKA_PRIVATE`: `true` (Key is private to this slot/token) +* `CKA_TOKEN`: `true` (Key persists to the slot/token rather than being for one + session only) +* `CKA_SENSITIVE`: `true` (Key is a sensitive value) +* `CKA_SIGN`: `true` (Key can be used for signing) +* `CKA_VERIFY`: `true` (Key can be used for verifying) +* `CKA_EXTRACTABLE`: `false` (Key cannot be exported) diff --git a/website/source/docs/configuration/storage/consul.html.md b/website/source/docs/configuration/storage/consul.html.md index b8f0ac3510e4..e327fe3696fe 100644 --- a/website/source/docs/configuration/storage/consul.html.md +++ b/website/source/docs/configuration/storage/consul.html.md @@ -86,6 +86,14 @@ at Consul's service discovery layer. - `service_tags` `(string: "")` – Specifies a comma-separated list of tags to attach to the service registration in Consul. +- `service_address` `(string: nil)` – Specifies a service-specific address to + set on the service registration in Consul. If unset, Vault will use what it + knows to be the HA redirect address - which is usually desirable. Setting + this parameter to `""` will tell Consul to leverage the configuration of the + node the service is registered on dynamically. This could be beneficial if + you intend to leverage Consul's + [`translate_wan_addrs`](consul-translate-wan-addrs) parameter. + - `token` `(string: "")` – Specifies the [Consul ACL token][consul-acl] with permission to read and write from the `path` in Consul's key-value store. This is **not** a Vault token. See the ACL section below for help. @@ -216,3 +224,4 @@ storage "consul" { [consul-acl]: https://www.consul.io/docs/guides/acl.html "Consul ACLs" [consul-consistency]: https://www.consul.io/api/index.html#consistency-modes "Consul Consistency Modes" [consul-encryption]: https://www.consul.io/docs/agent/encryption.html "Consul Encryption" +[consul-translate-wan-addrs]: https://www.consul.io/docs/agent/options.html#translate_wan_addrs "Consul Configuration" diff --git a/website/source/guides/configuration/generate-root.html.md b/website/source/guides/configuration/generate-root.html.md deleted file mode 100644 index ed88c7a4bb2d..000000000000 --- a/website/source/guides/configuration/generate-root.html.md +++ /dev/null @@ -1,158 +0,0 @@ ---- -layout: "guides" -page_title: "Generate Root Tokens using Unseal Keys - Guides" -sidebar_current: "guides-configuration-generate-root" -description: |- - Generate a new root token using a threshold of unseal keys. ---- - -# Generate Root Tokens Using Unseal Keys - -It is generally considered a best practice to not persist -[root tokens][root-tokens]. Instead a root token should be generated using -Vault's `generate-root` command only when absolutely necessary. This guide -demonstrates regenerating a root token. - -1. Unseal the vault using the existing quorum of unseal keys. You do not need to - be authenticated to generate a new root token, but the Vault must be unsealed - and a quorum of unseal keys must be available. - - ```shell - $ vault operator unseal - # ... - ``` - -### Using OTP - -In this method, an OTP is XORed with the generated token on final output. - -1. Generate a one-time password (OTP) to use for XORing the resulting token: - - ```text - $ vault operator generate-root -generate-otp - mOXx7iVimjE6LXQ2Zna6NA== - ``` - - Save this OTP because you will need it to get the decoded final root token. - -1. Initialize a root token generation, providing the OTP code from the step - above: - - ```text - $ vault operator generate-root -init -otp=mOXx7iVimjE6LXQ2Zna6NA== - Nonce f67f4da3-4ae4-68fb-4716-91da6b609c3e - Started true - Progress 0/5 - Complete false - ``` - - The nonce value should be distributed to all unseal key holders. - -1. Each unseal key holder providers their unseal key: - - ```text - $ vault operator generate-root - Root generation operation nonce: f67f4da3-4ae4-68fb-4716-91da6b609c3e - Unseal Key (will be hidden): ... - ``` - - If there is a tty, Vault will prompt for the key and automatically - complete the nonce value. If there is no tty, or if the value is piped - from stdin, the user must specify the nonce value from the `-init` - operation. - - ```text - $ echo $UNSEAL_KEY | vault operator generate-root -nonce=f67f4da3... - - ``` - -1. When the quorum of unseal keys are supplied, the final user will also get - the encoded root token. - - ```text - $ vault operator generate-root - Root generation operation nonce: f67f4da3-4ae4-68fb-4716-91da6b609c3e - Unseal Key (will be hidden): - - Nonce f67f4da3-4ae4-68fb-4716-91da6b609c3e - Started true - Progress 5/5 - Complete true - Root Token IxJpyqxn3YafOGhqhvP6cQ== - ``` - -1. Decode the encoded token using the OTP: - - ```text - $ vault operator generate-root \ - -decode=IxJpyqxn3YafOGhqhvP6cQ== \ - -otp=mOXx7iVimjE6LXQ2Zna6NA== - - 24bde68f-3df3-e137-cf4d-014fe9ebc43f - ``` - -### Using PGP - -1. Initialize a root token generation, providing the path to a GPG public key - or keybase username of a user to encrypted the resulting token. - - ```text - $ vault operator generate-root -init -pgp-key=keybase:sethvargo - Nonce e24dec5e-f1ea-2dfe-ecce-604022006976 - Started true - Progress 0/5 - Complete false - PGP Fingerprint e2f8e2974623ba2a0e933a59c921994f9c27e0ff - ``` - - The nonce value should be distributed to all unseal key holders. - -1. Each unseal key holder providers their unseal key: - - ```text - $ vault operator generate-root - Root generation operation nonce: e24dec5e-f1ea-2dfe-ecce-604022006976 - Unseal Key (will be hidden): ... - ``` - - If there is a tty, Vault will prompt for the key and automatically - complete the nonce value. If there is no tty, or if the value is piped - from stdin, the user must specify the nonce value from the `-init` - operation. - - ```text - $ echo $UNSEAL_KEY | vault generate-root -nonce=f67f4da3... - - ``` - -1. When the quorum of unseal keys are supplied, the final user will also get - the encoded root token. - - ```text - $ vault operator generate-root - Root generation operation nonce: e24dec5e-f1ea-2dfe-ecce-604022006976 - Unseal Key (will be hidden): - - Nonce e24dec5e-f1ea-2dfe-ecce-604022006976 - Started true - Progress 1/1 - Complete true - PGP Fingerprint e2f8e2974623ba2a0e933a59c921994f9c27e0ff - Root Token wcFMA0RVkFtoqzRlARAAI3Ux8kdSpfgXdF9mg... - ``` - -1. Decrypt the encrypted token using associated private key: - - ```text - $ echo "wcFMA0RVkFtoqzRlARAAI3Ux8kdSpfgXdF9mg..." | base64 --decode | gpg --decrypt - - d0f71e9b-ebff-6d8a-50ae-b8859f2e5671 - ``` - - or via keybase: - - ```text - $ echo "wcFMA0RVkFtoqzRlARAAI3Ux8kdSpfgXdF9mg..." | base64 --decode | keybase pgp decrypt - - d0f71e9b-ebff-6d8a-50ae-b8859f2e5671 - ``` - -[root-tokens]: /docs/concepts/tokens.html#root-tokens diff --git a/website/source/guides/configuration/index.html.md b/website/source/guides/configuration/index.html.md deleted file mode 100644 index eb12ae5fd94e..000000000000 --- a/website/source/guides/configuration/index.html.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -layout: "guides" -page_title: "Vault Configuration - Guides" -sidebar_current: "guides-configuration" -description: |- - Once a Vault instance has been installed, the next step is to configure auth - backends, secret backends, and manage keys. Vault configuration guides addresses - key concepts in configuring your Vault application. ---- - -# Vault Configuration - -This guide walks you through Vault configuration topics. - -- [Policies](/guides/configuration/policies.html) are used to instrument -Role-Based Access Control (RBAC) by specifying access privileges. Authoring of -policies is probably the first step the Vault administrator performs. This guide -walks you through creating example policies for `admin` and `provisioner` users. -- [AppRole Pull Authentication](/guides/configuration/authentication.html) guide -that introduces the steps to generate tokens for machines or apps by enabling -AppRole auth backend. -- [Token and Leases](/guides/configuration/lease.html) guide helps you -understand how tokens and leases work in Vault. The understanding of the -lease hierarchy and expiration mechanism helps you plan for break glass -procedures and more. -- [Root Token Generation](/guides/configuration/generate-root.html) guide -demonstrates the workflow of regenerating root tokens. It is considered to be a -best practice not to persist the initial **root** token. If a root token needs -to be regenerated, this guide helps you walk through the task. -- [Rekeying & Rotating](/guides/configuration/rekeying-and-rotating.html) guide -provides a high-level overview of Shamir's Secret Sharing Algorithm, and how to -perform _rekey_ and _rotate_ operations in Vault. -- [Building Plugin Backends](/guides/configuration/plugin-backends.html) guide -provides steps to build, register, and mount non-database external plugin -backends. diff --git a/website/source/guides/configuration/authentication.html.md b/website/source/guides/identity/authentication.html.md similarity index 98% rename from website/source/guides/configuration/authentication.html.md rename to website/source/guides/identity/authentication.html.md index 882cb0d227ec..e86d84e1079a 100644 --- a/website/source/guides/configuration/authentication.html.md +++ b/website/source/guides/identity/authentication.html.md @@ -1,7 +1,7 @@ --- layout: "guides" page_title: "AppRole Pull Authentication - Guides" -sidebar_current: "guides-configuration-authentication" +sidebar_current: "guides-identity-authentication" description: |- Authentication is a process in Vault by which user or machine-supplied information is verified to create a token with pre-configured policy. @@ -123,7 +123,7 @@ path "secret/mysql/*" { ``` If you are not familiar with policies, complete the -[policies](/guides/configuration/policies.html) guide. +[policies](/guides/identity/policies.html) guide. ## Steps @@ -235,7 +235,7 @@ $ vault write auth/approle/role/ [parameters] > specify `token_num_uses` and `token_ttl`. You may never want the app token to > expire. In such a case, specify the `period` so that the token generated by > this AppRole is a periodic token. To learn more about periodic token, refer to -> the [Tokens and Leases](/guides/configuration/lease.html#step4) guide. +> the [Tokens and Leases](/guides/identity/lease.html#step4) guide. **Example:** @@ -304,7 +304,7 @@ $ curl --header "X-Vault-Token: ..." --request POST \ > specify `token_num_uses` and `token_ttl`. You may never want the app token to > expire. In such a case, specify the `period` so that the token generated by > this AppRole is a periodic token. To learn more about periodic token, refer to -> the [Tokens and Leases](/guides/configuration/lease.html#step4) guide. +> the [Tokens and Leases](/guides/identity/lease.html#step4) guide. **NOTE:** To attach multiple policies, pass the policy names as a comma diff --git a/website/source/guides/identity/index.html.md b/website/source/guides/identity/index.html.md new file mode 100644 index 000000000000..53aefd6451e6 --- /dev/null +++ b/website/source/guides/identity/index.html.md @@ -0,0 +1,25 @@ +--- +layout: "guides" +page_title: "Identity and Access Management - Guides" +sidebar_current: "guides-identity" +description: |- + Once a Vault instance has been installed, the next step is to configure auth + backends, secret backends, and manage keys. Vault configuration guides addresses + key concepts in configuring your Vault application. +--- + +# Identity and Access Management + +This guide walks you through Identity and Access Management topics. + +- [Policies](/guides/identity/policies.html) are used to instrument +Role-Based Access Control (RBAC) by specifying access privileges. Authoring of +policies is probably the first step the Vault administrator performs. This guide +walks you through creating example policies for `admin` and `provisioner` users. +- [AppRole Pull Authentication](/guides/identity/authentication.html) guide +that introduces the steps to generate tokens for machines or apps by enabling +AppRole auth backend. +- [Token and Leases](/guides/identity/lease.html) guide helps you +understand how tokens and leases work in Vault. The understanding of the +lease hierarchy and expiration mechanism helps you plan for break glass +procedures and more. diff --git a/website/source/guides/configuration/lease.html.md b/website/source/guides/identity/lease.html.md similarity index 98% rename from website/source/guides/configuration/lease.html.md rename to website/source/guides/identity/lease.html.md index 9060e661cdb7..828aa072727d 100644 --- a/website/source/guides/configuration/lease.html.md +++ b/website/source/guides/identity/lease.html.md @@ -1,7 +1,7 @@ --- layout: "guides" page_title: "Tokens and Leases - Guides" -sidebar_current: "guides-configuration-lease" +sidebar_current: "guides-identity-lease" description: |- Tokens are the core method for authentication within Vault. For every authentication token and dynamic secret, Vault creates a lease @@ -131,7 +131,7 @@ path "sys/mounts/database/tune" { ``` If you are not familiar with policies, complete the -[policies](/guides/configuration/policies.html) guide. +[policies](/guides/identity/policies.html) guide. ## Steps @@ -625,7 +625,7 @@ are talking about long-running apps need to be able to renew its token indefinitely. -> For more details about AppRole, read the [AppRole Pull --Authentication](/guides/configuration/authentication.html) guide. +-Authentication](/guides/identity/authentication.html) guide. To create AppRole periodic tokens, create your AppRole role with `period` specified. @@ -803,5 +803,5 @@ renewable true ## Next steps Now you have learned the lifecycle of tokens and leases, read [AppRole Pull -Authentication](/guides/configuration/authentication.html) guide to learn how to generate +Authentication](/guides/identity/authentication.html) guide to learn how to generate tokens for apps or machines. diff --git a/website/source/guides/configuration/policies.html.md b/website/source/guides/identity/policies.html.md similarity index 99% rename from website/source/guides/configuration/policies.html.md rename to website/source/guides/identity/policies.html.md index 343b98e7d86f..1e3bd21824ea 100644 --- a/website/source/guides/configuration/policies.html.md +++ b/website/source/guides/identity/policies.html.md @@ -1,7 +1,7 @@ --- layout: "guides" page_title: "Policies - Guides" -sidebar_current: "guides-configuration-policies" +sidebar_current: "guides-identity-policies" description: |- Policies in Vault control what a user can access. --- @@ -584,5 +584,5 @@ $ curl --request POST --header "X-Vault-Token: ..." --data '{"path":"sys/auth/ap ## Next steps In this guide, you learned how to write policies based on given policy -requirements. Next, [AppRole Pull Authentication](/guides/configuration/authentication.html) +requirements. Next, [AppRole Pull Authentication](/guides/identity/authentication.html) guide demonstrates how to associate policies to a role. diff --git a/website/source/guides/generate-root.html.md b/website/source/guides/operations/generate-root.html.md similarity index 98% rename from website/source/guides/generate-root.html.md rename to website/source/guides/operations/generate-root.html.md index 998a5ce84aea..83592cc6d4ef 100644 --- a/website/source/guides/generate-root.html.md +++ b/website/source/guides/operations/generate-root.html.md @@ -1,7 +1,7 @@ --- layout: "guides" page_title: "Generate Root Tokens using Unseal Keys - Guides" -sidebar_current: "guides-generate-root" +sidebar_current: "guides-operations-generate-root" description: |- Generate a new root token using a threshold of unseal keys. --- diff --git a/website/source/guides/operations/index.html.md b/website/source/guides/operations/index.html.md index 83a3d02d663f..1445981eed41 100644 --- a/website/source/guides/operations/index.html.md +++ b/website/source/guides/operations/index.html.md @@ -21,3 +21,13 @@ and focus on defense in depth. walks you through the commands to activate the Vault servers in replication mode. Please note that [Vault Replication](/docs/vault-enterprise/replication/index.html) is a Vault Enterprise feature. +- [Root Token Generation](/guides/operations/generate-root.html) guide +demonstrates the workflow of regenerating root tokens. It is considered to be a +best practice not to persist the initial **root** token. If a root token needs +to be regenerated, this guide helps you walk through the task. +- [Rekeying & Rotating](/guides/operations/rekeying-and-rotating.html) guide +provides a high-level overview of Shamir's Secret Sharing Algorithm, and how to +perform _rekey_ and _rotate_ operations in Vault. +- [Building Plugin Backends](/guides/operations/plugin-backends.html) guide +provides steps to build, register, and mount non-database external plugin +backends. diff --git a/website/source/guides/configuration/plugin-backends.html.md b/website/source/guides/operations/plugin-backends.html.md similarity index 98% rename from website/source/guides/configuration/plugin-backends.html.md rename to website/source/guides/operations/plugin-backends.html.md index d31a9d6d4f5e..af53049e69c4 100644 --- a/website/source/guides/configuration/plugin-backends.html.md +++ b/website/source/guides/operations/plugin-backends.html.md @@ -1,7 +1,7 @@ --- layout: "guides" page_title: "Plugin Backends - Guides" -sidebar_current: "guides-configuration-plugin-backends" +sidebar_current: "guides-operations-plugin-backends" description: |- Learn how to build, register, and mount a custom plugin backend. --- diff --git a/website/source/guides/operations/production.html.md b/website/source/guides/operations/production.html.md index 13f3ad9466a4..e85c5f7e99e2 100644 --- a/website/source/guides/operations/production.html.md +++ b/website/source/guides/operations/production.html.md @@ -81,7 +81,7 @@ and practical. code](https://www.hashicorp.com/blog/codifying-vault-policies-and-configuration/), and using version control to manage policies. Once setup, the root token should be revoked to eliminate the risk of exposure. Root tokens can be - [generated when needed](/guides/configuration/generate-root.html), and should be + [generated when needed](/guides/operations/generate-root.html), and should be revoked as soon as possible. * **Enable Auditing**. Vault supports several auditing backends. Enabling diff --git a/website/source/guides/configuration/rekeying-and-rotating.html.md b/website/source/guides/operations/rekeying-and-rotating.html.md similarity index 98% rename from website/source/guides/configuration/rekeying-and-rotating.html.md rename to website/source/guides/operations/rekeying-and-rotating.html.md index c4d713536cf0..df8577a87edc 100644 --- a/website/source/guides/configuration/rekeying-and-rotating.html.md +++ b/website/source/guides/operations/rekeying-and-rotating.html.md @@ -1,7 +1,7 @@ --- layout: "guides" page_title: "Rekeying & Rotating Vault - Guides" -sidebar_current: "guides-configuration-rekeying-and-rotating" +sidebar_current: "guides-operations-rekeying-and-rotating" description: |- Vault supports generating new unseal keys as well as rotating the underlying encryption keys. This guide covers rekeying and rotating Vault's encryption diff --git a/website/source/guides/secret-mgmt/cubbyhole.html.md b/website/source/guides/secret-mgmt/cubbyhole.html.md index b0c6905f3dee..b1ee2bc3f874 100644 --- a/website/source/guides/secret-mgmt/cubbyhole.html.md +++ b/website/source/guides/secret-mgmt/cubbyhole.html.md @@ -539,7 +539,7 @@ Also, refer to [Cubbyhole Secret Backend HTTP API](/api/secret/cubbyhole/index.h ## Next steps -The use of [AppRole Pull Authentication](/guides/configuration/authentication.html) is a good +The use of [AppRole Pull Authentication](/guides/identity/authentication.html) is a good use case to leverage the response wrapping. Go through the guide if you have not done so. To better understand the lifecycle of Vault tokens, proceed to [Tokens -and Leases](/guides/configuration/lease.html) guide. +and Leases](/guides/identity/lease.html) guide. diff --git a/website/source/guides/secret-mgmt/dynamic-secrets.html.md b/website/source/guides/secret-mgmt/dynamic-secrets.html.md index 3e8e0167de26..bd6057ace8cf 100644 --- a/website/source/guides/secret-mgmt/dynamic-secrets.html.md +++ b/website/source/guides/secret-mgmt/dynamic-secrets.html.md @@ -124,7 +124,7 @@ path "auth/token/create" { ``` If you are not familiar with policies, complete the -[policies](/guides/configuration/policies.html) guide. +[policies](/guides/identity/policies.html) guide. ## Steps @@ -338,7 +338,7 @@ token_policies [apps default] Use the returned token to perform the remaining. -**NOTE:** [AppRole Pull Authentication](/guides/configuration/authentication.html) guide +**NOTE:** [AppRole Pull Authentication](/guides/identity/authentication.html) guide demonstrates more sophisticated way of generating a token for your apps. ```shell @@ -412,7 +412,7 @@ $ curl --header "X-Vault-Token: ..." --request POST \ Be sure to use the returned token to perform the remaining. -**NOTE:** [AppRole Pull Authentication](/guides/configuration/authentication.html) guide +**NOTE:** [AppRole Pull Authentication](/guides/identity/authentication.html) guide demonstrates more sophisticated way of generating a token for your apps. ```shell @@ -498,5 +498,5 @@ user name exists. This guide discussed how to generate credentials on--dataemand so that the access credentials no longer need to be written to disk. Next, learn about the -[Tokens and Leases](/guides/configuration/lease.html) so that you can control the lifecycle of +[Tokens and Leases](/guides/identity/lease.html) so that you can control the lifecycle of those credentials. diff --git a/website/source/guides/secret-mgmt/static-secrets.html.md b/website/source/guides/secret-mgmt/static-secrets.html.md index 4ebb8e1d8637..f91776d5cea4 100644 --- a/website/source/guides/secret-mgmt/static-secrets.html.md +++ b/website/source/guides/secret-mgmt/static-secrets.html.md @@ -94,7 +94,7 @@ path "auth/token/create" { ``` If you are not familiar with policies, complete the -[policies](/guides/configuration/policies.html) guide. +[policies](/guides/identity/policies.html) guide. ## Steps @@ -545,5 +545,5 @@ $ cat mongodb.txt This guide introduced the CLI commands and API endpoints to read and write secrets in key/value backend. To keep it simple, the `devops` persona generated a token for `apps`. Read [AppRole Pull -Authentication](/guides/configuration/authentication.html) guide to learn about +Authentication](/guides/identity/authentication.html) guide to learn about programmatically generate a token for apps. diff --git a/website/source/layouts/guides.erb b/website/source/layouts/guides.erb index 02d431f629f8..2d303c787502 100644 --- a/website/source/layouts/guides.erb +++ b/website/source/layouts/guides.erb @@ -16,29 +16,29 @@ > Replication Setup & Guidance + > + Root Token Generation + + > + Rekeying & Rotating + + > + Building Plugin Backends + - > - Vault Configuration + > + Identity and Access Management