From b7d6d55ac12c7ffbf9f78dbde26acd19ec7662f7 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Mon, 17 Sep 2018 23:03:00 -0400 Subject: [PATCH] The big one (#5346) --- .hooks/pre-push | 4 + Makefile | 8 +- audit/format.go | 125 +- audit/format_json_test.go | 4 +- audit/format_jsonx_test.go | 7 +- builtin/logical/database/backend_test.go | 107 +- .../logical/database/dbplugin/plugin_test.go | 17 +- command/agent/aws_end_to_end_test.go | 4 +- command/login_test.go | 5 +- command/operator_generate_root_test.go | 62 +- command/operator_init_test.go | 3 +- command/policy_fmt.go | 6 +- command/server.go | 97 +- command/server/seal/server_seal.go | 15 + command/server_devfourcluster.go | 313 ++++ helper/forwarding/types.pb.go | 84 +- helper/forwarding/types.proto | 1 + helper/identity/mfa/types.pb.go | 69 + helper/identity/mfa/types.proto | 7 + helper/identity/templating.go | 15 +- helper/identity/templating_test.go | 10 +- helper/identity/types.pb.go | 388 ++++- helper/identity/types.proto | 45 +- helper/license/feature.go | 12 + helper/namespace/namespace.go | 17 +- helper/testhelpers/testhelpers.go | 205 ++- http/auth_token_test.go | 2 +- http/forwarding_bench_test.go | 5 +- http/forwarding_test.go | 9 +- http/handler.go | 215 ++- http/handler_test.go | 122 +- http/help.go | 35 +- http/logical.go | 95 +- http/logical_test.go | 10 +- http/plugin_test.go | 8 +- http/sys_generate_root.go | 42 +- http/sys_generate_root_test.go | 38 +- http/sys_rekey_test.go | 354 ++-- http/sys_seal.go | 47 +- http/sys_seal_test.go | 60 +- http/util.go | 22 + logical/auth.go | 4 + logical/error.go | 12 + logical/framework/backend.go | 9 + logical/framework/path.go | 5 + logical/identity.pb.go | 16 +- logical/plugin/grpc_system.go | 6 + logical/plugin/pb/backend.pb.go | 576 ++++--- logical/plugin/pb/backend.proto | 25 + logical/plugin/pb/translation.go | 123 +- logical/plugin/system.go | 6 + logical/request.go | 8 + logical/request_util.go | 14 + logical/response_util.go | 8 +- logical/system_view.go | 9 + logical/testing/testing.go | 7 +- logical/token.go | 9 + physical/cache.go | 1 + physical/consul/consul.go | 46 +- physical/consul/consul_test.go | 54 +- physical/error.go | 103 ++ physical/physical.go | 15 +- physical/physical_access.go | 4 +- physical/physical_util.go | 10 + physical/transactions.go | 5 + physical/types.pb.go | 1 + vault/acl.go | 144 +- vault/acl_test.go | 288 +++- vault/acl_util.go | 14 + vault/audit.go | 68 +- vault/audit_broker.go | 15 +- vault/audit_test.go | 47 +- vault/auth.go | 271 ++- vault/auth_test.go | 43 +- vault/barrier_view.go | 15 +- vault/barrier_view_test.go | 1 + vault/barrier_view_util.go | 5 + vault/capabilities.go | 35 +- vault/capabilities_test.go | 47 +- vault/cluster.go | 90 +- vault/cluster_test.go | 5 +- vault/cluster_tls.go | 85 + vault/core.go | 254 +-- vault/core_test.go | 184 +- vault/core_util.go | 102 ++ vault/dynamic_system_view.go | 56 +- vault/expiration.go | 529 ++++-- vault/expiration_test.go | 256 ++- vault/expiration_util.go | 29 + vault/generate_root.go | 57 +- vault/generate_root_test.go | 54 +- vault/ha.go | 14 +- vault/identity_lookup.go | 8 +- vault/identity_lookup_test.go | 56 +- vault/identity_store.go | 123 +- vault/identity_store_aliases.go | 77 +- vault/identity_store_aliases_test.go | 53 +- vault/identity_store_entities.go | 95 +- vault/identity_store_entities_test.go | 93 +- vault/identity_store_group_aliases.go | 71 +- vault/identity_store_group_aliases_test.go | 38 +- vault/identity_store_groups.go | 63 +- vault/identity_store_groups_test.go | 137 +- vault/identity_store_schema.go | 56 +- vault/identity_store_test.go | 59 +- vault/identity_store_util.go | 213 ++- vault/init.go | 19 +- vault/init_test.go | 11 +- vault/logical_cubbyhole.go | 46 +- vault/logical_passthrough.go | 2 +- vault/logical_system.go | 1460 ++++------------ vault/logical_system_helpers.go | 79 + vault/logical_system_integ_test.go | 24 +- vault/logical_system_paths.go | 1080 ++++++++++++ vault/logical_system_test.go | 344 ++-- vault/mount.go | 450 +++-- vault/mount_test.go | 43 +- vault/mount_util.go | 42 + vault/namespaces.go | 18 + vault/plugin_reload.go | 79 +- vault/policy.go | 138 +- vault/policy_store.go | 387 ++++- vault/policy_store_test.go | 239 ++- vault/policy_store_util.go | 47 + vault/policy_test.go | 39 +- vault/policy_util.go | 5 + vault/rekey_test.go | 45 +- vault/replication_cluster_util.go | 11 + vault/request_forwarding.go | 212 +-- vault/request_forwarding_rpc.go | 133 ++ vault/request_forwarding_rpc_util.go | 17 + vault/request_forwarding_service.pb.go | 296 +++- vault/request_forwarding_service.proto | 18 + vault/request_forwarding_util.go | 18 + vault/request_handling.go | 415 +++-- vault/request_handling_test.go | 18 +- vault/request_handling_util.go | 32 + vault/rollback.go | 59 +- vault/rollback_test.go | 14 +- vault/router.go | 241 ++- vault/router_access.go | 6 +- vault/router_test.go | 103 +- vault/seal.go | 20 +- vault/seal/seal.go | 10 + vault/seal_testing.go | 32 +- vault/seal_testing_util.go | 9 + vault/sealunwrapper.go | 5 +- vault/sealunwrapper_test.go | 11 +- vault/testing.go | 115 +- vault/testing_util.go | 10 + vault/token_store.go | 1499 ++++++++++------- vault/token_store_test.go | 760 +++++---- vault/token_store_util.go | 27 + vault/wrapping.go | 35 +- vault/wrapping_util.go | 13 + version/version_base.go | 2 +- 156 files changed, 11177 insertions(+), 5181 deletions(-) create mode 100644 command/server/seal/server_seal.go create mode 100644 command/server_devfourcluster.go create mode 100644 helper/identity/mfa/types.pb.go create mode 100644 helper/identity/mfa/types.proto create mode 100644 helper/license/feature.go create mode 100644 http/util.go create mode 100644 logical/request_util.go create mode 100644 physical/error.go create mode 100644 physical/physical_util.go create mode 100644 vault/acl_util.go create mode 100644 vault/barrier_view_util.go create mode 100644 vault/cluster_tls.go create mode 100644 vault/core_util.go create mode 100644 vault/expiration_util.go create mode 100644 vault/logical_system_paths.go create mode 100644 vault/mount_util.go create mode 100644 vault/namespaces.go create mode 100644 vault/policy_store_util.go create mode 100644 vault/policy_util.go create mode 100644 vault/replication_cluster_util.go create mode 100644 vault/request_forwarding_rpc.go create mode 100644 vault/request_forwarding_rpc_util.go create mode 100644 vault/request_forwarding_util.go create mode 100644 vault/request_handling_util.go create mode 100644 vault/seal/seal.go create mode 100644 vault/seal_testing_util.go create mode 100644 vault/testing_util.go create mode 100644 vault/token_store_util.go create mode 100644 vault/wrapping_util.go diff --git a/.hooks/pre-push b/.hooks/pre-push index ac56a4821d..76d05f5f4d 100755 --- a/.hooks/pre-push +++ b/.hooks/pre-push @@ -6,6 +6,10 @@ if [ "$remote" = "enterprise" ]; then exit 0 fi +if [ "$remote" = "ent" ]; then + exit 0 +fi + if [ -f version/version_ent.go ]; then echo "Found enterprise version file while pushing to oss remote" exit 1 diff --git a/Makefile b/Makefile index b755301af1..67756e7122 100644 --- a/Makefile +++ b/Makefile @@ -143,18 +143,20 @@ proto: protoc helper/forwarding/types.proto --go_out=plugins=grpc:../../.. protoc logical/*.proto --go_out=plugins=grpc:../../.. protoc physical/types.proto --go_out=plugins=grpc:../../.. + protoc helper/identity/mfa/types.proto --go_out=plugins=grpc:../../.. protoc helper/identity/types.proto --go_out=plugins=grpc:../../.. protoc builtin/logical/database/dbplugin/*.proto --go_out=plugins=grpc:../../.. protoc logical/plugin/pb/*.proto --go_out=plugins=grpc:../../.. - sed -i -e 's/Idp/IDP/' -e 's/Url/URL/' -e 's/Id/ID/' -e 's/EntityId/EntityID/' -e 's/Api/API/' -e 's/Qr/QR/' -e 's/protobuf:"/sentinel:"" protobuf:"/' helper/identity/types.pb.go helper/storagepacker/types.pb.go logical/plugin/pb/backend.pb.go + sed -i -e 's/Idp/IDP/' -e 's/Url/URL/' -e 's/Id/ID/' -e 's/EntityId/EntityID/' -e 's/Api/API/' -e 's/Qr/QR/' -e 's/Totp/TOTP/' -e 's/Mfa/MFA/' -e 's/Pingid/PingID/' -e 's/protobuf:"/sentinel:"" protobuf:"/' -e 's/namespaceId/namespaceID/' -e 's/Ttl/TTL/' helper/identity/types.pb.go helper/storagepacker/types.pb.go logical/plugin/pb/backend.pb.go logical/identity.pb.go + sed -i '1s;^;// +build !enterprise\n;' physical/types.pb.go + sed -i '1s;^;// +build !enterprise\n;' helper/identity/mfa/types.pb.go fmtcheck: @true #@sh -c "'$(CURDIR)/scripts/gofmtcheck.sh'" fmt: - @true -#gofmt -w $(GOFMT_FILES) + gofmt -w $(GOFMT_FILES) spellcheck: @echo "==> Spell checking website..." diff --git a/audit/format.go b/audit/format.go index 55970ec360..64b5ea59d6 100644 --- a/audit/format.go +++ b/audit/format.go @@ -9,6 +9,7 @@ import ( "github.com/SermoDigital/jose/jws" "github.com/hashicorp/errwrap" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/helper/salt" "github.com/hashicorp/vault/logical" "github.com/mitchellh/copystructure" @@ -113,20 +114,26 @@ func (f *AuditFormatter) FormatRequest(ctx context.Context, w io.Writer, config errString = in.OuterErr.Error() } + ns, err := namespace.FromContext(ctx) + if err != nil { + return err + } + reqEntry := &AuditRequestEntry{ Type: "request", Error: errString, Auth: AuditAuth{ - ClientToken: auth.ClientToken, - Accessor: auth.Accessor, - DisplayName: auth.DisplayName, - Policies: auth.Policies, - TokenPolicies: auth.TokenPolicies, - IdentityPolicies: auth.IdentityPolicies, - Metadata: auth.Metadata, - EntityID: auth.EntityID, - RemainingUses: req.ClientTokenRemainingUses, + ClientToken: auth.ClientToken, + Accessor: auth.Accessor, + DisplayName: auth.DisplayName, + Policies: auth.Policies, + TokenPolicies: auth.TokenPolicies, + IdentityPolicies: auth.IdentityPolicies, + ExternalNamespacePolicies: auth.ExternalNamespacePolicies, + Metadata: auth.Metadata, + EntityID: auth.EntityID, + RemainingUses: req.ClientTokenRemainingUses, }, Request: AuditRequest{ @@ -134,12 +141,16 @@ func (f *AuditFormatter) FormatRequest(ctx context.Context, w io.Writer, config ClientToken: req.ClientToken, ClientTokenAccessor: req.ClientTokenAccessor, Operation: req.Operation, - Path: req.Path, - Data: req.Data, - PolicyOverride: req.PolicyOverride, - RemoteAddr: getRemoteAddr(req), - ReplicationCluster: req.ReplicationCluster, - Headers: req.Headers, + Namespace: AuditNamespace{ + ID: ns.ID, + Path: ns.Path, + }, + Path: req.Path, + Data: req.Data, + PolicyOverride: req.PolicyOverride, + RemoteAddr: getRemoteAddr(req), + ReplicationCluster: req.ReplicationCluster, + Headers: req.Headers, }, } @@ -276,17 +287,23 @@ func (f *AuditFormatter) FormatResponse(ctx context.Context, w io.Writer, config errString = in.OuterErr.Error() } + ns, err := namespace.FromContext(ctx) + if err != nil { + return err + } + var respAuth *AuditAuth if resp.Auth != nil { respAuth = &AuditAuth{ - ClientToken: resp.Auth.ClientToken, - Accessor: resp.Auth.Accessor, - DisplayName: resp.Auth.DisplayName, - Policies: resp.Auth.Policies, - TokenPolicies: resp.Auth.TokenPolicies, - IdentityPolicies: resp.Auth.IdentityPolicies, - Metadata: resp.Auth.Metadata, - NumUses: resp.Auth.NumUses, + ClientToken: resp.Auth.ClientToken, + Accessor: resp.Auth.Accessor, + DisplayName: resp.Auth.DisplayName, + Policies: resp.Auth.Policies, + TokenPolicies: resp.Auth.TokenPolicies, + IdentityPolicies: resp.Auth.IdentityPolicies, + ExternalNamespacePolicies: resp.Auth.ExternalNamespacePolicies, + Metadata: resp.Auth.Metadata, + NumUses: resp.Auth.NumUses, } } @@ -317,15 +334,16 @@ func (f *AuditFormatter) FormatResponse(ctx context.Context, w io.Writer, config Type: "response", Error: errString, Auth: AuditAuth{ - DisplayName: auth.DisplayName, - Policies: auth.Policies, - TokenPolicies: auth.TokenPolicies, - IdentityPolicies: auth.IdentityPolicies, - Metadata: auth.Metadata, - ClientToken: auth.ClientToken, - Accessor: auth.Accessor, - RemainingUses: req.ClientTokenRemainingUses, - EntityID: auth.EntityID, + DisplayName: auth.DisplayName, + Policies: auth.Policies, + TokenPolicies: auth.TokenPolicies, + IdentityPolicies: auth.IdentityPolicies, + ExternalNamespacePolicies: auth.ExternalNamespacePolicies, + Metadata: auth.Metadata, + ClientToken: auth.ClientToken, + Accessor: auth.Accessor, + RemainingUses: req.ClientTokenRemainingUses, + EntityID: auth.EntityID, }, Request: AuditRequest{ @@ -333,12 +351,16 @@ func (f *AuditFormatter) FormatResponse(ctx context.Context, w io.Writer, config ClientToken: req.ClientToken, ClientTokenAccessor: req.ClientTokenAccessor, Operation: req.Operation, - Path: req.Path, - Data: req.Data, - PolicyOverride: req.PolicyOverride, - RemoteAddr: getRemoteAddr(req), - ReplicationCluster: req.ReplicationCluster, - Headers: req.Headers, + Namespace: AuditNamespace{ + ID: ns.ID, + Path: ns.Path, + }, + Path: req.Path, + Data: req.Data, + PolicyOverride: req.PolicyOverride, + RemoteAddr: getRemoteAddr(req), + ReplicationCluster: req.ReplicationCluster, + Headers: req.Headers, }, Response: AuditResponse{ @@ -386,6 +408,7 @@ type AuditRequest struct { Operation logical.Operation `json:"operation"` ClientToken string `json:"client_token"` ClientTokenAccessor string `json:"client_token_accessor"` + Namespace AuditNamespace `json:"namespace"` Path string `json:"path"` Data map[string]interface{} `json:"data"` PolicyOverride bool `json:"policy_override"` @@ -403,16 +426,17 @@ type AuditResponse struct { } type AuditAuth struct { - ClientToken string `json:"client_token"` - Accessor string `json:"accessor"` - DisplayName string `json:"display_name"` - Policies []string `json:"policies"` - TokenPolicies []string `json:"token_policies,omitempty"` - IdentityPolicies []string `json:"identity_policies,omitempty"` - Metadata map[string]string `json:"metadata"` - NumUses int `json:"num_uses,omitempty"` - RemainingUses int `json:"remaining_uses,omitempty"` - EntityID string `json:"entity_id"` + ClientToken string `json:"client_token"` + Accessor string `json:"accessor"` + DisplayName string `json:"display_name"` + Policies []string `json:"policies"` + TokenPolicies []string `json:"token_policies,omitempty"` + IdentityPolicies []string `json:"identity_policies,omitempty"` + ExternalNamespacePolicies map[string][]string `json:"external_namespace_policies,omitempty"` + Metadata map[string]string `json:"metadata"` + NumUses int `json:"num_uses,omitempty"` + RemainingUses int `json:"remaining_uses,omitempty"` + EntityID string `json:"entity_id"` } type AuditSecret struct { @@ -428,6 +452,11 @@ type AuditResponseWrapInfo struct { WrappedAccessor string `json:"wrapped_accessor,omitempty"` } +type AuditNamespace struct { + ID string `json:"id"` + Path string `json:"path"` +} + // getRemoteAddr safely gets the remote address avoiding a nil pointer func getRemoteAddr(req *logical.Request) string { if req != nil && req.Connection != nil { diff --git a/audit/format_json_test.go b/audit/format_json_test.go index 9ab20ac35e..f84dcd090a 100644 --- a/audit/format_json_test.go +++ b/audit/format_json_test.go @@ -13,6 +13,7 @@ import ( "fmt" "github.com/hashicorp/vault/helper/jsonutil" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/helper/salt" "github.com/hashicorp/vault/logical" ) @@ -91,7 +92,7 @@ func TestFormatJSON_formatRequest(t *testing.T) { Request: tc.Req, OuterErr: tc.Err, } - if err := formatter.FormatRequest(context.Background(), &buf, config, in); err != nil { + if err := formatter.FormatRequest(namespace.RootContext(nil), &buf, config, in); err != nil { t.Fatalf("bad: %s\nerr: %s", name, err) } @@ -104,6 +105,7 @@ func TestFormatJSON_formatRequest(t *testing.T) { if err := jsonutil.DecodeJSON([]byte(expectedResultStr), &expectedjson); err != nil { t.Fatalf("bad json: %s", err) } + expectedjson.Request.Namespace = AuditNamespace{ID: "root"} var actualjson = new(AuditRequestEntry) if err := jsonutil.DecodeJSON([]byte(buf.String())[len(tc.Prefix):], &actualjson); err != nil { diff --git a/audit/format_jsonx_test.go b/audit/format_jsonx_test.go index 8775ef570b..5cbd5e2fb3 100644 --- a/audit/format_jsonx_test.go +++ b/audit/format_jsonx_test.go @@ -11,6 +11,7 @@ import ( "fmt" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/helper/salt" "github.com/hashicorp/vault/logical" ) @@ -52,7 +53,7 @@ func TestFormatJSONx_formatRequest(t *testing.T) { errors.New("this is an error"), "", "", - fmt.Sprintf(`bar%stesttokenrootthis is an errorbarupdate/foofalse127.0.0.160request`, + fmt.Sprintf(`bar%stesttokenrootthis is an errorbarrootupdate/foofalse127.0.0.160request`, fooSalted), }, "auth, request with prefix": { @@ -73,7 +74,7 @@ func TestFormatJSONx_formatRequest(t *testing.T) { errors.New("this is an error"), "", "@cee: ", - fmt.Sprintf(`bar%stesttokenrootthis is an errorbarupdate/foofalse127.0.0.160request`, + fmt.Sprintf(`bar%stesttokenrootthis is an errorbarrootupdate/foofalse127.0.0.160request`, fooSalted), }, } @@ -95,7 +96,7 @@ func TestFormatJSONx_formatRequest(t *testing.T) { Request: tc.Req, OuterErr: tc.Err, } - if err := formatter.FormatRequest(context.Background(), &buf, config, in); err != nil { + if err := formatter.FormatRequest(namespace.RootContext(nil), &buf, config, in); err != nil { t.Fatalf("bad: %s\nerr: %s", name, err) } diff --git a/builtin/logical/database/backend_test.go b/builtin/logical/database/backend_test.go index daa27316b5..b7fe89372e 100644 --- a/builtin/logical/database/backend_test.go +++ b/builtin/logical/database/backend_test.go @@ -14,6 +14,7 @@ import ( "github.com/go-test/deep" "github.com/hashicorp/vault/builtin/logical/database/dbplugin" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/helper/pluginutil" vaulthttp "github.com/hashicorp/vault/http" "github.com/hashicorp/vault/logical" @@ -58,7 +59,7 @@ func preparePostgresTestContainer(t *testing.T, s logical.Storage, b logical.Bac // exponential backoff-retry if err = pool.Retry(func() error { // This will cause a validation to run - resp, err := b.HandleRequest(context.Background(), &logical.Request{ + resp, err := b.HandleRequest(namespace.TestContext(), &logical.Request{ Storage: s, Operation: logical.UpdateOperation, Path: "config/postgresql", @@ -227,7 +228,7 @@ func TestBackend_config_connection(t *testing.T) { t.Fatal("expected not exists") } - resp, err = b.HandleRequest(context.Background(), configReq) + resp, err = b.HandleRequest(namespace.TestContext(), configReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v\n", err, resp) } @@ -242,7 +243,7 @@ func TestBackend_config_connection(t *testing.T) { "root_credentials_rotate_statements": []string{}, } configReq.Operation = logical.ReadOperation - resp, err = b.HandleRequest(context.Background(), configReq) + resp, err = b.HandleRequest(namespace.TestContext(), configReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, resp) } @@ -279,7 +280,7 @@ func TestBackend_config_connection(t *testing.T) { t.Fatal("expected exists") } - resp, err = b.HandleRequest(context.Background(), configReq) + resp, err = b.HandleRequest(namespace.TestContext(), configReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v\n", err, resp) } @@ -294,7 +295,7 @@ func TestBackend_config_connection(t *testing.T) { "root_credentials_rotate_statements": []string{}, } configReq.Operation = logical.ReadOperation - resp, err = b.HandleRequest(context.Background(), configReq) + resp, err = b.HandleRequest(namespace.TestContext(), configReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, resp) } @@ -320,7 +321,7 @@ func TestBackend_config_connection(t *testing.T) { Data: configData, } - resp, err = b.HandleRequest(context.Background(), configReq) + resp, err = b.HandleRequest(namespace.TestContext(), configReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v\n", err, resp) } @@ -335,7 +336,7 @@ func TestBackend_config_connection(t *testing.T) { "root_credentials_rotate_statements": []string{}, } configReq.Operation = logical.ReadOperation - resp, err = b.HandleRequest(context.Background(), configReq) + resp, err = b.HandleRequest(namespace.TestContext(), configReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, resp) } @@ -351,7 +352,7 @@ func TestBackend_config_connection(t *testing.T) { Storage: config.StorageView, Path: "config/", } - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatal(err) } @@ -381,7 +382,7 @@ func TestBackend_BadConnectionString(t *testing.T) { respCheck := func(req *logical.Request) { t.Helper() - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -440,7 +441,7 @@ func TestBackend_basic(t *testing.T) { Storage: config.StorageView, Data: data, } - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, resp) } @@ -457,7 +458,7 @@ func TestBackend_basic(t *testing.T) { Storage: config.StorageView, Data: data, } - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, resp) } @@ -469,7 +470,7 @@ func TestBackend_basic(t *testing.T) { Storage: config.StorageView, Data: data, } - credsResp, err := b.HandleRequest(context.Background(), req) + credsResp, err := b.HandleRequest(namespace.TestContext(), req) if err != nil || (credsResp != nil && credsResp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, credsResp) } @@ -486,7 +487,7 @@ func TestBackend_basic(t *testing.T) { Storage: config.StorageView, Data: data, } - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, resp) } @@ -498,7 +499,7 @@ func TestBackend_basic(t *testing.T) { Storage: config.StorageView, Data: data, } - credsResp, err = b.HandleRequest(context.Background(), req) + credsResp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil || (credsResp != nil && credsResp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, credsResp) } @@ -519,7 +520,7 @@ func TestBackend_basic(t *testing.T) { Storage: config.StorageView, Data: data, } - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, resp) } @@ -533,7 +534,7 @@ func TestBackend_basic(t *testing.T) { Storage: config.StorageView, Data: data, } - credsResp, err = b.HandleRequest(context.Background(), req) + credsResp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil || (credsResp != nil && credsResp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, credsResp) } @@ -546,7 +547,7 @@ func TestBackend_basic(t *testing.T) { } // Revoke creds - resp, err = b.HandleRequest(context.Background(), &logical.Request{ + resp, err = b.HandleRequest(namespace.TestContext(), &logical.Request{ Operation: logical.RevokeOperation, Storage: config.StorageView, Secret: &logical.Secret{ @@ -575,7 +576,7 @@ func TestBackend_basic(t *testing.T) { Storage: config.StorageView, Data: data, } - credsResp, err = b.HandleRequest(context.Background(), req) + credsResp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil || (credsResp != nil && credsResp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, credsResp) } @@ -589,13 +590,13 @@ func TestBackend_basic(t *testing.T) { Path: "roles/plugin-role-test", Storage: config.StorageView, } - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, resp) } // Revoke creds - resp, err = b.HandleRequest(context.Background(), &logical.Request{ + resp, err = b.HandleRequest(namespace.TestContext(), &logical.Request{ Operation: logical.RevokeOperation, Storage: config.StorageView, Secret: &logical.Secret{ @@ -647,7 +648,7 @@ func TestBackend_connectionCrud(t *testing.T) { Storage: config.StorageView, Data: data, } - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, resp) } @@ -666,7 +667,7 @@ func TestBackend_connectionCrud(t *testing.T) { Storage: config.StorageView, Data: data, } - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, resp) } @@ -685,7 +686,7 @@ func TestBackend_connectionCrud(t *testing.T) { Storage: config.StorageView, Data: data, } - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, resp) } @@ -694,7 +695,7 @@ func TestBackend_connectionCrud(t *testing.T) { } req.Operation = logical.ReadOperation - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, resp) } @@ -706,7 +707,7 @@ func TestBackend_connectionCrud(t *testing.T) { req.Operation = logical.UpdateOperation connURL = strings.Replace(connURL, "postgres:secret", "{{username}}:{{password}}", -1) data["connection_url"] = connURL - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, resp) } @@ -722,7 +723,7 @@ func TestBackend_connectionCrud(t *testing.T) { "root_credentials_rotate_statements": []string(nil), } req.Operation = logical.ReadOperation - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, resp) } @@ -740,7 +741,7 @@ func TestBackend_connectionCrud(t *testing.T) { Storage: config.StorageView, Data: data, } - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, resp) } @@ -753,7 +754,7 @@ func TestBackend_connectionCrud(t *testing.T) { Storage: config.StorageView, Data: data, } - credsResp, err := b.HandleRequest(context.Background(), req) + credsResp, err := b.HandleRequest(namespace.TestContext(), req) if err != nil || (credsResp != nil && credsResp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, credsResp) } @@ -774,14 +775,14 @@ func TestBackend_connectionCrud(t *testing.T) { Storage: config.StorageView, Data: data, } - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, resp) } // Read connection req.Operation = logical.ReadOperation - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, resp) } @@ -824,7 +825,7 @@ func TestBackend_roleCrud(t *testing.T) { Storage: config.StorageView, Data: data, } - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, resp) } @@ -844,7 +845,7 @@ func TestBackend_roleCrud(t *testing.T) { Storage: config.StorageView, Data: data, } - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, resp) } @@ -868,7 +869,7 @@ func TestBackend_roleCrud(t *testing.T) { Storage: config.StorageView, Data: data, } - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, resp) } @@ -912,7 +913,7 @@ func TestBackend_roleCrud(t *testing.T) { Storage: config.StorageView, Data: data, } - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v\n", err, resp) } @@ -936,7 +937,7 @@ func TestBackend_roleCrud(t *testing.T) { Storage: config.StorageView, Data: data, } - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, resp) } @@ -1050,7 +1051,7 @@ func TestBackend_roleCrud(t *testing.T) { Storage: config.StorageView, Data: data, } - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, resp) } @@ -1063,7 +1064,7 @@ func TestBackend_roleCrud(t *testing.T) { Storage: config.StorageView, Data: data, } - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, resp) } @@ -1101,7 +1102,7 @@ func TestBackend_allowedRoles(t *testing.T) { Storage: config.StorageView, Data: data, } - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, resp) } @@ -1119,7 +1120,7 @@ func TestBackend_allowedRoles(t *testing.T) { Storage: config.StorageView, Data: data, } - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, resp) } @@ -1136,7 +1137,7 @@ func TestBackend_allowedRoles(t *testing.T) { Storage: config.StorageView, Data: data, } - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, resp) } @@ -1149,7 +1150,7 @@ func TestBackend_allowedRoles(t *testing.T) { Storage: config.StorageView, Data: data, } - credsResp, err := b.HandleRequest(context.Background(), req) + credsResp, err := b.HandleRequest(namespace.TestContext(), req) if err != logical.ErrPermissionDenied { t.Fatalf("expected error to be:%s got:%#v\n", logical.ErrPermissionDenied, err) } @@ -1166,7 +1167,7 @@ func TestBackend_allowedRoles(t *testing.T) { Storage: config.StorageView, Data: data, } - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, resp) } @@ -1179,7 +1180,7 @@ func TestBackend_allowedRoles(t *testing.T) { Storage: config.StorageView, Data: data, } - credsResp, err = b.HandleRequest(context.Background(), req) + credsResp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil || (credsResp != nil && credsResp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, credsResp) } @@ -1200,7 +1201,7 @@ func TestBackend_allowedRoles(t *testing.T) { Storage: config.StorageView, Data: data, } - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, resp) } @@ -1213,7 +1214,7 @@ func TestBackend_allowedRoles(t *testing.T) { Storage: config.StorageView, Data: data, } - credsResp, err = b.HandleRequest(context.Background(), req) + credsResp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil || (credsResp != nil && credsResp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, credsResp) } @@ -1234,7 +1235,7 @@ func TestBackend_allowedRoles(t *testing.T) { Storage: config.StorageView, Data: data, } - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, resp) } @@ -1247,7 +1248,7 @@ func TestBackend_allowedRoles(t *testing.T) { Storage: config.StorageView, Data: data, } - credsResp, err = b.HandleRequest(context.Background(), req) + credsResp, err = b.HandleRequest(namespace.TestContext(), req) if err != logical.ErrPermissionDenied { t.Fatalf("expected error to be:%s got:%#v\n", logical.ErrPermissionDenied, err) } @@ -1260,7 +1261,7 @@ func TestBackend_allowedRoles(t *testing.T) { Storage: config.StorageView, Data: data, } - credsResp, err = b.HandleRequest(context.Background(), req) + credsResp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil || (credsResp != nil && credsResp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, credsResp) } @@ -1303,7 +1304,7 @@ func TestBackend_RotateRootCredentials(t *testing.T) { Storage: config.StorageView, Data: data, } - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, resp) } @@ -1320,7 +1321,7 @@ func TestBackend_RotateRootCredentials(t *testing.T) { Storage: config.StorageView, Data: data, } - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, resp) } @@ -1332,7 +1333,7 @@ func TestBackend_RotateRootCredentials(t *testing.T) { Storage: config.StorageView, Data: data, } - credsResp, err := b.HandleRequest(context.Background(), req) + credsResp, err := b.HandleRequest(namespace.TestContext(), req) if err != nil || (credsResp != nil && credsResp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, credsResp) } @@ -1344,7 +1345,7 @@ func TestBackend_RotateRootCredentials(t *testing.T) { Storage: config.StorageView, Data: data, } - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil || (credsResp != nil && credsResp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, credsResp) } @@ -1365,7 +1366,7 @@ func TestBackend_RotateRootCredentials(t *testing.T) { Storage: config.StorageView, Data: data, } - credsResp, err = b.HandleRequest(context.Background(), req) + credsResp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil || (credsResp != nil && credsResp.IsError()) { t.Fatalf("err:%s resp:%#v\n", err, credsResp) } diff --git a/builtin/logical/database/dbplugin/plugin_test.go b/builtin/logical/database/dbplugin/plugin_test.go index f4f8379336..0c20aef1ea 100644 --- a/builtin/logical/database/dbplugin/plugin_test.go +++ b/builtin/logical/database/dbplugin/plugin_test.go @@ -10,6 +10,7 @@ import ( log "github.com/hashicorp/go-hclog" plugin "github.com/hashicorp/go-plugin" "github.com/hashicorp/vault/builtin/logical/database/dbplugin" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/helper/pluginutil" vaulthttp "github.com/hashicorp/vault/http" "github.com/hashicorp/vault/logical" @@ -147,7 +148,7 @@ func TestPlugin_Init(t *testing.T) { cluster, sys := getCluster(t) defer cluster.Cleanup() - dbRaw, err := dbplugin.PluginFactory(context.Background(), "test-plugin", sys, log.NewNullLogger()) + dbRaw, err := dbplugin.PluginFactory(namespace.TestContext(), "test-plugin", sys, log.NewNullLogger()) if err != nil { t.Fatalf("err: %s", err) } @@ -171,7 +172,7 @@ func TestPlugin_CreateUser(t *testing.T) { cluster, sys := getCluster(t) defer cluster.Cleanup() - db, err := dbplugin.PluginFactory(context.Background(), "test-plugin", sys, log.NewNullLogger()) + db, err := dbplugin.PluginFactory(namespace.TestContext(), "test-plugin", sys, log.NewNullLogger()) if err != nil { t.Fatalf("err: %s", err) } @@ -211,7 +212,7 @@ func TestPlugin_RenewUser(t *testing.T) { cluster, sys := getCluster(t) defer cluster.Cleanup() - db, err := dbplugin.PluginFactory(context.Background(), "test-plugin", sys, log.NewNullLogger()) + db, err := dbplugin.PluginFactory(namespace.TestContext(), "test-plugin", sys, log.NewNullLogger()) if err != nil { t.Fatalf("err: %s", err) } @@ -245,7 +246,7 @@ func TestPlugin_RevokeUser(t *testing.T) { cluster, sys := getCluster(t) defer cluster.Cleanup() - db, err := dbplugin.PluginFactory(context.Background(), "test-plugin", sys, log.NewNullLogger()) + db, err := dbplugin.PluginFactory(namespace.TestContext(), "test-plugin", sys, log.NewNullLogger()) if err != nil { t.Fatalf("err: %s", err) } @@ -287,7 +288,7 @@ func TestPlugin_NetRPC_Init(t *testing.T) { cluster, sys := getCluster(t) defer cluster.Cleanup() - dbRaw, err := dbplugin.PluginFactory(context.Background(), "test-plugin-netRPC", sys, log.NewNullLogger()) + dbRaw, err := dbplugin.PluginFactory(namespace.TestContext(), "test-plugin-netRPC", sys, log.NewNullLogger()) if err != nil { t.Fatalf("err: %s", err) } @@ -311,7 +312,7 @@ func TestPlugin_NetRPC_CreateUser(t *testing.T) { cluster, sys := getCluster(t) defer cluster.Cleanup() - db, err := dbplugin.PluginFactory(context.Background(), "test-plugin-netRPC", sys, log.NewNullLogger()) + db, err := dbplugin.PluginFactory(namespace.TestContext(), "test-plugin-netRPC", sys, log.NewNullLogger()) if err != nil { t.Fatalf("err: %s", err) } @@ -351,7 +352,7 @@ func TestPlugin_NetRPC_RenewUser(t *testing.T) { cluster, sys := getCluster(t) defer cluster.Cleanup() - db, err := dbplugin.PluginFactory(context.Background(), "test-plugin-netRPC", sys, log.NewNullLogger()) + db, err := dbplugin.PluginFactory(namespace.TestContext(), "test-plugin-netRPC", sys, log.NewNullLogger()) if err != nil { t.Fatalf("err: %s", err) } @@ -385,7 +386,7 @@ func TestPlugin_NetRPC_RevokeUser(t *testing.T) { cluster, sys := getCluster(t) defer cluster.Cleanup() - db, err := dbplugin.PluginFactory(context.Background(), "test-plugin-netRPC", sys, log.NewNullLogger()) + db, err := dbplugin.PluginFactory(namespace.TestContext(), "test-plugin-netRPC", sys, log.NewNullLogger()) if err != nil { t.Fatalf("err: %s", err) } diff --git a/command/agent/aws_end_to_end_test.go b/command/agent/aws_end_to_end_test.go index bfa5665773..6f02fff5f5 100644 --- a/command/agent/aws_end_to_end_test.go +++ b/command/agent/aws_end_to_end_test.go @@ -101,8 +101,8 @@ func TestAWSEndToEnd(t *testing.T) { Logger: logger.Named("auth.aws"), MountPath: "auth/aws", Config: map[string]interface{}{ - "role": "test", - "type": "iam", + "role": "test", + "type": "iam", "credential_poll_interval": 1, }, }) diff --git a/command/login_test.go b/command/login_test.go index c1752c507a..81926314c9 100644 --- a/command/login_test.go +++ b/command/login_test.go @@ -10,6 +10,7 @@ import ( credToken "github.com/hashicorp/vault/builtin/credential/token" credUserpass "github.com/hashicorp/vault/builtin/credential/userpass" "github.com/hashicorp/vault/command/token" + "github.com/hashicorp/vault/vault" ) func testLoginCommand(tb testing.TB) (*cli.MockUi, *LoginCommand) { @@ -78,7 +79,7 @@ func TestLoginCommand_Run(t *testing.T) { t.Fatal(err) } - if l, exp := len(storedToken), 36; l != exp { + if l, exp := len(storedToken), vault.TokenLength; l != exp { t.Errorf("expected token to be %d characters, was %d: %q", exp, l, storedToken) } }) @@ -205,7 +206,7 @@ func TestLoginCommand_Run(t *testing.T) { // Verify only the token was printed token := ui.OutputWriter.String() - if l, exp := len(token), 36; l != exp { + if l, exp := len(token), vault.TokenLength; l != exp { t.Errorf("expected token to be %d characters, was %d: %q", exp, l, token) } diff --git a/command/operator_generate_root_test.go b/command/operator_generate_root_test.go index f8601183ab..790c129aa0 100644 --- a/command/operator_generate_root_test.go +++ b/command/operator_generate_root_test.go @@ -3,14 +3,15 @@ package command import ( + "encoding/base64" "io" "os" "regexp" "strings" "testing" - uuid "github.com/hashicorp/go-uuid" "github.com/hashicorp/vault/helper/xor" + "github.com/hashicorp/vault/vault" "github.com/mitchellh/cli" ) @@ -40,7 +41,7 @@ func TestOperatorGenerateRootCommand_Run(t *testing.T) { "-init", "-otp", "not-a-valid-otp", }, - "illegal base64 data at input", + "OTP string is wrong length", 2, }, { @@ -122,8 +123,8 @@ func TestOperatorGenerateRootCommand_Run(t *testing.T) { t.Run("decode", func(t *testing.T) { t.Parallel() - encoded := "L9MaZ/4mQanpOV6QeWd84g==" - otp := "dIeeezkjpDUv3fy7MYPOLQ==" + encoded := "Bxg9JQQqOCNKBRICNwMIRzo2J3cWCBRi" + otp := "3JhHkONiyiaNYj14nnD9xZQS" client, closer := testVaultServer(t) defer closer() @@ -150,7 +151,7 @@ func TestOperatorGenerateRootCommand_Run(t *testing.T) { w.Close() os.Stdout = old - expected := "5b54841c-c705-e59c-c6e4-a22b48e4b2cf" + expected := "4RUmoevJ3lsLni9sTXcNnRE1" combined := ui.OutputWriter.String() + ui.ErrorWriter.String() if combined != expected { t.Errorf("expected %q to be %q", combined, expected) @@ -160,7 +161,7 @@ func TestOperatorGenerateRootCommand_Run(t *testing.T) { t.Run("cancel", func(t *testing.T) { t.Parallel() - otp := "dIeeezkjpDUv3fy7MYPOLQ==" + otp := "3JhHkONiyiaNYj14nnD9xZQS" client, closer := testVaultServer(t) defer closer() @@ -199,7 +200,7 @@ func TestOperatorGenerateRootCommand_Run(t *testing.T) { t.Run("init_otp", func(t *testing.T) { t.Parallel() - otp := "dIeeezkjpDUv3fy7MYPOLQ==" + otp := "3JhHkONiyiaNYj14nnD9xZQS" client, closer := testVaultServer(t) defer closer() @@ -296,17 +297,16 @@ func TestOperatorGenerateRootCommand_Run(t *testing.T) { t.Run("provide_arg", func(t *testing.T) { t.Parallel() - otp := "dIeeezkjpDUv3fy7MYPOLQ==" - client, keys, closer := testVaultServerUnseal(t) defer closer() // Initialize a generation - status, err := client.Sys().GenerateRootInit(otp, "") + status, err := client.Sys().GenerateRootInit("", "") if err != nil { t.Fatal(err) } nonce := status.Nonce + otp := status.OTP // Supply the first n-1 unseal keys for _, key := range keys[:len(keys)-1] { @@ -340,16 +340,17 @@ func TestOperatorGenerateRootCommand_Run(t *testing.T) { t.Fatalf("no match: %#v", match) } - tokenBytes, err := xor.XORBase64(match[0][1], otp) - if err != nil { - t.Fatal(err) - } - token, err := uuid.FormatUUID(tokenBytes) + tokenBytes, err := base64.RawStdEncoding.DecodeString(match[0][1]) if err != nil { t.Fatal(err) } - if l, exp := len(token), 36; l != exp { + token, err := xor.XORBytes(tokenBytes, []byte(otp)) + if err != nil { + t.Fatal(err) + } + + if l, exp := len(token), vault.TokenLength; l != exp { t.Errorf("expected %d to be %d: %s", l, exp, token) } }) @@ -357,17 +358,16 @@ func TestOperatorGenerateRootCommand_Run(t *testing.T) { t.Run("provide_stdin", func(t *testing.T) { t.Parallel() - otp := "dIeeezkjpDUv3fy7MYPOLQ==" - client, keys, closer := testVaultServerUnseal(t) defer closer() // Initialize a generation - status, err := client.Sys().GenerateRootInit(otp, "") + status, err := client.Sys().GenerateRootInit("", "") if err != nil { t.Fatal(err) } nonce := status.Nonce + otp := status.OTP // Supply the first n-1 unseal keys for _, key := range keys[:len(keys)-1] { @@ -415,16 +415,28 @@ func TestOperatorGenerateRootCommand_Run(t *testing.T) { t.Fatalf("no match: %#v", match) } - tokenBytes, err := xor.XORBase64(match[0][1], otp) - if err != nil { - t.Fatal(err) - } - token, err := uuid.FormatUUID(tokenBytes) + // encodedOTP := base64.RawStdEncoding.EncodeToString([]byte(otp)) + + // tokenBytes, err := xor.XORBase64(match[0][1], encodedOTP) + // if err != nil { + // t.Fatal(err) + // } + // token, err := uuid.FormatUUID(tokenBytes) + // if err != nil { + // t.Fatal(err) + // } + + tokenBytes, err := base64.RawStdEncoding.DecodeString(match[0][1]) if err != nil { t.Fatal(err) } - if l, exp := len(token), 36; l != exp { + token, err := xor.XORBytes(tokenBytes, []byte(otp)) + if err != nil { + t.Fatal(err) + } + + if l, exp := len(token), vault.TokenLength; l != exp { t.Errorf("expected %d to be %d: %s", l, exp, token) } }) diff --git a/command/operator_init_test.go b/command/operator_init_test.go index b90b5d65c1..5d7740dee4 100644 --- a/command/operator_init_test.go +++ b/command/operator_init_test.go @@ -12,6 +12,7 @@ import ( "github.com/hashicorp/vault/api" "github.com/hashicorp/vault/helper/pgpkeys" + "github.com/hashicorp/vault/vault" "github.com/mitchellh/cli" ) @@ -332,7 +333,7 @@ func TestOperatorInitCommand_Run(t *testing.T) { root := match[0][1] decryptedRoot := testPGPDecrypt(t, pgpkeys.TestPrivKey1, root) - if l, exp := len(decryptedRoot), 36; l != exp { + if l, exp := len(decryptedRoot), vault.TokenLength; l != exp { t.Errorf("expected %d to be %d", l, exp) } }) diff --git a/command/policy_fmt.go b/command/policy_fmt.go index 01bde16e60..e9e7669032 100644 --- a/command/policy_fmt.go +++ b/command/policy_fmt.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/hashicorp/hcl/hcl/printer" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/vault" "github.com/mitchellh/cli" homedir "github.com/mitchellh/go-homedir" @@ -85,8 +86,9 @@ func (c *PolicyFmtCommand) Run(args []string) int { return 1 } - // Actually parse the policy - if _, err := vault.ParseACLPolicy(string(b)); err != nil { + // Actually parse the policy. We always use the root namespace here because + // we don't want to modify the results. + if _, err := vault.ParseACLPolicy(namespace.RootNamespace, string(b)); err != nil { c.UI.Error(err.Error()) return 1 } diff --git a/command/server.go b/command/server.go index 84194d348c..33818f3987 100644 --- a/command/server.go +++ b/command/server.go @@ -35,9 +35,11 @@ import ( sockaddr "github.com/hashicorp/go-sockaddr" "github.com/hashicorp/vault/audit" "github.com/hashicorp/vault/command/server" + serverseal "github.com/hashicorp/vault/command/server/seal" "github.com/hashicorp/vault/helper/gated-writer" "github.com/hashicorp/vault/helper/logging" "github.com/hashicorp/vault/helper/mlock" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/helper/parseutil" "github.com/hashicorp/vault/helper/reload" vaulthttp "github.com/hashicorp/vault/http" @@ -463,7 +465,25 @@ func (c *ServerCommand) Run(args []string) int { info["log level"] = c.flagLogLevel infoKeys = append(infoKeys, "log level") - var seal vault.Seal = vault.NewDefaultSeal() + sealType := "shamir" + if config.Seal != nil || os.Getenv("VAULT_SEAL_TYPE") != "" { + if config.Seal == nil { + sealType = os.Getenv("VAULT_SEAL_TYPE") + } else { + sealType = config.Seal.Type + } + } + + sealLogger := c.logger.Named(sealType) + allLoggers = append(allLoggers, sealLogger) + seal, sealConfigError := serverseal.ConfigureSeal(config, &infoKeys, &info, sealLogger, vault.NewDefaultSeal()) + if sealConfigError != nil { + if !errwrap.ContainsType(sealConfigError, new(logical.KeyNotFoundError)) { + c.UI.Error(fmt.Sprintf( + "Error parsing Seal configuration: %s", sealConfigError)) + return 1 + } + } // Ensure that the seal finalizer is called, even if using verify-only defer func() { @@ -481,24 +501,26 @@ func (c *ServerCommand) Run(args []string) int { } coreConfig := &vault.CoreConfig{ - Physical: backend, - RedirectAddr: config.Storage.RedirectAddr, - HAPhysical: nil, - Seal: seal, - AuditBackends: c.AuditBackends, - CredentialBackends: c.CredentialBackends, - LogicalBackends: c.LogicalBackends, - Logger: c.logger, - DisableCache: config.DisableCache, - DisableMlock: config.DisableMlock, - MaxLeaseTTL: config.MaxLeaseTTL, - DefaultLeaseTTL: config.DefaultLeaseTTL, - ClusterName: config.ClusterName, - CacheSize: config.CacheSize, - PluginDirectory: config.PluginDirectory, - EnableUI: config.EnableUI, - EnableRaw: config.EnableRawEndpoint, - AllLoggers: allLoggers, + Physical: backend, + RedirectAddr: config.Storage.RedirectAddr, + HAPhysical: nil, + Seal: seal, + AuditBackends: c.AuditBackends, + CredentialBackends: c.CredentialBackends, + LogicalBackends: c.LogicalBackends, + Logger: c.logger, + DisableCache: config.DisableCache, + DisableMlock: config.DisableMlock, + MaxLeaseTTL: config.MaxLeaseTTL, + DefaultLeaseTTL: config.DefaultLeaseTTL, + ClusterName: config.ClusterName, + CacheSize: config.CacheSize, + PluginDirectory: config.PluginDirectory, + EnableUI: config.EnableUI, + EnableRaw: config.EnableRawEndpoint, + DisableSealWrap: config.DisableSealWrap, + DisablePerformanceStandby: config.DisablePerformanceStandby, + AllLoggers: allLoggers, } if c.flagDev { coreConfig.DevToken = c.flagDevRootTokenID @@ -522,6 +544,10 @@ func (c *ServerCommand) Run(args []string) int { return c.enableThreeNodeDevCluster(coreConfig, info, infoKeys, c.flagDevListenAddr, os.Getenv("VAULT_DEV_TEMP_DIR")) } + if c.flagDevFourCluster { + return c.enableFourClusterDev(coreConfig, info, infoKeys, c.flagDevListenAddr, os.Getenv("VAULT_DEV_TEMP_DIR")) + } + var disableClustering bool // Initialize the separate HA storage backend, if it exists @@ -887,7 +913,7 @@ CLUSTER_SYNTHESIS_COMPLETE: return false } - if err := sd.RunServiceDiscovery(c.WaitGroup, c.ShutdownCh, coreConfig.RedirectAddr, activeFunc, core.Sealed); err != nil { + if err := sd.RunServiceDiscovery(c.WaitGroup, c.ShutdownCh, coreConfig.RedirectAddr, activeFunc, core.Sealed, core.PerfStandby); err != nil { c.UI.Error(fmt.Sprintf("Error initializing service discovery: %v", err)) return 1 } @@ -1014,6 +1040,18 @@ CLUSTER_SYNTHESIS_COMPLETE: go server.Serve(ln.Listener) } + if sealConfigError != nil { + init, err := core.Initialized(context.Background()) + if err != nil { + c.UI.Error(fmt.Sprintf("Error checking if core is initialized: %v", err)) + return 1 + } + if init { + c.UI.Error("Vault is initialized but no Seal key could be loaded") + return 1 + } + } + if newCoreError != nil { c.UI.Warn(wrapAtLength( "WARNING! A non-fatal error occurred during initialization. Please " + @@ -1126,6 +1164,8 @@ CLUSTER_SYNTHESIS_COMPLETE: } func (c *ServerCommand) enableDev(core *vault.Core, coreConfig *vault.CoreConfig) (*vault.InitResult, error) { + ctx := namespace.ContextWithNamespace(context.Background(), namespace.RootNamespace) + var recoveryConfig *vault.SealConfig barrierConfig := &vault.SealConfig{ SecretShares: 1, @@ -1143,8 +1183,6 @@ func (c *ServerCommand) enableDev(core *vault.Core, coreConfig *vault.CoreConfig barrierConfig.StoredShares = 1 } - ctx := context.Background() - // Initialize it with a basic single key init, err := core.Initialize(ctx, &vault.InitParams{ BarrierConfig: barrierConfig, @@ -1210,7 +1248,7 @@ func (c *ServerCommand) enableDev(core *vault.Core, coreConfig *vault.CoreConfig "no_default_policy": true, }, } - resp, err := core.HandleRequest(context.Background(), req) + resp, err := core.HandleRequest(ctx, req) if err != nil { return nil, errwrap.Wrapf(fmt.Sprintf("failed to create root token with ID %q: {{err}}", coreConfig.DevToken), err) } @@ -1226,7 +1264,7 @@ func (c *ServerCommand) enableDev(core *vault.Core, coreConfig *vault.CoreConfig req.ID = "dev-revoke-init-root" req.Path = "auth/token/revoke-self" req.Data = nil - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(ctx, req) if err != nil { return nil, errwrap.Wrapf("failed to revoke initial root token: {{err}}", err) } @@ -1253,7 +1291,7 @@ func (c *ServerCommand) enableDev(core *vault.Core, coreConfig *vault.CoreConfig }, }, } - resp, err := core.HandleRequest(context.Background(), req) + resp, err := core.HandleRequest(ctx, req) if err != nil { return nil, errwrap.Wrapf("error upgrading default K/V store: {{err}}", err) } @@ -1317,6 +1355,8 @@ func (c *ServerCommand) enableThreeNodeDevCluster(base *vault.CoreConfig, info m testCluster.Start() + ctx := namespace.ContextWithNamespace(context.Background(), namespace.RootNamespace) + if base.DevToken != "" { req := &logical.Request{ ID: "dev-gen-root", @@ -1330,7 +1370,7 @@ func (c *ServerCommand) enableThreeNodeDevCluster(base *vault.CoreConfig, info m "no_default_policy": true, }, } - resp, err := testCluster.Cores[0].HandleRequest(context.Background(), req) + resp, err := testCluster.Cores[0].HandleRequest(ctx, req) if err != nil { c.UI.Error(fmt.Sprintf("failed to create root token with ID %s: %s", base.DevToken, err)) return 1 @@ -1349,7 +1389,7 @@ func (c *ServerCommand) enableThreeNodeDevCluster(base *vault.CoreConfig, info m req.ID = "dev-revoke-init-root" req.Path = "auth/token/revoke-self" req.Data = nil - resp, err = testCluster.Cores[0].HandleRequest(context.Background(), req) + resp, err = testCluster.Cores[0].HandleRequest(ctx, req) if err != nil { c.UI.Output(fmt.Sprintf("failed to revoke initial root token: %s", err)) return 1 @@ -1482,7 +1522,8 @@ func (c *ServerCommand) addPlugin(path, token string, core *vault.Core) error { "command": name, }, } - if _, err := core.HandleRequest(context.Background(), req); err != nil { + ctx := namespace.ContextWithNamespace(context.Background(), namespace.RootNamespace) + if _, err := core.HandleRequest(ctx, req); err != nil { return err } diff --git a/command/server/seal/server_seal.go b/command/server/seal/server_seal.go new file mode 100644 index 0000000000..63c62d572c --- /dev/null +++ b/command/server/seal/server_seal.go @@ -0,0 +1,15 @@ +package seal + +import ( + log "github.com/hashicorp/go-hclog" + "github.com/hashicorp/vault/command/server" + "github.com/hashicorp/vault/vault" +) + +var ( + ConfigureSeal func(*server.Config, *[]string, *map[string]string, log.Logger, vault.Seal) (vault.Seal, error) = configureSeal +) + +func configureSeal(config *server.Config, infoKeys *[]string, info *map[string]string, logger log.Logger, inseal vault.Seal) (seal vault.Seal, err error) { + return inseal, nil +} diff --git a/command/server_devfourcluster.go b/command/server_devfourcluster.go new file mode 100644 index 0000000000..61d3973c9e --- /dev/null +++ b/command/server_devfourcluster.go @@ -0,0 +1,313 @@ +package command + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/x509" + "crypto/x509/pkix" + "encoding/base64" + "fmt" + "io/ioutil" + "math/big" + mathrand "math/rand" + "net" + "path/filepath" + "sort" + "strings" + "time" + + "github.com/hashicorp/vault/helper/namespace" + "github.com/hashicorp/vault/helper/testhelpers" + vaulthttp "github.com/hashicorp/vault/http" + "github.com/hashicorp/vault/logical" + "github.com/hashicorp/vault/vault" + "github.com/hashicorp/vault/version" + testing "github.com/mitchellh/go-testing-interface" + "github.com/pkg/errors" +) + +func (c *ServerCommand) enableFourClusterDev(base *vault.CoreConfig, info map[string]string, infoKeys []string, devListenAddress, tempDir string) int { + var err error + ctx := namespace.RootContext(nil) + clusters := map[string]*vault.TestCluster{} + + if base.DevToken == "" { + base.DevToken = "root" + } + base.EnableRaw = true + + // Without setting something in the future we get bombarded with warnings which are quite annoying during testing + base.DevLicenseDuration = 6 * time.Hour + + // Set a base temp dir + if tempDir == "" { + tempDir, err = ioutil.TempDir("", "vault-test-cluster-") + if err != nil { + c.UI.Error(fmt.Sprintf("failed to create top-level temp dir: %s", err)) + return 1 + } + } + + caKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + c.UI.Error(fmt.Sprintf("Failed to generate CA key: %s", err)) + return 1 + } + certIPs := []net.IP{ + net.IPv6loopback, + net.ParseIP("127.0.0.1"), + } + 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 { + c.UI.Error(fmt.Sprintf("Failed to generate certificate: %s", err)) + return 1 + } + + getCluster := func(name string) error { + factory := c.PhysicalBackends["inmem_transactional_ha"] + backend, err := factory(nil, c.logger) + if err != nil { + c.UI.Error(fmt.Sprintf("Error initializing storage of type %s: %s", "inmem_transactional_ha", err)) + return errors.New("") + } + base.Physical = backend + base.Seal = vault.NewDefaultSeal() + + testCluster := vault.NewTestCluster(&testing.RuntimeT{}, base, &vault.TestClusterOptions{ + HandlerFunc: vaulthttp.Handler, + //BaseListenAddress: c.flagDevListenAddr, + Logger: c.logger.Named(name), + TempDir: fmt.Sprintf("%s/%s", tempDir, name), + CAKey: caKey, + CACert: caBytes, + }) + + clusters[name] = testCluster + + for i, core := range testCluster.Cores { + info[fmt.Sprintf("%s node %d redirect address", name, i)] = fmt.Sprintf("https://%s", core.Listeners[0].Address.String()) + infoKeys = append(infoKeys, fmt.Sprintf("%s node %d redirect address", name, i)) + core.Server.Handler = vaulthttp.Handler(&vault.HandlerProperties{ + Core: core.Core, + }) + core.SetClusterHandler(core.Server.Handler) + } + + testCluster.Start() + + req := &logical.Request{ + ID: "dev-gen-root", + Operation: logical.UpdateOperation, + ClientToken: testCluster.RootToken, + Path: "auth/token/create", + Data: map[string]interface{}{ + "id": base.DevToken, + "policies": []string{"root"}, + "no_parent": true, + "no_default_policy": true, + }, + } + resp, err := testCluster.Cores[0].HandleRequest(ctx, req) + if err != nil { + c.UI.Error(fmt.Sprintf("failed to create root token with ID %s: %s", base.DevToken, err)) + return errors.New("") + } + if resp == nil { + c.UI.Error(fmt.Sprintf("nil response when creating root token with ID %s", base.DevToken)) + return errors.New("") + } + if resp.Auth == nil { + c.UI.Error(fmt.Sprintf("nil auth when creating root token with ID %s", base.DevToken)) + return errors.New("") + } + + testCluster.RootToken = resp.Auth.ClientToken + + req.ID = "dev-revoke-init-root" + req.Path = "auth/token/revoke-self" + req.Data = nil + resp, err = testCluster.Cores[0].HandleRequest(ctx, req) + if err != nil { + c.UI.Output(fmt.Sprintf("failed to revoke initial root token: %s", err)) + return errors.New("") + } + + for _, core := range testCluster.Cores { + core.Client.SetToken(base.DevToken) + } + + return nil + } + + err = getCluster("perf-pri") + if err != nil { + return 1 + } + err = getCluster("perf-pri-dr") + if err != nil { + return 1 + } + err = getCluster("perf-sec") + if err != nil { + return 1 + } + err = getCluster("perf-sec-dr") + if err != nil { + return 1 + } + + clusterCleanup := func() { + for name, cluster := range clusters { + cluster.Cleanup() + + // Shutdown will wait until after Vault is sealed, which means the + // request forwarding listeners will also be closed (and also + // waited for). + for _, core := range cluster.Cores { + if err := core.Shutdown(); err != nil { + c.UI.Error(fmt.Sprintf("Error with cluster %s core shutdown: %s", name, err)) + } + } + } + } + + defer c.cleanupGuard.Do(clusterCleanup) + + info["cluster parameters path"] = tempDir + infoKeys = append(infoKeys, "cluster parameters path") + + verInfo := version.GetVersion() + info["version"] = verInfo.FullVersionNumber(false) + infoKeys = append(infoKeys, "version") + if verInfo.Revision != "" { + info["version sha"] = strings.Trim(verInfo.Revision, "'") + infoKeys = append(infoKeys, "version sha") + } + infoKeys = append(infoKeys, "cgo") + info["cgo"] = "disabled" + if version.CgoEnabled { + info["cgo"] = "enabled" + } + + // Server configuration output + padding := 40 + sort.Strings(infoKeys) + c.UI.Output("==> Vault server configuration:\n") + for _, k := range infoKeys { + c.UI.Output(fmt.Sprintf( + "%s%s: %s", + strings.Repeat(" ", padding-len(k)), + strings.Title(k), + info[k])) + } + c.UI.Output("") + + // Set the token + tokenHelper, err := c.TokenHelper() + if err != nil { + c.UI.Error(fmt.Sprintf("Error getting token helper: %s", err)) + return 1 + } + if err := tokenHelper.Store(base.DevToken); err != nil { + c.UI.Error(fmt.Sprintf("Error storing in token helper: %s", err)) + return 1 + } + + if err := ioutil.WriteFile(filepath.Join(tempDir, "root_token"), []byte(base.DevToken), 0755); err != nil { + c.UI.Error(fmt.Sprintf("Error writing token to tempfile: %s", err)) + return 1 + } + + c.UI.Output(fmt.Sprintf( + "\nRoot Token: %s\n", base.DevToken, + )) + + for i, key := range clusters["perf-pri"].BarrierKeys { + c.UI.Output(fmt.Sprintf( + "Unseal Key %d: %s", + i+1, base64.StdEncoding.EncodeToString(key), + )) + } + + c.UI.Output(fmt.Sprintf( + "\nUseful env vars:\n"+ + "export VAULT_TOKEN=%s\n"+ + "export VAULT_CACERT=%s/perf-pri/ca_cert.pem\n", + base.DevToken, + tempDir, + )) + c.UI.Output(fmt.Sprintf("Addresses of initial active nodes:")) + clusterNames := []string{} + for name := range clusters { + clusterNames = append(clusterNames, name) + } + sort.Strings(clusterNames) + for _, name := range clusterNames { + c.UI.Output(fmt.Sprintf( + "%s:\n"+ + "export VAULT_ADDR=%s\n", + name, + clusters[name].Cores[0].Client.Address(), + )) + } + + // Output the header that the server has started + c.UI.Output("==> Vault clusters started! Log data will stream in below:\n") + + // Inform any tests that the server is ready + select { + case c.startedCh <- struct{}{}: + default: + } + + // Release the log gate. + c.logGate.Flush() + + testhelpers.SetupFourClusterReplication(&testing.RuntimeT{}, + clusters["perf-pri"], + clusters["perf-sec"], + clusters["perf-pri-dr"], + clusters["perf-sec-dr"], + ) + + // Wait for shutdown + shutdownTriggered := false + + for !shutdownTriggered { + select { + case <-c.ShutdownCh: + c.UI.Output("==> Vault shutdown triggered") + + // Stop the listners so that we don't process further client requests. + c.cleanupGuard.Do(clusterCleanup) + + shutdownTriggered = true + + case <-c.SighupCh: + c.UI.Output("==> Vault reload triggered") + for name, cluster := range clusters { + for _, core := range cluster.Cores { + if err := c.Reload(core.ReloadFuncsLock, core.ReloadFuncs, nil); err != nil { + c.UI.Error(fmt.Sprintf("Error(s) were encountered during reload of cluster %s cores: %s", name, err)) + } + } + } + } + } + + return 0 +} diff --git a/helper/forwarding/types.pb.go b/helper/forwarding/types.pb.go index 1389fd1d48..2b5e3bc01b 100644 --- a/helper/forwarding/types.pb.go +++ b/helper/forwarding/types.pb.go @@ -38,7 +38,7 @@ func (m *Request) Reset() { *m = Request{} } func (m *Request) String() string { return proto.CompactTextString(m) } func (*Request) ProtoMessage() {} func (*Request) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7ccf0973261c4726, []int{0} + return fileDescriptor_types_9f616cf8b37ed00d, []int{0} } func (m *Request) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Request.Unmarshal(m, b) @@ -129,7 +129,7 @@ func (m *URL) Reset() { *m = URL{} } func (m *URL) String() string { return proto.CompactTextString(m) } func (*URL) ProtoMessage() {} func (*URL) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7ccf0973261c4726, []int{1} + return fileDescriptor_types_9f616cf8b37ed00d, []int{1} } func (m *URL) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_URL.Unmarshal(m, b) @@ -209,7 +209,7 @@ func (m *HeaderEntry) Reset() { *m = HeaderEntry{} } func (m *HeaderEntry) String() string { return proto.CompactTextString(m) } func (*HeaderEntry) ProtoMessage() {} func (*HeaderEntry) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7ccf0973261c4726, []int{2} + return fileDescriptor_types_9f616cf8b37ed00d, []int{2} } func (m *HeaderEntry) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_HeaderEntry.Unmarshal(m, b) @@ -245,6 +245,7 @@ type Response struct { // Added in 0.6.2 to ensure that the content-type is set appropriately, as // well as any other information HeaderEntries map[string]*HeaderEntry `protobuf:"bytes,4,rep,name=header_entries,json=headerEntries,proto3" json:"header_entries,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + LastRemoteWal uint64 `protobuf:"varint,5,opt,name=last_remote_wal,json=lastRemoteWal,proto3" json:"last_remote_wal,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -254,7 +255,7 @@ func (m *Response) Reset() { *m = Response{} } func (m *Response) String() string { return proto.CompactTextString(m) } func (*Response) ProtoMessage() {} func (*Response) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7ccf0973261c4726, []int{3} + return fileDescriptor_types_9f616cf8b37ed00d, []int{3} } func (m *Response) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Response.Unmarshal(m, b) @@ -295,6 +296,13 @@ func (m *Response) GetHeaderEntries() map[string]*HeaderEntry { return nil } +func (m *Response) GetLastRemoteWal() uint64 { + if m != nil { + return m.LastRemoteWal + } + return 0 +} + func init() { proto.RegisterType((*Request)(nil), "forwarding.Request") proto.RegisterMapType((map[string]*HeaderEntry)(nil), "forwarding.Request.HeaderEntriesEntry") @@ -305,39 +313,41 @@ func init() { } func init() { - proto.RegisterFile("helper/forwarding/types.proto", fileDescriptor_types_7ccf0973261c4726) + proto.RegisterFile("helper/forwarding/types.proto", fileDescriptor_types_9f616cf8b37ed00d) } -var fileDescriptor_types_7ccf0973261c4726 = []byte{ - // 475 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x53, 0x4f, 0x8f, 0xd3, 0x3e, - 0x10, 0x55, 0x9a, 0x6e, 0xff, 0x4c, 0x77, 0x7f, 0xbf, 0xc5, 0x07, 0x30, 0x8b, 0x10, 0xa1, 0x12, - 0x10, 0x09, 0x48, 0xa4, 0x72, 0x41, 0xdc, 0x60, 0x85, 0xc4, 0x61, 0x41, 0x60, 0x69, 0x0f, 0x70, - 0x89, 0xdc, 0x78, 0x5a, 0x47, 0x34, 0x75, 0x6a, 0x3b, 0x5b, 0xe5, 0x63, 0xf1, 0x9d, 0x38, 0xf1, - 0x29, 0x90, 0x9d, 0xb0, 0x0d, 0x5a, 0x21, 0x4e, 0x9c, 0x32, 0xef, 0xbd, 0xc9, 0x78, 0xde, 0x8c, - 0x0d, 0xf7, 0x25, 0x6e, 0x2a, 0xd4, 0xe9, 0x4a, 0xe9, 0x3d, 0xd7, 0xa2, 0xd8, 0xae, 0x53, 0xdb, - 0x54, 0x68, 0x92, 0x4a, 0x2b, 0xab, 0x08, 0x1c, 0xf8, 0xf9, 0xf7, 0x01, 0x8c, 0x19, 0xee, 0x6a, - 0x34, 0x96, 0xdc, 0x86, 0x51, 0x89, 0x56, 0x2a, 0x41, 0x07, 0x51, 0x10, 0x4f, 0x59, 0x87, 0xc8, - 0x43, 0x08, 0x6b, 0xbd, 0xa1, 0x61, 0x14, 0xc4, 0xb3, 0xc5, 0xff, 0xc9, 0xe1, 0xef, 0xe4, 0x92, - 0x5d, 0x30, 0xa7, 0x91, 0xf7, 0xf0, 0x9f, 0x44, 0x2e, 0x50, 0x67, 0xb8, 0xb5, 0xba, 0x40, 0x43, - 0x87, 0x51, 0x18, 0xcf, 0x16, 0x8f, 0xfb, 0xd9, 0xdd, 0x39, 0xc9, 0x3b, 0x9f, 0xf9, 0xb6, 0x4d, - 0x74, 0x9f, 0x86, 0x9d, 0xc8, 0x3e, 0x47, 0x08, 0x0c, 0x97, 0x4a, 0x34, 0xf4, 0x28, 0x0a, 0xe2, - 0x63, 0xe6, 0x63, 0xc7, 0x49, 0x65, 0x2c, 0x1d, 0xf9, 0xde, 0x7c, 0x4c, 0x1e, 0xc0, 0x4c, 0x63, - 0xa9, 0x2c, 0x66, 0x5c, 0x08, 0x4d, 0xc7, 0x5e, 0x82, 0x96, 0x7a, 0x2d, 0x84, 0x26, 0x4f, 0xe1, - 0x56, 0x85, 0xa8, 0xb3, 0x1c, 0xb5, 0x2d, 0x56, 0x45, 0xce, 0x2d, 0x1a, 0x3a, 0x89, 0xc2, 0xf8, - 0x98, 0x9d, 0x3a, 0xe1, 0xbc, 0xc7, 0x9f, 0x7d, 0x06, 0x72, 0xb3, 0x35, 0x72, 0x0a, 0xe1, 0x57, - 0x6c, 0x68, 0xe0, 0x6b, 0xbb, 0x90, 0x3c, 0x87, 0xa3, 0x2b, 0xbe, 0xa9, 0xd1, 0x8f, 0x69, 0xb6, - 0xb8, 0xd3, 0xf7, 0x78, 0x28, 0xd0, 0xb0, 0x36, 0xeb, 0xd5, 0xe0, 0x65, 0x30, 0xff, 0x16, 0x40, - 0x78, 0xc9, 0x2e, 0xdc, 0x88, 0x4d, 0x2e, 0xb1, 0xc4, 0xae, 0x5e, 0x87, 0x1c, 0xaf, 0x2a, 0xbe, - 0xeb, 0x6a, 0x4e, 0x59, 0x87, 0xae, 0x4d, 0x0f, 0x7b, 0xa6, 0x09, 0x0c, 0x2b, 0x6e, 0xa5, 0x1f, - 0xce, 0x94, 0xf9, 0x98, 0xdc, 0x85, 0x89, 0xe6, 0xfb, 0xcc, 0xf3, 0xed, 0x80, 0xc6, 0x9a, 0xef, - 0x3f, 0x3a, 0xe9, 0x1e, 0x4c, 0x9d, 0xb4, 0xab, 0x51, 0x37, 0x74, 0xe2, 0x35, 0x97, 0xfb, 0xc9, - 0x61, 0x72, 0x06, 0x93, 0x95, 0xe6, 0xeb, 0x12, 0xb7, 0x96, 0x4e, 0x5b, 0xed, 0x17, 0x9e, 0x3f, - 0x82, 0x59, 0xcf, 0x8d, 0x6b, 0xd1, 0xfb, 0x31, 0x34, 0x88, 0x42, 0xd7, 0x62, 0x8b, 0xe6, 0x3f, - 0x02, 0x98, 0x30, 0x34, 0x95, 0xda, 0x1a, 0x74, 0x0b, 0x31, 0x96, 0xdb, 0xda, 0x64, 0xb9, 0x12, - 0xad, 0x99, 0x13, 0x06, 0x2d, 0x75, 0xae, 0x04, 0x5e, 0x6f, 0x36, 0xec, 0x6d, 0xf6, 0xc3, 0x1f, - 0x2e, 0xcf, 0x93, 0xdf, 0x2f, 0x4f, 0x7b, 0xc4, 0xdf, 0x6f, 0xcf, 0x3f, 0xdc, 0xe3, 0x9b, 0xe4, - 0xcb, 0xb3, 0x75, 0x61, 0x65, 0xbd, 0x4c, 0x72, 0x55, 0xa6, 0x92, 0x1b, 0x59, 0xe4, 0x4a, 0x57, - 0xe9, 0x15, 0xaf, 0x37, 0x36, 0xbd, 0xf1, 0xec, 0x96, 0x23, 0xff, 0xe2, 0x5e, 0xfc, 0x0c, 0x00, - 0x00, 0xff, 0xff, 0x03, 0xfa, 0xd9, 0x51, 0x92, 0x03, 0x00, 0x00, +var fileDescriptor_types_9f616cf8b37ed00d = []byte{ + // 497 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x53, 0xc1, 0x6e, 0xd3, 0x40, + 0x10, 0x95, 0xe3, 0xb4, 0x49, 0x26, 0x0d, 0x2d, 0x7b, 0x80, 0xa5, 0x08, 0x61, 0x22, 0x51, 0x22, + 0x01, 0x8e, 0x14, 0x2e, 0x88, 0x1b, 0x54, 0x48, 0x1c, 0x0a, 0x82, 0x95, 0x2a, 0x04, 0x17, 0x6b, + 0xe3, 0x9d, 0x64, 0x2d, 0xec, 0xac, 0xb3, 0xbb, 0x6e, 0xe4, 0xdf, 0xe0, 0x4f, 0xf8, 0x27, 0x3e, + 0x04, 0xed, 0xda, 0x34, 0x46, 0x15, 0x12, 0x17, 0x4e, 0x99, 0xf7, 0xde, 0x64, 0x3c, 0x6f, 0x66, + 0x16, 0x1e, 0x48, 0xcc, 0x4b, 0xd4, 0xf3, 0x95, 0xd2, 0x3b, 0xae, 0x45, 0xb6, 0x59, 0xcf, 0x6d, + 0x5d, 0xa2, 0x89, 0x4b, 0xad, 0xac, 0x22, 0xb0, 0xe7, 0xa7, 0x3f, 0x7b, 0x30, 0x60, 0xb8, 0xad, + 0xd0, 0x58, 0x72, 0x07, 0x0e, 0x0b, 0xb4, 0x52, 0x09, 0xda, 0x8b, 0x82, 0xd9, 0x88, 0xb5, 0x88, + 0x3c, 0x82, 0xb0, 0xd2, 0x39, 0x0d, 0xa3, 0x60, 0x36, 0x5e, 0x1c, 0xc7, 0xfb, 0x7f, 0xc7, 0x97, + 0xec, 0x82, 0x39, 0x8d, 0xbc, 0x87, 0x5b, 0x12, 0xb9, 0x40, 0x9d, 0xe0, 0xc6, 0xea, 0x0c, 0x0d, + 0xed, 0x47, 0xe1, 0x6c, 0xbc, 0x38, 0xeb, 0x66, 0xb7, 0xdf, 0x89, 0xdf, 0xf9, 0xcc, 0xb7, 0x4d, + 0xa2, 0xfb, 0xa9, 0xd9, 0x44, 0x76, 0x39, 0x42, 0xa0, 0xbf, 0x54, 0xa2, 0xa6, 0x07, 0x51, 0x30, + 0x3b, 0x62, 0x3e, 0x76, 0x9c, 0x54, 0xc6, 0xd2, 0x43, 0xdf, 0x9b, 0x8f, 0xc9, 0x43, 0x18, 0x6b, + 0x2c, 0x94, 0xc5, 0x84, 0x0b, 0xa1, 0xe9, 0xc0, 0x4b, 0xd0, 0x50, 0xaf, 0x85, 0xd0, 0xe4, 0x29, + 0xdc, 0x2e, 0x11, 0x75, 0x92, 0xa2, 0xb6, 0xd9, 0x2a, 0x4b, 0xb9, 0x45, 0x43, 0x87, 0x51, 0x38, + 0x3b, 0x62, 0x27, 0x4e, 0x38, 0xef, 0xf0, 0xa7, 0x5f, 0x80, 0xdc, 0x6c, 0x8d, 0x9c, 0x40, 0xf8, + 0x0d, 0x6b, 0x1a, 0xf8, 0xda, 0x2e, 0x24, 0xcf, 0xe1, 0xe0, 0x8a, 0xe7, 0x15, 0xfa, 0x31, 0x8d, + 0x17, 0x77, 0xbb, 0x1e, 0xf7, 0x05, 0x6a, 0xd6, 0x64, 0xbd, 0xea, 0xbd, 0x0c, 0xa6, 0x3f, 0x02, + 0x08, 0x2f, 0xd9, 0x85, 0x1b, 0xb1, 0x49, 0x25, 0x16, 0xd8, 0xd6, 0x6b, 0x91, 0xe3, 0x55, 0xc9, + 0xb7, 0x6d, 0xcd, 0x11, 0x6b, 0xd1, 0xb5, 0xe9, 0x7e, 0xc7, 0x34, 0x81, 0x7e, 0xc9, 0xad, 0xf4, + 0xc3, 0x19, 0x31, 0x1f, 0x93, 0x7b, 0x30, 0xd4, 0x7c, 0x97, 0x78, 0xbe, 0x19, 0xd0, 0x40, 0xf3, + 0xdd, 0x47, 0x27, 0xdd, 0x87, 0x91, 0x93, 0xb6, 0x15, 0xea, 0x9a, 0x0e, 0xbd, 0xe6, 0x72, 0x3f, + 0x39, 0x4c, 0x4e, 0x61, 0xb8, 0xd2, 0x7c, 0x5d, 0xe0, 0xc6, 0xd2, 0x51, 0xa3, 0xfd, 0xc6, 0xd3, + 0xc7, 0x30, 0xee, 0xb8, 0x71, 0x2d, 0x7a, 0x3f, 0x86, 0x06, 0x51, 0xe8, 0x5a, 0x6c, 0xd0, 0xf4, + 0x7b, 0x0f, 0x86, 0x0c, 0x4d, 0xa9, 0x36, 0x06, 0xdd, 0x42, 0x8c, 0xe5, 0xb6, 0x32, 0x49, 0xaa, + 0x44, 0x63, 0x66, 0xc2, 0xa0, 0xa1, 0xce, 0x95, 0xc0, 0xeb, 0xcd, 0x86, 0x9d, 0xcd, 0x7e, 0xf8, + 0xcb, 0xf1, 0x3c, 0xf9, 0xf3, 0x78, 0x9a, 0x4f, 0xfc, 0xc3, 0xf5, 0x9c, 0xc1, 0x71, 0xce, 0x8d, + 0x4d, 0xda, 0xd3, 0xd8, 0xf1, 0xdc, 0xcf, 0xaa, 0xcf, 0x26, 0x8e, 0x66, 0x9e, 0xfd, 0xcc, 0xf3, + 0xff, 0xb8, 0xef, 0x37, 0xf1, 0xd7, 0x67, 0xeb, 0xcc, 0xca, 0x6a, 0x19, 0xa7, 0xaa, 0x98, 0x4b, + 0x6e, 0x64, 0x96, 0x2a, 0x5d, 0xce, 0xaf, 0x78, 0x95, 0xdb, 0xf9, 0x8d, 0xe7, 0xb9, 0x3c, 0xf4, + 0x2f, 0xf3, 0xc5, 0xaf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfd, 0xbd, 0xb1, 0xfc, 0xba, 0x03, 0x00, + 0x00, } diff --git a/helper/forwarding/types.proto b/helper/forwarding/types.proto index 2189934923..8f1376a180 100644 --- a/helper/forwarding/types.proto +++ b/helper/forwarding/types.proto @@ -45,4 +45,5 @@ message Response { // Added in 0.6.2 to ensure that the content-type is set appropriately, as // well as any other information map header_entries = 4; + uint64 last_remote_wal = 5; } diff --git a/helper/identity/mfa/types.pb.go b/helper/identity/mfa/types.pb.go new file mode 100644 index 0000000000..adf8af56fd --- /dev/null +++ b/helper/identity/mfa/types.pb.go @@ -0,0 +1,69 @@ +// +build !enterprise +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: helper/identity/mfa/types.proto + +package mfa // import "github.com/hashicorp/vault/helper/identity/mfa" + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type Secret struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Secret) Reset() { *m = Secret{} } +func (m *Secret) String() string { return proto.CompactTextString(m) } +func (*Secret) ProtoMessage() {} +func (*Secret) Descriptor() ([]byte, []int) { + return fileDescriptor_types_13bac6e8bbc072d1, []int{0} +} +func (m *Secret) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Secret.Unmarshal(m, b) +} +func (m *Secret) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Secret.Marshal(b, m, deterministic) +} +func (dst *Secret) XXX_Merge(src proto.Message) { + xxx_messageInfo_Secret.Merge(dst, src) +} +func (m *Secret) XXX_Size() int { + return xxx_messageInfo_Secret.Size(m) +} +func (m *Secret) XXX_DiscardUnknown() { + xxx_messageInfo_Secret.DiscardUnknown(m) +} + +var xxx_messageInfo_Secret proto.InternalMessageInfo + +func init() { + proto.RegisterType((*Secret)(nil), "mfa.Secret") +} + +func init() { + proto.RegisterFile("helper/identity/mfa/types.proto", fileDescriptor_types_13bac6e8bbc072d1) +} + +var fileDescriptor_types_13bac6e8bbc072d1 = []byte{ + // 111 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0xcf, 0x48, 0xcd, 0x29, + 0x48, 0x2d, 0xd2, 0xcf, 0x4c, 0x49, 0xcd, 0x2b, 0xc9, 0x2c, 0xa9, 0xd4, 0xcf, 0x4d, 0x4b, 0xd4, + 0x2f, 0xa9, 0x2c, 0x48, 0x2d, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0xce, 0x4d, 0x4b, + 0x54, 0xe2, 0xe0, 0x62, 0x0b, 0x4e, 0x4d, 0x2e, 0x4a, 0x2d, 0x71, 0x32, 0x88, 0xd2, 0x4b, 0xcf, + 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0xcf, 0x48, 0x2c, 0xce, 0xc8, 0x4c, 0xce, + 0x2f, 0x2a, 0xd0, 0x2f, 0x4b, 0x2c, 0xcd, 0x29, 0xd1, 0xc7, 0x62, 0x58, 0x12, 0x1b, 0xd8, 0x1c, + 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xa9, 0xc9, 0x73, 0x5e, 0x6a, 0x00, 0x00, 0x00, +} diff --git a/helper/identity/mfa/types.proto b/helper/identity/mfa/types.proto new file mode 100644 index 0000000000..ab90807213 --- /dev/null +++ b/helper/identity/mfa/types.proto @@ -0,0 +1,7 @@ +syntax = "proto3"; + +option go_package = "github.com/hashicorp/vault/helper/identity/mfa"; + +package mfa; + +message Secret {} diff --git a/helper/identity/templating.go b/helper/identity/templating.go index 0848b0c716..74645b5577 100644 --- a/helper/identity/templating.go +++ b/helper/identity/templating.go @@ -4,6 +4,8 @@ import ( "errors" "fmt" "strings" + + "github.com/hashicorp/vault/helper/namespace" ) var ( @@ -18,6 +20,7 @@ type PopulateStringInput struct { String string Entity *Entity Groups []*Group + Namespace *namespace.Namespace } func PopulateString(p *PopulateStringInput) (bool, string, error) { @@ -58,7 +61,7 @@ func PopulateString(p *PopulateStringInput) (bool, string, error) { case 2: subst = true if !p.ValidityCheckOnly { - tmplStr, err := performTemplating(strings.TrimSpace(splitPiece[0]), p.Entity, p.Groups) + tmplStr, err := performTemplating(p.Namespace, strings.TrimSpace(splitPiece[0]), p.Entity, p.Groups) if err != nil { return false, "", err } @@ -73,7 +76,7 @@ func PopulateString(p *PopulateStringInput) (bool, string, error) { return subst, b.String(), nil } -func performTemplating(input string, entity *Entity, groups []*Group) (string, error) { +func performTemplating(ns *namespace.Namespace, input string, entity *Entity, groups []*Group) (string, error) { performAliasTemplating := func(trimmed string, alias *Alias) (string, error) { switch { case trimmed == "id": @@ -151,9 +154,15 @@ func performTemplating(input string, entity *Entity, groups []*Group) (string, e } var found *Group for _, group := range groups { - compare := group.Name + var compare string if ids { compare = group.ID + } else { + if ns != nil && group.NamespaceID == ns.ID { + compare = group.Name + } else { + continue + } } if compare == accessorSplit[0] { diff --git a/helper/identity/templating_test.go b/helper/identity/templating_test.go index fc01142f22..66bf5d545b 100644 --- a/helper/identity/templating_test.go +++ b/helper/identity/templating_test.go @@ -3,6 +3,8 @@ package identity import ( "errors" "testing" + + "github.com/hashicorp/vault/helper/namespace" ) func TestPopulate_Basic(t *testing.T) { @@ -165,9 +167,10 @@ func TestPopulate_Basic(t *testing.T) { var groups []*Group if test.groupName != "" { groups = append(groups, &Group{ - ID: "groupID", - Name: test.groupName, - Metadata: test.groupMetadata, + ID: "groupID", + Name: test.groupName, + Metadata: test.groupMetadata, + NamespaceID: namespace.RootNamespace.ID, }) } subst, out, err := PopulateString(&PopulateStringInput{ @@ -175,6 +178,7 @@ func TestPopulate_Basic(t *testing.T) { String: test.input, Entity: entity, Groups: groups, + Namespace: namespace.RootNamespace, }) if err != nil { if test.err == nil { diff --git a/helper/identity/types.pb.go b/helper/identity/types.pb.go index 019c555eb2..65a4e0c808 100644 --- a/helper/identity/types.pb.go +++ b/helper/identity/types.pb.go @@ -7,6 +7,7 @@ import proto "github.com/golang/protobuf/proto" import fmt "fmt" import math "math" import timestamp "github.com/golang/protobuf/ptypes/timestamp" +import mfa "github.com/hashicorp/vault/helper/identity/mfa" // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -56,7 +57,11 @@ type Group struct { // Memberships of the internal groups can be managed over the API whereas // the memberships on the external group --for which a corresponding alias // will be set-- will be managed automatically. - Type string `sentinel:"" protobuf:"bytes,12,opt,name=type,proto3" json:"type,omitempty"` + Type string `sentinel:"" protobuf:"bytes,12,opt,name=type,proto3" json:"type,omitempty"` + // NamespaceID is the identifier of the namespace to which this group + // belongs to. Do not return this value over the API when reading the + // group. + NamespaceID string `sentinel:"" protobuf:"bytes,13,opt,name=namespace_id,json=namespaceID,proto3" json:"namespace_id,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -66,7 +71,7 @@ func (m *Group) Reset() { *m = Group{} } func (m *Group) String() string { return proto.CompactTextString(m) } func (*Group) ProtoMessage() {} func (*Group) Descriptor() ([]byte, []int) { - return fileDescriptor_types_0360db4a8e77dd9b, []int{0} + return fileDescriptor_types_7c8884717ecd36e8, []int{0} } func (m *Group) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Group.Unmarshal(m, b) @@ -170,6 +175,13 @@ func (m *Group) GetType() string { return "" } +func (m *Group) GetNamespaceID() string { + if m != nil { + return m.NamespaceID + } + return "" +} + // Entity represents an entity that gets persisted and indexed. // Entity is fundamentally composed of zero or many aliases. type Entity struct { @@ -211,9 +223,16 @@ type Entity struct { // the entities belonging to a particular bucket during invalidation of the // storage key. BucketKeyHash string `sentinel:"" protobuf:"bytes,9,opt,name=bucket_key_hash,json=bucketKeyHash,proto3" json:"bucket_key_hash,omitempty"` + // MFASecrets holds the MFA secrets indexed by the identifier of the MFA + // method configuration. + MFASecrets map[string]*mfa.Secret `sentinel:"" protobuf:"bytes,10,rep,name=mfa_secrets,json=mfaSecrets,proto3" json:"mfa_secrets,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Disabled indicates whether tokens associated with the account should not // be able to be used - Disabled bool `sentinel:"" protobuf:"varint,11,opt,name=disabled,proto3" json:"disabled,omitempty"` + Disabled bool `sentinel:"" protobuf:"varint,11,opt,name=disabled,proto3" json:"disabled,omitempty"` + // NamespaceID is the identifier of the namespace to which this entity + // belongs to. Do not return this value over the API when reading the + // entity. + NamespaceID string `sentinel:"" protobuf:"bytes,12,opt,name=namespace_id,json=namespaceID,proto3" json:"namespace_id,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -223,7 +242,7 @@ func (m *Entity) Reset() { *m = Entity{} } func (m *Entity) String() string { return proto.CompactTextString(m) } func (*Entity) ProtoMessage() {} func (*Entity) Descriptor() ([]byte, []int) { - return fileDescriptor_types_0360db4a8e77dd9b, []int{1} + return fileDescriptor_types_7c8884717ecd36e8, []int{1} } func (m *Entity) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Entity.Unmarshal(m, b) @@ -306,6 +325,13 @@ func (m *Entity) GetBucketKeyHash() string { return "" } +func (m *Entity) GetMFASecrets() map[string]*mfa.Secret { + if m != nil { + return m.MFASecrets + } + return nil +} + func (m *Entity) GetDisabled() bool { if m != nil { return m.Disabled @@ -313,6 +339,13 @@ func (m *Entity) GetDisabled() bool { return false } +func (m *Entity) GetNamespaceID() string { + if m != nil { + return m.NamespaceID + } + return "" +} + // Alias represents the alias that gets stored inside of the // entity object in storage and also represents in an in-memory index of an // alias object. @@ -349,16 +382,19 @@ type Alias struct { LastUpdateTime *timestamp.Timestamp `sentinel:"" protobuf:"bytes,9,opt,name=last_update_time,json=lastUpdateTime,proto3" json:"last_update_time,omitempty"` // MergedFromCanonicalIDs is the FIFO history of merging activity MergedFromCanonicalIDs []string `sentinel:"" protobuf:"bytes,10,rep,name=merged_from_canonical_ids,json=mergedFromCanonicalIds,proto3" json:"merged_from_canonical_ids,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + // NamespaceID is the identifier of the namespace to which this alias + // belongs. + NamespaceID string `sentinel:"" protobuf:"bytes,11,opt,name=namespace_id,json=namespaceID,proto3" json:"namespace_id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *Alias) Reset() { *m = Alias{} } func (m *Alias) String() string { return proto.CompactTextString(m) } func (*Alias) ProtoMessage() {} func (*Alias) Descriptor() ([]byte, []int) { - return fileDescriptor_types_0360db4a8e77dd9b, []int{2} + return fileDescriptor_types_7c8884717ecd36e8, []int{2} } func (m *Alias) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Alias.Unmarshal(m, b) @@ -448,58 +484,306 @@ func (m *Alias) GetMergedFromCanonicalIDs() []string { return nil } +func (m *Alias) GetNamespaceID() string { + if m != nil { + return m.NamespaceID + } + return "" +} + +// Deprecated. Retained for backwards compatibility. +type EntityStorageEntry struct { + Personas []*PersonaIndexEntry `sentinel:"" protobuf:"bytes,1,rep,name=personas,proto3" json:"personas,omitempty"` + ID string `sentinel:"" protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` + Name string `sentinel:"" protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` + Metadata map[string]string `sentinel:"" protobuf:"bytes,4,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + CreationTime *timestamp.Timestamp `sentinel:"" protobuf:"bytes,5,opt,name=creation_time,json=creationTime,proto3" json:"creation_time,omitempty"` + LastUpdateTime *timestamp.Timestamp `sentinel:"" protobuf:"bytes,6,opt,name=last_update_time,json=lastUpdateTime,proto3" json:"last_update_time,omitempty"` + MergedEntityIDs []string `sentinel:"" protobuf:"bytes,7,rep,name=merged_entity_ids,json=mergedEntityIDs,proto3" json:"merged_entity_ids,omitempty"` + Policies []string `sentinel:"" protobuf:"bytes,8,rep,name=policies,proto3" json:"policies,omitempty"` + BucketKeyHash string `sentinel:"" protobuf:"bytes,9,opt,name=bucket_key_hash,json=bucketKeyHash,proto3" json:"bucket_key_hash,omitempty"` + MFASecrets map[string]*mfa.Secret `sentinel:"" protobuf:"bytes,10,rep,name=mfa_secrets,json=mfaSecrets,proto3" json:"mfa_secrets,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *EntityStorageEntry) Reset() { *m = EntityStorageEntry{} } +func (m *EntityStorageEntry) String() string { return proto.CompactTextString(m) } +func (*EntityStorageEntry) ProtoMessage() {} +func (*EntityStorageEntry) Descriptor() ([]byte, []int) { + return fileDescriptor_types_7c8884717ecd36e8, []int{3} +} +func (m *EntityStorageEntry) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_EntityStorageEntry.Unmarshal(m, b) +} +func (m *EntityStorageEntry) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_EntityStorageEntry.Marshal(b, m, deterministic) +} +func (dst *EntityStorageEntry) XXX_Merge(src proto.Message) { + xxx_messageInfo_EntityStorageEntry.Merge(dst, src) +} +func (m *EntityStorageEntry) XXX_Size() int { + return xxx_messageInfo_EntityStorageEntry.Size(m) +} +func (m *EntityStorageEntry) XXX_DiscardUnknown() { + xxx_messageInfo_EntityStorageEntry.DiscardUnknown(m) +} + +var xxx_messageInfo_EntityStorageEntry proto.InternalMessageInfo + +func (m *EntityStorageEntry) GetPersonas() []*PersonaIndexEntry { + if m != nil { + return m.Personas + } + return nil +} + +func (m *EntityStorageEntry) GetID() string { + if m != nil { + return m.ID + } + return "" +} + +func (m *EntityStorageEntry) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *EntityStorageEntry) GetMetadata() map[string]string { + if m != nil { + return m.Metadata + } + return nil +} + +func (m *EntityStorageEntry) GetCreationTime() *timestamp.Timestamp { + if m != nil { + return m.CreationTime + } + return nil +} + +func (m *EntityStorageEntry) GetLastUpdateTime() *timestamp.Timestamp { + if m != nil { + return m.LastUpdateTime + } + return nil +} + +func (m *EntityStorageEntry) GetMergedEntityIDs() []string { + if m != nil { + return m.MergedEntityIDs + } + return nil +} + +func (m *EntityStorageEntry) GetPolicies() []string { + if m != nil { + return m.Policies + } + return nil +} + +func (m *EntityStorageEntry) GetBucketKeyHash() string { + if m != nil { + return m.BucketKeyHash + } + return "" +} + +func (m *EntityStorageEntry) GetMFASecrets() map[string]*mfa.Secret { + if m != nil { + return m.MFASecrets + } + return nil +} + +// Deprecated. Retained for backwards compatibility. +type PersonaIndexEntry struct { + ID string `sentinel:"" protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + EntityID string `sentinel:"" protobuf:"bytes,2,opt,name=entity_id,json=entityId,proto3" json:"entity_id,omitempty"` + MountType string `sentinel:"" protobuf:"bytes,3,opt,name=mount_type,json=mountType,proto3" json:"mount_type,omitempty"` + MountAccessor string `sentinel:"" protobuf:"bytes,4,opt,name=mount_accessor,json=mountAccessor,proto3" json:"mount_accessor,omitempty"` + MountPath string `sentinel:"" protobuf:"bytes,5,opt,name=mount_path,json=mountPath,proto3" json:"mount_path,omitempty"` + Metadata map[string]string `sentinel:"" protobuf:"bytes,6,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Name string `sentinel:"" protobuf:"bytes,7,opt,name=name,proto3" json:"name,omitempty"` + CreationTime *timestamp.Timestamp `sentinel:"" protobuf:"bytes,8,opt,name=creation_time,json=creationTime,proto3" json:"creation_time,omitempty"` + LastUpdateTime *timestamp.Timestamp `sentinel:"" protobuf:"bytes,9,opt,name=last_update_time,json=lastUpdateTime,proto3" json:"last_update_time,omitempty"` + MergedFromEntityIDs []string `sentinel:"" protobuf:"bytes,10,rep,name=merged_from_entity_ids,json=mergedFromEntityIDs,proto3" json:"merged_from_entity_ids,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *PersonaIndexEntry) Reset() { *m = PersonaIndexEntry{} } +func (m *PersonaIndexEntry) String() string { return proto.CompactTextString(m) } +func (*PersonaIndexEntry) ProtoMessage() {} +func (*PersonaIndexEntry) Descriptor() ([]byte, []int) { + return fileDescriptor_types_7c8884717ecd36e8, []int{4} +} +func (m *PersonaIndexEntry) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_PersonaIndexEntry.Unmarshal(m, b) +} +func (m *PersonaIndexEntry) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_PersonaIndexEntry.Marshal(b, m, deterministic) +} +func (dst *PersonaIndexEntry) XXX_Merge(src proto.Message) { + xxx_messageInfo_PersonaIndexEntry.Merge(dst, src) +} +func (m *PersonaIndexEntry) XXX_Size() int { + return xxx_messageInfo_PersonaIndexEntry.Size(m) +} +func (m *PersonaIndexEntry) XXX_DiscardUnknown() { + xxx_messageInfo_PersonaIndexEntry.DiscardUnknown(m) +} + +var xxx_messageInfo_PersonaIndexEntry proto.InternalMessageInfo + +func (m *PersonaIndexEntry) GetID() string { + if m != nil { + return m.ID + } + return "" +} + +func (m *PersonaIndexEntry) GetEntityID() string { + if m != nil { + return m.EntityID + } + return "" +} + +func (m *PersonaIndexEntry) GetMountType() string { + if m != nil { + return m.MountType + } + return "" +} + +func (m *PersonaIndexEntry) GetMountAccessor() string { + if m != nil { + return m.MountAccessor + } + return "" +} + +func (m *PersonaIndexEntry) GetMountPath() string { + if m != nil { + return m.MountPath + } + return "" +} + +func (m *PersonaIndexEntry) GetMetadata() map[string]string { + if m != nil { + return m.Metadata + } + return nil +} + +func (m *PersonaIndexEntry) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *PersonaIndexEntry) GetCreationTime() *timestamp.Timestamp { + if m != nil { + return m.CreationTime + } + return nil +} + +func (m *PersonaIndexEntry) GetLastUpdateTime() *timestamp.Timestamp { + if m != nil { + return m.LastUpdateTime + } + return nil +} + +func (m *PersonaIndexEntry) GetMergedFromEntityIDs() []string { + if m != nil { + return m.MergedFromEntityIDs + } + return nil +} + func init() { proto.RegisterType((*Group)(nil), "identity.Group") proto.RegisterMapType((map[string]string)(nil), "identity.Group.MetadataEntry") proto.RegisterType((*Entity)(nil), "identity.Entity") proto.RegisterMapType((map[string]string)(nil), "identity.Entity.MetadataEntry") + proto.RegisterMapType((map[string]*mfa.Secret)(nil), "identity.Entity.MFASecretsEntry") proto.RegisterType((*Alias)(nil), "identity.Alias") proto.RegisterMapType((map[string]string)(nil), "identity.Alias.MetadataEntry") + proto.RegisterType((*EntityStorageEntry)(nil), "identity.EntityStorageEntry") + proto.RegisterMapType((map[string]string)(nil), "identity.EntityStorageEntry.MetadataEntry") + proto.RegisterMapType((map[string]*mfa.Secret)(nil), "identity.EntityStorageEntry.MFASecretsEntry") + proto.RegisterType((*PersonaIndexEntry)(nil), "identity.PersonaIndexEntry") + proto.RegisterMapType((map[string]string)(nil), "identity.PersonaIndexEntry.MetadataEntry") } -func init() { proto.RegisterFile("helper/identity/types.proto", fileDescriptor_types_0360db4a8e77dd9b) } +func init() { proto.RegisterFile("helper/identity/types.proto", fileDescriptor_types_7c8884717ecd36e8) } -var fileDescriptor_types_0360db4a8e77dd9b = []byte{ - // 656 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x95, 0x5d, 0x6f, 0xd3, 0x3c, - 0x14, 0xc7, 0xd5, 0xa6, 0x2f, 0xe9, 0x69, 0xd7, 0xed, 0xb1, 0x1e, 0xa1, 0x50, 0x34, 0xe8, 0x26, - 0x0d, 0x95, 0x09, 0x25, 0xd2, 0xb8, 0x61, 0xe3, 0x02, 0x0d, 0x18, 0x30, 0x21, 0x24, 0x14, 0x8d, - 0x1b, 0x6e, 0x22, 0x37, 0xf1, 0x1a, 0x6b, 0x49, 0x1c, 0xc5, 0xce, 0x44, 0xbe, 0x0e, 0x5f, 0x8d, - 0x6b, 0xbe, 0x03, 0xf2, 0x71, 0xd3, 0x96, 0x75, 0xbc, 0x4c, 0xdb, 0x9d, 0xfd, 0x3f, 0xc7, 0xc7, - 0xf6, 0xf9, 0xff, 0xe2, 0xc0, 0x83, 0x98, 0x25, 0x39, 0x2b, 0x3c, 0x1e, 0xb1, 0x4c, 0x71, 0x55, - 0x79, 0xaa, 0xca, 0x99, 0x74, 0xf3, 0x42, 0x28, 0x41, 0xec, 0x5a, 0x1d, 0x3d, 0x9a, 0x09, 0x31, - 0x4b, 0x98, 0x87, 0xfa, 0xb4, 0x3c, 0xf7, 0x14, 0x4f, 0x99, 0x54, 0x34, 0xcd, 0x4d, 0xea, 0xee, - 0xb7, 0x16, 0xb4, 0xdf, 0x15, 0xa2, 0xcc, 0xc9, 0x10, 0x9a, 0x3c, 0x72, 0x1a, 0xe3, 0xc6, 0xa4, - 0xe7, 0x37, 0x79, 0x44, 0x08, 0xb4, 0x32, 0x9a, 0x32, 0xa7, 0x89, 0x0a, 0x8e, 0xc9, 0x08, 0xec, - 0x5c, 0x24, 0x3c, 0xe4, 0x4c, 0x3a, 0xd6, 0xd8, 0x9a, 0xf4, 0xfc, 0xc5, 0x9c, 0x4c, 0x60, 0x2b, - 0xa7, 0x05, 0xcb, 0x54, 0x30, 0xd3, 0xf5, 0x02, 0x1e, 0x49, 0xa7, 0x85, 0x39, 0x43, 0xa3, 0xe3, - 0x36, 0xa7, 0x91, 0x24, 0xfb, 0xf0, 0x5f, 0xca, 0xd2, 0x29, 0x2b, 0x02, 0x73, 0x4a, 0x4c, 0x6d, - 0x63, 0xea, 0xa6, 0x09, 0x9c, 0xa0, 0xae, 0x73, 0x0f, 0xc1, 0x4e, 0x99, 0xa2, 0x11, 0x55, 0xd4, - 0xe9, 0x8c, 0xad, 0x49, 0xff, 0x60, 0xdb, 0xad, 0x6f, 0xe7, 0x62, 0x45, 0xf7, 0xe3, 0x3c, 0x7e, - 0x92, 0xa9, 0xa2, 0xf2, 0x17, 0xe9, 0xe4, 0x25, 0x6c, 0x84, 0x05, 0xa3, 0x8a, 0x8b, 0x2c, 0xd0, - 0xd7, 0x76, 0xba, 0xe3, 0xc6, 0xa4, 0x7f, 0x30, 0x72, 0x4d, 0x4f, 0xdc, 0xba, 0x27, 0xee, 0x59, - 0xdd, 0x13, 0x7f, 0x50, 0x2f, 0xd0, 0x12, 0x79, 0x03, 0x5b, 0x09, 0x95, 0x2a, 0x28, 0xf3, 0x88, - 0x2a, 0x66, 0x6a, 0xd8, 0x7f, 0xad, 0x31, 0xd4, 0x6b, 0x3e, 0xe3, 0x12, 0xac, 0xb2, 0x03, 0x83, - 0x54, 0x44, 0xfc, 0xbc, 0x0a, 0x78, 0x16, 0xb1, 0xaf, 0x4e, 0x6f, 0xdc, 0x98, 0xb4, 0xfc, 0xbe, - 0xd1, 0x4e, 0xb5, 0x44, 0x1e, 0xc3, 0xe6, 0xb4, 0x0c, 0x2f, 0x98, 0x0a, 0x2e, 0x58, 0x15, 0xc4, - 0x54, 0xc6, 0x0e, 0x60, 0xd7, 0x37, 0x8c, 0xfc, 0x81, 0x55, 0xef, 0xa9, 0x8c, 0xc9, 0x1e, 0xb4, - 0x69, 0xc2, 0xa9, 0x74, 0xfa, 0x78, 0x8a, 0xcd, 0x65, 0x27, 0x8e, 0xb5, 0xec, 0x9b, 0xa8, 0x76, - 0x4e, 0xd3, 0xe0, 0x0c, 0x8c, 0x73, 0x7a, 0x3c, 0x7a, 0x01, 0x1b, 0xbf, 0xf4, 0x89, 0x6c, 0x81, - 0x75, 0xc1, 0xaa, 0xb9, 0xdf, 0x7a, 0x48, 0xfe, 0x87, 0xf6, 0x25, 0x4d, 0xca, 0xda, 0x71, 0x33, - 0x39, 0x6a, 0x3e, 0x6f, 0xec, 0x7e, 0xb7, 0xa0, 0x63, 0x2c, 0x21, 0x4f, 0xa0, 0x8b, 0x9b, 0x30, - 0xe9, 0x34, 0xd0, 0x8e, 0xb5, 0x43, 0xd4, 0xf1, 0x39, 0x50, 0xcd, 0x35, 0xa0, 0xac, 0x15, 0xa0, - 0x8e, 0x56, 0xec, 0x6d, 0x61, 0xbd, 0x87, 0xcb, 0x7a, 0x66, 0xcb, 0x7f, 0xf7, 0xb7, 0x7d, 0x07, - 0xfe, 0x76, 0x6e, 0xec, 0x2f, 0xd2, 0x5c, 0xcc, 0x58, 0xb4, 0x4a, 0x73, 0xb7, 0xa6, 0x59, 0x07, - 0x96, 0x34, 0xaf, 0x7e, 0x3f, 0xf6, 0x95, 0xef, 0xe7, 0x1a, 0x08, 0x7a, 0xd7, 0x41, 0x30, 0x02, - 0x3b, 0xe2, 0x92, 0x4e, 0x13, 0x16, 0x21, 0x07, 0xb6, 0xbf, 0x98, 0xdf, 0xce, 0xe5, 0x1f, 0x16, - 0xb4, 0xd1, 0xc2, 0xb5, 0xa7, 0x60, 0x07, 0x06, 0x21, 0xcd, 0x44, 0xc6, 0x43, 0x9a, 0x04, 0x0b, - 0x4f, 0xfb, 0x0b, 0xed, 0x34, 0x22, 0xdb, 0x00, 0xa9, 0x28, 0x33, 0x15, 0x20, 0x79, 0xc6, 0xe2, - 0x1e, 0x2a, 0x67, 0x55, 0xce, 0xc8, 0x1e, 0x0c, 0x4d, 0x98, 0x86, 0x21, 0x93, 0x52, 0x14, 0x4e, - 0xcb, 0xdc, 0x0d, 0xd5, 0xe3, 0xb9, 0xb8, 0xac, 0x92, 0x53, 0x15, 0xa3, 0x9f, 0x75, 0x95, 0x4f, - 0x54, 0xc5, 0x7f, 0x7e, 0x0c, 0xf0, 0xe8, 0xbf, 0x85, 0xa5, 0x86, 0xaf, 0xbb, 0x02, 0xdf, 0x1a, - 0x40, 0xf6, 0x1d, 0x00, 0xd4, 0xbb, 0x31, 0x40, 0x87, 0x70, 0x7f, 0x0e, 0xd0, 0x79, 0x21, 0xd2, - 0x60, 0xb5, 0xd3, 0xd2, 0x01, 0xa4, 0xe4, 0x9e, 0x49, 0x78, 0x5b, 0x88, 0xf4, 0xf5, 0xb2, 0xe9, - 0xf2, 0x56, 0x7e, 0xbf, 0x7a, 0xfa, 0x65, 0x7f, 0xc6, 0x55, 0x5c, 0x4e, 0xdd, 0x50, 0xa4, 0x9e, - 0x06, 0x8e, 0x87, 0xa2, 0xc8, 0xbd, 0x4b, 0x5a, 0x26, 0xca, 0xbb, 0xf2, 0x7f, 0x99, 0x76, 0xf0, - 0x26, 0xcf, 0x7e, 0x06, 0x00, 0x00, 0xff, 0xff, 0xf6, 0x89, 0x41, 0x55, 0x79, 0x06, 0x00, 0x00, +var fileDescriptor_types_7c8884717ecd36e8 = []byte{ + // 861 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x96, 0xcf, 0x8f, 0xdb, 0x44, + 0x14, 0xc7, 0x95, 0x1f, 0x4e, 0xec, 0xe7, 0xfc, 0xd8, 0x0e, 0xa8, 0x32, 0x59, 0x95, 0x66, 0x2b, + 0x15, 0xa5, 0xab, 0xca, 0x91, 0xb6, 0x07, 0x68, 0x39, 0xa0, 0x05, 0xb6, 0x10, 0x50, 0xa5, 0xca, + 0x2d, 0x17, 0x2e, 0xd6, 0xc4, 0x9e, 0x24, 0xa3, 0xb5, 0x3d, 0x96, 0x67, 0x5c, 0x91, 0xff, 0x80, + 0x23, 0x17, 0xfe, 0x24, 0xfe, 0x28, 0x6e, 0x68, 0x66, 0x6c, 0xc7, 0x8d, 0xd3, 0xa5, 0x2b, 0x22, + 0x04, 0x52, 0x6f, 0xf6, 0x77, 0xde, 0xbc, 0x3c, 0xbf, 0xf7, 0x79, 0x5f, 0x05, 0x4e, 0x37, 0x24, + 0x4a, 0x49, 0x36, 0xa7, 0x21, 0x49, 0x04, 0x15, 0xdb, 0xb9, 0xd8, 0xa6, 0x84, 0xbb, 0x69, 0xc6, + 0x04, 0x43, 0x66, 0xa9, 0x4e, 0xee, 0xaf, 0x19, 0x5b, 0x47, 0x64, 0xae, 0xf4, 0x65, 0xbe, 0x9a, + 0x0b, 0x1a, 0x13, 0x2e, 0x70, 0x9c, 0xea, 0xd0, 0xc9, 0xfd, 0xfd, 0x3c, 0xf1, 0x0a, 0xd7, 0x73, + 0x3d, 0xf8, 0xa3, 0x0b, 0xc6, 0x77, 0x19, 0xcb, 0x53, 0x34, 0x82, 0x36, 0x0d, 0x9d, 0xd6, 0xb4, + 0x35, 0xb3, 0xbc, 0x36, 0x0d, 0x11, 0x82, 0x6e, 0x82, 0x63, 0xe2, 0xb4, 0x95, 0xa2, 0x9e, 0xd1, + 0x04, 0xcc, 0x94, 0x45, 0x34, 0xa0, 0x84, 0x3b, 0x9d, 0x69, 0x67, 0x66, 0x79, 0xd5, 0x3b, 0x9a, + 0xc1, 0x49, 0x8a, 0x33, 0x92, 0x08, 0x7f, 0x2d, 0xf3, 0xf9, 0x34, 0xe4, 0x4e, 0x57, 0xc5, 0x8c, + 0xb4, 0xae, 0x7e, 0x66, 0x11, 0x72, 0x74, 0x0e, 0x77, 0x62, 0x12, 0x2f, 0x49, 0xe6, 0xeb, 0xa2, + 0x54, 0xa8, 0xa1, 0x42, 0xc7, 0xfa, 0xe0, 0x4a, 0xe9, 0x32, 0xf6, 0x29, 0x98, 0x31, 0x11, 0x38, + 0xc4, 0x02, 0x3b, 0xbd, 0x69, 0x67, 0x66, 0x5f, 0xdc, 0x73, 0xcb, 0x8f, 0x71, 0x55, 0x46, 0xf7, + 0x45, 0x71, 0x7e, 0x95, 0x88, 0x6c, 0xeb, 0x55, 0xe1, 0xe8, 0x2b, 0x18, 0x06, 0x19, 0xc1, 0x82, + 0xb2, 0xc4, 0x97, 0x7d, 0x71, 0xfa, 0xd3, 0xd6, 0xcc, 0xbe, 0x98, 0xb8, 0xba, 0x69, 0x6e, 0xd9, + 0x34, 0xf7, 0x75, 0xd9, 0x34, 0x6f, 0x50, 0x5e, 0x90, 0x12, 0xfa, 0x16, 0x4e, 0x22, 0xcc, 0x85, + 0x9f, 0xa7, 0x21, 0x16, 0x44, 0xe7, 0x30, 0xff, 0x36, 0xc7, 0x48, 0xde, 0xf9, 0x49, 0x5d, 0x51, + 0x59, 0xce, 0x60, 0x10, 0xb3, 0x90, 0xae, 0xb6, 0x3e, 0x4d, 0x42, 0xf2, 0x8b, 0x63, 0x4d, 0x5b, + 0xb3, 0xae, 0x67, 0x6b, 0x6d, 0x21, 0x25, 0xf4, 0x19, 0x8c, 0x97, 0x79, 0x70, 0x4d, 0x84, 0x7f, + 0x4d, 0xb6, 0xfe, 0x06, 0xf3, 0x8d, 0x03, 0xaa, 0xeb, 0x43, 0x2d, 0xff, 0x48, 0xb6, 0xdf, 0x63, + 0xbe, 0x41, 0x0f, 0xc1, 0xc0, 0x11, 0xc5, 0xdc, 0xb1, 0x55, 0x15, 0xe3, 0x5d, 0x27, 0x2e, 0xa5, + 0xec, 0xe9, 0x53, 0x39, 0x39, 0x39, 0x62, 0x67, 0xa0, 0x27, 0x27, 0x9f, 0x65, 0x15, 0x72, 0x82, + 0x3c, 0xc5, 0x01, 0xf1, 0x69, 0xe8, 0x0c, 0xd5, 0x99, 0x5d, 0x69, 0x8b, 0x70, 0xf2, 0x25, 0x0c, + 0xdf, 0x6a, 0x25, 0x3a, 0x81, 0xce, 0x35, 0xd9, 0x16, 0x48, 0xc8, 0x47, 0xf4, 0x31, 0x18, 0x6f, + 0x70, 0x94, 0x97, 0x50, 0xe8, 0x97, 0x67, 0xed, 0x2f, 0x5a, 0x0f, 0x7e, 0x37, 0xa0, 0xa7, 0xa7, + 0x86, 0x1e, 0x41, 0x5f, 0xd5, 0x41, 0xb8, 0xd3, 0x52, 0x13, 0x6b, 0xd4, 0x59, 0x9e, 0x17, 0xcc, + 0xb5, 0x1b, 0xcc, 0x75, 0x6a, 0xcc, 0x3d, 0xab, 0x11, 0xd0, 0x55, 0xf9, 0x3e, 0xdd, 0xe5, 0xd3, + 0x3f, 0xf9, 0xfe, 0x08, 0x18, 0x47, 0x40, 0xa0, 0x77, 0x6b, 0x04, 0x14, 0xf0, 0xd9, 0x9a, 0x84, + 0x75, 0xe0, 0xfb, 0x25, 0xf0, 0xf2, 0x60, 0x07, 0x7c, 0x7d, 0xc5, 0xcc, 0xbd, 0x15, 0x3b, 0xc0, + 0x89, 0x75, 0x88, 0x93, 0x4b, 0xb0, 0xe3, 0x15, 0xf6, 0x39, 0x09, 0x32, 0x22, 0xb8, 0x03, 0xaa, + 0x6b, 0xd3, 0x66, 0xd7, 0x56, 0xf8, 0x95, 0x0e, 0xd1, 0x7d, 0x83, 0xb8, 0x12, 0x64, 0x19, 0x21, + 0xe5, 0x78, 0x19, 0x91, 0x50, 0xd1, 0x66, 0x7a, 0xd5, 0x7b, 0x83, 0xa5, 0xc1, 0x71, 0x59, 0x9a, + 0xfc, 0x00, 0xe3, 0xbd, 0xd2, 0x0e, 0x5c, 0x3f, 0xab, 0x5f, 0xb7, 0x2f, 0x6c, 0x37, 0x5e, 0x61, + 0x57, 0xdf, 0xa9, 0x73, 0xf9, 0x5b, 0x17, 0x0c, 0x05, 0x5d, 0xc3, 0xdf, 0xce, 0x60, 0x10, 0xe0, + 0x84, 0x25, 0x34, 0xc0, 0x91, 0x5f, 0x51, 0x68, 0x57, 0xda, 0x22, 0x44, 0xf7, 0x00, 0x62, 0x96, + 0x27, 0xc2, 0x57, 0xeb, 0xa4, 0xa1, 0xb4, 0x94, 0xf2, 0x5a, 0xee, 0xd4, 0x43, 0x18, 0xe9, 0x63, + 0x1c, 0x04, 0x84, 0x73, 0x96, 0x39, 0x5d, 0x3d, 0x0d, 0xa5, 0x5e, 0x16, 0xe2, 0x2e, 0x4b, 0x8a, + 0xc5, 0x46, 0x11, 0x58, 0x66, 0x79, 0x89, 0xc5, 0xe6, 0x66, 0x87, 0x53, 0xa5, 0xbf, 0x13, 0xef, + 0x72, 0x5d, 0xfa, 0xb5, 0x75, 0x69, 0x20, 0x6f, 0x1e, 0x01, 0x79, 0xeb, 0xd6, 0xc8, 0x3f, 0x85, + 0x4f, 0x0a, 0xe4, 0x57, 0x19, 0x8b, 0xfd, 0x7a, 0xa7, 0x35, 0x90, 0x96, 0x77, 0x57, 0x07, 0x3c, + 0xcf, 0x58, 0xfc, 0xcd, 0xae, 0xe9, 0xbc, 0x81, 0x97, 0x7d, 0x64, 0xab, 0xfa, 0xd5, 0x00, 0xa4, + 0x37, 0xe0, 0x95, 0x60, 0x19, 0x5e, 0x13, 0x9d, 0xe2, 0x73, 0x30, 0x53, 0x92, 0x71, 0x96, 0xe0, + 0xd2, 0xb7, 0x4e, 0x77, 0x73, 0x78, 0xa9, 0x4f, 0x94, 0x5d, 0x17, 0x53, 0x28, 0x83, 0xdf, 0xcb, + 0xc4, 0x9e, 0x37, 0x4c, 0xec, 0x7c, 0x7f, 0x1d, 0xeb, 0xc5, 0x7c, 0x30, 0xb4, 0xb7, 0x0d, 0xed, + 0xc5, 0x21, 0x43, 0x7b, 0x7c, 0x73, 0x07, 0xdf, 0x6d, 0x6e, 0xff, 0x1d, 0x77, 0xfa, 0xb3, 0x03, + 0x77, 0x1a, 0x68, 0x35, 0x9c, 0xea, 0x14, 0xac, 0xaa, 0xcd, 0x45, 0x3d, 0x26, 0x29, 0xfa, 0xfb, + 0xef, 0x78, 0xd4, 0x55, 0xc3, 0xa3, 0x1e, 0xdd, 0xb0, 0x1b, 0xff, 0x47, 0xbf, 0x7a, 0x02, 0x77, + 0xeb, 0x7e, 0x55, 0xc3, 0x5a, 0x9b, 0xd5, 0x47, 0x3b, 0xb3, 0xaa, 0xd0, 0xfe, 0x47, 0x1c, 0x7d, + 0xfd, 0xf8, 0xe7, 0xf3, 0x35, 0x15, 0x9b, 0x7c, 0xe9, 0x06, 0x2c, 0x9e, 0x4b, 0xf6, 0x69, 0xc0, + 0xb2, 0x74, 0xfe, 0x06, 0xe7, 0x91, 0x98, 0xef, 0xfd, 0x6f, 0x5f, 0xf6, 0xd4, 0x37, 0x3c, 0xf9, + 0x2b, 0x00, 0x00, 0xff, 0xff, 0x66, 0xa2, 0xa4, 0x7e, 0x19, 0x0c, 0x00, 0x00, } diff --git a/helper/identity/types.proto b/helper/identity/types.proto index 6937b7ea3f..c604bb0497 100644 --- a/helper/identity/types.proto +++ b/helper/identity/types.proto @@ -5,6 +5,7 @@ option go_package = "github.com/hashicorp/vault/helper/identity"; package identity; import "google/protobuf/timestamp.proto"; +import "helper/identity/mfa/types.proto"; // Group represents an identity group. message Group { @@ -56,14 +57,12 @@ message Group { // will be set-- will be managed automatically. string type = 12; - // **Enterprise only** - // NamespaceID is the identifier of the namespace to which this group + // NamespaceID is the identifier of the namespace to which this group // belongs to. Do not return this value over the API when reading the // group. - //string namespace_id = 13; + string namespace_id = 13; } - // Entity represents an entity that gets persisted and indexed. // Entity is fundamentally composed of zero or many aliases. message Entity { @@ -114,20 +113,18 @@ message Entity { // storage key. string bucket_key_hash = 9; - // **Enterprise only** // MFASecrets holds the MFA secrets indexed by the identifier of the MFA // method configuration. - //map mfa_secrets = 10; + map mfa_secrets = 10; // Disabled indicates whether tokens associated with the account should not // be able to be used bool disabled = 11; - // **Enterprise only** // NamespaceID is the identifier of the namespace to which this entity // belongs to. Do not return this value over the API when reading the // entity. - //string namespace_id = 12; + string namespace_id = 12; } // Alias represents the alias that gets stored inside of the @@ -175,4 +172,36 @@ message Alias { // MergedFromCanonicalIDs is the FIFO history of merging activity repeated string merged_from_canonical_ids = 10; + + // NamespaceID is the identifier of the namespace to which this alias + // belongs. + string namespace_id = 11; +} + +// Deprecated. Retained for backwards compatibility. +message EntityStorageEntry { + repeated PersonaIndexEntry personas = 1; + string id = 2; + string name = 3; + map metadata = 4; + google.protobuf.Timestamp creation_time = 5; + google.protobuf.Timestamp last_update_time= 6; + repeated string merged_entity_ids = 7; + repeated string policies = 8; + string bucket_key_hash = 9; + map mfa_secrets = 10; +} + +// Deprecated. Retained for backwards compatibility. +message PersonaIndexEntry { + string id = 1; + string entity_id = 2; + string mount_type = 3; + string mount_accessor = 4; + string mount_path = 5; + map metadata = 6; + string name = 7; + google.protobuf.Timestamp creation_time = 8; + google.protobuf.Timestamp last_update_time = 9; + repeated string merged_from_entity_ids = 10; } diff --git a/helper/license/feature.go b/helper/license/feature.go new file mode 100644 index 0000000000..98e83c617b --- /dev/null +++ b/helper/license/feature.go @@ -0,0 +1,12 @@ +// +build !enterprise + +package license + +// Features is a bitmask of feature flags +type Features uint + +const FeatureNone Features = 0 + +func (f Features) HasFeature(flag Features) bool { + return false +} diff --git a/helper/namespace/namespace.go b/helper/namespace/namespace.go index 4b3b92410a..d8327dc6eb 100644 --- a/helper/namespace/namespace.go +++ b/helper/namespace/namespace.go @@ -3,7 +3,6 @@ package namespace import ( "context" "errors" - "net/http" "strings" ) @@ -27,10 +26,6 @@ var ( } ) -var AdjustRequest = func(r *http.Request) (*http.Request, int) { - return r.WithContext(ContextWithNamespace(r.Context(), RootNamespace)), 0 -} - func (n *Namespace) HasParent(possibleParent *Namespace) bool { switch { case n.Path == "": @@ -105,3 +100,15 @@ func Canonicalize(nsPath string) string { return nsPath } + +func SplitIDFromString(input string) (string, string) { + idx := strings.LastIndex(input, ".") + if idx == -1 { + return input, "" + } + if idx == len(input)-1 { + return input, "" + } + + return input[:idx], input[idx+1:] +} diff --git a/helper/testhelpers/testhelpers.go b/helper/testhelpers/testhelpers.go index 08428ec39e..929970cd41 100644 --- a/helper/testhelpers/testhelpers.go +++ b/helper/testhelpers/testhelpers.go @@ -7,7 +7,7 @@ import ( "math/rand" "time" - uuid "github.com/hashicorp/go-uuid" + "github.com/hashicorp/vault/helper/consts" "github.com/hashicorp/vault/helper/xor" "github.com/hashicorp/vault/vault" "github.com/mitchellh/go-testing-interface" @@ -23,16 +23,6 @@ func GenerateRoot(t testing.T, cluster *vault.TestCluster, drToken bool) string } func GenerateRootWithError(t testing.T, cluster *vault.TestCluster, drToken bool) (string, error) { - buf := make([]byte, 16) - readLen, err := rand.Read(buf) - if err != nil { - return "", err - } - if readLen != 16 { - return "", fmt.Errorf("wrong readlen: %d", readLen) - } - otp := base64.StdEncoding.EncodeToString(buf) - // If recovery keys supported, use those to perform root token generation instead var keys [][]byte if cluster.Cores[0].SealAccess().RecoveryKeySupported() { @@ -46,7 +36,7 @@ func GenerateRootWithError(t testing.T, cluster *vault.TestCluster, drToken bool if drToken { f = client.Sys().GenerateDROperationTokenInit } - status, err := f(otp, "") + status, err := f("", "") if err != nil { return "", err } @@ -55,6 +45,8 @@ func GenerateRootWithError(t testing.T, cluster *vault.TestCluster, drToken bool return "", fmt.Errorf("need more keys than have, need %d have %d", status.Required, len(keys)) } + otp := status.OTP + for i, key := range keys { if i >= status.Required { break @@ -71,15 +63,16 @@ func GenerateRootWithError(t testing.T, cluster *vault.TestCluster, drToken bool if !status.Complete { return "", errors.New("generate root operation did not end successfully") } - tokenBytes, err := xor.XORBase64(status.EncodedToken, otp) + + tokenBytes, err := base64.RawStdEncoding.DecodeString(status.EncodedToken) if err != nil { return "", err } - token, err := uuid.FormatUUID(tokenBytes) + tokenBytes, err = xor.XORBytes(tokenBytes, []byte(otp)) if err != nil { return "", err } - return token, nil + return string(tokenBytes), nil } // RandomWithPrefix is used to generate a unique name with a prefix, for @@ -87,3 +80,185 @@ func GenerateRootWithError(t testing.T, cluster *vault.TestCluster, drToken bool func RandomWithPrefix(name string) string { return fmt.Sprintf("%s-%d", name, rand.New(rand.NewSource(time.Now().UnixNano())).Int()) } + +func EnsureCoresUnsealed(t testing.T, c *vault.TestCluster) { + t.Helper() + for _, core := range c.Cores { + if !core.Sealed() { + continue + } + + client := core.Client + client.Sys().ResetUnsealProcess() + for j := 0; j < len(c.BarrierKeys); j++ { + statusResp, err := client.Sys().Unseal(base64.StdEncoding.EncodeToString(c.BarrierKeys[j])) + if err != nil { + // Sometimes when we get here it's already unsealed on its own + // and then this fails for DR secondaries so check again + if core.Sealed() { + t.Fatal(err) + } + break + } + if statusResp == nil { + t.Fatal("nil status response during unseal") + } + if !statusResp.Sealed { + break + } + } + if core.Sealed() { + t.Fatal("core is still sealed") + } + } +} + +func WaitForReplicationState(t testing.T, c *vault.Core, state consts.ReplicationState) { + timeout := time.Now().Add(10 * time.Second) + for { + if time.Now().After(timeout) { + t.Fatalf("timeout waiting for core to have state %d", uint32(state)) + } + state := c.ReplicationState() + if state.HasState(state) { + break + } + time.Sleep(1 * time.Second) + } +} + +func SetupFourClusterReplication(t testing.T, perfPrimary, perfSecondary, perfDRSecondary, perfSecondaryDRSecondary *vault.TestCluster) { + // Enable dr primary + _, err := perfPrimary.Cores[0].Client.Logical().Write("sys/replication/dr/primary/enable", nil) + if err != nil { + t.Fatal(err) + } + + WaitForReplicationState(t, perfPrimary.Cores[0].Core, consts.ReplicationDRPrimary) + + // Enable performance primary + _, err = perfPrimary.Cores[0].Client.Logical().Write("sys/replication/primary/enable", nil) + if err != nil { + t.Fatal(err) + } + + WaitForReplicationState(t, perfPrimary.Cores[0].Core, consts.ReplicationPerformancePrimary) + + // get dr token + secret, err := perfPrimary.Cores[0].Client.Logical().Write("sys/replication/dr/primary/secondary-token", map[string]interface{}{ + "id": "1", + }) + if err != nil { + t.Fatal(err) + } + token := secret.WrapInfo.Token + + // enable dr secondary + secret, err = perfDRSecondary.Cores[0].Client.Logical().Write("sys/replication/dr/secondary/enable", map[string]interface{}{ + "token": token, + "ca_file": perfPrimary.CACertPEMFile, + }) + if err != nil { + t.Fatal(err) + } + + WaitForReplicationState(t, perfDRSecondary.Cores[0].Core, consts.ReplicationDRSecondary) + perfDRSecondary.BarrierKeys = perfPrimary.BarrierKeys + EnsureCoresUnsealed(t, perfDRSecondary) + + // get performance token + secret, err = perfPrimary.Cores[0].Client.Logical().Write("sys/replication/primary/secondary-token", map[string]interface{}{ + "id": "1", + }) + if err != nil { + t.Fatal(err) + } + + token = secret.WrapInfo.Token + + // enable performace secondary + secret, err = perfSecondary.Cores[0].Client.Logical().Write("sys/replication/secondary/enable", map[string]interface{}{ + "token": token, + "ca_file": perfPrimary.CACertPEMFile, + }) + if err != nil { + t.Fatal(err) + } + + WaitForReplicationState(t, perfSecondary.Cores[0].Core, consts.ReplicationPerformanceSecondary) + time.Sleep(time.Second * 3) + perfSecondary.BarrierKeys = perfPrimary.BarrierKeys + + EnsureCoresUnsealed(t, perfSecondary) + rootToken := GenerateRoot(t, perfSecondary, false) + perfSecondary.Cores[0].Client.SetToken(rootToken) + + // Enable dr primary on perf secondary + _, err = perfSecondary.Cores[0].Client.Logical().Write("sys/replication/dr/primary/enable", nil) + if err != nil { + t.Fatal(err) + } + + WaitForReplicationState(t, perfSecondary.Cores[0].Core, consts.ReplicationDRPrimary) + + // get dr token from perf secondary + secret, err = perfSecondary.Cores[0].Client.Logical().Write("sys/replication/dr/primary/secondary-token", map[string]interface{}{ + "id": "1", + }) + if err != nil { + t.Fatal(err) + } + + token = secret.WrapInfo.Token + + // enable dr secondary + secret, err = perfSecondaryDRSecondary.Cores[0].Client.Logical().Write("sys/replication/dr/secondary/enable", map[string]interface{}{ + "token": token, + "ca_file": perfSecondary.CACertPEMFile, + }) + if err != nil { + t.Fatal(err) + } + + WaitForReplicationState(t, perfSecondaryDRSecondary.Cores[0].Core, consts.ReplicationDRSecondary) + perfSecondaryDRSecondary.BarrierKeys = perfPrimary.BarrierKeys + EnsureCoresUnsealed(t, perfSecondaryDRSecondary) + + perfDRSecondary.Cores[0].Client.SetToken(perfPrimary.Cores[0].Client.Token()) + perfSecondaryDRSecondary.Cores[0].Client.SetToken(rootToken) +} + +func DeriveActiveCore(t testing.T, cluster *vault.TestCluster) *vault.TestClusterCore { + for i := 0; i < 10; i++ { + for _, core := range cluster.Cores { + leaderResp, err := core.Client.Sys().Leader() + if err != nil { + t.Fatal(err) + } + if leaderResp.IsSelf { + return core + } + } + time.Sleep(1 * time.Second) + } + t.Fatal("could not derive the active core") + return nil +} + +func WaitForNCoresSealed(t testing.T, cluster *vault.TestCluster, n int) { + for i := 0; i < 10; i++ { + sealed := 0 + for _, core := range cluster.Cores { + if core.Core.Sealed() { + sealed++ + } + } + + if sealed >= n { + return + } + time.Sleep(time.Second) + } + + t.Fatalf("%d cores were not sealed", n) +} diff --git a/http/auth_token_test.go b/http/auth_token_test.go index 6cc1850fe3..5faba6112e 100644 --- a/http/auth_token_test.go +++ b/http/auth_token_test.go @@ -165,7 +165,7 @@ func TestAuthTokenRenew(t *testing.T) { if err == nil { t.Fatal("should not be allowed to renew root token") } - if !strings.Contains(err.Error(), "lease is not renewable") { + if !strings.Contains(err.Error(), "invalid lease ID") { t.Fatalf("wrong error; got %v", err) } diff --git a/http/forwarding_bench_test.go b/http/forwarding_bench_test.go index 34a4f2ca10..2028fa185c 100644 --- a/http/forwarding_bench_test.go +++ b/http/forwarding_bench_test.go @@ -11,6 +11,7 @@ import ( log "github.com/hashicorp/go-hclog" "github.com/hashicorp/vault/builtin/logical/transit" + "github.com/hashicorp/vault/helper/consts" "github.com/hashicorp/vault/helper/forwarding" "github.com/hashicorp/vault/helper/logging" "github.com/hashicorp/vault/logical" @@ -58,7 +59,7 @@ func BenchmarkHTTP_Forwarding_Stress(b *testing.B) { if err != nil { b.Fatal(err) } - req.Header.Set(AuthHeaderName, cluster.RootToken) + req.Header.Set(consts.AuthHeaderName, cluster.RootToken) _, err = client.Do(req) if err != nil { b.Fatal(err) @@ -71,7 +72,7 @@ func BenchmarkHTTP_Forwarding_Stress(b *testing.B) { if err != nil { b.Fatal(err) } - req.Header.Set(AuthHeaderName, cluster.RootToken) + req.Header.Set(consts.AuthHeaderName, cluster.RootToken) w := forwarding.NewRPCResponseWriter() handler.ServeHTTP(w, req) switch w.StatusCode() { diff --git a/http/forwarding_test.go b/http/forwarding_test.go index 13db9f7916..8dc221b2fa 100644 --- a/http/forwarding_test.go +++ b/http/forwarding_test.go @@ -20,6 +20,7 @@ import ( "github.com/hashicorp/vault/api" credCert "github.com/hashicorp/vault/builtin/credential/cert" "github.com/hashicorp/vault/builtin/logical/transit" + "github.com/hashicorp/vault/helper/consts" "github.com/hashicorp/vault/helper/keysutil" "github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/vault" @@ -179,7 +180,7 @@ func testHTTP_Forwarding_Stress_Common(t *testing.T, parallel bool, num uint32) if err != nil { t.Fatal(err) } - req.Header.Set(AuthHeaderName, cluster.RootToken) + req.Header.Set(consts.AuthHeaderName, cluster.RootToken) _, err = client.Do(req) if err != nil { t.Fatal(err) @@ -232,7 +233,7 @@ func testHTTP_Forwarding_Stress_Common(t *testing.T, parallel bool, num uint32) if err != nil { return nil, err } - req.Header.Set(AuthHeaderName, cluster.RootToken) + req.Header.Set(consts.AuthHeaderName, cluster.RootToken) resp, err := client.Do(req) if err != nil { return nil, err @@ -472,7 +473,7 @@ func TestHTTP_Forwarding_ClientTLS(t *testing.T) { if err != nil { t.Fatal(err) } - req.Header.Set(AuthHeaderName, cluster.RootToken) + req.Header.Set(consts.AuthHeaderName, cluster.RootToken) _, err = client.Do(req) if err != nil { t.Fatal(err) @@ -494,7 +495,7 @@ func TestHTTP_Forwarding_ClientTLS(t *testing.T) { if err != nil { t.Fatal(err) } - req.Header.Set(AuthHeaderName, cluster.RootToken) + req.Header.Set(consts.AuthHeaderName, cluster.RootToken) _, err = client.Do(req) if err != nil { t.Fatal(err) diff --git a/http/handler.go b/http/handler.go index fd381c896b..b8d4b84015 100644 --- a/http/handler.go +++ b/http/handler.go @@ -21,15 +21,14 @@ import ( sockaddr "github.com/hashicorp/go-sockaddr" "github.com/hashicorp/vault/helper/consts" "github.com/hashicorp/vault/helper/jsonutil" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/helper/parseutil" + "github.com/hashicorp/vault/helper/pathmanager" "github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/vault" ) const ( - // AuthHeaderName is the name of the header containing the token. - AuthHeaderName = "X-Vault-Token" - // WrapTTLHeaderName is the name of the header containing a directive to // wrap the response WrapTTLHeaderName = "X-Vault-Wrap-TTL" @@ -62,11 +61,13 @@ const ( ) var ( - ReplicationStaleReadTimeout = 2 * time.Second - // Set to false by stub_asset if the ui build tag isn't enabled uiBuiltIn = true + // perfStandbyAlwaysForwardPaths is used to check a requested path against + // the always forward list + perfStandbyAlwaysForwardPaths = pathmanager.New() + injectDataIntoTopRoutes = []string{ "/v1/sys/audit", "/v1/sys/audit/", @@ -114,14 +115,11 @@ func Handler(props *vault.HandlerProperties) http.Handler { mux.Handle("/v1/sys/rekey-recovery-key/init", handleRequestForwarding(core, handleSysRekeyInit(core, true))) mux.Handle("/v1/sys/rekey-recovery-key/update", handleRequestForwarding(core, handleSysRekeyUpdate(core, true))) mux.Handle("/v1/sys/rekey-recovery-key/verify", handleRequestForwarding(core, handleSysRekeyVerify(core, true))) - mux.Handle("/v1/sys/wrapping/lookup", handleRequestForwarding(core, handleLogical(core, wrappingVerificationFunc))) - mux.Handle("/v1/sys/wrapping/rewrap", handleRequestForwarding(core, handleLogical(core, wrappingVerificationFunc))) - mux.Handle("/v1/sys/wrapping/unwrap", handleRequestForwarding(core, handleLogical(core, wrappingVerificationFunc))) for _, path := range injectDataIntoTopRoutes { - mux.Handle(path, handleRequestForwarding(core, handleLogicalWithInjector(core, nil))) + mux.Handle(path, handleRequestForwarding(core, handleLogicalWithInjector(core))) } - mux.Handle("/v1/sys/", handleRequestForwarding(core, handleLogical(core, nil))) - mux.Handle("/v1/", handleRequestForwarding(core, handleLogical(core, nil))) + mux.Handle("/v1/sys/", handleRequestForwarding(core, handleLogical(core))) + mux.Handle("/v1/", handleRequestForwarding(core, handleLogical(core))) if core.UIEnabled() == true { if uiBuiltIn { mux.Handle("/ui/", http.StripPrefix("/ui/", gziphandler.GzipHandler(handleUIHeaders(core, handleUI(http.FileServer(&UIAssetWrapper{FileSystem: assetFS()})))))) @@ -131,13 +129,13 @@ func Handler(props *vault.HandlerProperties) http.Handler { mux.Handle("/", handleRootRedirect()) } + additionalRoutes(mux, core) + // Wrap the handler in another handler to trigger all help paths. helpWrappedHandler := wrapHelpHandler(mux, core) corsWrappedHandler := wrapCORSHandler(helpWrappedHandler, core) - // Wrap the help wrapped handler with another layer with a generic - // handler - genericWrappedHandler := wrapGenericHandler(corsWrappedHandler, props.MaxRequestSize, props.MaxRequestDuration) + genericWrappedHandler := genericWrapping(core, corsWrappedHandler, props) // Wrap the handler with PrintablePathCheckHandler to check for non-printable // characters in the request path. @@ -152,7 +150,7 @@ func Handler(props *vault.HandlerProperties) http.Handler { // wrapGenericHandler wraps the handler with an extra layer of handler where // tasks that should be commonly handled for all the requests and/or responses // are performed. -func wrapGenericHandler(h http.Handler, maxRequestSize int64, maxRequestDuration time.Duration) http.Handler { +func wrapGenericHandler(core *vault.Core, h http.Handler, maxRequestSize int64, maxRequestDuration time.Duration) http.Handler { if maxRequestDuration == 0 { maxRequestDuration = vault.DefaultMaxRequestDuration } @@ -170,7 +168,26 @@ func wrapGenericHandler(h http.Handler, maxRequestSize int64, maxRequestDuration if maxRequestSize > 0 { ctx = context.WithValue(ctx, "max_request_size", maxRequestSize) } + ctx = context.WithValue(ctx, "original_request_path", r.URL.Path) r = r.WithContext(ctx) + + switch { + case strings.HasPrefix(r.URL.Path, "/v1/"): + newR, status := adjustRequest(core, r) + if status != 0 { + respondError(w, status, nil) + cancelFunc() + return + } + r = newR + + case strings.HasPrefix(r.URL.Path, "/ui/"), r.URL.Path == "/": + default: + respondError(w, http.StatusNotFound, nil) + cancelFunc() + return + } + h.ServeHTTP(w, r) cancelFunc() return @@ -268,12 +285,12 @@ func WrapForwardedForHandler(h http.Handler, authorizedAddrs []*sockaddr.SockAdd // A lookup on a token that is about to expire returns nil, which means by the // time we can validate a wrapping token lookup will return nil since it will // be revoked after the call. So we have to do the validation here. -func wrappingVerificationFunc(core *vault.Core, req *logical.Request) error { +func wrappingVerificationFunc(ctx context.Context, core *vault.Core, req *logical.Request) error { if req == nil { return fmt.Errorf("invalid request") } - valid, err := core.ValidateWrappingToken(req) + valid, err := core.ValidateWrappingToken(ctx, req) if err != nil { return errwrap.Wrapf("error validating wrapping token: {{err}}", err) } @@ -396,16 +413,18 @@ func parseRequest(r *http.Request, w http.ResponseWriter, out interface{}) error // falling back on the older behavior of redirecting the client func handleRequestForwarding(core *vault.Core, handler http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.Header.Get(vault.IntNoForwardingHeaderName) != "" { - handler.ServeHTTP(w, r) - return - } - - if r.Header.Get(NoRequestForwardingHeaderName) != "" { - // Forwarding explicitly disabled, fall back to previous behavior - core.Logger().Debug("handleRequestForwarding: forwarding disabled by client request") - handler.ServeHTTP(w, r) - return + // If we are a performance standby we can handle the request. + if core.PerfStandby() { + ns, err := namespace.FromContext(r.Context()) + if err != nil { + respondError(w, http.StatusBadRequest, err) + return + } + path := ns.TrimmedPath(r.URL.Path[len("/v1/"):]) + if !perfStandbyAlwaysForwardPaths.HasPath(path) { + handler.ServeHTTP(w, r) + return + } } // Note: in an HA setup, this call will also ensure that connections to @@ -432,38 +451,60 @@ func handleRequestForwarding(core *vault.Core, handler http.Handler) http.Handle return } - // Attempt forwarding the request. If we cannot forward -- perhaps it's - // been disabled on the active node -- this will return with an - // ErrCannotForward and we simply fall back - statusCode, header, retBytes, err := core.ForwardRequest(r) - if err != nil { - if err == vault.ErrCannotForward { - core.Logger().Debug("handleRequestForwarding: cannot forward (possibly disabled on active node), falling back") - } else { - core.Logger().Error("handleRequestForwarding: error forwarding request", "error", err) - } - - // Fall back to redirection - handler.ServeHTTP(w, r) - return - } - - if header != nil { - for k, v := range header { - w.Header()[k] = v - } - } - - w.WriteHeader(statusCode) - w.Write(retBytes) + forwardRequest(core, w, r) return }) } +func forwardRequest(core *vault.Core, w http.ResponseWriter, r *http.Request) { + if r.Header.Get(vault.IntNoForwardingHeaderName) != "" { + respondStandby(core, w, r.URL) + return + } + + if r.Header.Get(NoRequestForwardingHeaderName) != "" { + // Forwarding explicitly disabled, fall back to previous behavior + core.Logger().Debug("handleRequestForwarding: forwarding disabled by client request") + respondStandby(core, w, r.URL) + return + } + + // Attempt forwarding the request. If we cannot forward -- perhaps it's + // been disabled on the active node -- this will return with an + // ErrCannotForward and we simply fall back + statusCode, header, retBytes, err := core.ForwardRequest(r) + if err != nil { + if err == vault.ErrCannotForward { + core.Logger().Debug("handleRequestForwarding: cannot forward (possibly disabled on active node), falling back") + } else { + core.Logger().Error("handleRequestForwarding: error forwarding request", "error", err) + } + + // Fall back to redirection + respondStandby(core, w, r.URL) + return + } + + if header != nil { + for k, v := range header { + w.Header()[k] = v + } + } + + w.WriteHeader(statusCode) + w.Write(retBytes) +} + // request is a helper to perform a request and properly exit in the // case of an error. func request(core *vault.Core, w http.ResponseWriter, rawReq *http.Request, r *logical.Request) (*logical.Response, bool) { resp, err := core.HandleRequest(rawReq.Context(), r) + if r.LastRemoteWAL() > 0 && !vault.WaitUntilWALShipped(rawReq.Context(), core, r.LastRemoteWAL()) { + if resp == nil { + resp = &logical.Response{} + } + resp.AddWarning("Timeout hit while waiting for local replicated cluster to apply primary's write; this client may encounter stale reads of values written during this operation.") + } if errwrap.Contains(err, consts.ErrStandby.Error()) { respondStandby(core, w, rawReq.URL) return resp, false @@ -526,16 +567,19 @@ func respondStandby(core *vault.Core, w http.ResponseWriter, reqURL *url.URL) { } // requestAuth adds the token to the logical.Request if it exists. -func requestAuth(core *vault.Core, r *http.Request, req *logical.Request) *logical.Request { +func requestAuth(core *vault.Core, r *http.Request, req *logical.Request) (*logical.Request, error) { // Attach the header value if we have it - if v := r.Header.Get(AuthHeaderName); v != "" { + if v := r.Header.Get(consts.AuthHeaderName); v != "" { req.ClientToken = v // Also attach the accessor if we have it. This doesn't fail if it // doesn't exist because the request may be to an unauthenticated // endpoint/login endpoint where a bad current token doesn't matter, or // a token from a Vault version pre-accessors. - te, err := core.LookupToken(v) + te, err := core.LookupToken(r.Context(), v) + if err != nil && strings.Count(v, ".") != 2 { + return req, err + } if err == nil && te != nil { req.ClientTokenAccessor = te.Accessor req.ClientTokenRemainingUses = te.NumUses @@ -543,7 +587,22 @@ func requestAuth(core *vault.Core, r *http.Request, req *logical.Request) *logic } } - return req + return req, nil +} + +func requestPolicyOverride(r *http.Request, req *logical.Request) error { + raw := r.Header.Get(PolicyOverrideHeaderName) + if raw == "" { + return nil + } + + override, err := parseutil.ParseBool(raw) + if err != nil { + return err + } + + req.PolicyOverride = override + return nil } // requestWrapInfo adds the WrapInfo value to the logical.Request if wrap info exists @@ -576,6 +635,52 @@ func requestWrapInfo(r *http.Request, req *logical.Request) (*logical.Request, e return req, nil } +// parseMFAHeader parses the MFAHeaderName in the request headers and organizes +// them with MFA method name as the index. +func parseMFAHeader(req *logical.Request) error { + if req == nil { + return fmt.Errorf("request is nil") + } + + if req.Headers == nil { + return nil + } + + // Reset and initialize the credentials in the request + req.MFACreds = make(map[string][]string) + + for _, mfaHeaderValue := range req.Headers[canonicalMFAHeaderName] { + // Skip the header with no value in it + if mfaHeaderValue == "" { + continue + } + + // Handle the case where only method name is mentioned and no value + // is supplied + if !strings.Contains(mfaHeaderValue, ":") { + // Mark the presense of method name, but set an empty set to it + // indicating that there were no values supplied for the method + if req.MFACreds[mfaHeaderValue] == nil { + req.MFACreds[mfaHeaderValue] = []string{} + } + continue + } + + shardSplits := strings.SplitN(mfaHeaderValue, ":", 2) + if shardSplits[0] == "" { + return fmt.Errorf("invalid data in header %q; missing method name", MFAHeaderName) + } + + if shardSplits[1] == "" { + return fmt.Errorf("invalid data in header %q; missing method value", MFAHeaderName) + } + + req.MFACreds[shardSplits[0]] = append(req.MFACreds[shardSplits[0]], shardSplits[1]) + } + + return nil +} + func respondError(w http.ResponseWriter, status int, err error) { logical.AdjustErrorStatusCode(&status, err) diff --git a/http/handler_test.go b/http/handler_test.go index c474c5c1ed..b626fed28b 100644 --- a/http/handler_test.go +++ b/http/handler_test.go @@ -6,6 +6,7 @@ import ( "errors" "net/http" "net/http/httptest" + "net/textproto" "reflect" "strings" "testing" @@ -16,6 +17,93 @@ import ( "github.com/hashicorp/vault/vault" ) +func TestHandler_parseMFAHandler(t *testing.T) { + var err error + var expectedMFACreds logical.MFACreds + req := &logical.Request{ + Headers: make(map[string][]string), + } + + headerName := textproto.CanonicalMIMEHeaderKey(MFAHeaderName) + + // Set TOTP passcode in the MFA header + req.Headers[headerName] = []string{ + "my_totp:123456", + "my_totp:111111", + "my_second_mfa:hi=hello", + "my_third_mfa", + } + err = parseMFAHeader(req) + if err != nil { + t.Fatal(err) + } + + // Verify that it is being parsed properly + expectedMFACreds = logical.MFACreds{ + "my_totp": []string{ + "123456", + "111111", + }, + "my_second_mfa": []string{ + "hi=hello", + }, + "my_third_mfa": []string{}, + } + if !reflect.DeepEqual(expectedMFACreds, req.MFACreds) { + t.Fatalf("bad: parsed MFACreds; expected: %#v\n actual: %#v\n", expectedMFACreds, req.MFACreds) + } + + // Split the creds of a method type in different headers and check if they + // all get merged together + req.Headers[headerName] = []string{ + "my_mfa:passcode=123456", + "my_mfa:month=july", + "my_mfa:day=tuesday", + } + err = parseMFAHeader(req) + if err != nil { + t.Fatal(err) + } + + expectedMFACreds = logical.MFACreds{ + "my_mfa": []string{ + "passcode=123456", + "month=july", + "day=tuesday", + }, + } + if !reflect.DeepEqual(expectedMFACreds, req.MFACreds) { + t.Fatalf("bad: parsed MFACreds; expected: %#v\n actual: %#v\n", expectedMFACreds, req.MFACreds) + } + + // Header without method name should error out + req.Headers[headerName] = []string{ + ":passcode=123456", + } + err = parseMFAHeader(req) + if err == nil { + t.Fatalf("expected an error; actual: %#v\n", req.MFACreds) + } + + // Header without method name and method value should error out + req.Headers[headerName] = []string{ + ":", + } + err = parseMFAHeader(req) + if err == nil { + t.Fatalf("expected an error; actual: %#v\n", req.MFACreds) + } + + // Header without method name and method value should error out + req.Headers[headerName] = []string{ + "my_totp:", + } + err = parseMFAHeader(req) + if err == nil { + t.Fatalf("expected an error; actual: %#v\n", req.MFACreds) + } +} + func TestHandler_cors(t *testing.T) { core, _, _ := vault.TestCoreUnsealed(t) ln, addr := TestServer(t, core) @@ -106,7 +194,7 @@ func TestHandler_CacheControlNoStore(t *testing.T) { if err != nil { t.Fatalf("err: %s", err) } - req.Header.Set(AuthHeaderName, token) + req.Header.Set(consts.AuthHeaderName, token) req.Header.Set(WrapTTLHeaderName, "60s") client := cleanhttp.DefaultClient() @@ -139,7 +227,7 @@ func TestHandler_Accepted(t *testing.T) { if err != nil { t.Fatalf("err: %s", err) } - req.Header.Set(AuthHeaderName, token) + req.Header.Set(consts.AuthHeaderName, token) client := cleanhttp.DefaultClient() resp, err := client.Do(req) @@ -160,7 +248,7 @@ func TestSysMounts_headerAuth(t *testing.T) { if err != nil { t.Fatalf("err: %s", err) } - req.Header.Set(AuthHeaderName, token) + req.Header.Set(consts.AuthHeaderName, token) client := cleanhttp.DefaultClient() resp, err := client.Do(req) @@ -310,7 +398,7 @@ func TestSysMounts_headerAuth_Wrapped(t *testing.T) { if err != nil { t.Fatalf("err: %s", err) } - req.Header.Set(AuthHeaderName, token) + req.Header.Set(consts.AuthHeaderName, token) req.Header.Set(WrapTTLHeaderName, "60s") client := cleanhttp.DefaultClient() @@ -379,6 +467,30 @@ func TestHandler_sealed(t *testing.T) { testResponseStatus(t, resp, 503) } +func TestHandler_ui_default(t *testing.T) { + core := vault.TestCoreUI(t, false) + ln, addr := TestServer(t, core) + defer ln.Close() + + resp, err := http.Get(addr + "/ui/") + if err != nil { + t.Fatalf("err: %s", err) + } + testResponseStatus(t, resp, 404) +} + +func TestHandler_ui_enabled(t *testing.T) { + core := vault.TestCoreUI(t, true) + ln, addr := TestServer(t, core) + defer ln.Close() + + resp, err := http.Get(addr + "/ui/") + if err != nil { + t.Fatalf("err: %s", err) + } + testResponseStatus(t, resp, 200) +} + func TestHandler_error(t *testing.T) { w := httptest.NewRecorder() @@ -429,7 +541,7 @@ func testNonPrintable(t *testing.T, disable bool) { if err != nil { t.Fatalf("err: %s", err) } - req.Header.Set(AuthHeaderName, token) + req.Header.Set(consts.AuthHeaderName, token) client := cleanhttp.DefaultClient() resp, err := client.Do(req) diff --git a/http/help.go b/http/help.go index 597fbb0975..b724393196 100644 --- a/http/help.go +++ b/http/help.go @@ -3,6 +3,8 @@ package http import ( "net/http" + "github.com/hashicorp/errwrap" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/vault" ) @@ -24,22 +26,31 @@ func wrapHelpHandler(h http.Handler, core *vault.Core) http.Handler { }) } -func handleHelp(core *vault.Core, w http.ResponseWriter, req *http.Request) { - path, ok := stripPrefix("/v1/", req.URL.Path) - if !ok { - respondError(w, http.StatusNotFound, nil) +func handleHelp(core *vault.Core, w http.ResponseWriter, r *http.Request) { + ns, err := namespace.FromContext(r.Context()) + if err != nil { + respondError(w, http.StatusBadRequest, nil) + return + } + path := ns.TrimmedPath(r.URL.Path[len("/v1/"):]) + + req, err := requestAuth(core, r, &logical.Request{ + Operation: logical.HelpOperation, + Path: path, + Connection: getConnection(r), + }) + if err != nil { + if errwrap.Contains(err, logical.ErrPermissionDenied.Error()) { + respondError(w, http.StatusForbidden, nil) + return + } + respondError(w, http.StatusBadRequest, errwrap.Wrapf("error performing token check: {{err}}", err)) return } - lreq := requestAuth(core, req, &logical.Request{ - Operation: logical.HelpOperation, - Path: path, - Connection: getConnection(req), - }) - - resp, err := core.HandleRequest(req.Context(), lreq) + resp, err := core.HandleRequest(r.Context(), req) if err != nil { - respondErrorCommon(w, lreq, resp, err) + respondErrorCommon(w, req, resp, err) return } diff --git a/http/logical.go b/http/logical.go index e515e6deec..a211d6482a 100644 --- a/http/logical.go +++ b/http/logical.go @@ -3,6 +3,7 @@ package http import ( "encoding/base64" "encoding/json" + "fmt" "io" "net" "net/http" @@ -12,21 +13,17 @@ import ( "github.com/hashicorp/errwrap" "github.com/hashicorp/go-uuid" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/vault" ) -type PrepareRequestFunc func(*vault.Core, *logical.Request) error - func buildLogicalRequest(core *vault.Core, w http.ResponseWriter, r *http.Request) (*logical.Request, int, error) { - // Determine the path... - if !strings.HasPrefix(r.URL.Path, "/v1/") { - return nil, http.StatusNotFound, nil - } - path := r.URL.Path[len("/v1/"):] - if path == "" { - return nil, http.StatusNotFound, nil + ns, err := namespace.FromContext(r.Context()) + if err != nil { + return nil, http.StatusBadRequest, nil } + path := ns.TrimmedPath(r.URL.Path[len("/v1/"):]) var data map[string]interface{} @@ -103,13 +100,12 @@ func buildLogicalRequest(core *vault.Core, w http.ResponseWriter, r *http.Reques return nil, http.StatusMethodNotAllowed, nil } - var err error request_id, err := uuid.GenerateUUID() if err != nil { return nil, http.StatusBadRequest, errwrap.Wrapf("failed to generate identifier for the request: {{err}}", err) } - req := requestAuth(core, r, &logical.Request{ + req, err := requestAuth(core, r, &logical.Request{ ID: request_id, Operation: op, Path: path, @@ -117,24 +113,40 @@ func buildLogicalRequest(core *vault.Core, w http.ResponseWriter, r *http.Reques Connection: getConnection(r), Headers: r.Header, }) + if err != nil { + if errwrap.Contains(err, logical.ErrPermissionDenied.Error()) { + return nil, http.StatusForbidden, nil + } + return nil, http.StatusBadRequest, errwrap.Wrapf("error performing token check: {{err}}", err) + } req, err = requestWrapInfo(r, req) if err != nil { return nil, http.StatusBadRequest, errwrap.Wrapf("error parsing X-Vault-Wrap-TTL header: {{err}}", err) } + err = parseMFAHeader(req) + if err != nil { + return nil, http.StatusBadRequest, errwrap.Wrapf("failed to parse X-Vault-MFA header: {{err}}", err) + } + + err = requestPolicyOverride(r, req) + if err != nil { + return nil, http.StatusBadRequest, errwrap.Wrapf(fmt.Sprintf(`failed to parse %s header: {{err}}`, PolicyOverrideHeaderName), err) + } + return req, 0, nil } -func handleLogical(core *vault.Core, prepareRequestCallback PrepareRequestFunc) http.Handler { - return handleLogicalInternal(core, false, prepareRequestCallback) +func handleLogical(core *vault.Core) http.Handler { + return handleLogicalInternal(core, false) } -func handleLogicalWithInjector(core *vault.Core, prepareRequestCallback PrepareRequestFunc) http.Handler { - return handleLogicalInternal(core, true, prepareRequestCallback) +func handleLogicalWithInjector(core *vault.Core) http.Handler { + return handleLogicalInternal(core, true) } -func handleLogicalInternal(core *vault.Core, injectDataIntoTopLevel bool, prepareRequestCallback PrepareRequestFunc) http.Handler { +func handleLogicalInternal(core *vault.Core, injectDataIntoTopLevel bool) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { req, statusCode, err := buildLogicalRequest(core, w, r) if err != nil || statusCode != 0 { @@ -142,14 +154,53 @@ func handleLogicalInternal(core *vault.Core, injectDataIntoTopLevel bool, prepar return } - // Certain endpoints may require changes to the request object. They - // will have a callback registered to do the needed operations, so - // invoke it before proceeding. - if prepareRequestCallback != nil { - if err := prepareRequestCallback(core, req); err != nil { - respondError(w, http.StatusBadRequest, err) + // Always forward requests that are using a limited use count token + if core.PerfStandby() && req.ClientTokenRemainingUses > 0 { + forwardRequest(core, w, r) + return + } + + // req.Path will be relative by this point. The prefix check is first + // to fail faster if we're not in this situation since it's a hot path + switch { + case strings.HasPrefix(req.Path, "sys/wrapping/"), strings.HasPrefix(req.Path, "auth/token/"): + // Get the token ns info; if we match the paths below we want to + // swap in the token context (but keep the relative path) + if err != nil { + core.Logger().Warn("error looking up just-set context", "error", err) + respondError(w, http.StatusInternalServerError, err) return } + te := req.TokenEntry() + newCtx := r.Context() + if te != nil { + ns, err := vault.NamespaceByID(newCtx, te.NamespaceID, core) + if err != nil { + core.Logger().Warn("error looking up namespace from the token's namespace ID", "error", err) + respondError(w, http.StatusInternalServerError, err) + return + } + if ns != nil { + newCtx = namespace.ContextWithNamespace(newCtx, ns) + } + } + switch req.Path { + case "sys/wrapping/lookup", "sys/wrapping/rewrap", "sys/wrapping/unwrap": + r = r.WithContext(newCtx) + if err := wrappingVerificationFunc(r.Context(), core, req); err != nil { + if errwrap.Contains(err, logical.ErrPermissionDenied.Error()) { + respondError(w, http.StatusForbidden, err) + } else { + respondError(w, http.StatusBadRequest, err) + } + return + } + + // The -self paths have no meaning outside of the token NS, so + // requests for these paths always go to the token NS + case "auth/token/lookup-self", "auth/token/renew-self", "auth/token/revoke-self": + r = r.WithContext(newCtx) + } } // Make the internal request. We attach the connection info diff --git a/http/logical_test.go b/http/logical_test.go index 21a3c71072..971cec4c7c 100644 --- a/http/logical_test.go +++ b/http/logical_test.go @@ -15,7 +15,9 @@ import ( log "github.com/hashicorp/go-hclog" + "github.com/hashicorp/vault/helper/consts" "github.com/hashicorp/vault/helper/logging" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/physical" "github.com/hashicorp/vault/physical/inmem" @@ -267,8 +269,10 @@ func TestLogical_RequestSizeLimit(t *testing.T) { } func TestLogical_ListSuffix(t *testing.T) { - core, _, _ := vault.TestCoreUnsealed(t) + core, _, rootToken := vault.TestCoreUnsealed(t) req, _ := http.NewRequest("GET", "http://127.0.0.1:8200/v1/secret/foo", nil) + req = req.WithContext(namespace.RootContext(nil)) + req.Header.Add(consts.AuthHeaderName, rootToken) lreq, status, err := buildLogicalRequest(core, nil, req) if err != nil { t.Fatal(err) @@ -281,6 +285,8 @@ func TestLogical_ListSuffix(t *testing.T) { } req, _ = http.NewRequest("GET", "http://127.0.0.1:8200/v1/secret/foo?list=true", nil) + req = req.WithContext(namespace.RootContext(nil)) + req.Header.Add(consts.AuthHeaderName, rootToken) lreq, status, err = buildLogicalRequest(core, nil, req) if err != nil { t.Fatal(err) @@ -293,6 +299,8 @@ func TestLogical_ListSuffix(t *testing.T) { } req, _ = http.NewRequest("LIST", "http://127.0.0.1:8200/v1/secret/foo", nil) + req = req.WithContext(namespace.RootContext(nil)) + req.Header.Add(consts.AuthHeaderName, rootToken) lreq, status, err = buildLogicalRequest(core, nil, req) if err != nil { t.Fatal(err) diff --git a/http/plugin_test.go b/http/plugin_test.go index 5525eb483f..3fb55dd520 100644 --- a/http/plugin_test.go +++ b/http/plugin_test.go @@ -15,18 +15,24 @@ import ( "github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/logical/plugin" "github.com/hashicorp/vault/logical/plugin/mock" + "github.com/hashicorp/vault/physical" "github.com/hashicorp/vault/physical/inmem" "github.com/hashicorp/vault/vault" ) func getPluginClusterAndCore(t testing.TB, logger log.Logger) (*vault.TestCluster, *vault.TestClusterCore) { + inm, err := inmem.NewTransactionalInmem(nil, logger) + if err != nil { + t.Fatal(err) + } inmha, err := inmem.NewInmemHA(nil, logger) if err != nil { t.Fatal(err) } coreConfig := &vault.CoreConfig{ - Physical: inmha, + Physical: inm, + HAPhysical: inmha.(physical.HABackend), LogicalBackends: map[string]logical.Factory{ "plugin": bplugin.Factory, }, diff --git a/http/sys_generate_root.go b/http/sys_generate_root.go index 74456b943b..1d840d72be 100644 --- a/http/sys_generate_root.go +++ b/http/sys_generate_root.go @@ -7,6 +7,7 @@ import ( "fmt" "net/http" + "github.com/hashicorp/vault/helper/base62" "github.com/hashicorp/vault/vault" ) @@ -14,7 +15,7 @@ func handleSysGenerateRootAttempt(core *vault.Core, generateStrategy vault.Gener return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.Method { case "GET": - handleSysGenerateRootAttemptGet(core, w, r) + handleSysGenerateRootAttemptGet(core, w, r, "") case "POST", "PUT": handleSysGenerateRootAttemptPut(core, w, r, generateStrategy) case "DELETE": @@ -25,7 +26,7 @@ func handleSysGenerateRootAttempt(core *vault.Core, generateStrategy vault.Gener }) } -func handleSysGenerateRootAttemptGet(core *vault.Core, w http.ResponseWriter, r *http.Request) { +func handleSysGenerateRootAttemptGet(core *vault.Core, w http.ResponseWriter, r *http.Request, otp string) { ctx, cancel := core.GetContext() defer cancel() @@ -65,10 +66,12 @@ func handleSysGenerateRootAttemptGet(core *vault.Core, w http.ResponseWriter, r // Format the status status := &GenerateRootStatusResponse{ - Started: false, - Progress: progress, - Required: sealConfig.SecretThreshold, - Complete: false, + Started: false, + Progress: progress, + Required: sealConfig.SecretThreshold, + Complete: false, + OTPLength: vault.TokenLength, + OTP: otp, } if generationConfig != nil { status.Nonce = generationConfig.Nonce @@ -87,19 +90,32 @@ func handleSysGenerateRootAttemptPut(core *vault.Core, w http.ResponseWriter, r return } - if len(req.OTP) > 0 && len(req.PGPKey) > 0 { - respondError(w, http.StatusBadRequest, fmt.Errorf("only one of \"otp\" and \"pgp_key\" must be specified")) - return + var err error + var genned bool + + switch { + case len(req.PGPKey) > 0, len(req.OTP) > 0: + default: + genned = true + req.OTP, err = base62.Random(vault.TokenLength, true) + if err != nil { + respondError(w, http.StatusInternalServerError, err) + return + } } // Attemptialize the generation - err := core.GenerateRootInit(req.OTP, req.PGPKey, generateStrategy) - if err != nil { + if err := core.GenerateRootInit(req.OTP, req.PGPKey, generateStrategy); err != nil { respondError(w, http.StatusBadRequest, err) return } - handleSysGenerateRootAttemptGet(core, w, r) + if genned { + handleSysGenerateRootAttemptGet(core, w, r, req.OTP) + return + } + + handleSysGenerateRootAttemptGet(core, w, r, "") } func handleSysGenerateRootAttemptDelete(core *vault.Core, w http.ResponseWriter, r *http.Request) { @@ -184,6 +200,8 @@ type GenerateRootStatusResponse struct { EncodedToken string `json:"encoded_token"` EncodedRootToken string `json:"encoded_root_token"` PGPFingerprint string `json:"pgp_fingerprint"` + OTP string `json:"otp"` + OTPLength int `json:"otp_length"` } type GenerateRootUpdateRequest struct { diff --git a/http/sys_generate_root_test.go b/http/sys_generate_root_test.go index 73850f6626..1a7054b1e8 100644 --- a/http/sys_generate_root_test.go +++ b/http/sys_generate_root_test.go @@ -9,7 +9,6 @@ import ( "reflect" "testing" - "github.com/hashicorp/go-uuid" "github.com/hashicorp/vault/helper/pgpkeys" "github.com/hashicorp/vault/helper/xor" "github.com/hashicorp/vault/vault" @@ -36,6 +35,8 @@ func TestSysGenerateRootAttempt_Status(t *testing.T) { "encoded_root_token": "", "pgp_fingerprint": "", "nonce": "", + "otp": "", + "otp_length": json.Number("24"), } testResponseStatus(t, resp, 200) testResponseBody(t, resp, &actual) @@ -70,6 +71,8 @@ func TestSysGenerateRootAttempt_Setup_OTP(t *testing.T) { "encoded_token": "", "encoded_root_token": "", "pgp_fingerprint": "", + "otp": "", + "otp_length": json.Number("24"), } testResponseStatus(t, resp, 200) testResponseBody(t, resp, &actual) @@ -92,6 +95,8 @@ func TestSysGenerateRootAttempt_Setup_OTP(t *testing.T) { "encoded_token": "", "encoded_root_token": "", "pgp_fingerprint": "", + "otp": "", + "otp_length": json.Number("24"), } testResponseStatus(t, resp, 200) testResponseBody(t, resp, &actual) @@ -126,6 +131,8 @@ func TestSysGenerateRootAttempt_Setup_PGP(t *testing.T) { "encoded_token": "", "encoded_root_token": "", "pgp_fingerprint": "816938b8a29146fbe245dd29e7cbaf8e011db793", + "otp": "", + "otp_length": json.Number("24"), } testResponseStatus(t, resp, 200) testResponseBody(t, resp, &actual) @@ -163,6 +170,8 @@ func TestSysGenerateRootAttempt_Cancel(t *testing.T) { "encoded_token": "", "encoded_root_token": "", "pgp_fingerprint": "", + "otp": "", + "otp_length": json.Number("24"), } testResponseStatus(t, resp, 200) testResponseBody(t, resp, &actual) @@ -192,6 +201,8 @@ func TestSysGenerateRootAttempt_Cancel(t *testing.T) { "encoded_root_token": "", "pgp_fingerprint": "", "nonce": "", + "otp": "", + "otp_length": json.Number("24"), } testResponseStatus(t, resp, 200) testResponseBody(t, resp, &actual) @@ -251,18 +262,11 @@ func TestSysGenerateRoot_Update_OTP(t *testing.T) { defer ln.Close() TestServerAuth(t, addr, token) - otpBytes, err := vault.GenerateRandBytes(16) - if err != nil { - t.Fatal(err) - } - otp := base64.StdEncoding.EncodeToString(otpBytes) - - resp := testHttpPut(t, token, addr+"/v1/sys/generate-root/attempt", map[string]interface{}{ - "otp": otp, - }) + resp := testHttpPut(t, token, addr+"/v1/sys/generate-root/attempt", map[string]interface{}{}) var rootGenerationStatus map[string]interface{} testResponseStatus(t, resp, 200) testResponseBody(t, resp, &rootGenerationStatus) + otp := rootGenerationStatus["otp"].(string) var actual map[string]interface{} var expected map[string]interface{} @@ -280,6 +284,8 @@ func TestSysGenerateRoot_Update_OTP(t *testing.T) { "required": json.Number(fmt.Sprintf("%d", len(keys))), "started": true, "pgp_fingerprint": "", + "otp": "", + "otp_length": json.Number("0"), } if i+1 == len(keys) { expected["complete"] = true @@ -296,19 +302,22 @@ func TestSysGenerateRoot_Update_OTP(t *testing.T) { } expected["encoded_token"] = actual["encoded_token"] expected["encoded_root_token"] = actual["encoded_root_token"] + expected["encoded_token"] = actual["encoded_token"] if !reflect.DeepEqual(actual, expected) { t.Fatalf("\nexpected: %#v\nactual: %#v", expected, actual) } - decodedToken, err := xor.XORBase64(otp, actual["encoded_root_token"].(string)) + tokenBytes, err := base64.RawStdEncoding.DecodeString(expected["encoded_token"].(string)) if err != nil { t.Fatal(err) } - newRootToken, err := uuid.FormatUUID(decodedToken) + + tokenBytes, err = xor.XORBytes(tokenBytes, []byte(otp)) if err != nil { t.Fatal(err) } + newRootToken := string(tokenBytes) actual = map[string]interface{}{} expected = map[string]interface{}{ @@ -374,6 +383,8 @@ func TestSysGenerateRoot_Update_PGP(t *testing.T) { "required": json.Number(fmt.Sprintf("%d", len(keys))), "started": true, "pgp_fingerprint": "816938b8a29146fbe245dd29e7cbaf8e011db793", + "otp": "", + "otp_length": json.Number("0"), } if i+1 == len(keys) { expected["complete"] = true @@ -390,12 +401,13 @@ func TestSysGenerateRoot_Update_PGP(t *testing.T) { } expected["encoded_token"] = actual["encoded_token"] expected["encoded_root_token"] = actual["encoded_root_token"] + expected["encoded_token"] = actual["encoded_token"] if !reflect.DeepEqual(actual, expected) { t.Fatalf("\nexpected: %#v\nactual: %#v", expected, actual) } - decodedTokenBuf, err := pgpkeys.DecryptBytes(actual["encoded_root_token"].(string), pgpkeys.TestPrivKey1) + decodedTokenBuf, err := pgpkeys.DecryptBytes(actual["encoded_token"].(string), pgpkeys.TestPrivKey1) if err != nil { t.Fatal(err) } diff --git a/http/sys_rekey_test.go b/http/sys_rekey_test.go index 71971e4d8b..fd068ba48b 100644 --- a/http/sys_rekey_test.go +++ b/http/sys_rekey_test.go @@ -29,134 +29,140 @@ func TestSysRekey_Init_pgpKeysEntriesForRekey(t *testing.T) { } func TestSysRekey_Init_Status(t *testing.T) { - core, _, token := vault.TestCoreUnsealed(t) - ln, addr := TestServer(t, core) - defer ln.Close() - TestServerAuth(t, addr, token) + t.Run("status-barrier-default", func(t *testing.T) { + core, _, token := vault.TestCoreUnsealed(t) + ln, addr := TestServer(t, core) + defer ln.Close() + TestServerAuth(t, addr, token) - resp, err := http.Get(addr + "/v1/sys/rekey/init") - if err != nil { - t.Fatalf("err: %s", err) - } + resp, err := http.Get(addr + "/v1/sys/rekey/init") + if err != nil { + t.Fatalf("err: %s", err) + } - var actual map[string]interface{} - expected := map[string]interface{}{ - "started": false, - "t": json.Number("0"), - "n": json.Number("0"), - "progress": json.Number("0"), - "required": json.Number("3"), - "pgp_fingerprints": interface{}(nil), - "backup": false, - "nonce": "", - "verification_required": false, - } - testResponseStatus(t, resp, 200) - testResponseBody(t, resp, &actual) - if !reflect.DeepEqual(actual, expected) { - t.Fatalf("\nexpected: %#v\nactual: %#v", expected, actual) - } + var actual map[string]interface{} + expected := map[string]interface{}{ + "started": false, + "t": json.Number("0"), + "n": json.Number("0"), + "progress": json.Number("0"), + "required": json.Number("3"), + "pgp_fingerprints": interface{}(nil), + "backup": false, + "nonce": "", + "verification_required": false, + } + testResponseStatus(t, resp, 200) + testResponseBody(t, resp, &actual) + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("\nexpected: %#v\nactual: %#v", expected, actual) + } + }) } func TestSysRekey_Init_Setup(t *testing.T) { - core, _, token := vault.TestCoreUnsealed(t) - ln, addr := TestServer(t, core) - defer ln.Close() - TestServerAuth(t, addr, token) + t.Run("init-barrier-barrier-key", func(t *testing.T) { + core, _, token := vault.TestCoreUnsealed(t) + ln, addr := TestServer(t, core) + defer ln.Close() + TestServerAuth(t, addr, token) - // Start rekey - resp := testHttpPut(t, token, addr+"/v1/sys/rekey/init", map[string]interface{}{ - "secret_shares": 5, - "secret_threshold": 3, + // Start rekey + resp := testHttpPut(t, token, addr+"/v1/sys/rekey/init", map[string]interface{}{ + "secret_shares": 5, + "secret_threshold": 3, + }) + testResponseStatus(t, resp, 200) + + var actual map[string]interface{} + expected := map[string]interface{}{ + "started": true, + "t": json.Number("3"), + "n": json.Number("5"), + "progress": json.Number("0"), + "required": json.Number("3"), + "pgp_fingerprints": interface{}(nil), + "backup": false, + "verification_required": false, + } + testResponseStatus(t, resp, 200) + testResponseBody(t, resp, &actual) + if actual["nonce"].(string) == "" { + t.Fatalf("nonce was empty") + } + expected["nonce"] = actual["nonce"] + if diff := deep.Equal(actual, expected); diff != nil { + t.Fatal(diff) + } + + // Get rekey status + resp = testHttpGet(t, token, addr+"/v1/sys/rekey/init") + + actual = map[string]interface{}{} + expected = map[string]interface{}{ + "started": true, + "t": json.Number("3"), + "n": json.Number("5"), + "progress": json.Number("0"), + "required": json.Number("3"), + "pgp_fingerprints": interface{}(nil), + "backup": false, + "verification_required": false, + } + testResponseStatus(t, resp, 200) + testResponseBody(t, resp, &actual) + if actual["nonce"].(string) == "" { + t.Fatalf("nonce was empty") + } + if actual["nonce"].(string) == "" { + t.Fatalf("nonce was empty") + } + expected["nonce"] = actual["nonce"] + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("\nexpected: %#v\nactual: %#v", expected, actual) + } }) - testResponseStatus(t, resp, 200) - - var actual map[string]interface{} - expected := map[string]interface{}{ - "started": true, - "t": json.Number("3"), - "n": json.Number("5"), - "progress": json.Number("0"), - "required": json.Number("3"), - "pgp_fingerprints": interface{}(nil), - "backup": false, - "verification_required": false, - } - testResponseStatus(t, resp, 200) - testResponseBody(t, resp, &actual) - if actual["nonce"].(string) == "" { - t.Fatalf("nonce was empty") - } - expected["nonce"] = actual["nonce"] - if !reflect.DeepEqual(actual, expected) { - t.Fatalf("\nexpected: %#v\nactual: %#v", expected, actual) - } - - // Get rekey status - resp = testHttpGet(t, token, addr+"/v1/sys/rekey/init") - - actual = map[string]interface{}{} - expected = map[string]interface{}{ - "started": true, - "t": json.Number("3"), - "n": json.Number("5"), - "progress": json.Number("0"), - "required": json.Number("3"), - "pgp_fingerprints": interface{}(nil), - "backup": false, - "verification_required": false, - } - testResponseStatus(t, resp, 200) - testResponseBody(t, resp, &actual) - if actual["nonce"].(string) == "" { - t.Fatalf("nonce was empty") - } - if actual["nonce"].(string) == "" { - t.Fatalf("nonce was empty") - } - expected["nonce"] = actual["nonce"] - if !reflect.DeepEqual(actual, expected) { - t.Fatalf("\nexpected: %#v\nactual: %#v", expected, actual) - } } func TestSysRekey_Init_Cancel(t *testing.T) { - core, _, token := vault.TestCoreUnsealed(t) - ln, addr := TestServer(t, core) - defer ln.Close() - TestServerAuth(t, addr, token) + t.Run("cancel-barrier-barrier-key", func(t *testing.T) { + core, _, token := vault.TestCoreUnsealed(t) + ln, addr := TestServer(t, core) + defer ln.Close() + TestServerAuth(t, addr, token) - resp := testHttpPut(t, token, addr+"/v1/sys/rekey/init", map[string]interface{}{ - "secret_shares": 5, - "secret_threshold": 3, + resp := testHttpPut(t, token, addr+"/v1/sys/rekey/init", map[string]interface{}{ + "secret_shares": 5, + "secret_threshold": 3, + }) + testResponseStatus(t, resp, 200) + + resp = testHttpDelete(t, token, addr+"/v1/sys/rekey/init") + testResponseStatus(t, resp, 204) + + resp, err := http.Get(addr + "/v1/sys/rekey/init") + if err != nil { + t.Fatalf("err: %s", err) + } + + var actual map[string]interface{} + expected := map[string]interface{}{ + "started": false, + "t": json.Number("0"), + "n": json.Number("0"), + "progress": json.Number("0"), + "required": json.Number("3"), + "pgp_fingerprints": interface{}(nil), + "backup": false, + "nonce": "", + "verification_required": false, + } + testResponseStatus(t, resp, 200) + testResponseBody(t, resp, &actual) + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("\nexpected: %#v\nactual: %#v", expected, actual) + } }) - testResponseStatus(t, resp, 200) - - resp = testHttpDelete(t, token, addr+"/v1/sys/rekey/init") - testResponseStatus(t, resp, 204) - - resp, err := http.Get(addr + "/v1/sys/rekey/init") - if err != nil { - t.Fatalf("err: %s", err) - } - - var actual map[string]interface{} - expected := map[string]interface{}{ - "started": false, - "t": json.Number("0"), - "n": json.Number("0"), - "progress": json.Number("0"), - "required": json.Number("3"), - "pgp_fingerprints": interface{}(nil), - "backup": false, - "nonce": "", - "verification_required": false, - } - testResponseStatus(t, resp, 200) - testResponseBody(t, resp, &actual) - if !reflect.DeepEqual(actual, expected) { - t.Fatalf("\nexpected: %#v\nactual: %#v", expected, actual) - } } func TestSysRekey_badKey(t *testing.T) { @@ -172,71 +178,73 @@ func TestSysRekey_badKey(t *testing.T) { } func TestSysRekey_Update(t *testing.T) { - core, keys, token := vault.TestCoreUnsealed(t) - ln, addr := TestServer(t, core) - defer ln.Close() - TestServerAuth(t, addr, token) + t.Run("rekey-barrier-barrier-key", func(t *testing.T) { + core, keys, token := vault.TestCoreUnsealed(t) + ln, addr := TestServer(t, core) + defer ln.Close() + TestServerAuth(t, addr, token) - resp := testHttpPut(t, token, addr+"/v1/sys/rekey/init", map[string]interface{}{ - "secret_shares": 5, - "secret_threshold": 3, - }) - var rekeyStatus map[string]interface{} - testResponseStatus(t, resp, 200) - testResponseBody(t, resp, &rekeyStatus) - - var actual map[string]interface{} - var expected map[string]interface{} - - for i, key := range keys { - resp = testHttpPut(t, token, addr+"/v1/sys/rekey/update", map[string]interface{}{ - "nonce": rekeyStatus["nonce"].(string), - "key": hex.EncodeToString(key), + resp := testHttpPut(t, token, addr+"/v1/sys/rekey/init", map[string]interface{}{ + "secret_shares": 5, + "secret_threshold": 3, }) - - actual = map[string]interface{}{} - expected = map[string]interface{}{ - "started": true, - "nonce": rekeyStatus["nonce"].(string), - "backup": false, - "pgp_fingerprints": interface{}(nil), - "required": json.Number("3"), - "t": json.Number("3"), - "n": json.Number("5"), - "progress": json.Number(fmt.Sprintf("%d", i+1)), - "verification_required": false, - } + var rekeyStatus map[string]interface{} testResponseStatus(t, resp, 200) - testResponseBody(t, resp, &actual) + testResponseBody(t, resp, &rekeyStatus) - if i+1 == len(keys) { - delete(expected, "started") - delete(expected, "required") - delete(expected, "t") - delete(expected, "n") - delete(expected, "progress") - expected["complete"] = true - expected["keys"] = actual["keys"] - expected["keys_base64"] = actual["keys_base64"] + var actual map[string]interface{} + var expected map[string]interface{} + + for i, key := range keys { + resp = testHttpPut(t, token, addr+"/v1/sys/rekey/update", map[string]interface{}{ + "nonce": rekeyStatus["nonce"].(string), + "key": hex.EncodeToString(key), + }) + + actual = map[string]interface{}{} + expected = map[string]interface{}{ + "started": true, + "nonce": rekeyStatus["nonce"].(string), + "backup": false, + "pgp_fingerprints": interface{}(nil), + "required": json.Number("3"), + "t": json.Number("3"), + "n": json.Number("5"), + "progress": json.Number(fmt.Sprintf("%d", i+1)), + "verification_required": false, + } + testResponseStatus(t, resp, 200) + testResponseBody(t, resp, &actual) + + if i+1 == len(keys) { + delete(expected, "started") + delete(expected, "required") + delete(expected, "t") + delete(expected, "n") + delete(expected, "progress") + expected["complete"] = true + expected["keys"] = actual["keys"] + expected["keys_base64"] = actual["keys_base64"] + } + + if i+1 < len(keys) && (actual["nonce"] == nil || actual["nonce"].(string) == "") { + t.Fatalf("expected a nonce, i is %d, actual is %#v", i, actual) + } + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("\nexpected: \n%#v\nactual: \n%#v", expected, actual) + } } - if i+1 < len(keys) && (actual["nonce"] == nil || actual["nonce"].(string) == "") { - t.Fatalf("expected a nonce, i is %d, actual is %#v", i, actual) + retKeys := actual["keys"].([]interface{}) + if len(retKeys) != 5 { + t.Fatalf("bad: %#v", retKeys) } - - if diff := deep.Equal(actual, expected); diff != nil { - t.Fatal(diff) + keysB64 := actual["keys_base64"].([]interface{}) + if len(keysB64) != 5 { + t.Fatalf("bad: %#v", keysB64) } - } - - retKeys := actual["keys"].([]interface{}) - if len(retKeys) != 5 { - t.Fatalf("bad: %#v", retKeys) - } - keysB64 := actual["keys_base64"].([]interface{}) - if len(keysB64) != 5 { - t.Fatalf("bad: %#v", keysB64) - } + }) } func TestSysRekey_ReInitUpdate(t *testing.T) { diff --git a/http/sys_seal.go b/http/sys_seal.go index ad6aaf9bff..2c8f0f6341 100644 --- a/http/sys_seal.go +++ b/http/sys_seal.go @@ -36,10 +36,9 @@ func handleSysSeal(core *vault.Core) http.Handler { if errwrap.Contains(err, logical.ErrPermissionDenied.Error()) { respondError(w, http.StatusForbidden, err) return - } else { - respondError(w, http.StatusInternalServerError, err) - return } + respondError(w, http.StatusInternalServerError, err) + return } respondOk(w, nil) @@ -63,6 +62,10 @@ func handleSysStepDown(core *vault.Core) http.Handler { // Seal with the token above if err := core.StepDown(r.Context(), req); err != nil { + if errwrap.Contains(err, logical.ErrPermissionDenied.Error()) { + respondError(w, http.StatusForbidden, err) + return + } respondError(w, http.StatusInternalServerError, err) return } @@ -197,28 +200,30 @@ func handleSysSealStatusRaw(core *vault.Core, w http.ResponseWriter, r *http.Req progress, nonce := core.SecretProgress() respondOk(w, &SealStatusResponse{ - Type: sealConfig.Type, - Sealed: sealed, - T: sealConfig.SecretThreshold, - N: sealConfig.SecretShares, - Progress: progress, - Nonce: nonce, - Version: version.GetVersion().VersionNumber(), - ClusterName: clusterName, - ClusterID: clusterID, + Type: sealConfig.Type, + Sealed: sealed, + T: sealConfig.SecretThreshold, + N: sealConfig.SecretShares, + Progress: progress, + Nonce: nonce, + Version: version.GetVersion().VersionNumber(), + ClusterName: clusterName, + ClusterID: clusterID, + RecoverySeal: core.SealAccess().RecoveryKeySupported(), }) } type SealStatusResponse struct { - Type string `json:"type"` - Sealed bool `json:"sealed"` - T int `json:"t"` - N int `json:"n"` - Progress int `json:"progress"` - Nonce string `json:"nonce"` - Version string `json:"version"` - ClusterName string `json:"cluster_name,omitempty"` - ClusterID string `json:"cluster_id,omitempty"` + Type string `json:"type"` + Sealed bool `json:"sealed"` + T int `json:"t"` + N int `json:"n"` + Progress int `json:"progress"` + Nonce string `json:"nonce"` + Version string `json:"version"` + ClusterName string `json:"cluster_name,omitempty"` + ClusterID string `json:"cluster_id,omitempty"` + RecoverySeal bool `json:"recovery_seal"` } type UnsealRequest struct { diff --git a/http/sys_seal_test.go b/http/sys_seal_test.go index 1b42f1b867..765a40b932 100644 --- a/http/sys_seal_test.go +++ b/http/sys_seal_test.go @@ -1,7 +1,6 @@ package http import ( - "context" "encoding/hex" "encoding/json" "fmt" @@ -10,6 +9,7 @@ import ( "strconv" "testing" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/vault" ) @@ -27,12 +27,13 @@ func TestSysSealStatus(t *testing.T) { var actual map[string]interface{} expected := map[string]interface{}{ - "sealed": true, - "t": json.Number("3"), - "n": json.Number("3"), - "progress": json.Number("0"), - "nonce": "", - "type": "shamir", + "sealed": true, + "t": json.Number("3"), + "n": json.Number("3"), + "progress": json.Number("0"), + "nonce": "", + "type": "shamir", + "recovery_seal": false, } testResponseStatus(t, resp, 200) testResponseBody(t, resp, &actual) @@ -108,12 +109,13 @@ func TestSysUnseal(t *testing.T) { var actual map[string]interface{} expected := map[string]interface{}{ - "sealed": true, - "t": json.Number("3"), - "n": json.Number("3"), - "progress": json.Number(fmt.Sprintf("%d", i+1)), - "nonce": "", - "type": "shamir", + "sealed": true, + "t": json.Number("3"), + "n": json.Number("3"), + "progress": json.Number(fmt.Sprintf("%d", i+1)), + "nonce": "", + "type": "shamir", + "recovery_seal": false, } if i == len(keys)-1 { expected["sealed"] = false @@ -187,11 +189,12 @@ func TestSysUnseal_Reset(t *testing.T) { var actual map[string]interface{} expected := map[string]interface{}{ - "sealed": true, - "t": json.Number("3"), - "n": json.Number("5"), - "progress": json.Number(strconv.Itoa(i + 1)), - "type": "shamir", + "sealed": true, + "t": json.Number("3"), + "n": json.Number("5"), + "progress": json.Number(strconv.Itoa(i + 1)), + "type": "shamir", + "recovery_seal": false, } testResponseStatus(t, resp, 200) testResponseBody(t, resp, &actual) @@ -224,11 +227,12 @@ func TestSysUnseal_Reset(t *testing.T) { actual = map[string]interface{}{} expected := map[string]interface{}{ - "sealed": true, - "t": json.Number("3"), - "n": json.Number("5"), - "progress": json.Number("0"), - "type": "shamir", + "sealed": true, + "t": json.Number("3"), + "n": json.Number("5"), + "progress": json.Number("0"), + "type": "shamir", + "recovery_seal": false, } testResponseStatus(t, resp, 200) testResponseBody(t, resp, &actual) @@ -274,7 +278,7 @@ func TestSysSeal_Permissions(t *testing.T) { }, ClientToken: root, } - resp, err := core.HandleRequest(context.Background(), req) + resp, err := core.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -289,7 +293,7 @@ func TestSysSeal_Permissions(t *testing.T) { "policies": []string{"test"}, } - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v %v", err, resp) } @@ -312,7 +316,7 @@ func TestSysSeal_Permissions(t *testing.T) { }, ClientToken: root, } - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -333,7 +337,7 @@ func TestSysSeal_Permissions(t *testing.T) { }, ClientToken: root, } - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -354,7 +358,7 @@ func TestSysSeal_Permissions(t *testing.T) { }, ClientToken: root, } - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } diff --git a/http/util.go b/http/util.go new file mode 100644 index 0000000000..ee7c546cd9 --- /dev/null +++ b/http/util.go @@ -0,0 +1,22 @@ +package http + +import ( + "net/http" + + "github.com/hashicorp/vault/helper/namespace" + "github.com/hashicorp/vault/vault" +) + +var ( + adjustRequest = func(c *vault.Core, r *http.Request) (*http.Request, int) { + return r.WithContext(namespace.ContextWithNamespace(r.Context(), namespace.RootNamespace)), 0 + } + + genericWrapping = func(core *vault.Core, in http.Handler, props *vault.HandlerProperties) http.Handler { + // Wrap the help wrapped handler with another layer with a generic + // handler + return wrapGenericHandler(core, in, props.MaxRequestSize, props.MaxRequestDuration) + } + + additionalRoutes = func(mux *http.ServeMux, core *vault.Core) {} +) diff --git a/logical/auth.go b/logical/auth.go index 68f856f4be..cad3f1bfbb 100644 --- a/logical/auth.go +++ b/logical/auth.go @@ -34,6 +34,10 @@ type Auth struct { TokenPolicies []string `json:"token_policies" mapstructure:"token_policies" structs:"token_policies"` IdentityPolicies []string `json:"identity_policies" mapstructure:"identity_policies" structs:"identity_policies"` + // ExternalNamespacePolicies represent the policies authorized from + // different namespaces indexed by respective namespace identifiers + ExternalNamespacePolicies map[string][]string `json:"external_namespace_policies" mapstructure:"external_namespace_policies" structs:"external_namespace_policies"` + // Metadata is used to attach arbitrary string-type metadata to // an authenticated user. This metadata will be outputted into the // audit log. diff --git a/logical/error.go b/logical/error.go index b2017000e7..c79b645b98 100644 --- a/logical/error.go +++ b/logical/error.go @@ -76,3 +76,15 @@ type ReplicationCodedError struct { func (r *ReplicationCodedError) Error() string { return r.Msg } + +type KeyNotFoundError struct { + Err error +} + +func (e *KeyNotFoundError) WrappedErrors() []error { + return []error{e.Err} +} + +func (e *KeyNotFoundError) Error() string { + return e.Err.Error() +} diff --git a/logical/framework/backend.go b/logical/framework/backend.go index 4c787cbed1..a2dc68fa43 100644 --- a/logical/framework/backend.go +++ b/logical/framework/backend.go @@ -17,6 +17,7 @@ import ( "github.com/hashicorp/go-multierror" "github.com/hashicorp/vault/helper/errutil" + "github.com/hashicorp/vault/helper/license" "github.com/hashicorp/vault/helper/logging" "github.com/hashicorp/vault/helper/parseutil" "github.com/hashicorp/vault/logical" @@ -183,6 +184,14 @@ func (b *Backend) HandleRequest(ctx context.Context, req *logical.Request) (*log return nil, logical.ErrUnsupportedPath } + // Check if a feature is required and if the license has that feature + if path.FeatureRequired != license.FeatureNone { + hasFeature := b.system.HasFeature(path.FeatureRequired) + if !hasFeature { + return nil, logical.CodedError(401, "Feature Not Enabled") + } + } + // Build up the data for the route, with the URL taking priority // for the fields over the PUT data. raw := make(map[string]interface{}, len(path.Fields)) diff --git a/logical/framework/path.go b/logical/framework/path.go index e53dd196c7..39f2af5336 100644 --- a/logical/framework/path.go +++ b/logical/framework/path.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/hashicorp/errwrap" + "github.com/hashicorp/vault/helper/license" "github.com/hashicorp/vault/logical" ) @@ -69,6 +70,10 @@ type Path struct { // must have UpdateCapability on the path. ExistenceCheck ExistenceFunc + // FeatureRequired, if implemented, will validate if the given feature is + // enabled for the set of paths + FeatureRequired license.Features + // Help is text describing how to use this path. This will be used // to auto-generate the help operation. The Path will automatically // generate a parameter listing and URL structure based on the diff --git a/logical/identity.pb.go b/logical/identity.pb.go index 509db57a50..520d32b3a7 100644 --- a/logical/identity.pb.go +++ b/logical/identity.pb.go @@ -20,13 +20,13 @@ const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package type Entity struct { // ID is the unique identifier for the entity - ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"` + ID string `sentinel:"" protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"` // Name is the human-friendly unique identifier for the entity - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Name string `sentinel:"" protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` // Aliases contains thhe alias mappings for the given entity - Aliases []*Alias `protobuf:"bytes,3,rep,name=aliases,proto3" json:"aliases,omitempty"` + Aliases []*Alias `sentinel:"" protobuf:"bytes,3,rep,name=aliases,proto3" json:"aliases,omitempty"` // Metadata represents the custom data tied to this entity - Metadata map[string]string `protobuf:"bytes,4,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Metadata map[string]string `sentinel:"" protobuf:"bytes,4,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -86,14 +86,14 @@ func (m *Entity) GetMetadata() map[string]string { type Alias struct { // MountType is the backend mount's type to which this identity belongs - MountType string `protobuf:"bytes,1,opt,name=mount_type,json=mountType,proto3" json:"mount_type,omitempty"` + MountType string `sentinel:"" protobuf:"bytes,1,opt,name=mount_type,json=mountType,proto3" json:"mount_type,omitempty"` // MountAccessor is the identifier of the mount entry to which this // identity belongs - MountAccessor string `protobuf:"bytes,2,opt,name=mount_accessor,json=mountAccessor,proto3" json:"mount_accessor,omitempty"` + MountAccessor string `sentinel:"" protobuf:"bytes,2,opt,name=mount_accessor,json=mountAccessor,proto3" json:"mount_accessor,omitempty"` // Name is the identifier of this identity in its authentication source - Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` + Name string `sentinel:"" protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` // Metadata represents the custom data tied to this alias - Metadata map[string]string `protobuf:"bytes,4,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Metadata map[string]string `sentinel:"" protobuf:"bytes,4,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` diff --git a/logical/plugin/grpc_system.go b/logical/plugin/grpc_system.go index a0670801dd..bcf5e70b65 100644 --- a/logical/plugin/grpc_system.go +++ b/logical/plugin/grpc_system.go @@ -11,6 +11,7 @@ import ( "fmt" "github.com/hashicorp/vault/helper/consts" + "github.com/hashicorp/vault/helper/license" "github.com/hashicorp/vault/helper/pluginutil" "github.com/hashicorp/vault/helper/wrapping" "github.com/hashicorp/vault/logical" @@ -123,6 +124,11 @@ func (s *gRPCSystemViewClient) MlockEnabled() bool { return reply.Enabled } +func (s *gRPCSystemViewClient) HasFeature(feature license.Features) bool { + // Not implemented + return false +} + func (s *gRPCSystemViewClient) LocalMount() bool { reply, err := s.client.LocalMount(context.Background(), &pb.Empty{}) if err != nil { diff --git a/logical/plugin/pb/backend.pb.go b/logical/plugin/pb/backend.pb.go index 6738b2577f..712bc7357c 100644 --- a/logical/plugin/pb/backend.pb.go +++ b/logical/plugin/pb/backend.pb.go @@ -35,7 +35,7 @@ func (m *Empty) Reset() { *m = Empty{} } func (m *Empty) String() string { return proto.CompactTextString(m) } func (*Empty) ProtoMessage() {} func (*Empty) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{0} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{0} } func (m *Empty) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Empty.Unmarshal(m, b) @@ -66,7 +66,7 @@ func (m *Header) Reset() { *m = Header{} } func (m *Header) String() string { return proto.CompactTextString(m) } func (*Header) ProtoMessage() {} func (*Header) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{1} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{1} } func (m *Header) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Header.Unmarshal(m, b) @@ -117,7 +117,7 @@ func (m *ProtoError) Reset() { *m = ProtoError{} } func (m *ProtoError) String() string { return proto.CompactTextString(m) } func (*ProtoError) ProtoMessage() {} func (*ProtoError) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{2} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{2} } func (m *ProtoError) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_ProtoError.Unmarshal(m, b) @@ -180,7 +180,7 @@ func (m *Paths) Reset() { *m = Paths{} } func (m *Paths) String() string { return proto.CompactTextString(m) } func (*Paths) ProtoMessage() {} func (*Paths) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{3} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{3} } func (m *Paths) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Paths.Unmarshal(m, b) @@ -304,7 +304,7 @@ func (m *Request) Reset() { *m = Request{} } func (m *Request) String() string { return proto.CompactTextString(m) } func (*Request) ProtoMessage() {} func (*Request) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{4} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{4} } func (m *Request) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Request.Unmarshal(m, b) @@ -513,7 +513,11 @@ type Auth struct { GroupAliases []*logical.Alias `sentinel:"" protobuf:"bytes,12,rep,name=group_aliases,json=groupAliases,proto3" json:"group_aliases,omitempty"` // If set, restricts usage of the certificates to client IPs falling within // the range of the specified CIDR(s). - BoundCidrs []string `sentinel:"" protobuf:"bytes,13,rep,name=bound_cidrs,json=boundCidrs,proto3" json:"bound_cidrs,omitempty"` + BoundCidrs []string `sentinel:"" protobuf:"bytes,13,rep,name=bound_cidrs,json=boundCidrs,proto3" json:"bound_cidrs,omitempty"` + // TokenPolicies and IdentityPolicies break down the list in Policies to + // help determine where a policy was sourced + TokenPolicies []string `sentinel:"" protobuf:"bytes,14,rep,name=token_policies,json=tokenPolicies,proto3" json:"token_policies,omitempty"` + IdentityPolicies []string `sentinel:"" protobuf:"bytes,15,rep,name=identity_policies,json=identityPolicies,proto3" json:"identity_policies,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -523,7 +527,7 @@ func (m *Auth) Reset() { *m = Auth{} } func (m *Auth) String() string { return proto.CompactTextString(m) } func (*Auth) ProtoMessage() {} func (*Auth) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{5} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{5} } func (m *Auth) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Auth.Unmarshal(m, b) @@ -634,6 +638,186 @@ func (m *Auth) GetBoundCidrs() []string { return nil } +func (m *Auth) GetTokenPolicies() []string { + if m != nil { + return m.TokenPolicies + } + return nil +} + +func (m *Auth) GetIdentityPolicies() []string { + if m != nil { + return m.IdentityPolicies + } + return nil +} + +type TokenEntry struct { + ID string `sentinel:"" protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Accessor string `sentinel:"" protobuf:"bytes,2,opt,name=accessor,proto3" json:"accessor,omitempty"` + Parent string `sentinel:"" protobuf:"bytes,3,opt,name=parent,proto3" json:"parent,omitempty"` + Policies []string `sentinel:"" protobuf:"bytes,4,rep,name=policies,proto3" json:"policies,omitempty"` + Path string `sentinel:"" protobuf:"bytes,5,opt,name=path,proto3" json:"path,omitempty"` + Meta map[string]string `sentinel:"" protobuf:"bytes,6,rep,name=meta,proto3" json:"meta,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + DisplayName string `sentinel:"" protobuf:"bytes,7,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"` + NumUses int64 `sentinel:"" protobuf:"varint,8,opt,name=num_uses,json=numUses,proto3" json:"num_uses,omitempty"` + CreationTime int64 `sentinel:"" protobuf:"varint,9,opt,name=creation_time,json=creationTime,proto3" json:"creation_time,omitempty"` + TTL int64 `sentinel:"" protobuf:"varint,10,opt,name=ttl,proto3" json:"ttl,omitempty"` + ExplicitMaxTTL int64 `sentinel:"" protobuf:"varint,11,opt,name=explicit_max_ttl,json=explicitMaxTtl,proto3" json:"explicit_max_ttl,omitempty"` + Role string `sentinel:"" protobuf:"bytes,12,opt,name=role,proto3" json:"role,omitempty"` + Period int64 `sentinel:"" protobuf:"varint,13,opt,name=period,proto3" json:"period,omitempty"` + EntityID string `sentinel:"" protobuf:"bytes,14,opt,name=entity_id,json=entityId,proto3" json:"entity_id,omitempty"` + BoundCidrs []string `sentinel:"" protobuf:"bytes,15,rep,name=bound_cidrs,json=boundCidrs,proto3" json:"bound_cidrs,omitempty"` + NamespaceID string `sentinel:"" protobuf:"bytes,16,opt,name=namespace_id,json=namespaceID,proto3" json:"namespace_id,omitempty"` + CubbyholeID string `sentinel:"" protobuf:"bytes,17,opt,name=cubbyhole_id,json=cubbyholeId,proto3" json:"cubbyhole_id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *TokenEntry) Reset() { *m = TokenEntry{} } +func (m *TokenEntry) String() string { return proto.CompactTextString(m) } +func (*TokenEntry) ProtoMessage() {} +func (*TokenEntry) Descriptor() ([]byte, []int) { + return fileDescriptor_backend_2aa165f9d8e053c1, []int{6} +} +func (m *TokenEntry) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_TokenEntry.Unmarshal(m, b) +} +func (m *TokenEntry) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_TokenEntry.Marshal(b, m, deterministic) +} +func (dst *TokenEntry) XXX_Merge(src proto.Message) { + xxx_messageInfo_TokenEntry.Merge(dst, src) +} +func (m *TokenEntry) XXX_Size() int { + return xxx_messageInfo_TokenEntry.Size(m) +} +func (m *TokenEntry) XXX_DiscardUnknown() { + xxx_messageInfo_TokenEntry.DiscardUnknown(m) +} + +var xxx_messageInfo_TokenEntry proto.InternalMessageInfo + +func (m *TokenEntry) GetID() string { + if m != nil { + return m.ID + } + return "" +} + +func (m *TokenEntry) GetAccessor() string { + if m != nil { + return m.Accessor + } + return "" +} + +func (m *TokenEntry) GetParent() string { + if m != nil { + return m.Parent + } + return "" +} + +func (m *TokenEntry) GetPolicies() []string { + if m != nil { + return m.Policies + } + return nil +} + +func (m *TokenEntry) GetPath() string { + if m != nil { + return m.Path + } + return "" +} + +func (m *TokenEntry) GetMeta() map[string]string { + if m != nil { + return m.Meta + } + return nil +} + +func (m *TokenEntry) GetDisplayName() string { + if m != nil { + return m.DisplayName + } + return "" +} + +func (m *TokenEntry) GetNumUses() int64 { + if m != nil { + return m.NumUses + } + return 0 +} + +func (m *TokenEntry) GetCreationTime() int64 { + if m != nil { + return m.CreationTime + } + return 0 +} + +func (m *TokenEntry) GetTTL() int64 { + if m != nil { + return m.TTL + } + return 0 +} + +func (m *TokenEntry) GetExplicitMaxTTL() int64 { + if m != nil { + return m.ExplicitMaxTTL + } + return 0 +} + +func (m *TokenEntry) GetRole() string { + if m != nil { + return m.Role + } + return "" +} + +func (m *TokenEntry) GetPeriod() int64 { + if m != nil { + return m.Period + } + return 0 +} + +func (m *TokenEntry) GetEntityID() string { + if m != nil { + return m.EntityID + } + return "" +} + +func (m *TokenEntry) GetBoundCidrs() []string { + if m != nil { + return m.BoundCidrs + } + return nil +} + +func (m *TokenEntry) GetNamespaceID() string { + if m != nil { + return m.NamespaceID + } + return "" +} + +func (m *TokenEntry) GetCubbyholeID() string { + if m != nil { + return m.CubbyholeID + } + return "" +} + type LeaseOptions struct { TTL int64 `sentinel:"" protobuf:"varint,1,opt,name=TTL,proto3" json:"TTL,omitempty"` Renewable bool `sentinel:"" protobuf:"varint,2,opt,name=renewable,proto3" json:"renewable,omitempty"` @@ -649,7 +833,7 @@ func (m *LeaseOptions) Reset() { *m = LeaseOptions{} } func (m *LeaseOptions) String() string { return proto.CompactTextString(m) } func (*LeaseOptions) ProtoMessage() {} func (*LeaseOptions) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{6} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{7} } func (m *LeaseOptions) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_LeaseOptions.Unmarshal(m, b) @@ -723,7 +907,7 @@ func (m *Secret) Reset() { *m = Secret{} } func (m *Secret) String() string { return proto.CompactTextString(m) } func (*Secret) ProtoMessage() {} func (*Secret) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{7} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{8} } func (m *Secret) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Secret.Unmarshal(m, b) @@ -794,7 +978,7 @@ func (m *Response) Reset() { *m = Response{} } func (m *Response) String() string { return proto.CompactTextString(m) } func (*Response) ProtoMessage() {} func (*Response) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{8} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{9} } func (m *Response) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Response.Unmarshal(m, b) @@ -889,7 +1073,7 @@ func (m *ResponseWrapInfo) Reset() { *m = ResponseWrapInfo{} } func (m *ResponseWrapInfo) String() string { return proto.CompactTextString(m) } func (*ResponseWrapInfo) ProtoMessage() {} func (*ResponseWrapInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{9} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{10} } func (m *ResponseWrapInfo) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_ResponseWrapInfo.Unmarshal(m, b) @@ -991,7 +1175,7 @@ func (m *RequestWrapInfo) Reset() { *m = RequestWrapInfo{} } func (m *RequestWrapInfo) String() string { return proto.CompactTextString(m) } func (*RequestWrapInfo) ProtoMessage() {} func (*RequestWrapInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{10} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{11} } func (m *RequestWrapInfo) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_RequestWrapInfo.Unmarshal(m, b) @@ -1045,7 +1229,7 @@ func (m *HandleRequestArgs) Reset() { *m = HandleRequestArgs{} } func (m *HandleRequestArgs) String() string { return proto.CompactTextString(m) } func (*HandleRequestArgs) ProtoMessage() {} func (*HandleRequestArgs) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{11} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{12} } func (m *HandleRequestArgs) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_HandleRequestArgs.Unmarshal(m, b) @@ -1092,7 +1276,7 @@ func (m *HandleRequestReply) Reset() { *m = HandleRequestReply{} } func (m *HandleRequestReply) String() string { return proto.CompactTextString(m) } func (*HandleRequestReply) ProtoMessage() {} func (*HandleRequestReply) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{12} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{13} } func (m *HandleRequestReply) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_HandleRequestReply.Unmarshal(m, b) @@ -1138,7 +1322,7 @@ func (m *SpecialPathsReply) Reset() { *m = SpecialPathsReply{} } func (m *SpecialPathsReply) String() string { return proto.CompactTextString(m) } func (*SpecialPathsReply) ProtoMessage() {} func (*SpecialPathsReply) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{13} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{14} } func (m *SpecialPathsReply) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_SpecialPathsReply.Unmarshal(m, b) @@ -1178,7 +1362,7 @@ func (m *HandleExistenceCheckArgs) Reset() { *m = HandleExistenceCheckAr func (m *HandleExistenceCheckArgs) String() string { return proto.CompactTextString(m) } func (*HandleExistenceCheckArgs) ProtoMessage() {} func (*HandleExistenceCheckArgs) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{14} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{15} } func (m *HandleExistenceCheckArgs) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_HandleExistenceCheckArgs.Unmarshal(m, b) @@ -1226,7 +1410,7 @@ func (m *HandleExistenceCheckReply) Reset() { *m = HandleExistenceCheckR func (m *HandleExistenceCheckReply) String() string { return proto.CompactTextString(m) } func (*HandleExistenceCheckReply) ProtoMessage() {} func (*HandleExistenceCheckReply) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{15} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{16} } func (m *HandleExistenceCheckReply) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_HandleExistenceCheckReply.Unmarshal(m, b) @@ -1281,7 +1465,7 @@ func (m *SetupArgs) Reset() { *m = SetupArgs{} } func (m *SetupArgs) String() string { return proto.CompactTextString(m) } func (*SetupArgs) ProtoMessage() {} func (*SetupArgs) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{16} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{17} } func (m *SetupArgs) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_SetupArgs.Unmarshal(m, b) @@ -1334,7 +1518,7 @@ func (m *SetupReply) Reset() { *m = SetupReply{} } func (m *SetupReply) String() string { return proto.CompactTextString(m) } func (*SetupReply) ProtoMessage() {} func (*SetupReply) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{17} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{18} } func (m *SetupReply) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_SetupReply.Unmarshal(m, b) @@ -1373,7 +1557,7 @@ func (m *TypeReply) Reset() { *m = TypeReply{} } func (m *TypeReply) String() string { return proto.CompactTextString(m) } func (*TypeReply) ProtoMessage() {} func (*TypeReply) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{18} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{19} } func (m *TypeReply) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_TypeReply.Unmarshal(m, b) @@ -1411,7 +1595,7 @@ func (m *InvalidateKeyArgs) Reset() { *m = InvalidateKeyArgs{} } func (m *InvalidateKeyArgs) String() string { return proto.CompactTextString(m) } func (*InvalidateKeyArgs) ProtoMessage() {} func (*InvalidateKeyArgs) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{19} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{20} } func (m *InvalidateKeyArgs) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_InvalidateKeyArgs.Unmarshal(m, b) @@ -1451,7 +1635,7 @@ func (m *StorageEntry) Reset() { *m = StorageEntry{} } func (m *StorageEntry) String() string { return proto.CompactTextString(m) } func (*StorageEntry) ProtoMessage() {} func (*StorageEntry) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{20} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{21} } func (m *StorageEntry) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_StorageEntry.Unmarshal(m, b) @@ -1503,7 +1687,7 @@ func (m *StorageListArgs) Reset() { *m = StorageListArgs{} } func (m *StorageListArgs) String() string { return proto.CompactTextString(m) } func (*StorageListArgs) ProtoMessage() {} func (*StorageListArgs) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{21} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{22} } func (m *StorageListArgs) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_StorageListArgs.Unmarshal(m, b) @@ -1542,7 +1726,7 @@ func (m *StorageListReply) Reset() { *m = StorageListReply{} } func (m *StorageListReply) String() string { return proto.CompactTextString(m) } func (*StorageListReply) ProtoMessage() {} func (*StorageListReply) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{22} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{23} } func (m *StorageListReply) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_StorageListReply.Unmarshal(m, b) @@ -1587,7 +1771,7 @@ func (m *StorageGetArgs) Reset() { *m = StorageGetArgs{} } func (m *StorageGetArgs) String() string { return proto.CompactTextString(m) } func (*StorageGetArgs) ProtoMessage() {} func (*StorageGetArgs) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{23} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{24} } func (m *StorageGetArgs) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_StorageGetArgs.Unmarshal(m, b) @@ -1626,7 +1810,7 @@ func (m *StorageGetReply) Reset() { *m = StorageGetReply{} } func (m *StorageGetReply) String() string { return proto.CompactTextString(m) } func (*StorageGetReply) ProtoMessage() {} func (*StorageGetReply) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{24} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{25} } func (m *StorageGetReply) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_StorageGetReply.Unmarshal(m, b) @@ -1671,7 +1855,7 @@ func (m *StoragePutArgs) Reset() { *m = StoragePutArgs{} } func (m *StoragePutArgs) String() string { return proto.CompactTextString(m) } func (*StoragePutArgs) ProtoMessage() {} func (*StoragePutArgs) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{25} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{26} } func (m *StoragePutArgs) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_StoragePutArgs.Unmarshal(m, b) @@ -1709,7 +1893,7 @@ func (m *StoragePutReply) Reset() { *m = StoragePutReply{} } func (m *StoragePutReply) String() string { return proto.CompactTextString(m) } func (*StoragePutReply) ProtoMessage() {} func (*StoragePutReply) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{26} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{27} } func (m *StoragePutReply) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_StoragePutReply.Unmarshal(m, b) @@ -1747,7 +1931,7 @@ func (m *StorageDeleteArgs) Reset() { *m = StorageDeleteArgs{} } func (m *StorageDeleteArgs) String() string { return proto.CompactTextString(m) } func (*StorageDeleteArgs) ProtoMessage() {} func (*StorageDeleteArgs) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{27} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{28} } func (m *StorageDeleteArgs) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_StorageDeleteArgs.Unmarshal(m, b) @@ -1785,7 +1969,7 @@ func (m *StorageDeleteReply) Reset() { *m = StorageDeleteReply{} } func (m *StorageDeleteReply) String() string { return proto.CompactTextString(m) } func (*StorageDeleteReply) ProtoMessage() {} func (*StorageDeleteReply) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{28} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{29} } func (m *StorageDeleteReply) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_StorageDeleteReply.Unmarshal(m, b) @@ -1823,7 +2007,7 @@ func (m *TTLReply) Reset() { *m = TTLReply{} } func (m *TTLReply) String() string { return proto.CompactTextString(m) } func (*TTLReply) ProtoMessage() {} func (*TTLReply) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{29} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{30} } func (m *TTLReply) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_TTLReply.Unmarshal(m, b) @@ -1862,7 +2046,7 @@ func (m *SudoPrivilegeArgs) Reset() { *m = SudoPrivilegeArgs{} } func (m *SudoPrivilegeArgs) String() string { return proto.CompactTextString(m) } func (*SudoPrivilegeArgs) ProtoMessage() {} func (*SudoPrivilegeArgs) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{30} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{31} } func (m *SudoPrivilegeArgs) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_SudoPrivilegeArgs.Unmarshal(m, b) @@ -1907,7 +2091,7 @@ func (m *SudoPrivilegeReply) Reset() { *m = SudoPrivilegeReply{} } func (m *SudoPrivilegeReply) String() string { return proto.CompactTextString(m) } func (*SudoPrivilegeReply) ProtoMessage() {} func (*SudoPrivilegeReply) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{31} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{32} } func (m *SudoPrivilegeReply) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_SudoPrivilegeReply.Unmarshal(m, b) @@ -1945,7 +2129,7 @@ func (m *TaintedReply) Reset() { *m = TaintedReply{} } func (m *TaintedReply) String() string { return proto.CompactTextString(m) } func (*TaintedReply) ProtoMessage() {} func (*TaintedReply) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{32} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{33} } func (m *TaintedReply) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_TaintedReply.Unmarshal(m, b) @@ -1983,7 +2167,7 @@ func (m *CachingDisabledReply) Reset() { *m = CachingDisabledReply{} } func (m *CachingDisabledReply) String() string { return proto.CompactTextString(m) } func (*CachingDisabledReply) ProtoMessage() {} func (*CachingDisabledReply) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{33} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{34} } func (m *CachingDisabledReply) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_CachingDisabledReply.Unmarshal(m, b) @@ -2021,7 +2205,7 @@ func (m *ReplicationStateReply) Reset() { *m = ReplicationStateReply{} } func (m *ReplicationStateReply) String() string { return proto.CompactTextString(m) } func (*ReplicationStateReply) ProtoMessage() {} func (*ReplicationStateReply) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{34} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{35} } func (m *ReplicationStateReply) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_ReplicationStateReply.Unmarshal(m, b) @@ -2061,7 +2245,7 @@ func (m *ResponseWrapDataArgs) Reset() { *m = ResponseWrapDataArgs{} } func (m *ResponseWrapDataArgs) String() string { return proto.CompactTextString(m) } func (*ResponseWrapDataArgs) ProtoMessage() {} func (*ResponseWrapDataArgs) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{35} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{36} } func (m *ResponseWrapDataArgs) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_ResponseWrapDataArgs.Unmarshal(m, b) @@ -2114,7 +2298,7 @@ func (m *ResponseWrapDataReply) Reset() { *m = ResponseWrapDataReply{} } func (m *ResponseWrapDataReply) String() string { return proto.CompactTextString(m) } func (*ResponseWrapDataReply) ProtoMessage() {} func (*ResponseWrapDataReply) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{36} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{37} } func (m *ResponseWrapDataReply) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_ResponseWrapDataReply.Unmarshal(m, b) @@ -2159,7 +2343,7 @@ func (m *MlockEnabledReply) Reset() { *m = MlockEnabledReply{} } func (m *MlockEnabledReply) String() string { return proto.CompactTextString(m) } func (*MlockEnabledReply) ProtoMessage() {} func (*MlockEnabledReply) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{37} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{38} } func (m *MlockEnabledReply) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_MlockEnabledReply.Unmarshal(m, b) @@ -2197,7 +2381,7 @@ func (m *LocalMountReply) Reset() { *m = LocalMountReply{} } func (m *LocalMountReply) String() string { return proto.CompactTextString(m) } func (*LocalMountReply) ProtoMessage() {} func (*LocalMountReply) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{38} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{39} } func (m *LocalMountReply) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_LocalMountReply.Unmarshal(m, b) @@ -2235,7 +2419,7 @@ func (m *EntityInfoArgs) Reset() { *m = EntityInfoArgs{} } func (m *EntityInfoArgs) String() string { return proto.CompactTextString(m) } func (*EntityInfoArgs) ProtoMessage() {} func (*EntityInfoArgs) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{39} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{40} } func (m *EntityInfoArgs) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_EntityInfoArgs.Unmarshal(m, b) @@ -2274,7 +2458,7 @@ func (m *EntityInfoReply) Reset() { *m = EntityInfoReply{} } func (m *EntityInfoReply) String() string { return proto.CompactTextString(m) } func (*EntityInfoReply) ProtoMessage() {} func (*EntityInfoReply) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{40} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{41} } func (m *EntityInfoReply) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_EntityInfoReply.Unmarshal(m, b) @@ -2320,7 +2504,7 @@ func (m *PluginEnvReply) Reset() { *m = PluginEnvReply{} } func (m *PluginEnvReply) String() string { return proto.CompactTextString(m) } func (*PluginEnvReply) ProtoMessage() {} func (*PluginEnvReply) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{41} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{42} } func (m *PluginEnvReply) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_PluginEnvReply.Unmarshal(m, b) @@ -2366,7 +2550,7 @@ func (m *Connection) Reset() { *m = Connection{} } func (m *Connection) String() string { return proto.CompactTextString(m) } func (*Connection) ProtoMessage() {} func (*Connection) Descriptor() ([]byte, []int) { - return fileDescriptor_backend_6306a5aa9d5ea026, []int{42} + return fileDescriptor_backend_2aa165f9d8e053c1, []int{43} } func (m *Connection) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Connection.Unmarshal(m, b) @@ -2402,6 +2586,8 @@ func init() { proto.RegisterMapType((map[string]*Header)(nil), "pb.Request.HeadersEntry") proto.RegisterType((*Auth)(nil), "pb.Auth") proto.RegisterMapType((map[string]string)(nil), "pb.Auth.MetadataEntry") + proto.RegisterType((*TokenEntry)(nil), "pb.TokenEntry") + proto.RegisterMapType((map[string]string)(nil), "pb.TokenEntry.MetaEntry") proto.RegisterType((*LeaseOptions)(nil), "pb.LeaseOptions") proto.RegisterType((*Secret)(nil), "pb.Secret") proto.RegisterType((*Response)(nil), "pb.Response") @@ -3370,151 +3556,163 @@ var _SystemView_serviceDesc = grpc.ServiceDesc{ } func init() { - proto.RegisterFile("logical/plugin/pb/backend.proto", fileDescriptor_backend_6306a5aa9d5ea026) + proto.RegisterFile("logical/plugin/pb/backend.proto", fileDescriptor_backend_2aa165f9d8e053c1) } -var fileDescriptor_backend_6306a5aa9d5ea026 = []byte{ - // 2258 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0x5f, 0x73, 0xdb, 0xc6, - 0x11, 0x1f, 0x92, 0x22, 0x09, 0x2e, 0x49, 0x51, 0x3a, 0x2b, 0x2e, 0x4c, 0x3b, 0x35, 0x8b, 0xd4, - 0xb6, 0xe2, 0x89, 0x29, 0x9b, 0x69, 0x5a, 0xa7, 0x9d, 0xa4, 0xa3, 0xc8, 0x8a, 0xa3, 0x46, 0x4a, - 0x34, 0x90, 0xdc, 0xf4, 0xdf, 0x0c, 0x73, 0x02, 0x56, 0x24, 0x46, 0x20, 0x80, 0x1e, 0x0e, 0xb2, - 0xf9, 0xd4, 0x6f, 0xd1, 0xb7, 0x7e, 0x86, 0xbe, 0xf6, 0xad, 0x6f, 0x9d, 0xce, 0xf4, 0xb9, 0x5f, - 0xa3, 0x9f, 0xa1, 0x73, 0x7f, 0x00, 0x1e, 0x48, 0xaa, 0x71, 0x67, 0xda, 0xb7, 0xdb, 0x3f, 0xb7, - 0x77, 0xbb, 0xd8, 0xfd, 0xed, 0x1e, 0xe0, 0x7e, 0x18, 0x4f, 0x02, 0x8f, 0x86, 0x7b, 0x49, 0x98, - 0x4d, 0x82, 0x68, 0x2f, 0xb9, 0xd8, 0xbb, 0xa0, 0xde, 0x15, 0x46, 0xfe, 0x30, 0x61, 0x31, 0x8f, - 0x49, 0x35, 0xb9, 0xe8, 0xdf, 0x9f, 0xc4, 0xf1, 0x24, 0xc4, 0x3d, 0xc9, 0xb9, 0xc8, 0x2e, 0xf7, - 0x78, 0x30, 0xc3, 0x94, 0xd3, 0x59, 0xa2, 0x94, 0xfa, 0xb7, 0x73, 0x2b, 0x81, 0x8f, 0x11, 0x0f, - 0xf8, 0x5c, 0xf3, 0x77, 0xca, 0xd6, 0x15, 0xd7, 0x69, 0x42, 0xfd, 0x70, 0x96, 0xf0, 0xb9, 0x33, - 0x80, 0xc6, 0x17, 0x48, 0x7d, 0x64, 0xe4, 0x36, 0x34, 0xa6, 0x72, 0x65, 0x57, 0x06, 0xb5, 0xdd, - 0x96, 0xab, 0x29, 0xe7, 0xb7, 0x00, 0xa7, 0x62, 0xcf, 0x21, 0x63, 0x31, 0x23, 0x77, 0xc0, 0x42, - 0xc6, 0xc6, 0x7c, 0x9e, 0xa0, 0x5d, 0x19, 0x54, 0x76, 0xbb, 0x6e, 0x13, 0x19, 0x3b, 0x9f, 0x27, - 0x48, 0xbe, 0x07, 0x62, 0x39, 0x9e, 0xa5, 0x13, 0xbb, 0x3a, 0xa8, 0x08, 0x0b, 0xc8, 0xd8, 0x49, - 0x3a, 0xc9, 0xf7, 0x78, 0xb1, 0x8f, 0x76, 0x6d, 0x50, 0xd9, 0xad, 0xc9, 0x3d, 0x07, 0xb1, 0x8f, - 0xce, 0x1f, 0x2b, 0x50, 0x3f, 0xa5, 0x7c, 0x9a, 0x12, 0x02, 0x1b, 0x2c, 0x8e, 0xb9, 0x3e, 0x5c, - 0xae, 0xc9, 0x2e, 0xf4, 0xb2, 0x88, 0x66, 0x7c, 0x2a, 0x3c, 0xf2, 0x28, 0x47, 0xdf, 0xae, 0x4a, - 0xf1, 0x32, 0x9b, 0xbc, 0x07, 0xdd, 0x30, 0xf6, 0x68, 0x38, 0x4e, 0x79, 0xcc, 0xe8, 0x44, 0x9c, - 0x23, 0xf4, 0x3a, 0x92, 0x79, 0xa6, 0x78, 0xe4, 0x31, 0x6c, 0xa7, 0x48, 0xc3, 0xf1, 0x6b, 0x46, - 0x93, 0x42, 0x71, 0x43, 0x19, 0x14, 0x82, 0x6f, 0x18, 0x4d, 0xb4, 0xae, 0xf3, 0xd7, 0x06, 0x34, - 0x5d, 0xfc, 0x7d, 0x86, 0x29, 0x27, 0x9b, 0x50, 0x0d, 0x7c, 0xe9, 0x6d, 0xcb, 0xad, 0x06, 0x3e, - 0x19, 0x02, 0x71, 0x31, 0x09, 0xc5, 0xd1, 0x41, 0x1c, 0x1d, 0x84, 0x59, 0xca, 0x91, 0x69, 0x9f, - 0xd7, 0x48, 0xc8, 0x3d, 0x68, 0xc5, 0x09, 0x32, 0xc9, 0x93, 0x01, 0x68, 0xb9, 0x0b, 0x86, 0x70, - 0x3c, 0xa1, 0x7c, 0x6a, 0x6f, 0x48, 0x81, 0x5c, 0x0b, 0x9e, 0x4f, 0x39, 0xb5, 0xeb, 0x8a, 0x27, - 0xd6, 0xc4, 0x81, 0x46, 0x8a, 0x1e, 0x43, 0x6e, 0x37, 0x06, 0x95, 0xdd, 0xf6, 0x08, 0x86, 0xc9, - 0xc5, 0xf0, 0x4c, 0x72, 0x5c, 0x2d, 0x21, 0xf7, 0x60, 0x43, 0xc4, 0xc5, 0x6e, 0x4a, 0x0d, 0x4b, - 0x68, 0xec, 0x67, 0x7c, 0xea, 0x4a, 0x2e, 0x19, 0x41, 0x53, 0x7d, 0xd3, 0xd4, 0xb6, 0x06, 0xb5, - 0xdd, 0xf6, 0xc8, 0x16, 0x0a, 0xda, 0xcb, 0xa1, 0x4a, 0x83, 0xf4, 0x30, 0xe2, 0x6c, 0xee, 0xe6, - 0x8a, 0xe4, 0x07, 0xd0, 0xf1, 0xc2, 0x00, 0x23, 0x3e, 0xe6, 0xf1, 0x15, 0x46, 0x76, 0x4b, 0xde, - 0xa8, 0xad, 0x78, 0xe7, 0x82, 0x45, 0x46, 0xf0, 0x8e, 0xa9, 0x32, 0xa6, 0x9e, 0x87, 0x69, 0x1a, - 0x33, 0x1b, 0xa4, 0xee, 0x2d, 0x43, 0x77, 0x5f, 0x8b, 0x84, 0x59, 0x3f, 0x48, 0x93, 0x90, 0xce, - 0xc7, 0x11, 0x9d, 0xa1, 0xdd, 0x56, 0x66, 0x35, 0xef, 0x2b, 0x3a, 0x43, 0x72, 0x1f, 0xda, 0xb3, - 0x38, 0x8b, 0xf8, 0x38, 0x89, 0x83, 0x88, 0xdb, 0x1d, 0xa9, 0x01, 0x92, 0x75, 0x2a, 0x38, 0xe4, - 0x5d, 0x50, 0x94, 0x4a, 0xc6, 0xae, 0x8a, 0xab, 0xe4, 0xc8, 0x74, 0x7c, 0x00, 0x9b, 0x4a, 0x5c, - 0xdc, 0x67, 0x53, 0xaa, 0x74, 0x25, 0xb7, 0xb8, 0xc9, 0x53, 0x68, 0xc9, 0x7c, 0x08, 0xa2, 0xcb, - 0xd8, 0xee, 0xc9, 0xb8, 0xdd, 0x32, 0xc2, 0x22, 0x72, 0xe2, 0x28, 0xba, 0x8c, 0x5d, 0xeb, 0xb5, - 0x5e, 0x91, 0x4f, 0xe0, 0x6e, 0xc9, 0x5f, 0x86, 0x33, 0x1a, 0x44, 0x41, 0x34, 0x19, 0x67, 0x29, - 0xa6, 0xf6, 0x96, 0xcc, 0x70, 0xdb, 0xf0, 0xda, 0xcd, 0x15, 0x5e, 0xa5, 0x98, 0x92, 0xbb, 0xd0, - 0x52, 0x05, 0x3a, 0x0e, 0x7c, 0x7b, 0x5b, 0x5e, 0xc9, 0x52, 0x8c, 0x23, 0x9f, 0x3c, 0x82, 0x5e, - 0x12, 0x87, 0x81, 0x37, 0x1f, 0xc7, 0xd7, 0xc8, 0x58, 0xe0, 0xa3, 0x4d, 0x06, 0x95, 0x5d, 0xcb, - 0xdd, 0x54, 0xec, 0xaf, 0x35, 0x77, 0x5d, 0x69, 0xdc, 0x92, 0x8a, 0x2b, 0xa5, 0x31, 0x04, 0xf0, - 0xe2, 0x28, 0x42, 0x4f, 0xa6, 0xdf, 0x8e, 0xf4, 0x70, 0x53, 0x78, 0x78, 0x50, 0x70, 0x5d, 0x43, - 0xa3, 0xff, 0x39, 0x74, 0xcc, 0x54, 0x20, 0x5b, 0x50, 0xbb, 0xc2, 0xb9, 0x4e, 0x7f, 0xb1, 0x24, - 0x03, 0xa8, 0x5f, 0xd3, 0x30, 0x43, 0x99, 0xf2, 0x3a, 0x11, 0xd5, 0x16, 0x57, 0x09, 0x7e, 0x5a, - 0x7d, 0x5e, 0x71, 0xfe, 0xb4, 0x01, 0x1b, 0x22, 0xf9, 0xc8, 0x47, 0xd0, 0x0d, 0x91, 0xa6, 0x38, - 0x8e, 0x13, 0x71, 0x40, 0x2a, 0x4d, 0xb5, 0x47, 0x5b, 0x62, 0xdb, 0xb1, 0x10, 0x7c, 0xad, 0xf8, - 0x6e, 0x27, 0x34, 0x28, 0x51, 0xd2, 0x41, 0xc4, 0x91, 0x45, 0x34, 0x1c, 0xcb, 0x62, 0x50, 0x05, - 0xd6, 0xc9, 0x99, 0x2f, 0x44, 0x51, 0x2c, 0xe7, 0x51, 0x6d, 0x35, 0x8f, 0xfa, 0x60, 0xc9, 0xd8, - 0x05, 0x98, 0xea, 0x62, 0x2f, 0x68, 0x32, 0x02, 0x6b, 0x86, 0x9c, 0xea, 0x5a, 0x13, 0x25, 0x71, - 0x3b, 0xaf, 0x99, 0xe1, 0x89, 0x16, 0xa8, 0x82, 0x28, 0xf4, 0x56, 0x2a, 0xa2, 0xb1, 0x5a, 0x11, - 0x7d, 0xb0, 0x8a, 0xa4, 0x6b, 0xaa, 0x2f, 0x9c, 0xd3, 0x02, 0x66, 0x13, 0x64, 0x41, 0xec, 0xdb, - 0x96, 0x4c, 0x14, 0x4d, 0x09, 0x90, 0x8c, 0xb2, 0x99, 0x4a, 0xa1, 0x96, 0x02, 0xc9, 0x28, 0x9b, - 0xad, 0x66, 0x0c, 0x2c, 0x65, 0xcc, 0x0f, 0xa1, 0x4e, 0xc3, 0x80, 0xa6, 0xb2, 0x84, 0xc4, 0x97, - 0xd5, 0x78, 0x3f, 0xdc, 0x17, 0x5c, 0x57, 0x09, 0xc9, 0x87, 0xd0, 0x9d, 0xb0, 0x38, 0x4b, 0xc6, - 0x92, 0xc4, 0xd4, 0xee, 0x48, 0x6f, 0x97, 0xb5, 0x3b, 0x52, 0x69, 0x5f, 0xe9, 0x88, 0x0a, 0xbc, - 0x88, 0xb3, 0xc8, 0x1f, 0x7b, 0x81, 0xcf, 0x52, 0xbb, 0x2b, 0x83, 0x07, 0x92, 0x75, 0x20, 0x38, - 0xfd, 0x9f, 0x41, 0xb7, 0x14, 0xa5, 0x35, 0xb9, 0xb2, 0x63, 0xe6, 0x4a, 0xcb, 0xcc, 0x8f, 0x3f, - 0x57, 0xa0, 0x63, 0x7e, 0x7e, 0xb1, 0xf9, 0xfc, 0xfc, 0x58, 0x6e, 0xae, 0xb9, 0x62, 0x29, 0x80, - 0x93, 0x61, 0x84, 0xaf, 0xe9, 0x45, 0xa8, 0x0c, 0x58, 0xee, 0x82, 0x21, 0xa4, 0x41, 0xe4, 0x31, - 0x9c, 0x61, 0xc4, 0x75, 0x5f, 0x59, 0x30, 0xc8, 0xc7, 0x00, 0x41, 0x9a, 0x66, 0x38, 0x16, 0x8d, - 0x52, 0x82, 0x6b, 0x7b, 0xd4, 0x1f, 0xaa, 0x2e, 0x3a, 0xcc, 0xbb, 0xe8, 0xf0, 0x3c, 0xef, 0xa2, - 0x6e, 0x4b, 0x6a, 0x0b, 0x5a, 0x7c, 0xa2, 0x13, 0xfa, 0x46, 0xdc, 0xa5, 0xae, 0x3e, 0x91, 0xa2, - 0x9c, 0x3f, 0x40, 0x43, 0xe1, 0xed, 0xff, 0x35, 0xa5, 0xef, 0x80, 0xa5, 0x6c, 0x07, 0xbe, 0x4e, - 0xe7, 0xa6, 0xa4, 0x8f, 0x7c, 0xe7, 0x1f, 0x15, 0xb0, 0x5c, 0x4c, 0x93, 0x38, 0x4a, 0xd1, 0xe8, - 0x07, 0x95, 0xef, 0xec, 0x07, 0xd5, 0xb5, 0xfd, 0x20, 0xef, 0x32, 0x35, 0xa3, 0xcb, 0xf4, 0xc1, - 0x62, 0xe8, 0x07, 0x0c, 0x3d, 0xae, 0x3b, 0x52, 0x41, 0x0b, 0xd9, 0x6b, 0xca, 0x04, 0x90, 0xa5, - 0xb2, 0x5a, 0x5a, 0x6e, 0x41, 0x93, 0x67, 0x26, 0x8c, 0xaa, 0x06, 0xb5, 0xa3, 0x60, 0x54, 0x5d, - 0x77, 0x15, 0x47, 0x9d, 0xbf, 0x57, 0x61, 0x6b, 0x59, 0xbc, 0x26, 0x09, 0x76, 0xa0, 0xae, 0x0a, - 0x4d, 0x67, 0x10, 0x5f, 0x29, 0xb1, 0xda, 0x52, 0x89, 0xfd, 0x1c, 0xba, 0x1e, 0x43, 0xd9, 0x5d, - 0xdf, 0xf6, 0xeb, 0x77, 0xf2, 0x0d, 0x32, 0x01, 0xde, 0x87, 0x2d, 0x71, 0xcb, 0x04, 0xfd, 0x45, - 0xf3, 0x50, 0xad, 0xb8, 0xa7, 0xf9, 0x45, 0xfb, 0x78, 0x0c, 0xdb, 0xb9, 0xea, 0xa2, 0x46, 0x1b, - 0x25, 0xdd, 0xc3, 0xbc, 0x54, 0x6f, 0x43, 0xe3, 0x32, 0x66, 0x33, 0xca, 0x35, 0x28, 0x68, 0x4a, - 0xa4, 0x45, 0x71, 0x5f, 0x39, 0x0a, 0x58, 0x2a, 0x2d, 0x72, 0xa6, 0x18, 0x90, 0x04, 0x08, 0x14, - 0xc3, 0x8b, 0x04, 0x08, 0xcb, 0xb5, 0xf2, 0xa1, 0xc5, 0xf9, 0x15, 0xf4, 0x96, 0xfa, 0xd5, 0x9a, - 0x40, 0x2e, 0x8e, 0xaf, 0x96, 0x8e, 0x2f, 0x59, 0xae, 0x2d, 0x59, 0xfe, 0x35, 0x6c, 0x7f, 0x41, - 0x23, 0x3f, 0x44, 0x6d, 0x7f, 0x9f, 0x4d, 0x52, 0xd1, 0x79, 0xf5, 0xf8, 0x34, 0xd6, 0x83, 0x51, - 0xd7, 0x6d, 0x69, 0xce, 0x91, 0x4f, 0x1e, 0x40, 0x93, 0x29, 0x6d, 0x9d, 0x78, 0x6d, 0xa3, 0xa1, - 0xba, 0xb9, 0xcc, 0xf9, 0x16, 0x48, 0xc9, 0xb4, 0x98, 0x9c, 0xe6, 0x64, 0x57, 0x24, 0xa0, 0x4a, - 0x0a, 0x9d, 0xd8, 0x1d, 0x33, 0x8f, 0xdc, 0x42, 0x4a, 0x06, 0x50, 0x43, 0xc6, 0xf4, 0x11, 0xb2, - 0xa3, 0x2d, 0xe6, 0x54, 0x57, 0x88, 0x9c, 0x1f, 0xc1, 0xf6, 0x59, 0x82, 0x5e, 0x40, 0x43, 0x39, - 0x63, 0xaa, 0x03, 0xee, 0x43, 0x5d, 0x04, 0x39, 0xaf, 0xd9, 0x96, 0xdc, 0x28, 0xc5, 0x8a, 0xef, - 0x7c, 0x0b, 0xb6, 0xba, 0xd7, 0xe1, 0x9b, 0x20, 0xe5, 0x18, 0x79, 0x78, 0x30, 0x45, 0xef, 0xea, - 0x7f, 0xe8, 0xf9, 0x35, 0xdc, 0x59, 0x77, 0x42, 0x7e, 0xbf, 0xb6, 0x27, 0xa8, 0xf1, 0xa5, 0x00, - 0x5a, 0x79, 0x86, 0xe5, 0x82, 0x64, 0x7d, 0x2e, 0x38, 0xe2, 0x3b, 0xa2, 0xd8, 0x97, 0x6a, 0x48, - 0xd4, 0x54, 0x1e, 0x8f, 0xda, 0xcd, 0xf1, 0xf8, 0x4b, 0x05, 0x5a, 0x67, 0xc8, 0xb3, 0x44, 0xfa, - 0x72, 0x17, 0x5a, 0x17, 0x2c, 0xbe, 0x42, 0xb6, 0x70, 0xc5, 0x52, 0x8c, 0x23, 0x9f, 0x3c, 0x83, - 0xc6, 0x41, 0x1c, 0x5d, 0x06, 0x13, 0x39, 0x71, 0xb7, 0x47, 0x77, 0x14, 0xba, 0xe8, 0xbd, 0x43, - 0x25, 0x53, 0xad, 0x51, 0x2b, 0x92, 0x01, 0xb4, 0xf5, 0xbb, 0xe5, 0xd5, 0xab, 0xa3, 0x17, 0x79, - 0x2b, 0x36, 0x58, 0xfd, 0x8f, 0xa1, 0x6d, 0x6c, 0xfc, 0xaf, 0xba, 0xc5, 0xf7, 0x01, 0xe4, 0xe9, - 0x2a, 0x46, 0x5b, 0xca, 0x55, 0xbd, 0x53, 0xb8, 0x76, 0x1f, 0x5a, 0x62, 0xea, 0x53, 0x62, 0x02, - 0x1b, 0xc6, 0x03, 0x45, 0xae, 0x9d, 0x07, 0xb0, 0x7d, 0x14, 0x5d, 0xd3, 0x30, 0xf0, 0x29, 0xc7, - 0x2f, 0x71, 0x2e, 0x43, 0xb0, 0x72, 0x03, 0xe7, 0x0c, 0x3a, 0xfa, 0x09, 0xf0, 0x56, 0x77, 0xec, - 0xe8, 0x3b, 0xfe, 0xe7, 0x22, 0x7a, 0x1f, 0x7a, 0xda, 0xe8, 0x71, 0xa0, 0x4b, 0x48, 0x8c, 0x01, - 0x0c, 0x2f, 0x83, 0x37, 0xda, 0xb4, 0xa6, 0x9c, 0xe7, 0xb0, 0x65, 0xa8, 0x16, 0xee, 0x5c, 0xe1, - 0x3c, 0xcd, 0x9f, 0x46, 0x62, 0x9d, 0x47, 0xa0, 0xba, 0x88, 0x80, 0x03, 0x9b, 0x7a, 0xe7, 0x4b, - 0xe4, 0x37, 0x78, 0xf7, 0x65, 0x71, 0x91, 0x97, 0xa8, 0x8d, 0x3f, 0x84, 0x3a, 0x0a, 0x4f, 0xcd, - 0x16, 0x66, 0x46, 0xc0, 0x55, 0xe2, 0x35, 0x07, 0x3e, 0x2f, 0x0e, 0x3c, 0xcd, 0xd4, 0x81, 0x6f, - 0x69, 0xcb, 0x79, 0xaf, 0xb8, 0xc6, 0x69, 0xc6, 0x6f, 0xfa, 0xa2, 0x0f, 0x60, 0x5b, 0x2b, 0xbd, - 0xc0, 0x10, 0x39, 0xde, 0xe0, 0xd2, 0x43, 0x20, 0x25, 0xb5, 0x9b, 0xcc, 0xdd, 0x03, 0xeb, 0xfc, - 0xfc, 0xb8, 0x90, 0x96, 0xb1, 0xd1, 0xf9, 0x04, 0xb6, 0xcf, 0x32, 0x3f, 0x3e, 0x65, 0xc1, 0x75, - 0x10, 0xe2, 0x44, 0x1d, 0x96, 0xbf, 0xcc, 0x2a, 0xc6, 0xcb, 0x6c, 0x6d, 0x37, 0x72, 0x76, 0x81, - 0x94, 0xb6, 0x17, 0xdf, 0x2d, 0xcd, 0xfc, 0x58, 0x97, 0xb0, 0x5c, 0x3b, 0xbb, 0xd0, 0x39, 0xa7, - 0xa2, 0xdf, 0xfb, 0x4a, 0xc7, 0x86, 0x26, 0x57, 0xb4, 0x56, 0xcb, 0x49, 0x67, 0x04, 0x3b, 0x07, - 0xd4, 0x9b, 0x06, 0xd1, 0xe4, 0x45, 0x90, 0x8a, 0x81, 0x47, 0xef, 0xe8, 0x83, 0xe5, 0x6b, 0x86, - 0xde, 0x52, 0xd0, 0xce, 0x13, 0x78, 0xc7, 0x78, 0x7f, 0x9e, 0x71, 0x9a, 0xc7, 0x63, 0x07, 0xea, - 0xa9, 0xa0, 0xe4, 0x8e, 0xba, 0xab, 0x08, 0xe7, 0x2b, 0xd8, 0x31, 0x1b, 0xb0, 0x18, 0x3f, 0x72, - 0xc7, 0xe5, 0x60, 0x50, 0x31, 0x06, 0x03, 0x1d, 0xb3, 0xea, 0xa2, 0x9f, 0x6c, 0x41, 0xed, 0x17, - 0xdf, 0x9c, 0xeb, 0x64, 0x17, 0x4b, 0xe7, 0x77, 0xe2, 0xf8, 0xb2, 0x3d, 0x75, 0x7c, 0x69, 0x3a, - 0xa8, 0xbc, 0xcd, 0x74, 0xb0, 0x26, 0xdf, 0x9e, 0xc0, 0xf6, 0x49, 0x18, 0x7b, 0x57, 0x87, 0x91, - 0x11, 0x0d, 0x1b, 0x9a, 0x18, 0x99, 0xc1, 0xc8, 0x49, 0xe7, 0x11, 0xf4, 0x8e, 0xc5, 0xeb, 0xff, - 0x44, 0x3c, 0xf7, 0x8a, 0x28, 0xc8, 0x1f, 0x02, 0x5a, 0x55, 0x11, 0xce, 0x13, 0xd8, 0xd4, 0x2d, - 0x3a, 0xba, 0x8c, 0x73, 0x64, 0x5c, 0x34, 0xf3, 0x4a, 0x79, 0xe0, 0x76, 0x8e, 0xa1, 0xb7, 0x50, - 0x57, 0x76, 0x1f, 0x41, 0x43, 0x89, 0xb5, 0x6f, 0xbd, 0x62, 0xac, 0x56, 0x9a, 0xae, 0x16, 0xaf, - 0x71, 0x6a, 0x06, 0x9b, 0xa7, 0xf2, 0xc7, 0xcc, 0x61, 0x74, 0xad, 0x8c, 0x1d, 0x01, 0x51, 0xbf, - 0x6a, 0xc6, 0x18, 0x5d, 0x07, 0x2c, 0x8e, 0xe4, 0x7c, 0x5b, 0xd1, 0x23, 0x4c, 0x6e, 0xb8, 0xd8, - 0x94, 0x6b, 0xb8, 0xdb, 0xc9, 0x32, 0x6b, 0x6d, 0x0c, 0x61, 0xf1, 0xec, 0x13, 0xad, 0x86, 0xe1, - 0x2c, 0xe6, 0x38, 0xa6, 0xbe, 0x9f, 0x57, 0x0b, 0x28, 0xd6, 0xbe, 0xef, 0xb3, 0xd1, 0xbf, 0xaa, - 0xd0, 0xfc, 0x4c, 0x01, 0x38, 0xf9, 0x14, 0xba, 0xa5, 0x76, 0x4d, 0xde, 0x91, 0xef, 0xbe, 0xe5, - 0xe1, 0xa0, 0x7f, 0x7b, 0x85, 0xad, 0xfc, 0x7a, 0x0a, 0x1d, 0xb3, 0x19, 0x13, 0xd9, 0x78, 0xe5, - 0x4f, 0xa8, 0xbe, 0xb4, 0xb4, 0xda, 0xa9, 0xcf, 0x60, 0x67, 0x5d, 0x9b, 0x24, 0xf7, 0x16, 0x27, - 0xac, 0xb6, 0xe8, 0xfe, 0xbb, 0x37, 0x49, 0xf3, 0xf6, 0xda, 0x3c, 0x08, 0x91, 0x46, 0x59, 0x62, - 0xde, 0x60, 0xb1, 0x24, 0xcf, 0xa0, 0x5b, 0x6a, 0x14, 0xca, 0xcf, 0x95, 0xde, 0x61, 0x6e, 0x79, - 0x08, 0x75, 0xd9, 0x9c, 0x48, 0xb7, 0xd4, 0x25, 0xfb, 0x9b, 0x05, 0xa9, 0xce, 0x1e, 0xc0, 0x86, - 0xfc, 0x35, 0x61, 0x1c, 0x2c, 0x77, 0x14, 0x9d, 0x6b, 0xf4, 0xcf, 0x0a, 0x34, 0xf3, 0xdf, 0x55, - 0xcf, 0x60, 0x43, 0xf4, 0x00, 0x72, 0xcb, 0x80, 0xd1, 0xbc, 0x7f, 0xf4, 0x77, 0x96, 0x98, 0xea, - 0x80, 0x21, 0xd4, 0x5e, 0x22, 0x27, 0xc4, 0x10, 0xea, 0x66, 0xd0, 0xbf, 0x55, 0xe6, 0x15, 0xfa, - 0xa7, 0x59, 0x59, 0x5f, 0x63, 0x79, 0x49, 0xbf, 0x40, 0xe9, 0x9f, 0x40, 0x43, 0xa1, 0xac, 0x0a, - 0xca, 0x0a, 0x3e, 0xab, 0x8f, 0xbf, 0x8a, 0xc7, 0xa3, 0xbf, 0x6d, 0x00, 0x9c, 0xcd, 0x53, 0x8e, - 0xb3, 0x5f, 0x06, 0xf8, 0x9a, 0x3c, 0x86, 0xde, 0x0b, 0xbc, 0xa4, 0x59, 0xc8, 0xe5, 0x6b, 0x49, - 0xa0, 0x89, 0x11, 0x13, 0x39, 0xf0, 0x15, 0x60, 0xfd, 0x10, 0xda, 0x27, 0xf4, 0xcd, 0x77, 0xeb, - 0x7d, 0x0a, 0xdd, 0x12, 0x06, 0xeb, 0x2b, 0x2e, 0xa3, 0xba, 0xbe, 0xe2, 0x2a, 0x5a, 0x3f, 0x84, - 0xa6, 0x46, 0x66, 0xf3, 0x0c, 0xd9, 0xc3, 0x4a, 0x88, 0xfd, 0x63, 0xe8, 0x2d, 0xe1, 0xb2, 0xa9, - 0x2f, 0x7f, 0xa9, 0xad, 0xc5, 0xed, 0xe7, 0xe2, 0xb5, 0x53, 0xc6, 0x66, 0x73, 0xe3, 0x1d, 0x85, - 0x87, 0xeb, 0xc0, 0xfb, 0x65, 0xf9, 0x9d, 0x24, 0x5f, 0x89, 0xf6, 0x32, 0x7c, 0xe6, 0xe0, 0x9d, - 0x1b, 0x5a, 0x07, 0xc3, 0x4f, 0xa1, 0x63, 0x22, 0xe8, 0x4a, 0x09, 0xae, 0xc2, 0xeb, 0x07, 0x00, - 0x0b, 0x10, 0x35, 0xf5, 0x65, 0x7a, 0x2c, 0xe3, 0xeb, 0x47, 0x00, 0x0b, 0x68, 0x54, 0x59, 0x55, - 0x46, 0x56, 0xb5, 0x6d, 0x19, 0x3e, 0x1f, 0x43, 0xab, 0x80, 0x33, 0xf3, 0x0c, 0x69, 0xa0, 0x8c, - 0x8e, 0x9f, 0x0d, 0x7f, 0xf3, 0xc1, 0x24, 0xe0, 0xd3, 0xec, 0x62, 0xe8, 0xc5, 0xb3, 0xbd, 0x29, - 0x4d, 0xa7, 0x81, 0x17, 0xb3, 0x64, 0xef, 0x5a, 0x24, 0xd3, 0xde, 0xca, 0x9f, 0xf4, 0x8b, 0x86, - 0x7c, 0xec, 0x7d, 0xf8, 0xef, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc6, 0x0c, 0x9b, 0x12, 0x65, 0x17, - 0x00, 0x00, +var fileDescriptor_backend_2aa165f9d8e053c1 = []byte{ + // 2457 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x59, 0x5f, 0x73, 0xdb, 0xc6, + 0x11, 0x1f, 0xfe, 0x27, 0x97, 0xff, 0xa4, 0xb3, 0xa2, 0xc2, 0x8c, 0x53, 0x33, 0x48, 0x6d, 0x2b, + 0xae, 0x4d, 0xd9, 0x4a, 0xd3, 0x38, 0xed, 0x24, 0x1d, 0x45, 0x56, 0x1c, 0x35, 0x52, 0xa2, 0x81, + 0xe8, 0xa6, 0xff, 0x66, 0x90, 0x23, 0x70, 0xa2, 0x30, 0x02, 0x01, 0xf4, 0x70, 0x90, 0xc5, 0xa7, + 0x7e, 0x8b, 0x7e, 0x8d, 0xbe, 0x75, 0xfa, 0xd6, 0xb7, 0x4e, 0x67, 0xfa, 0xdc, 0xaf, 0xd1, 0x99, + 0x7e, 0x83, 0xce, 0xed, 0x1d, 0x40, 0x80, 0xa4, 0x62, 0x67, 0xa6, 0x7d, 0xbb, 0xdb, 0xdd, 0xdb, + 0xbb, 0x5b, 0xfc, 0xf6, 0xb7, 0x7b, 0x24, 0xdc, 0xf5, 0xc3, 0xa9, 0xe7, 0x50, 0x7f, 0x37, 0xf2, + 0x93, 0xa9, 0x17, 0xec, 0x46, 0x93, 0xdd, 0x09, 0x75, 0x2e, 0x59, 0xe0, 0x8e, 0x22, 0x1e, 0x8a, + 0x90, 0x94, 0xa3, 0xc9, 0xe0, 0xee, 0x34, 0x0c, 0xa7, 0x3e, 0xdb, 0x45, 0xc9, 0x24, 0x39, 0xdf, + 0x15, 0xde, 0x8c, 0xc5, 0x82, 0xce, 0x22, 0x65, 0x34, 0xd8, 0x4e, 0xbd, 0x78, 0x2e, 0x0b, 0x84, + 0x27, 0xe6, 0x5a, 0xbe, 0x55, 0xf4, 0xae, 0xa4, 0x66, 0x03, 0x6a, 0x87, 0xb3, 0x48, 0xcc, 0xcd, + 0x21, 0xd4, 0xbf, 0x60, 0xd4, 0x65, 0x9c, 0x6c, 0x43, 0xfd, 0x02, 0x47, 0x46, 0x69, 0x58, 0xd9, + 0x69, 0x59, 0x7a, 0x66, 0xfe, 0x0e, 0xe0, 0x54, 0xae, 0x39, 0xe4, 0x3c, 0xe4, 0xe4, 0x36, 0x34, + 0x19, 0xe7, 0xb6, 0x98, 0x47, 0xcc, 0x28, 0x0d, 0x4b, 0x3b, 0x5d, 0xab, 0xc1, 0x38, 0x1f, 0xcf, + 0x23, 0x46, 0x7e, 0x00, 0x72, 0x68, 0xcf, 0xe2, 0xa9, 0x51, 0x1e, 0x96, 0xa4, 0x07, 0xc6, 0xf9, + 0x49, 0x3c, 0x4d, 0xd7, 0x38, 0xa1, 0xcb, 0x8c, 0xca, 0xb0, 0xb4, 0x53, 0xc1, 0x35, 0x07, 0xa1, + 0xcb, 0xcc, 0x3f, 0x95, 0xa0, 0x76, 0x4a, 0xc5, 0x45, 0x4c, 0x08, 0x54, 0x79, 0x18, 0x0a, 0xbd, + 0x39, 0x8e, 0xc9, 0x0e, 0xf4, 0x93, 0x80, 0x26, 0xe2, 0x42, 0xde, 0xc8, 0xa1, 0x82, 0xb9, 0x46, + 0x19, 0xd5, 0xcb, 0x62, 0xf2, 0x1e, 0x74, 0xfd, 0xd0, 0xa1, 0xbe, 0x1d, 0x8b, 0x90, 0xd3, 0xa9, + 0xdc, 0x47, 0xda, 0x75, 0x50, 0x78, 0xa6, 0x64, 0xe4, 0x21, 0x6c, 0xc6, 0x8c, 0xfa, 0xf6, 0x2b, + 0x4e, 0xa3, 0xcc, 0xb0, 0xaa, 0x1c, 0x4a, 0xc5, 0x37, 0x9c, 0x46, 0xda, 0xd6, 0xfc, 0x5b, 0x1d, + 0x1a, 0x16, 0xfb, 0x43, 0xc2, 0x62, 0x41, 0x7a, 0x50, 0xf6, 0x5c, 0xbc, 0x6d, 0xcb, 0x2a, 0x7b, + 0x2e, 0x19, 0x01, 0xb1, 0x58, 0xe4, 0xcb, 0xad, 0xbd, 0x30, 0x38, 0xf0, 0x93, 0x58, 0x30, 0xae, + 0xef, 0xbc, 0x46, 0x43, 0xee, 0x40, 0x2b, 0x8c, 0x18, 0x47, 0x19, 0x06, 0xa0, 0x65, 0x2d, 0x04, + 0xf2, 0xe2, 0x11, 0x15, 0x17, 0x46, 0x15, 0x15, 0x38, 0x96, 0x32, 0x97, 0x0a, 0x6a, 0xd4, 0x94, + 0x4c, 0x8e, 0x89, 0x09, 0xf5, 0x98, 0x39, 0x9c, 0x09, 0xa3, 0x3e, 0x2c, 0xed, 0xb4, 0xf7, 0x60, + 0x14, 0x4d, 0x46, 0x67, 0x28, 0xb1, 0xb4, 0x86, 0xdc, 0x81, 0xaa, 0x8c, 0x8b, 0xd1, 0x40, 0x8b, + 0xa6, 0xb4, 0xd8, 0x4f, 0xc4, 0x85, 0x85, 0x52, 0xb2, 0x07, 0x0d, 0xf5, 0x4d, 0x63, 0xa3, 0x39, + 0xac, 0xec, 0xb4, 0xf7, 0x0c, 0x69, 0xa0, 0x6f, 0x39, 0x52, 0x30, 0x88, 0x0f, 0x03, 0xc1, 0xe7, + 0x56, 0x6a, 0x48, 0xde, 0x85, 0x8e, 0xe3, 0x7b, 0x2c, 0x10, 0xb6, 0x08, 0x2f, 0x59, 0x60, 0xb4, + 0xf0, 0x44, 0x6d, 0x25, 0x1b, 0x4b, 0x11, 0xd9, 0x83, 0xb7, 0xf2, 0x26, 0x36, 0x75, 0x1c, 0x16, + 0xc7, 0x21, 0x37, 0x00, 0x6d, 0x6f, 0xe5, 0x6c, 0xf7, 0xb5, 0x4a, 0xba, 0x75, 0xbd, 0x38, 0xf2, + 0xe9, 0xdc, 0x0e, 0xe8, 0x8c, 0x19, 0x6d, 0xe5, 0x56, 0xcb, 0xbe, 0xa2, 0x33, 0x46, 0xee, 0x42, + 0x7b, 0x16, 0x26, 0x81, 0xb0, 0xa3, 0xd0, 0x0b, 0x84, 0xd1, 0x41, 0x0b, 0x40, 0xd1, 0xa9, 0x94, + 0x90, 0x77, 0x40, 0xcd, 0x14, 0x18, 0xbb, 0x2a, 0xae, 0x28, 0x41, 0x38, 0xde, 0x83, 0x9e, 0x52, + 0x67, 0xe7, 0xe9, 0xa1, 0x49, 0x17, 0xa5, 0xd9, 0x49, 0x9e, 0x40, 0x0b, 0xf1, 0xe0, 0x05, 0xe7, + 0xa1, 0xd1, 0xc7, 0xb8, 0xdd, 0xca, 0x85, 0x45, 0x62, 0xe2, 0x28, 0x38, 0x0f, 0xad, 0xe6, 0x2b, + 0x3d, 0x22, 0x9f, 0xc0, 0xdb, 0x85, 0xfb, 0x72, 0x36, 0xa3, 0x5e, 0xe0, 0x05, 0x53, 0x3b, 0x89, + 0x59, 0x6c, 0x6c, 0x20, 0xc2, 0x8d, 0xdc, 0xad, 0xad, 0xd4, 0xe0, 0x65, 0xcc, 0x62, 0xf2, 0x36, + 0xb4, 0x54, 0x82, 0xda, 0x9e, 0x6b, 0x6c, 0xe2, 0x91, 0x9a, 0x4a, 0x70, 0xe4, 0x92, 0x07, 0xd0, + 0x8f, 0x42, 0xdf, 0x73, 0xe6, 0x76, 0x78, 0xc5, 0x38, 0xf7, 0x5c, 0x66, 0x90, 0x61, 0x69, 0xa7, + 0x69, 0xf5, 0x94, 0xf8, 0x6b, 0x2d, 0x5d, 0x97, 0x1a, 0xb7, 0xd0, 0x70, 0x25, 0x35, 0x46, 0x00, + 0x4e, 0x18, 0x04, 0xcc, 0x41, 0xf8, 0x6d, 0xe1, 0x0d, 0x7b, 0xf2, 0x86, 0x07, 0x99, 0xd4, 0xca, + 0x59, 0x0c, 0x3e, 0x87, 0x4e, 0x1e, 0x0a, 0x64, 0x03, 0x2a, 0x97, 0x6c, 0xae, 0xe1, 0x2f, 0x87, + 0x64, 0x08, 0xb5, 0x2b, 0xea, 0x27, 0x0c, 0x21, 0xaf, 0x81, 0xa8, 0x96, 0x58, 0x4a, 0xf1, 0xb3, + 0xf2, 0xb3, 0x92, 0xf9, 0x9f, 0x2a, 0x54, 0x25, 0xf8, 0xc8, 0x87, 0xd0, 0xf5, 0x19, 0x8d, 0x99, + 0x1d, 0x46, 0x72, 0x83, 0x18, 0x5d, 0xb5, 0xf7, 0x36, 0xe4, 0xb2, 0x63, 0xa9, 0xf8, 0x5a, 0xc9, + 0xad, 0x8e, 0x9f, 0x9b, 0xc9, 0x94, 0xf6, 0x02, 0xc1, 0x78, 0x40, 0x7d, 0x1b, 0x93, 0x41, 0x25, + 0x58, 0x27, 0x15, 0x3e, 0x97, 0x49, 0xb1, 0x8c, 0xa3, 0xca, 0x2a, 0x8e, 0x06, 0xd0, 0xc4, 0xd8, + 0x79, 0x2c, 0xd6, 0xc9, 0x9e, 0xcd, 0xc9, 0x1e, 0x34, 0x67, 0x4c, 0x50, 0x9d, 0x6b, 0x32, 0x25, + 0xb6, 0xd3, 0x9c, 0x19, 0x9d, 0x68, 0x85, 0x4a, 0x88, 0xcc, 0x6e, 0x25, 0x23, 0xea, 0xab, 0x19, + 0x31, 0x80, 0x66, 0x06, 0xba, 0x86, 0xfa, 0xc2, 0xe9, 0x5c, 0xd2, 0x6c, 0xc4, 0xb8, 0x17, 0xba, + 0x46, 0x13, 0x81, 0xa2, 0x67, 0x92, 0x24, 0x83, 0x64, 0xa6, 0x20, 0xd4, 0x52, 0x24, 0x19, 0x24, + 0xb3, 0x55, 0xc4, 0xc0, 0x12, 0x62, 0x7e, 0x04, 0x35, 0xea, 0x7b, 0x34, 0xc6, 0x14, 0x92, 0x5f, + 0x56, 0xf3, 0xfd, 0x68, 0x5f, 0x4a, 0x2d, 0xa5, 0x24, 0x1f, 0x40, 0x77, 0xca, 0xc3, 0x24, 0xb2, + 0x71, 0xca, 0x62, 0xa3, 0x83, 0xb7, 0x5d, 0xb6, 0xee, 0xa0, 0xd1, 0xbe, 0xb2, 0x91, 0x19, 0x38, + 0x09, 0x93, 0xc0, 0xb5, 0x1d, 0xcf, 0xe5, 0xb1, 0xd1, 0xc5, 0xe0, 0x01, 0x8a, 0x0e, 0xa4, 0x44, + 0xa6, 0x98, 0x4a, 0x81, 0x2c, 0xc0, 0x3d, 0xb4, 0xe9, 0xa2, 0xf4, 0x34, 0x8d, 0xf2, 0x8f, 0x61, + 0x33, 0x2d, 0x4a, 0x0b, 0xcb, 0x3e, 0x5a, 0x6e, 0xa4, 0x8a, 0xd4, 0x78, 0xf0, 0x73, 0xe8, 0x16, + 0x22, 0xbf, 0x06, 0x7f, 0x5b, 0x79, 0xfc, 0xb5, 0xf2, 0x98, 0xfb, 0x4b, 0x15, 0x00, 0x3f, 0x81, + 0x5a, 0xba, 0x4c, 0xdc, 0xf9, 0xef, 0x52, 0x5e, 0xf3, 0x5d, 0x28, 0x67, 0x81, 0xd0, 0x18, 0xd2, + 0xb3, 0xef, 0x84, 0x4f, 0x4a, 0xdd, 0xb5, 0x1c, 0x75, 0x3f, 0x82, 0xaa, 0x84, 0x8a, 0x51, 0x5f, + 0x30, 0xec, 0xe2, 0x44, 0x08, 0x2a, 0x05, 0x28, 0xb4, 0x5a, 0xc1, 0x6f, 0x63, 0x15, 0xbf, 0x79, + 0x60, 0x34, 0x8b, 0xc0, 0x78, 0x0f, 0xba, 0x0e, 0x67, 0x58, 0x46, 0x6c, 0xd9, 0x0f, 0x68, 0xe0, + 0x74, 0x52, 0xe1, 0xd8, 0x9b, 0x31, 0x19, 0x3f, 0x21, 0x7c, 0xc4, 0x4d, 0xc5, 0x92, 0x43, 0xb2, + 0x03, 0x1b, 0xec, 0x5a, 0x56, 0x29, 0x4f, 0xd8, 0x33, 0x7a, 0x6d, 0x4b, 0x75, 0x1b, 0xd5, 0xbd, + 0x54, 0x7e, 0x42, 0xaf, 0xc7, 0xc2, 0x57, 0x45, 0xd9, 0x67, 0x9a, 0x7c, 0x71, 0x9c, 0x03, 0x70, + 0xb7, 0x00, 0xe0, 0x02, 0x4a, 0x7b, 0x4b, 0x28, 0x5d, 0x82, 0x52, 0x7f, 0x05, 0x4a, 0xef, 0x42, + 0x47, 0x06, 0x20, 0x8e, 0xa8, 0xc3, 0xa4, 0x83, 0x0d, 0x15, 0x88, 0x4c, 0x76, 0xe4, 0x62, 0xe2, + 0x25, 0x93, 0xc9, 0xfc, 0x22, 0xf4, 0xd9, 0x82, 0x3b, 0xdb, 0x99, 0xec, 0xc8, 0x1d, 0x7c, 0x04, + 0xad, 0x2c, 0xc2, 0xdf, 0x0b, 0x38, 0x7f, 0x2e, 0x41, 0x27, 0xcf, 0x45, 0x72, 0xf1, 0x78, 0x7c, + 0x8c, 0x8b, 0x2b, 0x96, 0x1c, 0xca, 0x2a, 0xce, 0x59, 0xc0, 0x5e, 0xd1, 0x89, 0xaf, 0x1c, 0x34, + 0xad, 0x85, 0x40, 0x6a, 0xbd, 0xc0, 0xe1, 0x6c, 0x96, 0x22, 0xa8, 0x62, 0x2d, 0x04, 0xe4, 0x63, + 0x00, 0x2f, 0x8e, 0x13, 0xa6, 0xbe, 0x52, 0x15, 0x33, 0x75, 0x30, 0x52, 0x2d, 0xdd, 0x28, 0x6d, + 0xe9, 0x46, 0xe3, 0xb4, 0xa5, 0xb3, 0x5a, 0x68, 0x8d, 0x9f, 0x6f, 0x1b, 0xea, 0xf2, 0x63, 0x8c, + 0x8f, 0x11, 0x65, 0x15, 0x4b, 0xcf, 0xcc, 0x3f, 0x42, 0x5d, 0x15, 0xff, 0xff, 0x2b, 0xbf, 0xde, + 0x86, 0xa6, 0xf2, 0xed, 0xb9, 0x3a, 0x2f, 0x1a, 0x38, 0x3f, 0x72, 0xcd, 0x7f, 0x96, 0xa0, 0x69, + 0xb1, 0x38, 0x0a, 0x83, 0x98, 0xe5, 0x9a, 0x93, 0xd2, 0x6b, 0x9b, 0x93, 0xf2, 0xda, 0xe6, 0x24, + 0x6d, 0x79, 0x2a, 0xb9, 0x96, 0x67, 0x00, 0x4d, 0xce, 0x5c, 0x8f, 0x33, 0x47, 0xe8, 0xf6, 0x28, + 0x9b, 0x4b, 0xdd, 0x2b, 0xca, 0x65, 0x55, 0x8d, 0x91, 0xba, 0x5b, 0x56, 0x36, 0x27, 0x4f, 0xf3, + 0x35, 0x5d, 0x75, 0x4b, 0x5b, 0xaa, 0xa6, 0xab, 0xe3, 0xae, 0x16, 0x75, 0xf3, 0x1f, 0x65, 0xd8, + 0x58, 0x56, 0xaf, 0x01, 0xc1, 0x16, 0xd4, 0x14, 0xeb, 0x6b, 0x04, 0x89, 0x15, 0xbe, 0xaf, 0x2c, + 0xf1, 0xca, 0x2f, 0x96, 0x73, 0xf4, 0xf5, 0x5f, 0xbf, 0x98, 0xbf, 0xef, 0xc3, 0x86, 0x3c, 0x65, + 0xc4, 0xdc, 0x45, 0x27, 0xa3, 0x08, 0xa7, 0xaf, 0xe5, 0x59, 0x2f, 0xf3, 0x10, 0x36, 0x53, 0xd3, + 0x45, 0x2a, 0xd6, 0x0b, 0xb6, 0x87, 0x69, 0x46, 0x6e, 0x43, 0xfd, 0x3c, 0xe4, 0x33, 0x2a, 0x34, + 0xe7, 0xe8, 0x59, 0x81, 0x53, 0x90, 0xdc, 0x9a, 0x0a, 0x16, 0xa9, 0x50, 0x76, 0xeb, 0x32, 0xd7, + 0xb3, 0x4e, 0x1a, 0x49, 0xa7, 0x69, 0x35, 0xd3, 0x0e, 0xda, 0xfc, 0x35, 0xf4, 0x97, 0x9a, 0xa7, + 0x35, 0x81, 0x5c, 0x6c, 0x5f, 0x2e, 0x6c, 0x5f, 0xf0, 0x5c, 0x59, 0xf2, 0xfc, 0x1b, 0xd8, 0xfc, + 0x82, 0x06, 0xae, 0xcf, 0xb4, 0xff, 0x7d, 0x3e, 0x8d, 0x65, 0x1b, 0xa8, 0x7b, 0x79, 0x5b, 0x93, + 0x7d, 0xd7, 0x6a, 0x69, 0xc9, 0x91, 0x4b, 0xee, 0x41, 0x83, 0x2b, 0x6b, 0x0d, 0xbc, 0x76, 0xae, + 0xbb, 0xb3, 0x52, 0x9d, 0xf9, 0x2d, 0x90, 0x82, 0x6b, 0xd9, 0xc6, 0xcf, 0xc9, 0x8e, 0x04, 0xa0, + 0x02, 0x85, 0x06, 0x76, 0x27, 0x8f, 0x23, 0x2b, 0xd3, 0x92, 0x21, 0x54, 0x18, 0xe7, 0x7a, 0x0b, + 0x6c, 0xaf, 0x16, 0x8f, 0x26, 0x4b, 0xaa, 0xcc, 0x9f, 0xc0, 0xe6, 0x59, 0xc4, 0x1c, 0x8f, 0xfa, + 0xf8, 0xe0, 0x51, 0x1b, 0xdc, 0x85, 0x9a, 0x0c, 0x72, 0x9a, 0xb3, 0x2d, 0x5c, 0x88, 0x6a, 0x25, + 0x37, 0xbf, 0x05, 0x43, 0x9d, 0xeb, 0xf0, 0xda, 0x8b, 0x05, 0x0b, 0x1c, 0x76, 0x70, 0xc1, 0x9c, + 0xcb, 0xff, 0xe1, 0xcd, 0xaf, 0xe0, 0xf6, 0xba, 0x1d, 0xd2, 0xf3, 0xb5, 0x1d, 0x39, 0xb3, 0xcf, + 0x25, 0x55, 0xe3, 0x1e, 0x4d, 0x0b, 0x50, 0xf4, 0xb9, 0x94, 0xc8, 0xef, 0xc8, 0xe4, 0xba, 0x58, + 0x53, 0xa2, 0x9e, 0xa5, 0xf1, 0xa8, 0xdc, 0x1c, 0x8f, 0xbf, 0x96, 0xa0, 0x75, 0xc6, 0x44, 0x12, + 0xe1, 0x5d, 0xde, 0x86, 0xd6, 0x84, 0x87, 0x97, 0x8c, 0x2f, 0xae, 0xd2, 0x54, 0x82, 0x23, 0x97, + 0x3c, 0x85, 0xfa, 0x41, 0x18, 0x9c, 0x7b, 0x53, 0x7c, 0xfe, 0xb5, 0xf7, 0x6e, 0x2b, 0x76, 0xd1, + 0x6b, 0x47, 0x4a, 0xa7, 0xca, 0xaa, 0x36, 0x24, 0x43, 0x68, 0xeb, 0x47, 0xf4, 0xcb, 0x97, 0x47, + 0xcf, 0xd3, 0xbe, 0x30, 0x27, 0x1a, 0x7c, 0x0c, 0xed, 0xdc, 0xc2, 0xef, 0x55, 0x2d, 0x7e, 0x08, + 0x80, 0xbb, 0xab, 0x18, 0x6d, 0xa8, 0xab, 0xea, 0x95, 0xf2, 0x6a, 0x77, 0xa1, 0x25, 0x9f, 0x20, + 0x4a, 0x4d, 0xa0, 0x9a, 0x7b, 0x2d, 0xe3, 0xd8, 0xbc, 0x07, 0x9b, 0x47, 0xc1, 0x15, 0xf5, 0x3d, + 0x97, 0x0a, 0xf6, 0x25, 0x9b, 0x63, 0x08, 0x56, 0x4e, 0x60, 0x9e, 0x41, 0x47, 0xbf, 0x47, 0xdf, + 0xe8, 0x8c, 0x1d, 0x7d, 0xc6, 0xef, 0x4e, 0xa2, 0xf7, 0xa1, 0xaf, 0x9d, 0x1e, 0x7b, 0x3a, 0x85, + 0x64, 0x49, 0xe7, 0xec, 0xdc, 0xbb, 0xd6, 0xae, 0xf5, 0xcc, 0x7c, 0x06, 0x1b, 0x39, 0xd3, 0xec, + 0x3a, 0x97, 0x6c, 0x1e, 0xa7, 0xef, 0x74, 0x39, 0x4e, 0x23, 0x50, 0x5e, 0x44, 0xc0, 0x84, 0x9e, + 0x5e, 0xf9, 0x82, 0x89, 0x1b, 0x6e, 0xf7, 0x65, 0x76, 0x90, 0x17, 0x4c, 0x3b, 0xbf, 0x0f, 0x35, + 0x26, 0x6f, 0x9a, 0x2f, 0x61, 0xf9, 0x08, 0x58, 0x4a, 0xbd, 0x66, 0xc3, 0x67, 0xd9, 0x86, 0xa7, + 0x89, 0xda, 0xf0, 0x0d, 0x7d, 0x99, 0xef, 0x65, 0xc7, 0x38, 0x4d, 0xc4, 0x4d, 0x5f, 0xf4, 0x1e, + 0x6c, 0x6a, 0xa3, 0xe7, 0xcc, 0x67, 0x82, 0xdd, 0x70, 0xa5, 0xfb, 0x40, 0x0a, 0x66, 0x37, 0xb9, + 0xbb, 0x03, 0xcd, 0xf1, 0xf8, 0x38, 0xd3, 0x16, 0xb9, 0xd1, 0xfc, 0x04, 0x36, 0xcf, 0x12, 0x37, + 0x3c, 0xe5, 0xde, 0x95, 0xe7, 0xb3, 0xa9, 0xda, 0x2c, 0xed, 0x35, 0x4b, 0xb9, 0x5e, 0x73, 0x6d, + 0x35, 0x32, 0x77, 0x80, 0x14, 0x96, 0x67, 0xdf, 0x2d, 0x4e, 0xdc, 0x50, 0xa7, 0x30, 0x8e, 0xcd, + 0x1d, 0xe8, 0x8c, 0xa9, 0xac, 0xf7, 0xae, 0xb2, 0x31, 0xa0, 0x21, 0xd4, 0x5c, 0x9b, 0xa5, 0x53, + 0x73, 0x0f, 0xb6, 0x0e, 0xa8, 0x73, 0xe1, 0x05, 0xd3, 0xe7, 0x5e, 0x2c, 0x1b, 0x1e, 0xbd, 0x62, + 0x00, 0x4d, 0x57, 0x0b, 0xf4, 0x92, 0x6c, 0x6e, 0x3e, 0x86, 0xb7, 0x72, 0x3f, 0x86, 0x9c, 0x09, + 0x9a, 0xc6, 0x63, 0x0b, 0x6a, 0xb1, 0x9c, 0xe1, 0x8a, 0x9a, 0xa5, 0x26, 0xe6, 0x57, 0xb0, 0x95, + 0x2f, 0xc0, 0xb2, 0xfd, 0x48, 0x2f, 0x8e, 0x8d, 0x41, 0x29, 0xd7, 0x18, 0xe8, 0x98, 0x95, 0x17, + 0xf5, 0x64, 0x03, 0x2a, 0xbf, 0xfc, 0x66, 0xac, 0xc1, 0x2e, 0x87, 0xe6, 0xef, 0xe5, 0xf6, 0x45, + 0x7f, 0x6a, 0xfb, 0x42, 0x77, 0x50, 0x7a, 0x93, 0xee, 0x60, 0x0d, 0xde, 0x1e, 0xc3, 0xe6, 0x89, + 0x1f, 0x3a, 0x97, 0x87, 0x41, 0x2e, 0x1a, 0x06, 0x34, 0x58, 0x90, 0x0f, 0x46, 0x3a, 0x35, 0x1f, + 0x40, 0xff, 0x38, 0x74, 0xa8, 0x7f, 0x12, 0x26, 0x81, 0xc8, 0xa2, 0x80, 0xbf, 0x4e, 0x69, 0x53, + 0x35, 0x31, 0x1f, 0x43, 0x4f, 0x97, 0xe8, 0xe0, 0x3c, 0x4c, 0x99, 0x71, 0x51, 0xcc, 0x4b, 0xc5, + 0xbe, 0xda, 0x3c, 0x86, 0xfe, 0xc2, 0x5c, 0xf9, 0x7d, 0x00, 0x75, 0xa5, 0xd6, 0x77, 0xeb, 0x67, + 0x6f, 0x3c, 0x65, 0x69, 0x69, 0xf5, 0x9a, 0x4b, 0xcd, 0xa0, 0x77, 0x8a, 0xbf, 0x12, 0x1e, 0x06, + 0x57, 0xca, 0xd9, 0x11, 0x10, 0xf5, 0xbb, 0xa1, 0xcd, 0x82, 0x2b, 0x8f, 0x87, 0x01, 0xf6, 0xb7, + 0x25, 0xdd, 0xc2, 0xa4, 0x8e, 0xb3, 0x45, 0xa9, 0x85, 0xb5, 0x19, 0x2d, 0x8b, 0xd6, 0xc6, 0x10, + 0x16, 0xbf, 0x41, 0xc8, 0x52, 0xc3, 0xd9, 0x2c, 0x14, 0xcc, 0xa6, 0xae, 0x9b, 0x66, 0x0b, 0x28, + 0xd1, 0xbe, 0xeb, 0xf2, 0xbd, 0x7f, 0x97, 0xa1, 0xf1, 0x99, 0x22, 0x70, 0xf2, 0x29, 0x74, 0x0b, + 0xe5, 0x9a, 0xbc, 0x85, 0x3f, 0x42, 0x2c, 0x37, 0x07, 0x83, 0xed, 0x15, 0xb1, 0xba, 0xd7, 0x13, + 0xe8, 0xe4, 0x8b, 0x31, 0xc1, 0xc2, 0x8b, 0xbf, 0x88, 0x0e, 0xd0, 0xd3, 0x6a, 0xa5, 0x3e, 0x83, + 0xad, 0x75, 0x65, 0x92, 0xdc, 0x59, 0xec, 0xb0, 0x5a, 0xa2, 0x07, 0xef, 0xdc, 0xa4, 0x4d, 0xcb, + 0x6b, 0xe3, 0xc0, 0x67, 0x34, 0x48, 0xa2, 0xfc, 0x09, 0x16, 0x43, 0xf2, 0x14, 0xba, 0x85, 0x42, + 0xa1, 0xee, 0xb9, 0x52, 0x3b, 0xf2, 0x4b, 0xee, 0x43, 0x0d, 0x8b, 0x13, 0xe9, 0x16, 0xaa, 0xe4, + 0xa0, 0x97, 0x4d, 0xd5, 0xde, 0x43, 0xa8, 0xe2, 0xef, 0x64, 0xb9, 0x8d, 0x71, 0x45, 0x56, 0xb9, + 0xf6, 0xfe, 0x55, 0x82, 0x46, 0xfa, 0xdb, 0xe9, 0x53, 0xa8, 0xca, 0x1a, 0x40, 0x6e, 0xe5, 0x68, + 0x34, 0xad, 0x1f, 0x83, 0xad, 0x25, 0xa1, 0xda, 0x60, 0x04, 0x95, 0x17, 0x4c, 0x10, 0x92, 0x53, + 0xea, 0x62, 0x30, 0xb8, 0x55, 0x94, 0x65, 0xf6, 0xa7, 0x49, 0xd1, 0x5e, 0x73, 0x79, 0xc1, 0x3e, + 0x63, 0xe9, 0x8f, 0xa0, 0xae, 0x58, 0x56, 0x05, 0x65, 0x85, 0x9f, 0xd5, 0xc7, 0x5f, 0xe5, 0xe3, + 0xbd, 0xbf, 0x57, 0x01, 0xce, 0xe6, 0xb1, 0x60, 0xb3, 0x5f, 0x79, 0xec, 0x15, 0x79, 0x08, 0xfd, + 0xe7, 0xec, 0x9c, 0x26, 0xbe, 0xc0, 0xd7, 0x92, 0x64, 0x93, 0x5c, 0x4c, 0xb0, 0xe1, 0xcb, 0xc8, + 0xfa, 0x3e, 0xb4, 0x4f, 0xe8, 0xf5, 0xeb, 0xed, 0x3e, 0x85, 0x6e, 0x81, 0x83, 0xf5, 0x11, 0x97, + 0x59, 0x5d, 0x1f, 0x71, 0x95, 0xad, 0xef, 0x43, 0x43, 0x33, 0x73, 0x7e, 0x0f, 0xac, 0x61, 0x05, + 0xc6, 0xfe, 0x29, 0xf4, 0x97, 0x78, 0x39, 0x6f, 0x8f, 0xbf, 0x3e, 0xac, 0xe5, 0xed, 0x67, 0xf2, + 0xb5, 0x53, 0xe4, 0xe6, 0xfc, 0xc2, 0xdb, 0x8a, 0x0f, 0xd7, 0x91, 0xf7, 0x8b, 0xe2, 0x3b, 0x09, + 0x5f, 0x89, 0xc6, 0x32, 0x7d, 0xa6, 0xe4, 0x9d, 0x3a, 0x5a, 0x47, 0xc3, 0x4f, 0xa0, 0x93, 0x67, + 0xd0, 0x95, 0x14, 0x5c, 0xa5, 0xd7, 0x47, 0x00, 0x0b, 0x12, 0xcd, 0xdb, 0x23, 0x3c, 0x96, 0xf9, + 0xf5, 0x43, 0x80, 0x05, 0x35, 0x2a, 0x54, 0x15, 0x99, 0x55, 0x2d, 0x5b, 0xa6, 0xcf, 0x87, 0xd0, + 0xca, 0xe8, 0x2c, 0xbf, 0x07, 0x3a, 0x28, 0xb2, 0xe3, 0x67, 0xa3, 0xdf, 0x3e, 0x9a, 0x7a, 0xe2, + 0x22, 0x99, 0x8c, 0x9c, 0x70, 0xb6, 0x7b, 0x41, 0xe3, 0x0b, 0xcf, 0x09, 0x79, 0xb4, 0x7b, 0x25, + 0xc1, 0xb4, 0xbb, 0xf2, 0xb7, 0xce, 0xa4, 0x8e, 0x8f, 0xbd, 0x0f, 0xfe, 0x1b, 0x00, 0x00, 0xff, + 0xff, 0xf0, 0x04, 0x0e, 0x5a, 0xf2, 0x19, 0x00, 0x00, } diff --git a/logical/plugin/pb/backend.proto b/logical/plugin/pb/backend.proto index 8911499008..fdea91f5b0 100644 --- a/logical/plugin/pb/backend.proto +++ b/logical/plugin/pb/backend.proto @@ -198,6 +198,31 @@ message Auth { // If set, restricts usage of the certificates to client IPs falling within // the range of the specified CIDR(s). repeated string bound_cidrs = 13; + + // TokenPolicies and IdentityPolicies break down the list in Policies to + // help determine where a policy was sourced + repeated string token_policies = 14; + repeated string identity_policies = 15; +} + +message TokenEntry { + string id = 1; + string accessor = 2; + string parent = 3; + repeated string policies = 4; + string path = 5; + map meta = 6; + string display_name = 7; + int64 num_uses = 8; + int64 creation_time = 9; + int64 ttl = 10; + int64 explicit_max_ttl = 11; + string role = 12; + int64 period = 13; + string entity_id = 14; + repeated string bound_cidrs = 15; + string namespace_id = 16; + string cubbyhole_id = 17; } message LeaseOptions { diff --git a/logical/plugin/pb/translation.go b/logical/plugin/pb/translation.go index d0d220e789..d60cbf1db1 100644 --- a/logical/plugin/pb/translation.go +++ b/logical/plugin/pb/translation.go @@ -485,19 +485,21 @@ func LogicalAuthToProtoAuth(a *logical.Auth) (*Auth, error) { } return &Auth{ - LeaseOptions: lo, - InternalData: string(buf[:]), - DisplayName: a.DisplayName, - Policies: a.Policies, - Metadata: a.Metadata, - ClientToken: a.ClientToken, - Accessor: a.Accessor, - Period: int64(a.Period), - NumUses: int64(a.NumUses), - EntityID: a.EntityID, - Alias: a.Alias, - GroupAliases: a.GroupAliases, - BoundCidrs: boundCIDRs, + LeaseOptions: lo, + InternalData: string(buf[:]), + DisplayName: a.DisplayName, + Policies: a.Policies, + TokenPolicies: a.TokenPolicies, + IdentityPolicies: a.IdentityPolicies, + Metadata: a.Metadata, + ClientToken: a.ClientToken, + Accessor: a.Accessor, + Period: int64(a.Period), + NumUses: int64(a.NumUses), + EntityID: a.EntityID, + Alias: a.Alias, + GroupAliases: a.GroupAliases, + BoundCidrs: boundCIDRs, }, nil } @@ -528,18 +530,87 @@ func ProtoAuthToLogicalAuth(a *Auth) (*logical.Auth, error) { } return &logical.Auth{ - LeaseOptions: lo, - InternalData: data, - DisplayName: a.DisplayName, - Policies: a.Policies, - Metadata: a.Metadata, - ClientToken: a.ClientToken, - Accessor: a.Accessor, - Period: time.Duration(a.Period), - NumUses: int(a.NumUses), - EntityID: a.EntityID, - Alias: a.Alias, - GroupAliases: a.GroupAliases, - BoundCIDRs: boundCIDRs, + LeaseOptions: lo, + InternalData: data, + DisplayName: a.DisplayName, + Policies: a.Policies, + TokenPolicies: a.TokenPolicies, + IdentityPolicies: a.IdentityPolicies, + Metadata: a.Metadata, + ClientToken: a.ClientToken, + Accessor: a.Accessor, + Period: time.Duration(a.Period), + NumUses: int(a.NumUses), + EntityID: a.EntityID, + Alias: a.Alias, + GroupAliases: a.GroupAliases, + BoundCIDRs: boundCIDRs, + }, nil +} + +func LogicalTokenEntryToProtoTokenEntry(t *logical.TokenEntry) *TokenEntry { + if t == nil { + return nil + } + + boundCIDRs := make([]string, len(t.BoundCIDRs)) + for i, cidr := range t.BoundCIDRs { + boundCIDRs[i] = cidr.String() + } + + return &TokenEntry{ + ID: t.ID, + Accessor: t.Accessor, + Parent: t.Parent, + Policies: t.Policies, + Path: t.Path, + Meta: t.Meta, + DisplayName: t.DisplayName, + NumUses: int64(t.NumUses), + CreationTime: t.CreationTime, + TTL: int64(t.TTL), + ExplicitMaxTTL: int64(t.ExplicitMaxTTL), + Role: t.Role, + Period: int64(t.Period), + EntityID: t.EntityID, + BoundCidrs: boundCIDRs, + NamespaceID: t.NamespaceID, + CubbyholeID: t.CubbyholeID, + } +} + +func ProtoTokenEntryToLogicalTokenEntry(t *TokenEntry) (*logical.TokenEntry, error) { + if t == nil { + return nil, nil + } + + boundCIDRs, err := parseutil.ParseAddrs(t.BoundCidrs) + if err != nil { + return nil, err + } + if len(boundCIDRs) == 0 { + // On inbound auths, if auth.BoundCIDRs is empty, it will be nil. + // Let's match that behavior outbound. + boundCIDRs = nil + } + + return &logical.TokenEntry{ + ID: t.ID, + Accessor: t.Accessor, + Parent: t.Parent, + Policies: t.Policies, + Path: t.Path, + Meta: t.Meta, + DisplayName: t.DisplayName, + NumUses: int(t.NumUses), + CreationTime: t.CreationTime, + TTL: time.Duration(t.TTL), + ExplicitMaxTTL: time.Duration(t.ExplicitMaxTTL), + Role: t.Role, + Period: time.Duration(t.Period), + EntityID: t.EntityID, + BoundCIDRs: boundCIDRs, + NamespaceID: t.NamespaceID, + CubbyholeID: t.CubbyholeID, }, nil } diff --git a/logical/plugin/system.go b/logical/plugin/system.go index 21abb3c252..890f4ef5a2 100644 --- a/logical/plugin/system.go +++ b/logical/plugin/system.go @@ -8,6 +8,7 @@ import ( "fmt" "github.com/hashicorp/vault/helper/consts" + "github.com/hashicorp/vault/helper/license" "github.com/hashicorp/vault/helper/pluginutil" "github.com/hashicorp/vault/helper/wrapping" "github.com/hashicorp/vault/logical" @@ -109,6 +110,11 @@ func (s *SystemViewClient) LookupPlugin(ctx context.Context, name string) (*plug return nil, fmt.Errorf("cannot call LookupPlugin from a plugin backend") } +func (s *SystemViewClient) HasFeature(feature license.Features) bool { + // Not implemented + return false +} + func (s *SystemViewClient) MlockEnabled() bool { var reply MlockEnabledReply err := s.client.Call("Plugin.MlockEnabled", new(interface{}), &reply) diff --git a/logical/request.go b/logical/request.go index 4bc7f50a5a..8380270df8 100644 --- a/logical/request.go +++ b/logical/request.go @@ -51,6 +51,8 @@ func (r *RequestWrapInfo) SentinelKeys() []string { // by the router after policy checks; the token namespace would be the right // place to access them via Sentinel type Request struct { + entReq + // Id is the uuid associated with each request ID string `json:"id" structs:"id" mapstructure:"id" sentinel:""` @@ -140,6 +142,10 @@ type Request struct { // accessible. Unauthenticated bool `json:"unauthenticated" structs:"unauthenticated" mapstructure:"unauthenticated"` + // MFACreds holds the parsed MFA information supplied over the API as part of + // X-Vault-MFA header + MFACreds MFACreds `json:"mfa_creds" structs:"mfa_creds" mapstructure:"mfa_creds" sentinel:""` + // Cached token entry. This avoids another lookup in request handling when // we've already looked it up at http handling time. Note that this token // has not been "used", as in it will not properly take into account use @@ -272,3 +278,5 @@ const ( RenewOperation = "renew" RollbackOperation = "rollback" ) + +type MFACreds map[string][]string diff --git a/logical/request_util.go b/logical/request_util.go new file mode 100644 index 0000000000..38d6e3d5eb --- /dev/null +++ b/logical/request_util.go @@ -0,0 +1,14 @@ +// +build !enterprise + +package logical + +type entReq struct { + ControlGroup interface{} +} + +func (r *Request) EntReq() *entReq { + return &entReq{} +} + +func (r *Request) SetEntReq(*entReq) { +} diff --git a/logical/response_util.go b/logical/response_util.go index fbbd021d8c..b4df632385 100644 --- a/logical/response_util.go +++ b/logical/response_util.go @@ -70,11 +70,13 @@ func RespondErrorCommon(req *Request, resp *Response, err error) (int, error) { if errwrap.ContainsType(err, new(ReplicationCodedError)) { var allErrors error - codedErr := errwrap.GetType(err, new(ReplicationCodedError)).(*ReplicationCodedError) + var codedErr *ReplicationCodedError errwrap.Walk(err, func(inErr error) { newErr, ok := inErr.(*ReplicationCodedError) - if !ok { - allErrors = multierror.Append(allErrors, newErr) + if ok { + codedErr = newErr + } else { + allErrors = multierror.Append(allErrors, inErr) } }) if allErrors != nil { diff --git a/logical/system_view.go b/logical/system_view.go index 8eb72cb6e2..f970847485 100644 --- a/logical/system_view.go +++ b/logical/system_view.go @@ -6,6 +6,7 @@ import ( "time" "github.com/hashicorp/vault/helper/consts" + "github.com/hashicorp/vault/helper/license" "github.com/hashicorp/vault/helper/pluginutil" "github.com/hashicorp/vault/helper/wrapping" ) @@ -46,6 +47,9 @@ type SystemView interface { // ReplicationState indicates the state of cluster replication ReplicationState() consts.ReplicationState + // HasFeature returns true if the feature is currently enabled + HasFeature(feature license.Features) bool + // ResponseWrapData wraps the given data in a cubbyhole and returns the // token used to unwrap. ResponseWrapData(ctx context.Context, data map[string]interface{}, ttl time.Duration, jwt bool) (*wrapping.ResponseWrapInfo, error) @@ -77,6 +81,7 @@ type StaticSystemView struct { LocalMountVal bool ReplicationStateVal consts.ReplicationState EntityVal *Entity + Features license.Features VaultVersion string PluginEnvironment *PluginEnvironment } @@ -125,6 +130,10 @@ func (d StaticSystemView) EntityInfo(entityID string) (*Entity, error) { return d.EntityVal, nil } +func (d StaticSystemView) HasFeature(feature license.Features) bool { + return d.Features.HasFeature(feature) +} + func (d StaticSystemView) PluginEnv(_ context.Context) (*PluginEnvironment, error) { return d.PluginEnvironment, nil } diff --git a/logical/testing/testing.go b/logical/testing/testing.go index 03d3b220e8..865a4767c6 100644 --- a/logical/testing/testing.go +++ b/logical/testing/testing.go @@ -14,6 +14,7 @@ import ( "github.com/hashicorp/errwrap" "github.com/hashicorp/vault/api" "github.com/hashicorp/vault/helper/logging" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/http" "github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/physical/inmem" @@ -249,7 +250,7 @@ func Test(tt TestT, c TestCase) { req.Path = fmt.Sprintf("%s/%s", prefix, req.Path) // Make the request - resp, err := core.HandleRequest(context.Background(), req) + resp, err := core.HandleRequest(namespace.TestContext(), req) if resp != nil && resp.Secret != nil { // Revoke this secret later revoke = append(revoke, &logical.Request{ @@ -303,7 +304,7 @@ func Test(tt TestT, c TestCase) { logger.Warn("Revoking secret", "secret", fmt.Sprintf("%#v", req)) } req.ClientToken = client.Token() - resp, err := core.HandleRequest(context.Background(), req) + resp, err := core.HandleRequest(namespace.TestContext(), req) if err == nil && resp.IsError() { err = fmt.Errorf("erroneous response:\n\n%#v", resp) } @@ -320,7 +321,7 @@ func Test(tt TestT, c TestCase) { req := logical.RollbackRequest(prefix + "/") req.Data["immediate"] = true req.ClientToken = client.Token() - resp, err := core.HandleRequest(context.Background(), req) + resp, err := core.HandleRequest(namespace.TestContext(), req) if err == nil && resp.IsError() { err = fmt.Errorf("erroneous response:\n\n%#v", resp) } diff --git a/logical/token.go b/logical/token.go index 337791e486..a991441fe2 100644 --- a/logical/token.go +++ b/logical/token.go @@ -65,6 +65,15 @@ type TokenEntry struct { // The set of CIDRs that this token can be used with BoundCIDRs []*sockaddr.SockAddrMarshaler `json:"bound_cidrs"` + + // NamespaceID is the identifier of the namespace to which this token is + // confined to. Do not return this value over the API when the token is + // being looked up. + NamespaceID string `json:"namespace_id" mapstructure:"namespace_id" structs:"namespace_id" sentinel:""` + + // CubbyholeID is the identifier of the cubbyhole storage belonging to this + // token + CubbyholeID string `json:"cubbyhole_id" mapstructure:"cubbyhole_id" structs:"cubbyhole_id" sentinel:""` } func (te *TokenEntry) SentinelGet(key string) (interface{}, error) { diff --git a/physical/cache.go b/physical/cache.go index e6d8f90a5d..af6a39b83e 100644 --- a/physical/cache.go +++ b/physical/cache.go @@ -22,6 +22,7 @@ var cacheExceptionsPaths = []string{ "index/pages/", "index-dr/pages/", "sys/expire/", + "core/poison-pill", } // Cache is used to wrap an underlying physical backend diff --git a/physical/consul/consul.go b/physical/consul/consul.go index 554ab1a33b..beeb52bfdf 100644 --- a/physical/consul/consul.go +++ b/physical/consul/consul.go @@ -73,6 +73,7 @@ var _ physical.Backend = (*ConsulBackend)(nil) var _ physical.HABackend = (*ConsulBackend)(nil) var _ physical.Lock = (*ConsulLock)(nil) var _ physical.Transactional = (*ConsulBackend)(nil) +var _ physical.ServiceDiscovery = (*ConsulBackend)(nil) var ( hostnameRegex = regexp.MustCompile(`^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$`) @@ -97,8 +98,9 @@ type ConsulBackend struct { checkTimeout time.Duration consistencyMode string - notifyActiveCh chan notifyEvent - notifySealedCh chan notifyEvent + notifyActiveCh chan notifyEvent + notifySealedCh chan notifyEvent + notifyPerfStandbyCh chan notifyEvent sessionTTL string lockWaitTime time.Duration @@ -292,6 +294,7 @@ func NewConsulBackend(conf map[string]string, logger log.Logger) (physical.Backe consistencyMode: consistencyMode, notifyActiveCh: make(chan notifyEvent), notifySealedCh: make(chan notifyEvent), + notifyPerfStandbyCh: make(chan notifyEvent), sessionTTL: sessionTTL, lockWaitTime: lockWaitTime, } @@ -595,6 +598,18 @@ func (c *ConsulBackend) NotifyActiveStateChange() error { return nil } +func (c *ConsulBackend) NotifyPerformanceStandbyStateChange() error { + select { + case c.notifyPerfStandbyCh <- notifyEvent{}: + default: + // NOTE: If this occurs Vault's active status could be out of + // sync with Consul until reconcileTimer expires. + c.logger.Warn("concurrent state change notify dropped") + } + + return nil +} + func (c *ConsulBackend) NotifySealedStateChange() error { select { case c.notifySealedCh <- notifyEvent{}: @@ -611,7 +626,7 @@ func (c *ConsulBackend) checkDuration() time.Duration { return lib.DurationMinusBuffer(c.checkTimeout, checkMinBuffer, checkJitterFactor) } -func (c *ConsulBackend) RunServiceDiscovery(waitGroup *sync.WaitGroup, shutdownCh physical.ShutdownChannel, redirectAddr string, activeFunc physical.ActiveFunction, sealedFunc physical.SealedFunction) (err error) { +func (c *ConsulBackend) RunServiceDiscovery(waitGroup *sync.WaitGroup, shutdownCh physical.ShutdownChannel, redirectAddr string, activeFunc physical.ActiveFunction, sealedFunc physical.SealedFunction, perfStandbyFunc physical.PerformanceStandbyFunction) (err error) { if err := c.setRedirectAddr(redirectAddr); err != nil { return err } @@ -619,12 +634,12 @@ func (c *ConsulBackend) RunServiceDiscovery(waitGroup *sync.WaitGroup, shutdownC // 'server' command will wait for the below goroutine to complete waitGroup.Add(1) - go c.runEventDemuxer(waitGroup, shutdownCh, redirectAddr, activeFunc, sealedFunc) + go c.runEventDemuxer(waitGroup, shutdownCh, redirectAddr, activeFunc, sealedFunc, perfStandbyFunc) return nil } -func (c *ConsulBackend) runEventDemuxer(waitGroup *sync.WaitGroup, shutdownCh physical.ShutdownChannel, redirectAddr string, activeFunc physical.ActiveFunction, sealedFunc physical.SealedFunction) { +func (c *ConsulBackend) runEventDemuxer(waitGroup *sync.WaitGroup, shutdownCh physical.ShutdownChannel, redirectAddr string, activeFunc physical.ActiveFunction, sealedFunc physical.SealedFunction, perfStandbyFunc physical.PerformanceStandbyFunction) { // This defer statement should be executed last. So push it first. defer waitGroup.Done() @@ -660,6 +675,9 @@ func (c *ConsulBackend) runEventDemuxer(waitGroup *sync.WaitGroup, shutdownCh ph case <-c.notifySealedCh: // Run check timer immediately upon a seal state change notification checkTimer.Reset(0) + case <-c.notifyPerfStandbyCh: + // Run check timer immediately upon a seal state change notification + checkTimer.Reset(0) case <-reconcileTimer.C: // Unconditionally rearm the reconcileTimer reconcileTimer.Reset(reconcileTimeout - lib.RandomStagger(reconcileTimeout/checkJitterFactor)) @@ -671,7 +689,7 @@ func (c *ConsulBackend) runEventDemuxer(waitGroup *sync.WaitGroup, shutdownCh ph go func() { defer atomic.CompareAndSwapInt32(serviceRegLock, 1, 0) for !shutdown { - serviceID, err := c.reconcileConsul(registeredServiceID, activeFunc, sealedFunc) + serviceID, err := c.reconcileConsul(registeredServiceID, activeFunc, sealedFunc, perfStandbyFunc) if err != nil { if c.logger.IsWarn() { c.logger.Warn("reconcile unable to talk with Consul backend", "error", err) @@ -741,10 +759,11 @@ func (c *ConsulBackend) serviceID() string { // without any locks held and can be run concurrently, therefore no changes // to ConsulBackend can be made in this method (i.e. wtb const receiver for // compiler enforced safety). -func (c *ConsulBackend) reconcileConsul(registeredServiceID string, activeFunc physical.ActiveFunction, sealedFunc physical.SealedFunction) (serviceID string, err error) { +func (c *ConsulBackend) reconcileConsul(registeredServiceID string, activeFunc physical.ActiveFunction, sealedFunc physical.SealedFunction, perfStandbyFunc physical.PerformanceStandbyFunction) (serviceID string, err error) { // Query vault Core for its current state active := activeFunc() sealed := sealedFunc() + perfStandby := perfStandbyFunc() agent := c.client.Agent() catalog := c.client.Catalog() @@ -762,7 +781,7 @@ func (c *ConsulBackend) reconcileConsul(registeredServiceID string, activeFunc p } } - tags := c.fetchServiceTags(active) + tags := c.fetchServiceTags(active, perfStandby) var reregister bool @@ -839,12 +858,19 @@ func (c *ConsulBackend) runCheck(sealed bool) error { } // fetchServiceTags returns all of the relevant tags for Consul. -func (c *ConsulBackend) fetchServiceTags(active bool) []string { +func (c *ConsulBackend) fetchServiceTags(active bool, perfStandby bool) []string { activeTag := "standby" if active { activeTag = "active" } - return append(c.serviceTags, activeTag) + + result := append(c.serviceTags, activeTag) + + if perfStandby { + result = append(c.serviceTags, "performance-standby") + } + + return result } func (c *ConsulBackend) setRedirectAddr(addr string) (err error) { diff --git a/physical/consul/consul_test.go b/physical/consul/consul_test.go index fd22b44e17..8e36932404 100644 --- a/physical/consul/consul_test.go +++ b/physical/consul/consul_test.go @@ -80,6 +80,17 @@ func testSealedFunc(sealedPct float64) physical.SealedFunction { } } +func testPerformanceStandbyFunc(perfPct float64) physical.PerformanceStandbyFunction { + return func() bool { + var ps bool + unsealedProb := rand.Float64() + if unsealedProb > perfPct { + ps = true + } + return ps + } +} + func TestConsul_ServiceTags(t *testing.T) { consulConfig := map[string]string{ "path": "seaTech/", @@ -106,15 +117,25 @@ func TestConsul_ServiceTags(t *testing.T) { } expected := []string{"deadbeef", "cafeefac", "deadc0de", "feedface"} - actual := c.fetchServiceTags(false) + actual := c.fetchServiceTags(false, false) if !strutil.EquivalentSlices(actual, append(expected, "standby")) { t.Fatalf("bad: expected:%s actual:%s", append(expected, "standby"), actual) } - actual = c.fetchServiceTags(true) + actual = c.fetchServiceTags(true, false) if !strutil.EquivalentSlices(actual, append(expected, "active")) { t.Fatalf("bad: expected:%s actual:%s", append(expected, "active"), actual) } + + actual = c.fetchServiceTags(false, true) + if !strutil.EquivalentSlices(actual, append(expected, "performance-standby")) { + t.Fatalf("bad: expected:%s actual:%s", append(expected, "performance-standby"), actual) + } + + actual = c.fetchServiceTags(true, true) + if !strutil.EquivalentSlices(actual, append(expected, "performance-standby")) { + t.Fatalf("bad: expected:%s actual:%s", append(expected, "performance-standby"), actual) + } } func TestConsul_ServiceAddress(t *testing.T) { @@ -254,7 +275,7 @@ func TestConsul_newConsulBackend(t *testing.T) { var shutdownCh physical.ShutdownChannel waitGroup := &sync.WaitGroup{} - if err := c.RunServiceDiscovery(waitGroup, shutdownCh, test.redirectAddr, testActiveFunc(0.5), testSealedFunc(0.5)); err != nil { + if err := c.RunServiceDiscovery(waitGroup, shutdownCh, test.redirectAddr, testActiveFunc(0.5), testSealedFunc(0.5), testPerformanceStandbyFunc(0.5)); err != nil { t.Fatalf("bad: %v", err) } @@ -283,23 +304,36 @@ func TestConsul_newConsulBackend(t *testing.T) { func TestConsul_serviceTags(t *testing.T) { tests := []struct { - active bool - tags []string + active bool + perfStandby bool + tags []string }{ { - active: true, - tags: []string{"active"}, + active: true, + perfStandby: false, + tags: []string{"active"}, }, { - active: false, - tags: []string{"standby"}, + active: false, + perfStandby: false, + tags: []string{"standby"}, + }, + { + active: false, + perfStandby: true, + tags: []string{"performance-standby"}, + }, + { + active: true, + perfStandby: true, + tags: []string{"performance-standby"}, }, } c := testConsulBackend(t) for _, test := range tests { - tags := c.fetchServiceTags(test.active) + tags := c.fetchServiceTags(test.active, test.perfStandby) if !reflect.DeepEqual(tags[:], test.tags[:]) { t.Errorf("Bad %v: %v %v", test.active, tags, test.tags) } diff --git a/physical/error.go b/physical/error.go new file mode 100644 index 0000000000..cf206b1150 --- /dev/null +++ b/physical/error.go @@ -0,0 +1,103 @@ +package physical + +import ( + "context" + "errors" + "math/rand" + "time" + + log "github.com/hashicorp/go-hclog" +) + +const ( + // DefaultErrorPercent is used to determin how often we error + DefaultErrorPercent = 20 +) + +// ErrorInjector is used to add errors into underlying physical requests +type ErrorInjector struct { + backend Backend + errorPercent int + random *rand.Rand +} + +// TransactionalErrorInjector is the transactional version of the error +// injector +type TransactionalErrorInjector struct { + *ErrorInjector + Transactional +} + +// Verify ErrorInjector satisfies the correct interfaces +var _ Backend = (*ErrorInjector)(nil) +var _ Transactional = (*TransactionalErrorInjector)(nil) + +// NewErrorInjector returns a wrapped physical backend to inject error +func NewErrorInjector(b Backend, errorPercent int, logger log.Logger) *ErrorInjector { + if errorPercent < 0 || errorPercent > 100 { + errorPercent = DefaultJitterPercent + } + logger.Info("creating error injector") + + return &ErrorInjector{ + backend: b, + errorPercent: errorPercent, + random: rand.New(rand.NewSource(int64(time.Now().Nanosecond()))), + } +} + +// NewTransactionalErrorInjector creates a new transactional ErrorInjector +func NewTransactionalErrorInjector(b Backend, errorPercent int, logger log.Logger) *TransactionalErrorInjector { + return &TransactionalErrorInjector{ + ErrorInjector: NewErrorInjector(b, errorPercent, logger), + Transactional: b.(Transactional), + } +} + +func (e *ErrorInjector) SetErrorPercentage(p int) { + e.errorPercent = p +} + +func (e *ErrorInjector) addError() error { + roll := e.random.Intn(100) + if roll < e.errorPercent { + return errors.New("random error") + } + + return nil +} + +func (e *ErrorInjector) Put(ctx context.Context, entry *Entry) error { + if err := e.addError(); err != nil { + return err + } + return e.backend.Put(ctx, entry) +} + +func (e *ErrorInjector) Get(ctx context.Context, key string) (*Entry, error) { + if err := e.addError(); err != nil { + return nil, err + } + return e.backend.Get(ctx, key) +} + +func (e *ErrorInjector) Delete(ctx context.Context, key string) error { + if err := e.addError(); err != nil { + return err + } + return e.backend.Delete(ctx, key) +} + +func (e *ErrorInjector) List(ctx context.Context, prefix string) ([]string, error) { + if err := e.addError(); err != nil { + return nil, err + } + return e.backend.List(ctx, prefix) +} + +func (e *TransactionalErrorInjector) Transaction(ctx context.Context, txns []*TxnEntry) error { + if err := e.addError(); err != nil { + return err + } + return e.Transactional.Transaction(ctx, txns) +} diff --git a/physical/physical.go b/physical/physical.go index 9325186a4a..0f4b000251 100644 --- a/physical/physical.go +++ b/physical/physical.go @@ -75,6 +75,7 @@ type RedirectDetect interface { // Callback signatures for RunServiceDiscovery type ActiveFunction func() bool type SealedFunction func() bool +type PerformanceStandbyFunction func() bool // ServiceDiscovery is an optional interface that an HABackend can implement. // If they do, the state of a backend is advertised to the service discovery @@ -90,9 +91,14 @@ type ServiceDiscovery interface { // status to sealed or unsealed. NotifySealedStateChange() error + // NotifyPerformanceStandbyStateChange is used by Core to notify a backend + // capable of ServiceDiscovery that this Vault instance has changed it + // status to performance standby or standby. + NotifyPerformanceStandbyStateChange() error + // Run executes any background service discovery tasks until the // shutdown channel is closed. - RunServiceDiscovery(waitGroup *sync.WaitGroup, shutdownCh ShutdownChannel, redirectAddr string, activeFunc ActiveFunction, sealedFunc SealedFunction) error + RunServiceDiscovery(waitGroup *sync.WaitGroup, shutdownCh ShutdownChannel, redirectAddr string, activeFunc ActiveFunction, sealedFunc SealedFunction, perfStandbyFunc PerformanceStandbyFunction) error } type Lock interface { @@ -109,13 +115,6 @@ type Lock interface { Value() (bool, string, error) } -// Entry is used to represent data stored by the physical backend -type Entry struct { - Key string - Value []byte - SealWrap bool `json:"seal_wrap,omitempty"` -} - // Factory is the factory function to create a physical backend. type Factory func(config map[string]string, logger log.Logger) (Backend, error) diff --git a/physical/physical_access.go b/physical/physical_access.go index 58ac9739a7..7497313afc 100644 --- a/physical/physical_access.go +++ b/physical/physical_access.go @@ -1,6 +1,8 @@ package physical -import "context" +import ( + "context" +) // PhysicalAccess is a wrapper around physical.Backend that allows Core to // expose its physical storage operations through PhysicalAccess() while diff --git a/physical/physical_util.go b/physical/physical_util.go new file mode 100644 index 0000000000..c4863339da --- /dev/null +++ b/physical/physical_util.go @@ -0,0 +1,10 @@ +// +build !enterprise + +package physical + +// Entry is used to represent data stored by the physical backend +type Entry struct { + Key string + Value []byte + SealWrap bool `json:"seal_wrap,omitempty"` +} diff --git a/physical/transactions.go b/physical/transactions.go index 5c43e57dcf..19f0d2cbed 100644 --- a/physical/transactions.go +++ b/physical/transactions.go @@ -21,6 +21,11 @@ type Transactional interface { Transaction(context.Context, []*TxnEntry) error } +type TransactionalBackend interface { + Backend + Transactional +} + type PseudoTransactional interface { // An internal function should do no locking or permit pool acquisition. // Depending on the backend and if it natively supports transactions, these diff --git a/physical/types.pb.go b/physical/types.pb.go index 5f189d2ec9..5efd290040 100644 --- a/physical/types.pb.go +++ b/physical/types.pb.go @@ -1,3 +1,4 @@ +// +build !enterprise // Code generated by protoc-gen-go. DO NOT EDIT. // source: physical/types.proto diff --git a/vault/acl.go b/vault/acl.go index d027729d38..e16cf1ae39 100644 --- a/vault/acl.go +++ b/vault/acl.go @@ -6,10 +6,11 @@ import ( "reflect" "strings" - "github.com/armon/go-radix" + radix "github.com/armon/go-radix" "github.com/hashicorp/errwrap" multierror "github.com/hashicorp/go-multierror" "github.com/hashicorp/vault/helper/identity" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/helper/strutil" "github.com/hashicorp/vault/logical" "github.com/mitchellh/copystructure" @@ -26,6 +27,9 @@ type ACL struct { // root is enabled if the "root" named policy is present. root bool + + // Stores policies that are actually RGPs for later fetching + rgpPolicies []*Policy } type PolicyCheckOpts struct { @@ -42,14 +46,16 @@ type AuthResults struct { } type ACLResults struct { - Allowed bool - RootPrivs bool - IsRoot bool - MFAMethods []string + Allowed bool + RootPrivs bool + IsRoot bool + MFAMethods []string + ControlGroup *ControlGroup + CapabilitiesBitmap uint32 } -// New is used to construct a policy based ACL from a set of policies. -func NewACL(policies []*Policy) (*ACL, error) { +// NewACL is used to construct a policy based ACL from a set of policies. +func NewACL(ctx context.Context, policies []*Policy) (*ACL, error) { // Initialize a := &ACL{ exactRules: radix.New(), @@ -57,6 +63,14 @@ func NewACL(policies []*Policy) (*ACL, error) { root: false, } + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } + if ns == nil { + return nil, namespace.ErrNoNamespace + } + // Inject each policy for _, policy := range policies { // Ignore a nil policy object @@ -66,12 +80,19 @@ func NewACL(policies []*Policy) (*ACL, error) { switch policy.Type { case PolicyTypeACL: + case PolicyTypeRGP: + a.rgpPolicies = append(a.rgpPolicies, policy) + continue default: return nil, fmt.Errorf("unable to parse policy (wrong type)") } // Check if this is root if policy.Name == "root" { + if ns.ID != namespace.RootNamespaceID { + return nil, fmt.Errorf("root policy is only allowed in root namespace") + } + if len(policies) != 1 { return nil, fmt.Errorf("other policies present along with root") } @@ -196,6 +217,30 @@ func NewACL(policies []*Policy) (*ACL, error) { } } + if len(pc.Permissions.MFAMethods) > 0 { + if existingPerms.MFAMethods == nil { + existingPerms.MFAMethods = pc.Permissions.MFAMethods + } else { + for _, method := range pc.Permissions.MFAMethods { + existingPerms.MFAMethods = append(existingPerms.MFAMethods, method) + } + } + existingPerms.MFAMethods = strutil.RemoveDuplicates(existingPerms.MFAMethods, false) + } + + // No need to dedupe this list since any authorization can satisfy any factor + if pc.Permissions.ControlGroup != nil { + if len(pc.Permissions.ControlGroup.Factors) > 0 { + if existingPerms.ControlGroup == nil { + existingPerms.ControlGroup = pc.Permissions.ControlGroup + } else { + for _, authz := range pc.Permissions.ControlGroup.Factors { + existingPerms.ControlGroup.Factors = append(existingPerms.ControlGroup.Factors, authz) + } + } + } + } + INSERT: tree.Insert(pc.Prefix, existingPerms) } @@ -203,40 +248,21 @@ func NewACL(policies []*Policy) (*ACL, error) { return a, nil } -func (a *ACL) Capabilities(path string) (pathCapabilities []string) { - // Fast-path root - if a.root { +func (a *ACL) Capabilities(ctx context.Context, path string) (pathCapabilities []string) { + req := &logical.Request{ + Path: path, + // doesn't matter, but use List to trigger fallback behavior so we can + // model real behavior + Operation: logical.ListOperation, + } + + res := a.AllowOperation(ctx, req, true) + if res.IsRoot { return []string{RootCapability} } - // Find an exact matching rule, look for glob if no match - var capabilities uint32 - raw, ok := a.exactRules.Get(path) + capabilities := res.CapabilitiesBitmap - if ok { - perm := raw.(*ACLPermissions) - capabilities = perm.CapabilitiesBitmap - goto CHECK - } - if strings.HasSuffix(path, "/") { - raw, ok = a.exactRules.Get(strings.TrimSuffix(path, "/")) - if ok { - perm := raw.(*ACLPermissions) - capabilities = perm.CapabilitiesBitmap - goto CHECK - } - } - - // Find a glob rule, default deny if no match - _, raw, ok = a.globRules.LongestPrefix(path) - if !ok { - return []string{DenyCapability} - } else { - perm := raw.(*ACLPermissions) - capabilities = perm.CapabilitiesBitmap - } - -CHECK: if capabilities&SudoCapabilityInt > 0 { pathCapabilities = append(pathCapabilities, SudoCapability) } @@ -265,7 +291,7 @@ CHECK: } // AllowOperation is used to check if the given operation is permitted. -func (a *ACL) AllowOperation(req *logical.Request) (ret *ACLResults) { +func (a *ACL) AllowOperation(ctx context.Context, req *logical.Request, capCheckOnly bool) (ret *ACLResults) { ret = new(ACLResults) // Fast-path root @@ -276,7 +302,6 @@ func (a *ACL) AllowOperation(req *logical.Request) (ret *ACLResults) { return } op := req.Operation - path := req.Path // Help is always allowed if op == logical.HelpOperation { @@ -286,6 +311,12 @@ func (a *ACL) AllowOperation(req *logical.Request) (ret *ACLResults) { var permissions *ACLPermissions + ns, err := namespace.FromContext(ctx) + if err != nil { + return + } + path := ns.Path + req.Path + // Find an exact matching rule, look for glob if no match var capabilities uint32 raw, ok := a.exactRules.Get(path) @@ -307,10 +338,9 @@ func (a *ACL) AllowOperation(req *logical.Request) (ret *ACLResults) { _, raw, ok = a.globRules.LongestPrefix(path) if !ok { return - } else { - permissions = raw.(*ACLPermissions) - capabilities = permissions.CapabilitiesBitmap } + permissions = raw.(*ACLPermissions) + capabilities = permissions.CapabilitiesBitmap CHECK: // Check if the minimum permissions are met @@ -318,6 +348,16 @@ CHECK: // only need to check for the existence of other values ret.RootPrivs = capabilities&SudoCapabilityInt > 0 + // This is after the RootPrivs check so we can gate on it being from sudo + // rather than policy root + if capCheckOnly { + ret.CapabilitiesBitmap = capabilities + return ret + } + + ret.MFAMethods = permissions.MFAMethods + ret.ControlGroup = permissions.ControlGroup + operationAllowed := false switch op { case logical.ReadOperation: @@ -427,31 +467,33 @@ CHECK: ret.Allowed = true return } -func (c *Core) performPolicyChecks(ctx context.Context, acl *ACL, te *logical.TokenEntry, req *logical.Request, inEntity *identity.Entity, opts *PolicyCheckOpts) (ret *AuthResults) { - ret = new(AuthResults) + +func (c *Core) performPolicyChecks(ctx context.Context, acl *ACL, te *logical.TokenEntry, req *logical.Request, inEntity *identity.Entity, opts *PolicyCheckOpts) *AuthResults { + ret := new(AuthResults) // First, perform normal ACL checks if requested. The only time no ACL // should be applied is if we are only processing EGPs against a login // path in which case opts.Unauth will be set. if acl != nil && !opts.Unauth { - ret.ACLResults = acl.AllowOperation(req) + ret.ACLResults = acl.AllowOperation(ctx, req, false) ret.RootPrivs = ret.ACLResults.RootPrivs // Root is always allowed; skip Sentinel/MFA checks if ret.ACLResults.IsRoot { - //c.logger.Warn("policy: token is root, skipping checks") + //logger.Warn("token is root, skipping checks") ret.Allowed = true - return + return ret } if !ret.ACLResults.Allowed { - return + return ret } if !ret.RootPrivs && opts.RootPrivsRequired { - return + return ret } } - ret.Allowed = true - return + c.performEntPolicyChecks(ctx, acl, te, req, inEntity, opts, ret) + + return ret } func valueInParameterList(v interface{}, list []interface{}) bool { diff --git a/vault/acl_test.go b/vault/acl_test.go index 519fe8772f..f76c918f3e 100644 --- a/vault/acl_test.go +++ b/vault/acl_test.go @@ -1,62 +1,165 @@ package vault import ( + "context" "reflect" "sync" "testing" "time" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/logical" ) -func TestACL_Capabilities(t *testing.T) { - // Create the root policy ACL +func TestACL_NewACL(t *testing.T) { + t.Run("root-ns", func(t *testing.T) { + t.Parallel() + testNewACL(t, namespace.RootNamespace) + }) +} + +func testNewACL(t *testing.T, ns *namespace.Namespace) { + ctx := namespace.ContextWithNamespace(context.Background(), ns) policy := []*Policy{&Policy{Name: "root"}} - acl, err := NewACL(policy) + _, err := NewACL(ctx, policy) + switch ns.ID { + case namespace.RootNamespaceID: + if err != nil { + t.Fatal(err) + } + default: + if err == nil { + t.Fatal("expected an error") + } + } +} + +func TestACL_MFAMethods(t *testing.T) { + t.Run("root-ns", func(t *testing.T) { + t.Parallel() + testACLMFAMethods(t, namespace.RootNamespace) + }) +} + +func testACLMFAMethods(t *testing.T, ns *namespace.Namespace) { + mfaRules := ` +path "secret/foo/*" { + mfa_methods = ["mfa_method_1", "mfa_method_2", "mfa_method_3"] +} +path "secret/exact/path" { + mfa_methods = ["mfa_method_4", "mfa_method_5"] +} +path "secret/split/definition" { + mfa_methods = ["mfa_method_6", "mfa_method_7"] +} +path "secret/split/definition" { + mfa_methods = ["mfa_method_7", "mfa_method_8", "mfa_method_9"] +} + ` + + policy, err := ParseACLPolicy(ns, mfaRules) + if err != nil { + t.Fatal(err) + } + + ctx := namespace.ContextWithNamespace(context.Background(), ns) + acl, err := NewACL(ctx, []*Policy{policy}) + if err != nil { + t.Fatal(err) + } + + request := &logical.Request{ + Operation: logical.UpdateOperation, + Path: "secret/foo/testing/glob/pattern", + } + + actual := acl.AllowOperation(ctx, request, false).MFAMethods + expected := []string{"mfa_method_1", "mfa_method_2", "mfa_method_3"} + + if !reflect.DeepEqual(expected, actual) { + t.Fatalf("bad: MFA methods; expected: %#v\n actual: %#v\n", expected, actual) + } + + request.Path = "secret/exact/path" + actual = acl.AllowOperation(ctx, request, false).MFAMethods + expected = []string{"mfa_method_4", "mfa_method_5"} + + if !reflect.DeepEqual(expected, actual) { + t.Fatalf("bad: MFA methods; expected: %#v\n actual: %#v\n", expected, actual) + } + + request.Path = "secret/split/definition" + actual = acl.AllowOperation(ctx, request, false).MFAMethods + expected = []string{"mfa_method_6", "mfa_method_7", "mfa_method_8", "mfa_method_9"} + + if !reflect.DeepEqual(expected, actual) { + t.Fatalf("bad: MFA methods; expected: %#v\n actual: %#v\n", expected, actual) + } +} + +func TestACL_Capabilities(t *testing.T) { + t.Run("root-ns", func(t *testing.T) { + t.Parallel() + policy := []*Policy{&Policy{Name: "root"}} + ctx := namespace.RootContext(nil) + acl, err := NewACL(ctx, policy) + if err != nil { + t.Fatalf("err: %v", err) + } + + actual := acl.Capabilities(ctx, "any/path") + expected := []string{"root"} + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("bad: got\n%#v\nexpected\n%#v\n", actual, expected) + } + testACLCapabilities(t, namespace.RootNamespace) + }) +} + +func testACLCapabilities(t *testing.T, ns *namespace.Namespace) { + // Create the root policy ACL + ctx := namespace.ContextWithNamespace(context.Background(), ns) + policy, err := ParseACLPolicy(ns, aclPolicy) if err != nil { t.Fatalf("err: %v", err) } - actual := acl.Capabilities("any/path") - expected := []string{"root"} + acl, err := NewACL(ctx, []*Policy{policy}) + if err != nil { + t.Fatalf("err: %v", err) + } + + actual := acl.Capabilities(ctx, "dev") + expected := []string{"deny"} if !reflect.DeepEqual(actual, expected) { - t.Fatalf("bad: got\n%#v\nexpected\n%#v\n", actual, expected) + t.Fatalf("bad: path: %s\ngot\n%#v\nexpected\n%#v\n", "deny", actual, expected) } - policies, err := ParseACLPolicy(aclPolicy) - if err != nil { - t.Fatalf("err: %v", err) - } - - acl, err = NewACL([]*Policy{policies}) - if err != nil { - t.Fatalf("err: %v", err) - } - - actual = acl.Capabilities("dev") - expected = []string{"deny"} - if !reflect.DeepEqual(actual, expected) { - t.Fatalf("bad: path:%s\ngot\n%#v\nexpected\n%#v\n", "deny", actual, expected) - } - - actual = acl.Capabilities("dev/") + actual = acl.Capabilities(ctx, "dev/") expected = []string{"sudo", "read", "list", "update", "delete", "create"} if !reflect.DeepEqual(actual, expected) { - t.Fatalf("bad: path:%s\ngot\n%#v\nexpected\n%#v\n", "dev/", actual, expected) + t.Fatalf("bad: path: %s\ngot\n%#v\nexpected\n%#v\n", "dev/", actual, expected) } - actual = acl.Capabilities("stage/aws/test") + actual = acl.Capabilities(ctx, "stage/aws/test") expected = []string{"sudo", "read", "list", "update"} if !reflect.DeepEqual(actual, expected) { - t.Fatalf("bad: path:%s\ngot\n%#v\nexpected\n%#v\n", "stage/aws/test", actual, expected) + t.Fatalf("bad: path: %s\ngot\n%#v\nexpected\n%#v\n", "stage/aws/test", actual, expected) } - } func TestACL_Root(t *testing.T) { - // Create the root policy ACL + t.Run("root-ns", func(t *testing.T) { + t.Parallel() + testACLRoot(t, namespace.RootNamespace) + }) +} + +func testACLRoot(t *testing.T, ns *namespace.Namespace) { + // Create the root policy ACL. Always create on root namespace regardless of + // which namespace to ACL check on. policy := []*Policy{&Policy{Name: "root"}} - acl, err := NewACL(policy) + acl, err := NewACL(namespace.TestContext(), policy) if err != nil { t.Fatalf("err: %v", err) } @@ -64,7 +167,9 @@ func TestACL_Root(t *testing.T) { request := new(logical.Request) request.Operation = logical.UpdateOperation request.Path = "sys/mount/foo" - authResults := acl.AllowOperation(request) + ctx := namespace.ContextWithNamespace(context.Background(), ns) + + authResults := acl.AllowOperation(ctx, request, false) if !authResults.RootPrivs { t.Fatalf("expected root") } @@ -74,22 +179,32 @@ func TestACL_Root(t *testing.T) { } func TestACL_Single(t *testing.T) { - policy, err := ParseACLPolicy(aclPolicy) + t.Run("root-ns", func(t *testing.T) { + t.Parallel() + testACLSingle(t, namespace.RootNamespace) + }) +} + +func testACLSingle(t *testing.T, ns *namespace.Namespace) { + policy, err := ParseACLPolicy(ns, aclPolicy) if err != nil { t.Fatalf("err: %v", err) } - acl, err := NewACL([]*Policy{policy}) + ctx := namespace.ContextWithNamespace(context.Background(), ns) + acl, err := NewACL(ctx, []*Policy{policy}) if err != nil { t.Fatalf("err: %v", err) } // Type of operation is not important here as we only care about checking // sudo/root + ctx = namespace.ContextWithNamespace(context.Background(), ns) request := new(logical.Request) request.Operation = logical.ReadOperation request.Path = "sys/mount/foo" - authResults := acl.AllowOperation(request) + + authResults := acl.AllowOperation(ctx, request, false) if authResults.RootPrivs { t.Fatalf("unexpected root") } @@ -125,10 +240,12 @@ func TestACL_Single(t *testing.T) { } for _, tc := range tcases { + ctx := namespace.ContextWithNamespace(context.Background(), ns) request := new(logical.Request) request.Operation = tc.op request.Path = tc.path - authResults := acl.AllowOperation(request) + + authResults := acl.AllowOperation(ctx, request, false) if authResults.Allowed != tc.allowed { t.Fatalf("bad: case %#v: %v, %v", tc, authResults.Allowed, authResults.RootPrivs) } @@ -139,30 +256,34 @@ func TestACL_Single(t *testing.T) { } func TestACL_Layered(t *testing.T) { - policy1, err := ParseACLPolicy(aclPolicy) - if err != nil { - t.Fatalf("err: %v", err) - } + t.Run("root-ns", func(t *testing.T) { + t.Parallel() + policy1, err := ParseACLPolicy(namespace.RootNamespace, aclPolicy) + if err != nil { + t.Fatalf("err: %v", err) + } - policy2, err := ParseACLPolicy(aclPolicy2) - if err != nil { - t.Fatalf("err: %v", err) - } - - acl, err := NewACL([]*Policy{policy1, policy2}) - if err != nil { - t.Fatalf("err: %v", err) - } - testLayeredACL(t, acl) + policy2, err := ParseACLPolicy(namespace.RootNamespace, aclPolicy2) + if err != nil { + t.Fatalf("err: %v", err) + } + acl, err := NewACL(namespace.RootContext(nil), []*Policy{policy1, policy2}) + if err != nil { + t.Fatalf("err: %v", err) + } + testLayeredACL(t, acl, namespace.RootNamespace) + }) } -func testLayeredACL(t *testing.T, acl *ACL) { +func testLayeredACL(t *testing.T, acl *ACL, ns *namespace.Namespace) { // Type of operation is not important here as we only care about checking // sudo/root + ctx := namespace.ContextWithNamespace(context.Background(), ns) request := new(logical.Request) request.Operation = logical.ReadOperation request.Path = "sys/mount/foo" - authResults := acl.AllowOperation(request) + + authResults := acl.AllowOperation(ctx, request, false) if authResults.RootPrivs { t.Fatalf("unexpected root") } @@ -203,10 +324,12 @@ func testLayeredACL(t *testing.T, acl *ACL) { } for _, tc := range tcases { + ctx := namespace.ContextWithNamespace(context.Background(), ns) request := new(logical.Request) request.Operation = tc.op request.Path = tc.path - authResults := acl.AllowOperation(request) + + authResults := acl.AllowOperation(ctx, request, false) if authResults.Allowed != tc.allowed { t.Fatalf("bad: case %#v: %v, %v", tc, authResults.Allowed, authResults.RootPrivs) } @@ -217,11 +340,19 @@ func testLayeredACL(t *testing.T, acl *ACL) { } func TestACL_PolicyMerge(t *testing.T) { - policy, err := ParseACLPolicy(mergingPolicies) + t.Run("root-ns", func(t *testing.T) { + t.Parallel() + testACLPolicyMerge(t, namespace.RootNamespace) + }) +} + +func testACLPolicyMerge(t *testing.T, ns *namespace.Namespace) { + policy, err := ParseACLPolicy(ns, mergingPolicies) if err != nil { t.Fatalf("err: %v", err) } - acl, err := NewACL([]*Policy{policy}) + ctx := namespace.ContextWithNamespace(context.Background(), ns) + acl, err := NewACL(ctx, []*Policy{policy}) if err != nil { t.Fatalf("err: %v", err) } @@ -252,9 +383,10 @@ func TestACL_PolicyMerge(t *testing.T) { } for _, tc := range tcases { - raw, ok := acl.exactRules.Get(tc.path) + policyPath := ns.Path + tc.path + raw, ok := acl.exactRules.Get(policyPath) if !ok { - t.Fatalf("Could not find acl entry for path %s", tc.path) + t.Fatalf("Could not find acl entry for path %s", policyPath) } p := raw.(*ACLPermissions) @@ -277,11 +409,19 @@ func TestACL_PolicyMerge(t *testing.T) { } func TestACL_AllowOperation(t *testing.T) { - policy, err := ParseACLPolicy(permissionsPolicy) + t.Run("root-ns", func(t *testing.T) { + t.Parallel() + testACLAllowOperation(t, namespace.RootNamespace) + }) +} + +func testACLAllowOperation(t *testing.T, ns *namespace.Namespace) { + policy, err := ParseACLPolicy(ns, permissionsPolicy) if err != nil { t.Fatalf("err: %v", err) } - acl, err := NewACL([]*Policy{policy}) + ctx := namespace.ContextWithNamespace(context.Background(), ns) + acl, err := NewACL(ctx, []*Policy{policy}) if err != nil { t.Fatalf("err: %v", err) } @@ -328,7 +468,11 @@ func TestACL_AllowOperation(t *testing.T) { } for _, tc := range tcases { - request := logical.Request{Path: tc.path, Data: make(map[string]interface{})} + request := &logical.Request{ + Path: tc.path, + Data: make(map[string]interface{}), + } + for _, parameter := range tc.parameters { request.Data[parameter] = "" } @@ -339,7 +483,8 @@ func TestACL_AllowOperation(t *testing.T) { } for _, op := range toperations { request.Operation = op - authResults := acl.AllowOperation(&request) + ctx := namespace.ContextWithNamespace(context.Background(), ns) + authResults := acl.AllowOperation(ctx, request, false) if authResults.Allowed != tc.allowed { t.Fatalf("bad: case %#v: %v", tc, authResults.Allowed) } @@ -348,12 +493,20 @@ func TestACL_AllowOperation(t *testing.T) { } func TestACL_ValuePermissions(t *testing.T) { - policy, err := ParseACLPolicy(valuePermissionsPolicy) + t.Run("root-ns", func(t *testing.T) { + t.Parallel() + testACLValuePermissions(t, namespace.RootNamespace) + }) +} + +func testACLValuePermissions(t *testing.T, ns *namespace.Namespace) { + policy, err := ParseACLPolicy(ns, valuePermissionsPolicy) if err != nil { t.Fatalf("err: %v", err) } - acl, err := NewACL([]*Policy{policy}) + ctx := namespace.ContextWithNamespace(context.Background(), ns) + acl, err := NewACL(ctx, []*Policy{policy}) if err != nil { t.Fatalf("err: %v", err) } @@ -408,13 +561,18 @@ func TestACL_ValuePermissions(t *testing.T) { } for _, tc := range tcases { - request := logical.Request{Path: tc.path, Data: make(map[string]interface{})} + request := &logical.Request{ + Path: tc.path, + Data: make(map[string]interface{}), + } + ctx := namespace.ContextWithNamespace(context.Background(), ns) + for i, parameter := range tc.parameters { request.Data[parameter] = tc.values[i] } for _, op := range toperations { request.Operation = op - authResults := acl.AllowOperation(&request) + authResults := acl.AllowOperation(ctx, request, false) if authResults.Allowed != tc.allowed { t.Fatalf("bad: case %#v: %v", tc, authResults.Allowed) } @@ -424,7 +582,7 @@ func TestACL_ValuePermissions(t *testing.T) { // NOTE: this test doesn't catch any races ATM func TestACL_CreationRace(t *testing.T) { - policy, err := ParseACLPolicy(valuePermissionsPolicy) + policy, err := ParseACLPolicy(namespace.RootNamespace, valuePermissionsPolicy) if err != nil { t.Fatalf("err: %v", err) } @@ -440,7 +598,7 @@ func TestACL_CreationRace(t *testing.T) { if time.Now().After(stopTime) { return } - _, err := NewACL([]*Policy{policy}) + _, err := NewACL(namespace.TestContext(), []*Policy{policy}) if err != nil { t.Fatalf("err: %v", err) } diff --git a/vault/acl_util.go b/vault/acl_util.go new file mode 100644 index 0000000000..ade4c724c8 --- /dev/null +++ b/vault/acl_util.go @@ -0,0 +1,14 @@ +// +build !enterprise + +package vault + +import ( + "context" + + "github.com/hashicorp/vault/helper/identity" + "github.com/hashicorp/vault/logical" +) + +func (c *Core) performEntPolicyChecks(ctx context.Context, acl *ACL, te *logical.TokenEntry, req *logical.Request, inEntity *identity.Entity, opts *PolicyCheckOpts, ret *AuthResults) { + ret.Allowed = true +} diff --git a/vault/audit.go b/vault/audit.go index 491f47196a..0462dcd170 100644 --- a/vault/audit.go +++ b/vault/audit.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/go-uuid" "github.com/hashicorp/vault/audit" "github.com/hashicorp/vault/helper/jsonutil" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/helper/salt" "github.com/hashicorp/vault/logical" ) @@ -39,7 +40,7 @@ var ( ) // enableAudit is used to enable a new audit backend -func (c *Core) enableAudit(ctx context.Context, entry *MountEntry) error { +func (c *Core) enableAudit(ctx context.Context, entry *MountEntry, updateStorage bool) error { // Ensure we end the path in a slash if !strings.HasSuffix(entry.Path, "/") { entry.Path += "/" @@ -81,14 +82,16 @@ func (c *Core) enableAudit(ctx context.Context, entry *MountEntry) error { } entry.Accessor = accessor } - viewPath := auditBarrierPrefix + entry.UUID + "/" + viewPath := entry.ViewPath() view := NewBarrierView(c.barrier, viewPath) + addAuditPathChecker(c, entry, view, viewPath) + origViewReadOnlyErr := view.getReadOnlyErr() // Mark the view as read-only until the mounting is complete and // ensure that it is reset after. This ensures that there will be no // writes during the construction of the backend. view.setReadOnlyErr(logical.ErrSetupReadOnly) - defer view.setReadOnlyErr(nil) + defer view.setReadOnlyErr(origViewReadOnlyErr) // Lookup the new backend backend, err := c.newAuditBackend(ctx, entry, view, entry.Options) @@ -101,22 +104,33 @@ func (c *Core) enableAudit(ctx context.Context, entry *MountEntry) error { newTable := c.audit.shallowClone() newTable.Entries = append(newTable.Entries, entry) - if err := c.persistAudit(ctx, newTable, entry.Local); err != nil { - return errors.New("failed to update audit table") + + ns, err := namespace.FromContext(ctx) + if err != nil { + return err + } + entry.NamespaceID = ns.ID + entry.namespace = ns + + if updateStorage { + if err := c.persistAudit(ctx, newTable, entry.Local); err != nil { + return errors.New("failed to update audit table") + } } c.audit = newTable // Register the backend - c.auditBroker.Register(entry.Path, backend, view) + c.auditBroker.Register(entry.Path, backend, view, entry.Local) if c.logger.IsInfo() { c.logger.Info("enabled audit backend", "path", entry.Path, "type", entry.Type) } + return nil } // disableAudit is used to disable an existing audit backend -func (c *Core) disableAudit(ctx context.Context, path string) (bool, error) { +func (c *Core) disableAudit(ctx context.Context, path string, updateStorage bool) (bool, error) { // Ensure we end the path in a slash if !strings.HasSuffix(path, "/") { path += "/" @@ -127,7 +141,10 @@ func (c *Core) disableAudit(ctx context.Context, path string) (bool, error) { defer c.auditLock.Unlock() newTable := c.audit.shallowClone() - entry := newTable.remove(path) + entry, err := newTable.remove(ctx, path) + if err != nil { + return false, err + } // Ensure there was a match if entry == nil { @@ -142,9 +159,11 @@ func (c *Core) disableAudit(ctx context.Context, path string) (bool, error) { newTable.Entries = nil } - // Update the audit table - if err := c.persistAudit(ctx, newTable, entry.Local); err != nil { - return true, errors.New("failed to update audit table") + if updateStorage { + // Update the audit table + if err := c.persistAudit(ctx, newTable, entry.Local); err != nil { + return true, errors.New("failed to update audit table") + } } c.audit = newTable @@ -155,6 +174,8 @@ func (c *Core) disableAudit(ctx context.Context, path string) (bool, error) { c.logger.Info("disabled audit backend", "path", path) } + removeAuditPathChecker(c, entry) + return true, nil } @@ -222,6 +243,20 @@ func (c *Core) loadAudits(ctx context.Context) error { entry.Accessor = accessor needPersist = true } + + if entry.NamespaceID == "" { + entry.NamespaceID = namespace.RootNamespaceID + needPersist = true + } + // Get the namespace from the namespace ID and load it in memory + ns, err := NamespaceByID(ctx, entry.NamespaceID, c) + if err != nil { + return err + } + if ns == nil { + return namespace.ErrNoNamespace + } + entry.namespace = ns } if !needPersist { @@ -319,15 +354,17 @@ func (c *Core) setupAudits(ctx context.Context) error { for _, entry := range c.audit.Entries { // Create a barrier view using the UUID - viewPath := auditBarrierPrefix + entry.UUID + "/" + viewPath := entry.ViewPath() view := NewBarrierView(c.barrier, viewPath) + addAuditPathChecker(c, entry, view, viewPath) + origViewReadOnlyErr := view.getReadOnlyErr() // Mark the view as read-only until the mounting is complete and // ensure that it is reset after. This ensures that there will be no // writes during the construction of the backend. view.setReadOnlyErr(logical.ErrSetupReadOnly) c.postUnsealFuncs = append(c.postUnsealFuncs, func() { - view.setReadOnlyErr(nil) + view.setReadOnlyErr(origViewReadOnlyErr) }) // Initialize the backend @@ -342,9 +379,9 @@ func (c *Core) setupAudits(ctx context.Context) error { } // Mount the backend - broker.Register(entry.Path, backend, view) + broker.Register(entry.Path, backend, view, entry.Local) - successCount += 1 + successCount++ } if len(c.audit.Entries) > 0 && successCount == 0 { @@ -364,6 +401,7 @@ func (c *Core) teardownAudits() error { if c.audit != nil { for _, entry := range c.audit.Entries { c.removeAuditReloadFunc(entry) + removeAuditPathChecker(c, entry) } } diff --git a/vault/audit_broker.go b/vault/audit_broker.go index ae6f48ecc4..c5b5652745 100644 --- a/vault/audit_broker.go +++ b/vault/audit_broker.go @@ -15,6 +15,7 @@ import ( type backendEntry struct { backend audit.Backend view *BarrierView + local bool } // AuditBroker is used to provide a single ingest interface to auditable @@ -35,12 +36,13 @@ func NewAuditBroker(log log.Logger) *AuditBroker { } // Register is used to add new audit backend to the broker -func (a *AuditBroker) Register(name string, b audit.Backend, v *BarrierView) { +func (a *AuditBroker) Register(name string, b audit.Backend, v *BarrierView, local bool) { a.Lock() defer a.Unlock() a.backends[name] = backendEntry{ backend: b, view: v, + local: local, } } @@ -59,6 +61,17 @@ func (a *AuditBroker) IsRegistered(name string) bool { return ok } +// IsLocal is used to check if a given audit backend is registered +func (a *AuditBroker) IsLocal(name string) (bool, error) { + a.RLock() + defer a.RUnlock() + be, ok := a.backends[name] + if ok { + return be.local, nil + } + return false, fmt.Errorf("unknown audit backend %q", name) +} + // GetHash returns a hash using the salt of the given backend func (a *AuditBroker) GetHash(ctx context.Context, name string, input string) (string, error) { a.RLock() diff --git a/vault/audit_test.go b/vault/audit_test.go index 2b1e36fbc2..956d5036ce 100644 --- a/vault/audit_test.go +++ b/vault/audit_test.go @@ -17,6 +17,7 @@ import ( "github.com/hashicorp/vault/audit" "github.com/hashicorp/vault/helper/jsonutil" "github.com/hashicorp/vault/helper/logging" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/helper/salt" "github.com/hashicorp/vault/logical" "github.com/mitchellh/copystructure" @@ -122,7 +123,7 @@ func TestAudit_ReadOnlyViewDuringMount(t *testing.T) { Path: "foo", Type: "noop", } - err := c.enableAudit(context.Background(), me) + err := c.enableAudit(namespace.TestContext(), me, true) if err != nil { t.Fatalf("err: %v", err) } @@ -141,7 +142,7 @@ func TestCore_EnableAudit(t *testing.T) { Path: "foo", Type: "noop", } - err := c.enableAudit(context.Background(), me) + err := c.enableAudit(namespace.TestContext(), me, true) if err != nil { t.Fatalf("err: %v", err) } @@ -255,18 +256,22 @@ func TestCore_EnableAudit_Local(t *testing.T) { Type: auditTableType, Entries: []*MountEntry{ &MountEntry{ - Table: auditTableType, - Path: "noop/", - Type: "noop", - UUID: "abcd", - Accessor: "noop-abcd", + Table: auditTableType, + Path: "noop/", + Type: "noop", + UUID: "abcd", + Accessor: "noop-abcd", + NamespaceID: namespace.RootNamespaceID, + namespace: namespace.TestNamespace(), }, &MountEntry{ - Table: auditTableType, - Path: "noop2/", - Type: "noop", - UUID: "bcde", - Accessor: "noop-bcde", + Table: auditTableType, + Path: "noop2/", + Type: "noop", + UUID: "bcde", + Accessor: "noop-bcde", + NamespaceID: namespace.RootNamespaceID, + namespace: namespace.TestNamespace(), }, }, } @@ -334,7 +339,7 @@ func TestCore_DisableAudit(t *testing.T) { }, nil } - existed, err := c.disableAudit(context.Background(), "foo") + existed, err := c.disableAudit(namespace.TestContext(), "foo", true) if existed && err != nil { t.Fatalf("existed: %v; err: %v", existed, err) } @@ -344,12 +349,12 @@ func TestCore_DisableAudit(t *testing.T) { Path: "foo", Type: "noop", } - err = c.enableAudit(context.Background(), me) + err = c.enableAudit(namespace.TestContext(), me, true) if err != nil { t.Fatalf("err: %v", err) } - existed, err = c.disableAudit(context.Background(), "foo") + existed, err = c.disableAudit(namespace.TestContext(), "foo", true) if !existed || err != nil { t.Fatalf("existed: %v; err: %v", existed, err) } @@ -436,8 +441,8 @@ func TestAuditBroker_LogRequest(t *testing.T) { b := NewAuditBroker(l) a1 := &NoopAudit{} a2 := &NoopAudit{} - b.Register("foo", a1, nil) - b.Register("bar", a2, nil) + b.Register("foo", a1, nil, false) + b.Register("bar", a2, nil, false) auth := &logical.Auth{ ClientToken: "foo", @@ -522,8 +527,8 @@ func TestAuditBroker_LogResponse(t *testing.T) { b := NewAuditBroker(l) a1 := &NoopAudit{} a2 := &NoopAudit{} - b.Register("foo", a1, nil) - b.Register("bar", a2, nil) + b.Register("foo", a1, nil, false) + b.Register("bar", a2, nil, false) auth := &logical.Auth{ NumUses: 10, @@ -628,8 +633,8 @@ func TestAuditBroker_AuditHeaders(t *testing.T) { view := NewBarrierView(barrier, "headers/") a1 := &NoopAudit{} a2 := &NoopAudit{} - b.Register("foo", a1, nil) - b.Register("bar", a2, nil) + b.Register("foo", a1, nil, false) + b.Register("bar", a2, nil, false) auth := &logical.Auth{ ClientToken: "foo", diff --git a/vault/auth.go b/vault/auth.go index 594c46ccbf..1d5b671964 100644 --- a/vault/auth.go +++ b/vault/auth.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/go-uuid" "github.com/hashicorp/vault/helper/consts" "github.com/hashicorp/vault/helper/jsonutil" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/helper/strutil" "github.com/hashicorp/vault/logical" ) @@ -46,6 +47,11 @@ var ( // enableCredential is used to enable a new credential backend func (c *Core) enableCredential(ctx context.Context, entry *MountEntry) error { + return c.enableCredentialInternal(ctx, entry, MountTableUpdateStorage) +} + +// enableCredential is used to enable a new credential backend +func (c *Core) enableCredentialInternal(ctx context.Context, entry *MountEntry, updateStorage bool) error { // Ensure we end the path in a slash if !strings.HasSuffix(entry.Path, "/") { entry.Path += "/" @@ -59,15 +65,27 @@ func (c *Core) enableCredential(ctx context.Context, entry *MountEntry) error { c.authLock.Lock() defer c.authLock.Unlock() + ns, err := namespace.FromContext(ctx) + if err != nil { + return err + } + entry.NamespaceID = ns.ID + entry.namespace = ns + + // Populate cache + NamespaceByID(ctx, ns.ID, c) + // Look for matching name for _, ent := range c.auth.Entries { - switch { - // Existing is oauth/github/ new is oauth/ or - // existing is oauth/ and new is oauth/github/ - case strings.HasPrefix(ent.Path, entry.Path): - fallthrough - case strings.HasPrefix(entry.Path, ent.Path): - return logical.CodedError(409, "path is already in use") + if ns.ID == ent.NamespaceID { + switch { + // Existing is oauth/github/ new is oauth/ or + // existing is oauth/ and new is oauth/github/ + case strings.HasPrefix(ent.Path, entry.Path): + fallthrough + case strings.HasPrefix(entry.Path, ent.Path): + return logical.CodedError(409, "path is already in use") + } } } @@ -76,7 +94,7 @@ func (c *Core) enableCredential(ctx context.Context, entry *MountEntry) error { return fmt.Errorf("token credential backend cannot be instantiated") } - if conflict := c.router.MountConflict(credentialRoutePrefix + entry.Path); conflict != "" { + if conflict := c.router.MountConflict(ctx, credentialRoutePrefix+entry.Path); conflict != "" { return logical.CodedError(409, fmt.Sprintf("existing mount at %s", conflict)) } @@ -105,18 +123,28 @@ func (c *Core) enableCredential(ctx context.Context, entry *MountEntry) error { // Sync values to the cache entry.SyncCache() - viewPath := credentialBarrierPrefix + entry.UUID + "/" + viewPath := entry.ViewPath() view := NewBarrierView(c.barrier, viewPath) + + nilMount, err := preprocessMount(c, entry, view) + if err != nil { + return err + } + origViewReadOnlyErr := view.getReadOnlyErr() + // Mark the view as read-only until the mounting is complete and // ensure that it is reset after. This ensures that there will be no // writes during the construction of the backend. view.setReadOnlyErr(logical.ErrSetupReadOnly) - defer view.setReadOnlyErr(nil) + defer view.setReadOnlyErr(origViewReadOnlyErr) - var err error var backend logical.Backend + // Create the new backend sysView := c.mountEntrySysView(entry) - + conf := make(map[string]string) + if entry.Config.PluginName != "" { + conf["plugin_name"] = entry.Config.PluginName + } // Create the new backend backend, err = c.newCredentialBackend(ctx, entry, sysView, view) if err != nil { @@ -129,20 +157,34 @@ func (c *Core) enableCredential(ctx context.Context, entry *MountEntry) error { // Check for the correct backend type backendType := backend.Type() if entry.Type == "plugin" && backendType != logical.TypeCredential { - return fmt.Errorf("cannot mount %q of type %q as an auth method", entry.Config.PluginName, backendType) + return fmt.Errorf("cannot mount %q of type %q as an auth backend", entry.Config.PluginName, backendType) + } + + addPathCheckers(c, entry, backend, viewPath) + + // If the mount is filtered or we are on a DR secondary we don't want to + // keep the actual backend running, so we clean it up and set it to nil + // so the router does not have a pointer to the object. + if nilMount { + backend.Cleanup(ctx) + backend = nil } // Update the auth table newTable := c.auth.shallowClone() newTable.Entries = append(newTable.Entries, entry) - if err := c.persistAuth(ctx, newTable, &entry.Local); err != nil { - return errors.New("failed to update auth table") + if updateStorage { + if err := c.persistAuth(ctx, newTable, &entry.Local); err != nil { + if err == logical.ErrReadOnly && c.perfStandby { + return err + } + return errors.New("failed to update auth table") + } } c.auth = newTable - path := credentialRoutePrefix + entry.Path - if err := c.router.Mount(backend, path, entry, view); err != nil { + if err := c.router.Mount(backend, credentialRoutePrefix+entry.Path, entry, view); err != nil { return err } @@ -165,79 +207,121 @@ func (c *Core) disableCredential(ctx context.Context, path string) error { return fmt.Errorf("token credential backend cannot be disabled") } + return c.disableCredentialInternal(ctx, path, MountTableUpdateStorage) +} + +func (c *Core) disableCredentialInternal(ctx context.Context, path string, updateStorage bool) error { + path = credentialRoutePrefix + path + + ns, err := namespace.FromContext(ctx) + if err != nil { + return err + } + + // Verify exact match of the route + match := c.router.MatchingMount(ctx, path) + if match == "" || ns.Path+path != match { + return fmt.Errorf("no matching mount") + } + // Store the view for this backend - fullPath := credentialRoutePrefix + path - view := c.router.MatchingStorageByAPIPath(fullPath) + view := c.router.MatchingStorageByAPIPath(ctx, path) if view == nil { - return fmt.Errorf("no matching backend %q", fullPath) + return fmt.Errorf("no matching backend %q", path) } // Get the backend/mount entry for this path, used to remove ignored // replication prefixes - backend := c.router.MatchingBackend(fullPath) - entry := c.router.MatchingMountEntry(fullPath) + backend := c.router.MatchingBackend(ctx, path) + entry := c.router.MatchingMountEntry(ctx, path) // Mark the entry as tainted - if err := c.taintCredEntry(ctx, path); err != nil { + if err := c.taintCredEntry(ctx, path, updateStorage); err != nil { return err } // Taint the router path to prevent routing - if err := c.router.Taint(fullPath); err != nil { + if err := c.router.Taint(ctx, path); err != nil { return err } - if backend != nil { + if c.expiration != nil && backend != nil { // Revoke credentials from this path - if err := c.expiration.RevokePrefix(c.activeContext, fullPath, true); err != nil { + ns, err := namespace.FromContext(ctx) + if err != nil { return err } + revokeCtx := namespace.ContextWithNamespace(c.activeContext, ns) + if err := c.expiration.RevokePrefix(revokeCtx, path, true); err != nil { + return err + } + } + if backend != nil { // Call cleanup function if it exists backend.Cleanup(ctx) } // Unmount the backend - if err := c.router.Unmount(ctx, fullPath); err != nil { + if err := c.router.Unmount(ctx, path); err != nil { return err } + viewPath := entry.ViewPath() switch { - case entry.Local, !c.ReplicationState().HasState(consts.ReplicationPerformanceSecondary): + case !updateStorage: + case c.IsDRSecondary(), entry.Local, !c.ReplicationState().HasState(consts.ReplicationPerformanceSecondary): // Have writable storage, remove the whole thing if err := logical.ClearView(ctx, view); err != nil { c.logger.Error("failed to clear view for path being unmounted", "error", err, "path", path) return err } + case !entry.Local && c.ReplicationState().HasState(consts.ReplicationPerformanceSecondary): + if err := clearIgnoredPaths(ctx, c, backend, viewPath); err != nil { + return err + } } // Remove the mount table entry - if err := c.removeCredEntry(ctx, path); err != nil { + if err := c.removeCredEntry(ctx, strings.TrimPrefix(path, credentialRoutePrefix), updateStorage); err != nil { return err } + + removePathCheckers(c, entry, viewPath) + if c.logger.IsInfo() { c.logger.Info("disabled credential backend", "path", path) } + return nil } // removeCredEntry is used to remove an entry in the auth table -func (c *Core) removeCredEntry(ctx context.Context, path string) error { +func (c *Core) removeCredEntry(ctx context.Context, path string, updateStorage bool) error { c.authLock.Lock() defer c.authLock.Unlock() // Taint the entry from the auth table newTable := c.auth.shallowClone() - entry := newTable.remove(path) + entry, err := newTable.remove(ctx, path) + if err != nil { + return err + } if entry == nil { c.logger.Error("nil entry found removing entry in auth table", "path", path) return logical.CodedError(500, "failed to remove entry in auth table") } - // Update the auth table - if err := c.persistAuth(ctx, newTable, &entry.Local); err != nil { - return errors.New("failed to update auth table") + if updateStorage { + // Update the auth table + if err := c.persistAuth(ctx, newTable, &entry.Local); err != nil { + if err == logical.ErrReadOnly && c.perfStandby { + return err + } + + return errors.New("failed to update auth table") + } } c.auth = newTable @@ -250,7 +334,7 @@ func (c *Core) removeCredEntry(ctx context.Context, path string) error { // paths func (c *Core) remountCredEntryForce(ctx context.Context, path string) error { fullPath := credentialRoutePrefix + path - me := c.router.MatchingMountEntry(fullPath) + me := c.router.MatchingMountEntry(ctx, fullPath) if me == nil { return fmt.Errorf("cannot find mount for path %q", path) } @@ -267,23 +351,31 @@ func (c *Core) remountCredEntryForce(ctx context.Context, path string) error { } // taintCredEntry is used to mark an entry in the auth table as tainted -func (c *Core) taintCredEntry(ctx context.Context, path string) error { +func (c *Core) taintCredEntry(ctx context.Context, path string, updateStorage bool) error { c.authLock.Lock() defer c.authLock.Unlock() // Taint the entry from the auth table // We do this on the original since setting the taint operates // on the entries which a shallow clone shares anyways - entry := c.auth.setTaint(path, true) + entry, err := c.auth.setTaint(ctx, strings.TrimPrefix(path, credentialRoutePrefix), true) + if err != nil { + return err + } // Ensure there was a match if entry == nil { return fmt.Errorf("no matching backend") } - // Update the auth table - if err := c.persistAuth(ctx, c.auth, &entry.Local); err != nil { - return errors.New("failed to update auth table") + if updateStorage { + // Update the auth table + if err := c.persistAuth(ctx, c.auth, &entry.Local); err != nil { + if err == logical.ErrReadOnly && c.perfStandby { + return err + } + return errors.New("failed to update auth table") + } } return nil @@ -291,9 +383,6 @@ func (c *Core) taintCredEntry(ctx context.Context, path string) error { // loadCredentials is invoked as part of postUnseal to load the auth table func (c *Core) loadCredentials(ctx context.Context) error { - authTable := &MountTable{} - localAuthTable := &MountTable{} - // Load the existing mount table raw, err := c.barrier.Get(ctx, coreAuthConfigPath) if err != nil { @@ -310,9 +399,10 @@ func (c *Core) loadCredentials(ctx context.Context) error { defer c.authLock.Unlock() if raw != nil { - if err := jsonutil.DecodeJSON(raw.Value, authTable); err != nil { - c.logger.Error("failed to decode auth table", "error", err) - return errLoadAuthFailed + authTable, err := c.decodeMountTable(ctx, raw.Value) + if err != nil { + c.logger.Error("failed to decompress and/or decode the auth table", "error", err) + return err } c.auth = authTable } @@ -324,9 +414,10 @@ func (c *Core) loadCredentials(ctx context.Context) error { } if rawLocal != nil { - if err := jsonutil.DecodeJSON(rawLocal.Value, localAuthTable); err != nil { - c.logger.Error("failed to decode local auth table", "error", err) - return errLoadAuthFailed + localAuthTable, err := c.decodeMountTable(ctx, rawLocal.Value) + if err != nil { + c.logger.Error("failed to decompress and/or decode the local mount table", "error", err) + return err } if localAuthTable != nil && len(localAuthTable.Entries) > 0 { c.auth.Entries = append(c.auth.Entries, localAuthTable.Entries...) @@ -362,6 +453,19 @@ func (c *Core) loadCredentials(ctx context.Context) error { needPersist = true } + if entry.NamespaceID == "" { + entry.NamespaceID = namespace.RootNamespaceID + needPersist = true + } + ns, err := NamespaceByID(ctx, entry.NamespaceID, c) + if err != nil { + return err + } + if ns == nil { + return namespace.ErrNoNamespace + } + entry.namespace = ns + // Sync values to the cache entry.SyncCache() } @@ -374,6 +478,7 @@ func (c *Core) loadCredentials(ctx context.Context) error { c.logger.Error("failed to persist auth table", "error", err) return errLoadAuthFailed } + return nil } @@ -455,34 +560,50 @@ func (c *Core) persistAuth(ctx context.Context, table *MountTable, local *bool) // setupCredentials is invoked after we've loaded the auth table to // initialize the credential backends and setup the router func (c *Core) setupCredentials(ctx context.Context) error { - var err error var persistNeeded bool - var backendType logical.BackendType c.authLock.Lock() defer c.authLock.Unlock() - for _, entry := range c.auth.Entries { + for _, entry := range c.auth.sortEntriesByPathDepth().Entries { var backend logical.Backend // Create a barrier view using the UUID - viewPath := credentialBarrierPrefix + entry.UUID + "/" + viewPath := entry.ViewPath() + + // Singleton mounts cannot be filtered on a per-secondary basis + // from replication + if strutil.StrListContains(singletonMounts, entry.Type) { + addFilterablePath(c, viewPath) + } + view := NewBarrierView(c.barrier, viewPath) + // Determining the replicated state of the mount + nilMount, err := preprocessMount(c, entry, view) + if err != nil { + return err + } + origViewReadOnlyErr := view.getReadOnlyErr() + // Mark the view as read-only until the mounting is complete and // ensure that it is reset after. This ensures that there will be no // writes during the construction of the backend. view.setReadOnlyErr(logical.ErrSetupReadOnly) if strutil.StrListContains(singletonMounts, entry.Type) { - defer view.setReadOnlyErr(nil) + defer view.setReadOnlyErr(origViewReadOnlyErr) } else { c.postUnsealFuncs = append(c.postUnsealFuncs, func() { - view.setReadOnlyErr(nil) + view.setReadOnlyErr(origViewReadOnlyErr) }) } // Initialize the backend sysView := c.mountEntrySysView(entry) + conf := make(map[string]string) + if entry.Config.PluginName != "" { + conf["plugin_name"] = entry.Config.PluginName + } backend, err = c.newCredentialBackend(ctx, entry, sysView, view) if err != nil { @@ -500,10 +621,22 @@ func (c *Core) setupCredentials(ctx context.Context) error { return fmt.Errorf("nil backend returned from %q factory", entry.Type) } - // Check for the correct backend type - backendType = backend.Type() - if entry.Type == "plugin" && backendType != logical.TypeCredential { - return fmt.Errorf("cannot mount %q of type %q as an auth backend", entry.Config.PluginName, backendType) + { + // Check for the correct backend type + backendType := backend.Type() + if entry.Type == "plugin" && backendType != logical.TypeCredential { + return fmt.Errorf("cannot mount %q of type %q as an auth backend", entry.Config.PluginName, backendType) + } + + addPathCheckers(c, entry, backend, viewPath) + } + + // If the mount is filtered or we are on a DR secondary we don't want to + // keep the actual backend running, so we clean it up and set it to nil + // so the router does not have a pointer to the object. + if nilMount { + backend.Cleanup(ctx) + backend = nil } ROUTER_MOUNT: @@ -515,9 +648,13 @@ func (c *Core) setupCredentials(ctx context.Context) error { return errLoadAuthFailed } + if c.logger.IsInfo() { + c.logger.Info("successfully enabled credential backend", "type", entry.Type, "path", entry.Path) + } + // Ensure the path is tainted if set in the mount table if entry.Tainted { - c.router.Taint(path) + c.router.Taint(ctx, path) } // Check if this is the token store @@ -526,11 +663,17 @@ func (c *Core) setupCredentials(ctx context.Context) error { // this is loaded *after* the normal mounts, including cubbyhole c.router.tokenStoreSaltFunc = c.tokenStore.Salt - c.tokenStore.cubbyholeBackend = c.router.MatchingBackend("cubbyhole/").(*CubbyholeBackend) + if !c.IsDRSecondary() { + c.tokenStore.cubbyholeBackend = c.router.MatchingBackend(ctx, cubbyholeMountPath).(*CubbyholeBackend) + } } + + // Populate cache + NamespaceByID(ctx, entry.NamespaceID, c) } if persistNeeded { + // persist non-local auth return c.persistAuth(ctx, c.auth, nil) } @@ -546,10 +689,13 @@ func (c *Core) teardownCredentials(ctx context.Context) error { if c.auth != nil { authTable := c.auth.shallowClone() for _, e := range authTable.Entries { - backend := c.router.MatchingBackend(credentialRoutePrefix + e.Path) + backend := c.router.MatchingBackend(namespace.ContextWithNamespace(ctx, e.namespace), credentialRoutePrefix+e.Path) if backend != nil { backend.Cleanup(ctx) } + + viewPath := e.ViewPath() + removePathCheckers(c, e, viewPath) } } @@ -564,6 +710,7 @@ func (c *Core) newCredentialBackend(ctx context.Context, entry *MountEntry, sysV if alias, ok := credentialAliases[t]; ok { t = alias } + f, ok := c.credentialBackends[t] if !ok { return nil, fmt.Errorf("unknown backend type: %q", t) diff --git a/vault/auth_test.go b/vault/auth_test.go index 2a24ab716f..a17373d268 100644 --- a/vault/auth_test.go +++ b/vault/auth_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/hashicorp/vault/helper/jsonutil" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/logical" ) @@ -28,7 +29,7 @@ func TestAuth_ReadOnlyViewDuringMount(t *testing.T) { Path: "foo", Type: "noop", } - err := c.enableCredential(context.Background(), me) + err := c.enableCredential(namespace.TestContext(), me) if err != nil { t.Fatalf("err: %v", err) } @@ -74,14 +75,14 @@ func TestCore_EnableCredential(t *testing.T) { Path: "foo", Type: "noop", } - err := c.enableCredential(context.Background(), me) + err := c.enableCredential(namespace.TestContext(), me) if err != nil { t.Fatalf("err: %v", err) } - match := c.router.MatchingMount("auth/foo/bar") + match := c.router.MatchingMount(namespace.TestContext(), "auth/foo/bar") if match != "auth/foo/" { - t.Fatalf("missing mount") + t.Fatalf("missing mount, match: %q", match) } conf := &CoreConfig{ @@ -130,6 +131,8 @@ func TestCore_EnableCredential_Local(t *testing.T) { UUID: "abcd", Accessor: "noop-abcd", BackendAwareUUID: "abcde", + NamespaceID: namespace.RootNamespaceID, + namespace: namespace.TestNamespace(), }, &MountEntry{ Table: credentialTableType, @@ -138,6 +141,8 @@ func TestCore_EnableCredential_Local(t *testing.T) { UUID: "bcde", Accessor: "noop-bcde", BackendAwareUUID: "bcdea", + NamespaceID: namespace.RootNamespaceID, + namespace: namespace.TestNamespace(), }, }, } @@ -208,13 +213,13 @@ func TestCore_EnableCredential_twice_409(t *testing.T) { Path: "foo", Type: "noop", } - err := c.enableCredential(context.Background(), me) + err := c.enableCredential(namespace.TestContext(), me) if err != nil { t.Fatalf("err: %v", err) } // 2nd should be a 409 error - err2 := c.enableCredential(context.Background(), me) + err2 := c.enableCredential(namespace.TestContext(), me) switch err2.(type) { case logical.HTTPCodedError: if err2.(logical.HTTPCodedError).Code() != 409 { @@ -232,7 +237,7 @@ func TestCore_EnableCredential_Token(t *testing.T) { Path: "foo", Type: "token", } - err := c.enableCredential(context.Background(), me) + err := c.enableCredential(namespace.TestContext(), me) if err.Error() != "token credential backend cannot be instantiated" { t.Fatalf("err: %v", err) } @@ -244,9 +249,9 @@ func TestCore_DisableCredential(t *testing.T) { return &NoopBackend{}, nil } - err := c.disableCredential(context.Background(), "foo") - if err != nil && !strings.HasPrefix(err.Error(), "no matching backend") { - t.Fatalf("err: %v", err) + err := c.disableCredential(namespace.TestContext(), "foo") + if err != nil && !strings.HasPrefix(err.Error(), "no matching mount") { + t.Fatal(err) } me := &MountEntry{ @@ -254,17 +259,17 @@ func TestCore_DisableCredential(t *testing.T) { Path: "foo", Type: "noop", } - err = c.enableCredential(context.Background(), me) + err = c.enableCredential(namespace.TestContext(), me) if err != nil { t.Fatalf("err: %v", err) } - err = c.disableCredential(context.Background(), "foo") + err = c.disableCredential(namespace.TestContext(), "foo") if err != nil { t.Fatalf("err: %v", err) } - match := c.router.MatchingMount("auth/foo/bar") + match := c.router.MatchingMount(namespace.TestContext(), "auth/foo/bar") if match != "" { t.Fatalf("backend present") } @@ -295,7 +300,7 @@ func TestCore_DisableCredential(t *testing.T) { func TestCore_DisableCredential_Protected(t *testing.T) { c, _, _ := TestCoreUnsealed(t) - err := c.disableCredential(context.Background(), "token") + err := c.disableCredential(namespace.TestContext(), "token") if err.Error() != "token credential backend cannot be disabled" { t.Fatalf("err: %v", err) } @@ -315,13 +320,13 @@ func TestCore_DisableCredential_Cleanup(t *testing.T) { Path: "foo", Type: "noop", } - err := c.enableCredential(context.Background(), me) + err := c.enableCredential(namespace.TestContext(), me) if err != nil { t.Fatalf("err: %v", err) } // Store the view - view := c.router.MatchingStorageByAPIPath("auth/foo/") + view := c.router.MatchingStorageByAPIPath(namespace.TestContext(), "auth/foo/") // Inject data se := &logical.StorageEntry{ @@ -342,7 +347,7 @@ func TestCore_DisableCredential_Cleanup(t *testing.T) { Operation: logical.ReadOperation, Path: "auth/foo/login", } - resp, err := c.HandleRequest(context.Background(), r) + resp, err := c.HandleRequest(namespace.TestContext(), r) if err != nil { t.Fatalf("err: %v", err) } @@ -351,13 +356,13 @@ func TestCore_DisableCredential_Cleanup(t *testing.T) { } // Disable should cleanup - err = c.disableCredential(context.Background(), "foo") + err = c.disableCredential(namespace.TestContext(), "foo") if err != nil { t.Fatalf("err: %v", err) } // Token should be revoked - te, err := c.tokenStore.Lookup(context.Background(), resp.Auth.ClientToken) + te, err := c.tokenStore.Lookup(namespace.TestContext(), resp.Auth.ClientToken) if err != nil { t.Fatalf("err: %v", err) } diff --git a/vault/barrier_view.go b/vault/barrier_view.go index cc1a4251cd..94fbac9a8d 100644 --- a/vault/barrier_view.go +++ b/vault/barrier_view.go @@ -21,6 +21,7 @@ type BarrierView struct { prefix string readOnlyErr error readOnlyErrLock sync.RWMutex + iCheck interface{} } var ( @@ -36,6 +37,10 @@ func NewBarrierView(barrier BarrierStorage, prefix string) *BarrierView { } } +func (v *BarrierView) setICheck(iCheck interface{}) { + v.iCheck = iCheck +} + func (v *BarrierView) setReadOnlyErr(readOnlyErr error) { v.readOnlyErrLock.Lock() defer v.readOnlyErrLock.Unlock() @@ -101,7 +106,9 @@ func (v *BarrierView) Put(ctx context.Context, entry *logical.StorageEntry) erro roErr := v.getReadOnlyErr() if roErr != nil { - return roErr + if runICheck(v, expandedKey, roErr) { + return roErr + } } nested := &Entry{ @@ -122,7 +129,9 @@ func (v *BarrierView) Delete(ctx context.Context, key string) error { roErr := v.getReadOnlyErr() if roErr != nil { - return roErr + if runICheck(v, expandedKey, roErr) { + return roErr + } } return v.barrier.Delete(ctx, expandedKey) @@ -131,7 +140,7 @@ func (v *BarrierView) Delete(ctx context.Context, key string) error { // SubView constructs a nested sub-view using the given prefix func (v *BarrierView) SubView(prefix string) *BarrierView { sub := v.expandKey(prefix) - return &BarrierView{barrier: v.barrier, prefix: sub, readOnlyErr: v.getReadOnlyErr()} + return &BarrierView{barrier: v.barrier, prefix: sub, readOnlyErr: v.getReadOnlyErr(), iCheck: v.iCheck} } // expandKey is used to expand to the full key path with the prefix diff --git a/vault/barrier_view_test.go b/vault/barrier_view_test.go index 6df983a9a0..bd91387c23 100644 --- a/vault/barrier_view_test.go +++ b/vault/barrier_view_test.go @@ -283,6 +283,7 @@ func TestBarrierView_ClearView(t *testing.T) { t.Fatalf("have keys: %#v", out) } } + func TestBarrierView_Readonly(t *testing.T) { _, barrier, _ := mockBarrier(t) view := NewBarrierView(barrier, "foo/") diff --git a/vault/barrier_view_util.go b/vault/barrier_view_util.go new file mode 100644 index 0000000000..f7c6340590 --- /dev/null +++ b/vault/barrier_view_util.go @@ -0,0 +1,5 @@ +// +build !enterprise + +package vault + +func runICheck(v *BarrierView, expandedKey string, roErr error) bool { return true } diff --git a/vault/capabilities.go b/vault/capabilities.go index 8421239795..caae3516b8 100644 --- a/vault/capabilities.go +++ b/vault/capabilities.go @@ -4,10 +4,12 @@ import ( "context" "sort" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/logical" ) -// Capabilities is used to fetch the capabilities of the given token on the given path +// Capabilities is used to fetch the capabilities of the given token on the +// given path func (c *Core) Capabilities(ctx context.Context, token, path string) ([]string, error) { if path == "" { return nil, &logical.StatusBadRequest{Err: "missing path"} @@ -25,11 +27,22 @@ func (c *Core) Capabilities(ctx context.Context, token, path string) ([]string, return nil, &logical.StatusBadRequest{Err: "invalid token"} } - // Start with token entry policies - policies := te.Policies + tokenNS, err := NamespaceByID(ctx, te.NamespaceID, c) + if err != nil { + return nil, err + } + if tokenNS == nil { + return nil, namespace.ErrNoNamespace + } - // Fetch entity and entity group policies - entity, derivedPolicies, err := c.fetchEntityAndDerivedPolicies(te.EntityID) + var policyCount int + policyNames := make(map[string][]string) + policyNames[tokenNS.ID] = te.Policies + policyCount += len(te.Policies) + + // Attach token's namespace information to the context + ctx = namespace.ContextWithNamespace(ctx, tokenNS) + entity, identityPolicies, err := c.fetchEntityAndDerivedPolicies(ctx, tokenNS, te.EntityID) if err != nil { return nil, err } @@ -41,18 +54,22 @@ func (c *Core) Capabilities(ctx context.Context, token, path string) ([]string, c.logger.Warn("permission denied as the entity on the token is invalid") return nil, logical.ErrPermissionDenied } - policies = append(policies, derivedPolicies...) - if len(policies) == 0 { + for nsID, nsPolicies := range identityPolicies { + policyNames[nsID] = append(policyNames[nsID], nsPolicies...) + policyCount += len(nsPolicies) + } + + if policyCount == 0 { return []string{DenyCapability}, nil } - acl, err := c.policyStore.ACL(ctx, entity, policies...) + acl, err := c.policyStore.ACL(ctx, entity, policyNames) if err != nil { return nil, err } - capabilities := acl.Capabilities(path) + capabilities := acl.Capabilities(ctx, path) sort.Strings(capabilities) return capabilities, nil } diff --git a/vault/capabilities_test.go b/vault/capabilities_test.go index b03c545f17..85d8b9a629 100644 --- a/vault/capabilities_test.go +++ b/vault/capabilities_test.go @@ -1,13 +1,13 @@ package vault import ( - "context" "fmt" "reflect" "sort" "testing" "time" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/logical" ) @@ -15,7 +15,8 @@ func TestCapabilities_DerivedPolicies(t *testing.T) { var resp *logical.Response var err error - i, _, c := testIdentityStoreWithGithubAuth(t) + ctx := namespace.RootContext(nil) + i, _, c := testIdentityStoreWithGithubAuth(ctx, t) policy1 := ` name = "policy1" @@ -37,20 +38,20 @@ path "secret/sample" { } ` // Create the above policies - policy, _ := ParseACLPolicy(policy1) - err = c.policyStore.SetPolicy(context.Background(), policy) + policy, _ := ParseACLPolicy(namespace.RootNamespace, policy1) + err = c.policyStore.SetPolicy(ctx, policy) if err != nil { t.Fatalf("err: %v", err) } - policy, _ = ParseACLPolicy(policy2) - err = c.policyStore.SetPolicy(context.Background(), policy) + policy, _ = ParseACLPolicy(namespace.RootNamespace, policy2) + err = c.policyStore.SetPolicy(ctx, policy) if err != nil { t.Fatalf("err: %v", err) } - policy, _ = ParseACLPolicy(policy3) - err = c.policyStore.SetPolicy(context.Background(), policy) + policy, _ = ParseACLPolicy(namespace.RootNamespace, policy3) + err = c.policyStore.SetPolicy(ctx, policy) if err != nil { t.Fatalf("err: %v", err) } @@ -63,7 +64,7 @@ path "secret/sample" { "policies": "policy1", }, } - resp, err = i.HandleRequest(context.Background(), entityReq) + resp, err = i.HandleRequest(ctx, entityReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v\nerr: %#v\n", resp, err) } @@ -79,7 +80,7 @@ path "secret/sample" { } testMakeTokenDirectly(t, c.tokenStore, ent) - actual, err := c.Capabilities(context.Background(), "capabilitiestoken", "secret/sample") + actual, err := c.Capabilities(ctx, "capabilitiestoken", "secret/sample") if err != nil { t.Fatalf("err: %v", err) } @@ -99,12 +100,12 @@ path "secret/sample" { "policies": "policy3", }, } - resp, err = i.HandleRequest(context.Background(), groupReq) + resp, err = i.HandleRequest(ctx, groupReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v\nerr: %#v\n", resp, err) } - actual, err = c.Capabilities(context.Background(), "capabilitiestoken", "secret/sample") + actual, err = c.Capabilities(namespace.RootContext(nil), "capabilitiestoken", "secret/sample") if err != nil { t.Fatalf("err: %v", err) } @@ -119,15 +120,13 @@ path "secret/sample" { func TestCapabilities_TemplatedPolicies(t *testing.T) { var resp *logical.Response var err error - - i, _, c := testIdentityStoreWithGithubAuth(t) - + i, _, c := testIdentityStoreWithGithubAuth(namespace.RootContext(nil), t) // Create an entity and assign policy1 to it entityReq := &logical.Request{ Path: "entity", Operation: logical.UpdateOperation, } - resp, err = i.HandleRequest(context.Background(), entityReq) + resp, err = i.HandleRequest(namespace.RootContext(nil), entityReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v\nerr: %#v\n", resp, err) } @@ -168,19 +167,17 @@ func TestCapabilities_TemplatedPolicies(t *testing.T) { []string{"read"}, }, } - for _, tCase := range tCases { // Create the above policies - policy, err := ParseACLPolicy(tCase.policy) + policy, err := ParseACLPolicy(namespace.RootNamespace, tCase.policy) if err != nil { t.Fatalf("err: %v", err) } - err = c.policyStore.SetPolicy(context.Background(), policy) + err = c.policyStore.SetPolicy(namespace.RootContext(nil), policy) if err != nil { t.Fatalf("err: %v", err) } - - actual, err := c.Capabilities(context.Background(), "capabilitiestoken", tCase.path) + actual, err := c.Capabilities(namespace.RootContext(nil), "capabilitiestoken", tCase.path) if err != nil { t.Fatalf("err: %v", err) } @@ -195,7 +192,7 @@ func TestCapabilities_TemplatedPolicies(t *testing.T) { func TestCapabilities(t *testing.T) { c, _, token := TestCoreUnsealed(t) - actual, err := c.Capabilities(context.Background(), token, "path") + actual, err := c.Capabilities(namespace.RootContext(nil), token, "path") if err != nil { t.Fatalf("err: %s", err) } @@ -205,8 +202,8 @@ func TestCapabilities(t *testing.T) { } // Create a policy - policy, _ := ParseACLPolicy(aclPolicy) - err = c.policyStore.SetPolicy(context.Background(), policy) + policy, _ := ParseACLPolicy(namespace.RootNamespace, aclPolicy) + err = c.policyStore.SetPolicy(namespace.RootContext(nil), policy) if err != nil { t.Fatalf("err: %v", err) } @@ -220,7 +217,7 @@ func TestCapabilities(t *testing.T) { } testMakeTokenDirectly(t, c.tokenStore, ent) - actual, err = c.Capabilities(context.Background(), "capabilitiestoken", "foo/bar") + actual, err = c.Capabilities(namespace.RootContext(nil), "capabilitiestoken", "foo/bar") if err != nil { t.Fatalf("err: %s", err) } diff --git a/vault/cluster.go b/vault/cluster.go index cbe51cdbbb..356722a0e4 100644 --- a/vault/cluster.go +++ b/vault/cluster.go @@ -37,8 +37,9 @@ var ( ErrCannotForward = errors.New("cannot forward request; no connection or address not known") ) -// This is used for enterprise replication information type ReplicatedClusters struct { + DR *ReplicatedCluster + Performance *ReplicatedCluster } // This can be one of a few key types so the different params may or may not be filled @@ -206,7 +207,7 @@ func (c *Core) setupCluster(ctx context.Context) error { if c.ha != nil { // Create a private key if c.localClusterPrivateKey.Load().(*ecdsa.PrivateKey) == nil { - c.logger.Trace("generating cluster private key") + c.logger.Debug("generating cluster private key") key, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader) if err != nil { c.logger.Error("failed to generate local cluster key", "error", err) @@ -339,92 +340,15 @@ 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, repClusters *ReplicatedClusters) (*tls.Config, error) { +func (c *Core) ClusterTLSConfig(ctx context.Context, repClusters *ReplicatedClusters, perfStandbyCluster *ReplicatedCluster) (*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: - 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) - - return &tls.Certificate{ - Certificate: [][]byte{localCert}, - PrivateKey: c.localClusterPrivateKey.Load().(*ecdsa.PrivateKey), - Leaf: c.localClusterParsedCert.Load().(*x509.Certificate), - }, nil - } - } - - clientLookup := func(requestInfo *tls.CertificateRequestInfo) (*tls.Certificate, error) { - - if len(requestInfo.AcceptableCAs) != 1 { - return nil, fmt.Errorf("expected only a single acceptable CA") - } - - 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}, - PrivateKey: c.localClusterPrivateKey.Load().(*ecdsa.PrivateKey), - Leaf: c.localClusterParsedCert.Load().(*x509.Certificate), - }, nil - } - - serverConfigLookup := func(clientHello *tls.ClientHelloInfo) (*tls.Config, error) { - - for _, v := range clientHello.SupportedProtos { - switch v { - case "h2", requestForwardingALPN: - default: - return nil, fmt.Errorf("unknown ALPN proto %s", v) - } - } - - caPool := x509.NewCertPool() - - ret := &tls.Config{ - ClientAuth: tls.RequireAndVerifyClientCert, - GetCertificate: serverLookup, - GetClientCertificate: clientLookup, - MinVersion: tls.VersionTLS12, - RootCAs: caPool, - ClientCAs: caPool, - NextProtos: clientHello.SupportedProtos, - CipherSuites: c.clusterCipherSuites, - } - - switch { - default: - parsedCert := c.localClusterParsedCert.Load().(*x509.Certificate) - - if parsedCert == nil { - return nil, fmt.Errorf("forwarding connection client but no local cert") - } - - caPool.AddCert(parsedCert) - } - - return ret, nil - } - tlsConfig := &tls.Config{ ClientAuth: tls.RequireAndVerifyClientCert, - GetCertificate: serverLookup, - GetClientCertificate: clientLookup, - GetConfigForClient: serverConfigLookup, + GetCertificate: clusterTLSServerLookup(ctx, c, repClusters, perfStandbyCluster), + GetClientCertificate: clusterTLSClientLookup(ctx, c, repClusters, perfStandbyCluster), + GetConfigForClient: clusterTLSServerConfigLookup(ctx, c, repClusters, perfStandbyCluster), MinVersion: tls.VersionTLS12, CipherSuites: c.clusterCipherSuites, } diff --git a/vault/cluster_test.go b/vault/cluster_test.go index f1440dbf0b..0bf789d2ce 100644 --- a/vault/cluster_test.go +++ b/vault/cluster_test.go @@ -104,7 +104,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(), nil) + tlsConfig, err := cores[0].ClusterTLSConfig(context.Background(), nil, nil) if err != nil { if err.Error() != consts.ErrSealed.Error() { t.Fatal(err) @@ -346,6 +346,7 @@ func testCluster_ForwardRequests(t *testing.T, c *TestClusterCore, rootToken, re t.Fatal(err) } req.Header.Add("X-Vault-Token", rootToken) + req = req.WithContext(context.WithValue(req.Context(), "original_request_path", req.URL.Path)) statusCode, header, respBytes, err := c.ForwardRequest(req) if err != nil { @@ -391,7 +392,7 @@ func TestCluster_CustomCipherSuites(t *testing.T) { // Wait for core to become active TestWaitActive(t, core.Core) - tlsConf, err := core.Core.ClusterTLSConfig(context.Background(), nil) + tlsConf, err := core.Core.ClusterTLSConfig(context.Background(), nil, nil) if err != nil { t.Fatal(err) } diff --git a/vault/cluster_tls.go b/vault/cluster_tls.go new file mode 100644 index 0000000000..4a63ecfa35 --- /dev/null +++ b/vault/cluster_tls.go @@ -0,0 +1,85 @@ +package vault + +import ( + "context" + "crypto/ecdsa" + "crypto/tls" + "crypto/x509" + "fmt" +) + +var ( + clusterTLSServerLookup = func(ctx context.Context, c *Core, repClusters *ReplicatedClusters, _ *ReplicatedCluster) func(*tls.ClientHelloInfo) (*tls.Certificate, error) { + return func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) { + c.logger.Debug("performing server cert lookup") + + switch { + default: + 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) + + return &tls.Certificate{ + Certificate: [][]byte{localCert}, + PrivateKey: c.localClusterPrivateKey.Load().(*ecdsa.PrivateKey), + Leaf: c.localClusterParsedCert.Load().(*x509.Certificate), + }, nil + } + } + } + + clusterTLSClientLookup = func(ctx context.Context, c *Core, repClusters *ReplicatedClusters, _ *ReplicatedCluster) func(*tls.CertificateRequestInfo) (*tls.Certificate, error) { + return func(requestInfo *tls.CertificateRequestInfo) (*tls.Certificate, error) { + if len(requestInfo.AcceptableCAs) != 1 { + return nil, fmt.Errorf("expected only a single acceptable CA") + } + + 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}, + PrivateKey: c.localClusterPrivateKey.Load().(*ecdsa.PrivateKey), + Leaf: c.localClusterParsedCert.Load().(*x509.Certificate), + }, nil + } + } + + clusterTLSServerConfigLookup = func(ctx context.Context, c *Core, repClusters *ReplicatedClusters, repCluster *ReplicatedCluster) func(clientHello *tls.ClientHelloInfo) (*tls.Config, error) { + return func(clientHello *tls.ClientHelloInfo) (*tls.Config, error) { + //c.logger.Trace("performing server config lookup") + + caPool := x509.NewCertPool() + + ret := &tls.Config{ + ClientAuth: tls.RequireAndVerifyClientCert, + GetCertificate: clusterTLSServerLookup(ctx, c, repClusters, repCluster), + GetClientCertificate: clusterTLSClientLookup(ctx, c, repClusters, repCluster), + MinVersion: tls.VersionTLS12, + RootCAs: caPool, + ClientCAs: caPool, + NextProtos: clientHello.SupportedProtos, + CipherSuites: c.clusterCipherSuites, + } + + parsedCert := c.localClusterParsedCert.Load().(*x509.Certificate) + + if parsedCert == nil { + return nil, fmt.Errorf("forwarding connection client but no local cert") + } + + caPool.AddCert(parsedCert) + + return ret, nil + } + } +) diff --git a/vault/core.go b/vault/core.go index b6ea19c354..93d635e5a1 100644 --- a/vault/core.go +++ b/vault/core.go @@ -17,6 +17,7 @@ import ( "github.com/armon/go-metrics" log "github.com/hashicorp/go-hclog" + cache "github.com/patrickmn/go-cache" "google.golang.org/grpc" @@ -27,12 +28,12 @@ import ( "github.com/hashicorp/vault/helper/consts" "github.com/hashicorp/vault/helper/logging" "github.com/hashicorp/vault/helper/mlock" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/helper/reload" "github.com/hashicorp/vault/helper/tlsutil" "github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/physical" "github.com/hashicorp/vault/shamir" - cache "github.com/patrickmn/go-cache" ) const ( @@ -86,6 +87,7 @@ var ( startReplication = startReplicationImpl stopReplication = stopReplicationImpl LastRemoteWAL = lastRemoteWALImpl + WaitUntilWALShipped = waitUntilWALShippedImpl ) // NonFatalError is an error that can be returned during NewCore that should be @@ -113,6 +115,8 @@ func (e *ErrInvalidKey) Error() string { return fmt.Sprintf("invalid key: %v", e.Reason) } +type RegisterAuthFunc func(context.Context, time.Duration, string, *logical.Auth) error + type activeAdvertisement struct { RedirectAddr string `json:"redirect_addr"` ClusterAddr string `json:"cluster_addr,omitempty"` @@ -129,6 +133,8 @@ type unlockInformation struct { // interface for API handlers and is responsible for managing the logical and physical // backends, router, security barrier, and audit trails. type Core struct { + entCore + // N.B.: This is used to populate a dev token down replication, as // otherwise, after replication is started, a dev would have to go through // the generate-root process simply to talk to the new follower cluster. @@ -227,6 +233,9 @@ type Core struct { // systemBackend is the backend which is used to manage internal operations systemBackend *SystemBackend + // cubbyholeBackend is the backend which manages the per-token storage + cubbyholeBackend *CubbyholeBackend + // systemBarrierView is the barrier view for the system backend systemBarrierView *BarrierView @@ -338,6 +347,7 @@ type Core struct { atomicPrimaryClusterAddrs *atomic.Value atomicPrimaryFailoverAddrs *atomic.Value + // replicationState keeps the current replication state cached for quick // lookup; activeNodeReplicationState stores the active value on standbys replicationState *uint32 @@ -368,6 +378,16 @@ type Core struct { // Stores any funcs that should be run on successful postUnseal postUnsealFuncs []func() + // replicationFailure is used to mark when replication has entered an + // unrecoverable failure. + replicationFailure *uint32 + + // disablePerfStanby is used to tell a standby not to attempt to become a + // perf standby + disablePerfStandby bool + + licensingStopCh chan struct{} + // Stores loggers so we can reset the level allLoggers []log.Logger allLoggersLock sync.RWMutex @@ -422,9 +442,18 @@ type CoreConfig struct { PluginDirectory string `json:"plugin_directory" structs:"plugin_directory" mapstructure:"plugin_directory"` + DisableSealWrap bool `json:"disable_sealwrap" structs:"disable_sealwrap" mapstructure:"disable_sealwrap"` + ReloadFuncs *map[string][]reload.ReloadFunc ReloadFuncsLock *sync.RWMutex + // Licensing + LicensingConfig *LicensingConfig + // Don't set this unless in dev mode, ideally only when using inmem + DevLicenseDuration time.Duration + + DisablePerformanceStandby bool + AllLoggers []log.Logger } @@ -465,6 +494,7 @@ func NewCore(conf *CoreConfig) (*Core, error) { // Setup the core c := &Core{ + entCore: entCore{}, devToken: conf.DevToken, physical: conf.Physical, redirectAddr: conf.RedirectAddr, @@ -493,6 +523,8 @@ func NewCore(conf *CoreConfig) (*Core, error) { localClusterParsedCert: new(atomic.Value), activeNodeReplicationState: new(uint32), keepHALockOnStepDown: new(uint32), + replicationFailure: new(uint32), + disablePerfStandby: true, activeContextCancelFunc: new(atomic.Value), allLoggers: conf.AllLoggers, } @@ -521,28 +553,14 @@ func NewCore(conf *CoreConfig) (*Core, error) { Enabled: new(uint32), } - phys := conf.Physical - _, txnOK := conf.Physical.(physical.Transactional) if c.seal == nil { c.seal = NewDefaultSeal() } c.seal.SetCore(c) - unwrapperLogger := c.baseLogger.Named("storage.sealunwrapper") - c.allLoggers = append(c.allLoggers, unwrapperLogger) - c.sealUnwrapper = NewSealUnwrapper(phys, unwrapperLogger) - - var ok bool - - // Wrap the physical backend in a cache layer if enabled - cacheLogger := c.baseLogger.Named("storage.cache") - c.allLoggers = append(c.allLoggers, cacheLogger) - if txnOK { - c.physical = physical.NewTransactionalCache(c.sealUnwrapper, conf.CacheSize, cacheLogger) - } else { - c.physical = physical.NewCache(c.sealUnwrapper, conf.CacheSize, cacheLogger) + if err := coreInit(c, conf); err != nil { + return nil, err } - c.physicalCache = c.physical.(physical.ToggleablePurgemonster) if !conf.DisableMlock { // Ensure our memory usage is locked into physical RAM @@ -561,6 +579,8 @@ func NewCore(conf *CoreConfig) (*Core, error) { } var err error + var ok bool + if conf.PluginDirectory != "" { c.pluginDirectory, err = filepath.Abs(conf.PluginDirectory) if err != nil { @@ -574,6 +594,8 @@ func NewCore(conf *CoreConfig) (*Core, error) { return nil, errwrap.Wrapf("barrier setup failed: {{err}}", err) } + createSecondaries(c, conf) + if conf.HAPhysical != nil && conf.HAPhysical.HAEnabled() { c.ha = conf.HAPhysical } @@ -596,7 +618,7 @@ func NewCore(conf *CoreConfig) (*Core, error) { logicalBackends["kv"] = PassthroughBackendFactory } logicalBackends["cubbyhole"] = CubbyholeBackendFactory - logicalBackends["system"] = func(ctx context.Context, config *logical.BackendConfig) (logical.Backend, error) { + logicalBackends[systemMountType] = func(ctx context.Context, config *logical.BackendConfig) (logical.Backend, error) { sysBackendLogger := conf.Logger.Named("system") c.AddLogger(sysBackendLogger) b := NewSystemBackend(c, sysBackendLogger) @@ -605,13 +627,12 @@ func NewCore(conf *CoreConfig) (*Core, error) { } return b, nil } - logicalBackends["identity"] = func(ctx context.Context, config *logical.BackendConfig) (logical.Backend, error) { identityLogger := conf.Logger.Named("identity") c.AddLogger(identityLogger) return NewIdentityStore(ctx, c, config, identityLogger) } - + addExtraLogicalBackends(c, logicalBackends) c.logicalBackends = logicalBackends credentialBackends := make(map[string]logical.Factory) @@ -623,6 +644,7 @@ func NewCore(conf *CoreConfig) (*Core, error) { c.AddLogger(tsLogger) return NewTokenStore(ctx, tsLogger, c, config) } + addExtraCredentialBackends(c, credentialBackends) c.credentialBackends = credentialBackends auditBackends := make(map[string]audit.Factory) @@ -655,7 +677,7 @@ func (c *Core) GetContext() (context.Context, context.CancelFunc) { c.stateLock.RLock() defer c.stateLock.RUnlock() - return context.WithCancel(c.activeContext) + return context.WithCancel(namespace.RootContext(c.activeContext)) } // Sealed checks if the Vault is current sealed @@ -882,6 +904,10 @@ func (c *Core) unsealInternal(ctx context.Context, masterKey []byte) (bool, erro c.logger.Info("vault is unsealed") } + if err := preUnsealInternal(ctx, c); err != nil { + return false, err + } + // Do post-unseal setup if HA is not enabled if c.ha == nil { // We still need to set up cluster info even if it's not part of a @@ -893,8 +919,8 @@ func (c *Core) unsealInternal(ctx context.Context, masterKey []byte) (bool, erro return false, err } - ctx, ctxCancel := context.WithCancel(context.Background()) - if err := c.postUnseal(ctx, ctxCancel); err != nil { + ctx, ctxCancel := context.WithCancel(namespace.RootContext(nil)) + if err := c.postUnseal(ctx, ctxCancel, standardUnsealStrategy{}); err != nil { c.logger.Error("post-unseal setup failed", "error", err) c.barrier.Seal() c.logger.Warn("vault is sealed") @@ -944,7 +970,7 @@ func (c *Core) SealWithRequest(httpCtx context.Context, req *logical.Request) er // This will unlock the read lock // We use background context since we may not be active - ctx, cancel := context.WithCancel(context.Background()) + ctx, cancel := context.WithCancel(namespace.RootContext(nil)) defer cancel() go func() { @@ -978,7 +1004,7 @@ func (c *Core) Seal(token string) error { // This will unlock the read lock // We use background context since we may not be active - return c.sealInitCommon(context.Background(), req) + return c.sealInitCommon(namespace.RootContext(nil), req) } // sealInitCommon is common logic for Seal and SealWithRequest and is used to @@ -1005,7 +1031,7 @@ func (c *Core) sealInitCommon(ctx context.Context, req *logical.Request) (retErr return retErr } - acl, te, entity, identityPolicies, err := c.fetchACLTokenEntryAndEntity(req) + acl, te, entity, identityPolicies, err := c.fetchACLTokenEntryAndEntity(ctx, req) if err != nil { if errwrap.ContainsType(err, new(TemplateError)) { c.logger.Warn("permission denied due to a templated policy being invalid or containing directives not satisfied by the requestor", "error", err) @@ -1016,15 +1042,18 @@ func (c *Core) sealInitCommon(ctx context.Context, req *logical.Request) (retErr return retErr } + req.SetTokenEntry(te) + // Audit-log the request before going any further auth := &logical.Auth{ - ClientToken: req.ClientToken, - Policies: identityPolicies, - IdentityPolicies: identityPolicies, + ClientToken: req.ClientToken, } if te != nil { + auth.IdentityPolicies = identityPolicies[te.NamespaceID] + delete(identityPolicies, te.NamespaceID) + auth.ExternalNamespacePolicies = identityPolicies auth.TokenPolicies = te.Policies - auth.Policies = append(te.Policies, identityPolicies...) + auth.Policies = append(te.Policies, identityPolicies[te.NamespaceID]...) auth.Metadata = te.Meta auth.DisplayName = te.DisplayName auth.EntityID = te.EntityID @@ -1222,62 +1251,37 @@ func (c *Core) sealInternalWithOptions(grabStateLock, keepHALock bool) error { } } + postSealInternal(c) + c.logger.Info("vault is sealed") return nil } -// postUnseal is invoked after the barrier is unsealed, but before -// allowing any user operations. This allows us to setup any state that -// requires the Vault to be unsealed such as mount tables, logical backends, -// credential stores, etc. -func (c *Core) postUnseal(ctx context.Context, ctxCancelFunc context.CancelFunc) (retErr error) { - defer metrics.MeasureSince([]string{"core", "post_unseal"}, time.Now()) +type UnsealStrategy interface { + unseal(context.Context, log.Logger, *Core) error +} - // Clear any out - c.postUnsealFuncs = nil - - // Create a new request context - c.activeContext = ctx - c.activeContextCancelFunc.Store(ctxCancelFunc) - - defer func() { - if retErr != nil { - ctxCancelFunc() - c.preSeal() - } - }() - c.logger.Info("post-unseal setup starting") +type standardUnsealStrategy struct{} +func (s standardUnsealStrategy) unseal(ctx context.Context, logger log.Logger, c *Core) error { // Clear forwarding clients; we're active c.requestForwardingConnectionLock.Lock() c.clearForwardingClients() c.requestForwardingConnectionLock.Unlock() - // Enable the cache - c.physicalCache.Purge(ctx) - if !c.cachingDisabled { - c.physicalCache.SetEnabled(true) - } - - switch c.sealUnwrapper.(type) { - case *sealUnwrapper: - c.sealUnwrapper.(*sealUnwrapper).runUnwraps() - case *transactionalSealUnwrapper: - c.sealUnwrapper.(*transactionalSealUnwrapper).runUnwraps() - } - - // Purge these for safety in case of a rekey - c.seal.SetBarrierConfig(ctx, nil) - if c.seal.RecoveryKeySupported() { - c.seal.SetRecoveryConfig(ctx, nil) + if err := postUnsealPhysical(c); err != nil { + return err } if err := enterprisePostUnseal(c); err != nil { return err } - if err := c.ensureWrappingKey(ctx); err != nil { - return err + + if !c.IsDRSecondary() { + if err := c.ensureWrappingKey(ctx); err != nil { + return err + } } if err := c.setupPluginCatalog(); err != nil { return err @@ -1300,30 +1304,85 @@ func (c *Core) postUnseal(ctx context.Context, ctxCancelFunc context.CancelFunc) if err := c.setupCredentials(ctx); err != nil { return err } - if err := c.startRollback(); err != nil { - return err - } - if err := c.setupExpiration(); err != nil { - return err - } - if err := c.loadAudits(ctx); err != nil { - return err - } - if err := c.setupAudits(ctx); err != nil { - return err - } - if err := c.loadIdentityStoreArtifacts(ctx); err != nil { - return err - } - if err := c.setupAuditedHeadersConfig(ctx); err != nil { - return err + if !c.IsDRSecondary() { + if err := c.startRollback(); err != nil { + return err + } + if err := c.setupExpiration(expireLeaseStrategyRevoke); err != nil { + return err + } + if err := c.loadAudits(ctx); err != nil { + return err + } + if err := c.setupAudits(ctx); err != nil { + return err + } + if err := c.loadIdentityStoreArtifacts(ctx); err != nil { + return err + } + if err := loadMFAConfigs(ctx, c); err != nil { + return err + } + if err := c.setupAuditedHeadersConfig(ctx); err != nil { + return err + } + } else { + c.auditBroker = NewAuditBroker(c.logger) } - if c.ha != nil { + if c.ha != nil || shouldStartClusterListener(c) { if err := c.startClusterListener(ctx); err != nil { return err } } + + c.clusterParamsLock.Lock() + defer c.clusterParamsLock.Unlock() + if err := startReplication(c); err != nil { + return err + } + + return nil +} + +// postUnseal is invoked after the barrier is unsealed, but before +// allowing any user operations. This allows us to setup any state that +// requires the Vault to be unsealed such as mount tables, logical backends, +// credential stores, etc. +func (c *Core) postUnseal(ctx context.Context, ctxCancelFunc context.CancelFunc, unsealer UnsealStrategy) (retErr error) { + defer metrics.MeasureSince([]string{"core", "post_unseal"}, time.Now()) + + // Clear any out + c.postUnsealFuncs = nil + + // Create a new request context + c.activeContext = ctx + c.activeContextCancelFunc.Store(ctxCancelFunc) + + defer func() { + if retErr != nil { + ctxCancelFunc() + c.preSeal() + } + }() + c.logger.Info("post-unseal setup starting") + + // Enable the cache + c.physicalCache.Purge(ctx) + if !c.cachingDisabled { + c.physicalCache.SetEnabled(true) + } + + // Purge these for safety in case of a rekey + c.seal.SetBarrierConfig(ctx, nil) + if c.seal.RecoveryKeySupported() { + c.seal.SetRecoveryConfig(ctx, nil) + } + + if err := unsealer.unseal(ctx, c.logger, c); err != nil { + return err + } + c.metricsCh = make(chan struct{}) go c.emitMetrics(c.metricsCh) @@ -1357,6 +1416,12 @@ func (c *Core) preSeal() error { } var result error + c.clusterParamsLock.Lock() + if err := stopReplication(c); err != nil { + result = multierror.Append(result, errwrap.Wrapf("error stopping replication: {{err}}", err)) + } + c.clusterParamsLock.Unlock() + c.stopClusterListener() if err := c.teardownAudits(); err != nil { @@ -1381,16 +1446,7 @@ func (c *Core) preSeal() error { result = multierror.Append(result, err) } - switch c.sealUnwrapper.(type) { - case *sealUnwrapper: - c.sealUnwrapper.(*sealUnwrapper).stopUnwraps() - case *transactionalSealUnwrapper: - c.sealUnwrapper.(*transactionalSealUnwrapper).stopUnwraps() - } - - // Purge the cache - c.physicalCache.SetEnabled(false) - c.physicalCache.Purge(context.Background()) + preSealPhysical(c) c.logger.Info("pre-seal teardown complete") return result @@ -1454,6 +1510,10 @@ func (c *Core) AuditedHeadersConfig() *AuditedHeadersConfig { return c.auditedHeaders } +func waitUntilWALShippedImpl(ctx context.Context, c *Core, index uint64) bool { + return true +} + func lastRemoteWALImpl(c *Core) uint64 { return 0 } diff --git a/vault/core_test.go b/vault/core_test.go index 81ed890c69..aaed021f31 100644 --- a/vault/core_test.go +++ b/vault/core_test.go @@ -12,6 +12,7 @@ import ( "github.com/hashicorp/vault/audit" "github.com/hashicorp/vault/helper/consts" "github.com/hashicorp/vault/helper/logging" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/physical" "github.com/hashicorp/vault/physical/inmem" @@ -64,7 +65,7 @@ func TestCore_Unseal_MultiShare(t *testing.T) { SecretShares: 5, SecretThreshold: 3, } - res, err := c.Initialize(context.Background(), &InitParams{ + res, err := c.Initialize(namespace.RootContext(nil), &InitParams{ BarrierConfig: sealConf, RecoveryConfig: nil, }) @@ -140,7 +141,7 @@ func TestCore_Unseal_Single(t *testing.T) { SecretShares: 1, SecretThreshold: 1, } - res, err := c.Initialize(context.Background(), &InitParams{ + res, err := c.Initialize(namespace.RootContext(nil), &InitParams{ BarrierConfig: sealConf, RecoveryConfig: nil, }) @@ -180,17 +181,19 @@ func TestCore_Route_Sealed(t *testing.T) { SecretThreshold: 1, } + ctx := namespace.RootContext(nil) + // Should not route anything req := &logical.Request{ Operation: logical.ReadOperation, Path: "sys/mounts", } - _, err := c.HandleRequest(context.Background(), req) + _, err := c.HandleRequest(ctx, req) if err != consts.ErrSealed { t.Fatalf("err: %v", err) } - res, err := c.Initialize(context.Background(), &InitParams{ + res, err := c.Initialize(ctx, &InitParams{ BarrierConfig: sealConf, RecoveryConfig: nil, }) @@ -208,7 +211,7 @@ func TestCore_Route_Sealed(t *testing.T) { // Should not error after unseal req.ClientToken = res.RootToken - _, err = c.HandleRequest(context.Background(), req) + _, err = c.HandleRequest(ctx, req) if err != nil { t.Fatalf("err: %v", err) } @@ -256,10 +259,11 @@ func TestCore_Seal_BadToken(t *testing.T) { // GH-3497 func TestCore_Seal_SingleUse(t *testing.T) { c, keys, _ := TestCoreUnsealed(t) - c.tokenStore.create(context.Background(), &logical.TokenEntry{ - ID: "foo", - NumUses: 1, - Policies: []string{"root"}, + c.tokenStore.create(namespace.RootContext(nil), &logical.TokenEntry{ + ID: "foo", + NumUses: 1, + Policies: []string{"root"}, + NamespaceID: namespace.RootNamespaceID, }) if err := c.Seal("foo"); err != nil { t.Fatalf("err: %v", err) @@ -279,7 +283,7 @@ func TestCore_Seal_SingleUse(t *testing.T) { if err := c.Seal("foo"); err == nil { t.Fatal("expected error from revoked token") } - te, err := c.tokenStore.Lookup(context.Background(), "foo") + te, err := c.tokenStore.Lookup(namespace.RootContext(nil), "foo") if err != nil { t.Fatal(err) } @@ -301,7 +305,8 @@ func TestCore_HandleRequest_Lease(t *testing.T) { }, ClientToken: root, } - resp, err := c.HandleRequest(context.Background(), req) + ctx := namespace.RootContext(nil) + resp, err := c.HandleRequest(ctx, req) if err != nil { t.Fatalf("err: %v", err) } @@ -312,7 +317,7 @@ func TestCore_HandleRequest_Lease(t *testing.T) { // Read the key req.Operation = logical.ReadOperation req.Data = nil - resp, err = c.HandleRequest(context.Background(), req) + resp, err = c.HandleRequest(ctx, req) if err != nil { t.Fatalf("err: %v", err) } @@ -342,7 +347,8 @@ func TestCore_HandleRequest_Lease_MaxLength(t *testing.T) { }, ClientToken: root, } - resp, err := c.HandleRequest(context.Background(), req) + ctx := namespace.RootContext(nil) + resp, err := c.HandleRequest(ctx, req) if err != nil { t.Fatalf("err: %v", err) } @@ -353,7 +359,7 @@ func TestCore_HandleRequest_Lease_MaxLength(t *testing.T) { // Read the key req.Operation = logical.ReadOperation req.Data = nil - resp, err = c.HandleRequest(context.Background(), req) + resp, err = c.HandleRequest(ctx, req) if err != nil { t.Fatalf("err: %v", err) } @@ -383,7 +389,8 @@ func TestCore_HandleRequest_Lease_DefaultLength(t *testing.T) { }, ClientToken: root, } - resp, err := c.HandleRequest(context.Background(), req) + ctx := namespace.RootContext(nil) + resp, err := c.HandleRequest(ctx, req) if err != nil { t.Fatalf("err: %v", err) } @@ -394,7 +401,7 @@ func TestCore_HandleRequest_Lease_DefaultLength(t *testing.T) { // Read the key req.Operation = logical.ReadOperation req.Data = nil - resp, err = c.HandleRequest(context.Background(), req) + resp, err = c.HandleRequest(ctx, req) if err != nil { t.Fatalf("err: %v", err) } @@ -423,7 +430,7 @@ func TestCore_HandleRequest_MissingToken(t *testing.T) { "lease": "1h", }, } - resp, err := c.HandleRequest(context.Background(), req) + resp, err := c.HandleRequest(namespace.RootContext(nil), req) if err == nil || !errwrap.Contains(err, logical.ErrInvalidRequest.Error()) { t.Fatalf("err: %v", err) } @@ -444,7 +451,7 @@ func TestCore_HandleRequest_InvalidToken(t *testing.T) { }, ClientToken: "foobarbaz", } - resp, err := c.HandleRequest(context.Background(), req) + resp, err := c.HandleRequest(namespace.RootContext(nil), req) if err == nil || !errwrap.Contains(err, logical.ErrPermissionDenied.Error()) { t.Fatalf("err: %v", err) } @@ -462,7 +469,7 @@ func TestCore_HandleRequest_NoSlash(t *testing.T) { Path: "secret", ClientToken: root, } - resp, err := c.HandleRequest(context.Background(), req) + resp, err := c.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v, resp: %v", err, resp) } @@ -481,7 +488,7 @@ func TestCore_HandleRequest_RootPath(t *testing.T) { Path: "sys/policy", // root protected! ClientToken: "child", } - resp, err := c.HandleRequest(context.Background(), req) + resp, err := c.HandleRequest(namespace.RootContext(nil), req) if err == nil || !errwrap.Contains(err, logical.ErrPermissionDenied.Error()) { t.Fatalf("err: %v, resp: %v", err, resp) } @@ -500,7 +507,7 @@ func TestCore_HandleRequest_RootPath_WithSudo(t *testing.T) { }, ClientToken: root, } - resp, err := c.HandleRequest(context.Background(), req) + resp, err := c.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -515,7 +522,7 @@ func TestCore_HandleRequest_RootPath_WithSudo(t *testing.T) { Path: "sys/policy", // root protected! ClientToken: "child", } - resp, err = c.HandleRequest(context.Background(), req) + resp, err = c.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -538,7 +545,7 @@ func TestCore_HandleRequest_PermissionDenied(t *testing.T) { }, ClientToken: "child", } - resp, err := c.HandleRequest(context.Background(), req) + resp, err := c.HandleRequest(namespace.RootContext(nil), req) if err == nil || !errwrap.Contains(err, logical.ErrPermissionDenied.Error()) { t.Fatalf("err: %v, resp: %v", err, resp) } @@ -558,7 +565,7 @@ func TestCore_HandleRequest_PermissionAllowed(t *testing.T) { }, ClientToken: root, } - resp, err := c.HandleRequest(context.Background(), req) + resp, err := c.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -576,7 +583,7 @@ func TestCore_HandleRequest_PermissionAllowed(t *testing.T) { }, ClientToken: "child", } - resp, err = c.HandleRequest(context.Background(), req) + resp, err = c.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -599,7 +606,7 @@ func TestCore_HandleRequest_NoClientToken(t *testing.T) { req.Data["type"] = "noop" req.Data["description"] = "foo" req.ClientToken = root - _, err := c.HandleRequest(context.Background(), req) + _, err := c.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -609,7 +616,7 @@ func TestCore_HandleRequest_NoClientToken(t *testing.T) { Path: "foo/login", } req.ClientToken = root - if _, err := c.HandleRequest(context.Background(), req); err != nil { + if _, err := c.HandleRequest(namespace.RootContext(nil), req); err != nil { t.Fatalf("err: %v", err) } @@ -633,7 +640,7 @@ func TestCore_HandleRequest_ConnOnLogin(t *testing.T) { req := logical.TestRequest(t, logical.UpdateOperation, "sys/auth/foo") req.Data["type"] = "noop" req.ClientToken = root - _, err := c.HandleRequest(context.Background(), req) + _, err := c.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -643,7 +650,7 @@ func TestCore_HandleRequest_ConnOnLogin(t *testing.T) { Path: "auth/foo/login", Connection: &logical.Connection{}, } - if _, err := c.HandleRequest(context.Background(), req); err != nil { + if _, err := c.HandleRequest(namespace.RootContext(nil), req); err != nil { t.Fatalf("err: %v", err) } if noop.Requests[0].Connection == nil { @@ -674,7 +681,7 @@ func TestCore_HandleLogin_Token(t *testing.T) { req := logical.TestRequest(t, logical.UpdateOperation, "sys/auth/foo") req.Data["type"] = "noop" req.ClientToken = root - _, err := c.HandleRequest(context.Background(), req) + _, err := c.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -683,7 +690,7 @@ func TestCore_HandleLogin_Token(t *testing.T) { lreq := &logical.Request{ Path: "auth/foo/login", } - lresp, err := c.HandleRequest(context.Background(), lreq) + lresp, err := c.HandleRequest(namespace.RootContext(nil), lreq) if err != nil { t.Fatalf("err: %v", err) } @@ -695,7 +702,7 @@ func TestCore_HandleLogin_Token(t *testing.T) { } // Check the policy and metadata - te, err := c.tokenStore.Lookup(context.Background(), clientToken) + te, err := c.tokenStore.Lookup(namespace.RootContext(nil), clientToken) if err != nil { t.Fatalf("err: %v", err) } @@ -711,6 +718,7 @@ func TestCore_HandleLogin_Token(t *testing.T) { DisplayName: "foo-armon", TTL: time.Hour * 24, CreationTime: te.CreationTime, + NamespaceID: namespace.RootNamespaceID, } if !reflect.DeepEqual(te, expect) { @@ -738,7 +746,7 @@ func TestCore_HandleRequest_AuditTrail(t *testing.T) { req := logical.TestRequest(t, logical.UpdateOperation, "sys/audit/noop") req.Data["type"] = "noop" req.ClientToken = root - resp, err := c.HandleRequest(context.Background(), req) + resp, err := c.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -754,7 +762,7 @@ func TestCore_HandleRequest_AuditTrail(t *testing.T) { ClientToken: root, } req.ClientToken = root - if _, err := c.HandleRequest(context.Background(), req); err != nil { + if _, err := c.HandleRequest(namespace.RootContext(nil), req); err != nil { t.Fatalf("err: %v", err) } @@ -802,7 +810,7 @@ func TestCore_HandleRequest_AuditTrail_noHMACKeys(t *testing.T) { req := logical.TestRequest(t, logical.UpdateOperation, "sys/mounts/secret/tune") req.Data["audit_non_hmac_request_keys"] = "foo" req.ClientToken = root - resp, err := c.HandleRequest(context.Background(), req) + resp, err := c.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -810,7 +818,7 @@ func TestCore_HandleRequest_AuditTrail_noHMACKeys(t *testing.T) { req = logical.TestRequest(t, logical.UpdateOperation, "sys/mounts/secret/tune") req.Data["audit_non_hmac_response_keys"] = "baz" req.ClientToken = root - resp, err = c.HandleRequest(context.Background(), req) + resp, err = c.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -819,7 +827,7 @@ func TestCore_HandleRequest_AuditTrail_noHMACKeys(t *testing.T) { req = logical.TestRequest(t, logical.UpdateOperation, "sys/audit/noop") req.Data["type"] = "noop" req.ClientToken = root - resp, err = c.HandleRequest(context.Background(), req) + resp, err = c.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -834,7 +842,7 @@ func TestCore_HandleRequest_AuditTrail_noHMACKeys(t *testing.T) { ClientToken: root, } req.ClientToken = root - if _, err := c.HandleRequest(context.Background(), req); err != nil { + if _, err := c.HandleRequest(namespace.RootContext(nil), req); err != nil { t.Fatalf("err: %v", err) } @@ -876,7 +884,7 @@ func TestCore_HandleRequest_AuditTrail_noHMACKeys(t *testing.T) { ClientToken: root, } req.ClientToken = root - if _, err := c.HandleRequest(context.Background(), req); err != nil { + if _, err := c.HandleRequest(namespace.RootContext(nil), req); err != nil { t.Fatalf("err: %v", err) } if len(noop.RespNonHMACKeys) != 1 || noop.RespNonHMACKeys[0] != "baz" { @@ -920,7 +928,7 @@ func TestCore_HandleLogin_AuditTrail(t *testing.T) { req := logical.TestRequest(t, logical.UpdateOperation, "sys/auth/foo") req.Data["type"] = "noop" req.ClientToken = root - _, err := c.HandleRequest(context.Background(), req) + _, err := c.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -929,7 +937,7 @@ func TestCore_HandleLogin_AuditTrail(t *testing.T) { req = logical.TestRequest(t, logical.UpdateOperation, "sys/audit/noop") req.Data["type"] = "noop" req.ClientToken = root - _, err = c.HandleRequest(context.Background(), req) + _, err = c.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -938,7 +946,7 @@ func TestCore_HandleLogin_AuditTrail(t *testing.T) { lreq := &logical.Request{ Path: "auth/foo/login", } - lresp, err := c.HandleRequest(context.Background(), lreq) + lresp, err := c.HandleRequest(namespace.RootContext(nil), lreq) if err != nil { t.Fatalf("err: %v", err) } @@ -983,7 +991,7 @@ func TestCore_HandleRequest_CreateToken_Lease(t *testing.T) { req := logical.TestRequest(t, logical.UpdateOperation, "auth/token/create") req.ClientToken = root req.Data["policies"] = []string{"foo"} - resp, err := c.HandleRequest(context.Background(), req) + resp, err := c.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -998,7 +1006,7 @@ func TestCore_HandleRequest_CreateToken_Lease(t *testing.T) { } // Check the policy and metadata - te, err := c.tokenStore.Lookup(context.Background(), clientToken) + te, err := c.tokenStore.Lookup(namespace.RootContext(nil), clientToken) if err != nil { t.Fatalf("err: %v", err) } @@ -1011,6 +1019,7 @@ func TestCore_HandleRequest_CreateToken_Lease(t *testing.T) { DisplayName: "token", CreationTime: te.CreationTime, TTL: time.Hour * 24 * 32, + NamespaceID: namespace.RootNamespaceID, } if !reflect.DeepEqual(te, expect) { t.Fatalf("Bad: %#v expect: %#v", te, expect) @@ -1031,7 +1040,7 @@ func TestCore_HandleRequest_CreateToken_NoDefaultPolicy(t *testing.T) { req.ClientToken = root req.Data["policies"] = []string{"foo"} req.Data["no_default_policy"] = true - resp, err := c.HandleRequest(context.Background(), req) + resp, err := c.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1043,7 +1052,7 @@ func TestCore_HandleRequest_CreateToken_NoDefaultPolicy(t *testing.T) { } // Check the policy and metadata - te, err := c.tokenStore.Lookup(context.Background(), clientToken) + te, err := c.tokenStore.Lookup(namespace.RootContext(nil), clientToken) if err != nil { t.Fatalf("err: %v", err) } @@ -1056,6 +1065,7 @@ func TestCore_HandleRequest_CreateToken_NoDefaultPolicy(t *testing.T) { DisplayName: "token", CreationTime: te.CreationTime, TTL: time.Hour * 24 * 32, + NamespaceID: namespace.RootNamespaceID, } if !reflect.DeepEqual(te, expect) { t.Fatalf("Bad: %#v expect: %#v", te, expect) @@ -1069,7 +1079,7 @@ func TestCore_LimitedUseToken(t *testing.T) { req := logical.TestRequest(t, logical.UpdateOperation, "auth/token/create") req.ClientToken = root req.Data["num_uses"] = "1" - resp, err := c.HandleRequest(context.Background(), req) + resp, err := c.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1083,13 +1093,13 @@ func TestCore_LimitedUseToken(t *testing.T) { }, ClientToken: resp.Auth.ClientToken, } - _, err = c.HandleRequest(context.Background(), req) + _, err = c.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } // Second operation should fail - _, err = c.HandleRequest(context.Background(), req) + _, err = c.HandleRequest(namespace.RootContext(nil), req) if err == nil || !errwrap.Contains(err, logical.ErrPermissionDenied.Error()) { t.Fatalf("err: %v", err) } @@ -1310,7 +1320,7 @@ func TestCore_StepDown(t *testing.T) { } // Step down core - err = core.StepDown(context.Background(), req) + err = core.StepDown(namespace.RootContext(nil), req) if err != nil { t.Fatal("error stepping down core 1") } @@ -1352,7 +1362,7 @@ func TestCore_StepDown(t *testing.T) { } // Step down core2 - err = core2.StepDown(context.Background(), req) + err = core2.StepDown(namespace.RootContext(nil), req) if err != nil { t.Fatal("error stepping down core 1") } @@ -1446,13 +1456,13 @@ func TestCore_CleanLeaderPrefix(t *testing.T) { if err != nil { t.Fatal(err) } - core.barrier.Put(context.Background(), &Entry{ + core.barrier.Put(namespace.RootContext(nil), &Entry{ Key: coreLeaderPrefix + keyUUID, Value: []byte(valueUUID), }) } - entries, err := core.barrier.List(context.Background(), coreLeaderPrefix) + entries, err := core.barrier.List(namespace.RootContext(nil), coreLeaderPrefix) if err != nil { t.Fatalf("err: %v", err) } @@ -1548,7 +1558,7 @@ func TestCore_CleanLeaderPrefix(t *testing.T) { // Give time for the entries to clear out; it is conservative at 1/second time.Sleep(10 * leaderPrefixCleanDelay) - entries, err = core2.barrier.List(context.Background(), coreLeaderPrefix) + entries, err = core2.barrier.List(namespace.RootContext(nil), coreLeaderPrefix) if err != nil { t.Fatalf("err: %v", err) } @@ -1619,7 +1629,7 @@ func testCore_Standby_Common(t *testing.T, inm physical.Backend, inmha physical. }, ClientToken: root, } - _, err = core.HandleRequest(context.Background(), req) + _, err = core.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1668,7 +1678,7 @@ func testCore_Standby_Common(t *testing.T, inm physical.Backend, inmha physical. } // Request should fail in standby mode - _, err = core2.HandleRequest(context.Background(), req) + _, err = core2.HandleRequest(namespace.RootContext(nil), req) if err != consts.ErrStandby { t.Fatalf("err: %v", err) } @@ -1709,7 +1719,7 @@ func testCore_Standby_Common(t *testing.T, inm physical.Backend, inmha physical. Path: "secret/foo", ClientToken: root, } - resp, err := core2.HandleRequest(context.Background(), req) + resp, err := core2.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1772,7 +1782,7 @@ func TestCore_HandleRequest_Login_InternalData(t *testing.T) { req := logical.TestRequest(t, logical.UpdateOperation, "sys/auth/foo") req.Data["type"] = "noop" req.ClientToken = root - _, err := c.HandleRequest(context.Background(), req) + _, err := c.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1781,7 +1791,7 @@ func TestCore_HandleRequest_Login_InternalData(t *testing.T) { lreq := &logical.Request{ Path: "auth/foo/login", } - lresp, err := c.HandleRequest(context.Background(), lreq) + lresp, err := c.HandleRequest(namespace.RootContext(nil), lreq) if err != nil { t.Fatalf("err: %v", err) } @@ -1816,7 +1826,7 @@ func TestCore_HandleRequest_InternalData(t *testing.T) { req := logical.TestRequest(t, logical.UpdateOperation, "sys/mounts/foo") req.Data["type"] = "noop" req.ClientToken = root - _, err := c.HandleRequest(context.Background(), req) + _, err := c.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1827,7 +1837,7 @@ func TestCore_HandleRequest_InternalData(t *testing.T) { Path: "foo/test", ClientToken: root, } - lresp, err := c.HandleRequest(context.Background(), lreq) + lresp, err := c.HandleRequest(namespace.RootContext(nil), lreq) if err != nil { t.Fatalf("err: %v", err) } @@ -1859,7 +1869,7 @@ func TestCore_HandleLogin_ReturnSecret(t *testing.T) { req := logical.TestRequest(t, logical.UpdateOperation, "sys/auth/foo") req.Data["type"] = "noop" req.ClientToken = root - _, err := c.HandleRequest(context.Background(), req) + _, err := c.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1868,7 +1878,7 @@ func TestCore_HandleLogin_ReturnSecret(t *testing.T) { lreq := &logical.Request{ Path: "auth/foo/login", } - _, err = c.HandleRequest(context.Background(), lreq) + _, err = c.HandleRequest(namespace.RootContext(nil), lreq) if err != ErrInternalError { t.Fatalf("err: %v", err) } @@ -1888,7 +1898,7 @@ func TestCore_RenewSameLease(t *testing.T) { }, ClientToken: root, } - resp, err := c.HandleRequest(context.Background(), req) + resp, err := c.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1899,7 +1909,7 @@ func TestCore_RenewSameLease(t *testing.T) { // Read the key req.Operation = logical.ReadOperation req.Data = nil - resp, err = c.HandleRequest(context.Background(), req) + resp, err = c.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1911,7 +1921,7 @@ func TestCore_RenewSameLease(t *testing.T) { // Renew the lease req = logical.TestRequest(t, logical.UpdateOperation, "sys/renew/"+resp.Secret.LeaseID) req.ClientToken = root - resp, err = c.HandleRequest(context.Background(), req) + resp, err = c.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1924,7 +1934,7 @@ func TestCore_RenewSameLease(t *testing.T) { // Renew the lease (alternate path) req = logical.TestRequest(t, logical.UpdateOperation, "sys/leases/renew/"+resp.Secret.LeaseID) req.ClientToken = root - resp, err = c.HandleRequest(context.Background(), req) + resp, err = c.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1948,7 +1958,7 @@ func TestCore_RenewToken_SingleRegister(t *testing.T) { }, ClientToken: root, } - resp, err := c.HandleRequest(context.Background(), req) + resp, err := c.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1960,7 +1970,7 @@ func TestCore_RenewToken_SingleRegister(t *testing.T) { req.Data = map[string]interface{}{ "token": newClient, } - resp, err = c.HandleRequest(context.Background(), req) + resp, err = c.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1968,7 +1978,7 @@ func TestCore_RenewToken_SingleRegister(t *testing.T) { // Revoke using the renew prefix req = logical.TestRequest(t, logical.UpdateOperation, "sys/revoke-prefix/auth/token/renew/") req.ClientToken = root - resp, err = c.HandleRequest(context.Background(), req) + resp, err = c.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1979,7 +1989,7 @@ func TestCore_RenewToken_SingleRegister(t *testing.T) { "token": newClient, } req.ClientToken = newClient - resp, err = c.HandleRequest(context.Background(), req) + resp, err = c.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -2014,8 +2024,8 @@ path "secret/*" { ` ps := c.policyStore - policy, _ := ParseACLPolicy(secretWritingPolicy) - if err := ps.SetPolicy(context.Background(), policy); err != nil { + policy, _ := ParseACLPolicy(namespace.RootNamespace, secretWritingPolicy) + if err := ps.SetPolicy(namespace.RootContext(nil), policy); err != nil { t.Fatal(err) } @@ -2023,7 +2033,7 @@ path "secret/*" { req := logical.TestRequest(t, logical.UpdateOperation, "sys/auth/foo") req.Data["type"] = "noop" req.ClientToken = root - _, err := c.HandleRequest(context.Background(), req) + _, err := c.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -2032,7 +2042,7 @@ path "secret/*" { lreq := &logical.Request{ Path: "auth/foo/login", } - lresp, err := c.HandleRequest(context.Background(), lreq) + lresp, err := c.HandleRequest(namespace.RootContext(nil), lreq) if err == nil || lresp == nil || !lresp.IsError() { t.Fatalf("expected error trying to auth and receive root policy") } @@ -2042,7 +2052,7 @@ path "secret/*" { lreq = &logical.Request{ Path: "auth/foo/login", } - lresp, err = c.HandleRequest(context.Background(), lreq) + lresp, err = c.HandleRequest(namespace.RootContext(nil), lreq) if err != nil { t.Fatalf("err: %v", err) } @@ -2057,7 +2067,7 @@ path "secret/*" { }, ClientToken: lresp.Auth.ClientToken, } - resp, err := c.HandleRequest(context.Background(), req) + resp, err := c.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -2068,7 +2078,7 @@ path "secret/*" { // Read the key req.Operation = logical.ReadOperation req.Data = nil - resp, err = c.HandleRequest(context.Background(), req) + resp, err = c.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -2082,7 +2092,7 @@ path "secret/*" { "lease_id": resp.Secret.LeaseID, } req.ClientToken = lresp.Auth.ClientToken - _, err = c.HandleRequest(context.Background(), req) + _, err = c.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -2090,7 +2100,7 @@ path "secret/*" { // Disable the credential backend req = logical.TestRequest(t, logical.DeleteOperation, "sys/auth/foo") req.ClientToken = root - resp, err = c.HandleRequest(context.Background(), req) + resp, err = c.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v %#v", err, resp) } @@ -2110,7 +2120,7 @@ func TestCore_HandleRequest_MountPointType(t *testing.T) { req.Data["type"] = "noop" req.Data["description"] = "foo" req.ClientToken = root - _, err := c.HandleRequest(context.Background(), req) + _, err := c.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -2122,7 +2132,7 @@ func TestCore_HandleRequest_MountPointType(t *testing.T) { Connection: &logical.Connection{}, } req.ClientToken = root - if _, err := c.HandleRequest(context.Background(), req); err != nil { + if _, err := c.HandleRequest(namespace.RootContext(nil), req); err != nil { t.Fatalf("err: %v", err) } @@ -2194,7 +2204,7 @@ func TestCore_Standby_Rotate(t *testing.T) { Path: "sys/rotate", ClientToken: root, } - _, err = core.HandleRequest(context.Background(), req) + _, err = core.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -2214,7 +2224,7 @@ func TestCore_Standby_Rotate(t *testing.T) { Path: "sys/key-status", ClientToken: root, } - resp, err := core2.HandleRequest(context.Background(), req) + resp, err := core2.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -2242,7 +2252,7 @@ func TestCore_HandleRequest_Headers(t *testing.T) { req := logical.TestRequest(t, logical.UpdateOperation, "sys/mounts/foo") req.Data["type"] = "noop" req.ClientToken = root - _, err := c.HandleRequest(context.Background(), req) + _, err := c.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -2251,7 +2261,7 @@ func TestCore_HandleRequest_Headers(t *testing.T) { req = logical.TestRequest(t, logical.UpdateOperation, "sys/mounts/foo/tune") req.Data["passthrough_request_headers"] = []string{"Should-Passthrough", "should-passthrough-case-insensitive"} req.ClientToken = root - _, err = c.HandleRequest(context.Background(), req) + _, err = c.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -2267,7 +2277,7 @@ func TestCore_HandleRequest_Headers(t *testing.T) { "Should-Not-Passthrough": []string{"bar"}, }, } - _, err = c.HandleRequest(context.Background(), lreq) + _, err = c.HandleRequest(namespace.RootContext(nil), lreq) if err != nil { t.Fatalf("err: %v", err) } diff --git a/vault/core_util.go b/vault/core_util.go new file mode 100644 index 0000000000..aabdc88636 --- /dev/null +++ b/vault/core_util.go @@ -0,0 +1,102 @@ +// +build !enterprise + +package vault + +import ( + "context" + + "github.com/hashicorp/vault/helper/license" + "github.com/hashicorp/vault/helper/namespace" + "github.com/hashicorp/vault/logical" + "github.com/hashicorp/vault/physical" +) + +type entCore struct{} + +type LicensingConfig struct{} + +func coreInit(c *Core, conf *CoreConfig) error { + phys := conf.Physical + _, txnOK := phys.(physical.Transactional) + sealUnwrapperLogger := conf.Logger.Named("storage.sealunwrapper") + c.allLoggers = append(c.allLoggers, sealUnwrapperLogger) + c.sealUnwrapper = NewSealUnwrapper(phys, sealUnwrapperLogger) + // Wrap the physical backend in a cache layer if enabled + cacheLogger := c.baseLogger.Named("storage.cache") + c.allLoggers = append(c.allLoggers, cacheLogger) + if txnOK { + c.physical = physical.NewTransactionalCache(c.sealUnwrapper, conf.CacheSize, cacheLogger) + } else { + c.physical = physical.NewCache(c.sealUnwrapper, conf.CacheSize, cacheLogger) + } + c.physicalCache = c.physical.(physical.ToggleablePurgemonster) + + return nil +} + +func createSecondaries(*Core, *CoreConfig) {} + +func addExtraLogicalBackends(*Core, map[string]logical.Factory) {} + +func addExtraCredentialBackends(*Core, map[string]logical.Factory) {} + +func preUnsealInternal(context.Context, *Core) error { return nil } + +func postSealInternal(*Core) {} + +func preSealPhysical(c *Core) { + switch c.sealUnwrapper.(type) { + case *sealUnwrapper: + c.sealUnwrapper.(*sealUnwrapper).stopUnwraps() + case *transactionalSealUnwrapper: + c.sealUnwrapper.(*transactionalSealUnwrapper).stopUnwraps() + } + + // Purge the cache + c.physicalCache.SetEnabled(false) + c.physicalCache.Purge(context.Background()) +} + +func postUnsealPhysical(c *Core) error { + switch c.sealUnwrapper.(type) { + case *sealUnwrapper: + c.sealUnwrapper.(*sealUnwrapper).runUnwraps() + case *transactionalSealUnwrapper: + c.sealUnwrapper.(*transactionalSealUnwrapper).runUnwraps() + } + return nil +} + +func loadMFAConfigs(context.Context, *Core) error { return nil } + +func shouldStartClusterListener(*Core) bool { return true } + +func hasNamespaces(*Core) bool { return false } + +func (c *Core) Features() license.Features { + return license.FeatureNone +} + +func (c *Core) HasFeature(license.Features) bool { + return false +} + +func (c *Core) namepaceByPath(string) *namespace.Namespace { + return namespace.RootNamespace +} + +func (c *Core) setupReplicatedClusterPrimary(*ReplicatedCluster) error { return nil } + +func (c *Core) perfStandbyCount() int { return 0 } + +func (c *Core) removePrefixFromFilteredPaths(context.Context, string) error { + return nil +} + +func (c *Core) checkReplicatedFiltering(context.Context, *MountEntry, string) (bool, error) { + return false, nil +} + +func (c *Core) invalidateSentinelPolicy(PolicyType, string) {} + +func (c *Core) removePerfStandbySecondary(context.Context, string) {} diff --git a/vault/dynamic_system_view.go b/vault/dynamic_system_view.go index 52cb6a588f..a83e60c7bd 100644 --- a/vault/dynamic_system_view.go +++ b/vault/dynamic_system_view.go @@ -6,7 +6,10 @@ import ( "time" "github.com/hashicorp/errwrap" + "github.com/hashicorp/vault/helper/consts" + "github.com/hashicorp/vault/helper/license" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/helper/pluginutil" "github.com/hashicorp/vault/helper/wrapping" "github.com/hashicorp/vault/logical" @@ -42,14 +45,35 @@ func (d dynamicSystemView) SudoPrivilege(ctx context.Context, path string, token return false } - // Construct the corresponding ACL object - entity, entityPolicies, err := d.core.fetchEntityAndDerivedPolicies(te.EntityID) + policies := make(map[string][]string) + // Add token policies + policies[te.NamespaceID] = append(policies[te.NamespaceID], te.Policies...) + + tokenNS, err := NamespaceByID(ctx, te.NamespaceID, d.core) if err != nil { - d.core.logger.Error("failed to fetch entity information", "error", err) + d.core.logger.Error("failed to lookup token namespace", "error", err) + return false + } + if tokenNS == nil { + d.core.logger.Error("failed to lookup token namespace", "error", namespace.ErrNoNamespace) return false } - acl, err := d.core.policyStore.ACL(ctx, entity, append(entityPolicies, te.Policies...)...) + // Add identity policies from all the namespaces + entity, identityPolicies, err := d.core.fetchEntityAndDerivedPolicies(ctx, tokenNS, te.EntityID) + if err != nil { + d.core.logger.Error("failed to fetch identity policies", "error", err) + return false + } + for nsID, nsPolicies := range identityPolicies { + policies[nsID] = append(policies[nsID], nsPolicies...) + } + + tokenCtx := namespace.ContextWithNamespace(ctx, tokenNS) + + // Construct the corresponding ACL object. Derive and use a new context that + // uses the req.ClientToken's namespace + acl, err := d.core.policyStore.ACL(tokenCtx, entity, policies) if err != nil { d.core.logger.Error("failed to retrieve ACL for token's policies", "token_policies", te.Policies, "error", err) return false @@ -61,7 +85,7 @@ func (d dynamicSystemView) SudoPrivilege(ctx context.Context, path string, token req := new(logical.Request) req.Operation = logical.ReadOperation req.Path = path - authResults := acl.AllowOperation(req) + authResults := acl.AllowOperation(ctx, req, true) return authResults.RootPrivs } @@ -71,11 +95,13 @@ func (d dynamicSystemView) fetchTTLs() (def, max time.Duration) { def = d.core.defaultLeaseTTL max = d.core.maxLeaseTTL - if d.mountEntry.Config.DefaultLeaseTTL != 0 { - def = d.mountEntry.Config.DefaultLeaseTTL - } - if d.mountEntry.Config.MaxLeaseTTL != 0 { - max = d.mountEntry.Config.MaxLeaseTTL + if d.mountEntry != nil { + if d.mountEntry.Config.DefaultLeaseTTL != 0 { + def = d.mountEntry.Config.DefaultLeaseTTL + } + if d.mountEntry.Config.MaxLeaseTTL != 0 { + max = d.mountEntry.Config.MaxLeaseTTL + } } return @@ -98,7 +124,15 @@ func (d dynamicSystemView) LocalMount() bool { // Checks if this is a primary Vault instance. Caller should hold the stateLock // in read mode. func (d dynamicSystemView) ReplicationState() consts.ReplicationState { - return d.core.ReplicationState() + state := d.core.ReplicationState() + if d.core.perfStandby { + state |= consts.ReplicationPerformanceStandby + } + return state +} + +func (d dynamicSystemView) HasFeature(feature license.Features) bool { + return d.core.HasFeature(feature) } // ResponseWrapData wraps the given data in a cubbyhole and returns the diff --git a/vault/expiration.go b/vault/expiration.go index 191b1de11b..b50cc2914a 100644 --- a/vault/expiration.go +++ b/vault/expiration.go @@ -13,14 +13,14 @@ import ( "time" "github.com/armon/go-metrics" - log "github.com/hashicorp/go-hclog" - "github.com/hashicorp/errwrap" + log "github.com/hashicorp/go-hclog" multierror "github.com/hashicorp/go-multierror" - "github.com/hashicorp/go-uuid" + "github.com/hashicorp/vault/helper/base62" "github.com/hashicorp/vault/helper/consts" "github.com/hashicorp/vault/helper/jsonutil" "github.com/hashicorp/vault/helper/locksutil" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/logical/framework" ) @@ -86,11 +86,52 @@ type ExpirationManager struct { leaseCheckCounter *uint32 logLeaseExpirations bool + expireFunc ExpireLeaseStrategy +} + +type ExpireLeaseStrategy func(context.Context, *ExpirationManager, *leaseEntry) + +// revokeIDFunc is invoked when a given ID is expired +func expireLeaseStrategyRevoke(ctx context.Context, m *ExpirationManager, le *leaseEntry) { + for attempt := uint(0); attempt < maxRevokeAttempts; attempt++ { + revokeCtx, cancel := context.WithTimeout(ctx, DefaultMaxRequestDuration) + revokeCtx = namespace.ContextWithNamespace(revokeCtx, le.namespace) + + go func() { + select { + case <-ctx.Done(): + case <-m.quitCh: + cancel() + } + }() + + select { + case <-m.quitCh: + m.logger.Error("shutting down, not attempting further revocation of lease", "lease_id", le.LeaseID) + return + case <-m.quitContext.Done(): + m.logger.Error("core context canceled, not attempting further revocation of lease", "lease_id", le.LeaseID) + return + default: + } + + m.coreStateLock.RLock() + err := m.Revoke(revokeCtx, le.LeaseID) + m.coreStateLock.RUnlock() + cancel() + if err == nil { + return + } + + m.logger.Error("failed to revoke lease", "lease_id", le.LeaseID, "error", err) + time.Sleep((1 << attempt) * revokeRetryBase) + } + m.logger.Error("maximum revoke attempts reached", "lease_id", le.LeaseID) } // NewExpirationManager creates a new ExpirationManager that is backed // using a given view, and uses the provided router for revocation. -func NewExpirationManager(c *Core, view *BarrierView, logger log.Logger) *ExpirationManager { +func NewExpirationManager(c *Core, view *BarrierView, e ExpireLeaseStrategy, logger log.Logger) *ExpirationManager { exp := &ExpirationManager{ core: c, router: c.router, @@ -112,6 +153,7 @@ func NewExpirationManager(c *Core, view *BarrierView, logger log.Logger) *Expira leaseCheckCounter: new(uint32), logLeaseExpirations: os.Getenv("VAULT_SKIP_LOGGING_LEASE_EXPIRATIONS") == "", + expireFunc: e, } *exp.restoreMode = 1 @@ -125,7 +167,7 @@ func NewExpirationManager(c *Core, view *BarrierView, logger log.Logger) *Expira // setupExpiration is invoked after we've loaded the mount table to // initialize the expiration manager -func (c *Core) setupExpiration() error { +func (c *Core) setupExpiration(e ExpireLeaseStrategy) error { c.metricsMutex.Lock() defer c.metricsMutex.Unlock() // Create a sub-view @@ -134,7 +176,7 @@ func (c *Core) setupExpiration() error { // Create the manager expLogger := c.baseLogger.Named("expiration") c.AddLogger(expLogger) - mgr := NewExpirationManager(c, view, expLogger) + mgr := NewExpirationManager(c, view, e, expLogger) c.expiration = mgr // Link the token store to this @@ -182,13 +224,28 @@ func (m *ExpirationManager) inRestoreMode() bool { return atomic.LoadInt32(m.restoreMode) == 1 } +func (m *ExpirationManager) invalidate(key string) { + + switch { + case strings.HasPrefix(key, leaseViewPrefix): + // Clear from the pending expiration + leaseID := strings.TrimPrefix(key, leaseViewPrefix) + m.pendingLock.Lock() + if pending, ok := m.pending[leaseID]; ok { + pending.timer.Stop() + delete(m.pending, leaseID) + } + m.pendingLock.Unlock() + } +} + // Tidy cleans up the dangling storage entries for leases. It scans the storage // view to find all the available leases, checks if the token embedded in it is // either empty or invalid and in both the cases, it revokes them. It also uses // a token cache to avoid multiple lookups of the same token ID. It is normally // not required to use the API that invokes this. This is only intended to // clean up the corrupt storage due to bugs. -func (m *ExpirationManager) Tidy() error { +func (m *ExpirationManager) Tidy(ctx context.Context) error { if m.inRestoreMode() { return errors.New("cannot run tidy while restoring leases") } @@ -218,7 +275,7 @@ func (m *ExpirationManager) Tidy() error { logger.Info("tidying leases", "progress", countLease) } - le, err := m.loadEntry(m.quitContext, leaseID) + le, err := m.loadEntry(ctx, leaseID) if err != nil { tidyErrors = multierror.Append(tidyErrors, errwrap.Wrapf(fmt.Sprintf("failed to load the lease ID %q: {{err}}", leaseID), err)) return @@ -240,14 +297,9 @@ func (m *ExpirationManager) Tidy() error { isValid, ok = tokenCache[le.ClientToken] if !ok { - saltedID, err := m.tokenStore.SaltID(m.quitContext, le.ClientToken) - if err != nil { - tidyErrors = multierror.Append(tidyErrors, errwrap.Wrapf("failed to lookup salt id: {{err}}", err)) - return - } lock := locksutil.LockForKey(m.tokenStore.tokenLocks, le.ClientToken) lock.RLock() - te, err := m.tokenStore.lookupSalted(m.quitContext, saltedID, true) + te, err := m.tokenStore.lookupInternal(ctx, le.ClientToken, false, true) lock.RUnlock() if err != nil { @@ -279,7 +331,7 @@ func (m *ExpirationManager) Tidy() error { if revokeLease { // Force the revocation and skip going through the token store // again - err = m.revokeCommon(m.quitContext, leaseID, true, true) + err = m.revokeCommon(ctx, leaseID, true, true) if err != nil { tidyErrors = multierror.Append(tidyErrors, errwrap.Wrapf(fmt.Sprintf("failed to revoke an invalid lease with ID %q: {{err}}", leaseID), err)) return @@ -288,7 +340,12 @@ func (m *ExpirationManager) Tidy() error { } } - if err := logical.ScanView(m.quitContext, m.idView, tidyFunc); err != nil { + ns, err := namespace.FromContext(ctx) + if err != nil { + return err + } + leaseView := m.leaseView(ns) + if err := logical.ScanView(m.quitContext, leaseView, tidyFunc); err != nil { return err } @@ -330,14 +387,18 @@ func (m *ExpirationManager) Restore(errorFunc func()) (retErr error) { // Accumulate existing leases m.logger.Debug("collecting leases") - existing, err := logical.CollectKeys(m.quitContext, m.idView) + existing, leaseCount, err := m.collectLeases() if err != nil { - return errwrap.Wrapf("failed to scan for leases: {{err}}", err) + return err } - m.logger.Debug("leases collected", "num_existing", len(existing)) + m.logger.Debug("leases collected", "num_existing", leaseCount) // Make the channels used for the worker pool - broker := make(chan string) + type lease struct { + namespace *namespace.Namespace + id string + } + broker := make(chan *lease) quit := make(chan bool) // Buffer these channels to prevent deadlocks errs := make(chan error, len(existing)) @@ -354,13 +415,14 @@ func (m *ExpirationManager) Restore(errorFunc func()) (retErr error) { for { select { - case leaseID, ok := <-broker: + case lease, ok := <-broker: // broker has been closed, we are done if !ok { return } - err := m.processRestore(leaseID) + ctx := namespace.ContextWithNamespace(m.quitContext, lease.namespace) + err := m.processRestore(ctx, lease.id) if err != nil { errs <- err continue @@ -384,20 +446,27 @@ func (m *ExpirationManager) Restore(errorFunc func()) (retErr error) { wg.Add(1) go func() { defer wg.Done() - for i, leaseID := range existing { - if i > 0 && i%500 == 0 { - m.logger.Debug("leases loading", "progress", i) - } + i := 0 + for ns := range existing { + for _, leaseID := range existing[ns] { + i++ + if i%500 == 0 { + m.logger.Debug("leases loading", "progress", i) + } - select { - case <-quit: - return + select { + case <-quit: + return - case <-m.quitCh: - return + case <-m.quitCh: + return - default: - broker <- leaseID + default: + broker <- &lease{ + namespace: ns, + id: leaseID, + } + } } } @@ -406,7 +475,7 @@ func (m *ExpirationManager) Restore(errorFunc func()) (retErr error) { }() // Ensure all keys on the chan are processed - for i := 0; i < len(existing); i++ { + for i := 0; i < leaseCount; i++ { select { case err := <-errs: // Close all go routines @@ -436,7 +505,7 @@ func (m *ExpirationManager) Restore(errorFunc func()) (retErr error) { // processRestore takes a lease and restores it in the expiration manager if it has // not already been seen -func (m *ExpirationManager) processRestore(leaseID string) error { +func (m *ExpirationManager) processRestore(ctx context.Context, leaseID string) error { m.restoreRequestLock.RLock() defer m.restoreRequestLock.RUnlock() @@ -454,7 +523,7 @@ func (m *ExpirationManager) processRestore(leaseID string) error { } // Load lease and restore expiration timer - _, err := m.loadEntryInternal(m.quitContext, leaseID, true, false) + _, err := m.loadEntryInternal(ctx, leaseID, true, false) if err != nil { return err } @@ -560,13 +629,13 @@ func (m *ExpirationManager) revokeCommon(ctx context.Context, leaseID string, fo } // Delete the entry - if err := m.deleteEntry(ctx, leaseID); err != nil { + if err := m.deleteEntry(ctx, le); err != nil { return err } // Delete the secondary index, but only if it's a leased secret (not auth) if le.Secret != nil { - if err := m.removeIndexByToken(ctx, le.ClientToken, le.LeaseID); err != nil { + if err := m.removeIndexByToken(ctx, le); err != nil { return err } } @@ -609,9 +678,17 @@ func (m *ExpirationManager) RevokePrefix(ctx context.Context, prefix string, syn // token store's revokeSalted function. func (m *ExpirationManager) RevokeByToken(ctx context.Context, te *logical.TokenEntry) error { defer metrics.MeasureSince([]string{"expire", "revoke-by-token"}, time.Now()) + tokenNS, err := NamespaceByID(ctx, te.NamespaceID, m.core) + if err != nil { + return err + } + if tokenNS == nil { + return namespace.ErrNoNamespace + } + tokenCtx := namespace.ContextWithNamespace(ctx, tokenNS) // Lookup the leases - existing, err := m.lookupLeasesByToken(ctx, te.ID) + existing, err := m.lookupLeasesByToken(tokenCtx, te) if err != nil { return errwrap.Wrapf("failed to scan for leases: {{err}}", err) } @@ -645,12 +722,17 @@ func (m *ExpirationManager) RevokeByToken(ctx context.Context, te *logical.Token // te.Path should never be empty, but we check just in case if te.Path != "" { - saltedID, err := m.tokenStore.SaltID(ctx, te.ID) + saltCtx := namespace.ContextWithNamespace(ctx, tokenNS) + saltedID, err := m.tokenStore.SaltID(saltCtx, te.ID) if err != nil { return err } tokenLeaseID := path.Join(te.Path, saltedID) + if tokenNS.ID != namespace.RootNamespaceID { + tokenLeaseID = fmt.Sprintf("%s.%s", tokenLeaseID, tokenNS.ID) + } + // We want to skip the revokeEntry call as that will call back into // revocation logic in the token store, which is what is running this // function in the first place -- it'd be a deadlock loop. Since the only @@ -687,7 +769,12 @@ func (m *ExpirationManager) revokePrefixCommon(ctx context.Context, prefix strin } // Accumulate existing leases - sub := m.idView.SubView(prefix) + ns, err := namespace.FromContext(ctx) + if err != nil { + return err + } + view := m.leaseView(ns) + sub := view.SubView(prefix) existing, err := logical.CollectKeys(ctx, sub) if err != nil { return errwrap.Wrapf("failed to scan for leases: {{err}}", err) @@ -734,7 +821,16 @@ func (m *ExpirationManager) Renew(ctx context.Context, leaseID string, increment return logical.ErrorResponse("lease does not correspond to a secret"), nil } - sysView := m.router.MatchingSystemView(le.Path) + reqNS, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } + if reqNS.ID != le.namespace.ID { + return nil, errors.New("cannot renew a lease across namespaces") + } + + sysViewCtx := namespace.ContextWithNamespace(ctx, le.namespace) + sysView := m.router.MatchingSystemView(sysViewCtx, le.Path) if sysView == nil { return nil, fmt.Errorf("unable to retrieve system view from router") } @@ -792,22 +888,46 @@ func (m *ExpirationManager) Renew(ctx context.Context, leaseID string, increment // RenewToken is used to renew a token which does not need to // invoke a logical backend. -func (m *ExpirationManager) RenewToken(ctx context.Context, req *logical.Request, source string, token string, +func (m *ExpirationManager) RenewToken(ctx context.Context, req *logical.Request, te *logical.TokenEntry, increment time.Duration) (*logical.Response, error) { defer metrics.MeasureSince([]string{"expire", "renew-token"}, time.Now()) - // Compute the Lease ID - saltedID, err := m.tokenStore.SaltID(ctx, token) + tokenNS, err := NamespaceByID(ctx, te.NamespaceID, m.core) if err != nil { return nil, err } - leaseID := path.Join(source, saltedID) + if tokenNS == nil { + return nil, namespace.ErrNoNamespace + } + + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } + if ns.ID != tokenNS.ID { + return nil, errors.New("cannot renew a token across namespaces") + } + + // Compute the Lease ID + saltedID, err := m.tokenStore.SaltID(ctx, te.ID) + if err != nil { + return nil, err + } + + leaseID := path.Join(te.Path, saltedID) + + if ns.ID != namespace.RootNamespaceID { + leaseID = fmt.Sprintf("%s.%s", leaseID, ns.ID) + } // Load the entry le, err := m.loadEntry(ctx, leaseID) if err != nil { return nil, err } + if le == nil { + return logical.ErrorResponse("invalid lease ID"), logical.ErrInvalidRequest + } // Check if the lease is renewable. Note that this also checks for a nil // lease and errors in that case as well. @@ -832,7 +952,8 @@ func (m *ExpirationManager) RenewToken(ctx context.Context, req *logical.Request return nil, nil } - sysView := m.router.MatchingSystemView(le.Path) + sysViewCtx := namespace.ContextWithNamespace(ctx, le.namespace) + sysView := m.router.MatchingSystemView(sysViewCtx, le.Path) if sysView == nil { return nil, fmt.Errorf("unable to retrieve system view from router") } @@ -848,7 +969,7 @@ func (m *ExpirationManager) RenewToken(ctx context.Context, req *logical.Request resp.Auth.TTL = ttl // Attach the ClientToken - resp.Auth.ClientToken = token + resp.Auth.ClientToken = te.ID // Refresh groups if resp.Auth.EntityID != "" && @@ -903,37 +1024,23 @@ func (m *ExpirationManager) Register(ctx context.Context, req *logical.Request, } // Create a lease entry - leaseUUID, err := uuid.GenerateUUID() + leaseRand, err := base62.Random(TokenLength, true) if err != nil { return "", err } - leaseID := path.Join(req.Path, leaseUUID) + ns, err := namespace.FromContext(ctx) + if err != nil { + return "", err + } - defer func() { - // If there is an error we want to rollback as much as possible (note - // that errors here are ignored to do as much cleanup as we can). We - // want to revoke a generated secret (since an error means we may not - // be successfully tracking it), remove indexes, and delete the entry. - if retErr != nil { - revResp, err := m.router.Route(m.quitContext, logical.RevokeRequest(req.Path, resp.Secret, resp.Data)) - if err != nil { - retErr = multierror.Append(retErr, errwrap.Wrapf("an additional internal error was encountered revoking the newly-generated secret: {{err}}", err)) - } else if revResp != nil && revResp.IsError() { - retErr = multierror.Append(retErr, errwrap.Wrapf("an additional error was encountered revoking the newly-generated secret: {{err}}", revResp.Error())) - } + leaseID := path.Join(req.Path, leaseRand) - if err := m.deleteEntry(ctx, leaseID); err != nil { - retErr = multierror.Append(retErr, errwrap.Wrapf("an additional error was encountered deleting any lease associated with the newly-generated secret: {{err}}", err)) - } + if ns.ID != namespace.RootNamespaceID { + leaseID = fmt.Sprintf("%s.%s", leaseID, ns.ID) + } - if err := m.removeIndexByToken(ctx, req.ClientToken, leaseID); err != nil { - retErr = multierror.Append(retErr, errwrap.Wrapf("an additional error was encountered removing lease indexes associated with the newly-generated secret: {{err}}", err)) - } - } - }() - - le := leaseEntry{ + le := &leaseEntry{ LeaseID: leaseID, ClientToken: req.ClientToken, Path: req.Path, @@ -941,20 +1048,45 @@ func (m *ExpirationManager) Register(ctx context.Context, req *logical.Request, Secret: resp.Secret, IssueTime: time.Now(), ExpireTime: resp.Secret.ExpirationTime(), + namespace: ns, } + defer func() { + // If there is an error we want to rollback as much as possible (note + // that errors here are ignored to do as much cleanup as we can). We + // want to revoke a generated secret (since an error means we may not + // be successfully tracking it), remove indexes, and delete the entry. + if retErr != nil { + revokeCtx := namespace.ContextWithNamespace(m.quitContext, ns) + revResp, err := m.router.Route(revokeCtx, logical.RevokeRequest(req.Path, resp.Secret, resp.Data)) + if err != nil { + retErr = multierror.Append(retErr, errwrap.Wrapf("an additional internal error was encountered revoking the newly-generated secret: {{err}}", err)) + } else if revResp != nil && revResp.IsError() { + retErr = multierror.Append(retErr, errwrap.Wrapf("an additional error was encountered revoking the newly-generated secret: {{err}}", revResp.Error())) + } + + if err := m.deleteEntry(ctx, le); err != nil { + retErr = multierror.Append(retErr, errwrap.Wrapf("an additional error was encountered deleting any lease associated with the newly-generated secret: {{err}}", err)) + } + + if err := m.removeIndexByToken(ctx, le); err != nil { + retErr = multierror.Append(retErr, errwrap.Wrapf("an additional error was encountered removing lease indexes associated with the newly-generated secret: {{err}}", err)) + } + } + }() + // Encode the entry - if err := m.persistEntry(ctx, &le); err != nil { + if err := m.persistEntry(ctx, le); err != nil { return "", err } // Maintain secondary index by token - if err := m.createIndexByToken(ctx, le.ClientToken, le.LeaseID); err != nil { + if err := m.createIndexByToken(ctx, le); err != nil { return "", err } // Setup revocation timer if there is a lease - m.updatePending(&le, resp.Secret.LeaseTotal()) + m.updatePending(le, resp.Secret.LeaseTotal()) // Done return le.LeaseID, nil @@ -963,30 +1095,45 @@ func (m *ExpirationManager) Register(ctx context.Context, req *logical.Request, // RegisterAuth is used to take an Auth response with an associated lease. // The token does not get a LeaseID, but the lease management is handled by // the expiration manager. -func (m *ExpirationManager) RegisterAuth(ctx context.Context, source string, auth *logical.Auth) error { +func (m *ExpirationManager) RegisterAuth(ctx context.Context, te *logical.TokenEntry, auth *logical.Auth) error { defer metrics.MeasureSince([]string{"expire", "register-auth"}, time.Now()) if auth.ClientToken == "" { return fmt.Errorf("cannot register an auth lease with an empty token") } - if strings.Contains(source, "..") { + if strings.Contains(te.Path, "..") { return consts.ErrPathContainsParentReferences } - saltedID, err := m.tokenStore.SaltID(ctx, auth.ClientToken) + tokenNS, err := NamespaceByID(ctx, te.NamespaceID, m.core) + if err != nil { + return err + } + if tokenNS == nil { + return namespace.ErrNoNamespace + } + + saltCtx := namespace.ContextWithNamespace(ctx, tokenNS) + saltedID, err := m.tokenStore.SaltID(saltCtx, auth.ClientToken) if err != nil { return err } + leaseID := path.Join(te.Path, saltedID) + if tokenNS.ID != namespace.RootNamespaceID { + leaseID = fmt.Sprintf("%s.%s", leaseID, tokenNS.ID) + } + // Create a lease entry le := leaseEntry{ - LeaseID: path.Join(source, saltedID), + LeaseID: leaseID, ClientToken: auth.ClientToken, Auth: auth, - Path: source, + Path: te.Path, IssueTime: time.Now(), ExpireTime: auth.ExpirationTime(), + namespace: tokenNS, } // Encode the entry @@ -1002,15 +1149,29 @@ func (m *ExpirationManager) RegisterAuth(ctx context.Context, source string, aut // FetchLeaseTimesByToken is a helper function to use token values to compute // the leaseID, rather than pushing that logic back into the token store. -func (m *ExpirationManager) FetchLeaseTimesByToken(ctx context.Context, source, token string) (*leaseEntry, error) { +func (m *ExpirationManager) FetchLeaseTimesByToken(ctx context.Context, te *logical.TokenEntry) (*leaseEntry, error) { defer metrics.MeasureSince([]string{"expire", "fetch-lease-times-by-token"}, time.Now()) - // Compute the Lease ID - saltedID, err := m.tokenStore.SaltID(ctx, token) + tokenNS, err := NamespaceByID(ctx, te.NamespaceID, m.core) if err != nil { return nil, err } - leaseID := path.Join(source, saltedID) + if tokenNS == nil { + return nil, namespace.ErrNoNamespace + } + + saltCtx := namespace.ContextWithNamespace(ctx, tokenNS) + saltedID, err := m.tokenStore.SaltID(saltCtx, te.ID) + if err != nil { + return nil, err + } + + leaseID := path.Join(te.Path, saltedID) + + if tokenNS.ID != namespace.RootNamespaceID { + leaseID = fmt.Sprintf("%s.%s", leaseID, tokenNS.ID) + } + return m.FetchLeaseTimes(ctx, leaseID) } @@ -1029,7 +1190,7 @@ func (m *ExpirationManager) FetchLeaseTimes(ctx context.Context, leaseID string) } // Load the entry - le, err := m.loadEntry(ctx, leaseID) + le, err := m.loadEntryInternal(ctx, leaseID, true, false) if err != nil { return nil, err } @@ -1091,7 +1252,7 @@ func (m *ExpirationManager) updatePendingInternal(le *leaseEntry, leaseTotal tim pending.timer.Reset(leaseTotal) } else { timer := time.AfterFunc(leaseTotal, func() { - m.expireID(le.LeaseID) + m.expireFunc(m.quitContext, m, le) }) pending = pendingInfo{ timer: timer, @@ -1104,62 +1265,23 @@ func (m *ExpirationManager) updatePendingInternal(le *leaseEntry, leaseTotal tim m.pending[le.LeaseID] = pending } -// expireID is invoked when a given ID is expired -func (m *ExpirationManager) expireID(leaseID string) { - // Clear from the pending expiration - m.pendingLock.Lock() - delete(m.pending, leaseID) - m.pendingLock.Unlock() - - for attempt := uint(0); attempt < maxRevokeAttempts; attempt++ { - ctx, cancel := context.WithTimeout(m.quitContext, DefaultMaxRequestDuration) - - go func() { - select { - case <-ctx.Done(): - case <-m.quitCh: - cancel() - } - }() - - select { - case <-m.quitCh: - m.logger.Error("shutting down, not attempting further revocation of lease", "lease_id", leaseID) - return - case <-m.quitContext.Done(): - m.logger.Error("core context canceled, not attempting further revocation of lease", "lease_id", leaseID) - return - default: - } - - m.coreStateLock.RLock() - err := m.Revoke(ctx, leaseID) - m.coreStateLock.RUnlock() - cancel() - if err == nil { - return - } - - m.logger.Error("failed to revoke lease", "lease_id", leaseID, "error", err) - time.Sleep((1 << attempt) * revokeRetryBase) - } - m.logger.Error("maximum revoke attempts reached", "lease_id", leaseID) -} - // revokeEntry is used to attempt revocation of an internal entry func (m *ExpirationManager) revokeEntry(ctx context.Context, le *leaseEntry) error { // Revocation of login tokens is special since we can by-pass the // backend and directly interact with the token store if le.Auth != nil { - if err := m.tokenStore.revokeTree(ctx, le.ClientToken); err != nil { + if err := m.tokenStore.revokeTree(ctx, le); err != nil { return errwrap.Wrapf("failed to revoke token: {{err}}", err) } return nil } + // Make sure we're operating in the right namespace + nsCtx := namespace.ContextWithNamespace(ctx, le.namespace) + // Handle standard revocation via backends - resp, err := m.router.Route(m.quitContext, logical.RevokeRequest(le.Path, le.Secret, le.Data)) + resp, err := m.router.Route(nsCtx, logical.RevokeRequest(le.Path, le.Secret, le.Data)) if err != nil || (resp != nil && resp.IsError()) { return errwrap.Wrapf(fmt.Sprintf("failed to revoke entry: resp: %#v err: {{err}}", resp), err) } @@ -1172,8 +1294,12 @@ func (m *ExpirationManager) renewEntry(ctx context.Context, le *leaseEntry, incr secret.IssueTime = le.IssueTime secret.Increment = increment secret.LeaseID = "" + + // Make sure we're operating in the right namespace + nsCtx := namespace.ContextWithNamespace(ctx, le.namespace) + req := logical.RenewRequest(le.Path, &secret, le.Data) - resp, err := m.router.Route(ctx, req) + resp, err := m.router.Route(nsCtx, req) if err != nil || (resp != nil && resp.IsError()) { return nil, errwrap.Wrapf(fmt.Sprintf("failed to renew entry: resp: %#v err: {{err}}", resp), err) } @@ -1192,9 +1318,12 @@ func (m *ExpirationManager) renewAuthEntry(ctx context.Context, req *logical.Req auth.ClientToken = "" } + // Make sure we're operating in the right namespace + nsCtx := namespace.ContextWithNamespace(ctx, le.namespace) + authReq := logical.RenewAuthRequest(le.Path, &auth, nil) authReq.Connection = req.Connection - resp, err := m.router.Route(ctx, authReq) + resp, err := m.router.Route(nsCtx, authReq) if err != nil { return nil, errwrap.Wrapf("failed to renew entry: {{err}}", err) } @@ -1215,13 +1344,32 @@ func (m *ExpirationManager) loadEntry(ctx context.Context, leaseID string) (*lea defer m.unlockLease(leaseID) } } + + _, nsID := namespace.SplitIDFromString(leaseID) + if nsID != "" { + leaseNS, err := NamespaceByID(ctx, nsID, m.core) + if err != nil { + return nil, err + } + if leaseNS != nil { + ctx = namespace.ContextWithNamespace(ctx, leaseNS) + } + } else { + ctx = namespace.ContextWithNamespace(ctx, namespace.RootNamespace) + } return m.loadEntryInternal(ctx, leaseID, restoreMode, true) } // loadEntryInternal is used when you need to load an entry but also need to // control the lifecycle of the restoreLock func (m *ExpirationManager) loadEntryInternal(ctx context.Context, leaseID string, restoreMode bool, checkRestored bool) (*leaseEntry, error) { - out, err := m.idView.Get(ctx, leaseID) + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } + + view := m.leaseView(ns) + out, err := view.Get(ctx, leaseID) if err != nil { return nil, errwrap.Wrapf("failed to read lease entry: {{err}}", err) } @@ -1232,6 +1380,7 @@ func (m *ExpirationManager) loadEntryInternal(ctx context.Context, leaseID strin if err != nil { return nil, errwrap.Wrapf("failed to decode lease entry: {{err}}", err) } + le.namespace = ns if restoreMode { if checkRestored { @@ -1269,56 +1418,87 @@ func (m *ExpirationManager) persistEntry(ctx context.Context, le *leaseEntry) er if le.Auth != nil && len(le.Auth.Policies) == 1 && le.Auth.Policies[0] == "root" { ent.SealWrap = true } - if err := m.idView.Put(ctx, &ent); err != nil { + + view := m.leaseView(le.namespace) + if err := view.Put(ctx, &ent); err != nil { return errwrap.Wrapf("failed to persist lease entry: {{err}}", err) } return nil } // deleteEntry is used to delete a lease entry -func (m *ExpirationManager) deleteEntry(ctx context.Context, leaseID string) error { - if err := m.idView.Delete(ctx, leaseID); err != nil { +func (m *ExpirationManager) deleteEntry(ctx context.Context, le *leaseEntry) error { + view := m.leaseView(le.namespace) + if err := view.Delete(ctx, le.LeaseID); err != nil { return errwrap.Wrapf("failed to delete lease entry: {{err}}", err) } return nil } // createIndexByToken creates a secondary index from the token to a lease entry -func (m *ExpirationManager) createIndexByToken(ctx context.Context, token, leaseID string) error { - saltedID, err := m.tokenStore.SaltID(ctx, token) +func (m *ExpirationManager) createIndexByToken(ctx context.Context, le *leaseEntry) error { + tokenNS := namespace.RootNamespace + saltCtx := namespace.ContextWithNamespace(ctx, namespace.RootNamespace) + _, nsID := namespace.SplitIDFromString(le.ClientToken) + if nsID != "" { + tokenNS, err := NamespaceByID(ctx, nsID, m.core) + if err != nil { + return err + } + if tokenNS != nil { + saltCtx = namespace.ContextWithNamespace(ctx, tokenNS) + } + } + + saltedID, err := m.tokenStore.SaltID(saltCtx, le.ClientToken) if err != nil { return err } - leaseSaltedID, err := m.tokenStore.SaltID(ctx, leaseID) + leaseSaltedID, err := m.tokenStore.SaltID(saltCtx, le.LeaseID) if err != nil { return err } ent := logical.StorageEntry{ Key: saltedID + "/" + leaseSaltedID, - Value: []byte(leaseID), + Value: []byte(le.LeaseID), } - if err := m.tokenView.Put(ctx, &ent); err != nil { + tokenView := m.tokenIndexView(tokenNS) + if err := tokenView.Put(ctx, &ent); err != nil { return errwrap.Wrapf("failed to persist lease index entry: {{err}}", err) } return nil } // indexByToken looks up the secondary index from the token to a lease entry -func (m *ExpirationManager) indexByToken(ctx context.Context, token, leaseID string) (*logical.StorageEntry, error) { - saltedID, err := m.tokenStore.SaltID(ctx, token) +func (m *ExpirationManager) indexByToken(ctx context.Context, le *leaseEntry) (*logical.StorageEntry, error) { + tokenNS := namespace.RootNamespace + saltCtx := namespace.ContextWithNamespace(ctx, tokenNS) + _, nsID := namespace.SplitIDFromString(le.ClientToken) + if nsID != "" { + tokenNS, err := NamespaceByID(ctx, nsID, m.core) + if err != nil { + return nil, err + } + if tokenNS != nil { + saltCtx = namespace.ContextWithNamespace(ctx, tokenNS) + } + } + + saltedID, err := m.tokenStore.SaltID(saltCtx, le.ClientToken) if err != nil { return nil, err } - leaseSaltedID, err := m.tokenStore.SaltID(ctx, leaseID) + leaseSaltedID, err := m.tokenStore.SaltID(saltCtx, le.LeaseID) if err != nil { return nil, err } key := saltedID + "/" + leaseSaltedID - entry, err := m.tokenView.Get(ctx, key) + tokenView := m.tokenIndexView(tokenNS) + entry, err := tokenView.Get(ctx, key) if err != nil { return nil, fmt.Errorf("failed to look up secondary index entry") } @@ -1326,19 +1506,33 @@ func (m *ExpirationManager) indexByToken(ctx context.Context, token, leaseID str } // removeIndexByToken removes the secondary index from the token to a lease entry -func (m *ExpirationManager) removeIndexByToken(ctx context.Context, token, leaseID string) error { - saltedID, err := m.tokenStore.SaltID(ctx, token) +func (m *ExpirationManager) removeIndexByToken(ctx context.Context, le *leaseEntry) error { + tokenNS := namespace.RootNamespace + saltCtx := namespace.ContextWithNamespace(ctx, namespace.RootNamespace) + _, nsID := namespace.SplitIDFromString(le.ClientToken) + if nsID != "" { + tokenNS, err := NamespaceByID(ctx, nsID, m.core) + if err != nil { + return err + } + if tokenNS != nil { + saltCtx = namespace.ContextWithNamespace(ctx, tokenNS) + } + } + + saltedID, err := m.tokenStore.SaltID(saltCtx, le.ClientToken) if err != nil { return err } - leaseSaltedID, err := m.tokenStore.SaltID(ctx, leaseID) + leaseSaltedID, err := m.tokenStore.SaltID(saltCtx, le.LeaseID) if err != nil { return err } key := saltedID + "/" + leaseSaltedID - if err := m.tokenView.Delete(ctx, key); err != nil { + tokenView := m.tokenIndexView(tokenNS) + if err := tokenView.Delete(ctx, key); err != nil { return errwrap.Wrapf("failed to delete lease index entry: {{err}}", err) } return nil @@ -1349,12 +1543,25 @@ func (m *ExpirationManager) removeIndexByToken(ctx context.Context, token, lease // it's created. func (m *ExpirationManager) CreateOrFetchRevocationLeaseByToken(ctx context.Context, te *logical.TokenEntry) (string, error) { // Fetch the saltedID of the token and construct the leaseID - saltedID, err := m.tokenStore.SaltID(ctx, te.ID) + tokenNS, err := NamespaceByID(ctx, te.NamespaceID, m.core) + if err != nil { + return "", err + } + if tokenNS == nil { + return "", namespace.ErrNoNamespace + } + + saltCtx := namespace.ContextWithNamespace(ctx, tokenNS) + saltedID, err := m.tokenStore.SaltID(saltCtx, te.ID) if err != nil { return "", err } leaseID := path.Join(te.Path, saltedID) + if tokenNS.ID != namespace.RootNamespaceID { + leaseID = fmt.Sprintf("%s.%s", leaseID, tokenNS.ID) + } + // Load the entry le, err := m.loadEntry(ctx, leaseID) if err != nil { @@ -1383,6 +1590,7 @@ func (m *ExpirationManager) CreateOrFetchRevocationLeaseByToken(ctx context.Cont Path: te.Path, IssueTime: now, ExpireTime: now.Add(time.Nanosecond), + namespace: tokenNS, } // Encode the entry @@ -1395,15 +1603,26 @@ func (m *ExpirationManager) CreateOrFetchRevocationLeaseByToken(ctx context.Cont } // lookupLeasesByToken is used to lookup all the leaseID's via the tokenID -func (m *ExpirationManager) lookupLeasesByToken(ctx context.Context, token string) ([]string, error) { - saltedID, err := m.tokenStore.SaltID(ctx, token) +func (m *ExpirationManager) lookupLeasesByToken(ctx context.Context, te *logical.TokenEntry) ([]string, error) { + tokenNS, err := NamespaceByID(ctx, te.NamespaceID, m.core) + if err != nil { + return nil, err + } + if tokenNS == nil { + return nil, namespace.ErrNoNamespace + } + + saltCtx := namespace.ContextWithNamespace(ctx, tokenNS) + saltedID, err := m.tokenStore.SaltID(saltCtx, te.ID) if err != nil { return nil, err } + tokenView := m.tokenIndexView(tokenNS) + // Scan via the index for sub-leases prefix := saltedID + "/" - subKeys, err := m.tokenView.List(ctx, prefix) + subKeys, err := tokenView.List(ctx, prefix) if err != nil { return nil, errwrap.Wrapf("failed to list leases: {{err}}", err) } @@ -1411,7 +1630,7 @@ func (m *ExpirationManager) lookupLeasesByToken(ctx context.Context, token strin // Read each index entry leaseIDs := make([]string, 0, len(subKeys)) for _, sub := range subKeys { - out, err := m.tokenView.Get(ctx, prefix+sub) + out, err := tokenView.Get(ctx, prefix+sub) if err != nil { return nil, errwrap.Wrapf("failed to read lease index: {{err}}", err) } @@ -1452,6 +1671,8 @@ type leaseEntry struct { IssueTime time.Time `json:"issue_time"` ExpireTime time.Time `json:"expire_time"` LastRenewalTime time.Time `json:"last_renewal_time"` + + namespace *namespace.Namespace } // encode is used to JSON encode the lease entry diff --git a/vault/expiration_test.go b/vault/expiration_test.go index 6660f2068b..6af9bcb619 100644 --- a/vault/expiration_test.go +++ b/vault/expiration_test.go @@ -16,6 +16,7 @@ import ( log "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-uuid" "github.com/hashicorp/vault/helper/logging" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/logical/framework" "github.com/hashicorp/vault/physical" @@ -64,7 +65,7 @@ func TestExpiration_Tidy(t *testing.T) { } // Scan the storage with the count func set - if err = logical.ScanView(context.Background(), exp.idView, countFunc); err != nil { + if err = logical.ScanView(namespace.TestContext(), exp.idView, countFunc); err != nil { t.Fatal(err) } @@ -75,12 +76,13 @@ func TestExpiration_Tidy(t *testing.T) { // Create a lease entry without a client token in it le := &leaseEntry{ - LeaseID: "lease/with/no/client/token", - Path: "foo/bar", + LeaseID: "lease/with/no/client/token", + Path: "foo/bar", + namespace: namespace.TestNamespace(), } // Persist the invalid lease entry - if err = exp.persistEntry(context.Background(), le); err != nil { + if err = exp.persistEntry(namespace.TestContext(), le); err != nil { t.Fatalf("error persisting entry: %v", err) } @@ -96,7 +98,7 @@ func TestExpiration_Tidy(t *testing.T) { } // Run the tidy operation - err = exp.Tidy() + err = exp.Tidy(namespace.TestContext()) if err != nil { t.Fatal(err) } @@ -115,7 +117,7 @@ func TestExpiration_Tidy(t *testing.T) { le.ClientToken = "invalidtoken" // Persist the invalid lease entry - if err = exp.persistEntry(context.Background(), le); err != nil { + if err = exp.persistEntry(namespace.TestContext(), le); err != nil { t.Fatalf("error persisting entry: %v", err) } @@ -131,7 +133,7 @@ func TestExpiration_Tidy(t *testing.T) { } // Run the tidy operation - err = exp.Tidy() + err = exp.Tidy(namespace.TestContext()) if err != nil { t.Fatal(err) } @@ -147,7 +149,7 @@ func TestExpiration_Tidy(t *testing.T) { } // Attach an invalid token with 2 leases - if err = exp.persistEntry(context.Background(), le); err != nil { + if err = exp.persistEntry(namespace.TestContext(), le); err != nil { t.Fatalf("error persisting entry: %v", err) } @@ -157,7 +159,7 @@ func TestExpiration_Tidy(t *testing.T) { } // Run the tidy operation - err = exp.Tidy() + err = exp.Tidy(namespace.TestContext()) if err != nil { t.Fatal(err) } @@ -188,7 +190,7 @@ func TestExpiration_Tidy(t *testing.T) { "test_key": "test_value", }, } - _, err := exp.Register(context.Background(), req, resp) + _, err := exp.Register(namespace.TestContext(), req, resp) if err != nil { t.Fatalf("err: %v", err) } @@ -211,11 +213,11 @@ func TestExpiration_Tidy(t *testing.T) { // one tidy operation can be in flight at any time. One of these requests // should error out. go func() { - errCh1 <- exp.Tidy() + errCh1 <- exp.Tidy(namespace.TestContext()) }() go func() { - errCh2 <- exp.Tidy() + errCh2 <- exp.Tidy(namespace.TestContext()) }() var err1, err2 error @@ -241,12 +243,12 @@ func TestExpiration_Tidy(t *testing.T) { le.ClientToken = root.ID // Attach a valid token with the leases - if err = exp.persistEntry(context.Background(), le); err != nil { + if err = exp.persistEntry(namespace.TestContext(), le); err != nil { t.Fatalf("error persisting entry: %v", err) } // Run the tidy operation - err = exp.Tidy() + err = exp.Tidy(namespace.TestContext()) if err != nil { t.Fatal(err) } @@ -318,7 +320,7 @@ func benchmarkExpirationBackend(b *testing.B, physicalBackend physical.Backend, if err != nil { b.Fatal(err) } - err = exp.router.Mount(noop, "prod/aws/", &MountEntry{Path: "prod/aws/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor"}, view) + err = exp.router.Mount(noop, "prod/aws/", &MountEntry{Path: "prod/aws/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.TestNamespace()}, view) if err != nil { b.Fatal(err) } @@ -378,7 +380,7 @@ func TestExpiration_Restore(t *testing.T) { if err != nil { t.Fatal(err) } - err = exp.router.Mount(noop, "prod/aws/", &MountEntry{Path: "prod/aws/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor"}, view) + err = exp.router.Mount(noop, "prod/aws/", &MountEntry{Path: "prod/aws/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.TestNamespace()}, view) if err != nil { t.Fatal(err) } @@ -405,7 +407,7 @@ func TestExpiration_Restore(t *testing.T) { "secret_key": "abcd", }, } - _, err := exp.Register(context.Background(), req, resp) + _, err := exp.Register(namespace.TestContext(), req, resp) if err != nil { t.Fatalf("err: %v", err) } @@ -462,7 +464,7 @@ func TestExpiration_Register(t *testing.T) { }, } - id, err := exp.Register(context.Background(), req, resp) + id, err := exp.Register(namespace.TestContext(), req, resp) if err != nil { t.Fatalf("err: %v", err) } @@ -490,12 +492,20 @@ func TestExpiration_RegisterAuth(t *testing.T) { }, } - err = exp.RegisterAuth(context.Background(), "auth/github/login", auth) + te := &logical.TokenEntry{ + Path: "auth/github/login", + NamespaceID: namespace.RootNamespaceID, + } + err = exp.RegisterAuth(namespace.TestContext(), te, auth) if err != nil { t.Fatalf("err: %v", err) } - err = exp.RegisterAuth(context.Background(), "auth/github/../login", auth) + te = &logical.TokenEntry{ + Path: "auth/github/../login", + NamespaceID: namespace.RootNamespaceID, + } + err = exp.RegisterAuth(namespace.TestContext(), te, auth) if err == nil { t.Fatal("expected error") } @@ -512,13 +522,23 @@ func TestExpiration_RegisterAuth_NoLease(t *testing.T) { ClientToken: root.ID, } - err = exp.RegisterAuth(context.Background(), "auth/github/login", auth) + te := &logical.TokenEntry{ + ID: root.ID, + Path: "auth/github/login", + NamespaceID: namespace.RootNamespaceID, + } + err = exp.RegisterAuth(namespace.TestContext(), te, auth) if err != nil { t.Fatalf("err: %v", err) } // Should not be able to renew, no expiration - resp, err := exp.RenewToken(context.Background(), &logical.Request{}, "auth/github/login", root.ID, 0) + te = &logical.TokenEntry{ + ID: root.ID, + Path: "auth/github/login", + NamespaceID: namespace.RootNamespaceID, + } + resp, err := exp.RenewToken(namespace.TestContext(), &logical.Request{}, te, 0) if err != nil && (err != logical.ErrInvalidRequest || (resp != nil && resp.IsError() && resp.Error().Error() != "lease not found or lease is not renewable")) { t.Fatalf("bad: err:%v resp:%#v", err, resp) } @@ -530,7 +550,7 @@ func TestExpiration_RegisterAuth_NoLease(t *testing.T) { time.Sleep(20 * time.Millisecond) // Verify token does not get revoked - out, err := exp.tokenStore.Lookup(context.Background(), root.ID) + out, err := exp.tokenStore.Lookup(namespace.TestContext(), root.ID) if err != nil { t.Fatalf("err: %v", err) } @@ -548,7 +568,7 @@ func TestExpiration_Revoke(t *testing.T) { if err != nil { t.Fatal(err) } - err = exp.router.Mount(noop, "prod/aws/", &MountEntry{Path: "prod/aws/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor"}, view) + err = exp.router.Mount(noop, "prod/aws/", &MountEntry{Path: "prod/aws/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.TestNamespace()}, view) if err != nil { t.Fatal(err) } @@ -570,12 +590,12 @@ func TestExpiration_Revoke(t *testing.T) { }, } - id, err := exp.Register(context.Background(), req, resp) + id, err := exp.Register(namespace.TestContext(), req, resp) if err != nil { t.Fatalf("err: %v", err) } - if err := exp.Revoke(context.Background(), id); err != nil { + if err := exp.Revoke(namespace.TestContext(), id); err != nil { t.Fatalf("err: %v", err) } @@ -594,7 +614,7 @@ func TestExpiration_RevokeOnExpire(t *testing.T) { if err != nil { t.Fatal(err) } - err = exp.router.Mount(noop, "prod/aws/", &MountEntry{Path: "prod/aws/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor"}, view) + err = exp.router.Mount(noop, "prod/aws/", &MountEntry{Path: "prod/aws/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.TestNamespace()}, view) if err != nil { t.Fatal(err) } @@ -616,7 +636,7 @@ func TestExpiration_RevokeOnExpire(t *testing.T) { }, } - _, err = exp.Register(context.Background(), req, resp) + _, err = exp.Register(namespace.TestContext(), req, resp) if err != nil { t.Fatalf("err: %v", err) } @@ -651,7 +671,7 @@ func TestExpiration_RevokePrefix(t *testing.T) { if err != nil { t.Fatal(err) } - err = exp.router.Mount(noop, "prod/aws/", &MountEntry{Path: "prod/aws/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor"}, view) + err = exp.router.Mount(noop, "prod/aws/", &MountEntry{Path: "prod/aws/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.TestNamespace()}, view) if err != nil { t.Fatal(err) } @@ -678,14 +698,14 @@ func TestExpiration_RevokePrefix(t *testing.T) { "secret_key": "abcd", }, } - _, err := exp.Register(context.Background(), req, resp) + _, err := exp.Register(namespace.TestContext(), req, resp) if err != nil { t.Fatalf("err: %v", err) } } // Should nuke all the keys - if err := exp.RevokePrefix(context.Background(), "prod/aws/", true); err != nil { + if err := exp.RevokePrefix(namespace.TestContext(), "prod/aws/", true); err != nil { t.Fatalf("err: %v", err) } @@ -719,7 +739,7 @@ func TestExpiration_RevokeByToken(t *testing.T) { if err != nil { t.Fatal(err) } - err = exp.router.Mount(noop, "prod/aws/", &MountEntry{Path: "prod/aws/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor"}, view) + err = exp.router.Mount(noop, "prod/aws/", &MountEntry{Path: "prod/aws/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.TestNamespace()}, view) if err != nil { t.Fatal(err) } @@ -746,7 +766,7 @@ func TestExpiration_RevokeByToken(t *testing.T) { "secret_key": "abcd", }, } - _, err := exp.Register(context.Background(), req, resp) + _, err := exp.Register(namespace.TestContext(), req, resp) if err != nil { t.Fatalf("err: %v", err) } @@ -754,9 +774,10 @@ func TestExpiration_RevokeByToken(t *testing.T) { // Should nuke all the keys te := &logical.TokenEntry{ - ID: "foobarbaz", + ID: "foobarbaz", + NamespaceID: namespace.RootNamespaceID, } - if err := exp.RevokeByToken(context.Background(), te); err != nil { + if err := exp.RevokeByToken(namespace.TestContext(), te); err != nil { t.Fatalf("err: %v", err) } @@ -806,7 +827,7 @@ func TestExpiration_RevokeByToken_Blocking(t *testing.T) { if err != nil { t.Fatal(err) } - err = exp.router.Mount(noop, "prod/aws/", &MountEntry{Path: "prod/aws/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor"}, view) + err = exp.router.Mount(noop, "prod/aws/", &MountEntry{Path: "prod/aws/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.TestNamespace()}, view) if err != nil { t.Fatal(err) } @@ -833,7 +854,7 @@ func TestExpiration_RevokeByToken_Blocking(t *testing.T) { "secret_key": "abcd", }, } - _, err := exp.Register(context.Background(), req, resp) + _, err := exp.Register(namespace.TestContext(), req, resp) if err != nil { t.Fatalf("err: %v", err) } @@ -841,9 +862,10 @@ func TestExpiration_RevokeByToken_Blocking(t *testing.T) { // Should nuke all the keys te := &logical.TokenEntry{ - ID: "foobarbaz", + ID: "foobarbaz", + NamespaceID: namespace.RootNamespaceID, } - if err := exp.RevokeByToken(context.Background(), te); err != nil { + if err := exp.RevokeByToken(namespace.TestContext(), te); err != nil { t.Fatalf("err: %v", err) } @@ -898,13 +920,24 @@ func TestExpiration_RenewToken(t *testing.T) { Renewable: true, }, } - err = exp.RegisterAuth(context.Background(), "auth/token/login", auth) + + te := &logical.TokenEntry{ + ID: root.ID, + Path: "auth/token/login", + NamespaceID: namespace.RootNamespaceID, + } + err = exp.RegisterAuth(namespace.TestContext(), te, auth) if err != nil { t.Fatalf("err: %v", err) } // Renew the token - out, err := exp.RenewToken(context.Background(), &logical.Request{}, "auth/token/login", root.ID, 0) + te = &logical.TokenEntry{ + ID: root.ID, + Path: "auth/token/login", + NamespaceID: namespace.RootNamespaceID, + } + out, err := exp.RenewToken(namespace.TestContext(), &logical.Request{}, te, 0) if err != nil { t.Fatalf("err: %v", err) } @@ -922,8 +955,9 @@ func TestExpiration_RenewToken_period(t *testing.T) { DisplayName: "root", CreationTime: time.Now().Unix(), Period: time.Minute, + NamespaceID: namespace.RootNamespaceID, } - if err := exp.tokenStore.create(context.Background(), root); err != nil { + if err := exp.tokenStore.create(namespace.TestContext(), root); err != nil { t.Fatalf("err: %v", err) } @@ -936,13 +970,23 @@ func TestExpiration_RenewToken_period(t *testing.T) { }, Period: time.Minute, } - err := exp.RegisterAuth(context.Background(), "auth/token/login", auth) + te := &logical.TokenEntry{ + ID: root.ID, + Path: "auth/token/login", + NamespaceID: namespace.RootNamespaceID, + } + err := exp.RegisterAuth(namespace.TestContext(), te, auth) if err != nil { t.Fatalf("err: %v", err) } // Renew the token - out, err := exp.RenewToken(context.Background(), &logical.Request{}, "auth/token/login", root.ID, 0) + te = &logical.TokenEntry{ + ID: root.ID, + Path: "auth/token/login", + NamespaceID: namespace.RootNamespaceID, + } + out, err := exp.RenewToken(namespace.TestContext(), &logical.Request{}, te, 0) if err != nil { t.Fatalf("err: %v", err) } @@ -984,7 +1028,7 @@ func TestExpiration_RenewToken_period_backend(t *testing.T) { if err != nil { t.Fatal(err) } - err = exp.router.Mount(noop, "auth/foo/", &MountEntry{Path: "auth/foo/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor"}, view) + err = exp.router.Mount(noop, "auth/foo/", &MountEntry{Path: "auth/foo/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.TestNamespace()}, view) if err != nil { t.Fatal(err) } @@ -998,15 +1042,25 @@ func TestExpiration_RenewToken_period_backend(t *testing.T) { }, Period: 5 * time.Second, } + te := &logical.TokenEntry{ + ID: root.ID, + Path: "auth/foo/login", + NamespaceID: namespace.RootNamespaceID, + } - err = exp.RegisterAuth(context.Background(), "auth/foo/login", auth) + err = exp.RegisterAuth(namespace.TestContext(), te, auth) if err != nil { t.Fatalf("err: %v", err) } // Wait 3 seconds time.Sleep(3 * time.Second) - resp, err := exp.RenewToken(context.Background(), &logical.Request{}, "auth/foo/login", root.ID, 0) + te = &logical.TokenEntry{ + ID: root.ID, + Path: "auth/foo/login", + NamespaceID: namespace.RootNamespaceID, + } + resp, err := exp.RenewToken(namespace.TestContext(), &logical.Request{}, te, 0) if err != nil { t.Fatalf("err: %v", err) } @@ -1019,7 +1073,7 @@ func TestExpiration_RenewToken_period_backend(t *testing.T) { // Wait another 3 seconds. If period works correctly, this should not fail time.Sleep(3 * time.Second) - resp, err = exp.RenewToken(context.Background(), &logical.Request{}, "auth/foo/login", root.ID, 0) + resp, err = exp.RenewToken(namespace.TestContext(), &logical.Request{}, te, 0) if err != nil { t.Fatalf("err: %v", err) } @@ -1046,14 +1100,24 @@ func TestExpiration_RenewToken_NotRenewable(t *testing.T) { Renewable: false, }, } - err = exp.RegisterAuth(context.Background(), "auth/github/login", auth) + te := &logical.TokenEntry{ + ID: root.ID, + Path: "auth/foo/login", + NamespaceID: namespace.RootNamespaceID, + } + err = exp.RegisterAuth(namespace.TestContext(), te, auth) if err != nil { t.Fatalf("err: %v", err) } // Attempt to renew the token - resp, err := exp.RenewToken(context.Background(), &logical.Request{}, "auth/github/login", root.ID, 0) - if err != nil && (err != logical.ErrInvalidRequest || (resp != nil && resp.IsError() && resp.Error().Error() != "lease is not renewable")) { + te = &logical.TokenEntry{ + ID: root.ID, + Path: "auth/github/login", + NamespaceID: namespace.RootNamespaceID, + } + resp, err := exp.RenewToken(namespace.TestContext(), &logical.Request{}, te, 0) + if err != nil && (err != logical.ErrInvalidRequest || (resp != nil && resp.IsError() && resp.Error().Error() != "invalid lease ID")) { t.Fatalf("bad: err:%v resp:%#v", err, resp) } if resp == nil { @@ -1071,7 +1135,7 @@ func TestExpiration_Renew(t *testing.T) { if err != nil { t.Fatal(err) } - err = exp.router.Mount(noop, "prod/aws/", &MountEntry{Path: "prod/aws/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor"}, view) + err = exp.router.Mount(noop, "prod/aws/", &MountEntry{Path: "prod/aws/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.TestNamespace()}, view) if err != nil { t.Fatal(err) } @@ -1094,7 +1158,7 @@ func TestExpiration_Renew(t *testing.T) { }, } - id, err := exp.Register(context.Background(), req, resp) + id, err := exp.Register(namespace.TestContext(), req, resp) if err != nil { t.Fatalf("err: %v", err) } @@ -1111,7 +1175,7 @@ func TestExpiration_Renew(t *testing.T) { }, } - out, err := exp.Renew(context.Background(), id, 0) + out, err := exp.Renew(namespace.TestContext(), id, 0) if err != nil { t.Fatalf("err: %v", err) } @@ -1141,7 +1205,7 @@ func TestExpiration_Renew_NotRenewable(t *testing.T) { if err != nil { t.Fatal(err) } - err = exp.router.Mount(noop, "prod/aws/", &MountEntry{Path: "prod/aws/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor"}, view) + err = exp.router.Mount(noop, "prod/aws/", &MountEntry{Path: "prod/aws/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.TestNamespace()}, view) if err != nil { t.Fatal(err) } @@ -1164,12 +1228,12 @@ func TestExpiration_Renew_NotRenewable(t *testing.T) { }, } - id, err := exp.Register(context.Background(), req, resp) + id, err := exp.Register(namespace.TestContext(), req, resp) if err != nil { t.Fatalf("err: %v", err) } - _, err = exp.Renew(context.Background(), id, 0) + _, err = exp.Renew(namespace.TestContext(), id, 0) if err.Error() != "lease is not renewable" { t.Fatalf("err: %v", err) } @@ -1191,7 +1255,7 @@ func TestExpiration_Renew_RevokeOnExpire(t *testing.T) { if err != nil { t.Fatal(err) } - err = exp.router.Mount(noop, "prod/aws/", &MountEntry{Path: "prod/aws/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor"}, view) + err = exp.router.Mount(noop, "prod/aws/", &MountEntry{Path: "prod/aws/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.TestNamespace()}, view) if err != nil { t.Fatal(err) } @@ -1214,7 +1278,7 @@ func TestExpiration_Renew_RevokeOnExpire(t *testing.T) { }, } - id, err := exp.Register(context.Background(), req, resp) + id, err := exp.Register(namespace.TestContext(), req, resp) if err != nil { t.Fatalf("err: %v", err) } @@ -1231,7 +1295,7 @@ func TestExpiration_Renew_RevokeOnExpire(t *testing.T) { }, } - _, err = exp.Renew(context.Background(), id, 0) + _, err = exp.Renew(namespace.TestContext(), id, 0) if err != nil { t.Fatalf("err: %v", err) } @@ -1267,7 +1331,7 @@ func TestExpiration_revokeEntry(t *testing.T) { if err != nil { t.Fatal(err) } - err = exp.router.Mount(noop, "foo/bar/", &MountEntry{Path: "foo/bar/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor"}, view) + err = exp.router.Mount(noop, "foo/bar/", &MountEntry{Path: "foo/bar/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.TestNamespace()}, view) if err != nil { t.Fatal(err) } @@ -1285,9 +1349,10 @@ func TestExpiration_revokeEntry(t *testing.T) { }, IssueTime: time.Now(), ExpireTime: time.Now(), + namespace: namespace.TestNamespace(), } - err = exp.revokeEntry(context.Background(), le) + err = exp.revokeEntry(namespace.TestContext(), le) if err != nil { t.Fatalf("err: %v", err) } @@ -1336,17 +1401,18 @@ func TestExpiration_revokeEntry_token(t *testing.T) { Path: "foo/bar", IssueTime: time.Now(), ExpireTime: time.Now(), + namespace: namespace.TestNamespace(), } - if err := exp.persistEntry(context.Background(), le); err != nil { + if err := exp.persistEntry(namespace.TestContext(), le); err != nil { t.Fatalf("error persisting entry: %v", err) } - if err := exp.createIndexByToken(context.Background(), le.ClientToken, le.LeaseID); err != nil { + if err := exp.createIndexByToken(namespace.TestContext(), le); err != nil { t.Fatalf("error creating secondary index: %v", err) } exp.updatePending(le, le.Secret.LeaseTotal()) - indexEntry, err := exp.indexByToken(context.Background(), le.ClientToken, le.LeaseID) + indexEntry, err := exp.indexByToken(namespace.TestContext(), le) if err != nil { t.Fatalf("err: %v", err) } @@ -1354,14 +1420,14 @@ func TestExpiration_revokeEntry_token(t *testing.T) { t.Fatalf("err: should have found a secondary index entry") } - err = exp.revokeEntry(context.Background(), le) + err = exp.revokeEntry(namespace.TestContext(), le) if err != nil { t.Fatalf("err: %v", err) } time.Sleep(300 * time.Millisecond) - out, err := exp.tokenStore.Lookup(context.Background(), le.ClientToken) + out, err := exp.tokenStore.Lookup(namespace.TestContext(), le.ClientToken) if err != nil { t.Fatalf("err: %v", err) } @@ -1369,7 +1435,7 @@ func TestExpiration_revokeEntry_token(t *testing.T) { t.Fatalf("bad: %v", out) } - indexEntry, err = exp.indexByToken(context.Background(), le.ClientToken, le.LeaseID) + indexEntry, err = exp.indexByToken(namespace.TestContext(), le) if err != nil { t.Fatalf("err: %v", err) } @@ -1400,7 +1466,7 @@ func TestExpiration_renewEntry(t *testing.T) { if err != nil { t.Fatal(err) } - err = exp.router.Mount(noop, "foo/bar/", &MountEntry{Path: "foo/bar/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor"}, view) + err = exp.router.Mount(noop, "foo/bar/", &MountEntry{Path: "foo/bar/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.TestNamespace()}, view) if err != nil { t.Fatal(err) } @@ -1418,9 +1484,10 @@ func TestExpiration_renewEntry(t *testing.T) { }, IssueTime: time.Now(), ExpireTime: time.Now(), + namespace: namespace.TestNamespace(), } - resp, err := exp.renewEntry(context.Background(), le, 0) + resp, err := exp.renewEntry(namespace.TestContext(), le, 0) if err != nil { t.Fatalf("err: %v", err) } @@ -1465,7 +1532,7 @@ func TestExpiration_revokeEntry_rejected(t *testing.T) { if err != nil { t.Fatal(err) } - err = exp.router.Mount(noop, "foo/bar/", &MountEntry{Path: "foo/bar/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor"}, view) + err = exp.router.Mount(noop, "foo/bar/", &MountEntry{Path: "foo/bar/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.TestNamespace()}, view) if err != nil { t.Fatal(err) } @@ -1483,14 +1550,15 @@ func TestExpiration_revokeEntry_rejected(t *testing.T) { }, IssueTime: time.Now(), ExpireTime: time.Now().Add(time.Minute), + namespace: namespace.TestNamespace(), } - err = exp.persistEntry(context.Background(), le) + err = exp.persistEntry(namespace.TestContext(), le) if err != nil { t.Fatal(err) } - err = exp.LazyRevoke(context.Background(), le.LeaseID) + err = exp.LazyRevoke(namespace.TestContext(), le.LeaseID) if err != nil { t.Fatal(err) } @@ -1507,7 +1575,7 @@ func TestExpiration_revokeEntry_rejected(t *testing.T) { t.Fatal(err) } - err = core.setupExpiration() + err = core.setupExpiration(expireLeaseStrategyRevoke) if err != nil { t.Fatal(err) } @@ -1523,7 +1591,7 @@ func TestExpiration_revokeEntry_rejected(t *testing.T) { // Now let the revocation actually process time.Sleep(1 * time.Second) - le, err = exp.FetchLeaseTimes(context.Background(), le.LeaseID) + le, err = exp.FetchLeaseTimes(namespace.TestContext(), le.LeaseID) if err != nil { t.Fatal(err) } @@ -1551,7 +1619,7 @@ func TestExpiration_renewAuthEntry(t *testing.T) { if err != nil { t.Fatal(err) } - err = exp.router.Mount(noop, "auth/foo/", &MountEntry{Path: "auth/foo/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor"}, view) + err = exp.router.Mount(noop, "auth/foo/", &MountEntry{Path: "auth/foo/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.TestNamespace()}, view) if err != nil { t.Fatal(err) } @@ -1570,9 +1638,10 @@ func TestExpiration_renewAuthEntry(t *testing.T) { }, IssueTime: time.Now(), ExpireTime: time.Now().Add(time.Minute), + namespace: namespace.TestNamespace(), } - resp, err := exp.renewAuthEntry(context.Background(), &logical.Request{}, le, 0) + resp, err := exp.renewAuthEntry(namespace.TestContext(), &logical.Request{}, le, 0) if err != nil { t.Fatalf("err: %v", err) } @@ -1613,12 +1682,13 @@ func TestExpiration_PersistLoadDelete(t *testing.T) { IssueTime: lastTime, ExpireTime: lastTime, LastRenewalTime: lastTime, + namespace: namespace.TestNamespace(), } - if err := exp.persistEntry(context.Background(), le); err != nil { + if err := exp.persistEntry(namespace.TestContext(), le); err != nil { t.Fatalf("err: %v", err) } - out, err := exp.loadEntry(context.Background(), "foo/bar/1234") + out, err := exp.loadEntry(namespace.TestContext(), "foo/bar/1234") if err != nil { t.Fatalf("err: %v", err) } @@ -1634,12 +1704,12 @@ func TestExpiration_PersistLoadDelete(t *testing.T) { t.Fatalf("bad: expected:\n%#v\nactual:\n%#v", le, out) } - err = exp.deleteEntry(context.Background(), "foo/bar/1234") + err = exp.deleteEntry(namespace.TestContext(), le) if err != nil { t.Fatalf("err: %v", err) } - out, err = exp.loadEntry(context.Background(), "foo/bar/1234") + out, err = exp.loadEntry(namespace.TestContext(), "foo/bar/1234") if err != nil { t.Fatalf("err: %v", err) } @@ -1722,7 +1792,7 @@ func TestExpiration_RevokeForce(t *testing.T) { Accessor: "badrenewaccessor", } - err := core.mount(context.Background(), me) + err := core.mount(namespace.TestContext(), me) if err != nil { t.Fatal(err) } @@ -1733,7 +1803,7 @@ func TestExpiration_RevokeForce(t *testing.T) { ClientToken: root, } - resp, err := core.HandleRequest(context.Background(), req) + resp, err := core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatal(err) } @@ -1747,13 +1817,13 @@ func TestExpiration_RevokeForce(t *testing.T) { req.Operation = logical.UpdateOperation req.Path = "sys/revoke-prefix/badrenew/creds" - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err == nil { t.Fatal("expected error") } req.Path = "sys/revoke-force/badrenew/creds" - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("got error: %s", err) } @@ -1770,7 +1840,7 @@ func TestExpiration_RevokeForceSingle(t *testing.T) { Accessor: "badrenewaccessor", } - err := core.mount(context.Background(), me) + err := core.mount(namespace.TestContext(), me) if err != nil { t.Fatal(err) } @@ -1781,7 +1851,7 @@ func TestExpiration_RevokeForceSingle(t *testing.T) { ClientToken: root, } - resp, err := core.HandleRequest(context.Background(), req) + resp, err := core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatal(err) } @@ -1796,7 +1866,7 @@ func TestExpiration_RevokeForceSingle(t *testing.T) { req.Operation = logical.UpdateOperation req.Path = "sys/leases/lookup" req.Data = map[string]interface{}{"lease_id": leaseID} - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatal(err) } @@ -1809,20 +1879,20 @@ func TestExpiration_RevokeForceSingle(t *testing.T) { req.Path = "sys/revoke-prefix/" + leaseID - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err == nil { t.Fatal("expected error") } req.Path = "sys/revoke-force/" + leaseID - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("got error: %s", err) } req.Path = "sys/leases/lookup" req.Data = map[string]interface{}{"lease_id": leaseID} - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err == nil { t.Fatal("expected error") } @@ -1834,7 +1904,7 @@ func TestExpiration_RevokeForceSingle(t *testing.T) { func badRenewFactory(ctx context.Context, conf *logical.BackendConfig) (logical.Backend, error) { be := &framework.Backend{ Paths: []*framework.Path{ - &framework.Path{ + { Pattern: "creds", Callbacks: map[logical.Operation]framework.OperationFunc{ logical.ReadOperation: func(context.Context, *logical.Request, *framework.FieldData) (*logical.Response, error) { @@ -1862,7 +1932,7 @@ func badRenewFactory(ctx context.Context, conf *logical.BackendConfig) (logical. }, } - err := be.Setup(context.Background(), conf) + err := be.Setup(namespace.TestContext(), conf) if err != nil { return nil, err } diff --git a/vault/expiration_util.go b/vault/expiration_util.go new file mode 100644 index 0000000000..ab1454a25e --- /dev/null +++ b/vault/expiration_util.go @@ -0,0 +1,29 @@ +// +build !enterprise + +package vault + +import ( + "github.com/hashicorp/errwrap" + "github.com/hashicorp/vault/helper/namespace" + "github.com/hashicorp/vault/logical" +) + +func (m *ExpirationManager) leaseView(*namespace.Namespace) *BarrierView { + return m.idView +} + +func (m *ExpirationManager) tokenIndexView(*namespace.Namespace) *BarrierView { + return m.tokenView +} + +func (m *ExpirationManager) collectLeases() (map[*namespace.Namespace][]string, int, error) { + leaseCount := 0 + existing := make(map[*namespace.Namespace][]string) + keys, err := logical.CollectKeys(m.quitContext, m.leaseView(namespace.RootNamespace)) + if err != nil { + return nil, 0, errwrap.Wrapf("failed to scan for leases: {{err}}", err) + } + existing[namespace.RootNamespace] = keys + leaseCount += len(keys) + return existing, leaseCount, nil +} diff --git a/vault/generate_root.go b/vault/generate_root.go index 9a8750a0e9..1b7d6e50dc 100644 --- a/vault/generate_root.go +++ b/vault/generate_root.go @@ -20,6 +20,10 @@ var ( // GenerateStandardRootTokenStrategy is the strategy used to generate a // typical root token GenerateStandardRootTokenStrategy GenerateRootStrategy = generateStandardRootToken{} + + // GenerateDROperationTokenStrategy is the strategy used to generate a + // DR operational token + GenerateDROperationTokenStrategy GenerateRootStrategy = generateStandardRootToken{} ) // GenerateRootStrategy allows us to swap out the strategy we want to use to @@ -117,12 +121,8 @@ func (c *Core) GenerateRootInit(otp, pgpKey string, strategy GenerateRootStrateg var fingerprint string switch { case len(otp) > 0: - otpBytes, err := base64.StdEncoding.DecodeString(otp) - if err != nil { - return errwrap.Wrapf("error decoding base64 OTP value: {{err}}", err) - } - if otpBytes == nil || len(otpBytes) != 16 { - return fmt.Errorf("decoded OTP value is invalid or wrong length") + if len(otp) != TokenLength { + return fmt.Errorf("OTP string is wrong length") } case len(pgpKey) > 0: @@ -136,7 +136,7 @@ func (c *Core) GenerateRootInit(otp, pgpKey string, strategy GenerateRootStrateg fingerprint = fingerprints[0] default: - return fmt.Errorf("unreachable condition") + return fmt.Errorf("otp or pgp_key parameter must be provided") } c.stateLock.RLock() @@ -171,8 +171,14 @@ func (c *Core) GenerateRootInit(otp, pgpKey string, strategy GenerateRootStrateg } if c.logger.IsInfo() { - c.logger.Info("root generation initialized", "nonce", c.generateRootConfig.Nonce) + switch strategy.(type) { + case generateStandardRootToken: + c.logger.Info("root generation initialized", "nonce", c.generateRootConfig.Nonce) + default: + c.logger.Info("dr operation token generation initialized", "nonce", c.generateRootConfig.Nonce) + } } + return nil } @@ -284,45 +290,35 @@ func (c *Core) GenerateRootUpdate(ctx context.Context, key []byte, nonce string, } // Run the generate strategy - tokenUUID, cleanupFunc, err := strategy.generate(ctx, c) + token, cleanupFunc, err := strategy.generate(ctx, c) if err != nil { return nil, err } - uuidBytes, err := uuid.ParseUUID(tokenUUID) - if err != nil { - cleanupFunc() - c.logger.Error("error getting generated token bytes", "error", err) - return nil, err - } - if uuidBytes == nil { - cleanupFunc() - c.logger.Error("got nil parsed UUID bytes") - return nil, fmt.Errorf("got nil parsed UUID bytes") - } - var tokenBytes []byte + // Get the encoded value first so that if there is an error we don't create // the root token. switch { case len(c.generateRootConfig.OTP) > 0: // This function performs decoding checks so rather than decode the OTP, // just encode the value we're passing in. - tokenBytes, err = xor.XORBase64(c.generateRootConfig.OTP, base64.StdEncoding.EncodeToString(uuidBytes)) + tokenBytes, err = xor.XORBytes([]byte(c.generateRootConfig.OTP), []byte(token)) if err != nil { cleanupFunc() c.logger.Error("xor of root token failed", "error", err) return nil, err } + token = base64.RawStdEncoding.EncodeToString(tokenBytes) case len(c.generateRootConfig.PGPKey) > 0: - _, tokenBytesArr, err := pgpkeys.EncryptShares([][]byte{[]byte(tokenUUID)}, []string{c.generateRootConfig.PGPKey}) + _, tokenBytesArr, err := pgpkeys.EncryptShares([][]byte{[]byte(token)}, []string{c.generateRootConfig.PGPKey}) if err != nil { cleanupFunc() c.logger.Error("error encrypting new root token", "error", err) return nil, err } - tokenBytes = tokenBytesArr[0] + token = base64.StdEncoding.EncodeToString(tokenBytesArr[0]) default: cleanupFunc() @@ -332,12 +328,19 @@ func (c *Core) GenerateRootUpdate(ctx context.Context, key []byte, nonce string, results := &GenerateRootResult{ Progress: progress, Required: config.SecretThreshold, - EncodedToken: base64.StdEncoding.EncodeToString(tokenBytes), + EncodedToken: token, PGPFingerprint: c.generateRootConfig.PGPFingerprint, } - if c.logger.IsInfo() { - c.logger.Info("root generation finished", "nonce", c.generateRootConfig.Nonce) + switch strategy.(type) { + case generateStandardRootToken: + if c.logger.IsInfo() { + c.logger.Info("root generation finished", "nonce", c.generateRootConfig.Nonce) + } + default: + if c.logger.IsInfo() { + c.logger.Info("dr operation token generation finished", "nonce", c.generateRootConfig.Nonce) + } } c.generateRootProgress = nil diff --git a/vault/generate_root_test.go b/vault/generate_root_test.go index e674783e10..f9a90d0795 100644 --- a/vault/generate_root_test.go +++ b/vault/generate_root_test.go @@ -1,24 +1,22 @@ package vault import ( - "context" "encoding/base64" "testing" - "github.com/hashicorp/go-uuid" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/helper/pgpkeys" "github.com/hashicorp/vault/helper/xor" ) func TestCore_GenerateRoot_Lifecycle(t *testing.T) { - bc, rc := TestSealDefConfigs() - c, masterKeys, _, _ := TestCoreUnsealedWithConfigs(t, bc, rc) + c, masterKeys, _ := TestCoreUnsealed(t) testCore_GenerateRoot_Lifecycle_Common(t, c, masterKeys) } func testCore_GenerateRoot_Lifecycle_Common(t *testing.T, c *Core, keys [][]byte) { // Verify update not allowed - if _, err := c.GenerateRootUpdate(context.Background(), keys[0], "", GenerateStandardRootTokenStrategy); err == nil { + if _, err := c.GenerateRootUpdate(namespace.RootContext(nil), keys[0], "", GenerateStandardRootTokenStrategy); err == nil { t.Fatalf("no root generation in progress") } @@ -107,10 +105,7 @@ func testCore_GenerateRoot_Init_Common(t *testing.T, c *Core) { } func TestCore_GenerateRoot_InvalidMasterNonce(t *testing.T) { - bc, _ := TestSealDefConfigs() - bc.SecretShares = 3 - bc.SecretThreshold = 3 - c, masterKeys, _, _ := TestCoreUnsealedWithConfigs(t, bc, nil) + c, masterKeys, _ := TestCoreUnsealed(t) // Pass in master keys as they'll be invalid masterKeys[0][0]++ testCore_GenerateRoot_InvalidMasterNonce_Common(t, c, masterKeys) @@ -137,14 +132,14 @@ func testCore_GenerateRoot_InvalidMasterNonce_Common(t *testing.T, c *Core, keys } // Provide the nonce (invalid) - _, err = c.GenerateRootUpdate(context.Background(), keys[0], "abcd", GenerateStandardRootTokenStrategy) + _, err = c.GenerateRootUpdate(namespace.RootContext(nil), keys[0], "abcd", GenerateStandardRootTokenStrategy) if err == nil { t.Fatalf("expected error") } // Provide the master (invalid) for _, key := range keys { - _, err = c.GenerateRootUpdate(context.Background(), key, rgconf.Nonce, GenerateStandardRootTokenStrategy) + _, err = c.GenerateRootUpdate(namespace.RootContext(nil), key, rgconf.Nonce, GenerateStandardRootTokenStrategy) } if err == nil { t.Fatalf("expected error") @@ -152,9 +147,8 @@ func testCore_GenerateRoot_InvalidMasterNonce_Common(t *testing.T, c *Core, keys } func TestCore_GenerateRoot_Update_OTP(t *testing.T) { - bc, rc := TestSealDefConfigs() - c, masterKeys, _, _ := TestCoreUnsealedWithConfigs(t, bc, rc) - testCore_GenerateRoot_Update_OTP_Common(t, c, masterKeys[0:bc.SecretThreshold]) + c, masterKeys, _ := TestCoreUnsealed(t) + testCore_GenerateRoot_Update_OTP_Common(t, c, masterKeys) } func testCore_GenerateRoot_Update_OTP_Common(t *testing.T, c *Core, keys [][]byte) { @@ -182,10 +176,13 @@ func testCore_GenerateRoot_Update_OTP_Common(t *testing.T, c *Core, keys [][]byt // Provide the keys var result *GenerateRootResult for _, key := range keys { - result, err = c.GenerateRootUpdate(context.Background(), key, rkconf.Nonce, GenerateStandardRootTokenStrategy) + result, err = c.GenerateRootUpdate(namespace.RootContext(nil), key, rkconf.Nonce, GenerateStandardRootTokenStrategy) if err != nil { t.Fatalf("err: %v", err) } + if result.EncodedToken != "" { + break + } } if result == nil { t.Fatalf("Bad, result is nil") @@ -211,17 +208,20 @@ func testCore_GenerateRoot_Update_OTP_Common(t *testing.T, c *Core, keys [][]byt t.Fatalf("bad: %v", conf) } - tokenBytes, err := xor.XORBase64(encodedToken, otp) - if err != nil { - t.Fatal(err) - } - token, err := uuid.FormatUUID(tokenBytes) + tokenBytes, err := base64.StdEncoding.DecodeString(encodedToken) if err != nil { t.Fatal(err) } + tokenBytes, err = xor.XORBytes(tokenBytes, []byte(otp)) + if err != nil { + t.Fatal(err) + } + + token := string(tokenBytes) + // Ensure that the token is a root token - te, err := c.tokenStore.Lookup(context.Background(), token) + te, err := c.tokenStore.Lookup(namespace.RootContext(nil), token) if err != nil { t.Fatalf("err: %v", err) } @@ -235,9 +235,8 @@ func testCore_GenerateRoot_Update_OTP_Common(t *testing.T, c *Core, keys [][]byt } func TestCore_GenerateRoot_Update_PGP(t *testing.T) { - bc, rc := TestSealDefConfigs() - c, masterKeys, _, _ := TestCoreUnsealedWithConfigs(t, bc, rc) - testCore_GenerateRoot_Update_PGP_Common(t, c, masterKeys[0:bc.SecretThreshold]) + c, masterKeys, _ := TestCoreUnsealed(t) + testCore_GenerateRoot_Update_PGP_Common(t, c, masterKeys) } func testCore_GenerateRoot_Update_PGP_Common(t *testing.T, c *Core, keys [][]byte) { @@ -259,10 +258,13 @@ func testCore_GenerateRoot_Update_PGP_Common(t *testing.T, c *Core, keys [][]byt // Provide the keys var result *GenerateRootResult for _, key := range keys { - result, err = c.GenerateRootUpdate(context.Background(), key, rkconf.Nonce, GenerateStandardRootTokenStrategy) + result, err = c.GenerateRootUpdate(namespace.RootContext(nil), key, rkconf.Nonce, GenerateStandardRootTokenStrategy) if err != nil { t.Fatalf("err: %v", err) } + if result.EncodedToken != "" { + break + } } if result == nil { t.Fatalf("Bad, result is nil") @@ -299,7 +301,7 @@ func testCore_GenerateRoot_Update_PGP_Common(t *testing.T, c *Core, keys [][]byt token := ptBuf.String() // Ensure that the token is a root token - te, err := c.tokenStore.Lookup(context.Background(), token) + te, err := c.tokenStore.Lookup(namespace.RootContext(nil), token) if err != nil { t.Fatalf("err: %v", err) } diff --git a/vault/ha.go b/vault/ha.go index 29ffc3141d..7d4e5990da 100644 --- a/vault/ha.go +++ b/vault/ha.go @@ -210,7 +210,7 @@ func (c *Core) StepDown(httpCtx context.Context, req *logical.Request) (retErr e } }() - acl, te, entity, identityPolicies, err := c.fetchACLTokenEntryAndEntity(req) + acl, te, entity, identityPolicies, err := c.fetchACLTokenEntryAndEntity(ctx, req) if err != nil { if errwrap.ContainsType(err, new(TemplateError)) { c.logger.Warn("permission denied due to a templated policy being invalid or containing directives not satisfied by the requestor", "error", err) @@ -222,13 +222,14 @@ func (c *Core) StepDown(httpCtx context.Context, req *logical.Request) (retErr e // Audit-log the request before going any further auth := &logical.Auth{ - ClientToken: req.ClientToken, - Policies: identityPolicies, - IdentityPolicies: identityPolicies, + ClientToken: req.ClientToken, } if te != nil { + auth.IdentityPolicies = identityPolicies[te.NamespaceID] + delete(identityPolicies, te.NamespaceID) + auth.ExternalNamespacePolicies = identityPolicies auth.TokenPolicies = te.Policies - auth.Policies = append(te.Policies, identityPolicies...) + auth.Policies = append(te.Policies, identityPolicies[te.NamespaceID]...) auth.Metadata = te.Meta auth.DisplayName = te.DisplayName auth.EntityID = te.EntityID @@ -477,6 +478,7 @@ func (c *Core) waitForLeadership(newLeaderCh chan func(), manualStepDownCh, stop metrics.MeasureSince([]string{"core", "leadership_setup_failed"}, activeTime) continue } + } // Advertise as leader if err := c.advertiseLeader(activeCtx, uuid, leaderLostCh); err != nil { @@ -490,7 +492,7 @@ func (c *Core) waitForLeadership(newLeaderCh chan func(), manualStepDownCh, stop } // Attempt the post-unseal process - err = c.postUnseal(activeCtx, activeCtxCancel) + err = c.postUnseal(activeCtx, activeCtxCancel, standardUnsealStrategy{}) if err == nil { c.standby = false } diff --git a/vault/identity_lookup.go b/vault/identity_lookup.go index 20d7ea0b62..4a4b0eb017 100644 --- a/vault/identity_lookup.go +++ b/vault/identity_lookup.go @@ -145,7 +145,7 @@ func (i *IdentityStore) pathLookupEntityUpdate() framework.OperationFunc { } case name != "": - entity, err = i.MemDBEntityByName(name, false) + entity, err = i.MemDBEntityByName(ctx, name, false) if err != nil { return nil, err } @@ -185,7 +185,7 @@ func (i *IdentityStore) pathLookupEntityUpdate() framework.OperationFunc { return nil, nil } - return i.handleEntityReadCommon(entity) + return i.handleEntityReadCommon(ctx, entity) } } @@ -256,7 +256,7 @@ func (i *IdentityStore) pathLookupGroupUpdate() framework.OperationFunc { return nil, err } case name != "": - group, err = i.MemDBGroupByName(name, false) + group, err = i.MemDBGroupByName(ctx, name, false) if err != nil { return nil, err } @@ -295,7 +295,7 @@ func (i *IdentityStore) pathLookupGroupUpdate() framework.OperationFunc { return nil, nil } - return i.handleGroupReadCommon(group) + return i.handleGroupReadCommon(ctx, group) } } diff --git a/vault/identity_lookup_test.go b/vault/identity_lookup_test.go index 5a67b5b0da..a132f960c9 100644 --- a/vault/identity_lookup_test.go +++ b/vault/identity_lookup_test.go @@ -1,9 +1,9 @@ package vault import ( - "context" "testing" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/logical" ) @@ -11,13 +11,14 @@ func TestIdentityStore_Lookup_Entity(t *testing.T) { var err error var resp *logical.Response - i, accessor, _ := testIdentityStoreWithGithubAuth(t) + ctx := namespace.RootContext(nil) + i, accessor, _ := testIdentityStoreWithGithubAuth(ctx, t) entityReq := &logical.Request{ Path: "entity", Operation: logical.UpdateOperation, } - resp, err = i.HandleRequest(context.Background(), entityReq) + resp, err = i.HandleRequest(ctx, entityReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: err: %#v\nresp: %v", err, resp) } @@ -33,7 +34,7 @@ func TestIdentityStore_Lookup_Entity(t *testing.T) { }, } - resp, err = i.HandleRequest(context.Background(), aliasReq) + resp, err = i.HandleRequest(ctx, aliasReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: err: %#v\nresp: %v", err, resp) } @@ -51,7 +52,7 @@ func TestIdentityStore_Lookup_Entity(t *testing.T) { "id": entityID, }, } - resp, err = i.HandleRequest(context.Background(), lookupReq) + resp, err = i.HandleRequest(ctx, lookupReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: err: %#v\nresp: %v", err, resp) } @@ -64,7 +65,7 @@ func TestIdentityStore_Lookup_Entity(t *testing.T) { "name": entity.Name, } - resp, err = i.HandleRequest(context.Background(), lookupReq) + resp, err = i.HandleRequest(ctx, lookupReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: err: %#v\nresp: %v", err, resp) } @@ -77,7 +78,7 @@ func TestIdentityStore_Lookup_Entity(t *testing.T) { "alias_id": aliasID, } - resp, err = i.HandleRequest(context.Background(), lookupReq) + resp, err = i.HandleRequest(ctx, lookupReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: err: %#v\nresp: %v", err, resp) } @@ -91,7 +92,7 @@ func TestIdentityStore_Lookup_Entity(t *testing.T) { "alias_mount_accessor": accessor, } - resp, err = i.HandleRequest(context.Background(), lookupReq) + resp, err = i.HandleRequest(ctx, lookupReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: err: %#v\nresp: %v", err, resp) } @@ -106,7 +107,7 @@ func TestIdentityStore_Lookup_Entity(t *testing.T) { "name": entity.Name, } - resp, err = i.HandleRequest(context.Background(), lookupReq) + resp, err = i.HandleRequest(ctx, lookupReq) if err != nil { t.Fatal(err) } @@ -119,7 +120,7 @@ func TestIdentityStore_Lookup_Entity(t *testing.T) { "alias_name": "testaliasname", } - resp, err = i.HandleRequest(context.Background(), lookupReq) + resp, err = i.HandleRequest(ctx, lookupReq) if err != nil { t.Fatal(err) } @@ -132,7 +133,7 @@ func TestIdentityStore_Lookup_Entity(t *testing.T) { "alias_mount_accessor": accessor, } - resp, err = i.HandleRequest(context.Background(), lookupReq) + resp, err = i.HandleRequest(ctx, lookupReq) if err != nil { t.Fatal(err) } @@ -143,7 +144,7 @@ func TestIdentityStore_Lookup_Entity(t *testing.T) { // Don't supply any criteria lookupReq.Data = nil - resp, err = i.HandleRequest(context.Background(), lookupReq) + resp, err = i.HandleRequest(ctx, lookupReq) if err != nil { t.Fatal(err) } @@ -154,7 +155,7 @@ func TestIdentityStore_Lookup_Entity(t *testing.T) { // Delete the alias in the entity aliasReq.Path = "entity-alias/id/" + aliasID aliasReq.Operation = logical.DeleteOperation - resp, err = i.HandleRequest(context.Background(), aliasReq) + resp, err = i.HandleRequest(ctx, aliasReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: err: %#v\nresp: %v", err, resp) } @@ -163,7 +164,7 @@ func TestIdentityStore_Lookup_Entity(t *testing.T) { "alias_id": aliasID, } - resp, err = i.HandleRequest(context.Background(), lookupReq) + resp, err = i.HandleRequest(ctx, lookupReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: err: %#v\nresp: %v", err, resp) } @@ -176,13 +177,14 @@ func TestIdentityStore_Lookup_Group(t *testing.T) { var err error var resp *logical.Response - i, accessor, _ := testIdentityStoreWithGithubAuth(t) + ctx := namespace.RootContext(nil) + i, accessor, _ := testIdentityStoreWithGithubAuth(ctx, t) groupReq := &logical.Request{ Path: "group", Operation: logical.UpdateOperation, } - resp, err = i.HandleRequest(context.Background(), groupReq) + resp, err = i.HandleRequest(ctx, groupReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v\n err: %#v\n", resp, err) } @@ -197,7 +199,7 @@ func TestIdentityStore_Lookup_Group(t *testing.T) { }, } - resp, err = i.HandleRequest(context.Background(), lookupReq) + resp, err = i.HandleRequest(ctx, lookupReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v\n err: %#v\n", resp, err) } @@ -209,7 +211,7 @@ func TestIdentityStore_Lookup_Group(t *testing.T) { "name": groupName, } - resp, err = i.HandleRequest(context.Background(), lookupReq) + resp, err = i.HandleRequest(ctx, lookupReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v\n err: %#v\n", resp, err) } @@ -221,7 +223,7 @@ func TestIdentityStore_Lookup_Group(t *testing.T) { lookupReq.Data = map[string]interface{}{ "alias_id": "invalidaliasid", } - resp, err = i.HandleRequest(context.Background(), lookupReq) + resp, err = i.HandleRequest(ctx, lookupReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v\n err: %#v\n", resp, err) } @@ -232,7 +234,7 @@ func TestIdentityStore_Lookup_Group(t *testing.T) { groupReq.Data = map[string]interface{}{ "type": "external", } - resp, err = i.HandleRequest(context.Background(), groupReq) + resp, err = i.HandleRequest(ctx, groupReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v\n err: %#v\n", resp, err) } @@ -247,7 +249,7 @@ func TestIdentityStore_Lookup_Group(t *testing.T) { "mount_accessor": accessor, }, } - resp, err = i.HandleRequest(context.Background(), aliasReq) + resp, err = i.HandleRequest(ctx, aliasReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v\n err: %#v\n", resp, err) } @@ -257,7 +259,7 @@ func TestIdentityStore_Lookup_Group(t *testing.T) { "alias_id": aliasID, } - resp, err = i.HandleRequest(context.Background(), lookupReq) + resp, err = i.HandleRequest(ctx, lookupReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v\n err: %#v\n", resp, err) } @@ -270,7 +272,7 @@ func TestIdentityStore_Lookup_Group(t *testing.T) { "alias_mount_accessor": accessor, } - resp, err = i.HandleRequest(context.Background(), lookupReq) + resp, err = i.HandleRequest(ctx, lookupReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v\n err: %#v\n", resp, err) } @@ -284,7 +286,7 @@ func TestIdentityStore_Lookup_Group(t *testing.T) { "name": groupName, } - resp, err = i.HandleRequest(context.Background(), lookupReq) + resp, err = i.HandleRequest(ctx, lookupReq) if err != nil { t.Fatal(err) } @@ -297,7 +299,7 @@ func TestIdentityStore_Lookup_Group(t *testing.T) { "alias_name": "testgroupalias", } - resp, err = i.HandleRequest(context.Background(), lookupReq) + resp, err = i.HandleRequest(ctx, lookupReq) if err != nil { t.Fatal(err) } @@ -310,7 +312,7 @@ func TestIdentityStore_Lookup_Group(t *testing.T) { "alias_mount_accessor": accessor, } - resp, err = i.HandleRequest(context.Background(), lookupReq) + resp, err = i.HandleRequest(ctx, lookupReq) if err != nil { t.Fatal(err) } @@ -321,7 +323,7 @@ func TestIdentityStore_Lookup_Group(t *testing.T) { // Don't supply any criteria lookupReq.Data = nil - resp, err = i.HandleRequest(context.Background(), lookupReq) + resp, err = i.HandleRequest(ctx, lookupReq) if err != nil { t.Fatal(err) } diff --git a/vault/identity_store.go b/vault/identity_store.go index 318c48a886..c0c435614b 100644 --- a/vault/identity_store.go +++ b/vault/identity_store.go @@ -9,7 +9,9 @@ import ( "github.com/hashicorp/errwrap" log "github.com/hashicorp/go-hclog" memdb "github.com/hashicorp/go-memdb" + "github.com/hashicorp/vault/helper/consts" "github.com/hashicorp/vault/helper/identity" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/helper/storagepacker" "github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/logical/framework" @@ -19,6 +21,12 @@ const ( groupBucketsPrefix = "packer/group/buckets/" ) +var ( + sendGroupUpgrade = func(*IdentityStore, *identity.Group) (bool, error) { return false, nil } + parseExtraEntityFromBucket = func(context.Context, *IdentityStore, *identity.Entity) (bool, error) { return false, nil } + addExtraEntityDataToResponse = func(*identity.Entity, map[string]interface{}) {} +) + func (c *Core) IdentityStore() *IdentityStore { return c.identityStore } @@ -56,15 +64,8 @@ func NewIdentityStore(ctx context.Context, core *Core, config *logical.BackendCo iStore.Backend = &framework.Backend{ BackendType: logical.TypeLogical, - Paths: framework.PathAppend( - entityPaths(iStore), - aliasPaths(iStore), - groupAliasPaths(iStore), - groupPaths(iStore), - lookupPaths(iStore), - upgradePaths(iStore), - ), - Invalidate: iStore.Invalidate, + Paths: iStore.paths(), + Invalidate: iStore.Invalidate, } err = iStore.Setup(ctx, config) @@ -75,6 +76,17 @@ func NewIdentityStore(ctx context.Context, core *Core, config *logical.BackendCo return iStore, nil } +func (i *IdentityStore) paths() []*framework.Path { + return framework.PathAppend( + entityPaths(i), + aliasPaths(i), + groupAliasPaths(i), + groupPaths(i), + lookupPaths(i), + upgradePaths(i), + ) +} + // Invalidate is a callback wherein the backend is informed that the value at // the given key is updated. In identity store's case, it would be the entity // storage entries that get updated. The value needs to be read and MemDB needs @@ -82,6 +94,9 @@ func NewIdentityStore(ctx context.Context, core *Core, config *logical.BackendCo func (i *IdentityStore) Invalidate(ctx context.Context, key string) { i.logger.Debug("invalidate notification received", "key", key) + i.lock.Lock() + defer i.lock.Unlock() + switch { // Check if the key is a storage entry key for an entity bucket case strings.HasPrefix(key, storagepacker.StoragePackerBucketsPrefix): @@ -146,7 +161,7 @@ func (i *IdentityStore) Invalidate(ctx context.Context, key string) { } // Only update MemDB and don't touch the storage - err = i.upsertEntityInTxn(txn, entity, nil, false) + err = i.upsertEntityInTxn(ctx, txn, entity, nil, false) if err != nil { i.logger.Error("failed to update entity in MemDB", "error", err) return @@ -237,10 +252,81 @@ func (i *IdentityStore) parseEntityFromBucketItem(ctx context.Context, item *sto return nil, fmt.Errorf("nil item") } + persistNeeded := false + var entity identity.Entity err := ptypes.UnmarshalAny(item.Message, &entity) if err != nil { - return nil, errwrap.Wrapf("failed to decode entity from storage bucket item: {{err}}", err) + // If we encounter an error, it would mean that the format of the + // entity is an older one. Try decoding using the older format and if + // successful, upgrage the storage with the newer format. + var oldEntity identity.EntityStorageEntry + oldEntityErr := ptypes.UnmarshalAny(item.Message, &oldEntity) + if oldEntityErr != nil { + return nil, errwrap.Wrapf("failed to decode entity from storage bucket item: {{err}}", err) + } + + i.logger.Debug("upgrading the entity using patch introduced with vault 0.8.2.1", "entity_id", oldEntity.ID) + + // Successfully decoded entity using older format. Entity is stored + // with older format. Upgrade it. + entity.ID = oldEntity.ID + entity.Name = oldEntity.Name + entity.Metadata = oldEntity.Metadata + entity.CreationTime = oldEntity.CreationTime + entity.LastUpdateTime = oldEntity.LastUpdateTime + entity.MergedEntityIDs = oldEntity.MergedEntityIDs + entity.Policies = oldEntity.Policies + entity.BucketKeyHash = oldEntity.BucketKeyHash + entity.MFASecrets = oldEntity.MFASecrets + // Copy each alias individually since the format of aliases were + // also different + for _, oldAlias := range oldEntity.Personas { + var newAlias identity.Alias + newAlias.ID = oldAlias.ID + newAlias.Name = oldAlias.Name + newAlias.CanonicalID = oldAlias.EntityID + newAlias.MountType = oldAlias.MountType + newAlias.MountAccessor = oldAlias.MountAccessor + newAlias.MountPath = oldAlias.MountPath + newAlias.Metadata = oldAlias.Metadata + newAlias.CreationTime = oldAlias.CreationTime + newAlias.LastUpdateTime = oldAlias.LastUpdateTime + newAlias.MergedFromCanonicalIDs = oldAlias.MergedFromEntityIDs + entity.Aliases = append(entity.Aliases, &newAlias) + } + + persistNeeded = true + } + + pN, err := parseExtraEntityFromBucket(ctx, i, &entity) + if err != nil { + return nil, err + } + if pN { + persistNeeded = true + } + + if persistNeeded && !i.core.ReplicationState().HasState(consts.ReplicationPerformanceSecondary) { + entityAsAny, err := ptypes.MarshalAny(&entity) + if err != nil { + return nil, err + } + + item := &storagepacker.Item{ + ID: entity.ID, + Message: entityAsAny, + } + + // Store the entity with new format + err = i.entityPacker.PutItem(item) + if err != nil { + return nil, err + } + } + + if entity.NamespaceID == "" { + entity.NamespaceID = namespace.RootNamespaceID } return &entity, nil @@ -257,6 +343,10 @@ func (i *IdentityStore) parseGroupFromBucketItem(item *storagepacker.Item) (*ide return nil, errwrap.Wrapf("failed to decode group from storage bucket item: {{err}}", err) } + if group.NamespaceID == "" { + group.NamespaceID = namespace.RootNamespaceID + } + return &group, nil } @@ -305,7 +395,7 @@ func (i *IdentityStore) entityByAliasFactorsInTxn(txn *memdb.Txn, mountAccessor, // CreateOrFetchEntity creates a new entity. This is used by core to // associate each login attempt by an alias to a unified entity in Vault. -func (i *IdentityStore) CreateOrFetchEntity(alias *logical.Alias) (*identity.Entity, error) { +func (i *IdentityStore) CreateOrFetchEntity(ctx context.Context, alias *logical.Alias) (*identity.Entity, error) { var entity *identity.Entity var err error @@ -355,9 +445,8 @@ func (i *IdentityStore) CreateOrFetchEntity(alias *logical.Alias) (*identity.Ent return entity, nil } - entity = &identity.Entity{} - - err = i.sanitizeEntity(entity) + entity = new(identity.Entity) + err = i.sanitizeEntity(ctx, entity) if err != nil { return nil, err } @@ -372,7 +461,7 @@ func (i *IdentityStore) CreateOrFetchEntity(alias *logical.Alias) (*identity.Ent MountType: mountValidationResp.MountType, } - err = i.sanitizeAlias(newAlias) + err = i.sanitizeAlias(ctx, newAlias) if err != nil { return nil, err } @@ -385,7 +474,7 @@ func (i *IdentityStore) CreateOrFetchEntity(alias *logical.Alias) (*identity.Ent } // Update MemDB and persist entity object - err = i.upsertEntityInTxn(txn, entity, nil, true) + err = i.upsertEntityInTxn(ctx, txn, entity, nil, true) if err != nil { return nil, err } diff --git a/vault/identity_store_aliases.go b/vault/identity_store_aliases.go index d4debc4c0c..43b1ddbb06 100644 --- a/vault/identity_store_aliases.go +++ b/vault/identity_store_aliases.go @@ -6,9 +6,8 @@ import ( "strings" "github.com/golang/protobuf/ptypes" - "github.com/hashicorp/errwrap" - memdb "github.com/hashicorp/go-memdb" "github.com/hashicorp/vault/helper/identity" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/helper/storagepacker" "github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/logical/framework" @@ -237,7 +236,7 @@ func (i *IdentityStore) handleAliasUpdateCommon() framework.OperationFunc { // ID creation and other validations; This is more useful for new entities // and may not perform anything for the existing entities. Placing the // check here to make the flow common for both new and existing entities. - err = i.sanitizeEntity(entity) + err = i.sanitizeEntity(ctx, entity) if err != nil { return nil, err } @@ -251,7 +250,7 @@ func (i *IdentityStore) handleAliasUpdateCommon() framework.OperationFunc { alias.CanonicalID = entity.ID // ID creation and other validations - err = i.sanitizeAlias(alias) + err = i.sanitizeAlias(ctx, alias) if err != nil { return nil, err } @@ -260,7 +259,7 @@ func (i *IdentityStore) handleAliasUpdateCommon() framework.OperationFunc { // aliases in storage. If the alias is being transferred over from // one entity to another, previous entity needs to get refreshed in MemDB // and persisted in storage as well. - if err := i.upsertEntity(entity, previousEntity, true); err != nil { + if err := i.upsertEntity(ctx, entity, previousEntity, true); err != nil { return nil, err } @@ -288,15 +287,23 @@ func (i *IdentityStore) pathAliasIDRead() framework.OperationFunc { return nil, err } - return i.handleAliasReadCommon(alias) + return i.handleAliasReadCommon(ctx, alias) } } -func (i *IdentityStore) handleAliasReadCommon(alias *identity.Alias) (*logical.Response, error) { +func (i *IdentityStore) handleAliasReadCommon(ctx context.Context, alias *identity.Alias) (*logical.Response, error) { if alias == nil { return nil, nil } + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } + if ns.ID != alias.NamespaceID { + return nil, nil + } + respData := map[string]interface{}{} respData["id"] = alias.ID respData["canonical_id"] = alias.CanonicalID @@ -345,6 +352,14 @@ func (i *IdentityStore) pathAliasIDDelete() framework.OperationFunc { return nil, nil } + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } + if ns.ID != alias.NamespaceID { + return nil, logical.ErrUnsupportedPath + } + // Fetch the associated entity entity, err := i.MemDBEntityByAliasIDInTxn(txn, alias.ID, true) if err != nil { @@ -399,53 +414,7 @@ func (i *IdentityStore) pathAliasIDDelete() framework.OperationFunc { // store func (i *IdentityStore) pathAliasIDList() framework.OperationFunc { return func(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { - ws := memdb.NewWatchSet() - iter, err := i.MemDBAliases(ws, false) - if err != nil { - return nil, errwrap.Wrapf("failed to fetch iterator for aliases in memdb: {{err}}", err) - } - - var aliasIDs []string - aliasInfo := map[string]interface{}{} - - type mountInfo struct { - MountType string - MountPath string - } - mountAccessorMap := map[string]mountInfo{} - - for { - raw := iter.Next() - if raw == nil { - break - } - alias := raw.(*identity.Alias) - aliasIDs = append(aliasIDs, alias.ID) - aliasInfoEntry := map[string]interface{}{ - "name": alias.Name, - "canonical_id": alias.CanonicalID, - "mount_accessor": alias.MountAccessor, - } - - mi, ok := mountAccessorMap[alias.MountAccessor] - if ok { - aliasInfoEntry["mount_type"] = mi.MountType - aliasInfoEntry["mount_path"] = mi.MountPath - } else { - mi = mountInfo{} - if mountValidationResp := i.core.router.validateMountByAccessor(alias.MountAccessor); mountValidationResp != nil { - mi.MountType = mountValidationResp.MountType - mi.MountPath = mountValidationResp.MountPath - aliasInfoEntry["mount_type"] = mi.MountType - aliasInfoEntry["mount_path"] = mi.MountPath - } - mountAccessorMap[alias.MountAccessor] = mi - } - - aliasInfo[alias.ID] = aliasInfoEntry - } - - return logical.ListResponseWithInfo(aliasIDs, aliasInfo), nil + return i.handleAliasListCommon(ctx, false) } } diff --git a/vault/identity_store_aliases_test.go b/vault/identity_store_aliases_test.go index e9b6012be9..1e1494eda0 100644 --- a/vault/identity_store_aliases_test.go +++ b/vault/identity_store_aliases_test.go @@ -1,11 +1,11 @@ package vault import ( - "context" "reflect" "testing" "github.com/hashicorp/vault/helper/identity" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/logical" ) @@ -14,7 +14,9 @@ import ( func TestIdentityStore_AliasSameAliasNames(t *testing.T) { var err error var resp *logical.Response - is, githubAccessor, _ := testIdentityStoreWithGithubAuth(t) + + ctx := namespace.RootContext(nil) + is, githubAccessor, _ := testIdentityStoreWithGithubAuth(ctx, t) aliasData := map[string]interface{}{ "name": "testaliasname", @@ -28,13 +30,13 @@ func TestIdentityStore_AliasSameAliasNames(t *testing.T) { } // Register an alias - resp, err = is.HandleRequest(context.Background(), aliasReq) + resp, err = is.HandleRequest(ctx, aliasReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } // Register another alias with same name - resp, err = is.HandleRequest(context.Background(), aliasReq) + resp, err = is.HandleRequest(ctx, aliasReq) if err != nil { t.Fatal(err) } @@ -46,7 +48,8 @@ func TestIdentityStore_AliasSameAliasNames(t *testing.T) { func TestIdentityStore_MemDBAliasIndexes(t *testing.T) { var err error - is, githubAccessor, _ := testIdentityStoreWithGithubAuth(t) + ctx := namespace.RootContext(nil) + is, githubAccessor, _ := testIdentityStoreWithGithubAuth(ctx, t) if is == nil { t.Fatal("failed to create test identity store") } @@ -147,7 +150,8 @@ func TestIdentityStore_AliasRegister(t *testing.T) { var err error var resp *logical.Response - is, githubAccessor, _ := testIdentityStoreWithGithubAuth(t) + ctx := namespace.RootContext(nil) + is, githubAccessor, _ := testIdentityStoreWithGithubAuth(ctx, t) if is == nil { t.Fatal("failed to create test alias store") @@ -166,7 +170,7 @@ func TestIdentityStore_AliasRegister(t *testing.T) { } // Register the alias - resp, err = is.HandleRequest(context.Background(), aliasReq) + resp, err = is.HandleRequest(ctx, aliasReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } @@ -195,7 +199,8 @@ func TestIdentityStore_AliasRegister(t *testing.T) { func TestIdentityStore_AliasUpdate(t *testing.T) { var err error var resp *logical.Response - is, githubAccessor, _ := testIdentityStoreWithGithubAuth(t) + ctx := namespace.RootContext(nil) + is, githubAccessor, _ := testIdentityStoreWithGithubAuth(ctx, t) aliasData := map[string]interface{}{ "name": "testaliasname", @@ -209,7 +214,7 @@ func TestIdentityStore_AliasUpdate(t *testing.T) { } // This will create an alias and a corresponding entity - resp, err = is.HandleRequest(context.Background(), aliasReq) + resp, err = is.HandleRequest(ctx, aliasReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } @@ -222,13 +227,13 @@ func TestIdentityStore_AliasUpdate(t *testing.T) { aliasReq.Data = updateData aliasReq.Path = "entity-alias/id/" + aliasID - resp, err = is.HandleRequest(context.Background(), aliasReq) + resp, err = is.HandleRequest(ctx, aliasReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } aliasReq.Operation = logical.ReadOperation - resp, err = is.HandleRequest(context.Background(), aliasReq) + resp, err = is.HandleRequest(ctx, aliasReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } @@ -241,7 +246,8 @@ func TestIdentityStore_AliasUpdate(t *testing.T) { func TestIdentityStore_AliasUpdate_ByID(t *testing.T) { var err error var resp *logical.Response - is, githubAccessor, _ := testIdentityStoreWithGithubAuth(t) + ctx := namespace.RootContext(nil) + is, githubAccessor, _ := testIdentityStoreWithGithubAuth(ctx, t) updateData := map[string]interface{}{ "name": "updatedaliasname", @@ -255,7 +261,7 @@ func TestIdentityStore_AliasUpdate_ByID(t *testing.T) { } // Try to update an non-existent alias - resp, err = is.HandleRequest(context.Background(), updateReq) + resp, err = is.HandleRequest(ctx, updateReq) if err != nil { t.Fatal(err) } @@ -274,7 +280,7 @@ func TestIdentityStore_AliasUpdate_ByID(t *testing.T) { Data: registerData, } - resp, err = is.HandleRequest(context.Background(), registerReq) + resp, err = is.HandleRequest(ctx, registerReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } @@ -289,7 +295,7 @@ func TestIdentityStore_AliasUpdate_ByID(t *testing.T) { } updateReq.Path = "entity-alias/id/" + id - resp, err = is.HandleRequest(context.Background(), updateReq) + resp, err = is.HandleRequest(ctx, updateReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } @@ -298,7 +304,7 @@ func TestIdentityStore_AliasUpdate_ByID(t *testing.T) { Operation: logical.ReadOperation, Path: updateReq.Path, } - resp, err = is.HandleRequest(context.Background(), readReq) + resp, err = is.HandleRequest(ctx, readReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } @@ -309,7 +315,7 @@ func TestIdentityStore_AliasUpdate_ByID(t *testing.T) { delete(registerReq.Data, "name") - resp, err = is.HandleRequest(context.Background(), registerReq) + resp, err = is.HandleRequest(ctx, registerReq) if err != nil { t.Fatal(err) } @@ -320,7 +326,7 @@ func TestIdentityStore_AliasUpdate_ByID(t *testing.T) { registerReq.Data["name"] = "testaliasname" delete(registerReq.Data, "mount_accessor") - resp, err = is.HandleRequest(context.Background(), registerReq) + resp, err = is.HandleRequest(ctx, registerReq) if err != nil { t.Fatal(err) } @@ -333,7 +339,8 @@ func TestIdentityStore_AliasReadDelete(t *testing.T) { var err error var resp *logical.Response - is, githubAccessor, _ := testIdentityStoreWithGithubAuth(t) + ctx := namespace.RootContext(nil) + is, githubAccessor, _ := testIdentityStoreWithGithubAuth(ctx, t) registerData := map[string]interface{}{ "name": "testaliasname", @@ -347,7 +354,7 @@ func TestIdentityStore_AliasReadDelete(t *testing.T) { Data: registerData, } - resp, err = is.HandleRequest(context.Background(), registerReq) + resp, err = is.HandleRequest(ctx, registerReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } @@ -366,7 +373,7 @@ func TestIdentityStore_AliasReadDelete(t *testing.T) { Operation: logical.ReadOperation, Path: "entity-alias/id/" + id, } - resp, err = is.HandleRequest(context.Background(), aliasReq) + resp, err = is.HandleRequest(ctx, aliasReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } @@ -379,13 +386,13 @@ func TestIdentityStore_AliasReadDelete(t *testing.T) { } aliasReq.Operation = logical.DeleteOperation - resp, err = is.HandleRequest(context.Background(), aliasReq) + resp, err = is.HandleRequest(ctx, aliasReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } aliasReq.Operation = logical.ReadOperation - resp, err = is.HandleRequest(context.Background(), aliasReq) + resp, err = is.HandleRequest(ctx, aliasReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } diff --git a/vault/identity_store_entities.go b/vault/identity_store_entities.go index f75a3b27ba..2711db0c8a 100644 --- a/vault/identity_store_entities.go +++ b/vault/identity_store_entities.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/errwrap" memdb "github.com/hashicorp/go-memdb" "github.com/hashicorp/vault/helper/identity" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/helper/storagepacker" "github.com/hashicorp/vault/helper/strutil" "github.com/hashicorp/vault/logical" @@ -154,7 +155,7 @@ func (i *IdentityStore) pathEntityMergeID() framework.OperationFunc { return nil, err } - userErr, intErr := i.mergeEntity(txn, toEntity, fromEntityIDs, force, true, false) + userErr, intErr := i.mergeEntity(ctx, txn, toEntity, fromEntityIDs, force, true, false) if userErr != nil { return logical.ErrorResponse(userErr.Error()), nil } @@ -176,7 +177,7 @@ func (i *IdentityStore) handleEntityUpdateCommon() framework.OperationFunc { i.lock.Lock() defer i.lock.Unlock() - var entity *identity.Entity + entity := new(identity.Entity) var err error entityID := d.Get("id").(string) @@ -193,17 +194,17 @@ func (i *IdentityStore) handleEntityUpdateCommon() framework.OperationFunc { // Get the name entityName := d.Get("name").(string) if entityName != "" { - entityByName, err := i.MemDBEntityByName(entityName, false) + entityByName, err := i.MemDBEntityByName(ctx, entityName, false) if err != nil { return nil, err } switch { case entityByName == nil: // Not found, safe to use this name with an existing or new entity - case entity == nil: + case entity.ID == "": // We found an entity by name, but we don't currently allow // updating based on name, only ID, so return an error - return logical.ErrorResponse("entity name is already in use"), nil + return logical.ErrorResponse("updating entity by name is not currently supported"), nil case entity.ID == entityByName.ID: // Same exact entity, carry on (this is basically a noop then) default: @@ -211,11 +212,6 @@ func (i *IdentityStore) handleEntityUpdateCommon() framework.OperationFunc { } } - // Entity will be nil when a new entity is being registered; create a new - // struct in that case. - if entity == nil { - entity = &identity.Entity{} - } if entityName != "" { entity.Name = entityName } @@ -244,7 +240,7 @@ func (i *IdentityStore) handleEntityUpdateCommon() framework.OperationFunc { entity.Metadata = metadata.(map[string]string) } // ID creation and some validations - err = i.sanitizeEntity(entity) + err = i.sanitizeEntity(ctx, entity) if err != nil { return nil, err } @@ -263,7 +259,7 @@ func (i *IdentityStore) handleEntityUpdateCommon() framework.OperationFunc { // Update MemDB and persist entity object. New entities have not been // looked up yet so we need to take the lock on the entity on upsert - if err := i.upsertEntity(entity, nil, true); err != nil { + if err := i.upsertEntity(ctx, entity, nil, true); err != nil { return nil, err } @@ -291,11 +287,19 @@ func (i *IdentityStore) pathEntityIDRead() framework.OperationFunc { return nil, nil } - return i.handleEntityReadCommon(entity) + return i.handleEntityReadCommon(ctx, entity) } } -func (i *IdentityStore) handleEntityReadCommon(entity *identity.Entity) (*logical.Response, error) { +func (i *IdentityStore) handleEntityReadCommon(ctx context.Context, entity *identity.Entity) (*logical.Response, error) { + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } + if ns.ID != entity.NamespaceID { + return nil, nil + } + respData := map[string]interface{}{} respData["id"] = entity.ID respData["name"] = entity.Name @@ -333,6 +337,8 @@ func (i *IdentityStore) handleEntityReadCommon(entity *identity.Entity) (*logica // formats respData["aliases"] = aliasesToReturn + addExtraEntityDataToResponse(entity, respData) + // Fetch the groups this entity belongs to and return their identifiers groups, inheritedGroups, err := i.groupsByEntityID(entity.ID) if err != nil { @@ -378,12 +384,19 @@ func (i *IdentityStore) pathEntityIDDelete() framework.OperationFunc { if err != nil { return nil, err } - // If there is no entity for the ID, do nothing if entity == nil { return nil, nil } + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } + if entity.NamespaceID != ns.ID { + return nil, logical.ErrUnsupportedPath + } + // Delete all the aliases in the entity. This function will also remove // the corresponding alias indexes too. err = i.deleteAliasesInEntityInTxn(txn, entity, entity.Aliases) @@ -414,11 +427,16 @@ func (i *IdentityStore) pathEntityIDDelete() framework.OperationFunc { // store func (i *IdentityStore) pathEntityIDList() framework.OperationFunc { return func(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } + ws := memdb.NewWatchSet() txn := i.db.Txn(false) - iter, err := txn.Get(entitiesTable, "id") + iter, err := txn.Get(entitiesTable, "namespace_id", ns.ID) if err != nil { return nil, errwrap.Wrapf("failed to fetch iterator for entities in memdb: {{err}}", err) } @@ -479,7 +497,7 @@ func (i *IdentityStore) pathEntityIDList() framework.OperationFunc { } } -func (i *IdentityStore) mergeEntity(txn *memdb.Txn, toEntity *identity.Entity, fromEntityIDs []string, force, grabLock, mergePolicies bool) (error, error) { +func (i *IdentityStore) mergeEntity(ctx context.Context, txn *memdb.Txn, toEntity *identity.Entity, fromEntityIDs []string, force, grabLock, mergePolicies bool) (error, error) { if grabLock { i.lock.Lock() defer i.lock.Unlock() @@ -489,6 +507,43 @@ func (i *IdentityStore) mergeEntity(txn *memdb.Txn, toEntity *identity.Entity, f return errors.New("entity id to merge to is invalid"), nil } + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } + if toEntity.NamespaceID != ns.ID { + return errors.New("entity id to merge into does not belong to the request's namespace"), nil + } + + // Merge the MFA secrets + for _, fromEntityID := range fromEntityIDs { + if fromEntityID == toEntity.ID { + return errors.New("to_entity_id should not be present in from_entity_ids"), nil + } + + fromEntity, err := i.MemDBEntityByID(fromEntityID, false) + if err != nil { + return nil, err + } + + if fromEntity == nil { + return errors.New("entity id to merge from is invalid"), nil + } + + if fromEntity.NamespaceID != toEntity.NamespaceID { + return errors.New("entity id to merge from does not belong to this namespace"), nil + } + + for configID, configSecret := range fromEntity.MFASecrets { + _, ok := toEntity.MFASecrets[configID] + if ok && !force { + return nil, fmt.Errorf("conflicting MFA config ID %q in entity ID %q", configID, fromEntity.ID) + } else { + toEntity.MFASecrets[configID] = configSecret + } + } + } + for _, fromEntityID := range fromEntityIDs { if fromEntityID == toEntity.ID { return errors.New("to_entity_id should not be present in from_entity_ids"), nil @@ -503,6 +558,10 @@ func (i *IdentityStore) mergeEntity(txn *memdb.Txn, toEntity *identity.Entity, f return errors.New("entity id to merge from is invalid"), nil } + if fromEntity.NamespaceID != toEntity.NamespaceID { + return errors.New("entity id to merge from does not belong to this namespace"), nil + } + for _, alias := range fromEntity.Aliases { // Set the desired canonical ID alias.CanonicalID = toEntity.ID @@ -546,7 +605,7 @@ func (i *IdentityStore) mergeEntity(txn *memdb.Txn, toEntity *identity.Entity, f } // Update MemDB with changes to the entity we are merging to - err := i.MemDBUpsertEntityInTxn(txn, toEntity) + err = i.MemDBUpsertEntityInTxn(txn, toEntity) if err != nil { return nil, err } diff --git a/vault/identity_store_entities_test.go b/vault/identity_store_entities_test.go index 67517a9f97..12f1336a06 100644 --- a/vault/identity_store_entities_test.go +++ b/vault/identity_store_entities_test.go @@ -10,6 +10,7 @@ import ( uuid "github.com/hashicorp/go-uuid" credGithub "github.com/hashicorp/vault/builtin/credential/github" "github.com/hashicorp/vault/helper/identity" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/logical" ) @@ -17,14 +18,15 @@ func TestIdentityStore_EntityReadGroupIDs(t *testing.T) { var err error var resp *logical.Response - i, _, _ := testIdentityStoreWithGithubAuth(t) + ctx := namespace.RootContext(nil) + i, _, _ := testIdentityStoreWithGithubAuth(ctx, t) entityReq := &logical.Request{ Path: "entity", Operation: logical.UpdateOperation, } - resp, err = i.HandleRequest(context.Background(), entityReq) + resp, err = i.HandleRequest(ctx, entityReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v\nerr: %v", resp, err) } @@ -41,7 +43,7 @@ func TestIdentityStore_EntityReadGroupIDs(t *testing.T) { }, } - resp, err = i.HandleRequest(context.Background(), groupReq) + resp, err = i.HandleRequest(ctx, groupReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v\nerr: %v", resp, err) } @@ -53,7 +55,7 @@ func TestIdentityStore_EntityReadGroupIDs(t *testing.T) { groupReq.Data = map[string]interface{}{ "member_group_ids": []string{groupID}, } - resp, err = i.HandleRequest(context.Background(), groupReq) + resp, err = i.HandleRequest(ctx, groupReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v\nerr: %v", resp, err) } @@ -69,7 +71,7 @@ func TestIdentityStore_EntityReadGroupIDs(t *testing.T) { }, } - resp, err = i.HandleRequest(context.Background(), lookupReq) + resp, err = i.HandleRequest(ctx, lookupReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v\nerr: %v", resp, err) } @@ -97,7 +99,8 @@ func TestIdentityStore_EntityCreateUpdate(t *testing.T) { var err error var resp *logical.Response - is, _, _ := testIdentityStoreWithGithubAuth(t) + ctx := namespace.RootContext(nil) + is, _, _ := testIdentityStoreWithGithubAuth(ctx, t) entityData := map[string]interface{}{ "name": "testentityname", @@ -112,7 +115,7 @@ func TestIdentityStore_EntityCreateUpdate(t *testing.T) { } // Create the entity - resp, err = is.HandleRequest(context.Background(), entityReq) + resp, err = is.HandleRequest(ctx, entityReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } @@ -128,7 +131,7 @@ func TestIdentityStore_EntityCreateUpdate(t *testing.T) { entityReq.Data = updateData // Update the entity - resp, err = is.HandleRequest(context.Background(), entityReq) + resp, err = is.HandleRequest(ctx, entityReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } @@ -137,7 +140,7 @@ func TestIdentityStore_EntityCreateUpdate(t *testing.T) { entityReq.Operation = logical.ReadOperation // Read the entity - resp, err = is.HandleRequest(context.Background(), entityReq) + resp, err = is.HandleRequest(ctx, entityReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } @@ -190,7 +193,8 @@ func TestIdentityStore_CloneImmutability(t *testing.T) { func TestIdentityStore_MemDBImmutability(t *testing.T) { var err error - is, githubAccessor, _ := testIdentityStoreWithGithubAuth(t) + ctx := namespace.RootContext(nil) + is, githubAccessor, _ := testIdentityStoreWithGithubAuth(ctx, t) validateMountResp := is.core.router.validateMountByAccessor(githubAccessor) if validateMountResp == nil { @@ -254,7 +258,8 @@ func TestIdentityStore_ListEntities(t *testing.T) { var err error var resp *logical.Response - is, _, _ := testIdentityStoreWithGithubAuth(t) + ctx := namespace.RootContext(nil) + is, _, _ := testIdentityStoreWithGithubAuth(ctx, t) entityReq := &logical.Request{ Operation: logical.UpdateOperation, @@ -263,7 +268,7 @@ func TestIdentityStore_ListEntities(t *testing.T) { expected := []string{} for i := 0; i < 10; i++ { - resp, err = is.HandleRequest(context.Background(), entityReq) + resp, err = is.HandleRequest(ctx, entityReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } @@ -275,7 +280,7 @@ func TestIdentityStore_ListEntities(t *testing.T) { Path: "entity/id", } - resp, err = is.HandleRequest(context.Background(), listReq) + resp, err = is.HandleRequest(ctx, listReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } @@ -315,6 +320,7 @@ func TestIdentityStore_LoadingEntities(t *testing.T) { Path: "github/", Type: "github", Description: "github auth", + namespace: namespace.RootNamespace, } // Mount UUID for github auth @@ -350,7 +356,7 @@ func TestIdentityStore_LoadingEntities(t *testing.T) { } // Identity store will be mounted by now, just fetch it from router - identitystore := c.router.MatchingBackend("identity/") + identitystore := c.router.MatchingBackend(namespace.TestContext(), "identity/") if identitystore == nil { t.Fatalf("failed to fetch identity store from router") } @@ -369,8 +375,10 @@ func TestIdentityStore_LoadingEntities(t *testing.T) { Data: registerData, } + ctx := namespace.RootContext(nil) + // Register the entity - resp, err = is.HandleRequest(context.Background(), registerReq) + resp, err = is.HandleRequest(ctx, registerReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } @@ -383,7 +391,7 @@ func TestIdentityStore_LoadingEntities(t *testing.T) { } // Ensure that entity is created - resp, err = is.HandleRequest(context.Background(), readReq) + resp, err = is.HandleRequest(ctx, readReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } @@ -413,7 +421,7 @@ func TestIdentityStore_LoadingEntities(t *testing.T) { } // Check if the entity is restored - resp, err = is.HandleRequest(context.Background(), readReq) + resp, err = is.HandleRequest(ctx, readReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } @@ -426,7 +434,8 @@ func TestIdentityStore_LoadingEntities(t *testing.T) { func TestIdentityStore_MemDBEntityIndexes(t *testing.T) { var err error - is, githubAccessor, _ := testIdentityStoreWithGithubAuth(t) + ctx := namespace.RootContext(nil) + is, githubAccessor, _ := testIdentityStoreWithGithubAuth(ctx, t) validateMountResp := is.core.router.validateMountByAccessor(githubAccessor) if validateMountResp == nil { @@ -490,7 +499,7 @@ func TestIdentityStore_MemDBEntityIndexes(t *testing.T) { } // Fetch the entity using its name - entityFetched, err = is.MemDBEntityByName(entity.Name, false) + entityFetched, err = is.MemDBEntityByName(namespace.RootContext(nil), entity.Name, false) if err != nil { t.Fatal(err) } @@ -523,7 +532,7 @@ func TestIdentityStore_MemDBEntityIndexes(t *testing.T) { t.Fatalf("bad: entity; expected: nil, actual: %#v\n", entityFetched) } - entityFetched, err = is.MemDBEntityByName(entity.Name, false) + entityFetched, err = is.MemDBEntityByName(namespace.RootContext(nil), entity.Name, false) if err != nil { t.Fatal(err) } @@ -531,6 +540,7 @@ func TestIdentityStore_MemDBEntityIndexes(t *testing.T) { if entityFetched != nil { t.Fatalf("bad: entity; expected: nil, actual: %#v\n", entityFetched) } + } // This test is required because MemDB does not take care of ensuring @@ -539,7 +549,8 @@ func TestIdentityStore_MemDBEntityIndexes(t *testing.T) { func TestIdentityStore_EntitySameEntityNames(t *testing.T) { var err error var resp *logical.Response - is, _, _ := testIdentityStoreWithGithubAuth(t) + ctx := namespace.RootContext(nil) + is, _, _ := testIdentityStoreWithGithubAuth(ctx, t) registerData := map[string]interface{}{ "name": "testentityname", @@ -552,13 +563,13 @@ func TestIdentityStore_EntitySameEntityNames(t *testing.T) { } // Register an entity - resp, err = is.HandleRequest(context.Background(), registerReq) + resp, err = is.HandleRequest(ctx, registerReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } // Register another entity with same name - resp, err = is.HandleRequest(context.Background(), registerReq) + resp, err = is.HandleRequest(ctx, registerReq) if err != nil { t.Fatal(err) } @@ -571,7 +582,8 @@ func TestIdentityStore_EntityCRUD(t *testing.T) { var err error var resp *logical.Response - is, _, _ := testIdentityStoreWithGithubAuth(t) + ctx := namespace.RootContext(nil) + is, _, _ := testIdentityStoreWithGithubAuth(ctx, t) registerData := map[string]interface{}{ "name": "testentityname", @@ -586,7 +598,7 @@ func TestIdentityStore_EntityCRUD(t *testing.T) { } // Register the entity - resp, err = is.HandleRequest(context.Background(), registerReq) + resp, err = is.HandleRequest(ctx, registerReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } @@ -605,7 +617,7 @@ func TestIdentityStore_EntityCRUD(t *testing.T) { Operation: logical.ReadOperation, } - resp, err = is.HandleRequest(context.Background(), readReq) + resp, err = is.HandleRequest(ctx, readReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } @@ -628,12 +640,12 @@ func TestIdentityStore_EntityCRUD(t *testing.T) { Data: updateData, } - resp, err = is.HandleRequest(context.Background(), updateReq) + resp, err = is.HandleRequest(ctx, updateReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } - resp, err = is.HandleRequest(context.Background(), readReq) + resp, err = is.HandleRequest(ctx, readReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } @@ -649,12 +661,12 @@ func TestIdentityStore_EntityCRUD(t *testing.T) { Operation: logical.DeleteOperation, } - resp, err = is.HandleRequest(context.Background(), deleteReq) + resp, err = is.HandleRequest(ctx, deleteReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } - resp, err = is.HandleRequest(context.Background(), readReq) + resp, err = is.HandleRequest(ctx, readReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } @@ -667,7 +679,8 @@ func TestIdentityStore_MergeEntitiesByID(t *testing.T) { var err error var resp *logical.Response - is, githubAccessor, _ := testIdentityStoreWithGithubAuth(t) + ctx := namespace.RootContext(nil) + is, githubAccessor, _ := testIdentityStoreWithGithubAuth(ctx, t) registerData := map[string]interface{}{ "name": "testentityname2", @@ -710,7 +723,7 @@ func TestIdentityStore_MergeEntitiesByID(t *testing.T) { } // Register the entity - resp, err = is.HandleRequest(context.Background(), registerReq) + resp, err = is.HandleRequest(ctx, registerReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } @@ -728,14 +741,14 @@ func TestIdentityStore_MergeEntitiesByID(t *testing.T) { } // Register the alias - resp, err = is.HandleRequest(context.Background(), aliasReq) + resp, err = is.HandleRequest(ctx, aliasReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } // Register the alias aliasReq.Data = aliasRegisterData2 - resp, err = is.HandleRequest(context.Background(), aliasReq) + resp, err = is.HandleRequest(ctx, aliasReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } @@ -753,7 +766,7 @@ func TestIdentityStore_MergeEntitiesByID(t *testing.T) { registerReq.Data = registerData2 // Register another entity - resp, err = is.HandleRequest(context.Background(), registerReq) + resp, err = is.HandleRequest(ctx, registerReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } @@ -770,14 +783,14 @@ func TestIdentityStore_MergeEntitiesByID(t *testing.T) { } // Register the alias - resp, err = is.HandleRequest(context.Background(), aliasReq) + resp, err = is.HandleRequest(ctx, aliasReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } // Register the alias aliasReq.Data = aliasRegisterData4 - resp, err = is.HandleRequest(context.Background(), aliasReq) + resp, err = is.HandleRequest(ctx, aliasReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } @@ -804,7 +817,7 @@ func TestIdentityStore_MergeEntitiesByID(t *testing.T) { Data: mergeData, } - resp, err = is.HandleRequest(context.Background(), mergeReq) + resp, err = is.HandleRequest(ctx, mergeReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } @@ -813,7 +826,7 @@ func TestIdentityStore_MergeEntitiesByID(t *testing.T) { Operation: logical.ReadOperation, Path: "entity/id/" + entityID2, } - resp, err = is.HandleRequest(context.Background(), entityReq) + resp, err = is.HandleRequest(ctx, entityReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } @@ -822,7 +835,7 @@ func TestIdentityStore_MergeEntitiesByID(t *testing.T) { } entityReq.Path = "entity/id/" + entityID1 - resp, err = is.HandleRequest(context.Background(), entityReq) + resp, err = is.HandleRequest(ctx, entityReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } diff --git a/vault/identity_store_group_aliases.go b/vault/identity_store_group_aliases.go index 57b8734488..4a57b0aadd 100644 --- a/vault/identity_store_group_aliases.go +++ b/vault/identity_store_group_aliases.go @@ -5,9 +5,8 @@ import ( "fmt" "strings" - "github.com/hashicorp/errwrap" - memdb "github.com/hashicorp/go-memdb" "github.com/hashicorp/vault/helper/identity" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/logical/framework" ) @@ -92,7 +91,7 @@ func (i *IdentityStore) pathGroupAliasRegister() framework.OperationFunc { i.groupLock.Lock() defer i.groupLock.Unlock() - return i.handleGroupAliasUpdateCommon(req, d, nil) + return i.handleGroupAliasUpdateCommon(ctx, req, d, nil) } } @@ -114,14 +113,14 @@ func (i *IdentityStore) pathGroupAliasIDUpdate() framework.OperationFunc { return logical.ErrorResponse("invalid group alias ID"), nil } - return i.handleGroupAliasUpdateCommon(req, d, groupAlias) + return i.handleGroupAliasUpdateCommon(ctx, req, d, groupAlias) } } -func (i *IdentityStore) handleGroupAliasUpdateCommon(req *logical.Request, d *framework.FieldData, groupAlias *identity.Alias) (*logical.Response, error) { - var err error +func (i *IdentityStore) handleGroupAliasUpdateCommon(ctx context.Context, req *logical.Request, d *framework.FieldData, groupAlias *identity.Alias) (*logical.Response, error) { var newGroupAlias bool var group *identity.Group + var err error if groupAlias == nil { groupAlias = &identity.Alias{} @@ -214,7 +213,7 @@ func (i *IdentityStore) handleGroupAliasUpdateCommon(req *logical.Request, d *fr // Explicitly correct for previous versions that persisted this group.Alias.MountType = "" - err = i.sanitizeAndUpsertGroup(group, nil) + err = i.sanitizeAndUpsertGroup(ctx, group, nil) if err != nil { return nil, err } @@ -241,7 +240,7 @@ func (i *IdentityStore) pathGroupAliasIDRead() framework.OperationFunc { return nil, err } - return i.handleAliasReadCommon(groupAlias) + return i.handleAliasReadCommon(ctx, groupAlias) } } @@ -268,6 +267,14 @@ func (i *IdentityStore) pathGroupAliasIDDelete() framework.OperationFunc { return nil, nil } + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } + if ns.ID != alias.NamespaceID { + return nil, logical.ErrUnsupportedOperation + } + group, err := i.MemDBGroupByAliasIDInTxn(txn, alias.ID, true) if err != nil { return nil, err @@ -302,53 +309,7 @@ func (i *IdentityStore) pathGroupAliasIDDelete() framework.OperationFunc { // identity store func (i *IdentityStore) pathGroupAliasIDList() framework.OperationFunc { return func(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { - ws := memdb.NewWatchSet() - iter, err := i.MemDBAliases(ws, true) - if err != nil { - return nil, errwrap.Wrapf("failed to fetch iterator for group aliases in memdb: {{err}}", err) - } - - var groupAliasIDs []string - aliasInfo := map[string]interface{}{} - - type mountInfo struct { - MountType string - MountPath string - } - mountAccessorMap := map[string]mountInfo{} - - for { - raw := iter.Next() - if raw == nil { - break - } - alias := raw.(*identity.Alias) - groupAliasIDs = append(groupAliasIDs, alias.ID) - entry := map[string]interface{}{ - "name": alias.Name, - "canonical_id": alias.CanonicalID, - "mount_accessor": alias.MountAccessor, - } - - mi, ok := mountAccessorMap[alias.MountAccessor] - if ok { - entry["mount_type"] = mi.MountType - entry["mount_path"] = mi.MountPath - } else { - mi = mountInfo{} - if mountValidationResp := i.core.router.validateMountByAccessor(alias.MountAccessor); mountValidationResp != nil { - mi.MountType = mountValidationResp.MountType - mi.MountPath = mountValidationResp.MountPath - entry["mount_type"] = mi.MountType - entry["mount_path"] = mi.MountPath - } - mountAccessorMap[alias.MountAccessor] = mi - } - - aliasInfo[alias.ID] = entry - } - - return logical.ListResponseWithInfo(groupAliasIDs, aliasInfo), nil + return i.handleAliasListCommon(ctx, true) } } diff --git a/vault/identity_store_group_aliases_test.go b/vault/identity_store_group_aliases_test.go index c227ee5de8..820852d64d 100644 --- a/vault/identity_store_group_aliases_test.go +++ b/vault/identity_store_group_aliases_test.go @@ -1,10 +1,10 @@ package vault import ( - "context" "testing" "github.com/hashicorp/vault/helper/identity" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/logical" ) @@ -12,9 +12,10 @@ func TestIdentityStore_GroupAliasDeletionOnGroupDeletion(t *testing.T) { var resp *logical.Response var err error - i, accessor, _ := testIdentityStoreWithGithubAuth(t) + ctx := namespace.RootContext(nil) + i, accessor, _ := testIdentityStoreWithGithubAuth(ctx, t) - resp, err = i.HandleRequest(context.Background(), &logical.Request{ + resp, err = i.HandleRequest(ctx, &logical.Request{ Path: "group", Operation: logical.UpdateOperation, Data: map[string]interface{}{ @@ -26,7 +27,7 @@ func TestIdentityStore_GroupAliasDeletionOnGroupDeletion(t *testing.T) { } groupID := resp.Data["id"].(string) - resp, err = i.HandleRequest(context.Background(), &logical.Request{ + resp, err = i.HandleRequest(ctx, &logical.Request{ Path: "group-alias", Operation: logical.UpdateOperation, Data: map[string]interface{}{ @@ -40,7 +41,7 @@ func TestIdentityStore_GroupAliasDeletionOnGroupDeletion(t *testing.T) { } groupAliasID := resp.Data["id"].(string) - resp, err = i.HandleRequest(context.Background(), &logical.Request{ + resp, err = i.HandleRequest(ctx, &logical.Request{ Path: "group/id/" + groupID, Operation: logical.DeleteOperation, }) @@ -48,7 +49,7 @@ func TestIdentityStore_GroupAliasDeletionOnGroupDeletion(t *testing.T) { t.Fatalf("bad: resp: %#v\nerr: %v", resp, err) } - resp, err = i.HandleRequest(context.Background(), &logical.Request{ + resp, err = i.HandleRequest(ctx, &logical.Request{ Path: "group-alias/id/" + groupAliasID, Operation: logical.ReadOperation, }) @@ -63,7 +64,8 @@ func TestIdentityStore_GroupAliasDeletionOnGroupDeletion(t *testing.T) { func TestIdentityStore_GroupAliases_CRUD(t *testing.T) { var resp *logical.Response var err error - i, accessor, _ := testIdentityStoreWithGithubAuth(t) + ctx := namespace.RootContext(nil) + i, accessor, _ := testIdentityStoreWithGithubAuth(ctx, t) groupReq := &logical.Request{ Path: "group", @@ -72,7 +74,7 @@ func TestIdentityStore_GroupAliases_CRUD(t *testing.T) { "type": "external", }, } - resp, err = i.HandleRequest(context.Background(), groupReq) + resp, err = i.HandleRequest(ctx, groupReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v\nerr: %v\n", resp, err) } @@ -88,7 +90,7 @@ func TestIdentityStore_GroupAliases_CRUD(t *testing.T) { "mount_type": "ldap", }, } - resp, err = i.HandleRequest(context.Background(), groupAliasReq) + resp, err = i.HandleRequest(ctx, groupAliasReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v\nerr: %v\n", resp, err) } @@ -96,7 +98,7 @@ func TestIdentityStore_GroupAliases_CRUD(t *testing.T) { groupAliasReq.Path = "group-alias/id/" + groupAliasID groupAliasReq.Operation = logical.ReadOperation - resp, err = i.HandleRequest(context.Background(), groupAliasReq) + resp, err = i.HandleRequest(ctx, groupAliasReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v\nerr: %v\n", resp, err) } @@ -105,7 +107,7 @@ func TestIdentityStore_GroupAliases_CRUD(t *testing.T) { t.Fatalf("bad: group alias: %#v\n", resp.Data) } - resp, err = i.HandleRequest(context.Background(), &logical.Request{ + resp, err = i.HandleRequest(ctx, &logical.Request{ Path: "group-alias/id/" + groupAliasID, Operation: logical.UpdateOperation, Data: map[string]interface{}{ @@ -123,13 +125,13 @@ func TestIdentityStore_GroupAliases_CRUD(t *testing.T) { } groupAliasReq.Operation = logical.DeleteOperation - resp, err = i.HandleRequest(context.Background(), groupAliasReq) + resp, err = i.HandleRequest(ctx, groupAliasReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v\nerr: %v\n", resp, err) } groupAliasReq.Operation = logical.ReadOperation - resp, err = i.HandleRequest(context.Background(), groupAliasReq) + resp, err = i.HandleRequest(ctx, groupAliasReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v\nerr: %v\n", resp, err) } @@ -141,7 +143,8 @@ func TestIdentityStore_GroupAliases_CRUD(t *testing.T) { func TestIdentityStore_GroupAliases_MemDBIndexes(t *testing.T) { var err error - i, accessor, _ := testIdentityStoreWithGithubAuth(t) + ctx := namespace.RootContext(nil) + i, accessor, _ := testIdentityStoreWithGithubAuth(ctx, t) group := &identity.Group{ ID: "testgroupid", @@ -204,13 +207,14 @@ func TestIdentityStore_GroupAliases_AliasOnInternalGroup(t *testing.T) { var err error var resp *logical.Response - i, accessor, _ := testIdentityStoreWithGithubAuth(t) + ctx := namespace.RootContext(nil) + i, accessor, _ := testIdentityStoreWithGithubAuth(ctx, t) groupReq := &logical.Request{ Path: "group", Operation: logical.UpdateOperation, } - resp, err = i.HandleRequest(context.Background(), groupReq) + resp, err = i.HandleRequest(ctx, groupReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v; err: %v", resp, err) } @@ -225,7 +229,7 @@ func TestIdentityStore_GroupAliases_AliasOnInternalGroup(t *testing.T) { "canonical_id": groupID, }, } - resp, err = i.HandleRequest(context.Background(), aliasReq) + resp, err = i.HandleRequest(ctx, aliasReq) if err != nil { t.Fatal(err) } diff --git a/vault/identity_store_groups.go b/vault/identity_store_groups.go index f61c1fd491..e695f11fbe 100644 --- a/vault/identity_store_groups.go +++ b/vault/identity_store_groups.go @@ -7,8 +7,8 @@ import ( "github.com/golang/protobuf/ptypes" "github.com/hashicorp/errwrap" - memdb "github.com/hashicorp/go-memdb" "github.com/hashicorp/vault/helper/identity" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/helper/strutil" "github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/logical/framework" @@ -132,7 +132,7 @@ func (i *IdentityStore) pathGroupRegister() framework.OperationFunc { i.groupLock.Lock() defer i.groupLock.Unlock() - return i.handleGroupUpdateCommon(req, d, nil) + return i.handleGroupUpdateCommon(ctx, req, d, nil) } } @@ -154,15 +154,14 @@ func (i *IdentityStore) pathGroupIDUpdate() framework.OperationFunc { return logical.ErrorResponse("invalid group ID"), nil } - return i.handleGroupUpdateCommon(req, d, group) + return i.handleGroupUpdateCommon(ctx, req, d, group) } } -func (i *IdentityStore) handleGroupUpdateCommon(req *logical.Request, d *framework.FieldData, group *identity.Group) (*logical.Response, error) { - var err error +func (i *IdentityStore) handleGroupUpdateCommon(ctx context.Context, req *logical.Request, d *framework.FieldData, group *identity.Group) (*logical.Response, error) { var newGroup bool if group == nil { - group = &identity.Group{} + group = new(identity.Group) newGroup = true } @@ -199,7 +198,7 @@ func (i *IdentityStore) handleGroupUpdateCommon(req *logical.Request, d *framewo groupName := d.Get("name").(string) if groupName != "" { // Check if there is a group already existing for the given name - groupByName, err := i.MemDBGroupByName(groupName, false) + groupByName, err := i.MemDBGroupByName(ctx, groupName, false) if err != nil { return nil, err } @@ -209,7 +208,11 @@ func (i *IdentityStore) handleGroupUpdateCommon(req *logical.Request, d *framewo // modified into something which is already tied to a different group, // error out. switch { - case (newGroup && groupByName != nil), (groupByName != nil && group.ID != "" && groupByName.ID != group.ID): + case groupByName == nil: + // Allowed + case newGroup: + return logical.ErrorResponse("updating a group by name is not currently supported"), nil + case group.ID != "" && groupByName.ID != group.ID: return logical.ErrorResponse("group name is already in use"), nil } group.Name = groupName @@ -243,7 +246,7 @@ func (i *IdentityStore) handleGroupUpdateCommon(req *logical.Request, d *framewo memberGroupIDs = memberGroupIDsRaw.([]string) } - err = i.sanitizeAndUpsertGroup(group, memberGroupIDs) + err = i.sanitizeAndUpsertGroup(ctx, group, memberGroupIDs) if err != nil { return nil, err } @@ -268,16 +271,27 @@ func (i *IdentityStore) pathGroupIDRead() framework.OperationFunc { if err != nil { return nil, err } + if group == nil { + return nil, nil + } - return i.handleGroupReadCommon(group) + return i.handleGroupReadCommon(ctx, group) } } -func (i *IdentityStore) handleGroupReadCommon(group *identity.Group) (*logical.Response, error) { +func (i *IdentityStore) handleGroupReadCommon(ctx context.Context, group *identity.Group) (*logical.Response, error) { if group == nil { return nil, nil } + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } + if ns.ID != group.NamespaceID { + return nil, nil + } + respData := map[string]interface{}{} respData["id"] = group.ID respData["name"] = group.Name @@ -354,6 +368,14 @@ func (i *IdentityStore) pathGroupIDDelete() framework.OperationFunc { return nil, nil } + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } + if group.NamespaceID != ns.ID { + return nil, logical.ErrUnsupportedPath + } + // Delete group alias from memdb if group.Type == groupTypeExternal && group.Alias != nil { err = i.MemDBDeleteAliasByIDInTxn(txn, group.Alias.ID, true) @@ -384,17 +406,18 @@ func (i *IdentityStore) pathGroupIDDelete() framework.OperationFunc { // pathGroupIDList lists the IDs of all the groups in the identity store func (i *IdentityStore) pathGroupIDList() framework.OperationFunc { return func(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { - ws := memdb.NewWatchSet() + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } txn := i.db.Txn(false) - iter, err := txn.Get(groupsTable, "id") + iter, err := txn.Get(groupsTable, "namespace_id", ns.ID) if err != nil { - return nil, errwrap.Wrapf("failed to fetch iterator for group in memdb: {{err}}", err) + return nil, errwrap.Wrapf("failed to lookup groups using namespace ID: {{err}}", err) } - ws.Add(iter.WatchCh()) - var groupIDs []string groupInfo := map[string]interface{}{} @@ -404,12 +427,8 @@ func (i *IdentityStore) pathGroupIDList() framework.OperationFunc { } mountAccessorMap := map[string]mountInfo{} - for { - raw := iter.Next() - if raw == nil { - break - } - group := raw.(*identity.Group) + for entry := iter.Next(); entry != nil; entry = iter.Next() { + group := entry.(*identity.Group) groupIDs = append(groupIDs, group.ID) groupInfoEntry := map[string]interface{}{ "name": group.Name, diff --git a/vault/identity_store_groups_test.go b/vault/identity_store_groups_test.go index 97eef8b98a..df6937008a 100644 --- a/vault/identity_store_groups_test.go +++ b/vault/identity_store_groups_test.go @@ -1,13 +1,13 @@ package vault import ( - "context" "reflect" "sort" "testing" "github.com/go-test/deep" "github.com/hashicorp/vault/helper/identity" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/logical" ) @@ -15,7 +15,8 @@ func TestIdentityStore_Groups_TypeMembershipAdditions(t *testing.T) { var err error var resp *logical.Response - i, _, _ := testIdentityStoreWithGithubAuth(t) + ctx := namespace.RootContext(nil) + i, _, _ := testIdentityStoreWithGithubAuth(ctx, t) groupReq := &logical.Request{ Path: "group", Operation: logical.UpdateOperation, @@ -25,7 +26,7 @@ func TestIdentityStore_Groups_TypeMembershipAdditions(t *testing.T) { }, } - resp, err = i.HandleRequest(context.Background(), groupReq) + resp, err = i.HandleRequest(ctx, groupReq) if err != nil { t.Fatal(err) } @@ -38,7 +39,7 @@ func TestIdentityStore_Groups_TypeMembershipAdditions(t *testing.T) { "member_group_ids": "samplegroupid", } - resp, err = i.HandleRequest(context.Background(), groupReq) + resp, err = i.HandleRequest(ctx, groupReq) if err != nil { t.Fatal(err) } @@ -51,13 +52,14 @@ func TestIdentityStore_Groups_TypeImmutability(t *testing.T) { var err error var resp *logical.Response - i, _, _ := testIdentityStoreWithGithubAuth(t) + ctx := namespace.RootContext(nil) + i, _, _ := testIdentityStoreWithGithubAuth(ctx, t) groupReq := &logical.Request{ Path: "group", Operation: logical.UpdateOperation, } - resp, err = i.HandleRequest(context.Background(), groupReq) + resp, err = i.HandleRequest(ctx, groupReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v, err: %v", resp, err) } @@ -66,7 +68,7 @@ func TestIdentityStore_Groups_TypeImmutability(t *testing.T) { groupReq.Data = map[string]interface{}{ "type": "external", } - resp, err = i.HandleRequest(context.Background(), groupReq) + resp, err = i.HandleRequest(ctx, groupReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v, err: %v", resp, err) } @@ -77,7 +79,7 @@ func TestIdentityStore_Groups_TypeImmutability(t *testing.T) { "type": "external", } groupReq.Path = "group/id/" + internalGroupID - resp, err = i.HandleRequest(context.Background(), groupReq) + resp, err = i.HandleRequest(ctx, groupReq) if err != nil { t.Fatal(err) } @@ -90,7 +92,7 @@ func TestIdentityStore_Groups_TypeImmutability(t *testing.T) { "type": "internal", } groupReq.Path = "group/id/" + externalGroupID - resp, err = i.HandleRequest(context.Background(), groupReq) + resp, err = i.HandleRequest(ctx, groupReq) if err != nil { t.Fatal(err) } @@ -101,7 +103,8 @@ func TestIdentityStore_Groups_TypeImmutability(t *testing.T) { func TestIdentityStore_MemDBGroupIndexes(t *testing.T) { var err error - i, _, _ := testIdentityStoreWithGithubAuth(t) + ctx := namespace.RootContext(nil) + i, _, _ := testIdentityStoreWithGithubAuth(ctx, t) // Create a dummy group group := &identity.Group{ @@ -153,7 +156,7 @@ func TestIdentityStore_MemDBGroupIndexes(t *testing.T) { var fetchedGroup *identity.Group // Fetch group given the name - fetchedGroup, err = i.MemDBGroupByName("testgroupname", false) + fetchedGroup, err = i.MemDBGroupByName(namespace.RootContext(nil), "testgroupname", false) if err != nil { t.Fatal(err) } @@ -210,21 +213,23 @@ func TestIdentityStore_MemDBGroupIndexes(t *testing.T) { func TestIdentityStore_GroupsCreateUpdate(t *testing.T) { var resp *logical.Response var err error - is, _, _ := testIdentityStoreWithGithubAuth(t) + + ctx := namespace.RootContext(nil) + is, _, _ := testIdentityStoreWithGithubAuth(ctx, t) // Create an entity and get its ID entityRegisterReq := &logical.Request{ Operation: logical.UpdateOperation, Path: "entity", } - resp, err = is.HandleRequest(context.Background(), entityRegisterReq) + resp, err = is.HandleRequest(ctx, entityRegisterReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v, err: %v", resp, err) } entityID1 := resp.Data["id"].(string) // Create another entity and get its ID - resp, err = is.HandleRequest(context.Background(), entityRegisterReq) + resp, err = is.HandleRequest(ctx, entityRegisterReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v, err: %v", resp, err) } @@ -243,14 +248,14 @@ func TestIdentityStore_GroupsCreateUpdate(t *testing.T) { Path: "group", Data: groupData, } - resp, err = is.HandleRequest(context.Background(), groupReq) + resp, err = is.HandleRequest(ctx, groupReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v, err: %v", resp, err) } memberGroupID1 := resp.Data["id"].(string) // Create another group and get its ID - resp, err = is.HandleRequest(context.Background(), groupReq) + resp, err = is.HandleRequest(ctx, groupReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v, err: %v", resp, err) } @@ -258,7 +263,7 @@ func TestIdentityStore_GroupsCreateUpdate(t *testing.T) { // Create a group with the above 2 groups as its members groupData["member_group_ids"] = []string{memberGroupID1, memberGroupID2} - resp, err = is.HandleRequest(context.Background(), groupReq) + resp, err = is.HandleRequest(ctx, groupReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v, err: %v", resp, err) } @@ -270,7 +275,7 @@ func TestIdentityStore_GroupsCreateUpdate(t *testing.T) { Operation: logical.ReadOperation, Path: "group/id/" + groupID, } - resp, err = is.HandleRequest(context.Background(), groupReq) + resp, err = is.HandleRequest(ctx, groupReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v, err: %v", resp, err) } @@ -305,14 +310,14 @@ func TestIdentityStore_GroupsCreateUpdate(t *testing.T) { groupData["id"] = groupID groupData["policies"] = "updatedpolicy1,updatedpolicy2" groupData["metadata"] = []string{"updatedkey=updatedvalue"} - resp, err = is.HandleRequest(context.Background(), groupReq) + resp, err = is.HandleRequest(ctx, groupReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v, err: %v", resp, err) } // Check if updates are reflected groupReq.Operation = logical.ReadOperation - resp, err = is.HandleRequest(context.Background(), groupReq) + resp, err = is.HandleRequest(ctx, groupReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v, err: %v", resp, err) } @@ -331,21 +336,22 @@ func TestIdentityStore_GroupsCreateUpdate(t *testing.T) { func TestIdentityStore_GroupsCRUD_ByID(t *testing.T) { var resp *logical.Response var err error - is, _, _ := testIdentityStoreWithGithubAuth(t) + ctx := namespace.RootContext(nil) + is, _, _ := testIdentityStoreWithGithubAuth(ctx, t) // Create an entity and get its ID entityRegisterReq := &logical.Request{ Operation: logical.UpdateOperation, Path: "entity", } - resp, err = is.HandleRequest(context.Background(), entityRegisterReq) + resp, err = is.HandleRequest(ctx, entityRegisterReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v, err: %v", resp, err) } entityID1 := resp.Data["id"].(string) // Create another entity and get its ID - resp, err = is.HandleRequest(context.Background(), entityRegisterReq) + resp, err = is.HandleRequest(ctx, entityRegisterReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v, err: %v", resp, err) } @@ -364,14 +370,14 @@ func TestIdentityStore_GroupsCRUD_ByID(t *testing.T) { Path: "group", Data: groupData, } - resp, err = is.HandleRequest(context.Background(), groupRegisterReq) + resp, err = is.HandleRequest(ctx, groupRegisterReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v, err: %v", resp, err) } memberGroupID1 := resp.Data["id"].(string) // Create another group and get its ID - resp, err = is.HandleRequest(context.Background(), groupRegisterReq) + resp, err = is.HandleRequest(ctx, groupRegisterReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v, err: %v", resp, err) } @@ -379,7 +385,7 @@ func TestIdentityStore_GroupsCRUD_ByID(t *testing.T) { // Create a group with the above 2 groups as its members groupData["member_group_ids"] = []string{memberGroupID1, memberGroupID2} - resp, err = is.HandleRequest(context.Background(), groupRegisterReq) + resp, err = is.HandleRequest(ctx, groupRegisterReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v, err: %v", resp, err) } @@ -391,7 +397,7 @@ func TestIdentityStore_GroupsCRUD_ByID(t *testing.T) { Operation: logical.ReadOperation, Path: "group/id/" + groupID, } - resp, err = is.HandleRequest(context.Background(), groupReq) + resp, err = is.HandleRequest(ctx, groupReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v, err: %v", resp, err) } @@ -423,14 +429,14 @@ func TestIdentityStore_GroupsCRUD_ByID(t *testing.T) { groupReq.Data = groupData groupData["policies"] = "updatedpolicy1,updatedpolicy2" groupData["metadata"] = []string{"updatedkey=updatedvalue"} - resp, err = is.HandleRequest(context.Background(), groupReq) + resp, err = is.HandleRequest(ctx, groupReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v, err: %v", resp, err) } // Check if updates are reflected groupReq.Operation = logical.ReadOperation - resp, err = is.HandleRequest(context.Background(), groupReq) + resp, err = is.HandleRequest(ctx, groupReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v, err: %v", resp, err) } @@ -447,13 +453,13 @@ func TestIdentityStore_GroupsCRUD_ByID(t *testing.T) { // Check if delete is working properly groupReq.Operation = logical.DeleteOperation - resp, err = is.HandleRequest(context.Background(), groupReq) + resp, err = is.HandleRequest(ctx, groupReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v, err: %v", resp, err) } groupReq.Operation = logical.ReadOperation - resp, err = is.HandleRequest(context.Background(), groupReq) + resp, err = is.HandleRequest(ctx, groupReq) if err != nil { t.Fatal(err) } @@ -465,7 +471,8 @@ func TestIdentityStore_GroupsCRUD_ByID(t *testing.T) { func TestIdentityStore_GroupMultiCase(t *testing.T) { var resp *logical.Response var err error - is, _, _ := testIdentityStoreWithGithubAuth(t) + ctx := namespace.RootContext(nil) + is, _, _ := testIdentityStoreWithGithubAuth(ctx, t) groupRegisterReq := &logical.Request{ Operation: logical.UpdateOperation, Path: "group", @@ -477,7 +484,7 @@ func TestIdentityStore_GroupMultiCase(t *testing.T) { "policies": "buildpolicy", } groupRegisterReq.Data = buildGroupData - resp, err = is.HandleRequest(context.Background(), groupRegisterReq) + resp, err = is.HandleRequest(ctx, groupRegisterReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v, err: %v", resp, err) } @@ -489,7 +496,7 @@ func TestIdentityStore_GroupMultiCase(t *testing.T) { "policies": "deploypolicy", } groupRegisterReq.Data = deployGroupData - resp, err = is.HandleRequest(context.Background(), groupRegisterReq) + resp, err = is.HandleRequest(ctx, groupRegisterReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v, err: %v", resp, err) } @@ -500,7 +507,7 @@ func TestIdentityStore_GroupMultiCase(t *testing.T) { Operation: logical.UpdateOperation, Path: "entity", } - resp, err = is.HandleRequest(context.Background(), entityRegisterReq) + resp, err = is.HandleRequest(ctx, entityRegisterReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v, err: %v", resp, err) } @@ -514,22 +521,27 @@ func TestIdentityStore_GroupMultiCase(t *testing.T) { "member_entity_ids": []string{entityID1}, }, } - resp, err = is.HandleRequest(context.Background(), entityIDReq) + resp, err = is.HandleRequest(ctx, entityIDReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v, err: %v", resp, err) } // Add the entity as a member of the 'deploy` group entityIDReq.Path = "group/id/" + deployGroupID - resp, err = is.HandleRequest(context.Background(), entityIDReq) + resp, err = is.HandleRequest(ctx, entityIDReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v, err: %v", resp, err) } - policies, err := is.groupPoliciesByEntityID(entityID1) + policiesResult, err := is.groupPoliciesByEntityID(entityID1) if err != nil { t.Fatal(err) } + + policies := []string{} + for _, nsPolicies := range policiesResult { + policies = append(policies, nsPolicies...) + } sort.Strings(policies) expected := []string{"deploypolicy", "buildpolicy"} sort.Strings(expected) @@ -549,7 +561,8 @@ Test groups hierarchy: func TestIdentityStore_GroupHierarchyCases(t *testing.T) { var resp *logical.Response var err error - is, _, _ := testIdentityStoreWithGithubAuth(t) + ctx := namespace.RootContext(nil) + is, _, _ := testIdentityStoreWithGithubAuth(ctx, t) groupRegisterReq := &logical.Request{ Operation: logical.UpdateOperation, Path: "group", @@ -561,7 +574,7 @@ func TestIdentityStore_GroupHierarchyCases(t *testing.T) { "policies": "kubepolicy", } groupRegisterReq.Data = kubeGroupData - resp, err = is.HandleRequest(context.Background(), groupRegisterReq) + resp, err = is.HandleRequest(ctx, groupRegisterReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v, err: %v", resp, err) } @@ -573,7 +586,7 @@ func TestIdentityStore_GroupHierarchyCases(t *testing.T) { "policies": "identitypolicy", } groupRegisterReq.Data = identityGroupData - resp, err = is.HandleRequest(context.Background(), groupRegisterReq) + resp, err = is.HandleRequest(ctx, groupRegisterReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v, err: %v", resp, err) } @@ -585,7 +598,7 @@ func TestIdentityStore_GroupHierarchyCases(t *testing.T) { "policies": "buildpolicy", } groupRegisterReq.Data = buildGroupData - resp, err = is.HandleRequest(context.Background(), groupRegisterReq) + resp, err = is.HandleRequest(ctx, groupRegisterReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v, err: %v", resp, err) } @@ -597,7 +610,7 @@ func TestIdentityStore_GroupHierarchyCases(t *testing.T) { "policies": "deploypolicy", } groupRegisterReq.Data = deployGroupData - resp, err = is.HandleRequest(context.Background(), groupRegisterReq) + resp, err = is.HandleRequest(ctx, groupRegisterReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v, err: %v", resp, err) } @@ -611,7 +624,7 @@ func TestIdentityStore_GroupHierarchyCases(t *testing.T) { "member_group_ids": vaultMemberGroupIDs, } groupRegisterReq.Data = vaultGroupData - resp, err = is.HandleRequest(context.Background(), groupRegisterReq) + resp, err = is.HandleRequest(ctx, groupRegisterReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v, err: %v", resp, err) } @@ -625,7 +638,7 @@ func TestIdentityStore_GroupHierarchyCases(t *testing.T) { "member_group_ids": opsMemberGroupIDs, } groupRegisterReq.Data = opsGroupData - resp, err = is.HandleRequest(context.Background(), groupRegisterReq) + resp, err = is.HandleRequest(ctx, groupRegisterReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v, err: %v", resp, err) } @@ -640,7 +653,7 @@ func TestIdentityStore_GroupHierarchyCases(t *testing.T) { } groupRegisterReq.Data = engGroupData - resp, err = is.HandleRequest(context.Background(), groupRegisterReq) + resp, err = is.HandleRequest(ctx, groupRegisterReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v, err: %v", resp, err) } @@ -708,7 +721,7 @@ func TestIdentityStore_GroupHierarchyCases(t *testing.T) { groupUpdateReq.Path = "group/name/kube" groupUpdateReq.Data = kubeGroupData kubeGroupData["member_group_ids"] = []string{engGroupID} - resp, err = is.HandleRequest(context.Background(), groupUpdateReq) + resp, err = is.HandleRequest(ctx, groupUpdateReq) if err == nil { t.Fatalf("expected an error response") } @@ -718,7 +731,7 @@ func TestIdentityStore_GroupHierarchyCases(t *testing.T) { Operation: logical.UpdateOperation, Path: "entity", } - resp, err = is.HandleRequest(context.Background(), entityRegisterReq) + resp, err = is.HandleRequest(ctx, entityRegisterReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v, err: %v", resp, err) } @@ -732,13 +745,13 @@ func TestIdentityStore_GroupHierarchyCases(t *testing.T) { "member_entity_ids": []string{entityID1}, }, } - resp, err = is.HandleRequest(context.Background(), entityIDReq) + resp, err = is.HandleRequest(ctx, entityIDReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v, err: %v", resp, err) } // Create a second entity ID - resp, err = is.HandleRequest(context.Background(), entityRegisterReq) + resp, err = is.HandleRequest(ctx, entityRegisterReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v, err: %v", resp, err) } @@ -749,13 +762,13 @@ func TestIdentityStore_GroupHierarchyCases(t *testing.T) { entityIDReq.Data = map[string]interface{}{ "member_entity_ids": []string{entityID2}, } - resp, err = is.HandleRequest(context.Background(), entityIDReq) + resp, err = is.HandleRequest(ctx, entityIDReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v, err: %v", resp, err) } // Create a third entity ID - resp, err = is.HandleRequest(context.Background(), entityRegisterReq) + resp, err = is.HandleRequest(ctx, entityRegisterReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v, err: %v", resp, err) } @@ -766,15 +779,19 @@ func TestIdentityStore_GroupHierarchyCases(t *testing.T) { entityIDReq.Data = map[string]interface{}{ "member_entity_ids": []string{entityID3}, } - resp, err = is.HandleRequest(context.Background(), entityIDReq) + resp, err = is.HandleRequest(ctx, entityIDReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v, err: %v", resp, err) } - policies, err := is.groupPoliciesByEntityID(entityID1) + policiesResult, err := is.groupPoliciesByEntityID(entityID1) if err != nil { t.Fatal(err) } + var policies []string + for _, nsPolicies := range policiesResult { + policies = append(policies, nsPolicies...) + } sort.Strings(policies) expected := []string{"kubepolicy", "vaultpolicy", "engpolicy"} sort.Strings(expected) @@ -782,10 +799,14 @@ func TestIdentityStore_GroupHierarchyCases(t *testing.T) { t.Fatalf("bad: policies; expected: %#v\nactual:%#v", expected, policies) } - policies, err = is.groupPoliciesByEntityID(entityID2) + policiesResult, err = is.groupPoliciesByEntityID(entityID2) if err != nil { t.Fatal(err) } + policies = nil + for _, nsPolicies := range policiesResult { + policies = append(policies, nsPolicies...) + } sort.Strings(policies) expected = []string{"opspolicy", "engpolicy"} sort.Strings(expected) @@ -793,10 +814,14 @@ func TestIdentityStore_GroupHierarchyCases(t *testing.T) { t.Fatalf("bad: policies; expected: %#v\nactual:%#v", expected, policies) } - policies, err = is.groupPoliciesByEntityID(entityID3) + policiesResult, err = is.groupPoliciesByEntityID(entityID3) if err != nil { t.Fatal(err) } + policies = nil + for _, nsPolicies := range policiesResult { + policies = append(policies, nsPolicies...) + } if len(policies) != 1 && policies[0] != "engpolicy" { t.Fatalf("bad: policies; expected: 'engpolicy'\nactual:%#v", policies) diff --git a/vault/identity_store_schema.go b/vault/identity_store_schema.go index 23c0239ecc..34915f4534 100644 --- a/vault/identity_store_schema.go +++ b/vault/identity_store_schema.go @@ -61,6 +61,12 @@ func aliasesTableSchema() *memdb.TableSchema { }, }, }, + "namespace_id": &memdb.IndexSchema{ + Name: "namespace_id", + Indexer: &memdb.StringFieldIndex{ + Field: "NamespaceID", + }, + }, }, } } @@ -79,8 +85,15 @@ func entitiesTableSchema() *memdb.TableSchema { "name": &memdb.IndexSchema{ Name: "name", Unique: true, - Indexer: &memdb.StringFieldIndex{ - Field: "Name", + Indexer: &memdb.CompoundIndex{ + Indexes: []memdb.Indexer{ + &memdb.StringFieldIndex{ + Field: "NamespaceID", + }, + &memdb.StringFieldIndex{ + Field: "Name", + }, + }, }, }, "merged_entity_ids": &memdb.IndexSchema{ @@ -92,13 +105,17 @@ func entitiesTableSchema() *memdb.TableSchema { }, }, "bucket_key_hash": &memdb.IndexSchema{ - Name: "bucket_key_hash", - Unique: false, - AllowMissing: false, + Name: "bucket_key_hash", Indexer: &memdb.StringFieldIndex{ Field: "BucketKeyHash", }, }, + "namespace_id": &memdb.IndexSchema{ + Name: "namespace_id", + Indexer: &memdb.StringFieldIndex{ + Field: "NamespaceID", + }, + }, }, } } @@ -117,13 +134,19 @@ func groupsTableSchema() *memdb.TableSchema { "name": { Name: "name", Unique: true, - Indexer: &memdb.StringFieldIndex{ - Field: "Name", + Indexer: &memdb.CompoundIndex{ + Indexes: []memdb.Indexer{ + &memdb.StringFieldIndex{ + Field: "NamespaceID", + }, + &memdb.StringFieldIndex{ + Field: "Name", + }, + }, }, }, "member_entity_ids": { Name: "member_entity_ids", - Unique: false, AllowMissing: true, Indexer: &memdb.StringSliceFieldIndex{ Field: "MemberEntityIDs", @@ -131,20 +154,23 @@ func groupsTableSchema() *memdb.TableSchema { }, "parent_group_ids": { Name: "parent_group_ids", - Unique: false, AllowMissing: true, Indexer: &memdb.StringSliceFieldIndex{ Field: "ParentGroupIDs", }, }, "bucket_key_hash": &memdb.IndexSchema{ - Name: "bucket_key_hash", - Unique: false, - AllowMissing: false, + Name: "bucket_key_hash", Indexer: &memdb.StringFieldIndex{ Field: "BucketKeyHash", }, }, + "namespace_id": &memdb.IndexSchema{ + Name: "namespace_id", + Indexer: &memdb.StringFieldIndex{ + Field: "NamespaceID", + }, + }, }, } } @@ -174,6 +200,12 @@ func groupAliasesTableSchema() *memdb.TableSchema { }, }, }, + "namespace_id": &memdb.IndexSchema{ + Name: "namespace_id", + Indexer: &memdb.StringFieldIndex{ + Field: "NamespaceID", + }, + }, }, } } diff --git a/vault/identity_store_test.go b/vault/identity_store_test.go index f9d3b22726..a6827cdb9f 100644 --- a/vault/identity_store_test.go +++ b/vault/identity_store_test.go @@ -10,13 +10,15 @@ import ( uuid "github.com/hashicorp/go-uuid" credGithub "github.com/hashicorp/vault/builtin/credential/github" "github.com/hashicorp/vault/helper/identity" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/helper/storagepacker" "github.com/hashicorp/vault/logical" ) func TestIdentityStore_EntityIDPassthrough(t *testing.T) { // Enable GitHub auth and initialize - is, ghAccessor, core := testIdentityStoreWithGithubAuth(t) + ctx := namespace.TestContext() + is, ghAccessor, core := testIdentityStoreWithGithubAuth(ctx, t) alias := &logical.Alias{ MountType: "github", MountAccessor: ghAccessor, @@ -24,7 +26,7 @@ func TestIdentityStore_EntityIDPassthrough(t *testing.T) { } // Create an entity with GitHub alias - entity, err := is.CreateOrFetchEntity(alias) + entity, err := is.CreateOrFetchEntity(ctx, alias) if err != nil { t.Fatal(err) } @@ -39,8 +41,9 @@ func TestIdentityStore_EntityIDPassthrough(t *testing.T) { Policies: []string{"root"}, CreationTime: time.Now().Unix(), EntityID: entity.ID, + NamespaceID: namespace.RootNamespaceID, } - if err := core.tokenStore.create(context.Background(), ent); err != nil { + if err := core.tokenStore.create(ctx, ent); err != nil { t.Fatalf("err: %s", err) } @@ -65,13 +68,13 @@ func TestIdentityStore_EntityIDPassthrough(t *testing.T) { if err != nil { t.Fatal(err) } - err = core.router.Mount(noop, "test/backend/", &MountEntry{Path: "test/backend/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor"}, view) + err = core.router.Mount(noop, "test/backend/", &MountEntry{Path: "test/backend/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.RootNamespace}, view) if err != nil { t.Fatal(err) } // Make the request with the above created token - resp, err := core.HandleRequest(context.Background(), &logical.Request{ + resp, err := core.HandleRequest(ctx, &logical.Request{ ClientToken: "testtokenid", Operation: logical.ReadOperation, Path: "test/backend/foo", @@ -87,14 +90,15 @@ func TestIdentityStore_EntityIDPassthrough(t *testing.T) { } func TestIdentityStore_CreateOrFetchEntity(t *testing.T) { - is, ghAccessor, _ := testIdentityStoreWithGithubAuth(t) + ctx := namespace.RootContext(nil) + is, ghAccessor, _ := testIdentityStoreWithGithubAuth(ctx, t) alias := &logical.Alias{ MountType: "github", MountAccessor: ghAccessor, Name: "githubuser", } - entity, err := is.CreateOrFetchEntity(alias) + entity, err := is.CreateOrFetchEntity(ctx, alias) if err != nil { t.Fatal(err) } @@ -110,7 +114,7 @@ func TestIdentityStore_CreateOrFetchEntity(t *testing.T) { t.Fatalf("bad: alias name; expected: %q, actual: %q", alias.Name, entity.Aliases[0].Name) } - entity, err = is.CreateOrFetchEntity(alias) + entity, err = is.CreateOrFetchEntity(ctx, alias) if err != nil { t.Fatal(err) } @@ -131,7 +135,8 @@ func TestIdentityStore_EntityByAliasFactors(t *testing.T) { var err error var resp *logical.Response - is, ghAccessor, _ := testIdentityStoreWithGithubAuth(t) + ctx := namespace.RootContext(nil) + is, ghAccessor, _ := testIdentityStoreWithGithubAuth(ctx, t) registerData := map[string]interface{}{ "name": "testentityname", @@ -146,7 +151,7 @@ func TestIdentityStore_EntityByAliasFactors(t *testing.T) { } // Register the entity - resp, err = is.HandleRequest(context.Background(), registerReq) + resp, err = is.HandleRequest(ctx, registerReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } @@ -170,7 +175,7 @@ func TestIdentityStore_EntityByAliasFactors(t *testing.T) { Data: aliasData, } - resp, err = is.HandleRequest(context.Background(), aliasReq) + resp, err = is.HandleRequest(ctx, aliasReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } @@ -194,7 +199,8 @@ func TestIdentityStore_WrapInfoInheritance(t *testing.T) { var err error var resp *logical.Response - core, is, ts, _ := testCoreWithIdentityTokenGithub(t) + ctx := namespace.RootContext(nil) + core, is, ts, _ := testCoreWithIdentityTokenGithub(ctx, t) registerData := map[string]interface{}{ "name": "testentityname", @@ -209,7 +215,7 @@ func TestIdentityStore_WrapInfoInheritance(t *testing.T) { } // Register the entity - resp, err = is.HandleRequest(context.Background(), registerReq) + resp, err = is.HandleRequest(ctx, registerReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } @@ -245,7 +251,7 @@ func TestIdentityStore_WrapInfoInheritance(t *testing.T) { }, } - resp, err = core.HandleRequest(context.Background(), wrapReq) + resp, err = core.HandleRequest(ctx, wrapReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v, err: %v", resp, err) } @@ -279,7 +285,8 @@ func TestIdentityStore_TokenEntityInheritance(t *testing.T) { ClientToken: te.ID, } - resp, err := ts.HandleRequest(context.Background(), tokenReq) + ctx := namespace.RootContext(nil) + resp, err := ts.HandleRequest(ctx, tokenReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v err: %v", err, resp) } @@ -290,7 +297,7 @@ func TestIdentityStore_TokenEntityInheritance(t *testing.T) { // Create an orphan token; this should not inherit the EntityID tokenReq.Path = "create-orphan" - resp, err = ts.HandleRequest(context.Background(), tokenReq) + resp, err = ts.HandleRequest(ctx, tokenReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("bad: resp: %#v err: %v", err, resp) } @@ -315,7 +322,7 @@ func TestIdentityStore_MergeConflictingAliases(t *testing.T) { Description: "github auth", } - err = c.enableCredential(context.Background(), meGH) + err = c.enableCredential(namespace.TestContext(), meGH) if err != nil { t.Fatal(err) } @@ -384,7 +391,7 @@ func TestIdentityStore_MergeConflictingAliases(t *testing.T) { t.Fatal("still sealed") } - newEntity, err := c.identityStore.CreateOrFetchEntity(&logical.Alias{ + newEntity, err := c.identityStore.CreateOrFetchEntity(namespace.TestContext(), &logical.Alias{ MountAccessor: meGH.Accessor, MountType: "github", Name: "githubuser", @@ -416,18 +423,18 @@ func TestIdentityStore_MergeConflictingAliases(t *testing.T) { } } -func testCoreWithIdentityTokenGithub(t *testing.T) (*Core, *IdentityStore, *TokenStore, string) { - is, ghAccessor, core := testIdentityStoreWithGithubAuth(t) +func testCoreWithIdentityTokenGithub(ctx context.Context, t *testing.T) (*Core, *IdentityStore, *TokenStore, string) { + is, ghAccessor, core := testIdentityStoreWithGithubAuth(ctx, t) return core, is, core.tokenStore, ghAccessor } -func testCoreWithIdentityTokenGithubRoot(t *testing.T) (*Core, *IdentityStore, *TokenStore, string, string) { - is, ghAccessor, core, root := testIdentityStoreWithGithubAuthRoot(t) +func testCoreWithIdentityTokenGithubRoot(ctx context.Context, t *testing.T) (*Core, *IdentityStore, *TokenStore, string, string) { + is, ghAccessor, core, root := testIdentityStoreWithGithubAuthRoot(ctx, t) return core, is, core.tokenStore, ghAccessor, root } -func testIdentityStoreWithGithubAuth(t *testing.T) (*IdentityStore, string, *Core) { - is, ghA, c, _ := testIdentityStoreWithGithubAuthRoot(t) +func testIdentityStoreWithGithubAuth(ctx context.Context, t *testing.T) (*IdentityStore, string, *Core) { + is, ghA, c, _ := testIdentityStoreWithGithubAuthRoot(ctx, t) return is, ghA, c } @@ -435,7 +442,7 @@ func testIdentityStoreWithGithubAuth(t *testing.T) (*IdentityStore, string, *Cor // which is mounted by default. This function also enables the github auth // backend to assist with testing aliases and entities that require an valid // mount accessor of an auth backend. -func testIdentityStoreWithGithubAuthRoot(t *testing.T) (*IdentityStore, string, *Core, string) { +func testIdentityStoreWithGithubAuthRoot(ctx context.Context, t *testing.T) (*IdentityStore, string, *Core, string) { // Add github credential factory to core config err := AddTestCredentialBackend("github", credGithub.Factory) if err != nil { @@ -451,7 +458,7 @@ func testIdentityStoreWithGithubAuthRoot(t *testing.T) (*IdentityStore, string, Description: "github auth", } - err = c.enableCredential(context.Background(), meGH) + err = c.enableCredential(ctx, meGH) if err != nil { t.Fatal(err) } diff --git a/vault/identity_store_util.go b/vault/identity_store_util.go index 668fabd5a4..6102aaa57c 100644 --- a/vault/identity_store_util.go +++ b/vault/identity_store_util.go @@ -12,6 +12,8 @@ import ( uuid "github.com/hashicorp/go-uuid" "github.com/hashicorp/vault/helper/consts" "github.com/hashicorp/vault/helper/identity" + "github.com/hashicorp/vault/helper/identity/mfa" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/helper/storagepacker" "github.com/hashicorp/vault/helper/strutil" "github.com/hashicorp/vault/logical" @@ -186,7 +188,7 @@ func (i *IdentityStore) loadEntities(ctx context.Context) error { } // Only update MemDB and don't hit the storage again - err = i.upsertEntity(entity, nil, false) + err = i.upsertEntity(ctx, entity, nil, false) if err != nil { return errwrap.Wrapf("failed to update entity in MemDB: {{err}}", err) } @@ -210,7 +212,7 @@ func (i *IdentityStore) loadEntities(ctx context.Context) error { // one entity to another, both the source and destination entities should get // updated, in which case, callers should send in both entity and // previousEntity. -func (i *IdentityStore) upsertEntityInTxn(txn *memdb.Txn, entity *identity.Entity, previousEntity *identity.Entity, persist bool) error { +func (i *IdentityStore) upsertEntityInTxn(ctx context.Context, txn *memdb.Txn, entity *identity.Entity, previousEntity *identity.Entity, persist bool) error { var err error if txn == nil { @@ -230,7 +232,7 @@ func (i *IdentityStore) upsertEntityInTxn(txn *memdb.Txn, entity *identity.Entit if aliasByFactors != nil && aliasByFactors.CanonicalID != entity.ID { i.logger.Warn("alias is already tied to a different entity; these entities are being merged", "alias_id", alias.ID, "other_entity_id", aliasByFactors.CanonicalID) - respErr, intErr := i.mergeEntity(txn, entity, []string{aliasByFactors.CanonicalID}, true, false, true) + respErr, intErr := i.mergeEntity(ctx, txn, entity, []string{aliasByFactors.CanonicalID}, true, false, true) switch { case respErr != nil: return respErr @@ -302,13 +304,13 @@ func (i *IdentityStore) upsertEntityInTxn(txn *memdb.Txn, entity *identity.Entit // entity to another, both the source and destination entities should get // updated, in which case, callers should send in both entity and // previousEntity. -func (i *IdentityStore) upsertEntity(entity *identity.Entity, previousEntity *identity.Entity, persist bool) error { +func (i *IdentityStore) upsertEntity(ctx context.Context, entity *identity.Entity, previousEntity *identity.Entity, persist bool) error { // Create a MemDB transaction to update both alias and entity txn := i.db.Txn(true) defer txn.Abort() - err := i.upsertEntityInTxn(txn, entity, previousEntity, persist) + err := i.upsertEntityInTxn(ctx, txn, entity, previousEntity, persist) if err != nil { return err } @@ -327,6 +329,10 @@ func (i *IdentityStore) MemDBUpsertAliasInTxn(txn *memdb.Txn, alias *identity.Al return fmt.Errorf("alias is nil") } + if alias.NamespaceID == "" { + alias.NamespaceID = namespace.RootNamespaceID + } + tableName := entityAliasesTable if groupAlias { tableName = groupAliasesTable @@ -507,6 +513,10 @@ func (i *IdentityStore) MemDBUpsertEntityInTxn(txn *memdb.Txn, entity *identity. return fmt.Errorf("entity is nil") } + if entity.NamespaceID == "" { + entity.NamespaceID = namespace.RootNamespaceID + } + entityRaw, err := txn.First(entitiesTable, "id", entity.ID) if err != nil { return errwrap.Wrapf("failed to lookup entity from memdb using entity id: {{err}}", err) @@ -566,14 +576,19 @@ func (i *IdentityStore) MemDBEntityByID(entityID string, clone bool) (*identity. return i.MemDBEntityByIDInTxn(txn, entityID, clone) } -func (i *IdentityStore) MemDBEntityByName(entityName string, clone bool) (*identity.Entity, error) { +func (i *IdentityStore) MemDBEntityByName(ctx context.Context, entityName string, clone bool) (*identity.Entity, error) { if entityName == "" { return nil, fmt.Errorf("missing entity name") } + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } + txn := i.db.Txn(false) - entityRaw, err := txn.First(entitiesTable, "name", entityName) + entityRaw, err := txn.First(entitiesTable, "name", ns.ID, entityName) if err != nil { return nil, errwrap.Wrapf("failed to fetch entity from memdb using entity name: {{err}}", err) } @@ -719,7 +734,7 @@ func (i *IdentityStore) MemDBDeleteEntityByIDInTxn(txn *memdb.Txn, entityID stri return nil } -func (i *IdentityStore) sanitizeAlias(alias *identity.Alias) error { +func (i *IdentityStore) sanitizeAlias(ctx context.Context, alias *identity.Alias) error { var err error if alias == nil { @@ -750,6 +765,22 @@ func (i *IdentityStore) sanitizeAlias(alias *identity.Alias) error { } } + if alias.NamespaceID == "" { + ns, err := namespace.FromContext(ctx) + if err != nil { + return err + } + alias.NamespaceID = ns.ID + } + + ns, err := namespace.FromContext(ctx) + if err != nil { + return err + } + if ns.ID != alias.NamespaceID { + return fmt.Errorf("alias belongs to a different namespace") + } + // Set the creation and last update times if alias.CreationTime == nil { alias.CreationTime = ptypes.TimestampNow() @@ -761,7 +792,7 @@ func (i *IdentityStore) sanitizeAlias(alias *identity.Alias) error { return nil } -func (i *IdentityStore) sanitizeEntity(entity *identity.Entity) error { +func (i *IdentityStore) sanitizeEntity(ctx context.Context, entity *identity.Entity) error { var err error if entity == nil { @@ -779,9 +810,20 @@ func (i *IdentityStore) sanitizeEntity(entity *identity.Entity) error { entity.BucketKeyHash = i.entityPacker.BucketKeyHashByItemID(entity.ID) } + ns, err := namespace.FromContext(ctx) + if err != nil { + return err + } + if entity.NamespaceID == "" { + entity.NamespaceID = ns.ID + } + if ns.ID != entity.NamespaceID { + return fmt.Errorf("entity does not belong to this namespace") + } + // Create a name if there isn't one already if entity.Name == "" { - entity.Name, err = i.generateName("entity") + entity.Name, err = i.generateName(ctx, "entity") if err != nil { return fmt.Errorf("failed to generate entity name") } @@ -801,10 +843,16 @@ func (i *IdentityStore) sanitizeEntity(entity *identity.Entity) error { entity.LastUpdateTime = ptypes.TimestampNow() } + // Ensure that MFASecrets is non-nil at any time. This is useful when MFA + // secret generation procedures try to append MFA info to entity. + if entity.MFASecrets == nil { + entity.MFASecrets = make(map[string]*mfa.Secret) + } + return nil } -func (i *IdentityStore) sanitizeAndUpsertGroup(group *identity.Group, memberGroupIDs []string) error { +func (i *IdentityStore) sanitizeAndUpsertGroup(ctx context.Context, group *identity.Group, memberGroupIDs []string) error { var err error if group == nil { @@ -822,9 +870,24 @@ func (i *IdentityStore) sanitizeAndUpsertGroup(group *identity.Group, memberGrou group.BucketKeyHash = i.groupPacker.BucketKeyHashByItemID(group.ID) } + if group.NamespaceID == "" { + ns, err := namespace.FromContext(ctx) + if err != nil { + return err + } + group.NamespaceID = ns.ID + } + ns, err := namespace.FromContext(ctx) + if err != nil { + return err + } + if ns.ID != group.NamespaceID { + return fmt.Errorf("group does not belong to this namespace") + } + // Create a name if there isn't one already if group.Name == "" { - group.Name, err = i.generateName("group") + group.Name, err = i.generateName(ctx, "group") if err != nil { return fmt.Errorf("failed to generate group name") } @@ -923,7 +986,7 @@ func (i *IdentityStore) sanitizeAndUpsertGroup(group *identity.Group, memberGrou // Sanitize the group alias if group.Alias != nil { group.Alias.CanonicalID = group.ID - err = i.sanitizeAlias(group.Alias) + err = i.sanitizeAlias(ctx, group.Alias) if err != nil { return err } @@ -1018,7 +1081,7 @@ func validateMetaPair(key, value string) error { return nil } -func (i *IdentityStore) MemDBGroupByNameInTxn(txn *memdb.Txn, groupName string, clone bool) (*identity.Group, error) { +func (i *IdentityStore) MemDBGroupByNameInTxn(ctx context.Context, txn *memdb.Txn, groupName string, clone bool) (*identity.Group, error) { if groupName == "" { return nil, fmt.Errorf("missing group name") } @@ -1027,7 +1090,12 @@ func (i *IdentityStore) MemDBGroupByNameInTxn(txn *memdb.Txn, groupName string, return nil, fmt.Errorf("txn is nil") } - groupRaw, err := txn.First(groupsTable, "name", groupName) + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } + + groupRaw, err := txn.First(groupsTable, "name", ns.ID, groupName) if err != nil { return nil, errwrap.Wrapf("failed to fetch group from memdb using group name: {{err}}", err) } @@ -1048,14 +1116,14 @@ func (i *IdentityStore) MemDBGroupByNameInTxn(txn *memdb.Txn, groupName string, return group, nil } -func (i *IdentityStore) MemDBGroupByName(groupName string, clone bool) (*identity.Group, error) { +func (i *IdentityStore) MemDBGroupByName(ctx context.Context, groupName string, clone bool) (*identity.Group, error) { if groupName == "" { return nil, fmt.Errorf("missing group name") } txn := i.db.Txn(false) - return i.MemDBGroupByNameInTxn(txn, groupName, clone) + return i.MemDBGroupByNameInTxn(ctx, txn, groupName, clone) } func (i *IdentityStore) UpsertGroup(group *identity.Group, persist bool) error { @@ -1092,6 +1160,7 @@ func (i *IdentityStore) UpsertGroupInTxn(txn *memdb.Txn, group *identity.Group, return err } } + // Insert or update group in MemDB using the transaction created above err = i.MemDBUpsertGroupInTxn(txn, group) if err != nil { @@ -1109,10 +1178,15 @@ func (i *IdentityStore) UpsertGroupInTxn(txn *memdb.Txn, group *identity.Group, Message: groupAsAny, } - err = i.groupPacker.PutItem(item) + sent, err := sendGroupUpgrade(i, group) if err != nil { return err } + if !sent { + if err := i.groupPacker.PutItem(item); err != nil { + return err + } + } } return nil @@ -1127,6 +1201,10 @@ func (i *IdentityStore) MemDBUpsertGroupInTxn(txn *memdb.Txn, group *identity.Gr return fmt.Errorf("group is nil") } + if group.NamespaceID == "" { + group.NamespaceID = namespace.RootNamespaceID + } + groupRaw, err := txn.First(groupsTable, "id", group.ID) if err != nil { return errwrap.Wrapf("failed to lookup group from memdb using group id: {{err}}", err) @@ -1282,7 +1360,7 @@ func (i *IdentityStore) MemDBGroupsByMemberEntityIDInTxn(txn *memdb.Txn, entityI return groups, nil } -func (i *IdentityStore) groupPoliciesByEntityID(entityID string) ([]string, error) { +func (i *IdentityStore) groupPoliciesByEntityID(entityID string) (map[string][]string, error) { if entityID == "" { return nil, fmt.Errorf("empty entity ID") } @@ -1293,16 +1371,15 @@ func (i *IdentityStore) groupPoliciesByEntityID(entityID string) ([]string, erro } visited := make(map[string]bool) - var policies []string + policies := make(map[string][]string) for _, group := range groups { - groupPolicies, err := i.collectPoliciesReverseDFS(group, visited, nil) + err := i.collectPoliciesReverseDFS(group, visited, policies) if err != nil { return nil, err } - policies = append(policies, groupPolicies...) } - return strutil.RemoveDuplicates(policies, false), nil + return policies, nil } func (i *IdentityStore) groupsByEntityID(entityID string) ([]*identity.Group, []*identity.Group, error) { @@ -1379,36 +1456,35 @@ func (i *IdentityStore) collectGroupsReverseDFS(group *identity.Group, visited m return groups, nil } -func (i *IdentityStore) collectPoliciesReverseDFS(group *identity.Group, visited map[string]bool, policies []string) ([]string, error) { +func (i *IdentityStore) collectPoliciesReverseDFS(group *identity.Group, visited map[string]bool, policies map[string][]string) error { if group == nil { - return nil, fmt.Errorf("nil group") + return fmt.Errorf("nil group") } // If traversal for a groupID is performed before, skip it if visited[group.ID] { - return policies, nil + return nil } visited[group.ID] = true - policies = append(policies, group.Policies...) + policies[group.NamespaceID] = append(policies[group.NamespaceID], group.Policies...) // Traverse all the parent groups for _, parentGroupID := range group.ParentGroupIDs { parentGroup, err := i.MemDBGroupByID(parentGroupID, false) if err != nil { - return nil, err + return err } if parentGroup == nil { continue } - parentPolicies, err := i.collectPoliciesReverseDFS(parentGroup, visited, policies) + err = i.collectPoliciesReverseDFS(parentGroup, visited, policies) if err != nil { - return nil, fmt.Errorf("failed to collect policies at parent group ID %q", parentGroup.ID) + return fmt.Errorf("failed to collect policies at parent group ID %q", parentGroup.ID) } - policies = append(policies, parentPolicies...) } - return strutil.RemoveDuplicates(policies, false), nil + return nil } func (i *IdentityStore) detectCycleDFS(visited map[string]bool, startingGroupID, groupID string) (bool, error) { @@ -1464,7 +1540,7 @@ func (i *IdentityStore) memberGroupIDsByID(groupID string) ([]string, error) { return memberGroupIDs, nil } -func (i *IdentityStore) generateName(entryType string) (string, error) { +func (i *IdentityStore) generateName(ctx context.Context, entryType string) (string, error) { var name string OUTER: for { @@ -1476,7 +1552,7 @@ OUTER: switch entryType { case "entity": - entity, err := i.MemDBEntityByName(name, false) + entity, err := i.MemDBEntityByName(ctx, name, false) if err != nil { return "", err } @@ -1484,7 +1560,7 @@ OUTER: break OUTER } case "group": - group, err := i.MemDBGroupByName(name, false) + group, err := i.MemDBGroupByName(ctx, name, false) if err != nil { return "", err } @@ -1553,6 +1629,7 @@ func (i *IdentityStore) MemDBGroupByAliasID(aliasID string, clone bool) (*identi } func (i *IdentityStore) refreshExternalGroupMembershipsByEntityID(entityID string, groupAliases []*logical.Alias) ([]*logical.Alias, error) { + i.logger.Debug("refreshing external group memberships", "entity_id", entityID, "group_aliases", groupAliases) if entityID == "" { return nil, fmt.Errorf("empty entity ID") } @@ -1590,6 +1667,7 @@ func (i *IdentityStore) refreshExternalGroupMembershipsByEntityID(entityID strin if mappingGroup == nil { return nil, fmt.Errorf("group unavailable for a valid alias ID %q", aliasByFactors.ID) } + newGroups = append(newGroups, mappingGroup) validAliases = append(validAliases, alias) } @@ -1673,3 +1751,68 @@ func diffGroups(old, new []*identity.Group) *groupDiff { return diff } + +func (i *IdentityStore) handleAliasListCommon(ctx context.Context, groupAlias bool) (*logical.Response, error) { + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } + + tableName := entityAliasesTable + if groupAlias { + tableName = groupAliasesTable + } + + ws := memdb.NewWatchSet() + + txn := i.db.Txn(false) + + iter, err := txn.Get(tableName, "namespace_id", ns.ID) + if err != nil { + return nil, errwrap.Wrapf("failed to fetch iterator for aliases in memdb: {{err}}", err) + } + + ws.Add(iter.WatchCh()) + + var aliasIDs []string + aliasInfo := map[string]interface{}{} + + type mountInfo struct { + MountType string + MountPath string + } + mountAccessorMap := map[string]mountInfo{} + + for { + raw := iter.Next() + if raw == nil { + break + } + alias := raw.(*identity.Alias) + aliasIDs = append(aliasIDs, alias.ID) + aliasInfoEntry := map[string]interface{}{ + "name": alias.Name, + "canonical_id": alias.CanonicalID, + "mount_accessor": alias.MountAccessor, + } + + mi, ok := mountAccessorMap[alias.MountAccessor] + if ok { + aliasInfoEntry["mount_type"] = mi.MountType + aliasInfoEntry["mount_path"] = mi.MountPath + } else { + mi = mountInfo{} + if mountValidationResp := i.core.router.validateMountByAccessor(alias.MountAccessor); mountValidationResp != nil { + mi.MountType = mountValidationResp.MountType + mi.MountPath = mountValidationResp.MountPath + aliasInfoEntry["mount_type"] = mi.MountType + aliasInfoEntry["mount_path"] = mi.MountPath + } + mountAccessorMap[alias.MountAccessor] = mi + } + + aliasInfo[alias.ID] = aliasInfoEntry + } + + return logical.ListResponseWithInfo(aliasIDs, aliasInfo), nil +} diff --git a/vault/init.go b/vault/init.go index d6b7de4e73..ddd127361d 100644 --- a/vault/init.go +++ b/vault/init.go @@ -7,6 +7,7 @@ import ( "fmt" "github.com/hashicorp/errwrap" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/helper/pgpkeys" "github.com/hashicorp/vault/shamir" ) @@ -27,6 +28,10 @@ type InitResult struct { RootToken string } +var ( + initPTFunc = func(c *Core) func() { return nil } +) + // Initialized checks if the Vault is already initialized func (c *Core) Initialized(ctx context.Context) (bool, error) { // Check the barrier first @@ -141,6 +146,11 @@ func (c *Core) Initialize(ctx context.Context, initParams *InitParams) (*InitRes return nil, err } + initPTCleanup := initPTFunc(c) + if initPTCleanup != nil { + defer initPTCleanup() + } + // Initialize the barrier if err := c.barrier.Initialize(ctx, barrierKey); err != nil { c.logger.Error("failed to initialize barrier", "error", err) @@ -196,8 +206,13 @@ func (c *Core) Initialize(ctx context.Context, initParams *InitParams) (*InitRes return nil, err } - activeCtx, ctxCancel := context.WithCancel(context.Background()) - if err := c.postUnseal(activeCtx, ctxCancel); err != nil { + // Start tracking + if initPTCleanup != nil { + initPTCleanup() + } + + activeCtx, ctxCancel := context.WithCancel(namespace.RootContext(nil)) + if err := c.postUnseal(activeCtx, ctxCancel, standardUnsealStrategy{}); err != nil { c.logger.Error("post-unseal setup failed during init", "error", err) return nil, err } diff --git a/vault/init_test.go b/vault/init_test.go index 8ed537da1a..b865f7c988 100644 --- a/vault/init_test.go +++ b/vault/init_test.go @@ -15,13 +15,13 @@ import ( func TestCore_Init(t *testing.T) { c, conf := testCore_NewTestCore(t, nil) testCore_Init_Common(t, c, conf, &SealConfig{SecretShares: 5, SecretThreshold: 3}, nil) - - c, conf = testCore_NewTestCore(t, NewTestSeal(t, nil)) - bc, _ := TestSealDefConfigs() - testCore_Init_Common(t, c, conf, bc, nil) } func testCore_NewTestCore(t *testing.T, seal Seal) (*Core, *CoreConfig) { + return testCore_NewTestCoreLicensing(t, seal, nil) +} + +func testCore_NewTestCoreLicensing(t *testing.T, seal Seal, licensingConfig *LicensingConfig) (*Core, *CoreConfig) { logger := logging.NewVaultLogger(log.Trace) inm, err := inmem.NewInmem(nil, logger) @@ -34,7 +34,8 @@ func testCore_NewTestCore(t *testing.T, seal Seal) (*Core, *CoreConfig) { LogicalBackends: map[string]logical.Factory{ "kv": LeasedPassthroughBackendFactory, }, - Seal: seal, + Seal: seal, + LicensingConfig: licensingConfig, } c, err := NewCore(conf) if err != nil { diff --git a/vault/logical_cubbyhole.go b/vault/logical_cubbyhole.go index 08055ddb38..fe4670ed0d 100644 --- a/vault/logical_cubbyhole.go +++ b/vault/logical_cubbyhole.go @@ -14,36 +14,19 @@ import ( // CubbyholeBackendFactory constructs a new cubbyhole backend func CubbyholeBackendFactory(ctx context.Context, conf *logical.BackendConfig) (logical.Backend, error) { - var b CubbyholeBackend + b := &CubbyholeBackend{} b.Backend = &framework.Backend{ Help: strings.TrimSpace(cubbyholeHelp), - - Paths: []*framework.Path{ - &framework.Path{ - Pattern: ".*", - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ReadOperation: b.handleRead, - logical.CreateOperation: b.handleWrite, - logical.UpdateOperation: b.handleWrite, - logical.DeleteOperation: b.handleDelete, - logical.ListOperation: b.handleList, - }, - - ExistenceCheck: b.handleExistenceCheck, - - HelpSynopsis: strings.TrimSpace(cubbyholeHelpSynopsis), - HelpDescription: strings.TrimSpace(cubbyholeHelpDescription), - }, - }, } + b.Backend.Paths = append(b.Backend.Paths, b.paths()...) + if conf == nil { return nil, fmt.Errorf("configuration passed into backend is nil") } b.Backend.Setup(ctx, conf) - return &b, nil + return b, nil } // CubbyholeBackend is used for storing secrets directly into the physical @@ -57,6 +40,27 @@ type CubbyholeBackend struct { storageView logical.Storage } +func (b *CubbyholeBackend) paths() []*framework.Path { + return []*framework.Path{ + { + Pattern: ".*", + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.ReadOperation: b.handleRead, + logical.CreateOperation: b.handleWrite, + logical.UpdateOperation: b.handleWrite, + logical.DeleteOperation: b.handleDelete, + logical.ListOperation: b.handleList, + }, + + ExistenceCheck: b.handleExistenceCheck, + + HelpSynopsis: strings.TrimSpace(cubbyholeHelpSynopsis), + HelpDescription: strings.TrimSpace(cubbyholeHelpDescription), + }, + } +} + func (b *CubbyholeBackend) revoke(ctx context.Context, saltedToken string) error { if saltedToken == "" { return fmt.Errorf("client token empty during revocation") diff --git a/vault/logical_passthrough.go b/vault/logical_passthrough.go index 3a40da2515..aacd2e0337 100644 --- a/vault/logical_passthrough.go +++ b/vault/logical_passthrough.go @@ -41,7 +41,7 @@ func LeaseSwitchedPassthroughBackend(ctx context.Context, conf *logical.BackendC }, Paths: []*framework.Path{ - &framework.Path{ + { Pattern: ".*", Callbacks: map[logical.Operation]framework.OperationFunc{ diff --git a/vault/logical_system.go b/vault/logical_system.go index a39bb61785..db1cd193fc 100644 --- a/vault/logical_system.go +++ b/vault/logical_system.go @@ -19,11 +19,13 @@ import ( "github.com/hashicorp/errwrap" log "github.com/hashicorp/go-hclog" + memdb "github.com/hashicorp/go-memdb" uuid "github.com/hashicorp/go-uuid" "github.com/hashicorp/vault/helper/compressutil" "github.com/hashicorp/vault/helper/consts" "github.com/hashicorp/vault/helper/identity" "github.com/hashicorp/vault/helper/jsonutil" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/helper/parseutil" "github.com/hashicorp/vault/helper/strutil" "github.com/hashicorp/vault/helper/wrapping" @@ -37,34 +39,42 @@ var ( // This is both for security and to prevent disrupting Vault. protectedPaths = []string{ keyringPath, + // Changing the cluster info path can change the cluster ID which can be disruptive coreLocalClusterInfoPath, } - - replicationPaths = func(b *SystemBackend) []*framework.Path { - return []*framework.Path{ - &framework.Path{ - Pattern: "replication/status", - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ReadOperation: func(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { - resp := &logical.Response{ - Data: map[string]interface{}{ - "mode": "disabled", - }, - } - return resp, nil - }, - }, - }, - } - } ) -func NewSystemBackend(core *Core, logger log.Logger) *SystemBackend { - b := &SystemBackend{ - Core: core, - logger: logger, +func systemBackendMemDBSchema() *memdb.DBSchema { + systemSchema := &memdb.DBSchema{ + Tables: make(map[string]*memdb.TableSchema), } + schemas := getSystemSchemas() + + for _, schemaFunc := range schemas { + schema := schemaFunc() + if _, ok := systemSchema.Tables[schema.Name]; ok { + panic(fmt.Sprintf("duplicate table name: %s", schema.Name)) + } + systemSchema.Tables[schema.Name] = schema + } + + return systemSchema +} + +func NewSystemBackend(core *Core, logger log.Logger) *SystemBackend { + db, _ := memdb.NewMemDB(systemBackendMemDBSchema()) + + b := &SystemBackend{ + Core: core, + db: db, + logger: logger, + mfaLogger: core.baseLogger.Named("mfa"), + mfaLock: &sync.RWMutex{}, + } + + core.AddLogger(b.mfaLogger) + b.Backend = &framework.Backend{ Help: strings.TrimSpace(sysHelpRoot), @@ -77,7 +87,11 @@ func NewSystemBackend(core *Core, logger log.Logger) *SystemBackend { "raw", "raw/*", "replication/primary/secondary-token", + "replication/performance/primary/secondary-token", + "replication/dr/primary/secondary-token", "replication/reindex", + "replication/dr/reindex", + "replication/performance/reindex", "rotate", "config/cors", "config/auditing/*", @@ -96,1025 +110,39 @@ func NewSystemBackend(core *Core, logger log.Logger) *SystemBackend { "replication/status", "internal/ui/mounts", "internal/ui/mounts/*", - }, - }, - - Paths: []*framework.Path{ - &framework.Path{ - Pattern: "capabilities-accessor$", - - Fields: map[string]*framework.FieldSchema{ - "accessor": &framework.FieldSchema{ - Type: framework.TypeString, - Description: "Accessor of the token for which capabilities are being queried.", - }, - "path": &framework.FieldSchema{ - Type: framework.TypeCommaStringSlice, - Description: "(DEPRECATED) Path on which capabilities are being queried. Use 'paths' instead.", - }, - "paths": &framework.FieldSchema{ - Type: framework.TypeCommaStringSlice, - Description: "Paths on which capabilities are being queried.", - }, - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: b.handleCapabilitiesAccessor, - }, - - HelpSynopsis: strings.TrimSpace(sysHelp["capabilities_accessor"][0]), - HelpDescription: strings.TrimSpace(sysHelp["capabilities_accessor"][1]), + "internal/ui/namespaces", + "replication/performance/status", + "replication/dr/status", + "replication/dr/secondary/promote", + "replication/dr/secondary/update-primary", + "replication/dr/secondary/operation-token/delete", + "replication/dr/secondary/license", + "replication/dr/secondary/reindex", }, - &framework.Path{ - Pattern: "config/cors$", - - Fields: map[string]*framework.FieldSchema{ - "enable": &framework.FieldSchema{ - Type: framework.TypeBool, - Description: "Enables or disables CORS headers on requests.", - }, - "allowed_origins": &framework.FieldSchema{ - Type: framework.TypeCommaStringSlice, - Description: "A comma-separated string or array of strings indicating origins that may make cross-origin requests.", - }, - "allowed_headers": &framework.FieldSchema{ - Type: framework.TypeCommaStringSlice, - Description: "A comma-separated string or array of strings indicating headers that are allowed on cross-origin requests.", - }, - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ReadOperation: b.handleCORSRead, - logical.UpdateOperation: b.handleCORSUpdate, - logical.DeleteOperation: b.handleCORSDelete, - }, - - HelpDescription: strings.TrimSpace(sysHelp["config/cors"][0]), - HelpSynopsis: strings.TrimSpace(sysHelp["config/cors"][1]), - }, - - &framework.Path{ - Pattern: "config/ui/headers/" + framework.GenericNameRegex("header"), - - Fields: map[string]*framework.FieldSchema{ - "header": &framework.FieldSchema{ - Type: framework.TypeString, - Description: "The name of the header.", - }, - "values": &framework.FieldSchema{ - Type: framework.TypeStringSlice, - Description: "The values to set the header.", - }, - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ReadOperation: b.handleConfigUIHeadersRead, - logical.UpdateOperation: b.handleConfigUIHeadersUpdate, - logical.DeleteOperation: b.handleConfigUIHeadersDelete, - }, - - HelpDescription: strings.TrimSpace(sysHelp["config/ui/headers"][0]), - HelpSynopsis: strings.TrimSpace(sysHelp["config/ui/headers"][1]), - }, - - &framework.Path{ - Pattern: "config/ui/headers/$", - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ListOperation: b.handleConfigUIHeadersList, - }, - - HelpDescription: strings.TrimSpace(sysHelp["config/ui/headers"][0]), - HelpSynopsis: strings.TrimSpace(sysHelp["config/ui/headers"][1]), - }, - - &framework.Path{ - Pattern: "capabilities$", - - Fields: map[string]*framework.FieldSchema{ - "token": &framework.FieldSchema{ - Type: framework.TypeString, - Description: "Token for which capabilities are being queried.", - }, - "path": &framework.FieldSchema{ - Type: framework.TypeCommaStringSlice, - Description: "(DEPRECATED) Path on which capabilities are being queried. Use 'paths' instead.", - }, - "paths": &framework.FieldSchema{ - Type: framework.TypeCommaStringSlice, - Description: "Paths on which capabilities are being queried.", - }, - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: b.handleCapabilities, - }, - - HelpSynopsis: strings.TrimSpace(sysHelp["capabilities"][0]), - HelpDescription: strings.TrimSpace(sysHelp["capabilities"][1]), - }, - - &framework.Path{ - Pattern: "capabilities-self$", - - Fields: map[string]*framework.FieldSchema{ - "token": &framework.FieldSchema{ - Type: framework.TypeString, - Description: "Token for which capabilities are being queried.", - }, - "path": &framework.FieldSchema{ - Type: framework.TypeCommaStringSlice, - Description: "(DEPRECATED) Path on which capabilities are being queried. Use 'paths' instead.", - }, - "paths": &framework.FieldSchema{ - Type: framework.TypeCommaStringSlice, - Description: "Paths on which capabilities are being queried.", - }, - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: b.handleCapabilities, - }, - - HelpSynopsis: strings.TrimSpace(sysHelp["capabilities_self"][0]), - HelpDescription: strings.TrimSpace(sysHelp["capabilities_self"][1]), - }, - - &framework.Path{ - Pattern: "generate-root(/attempt)?$", - HelpSynopsis: strings.TrimSpace(sysHelp["generate-root"][0]), - HelpDescription: strings.TrimSpace(sysHelp["generate-root"][1]), - }, - - &framework.Path{ - Pattern: "init$", - HelpSynopsis: strings.TrimSpace(sysHelp["init"][0]), - HelpDescription: strings.TrimSpace(sysHelp["init"][1]), - }, - - &framework.Path{ - Pattern: "rekey/backup$", - - Fields: map[string]*framework.FieldSchema{}, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ReadOperation: b.handleRekeyRetrieveBarrier, - logical.DeleteOperation: b.handleRekeyDeleteBarrier, - }, - - HelpSynopsis: strings.TrimSpace(sysHelp["rekey_backup"][0]), - HelpDescription: strings.TrimSpace(sysHelp["rekey_backup"][0]), - }, - - &framework.Path{ - Pattern: "rekey/recovery-key-backup$", - - Fields: map[string]*framework.FieldSchema{}, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ReadOperation: b.handleRekeyRetrieveRecovery, - logical.DeleteOperation: b.handleRekeyDeleteRecovery, - }, - - HelpSynopsis: strings.TrimSpace(sysHelp["rekey_backup"][0]), - HelpDescription: strings.TrimSpace(sysHelp["rekey_backup"][0]), - }, - - &framework.Path{ - Pattern: "auth/(?P.+?)/tune$", - Fields: map[string]*framework.FieldSchema{ - "path": &framework.FieldSchema{ - Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["auth_tune"][0]), - }, - "default_lease_ttl": &framework.FieldSchema{ - Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["tune_default_lease_ttl"][0]), - }, - "max_lease_ttl": &framework.FieldSchema{ - Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["tune_max_lease_ttl"][0]), - }, - "description": &framework.FieldSchema{ - Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["auth_desc"][0]), - }, - "audit_non_hmac_request_keys": &framework.FieldSchema{ - Type: framework.TypeCommaStringSlice, - Description: strings.TrimSpace(sysHelp["tune_audit_non_hmac_request_keys"][0]), - }, - "audit_non_hmac_response_keys": &framework.FieldSchema{ - Type: framework.TypeCommaStringSlice, - Description: strings.TrimSpace(sysHelp["tune_audit_non_hmac_response_keys"][0]), - }, - "options": &framework.FieldSchema{ - Type: framework.TypeKVPairs, - Description: strings.TrimSpace(sysHelp["tune_mount_options"][0]), - }, - "listing_visibility": &framework.FieldSchema{ - Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["listing_visibility"][0]), - }, - "passthrough_request_headers": &framework.FieldSchema{ - Type: framework.TypeCommaStringSlice, - Description: strings.TrimSpace(sysHelp["passthrough_request_headers"][0]), - }, - }, - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ReadOperation: b.handleAuthTuneRead, - logical.UpdateOperation: b.handleAuthTuneWrite, - }, - HelpSynopsis: strings.TrimSpace(sysHelp["auth_tune"][0]), - HelpDescription: strings.TrimSpace(sysHelp["auth_tune"][1]), - }, - - &framework.Path{ - Pattern: "mounts/(?P.+?)/tune$", - - Fields: map[string]*framework.FieldSchema{ - "path": &framework.FieldSchema{ - Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["mount_path"][0]), - }, - "default_lease_ttl": &framework.FieldSchema{ - Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["tune_default_lease_ttl"][0]), - }, - "max_lease_ttl": &framework.FieldSchema{ - Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["tune_max_lease_ttl"][0]), - }, - "description": &framework.FieldSchema{ - Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["auth_desc"][0]), - }, - "audit_non_hmac_request_keys": &framework.FieldSchema{ - Type: framework.TypeCommaStringSlice, - Description: strings.TrimSpace(sysHelp["tune_audit_non_hmac_request_keys"][0]), - }, - "audit_non_hmac_response_keys": &framework.FieldSchema{ - Type: framework.TypeCommaStringSlice, - Description: strings.TrimSpace(sysHelp["tune_audit_non_hmac_response_keys"][0]), - }, - "options": &framework.FieldSchema{ - Type: framework.TypeKVPairs, - Description: strings.TrimSpace(sysHelp["tune_mount_options"][0]), - }, - "listing_visibility": &framework.FieldSchema{ - Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["listing_visibility"][0]), - }, - "passthrough_request_headers": &framework.FieldSchema{ - Type: framework.TypeCommaStringSlice, - Description: strings.TrimSpace(sysHelp["passthrough_request_headers"][0]), - }, - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ReadOperation: b.handleMountTuneRead, - logical.UpdateOperation: b.handleMountTuneWrite, - }, - - HelpSynopsis: strings.TrimSpace(sysHelp["mount_tune"][0]), - HelpDescription: strings.TrimSpace(sysHelp["mount_tune"][1]), - }, - - &framework.Path{ - Pattern: "mounts/(?P.+?)", - - Fields: map[string]*framework.FieldSchema{ - "path": &framework.FieldSchema{ - Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["mount_path"][0]), - }, - "type": &framework.FieldSchema{ - Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["mount_type"][0]), - }, - "description": &framework.FieldSchema{ - Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["mount_desc"][0]), - }, - "config": &framework.FieldSchema{ - Type: framework.TypeMap, - Description: strings.TrimSpace(sysHelp["mount_config"][0]), - }, - "local": &framework.FieldSchema{ - Type: framework.TypeBool, - Default: false, - Description: strings.TrimSpace(sysHelp["mount_local"][0]), - }, - "seal_wrap": &framework.FieldSchema{ - Type: framework.TypeBool, - Default: false, - Description: strings.TrimSpace(sysHelp["seal_wrap"][0]), - }, - "plugin_name": &framework.FieldSchema{ - Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["mount_plugin_name"][0]), - }, - "options": &framework.FieldSchema{ - Type: framework.TypeKVPairs, - Description: strings.TrimSpace(sysHelp["mount_options"][0]), - }, - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: b.handleMount, - logical.DeleteOperation: b.handleUnmount, - }, - - HelpSynopsis: strings.TrimSpace(sysHelp["mount"][0]), - HelpDescription: strings.TrimSpace(sysHelp["mount"][1]), - }, - - &framework.Path{ - Pattern: "mounts$", - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ReadOperation: b.handleMountTable, - }, - - HelpSynopsis: strings.TrimSpace(sysHelp["mounts"][0]), - HelpDescription: strings.TrimSpace(sysHelp["mounts"][1]), - }, - - &framework.Path{ - Pattern: "remount", - - Fields: map[string]*framework.FieldSchema{ - "from": &framework.FieldSchema{ - Type: framework.TypeString, - Description: "The previous mount point.", - }, - "to": &framework.FieldSchema{ - Type: framework.TypeString, - Description: "The new mount point.", - }, - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: b.handleRemount, - }, - - HelpSynopsis: strings.TrimSpace(sysHelp["remount"][0]), - HelpDescription: strings.TrimSpace(sysHelp["remount"][1]), - }, - - &framework.Path{ - Pattern: "leases/lookup/(?P.+?)?", - - Fields: map[string]*framework.FieldSchema{ - "prefix": &framework.FieldSchema{ - Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["leases-list-prefix"][0]), - }, - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ListOperation: b.handleLeaseLookupList, - }, - - HelpSynopsis: strings.TrimSpace(sysHelp["leases"][0]), - HelpDescription: strings.TrimSpace(sysHelp["leases"][1]), - }, - - &framework.Path{ - Pattern: "leases/lookup", - - Fields: map[string]*framework.FieldSchema{ - "lease_id": &framework.FieldSchema{ - Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["lease_id"][0]), - }, - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: b.handleLeaseLookup, - }, - - HelpSynopsis: strings.TrimSpace(sysHelp["leases"][0]), - HelpDescription: strings.TrimSpace(sysHelp["leases"][1]), - }, - - &framework.Path{ - Pattern: "(leases/)?renew" + framework.OptionalParamRegex("url_lease_id"), - - Fields: map[string]*framework.FieldSchema{ - "url_lease_id": &framework.FieldSchema{ - Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["lease_id"][0]), - }, - "lease_id": &framework.FieldSchema{ - Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["lease_id"][0]), - }, - "increment": &framework.FieldSchema{ - Type: framework.TypeDurationSecond, - Description: strings.TrimSpace(sysHelp["increment"][0]), - }, - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: b.handleRenew, - }, - - HelpSynopsis: strings.TrimSpace(sysHelp["renew"][0]), - HelpDescription: strings.TrimSpace(sysHelp["renew"][1]), - }, - - &framework.Path{ - Pattern: "(leases/)?revoke" + framework.OptionalParamRegex("url_lease_id"), - - Fields: map[string]*framework.FieldSchema{ - "url_lease_id": &framework.FieldSchema{ - Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["lease_id"][0]), - }, - "lease_id": &framework.FieldSchema{ - Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["lease_id"][0]), - }, - "sync": &framework.FieldSchema{ - Type: framework.TypeBool, - Default: true, - Description: strings.TrimSpace(sysHelp["revoke-sync"][0]), - }, - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: b.handleRevoke, - }, - - HelpSynopsis: strings.TrimSpace(sysHelp["revoke"][0]), - HelpDescription: strings.TrimSpace(sysHelp["revoke"][1]), - }, - - &framework.Path{ - Pattern: "(leases/)?revoke-force/(?P.+)", - - Fields: map[string]*framework.FieldSchema{ - "prefix": &framework.FieldSchema{ - Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["revoke-force-path"][0]), - }, - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: b.handleRevokeForce, - }, - - HelpSynopsis: strings.TrimSpace(sysHelp["revoke-force"][0]), - HelpDescription: strings.TrimSpace(sysHelp["revoke-force"][1]), - }, - - &framework.Path{ - Pattern: "(leases/)?revoke-prefix/(?P.+)", - - Fields: map[string]*framework.FieldSchema{ - "prefix": &framework.FieldSchema{ - Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["revoke-prefix-path"][0]), - }, - "sync": &framework.FieldSchema{ - Type: framework.TypeBool, - Default: true, - Description: strings.TrimSpace(sysHelp["revoke-sync"][0]), - }, - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: b.handleRevokePrefix, - }, - - HelpSynopsis: strings.TrimSpace(sysHelp["revoke-prefix"][0]), - HelpDescription: strings.TrimSpace(sysHelp["revoke-prefix"][1]), - }, - - &framework.Path{ - Pattern: "leases/tidy$", - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: b.handleTidyLeases, - }, - - HelpSynopsis: strings.TrimSpace(sysHelp["tidy_leases"][0]), - HelpDescription: strings.TrimSpace(sysHelp["tidy_leases"][1]), - }, - - &framework.Path{ - Pattern: "auth$", - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ReadOperation: b.handleAuthTable, - }, - - HelpSynopsis: strings.TrimSpace(sysHelp["auth-table"][0]), - HelpDescription: strings.TrimSpace(sysHelp["auth-table"][1]), - }, - - &framework.Path{ - Pattern: "auth/(?P.+)", - - Fields: map[string]*framework.FieldSchema{ - "path": &framework.FieldSchema{ - Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["auth_path"][0]), - }, - "type": &framework.FieldSchema{ - Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["auth_type"][0]), - }, - "description": &framework.FieldSchema{ - Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["auth_desc"][0]), - }, - "config": &framework.FieldSchema{ - Type: framework.TypeMap, - Description: strings.TrimSpace(sysHelp["auth_config"][0]), - }, - "local": &framework.FieldSchema{ - Type: framework.TypeBool, - Default: false, - Description: strings.TrimSpace(sysHelp["mount_local"][0]), - }, - "seal_wrap": &framework.FieldSchema{ - Type: framework.TypeBool, - Default: false, - Description: strings.TrimSpace(sysHelp["seal_wrap"][0]), - }, - "plugin_name": &framework.FieldSchema{ - Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["auth_plugin"][0]), - }, - "options": &framework.FieldSchema{ - Type: framework.TypeKVPairs, - Description: strings.TrimSpace(sysHelp["auth_options"][0]), - }, - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: b.handleEnableAuth, - logical.DeleteOperation: b.handleDisableAuth, - }, - - HelpSynopsis: strings.TrimSpace(sysHelp["auth"][0]), - HelpDescription: strings.TrimSpace(sysHelp["auth"][1]), - }, - - &framework.Path{ - Pattern: "policy/?$", - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ReadOperation: b.handlePoliciesList(PolicyTypeACL), - logical.ListOperation: b.handlePoliciesList(PolicyTypeACL), - }, - - HelpSynopsis: strings.TrimSpace(sysHelp["policy-list"][0]), - HelpDescription: strings.TrimSpace(sysHelp["policy-list"][1]), - }, - - &framework.Path{ - Pattern: "policy/(?P.+)", - - Fields: map[string]*framework.FieldSchema{ - "name": &framework.FieldSchema{ - Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["policy-name"][0]), - }, - "rules": &framework.FieldSchema{ - Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["policy-rules"][0]), - }, - "policy": &framework.FieldSchema{ - Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["policy-rules"][0]), - }, - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ReadOperation: b.handlePoliciesRead(PolicyTypeACL), - logical.UpdateOperation: b.handlePoliciesSet(PolicyTypeACL), - logical.DeleteOperation: b.handlePoliciesDelete(PolicyTypeACL), - }, - - HelpSynopsis: strings.TrimSpace(sysHelp["policy"][0]), - HelpDescription: strings.TrimSpace(sysHelp["policy"][1]), - }, - - &framework.Path{ - Pattern: "policies/acl/?$", - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ListOperation: b.handlePoliciesList(PolicyTypeACL), - }, - - HelpSynopsis: strings.TrimSpace(sysHelp["policy-list"][0]), - HelpDescription: strings.TrimSpace(sysHelp["policy-list"][1]), - }, - - &framework.Path{ - Pattern: "policies/acl/(?P.+)", - - Fields: map[string]*framework.FieldSchema{ - "name": &framework.FieldSchema{ - Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["policy-name"][0]), - }, - "policy": &framework.FieldSchema{ - Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["policy-rules"][0]), - }, - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ReadOperation: b.handlePoliciesRead(PolicyTypeACL), - logical.UpdateOperation: b.handlePoliciesSet(PolicyTypeACL), - logical.DeleteOperation: b.handlePoliciesDelete(PolicyTypeACL), - }, - - HelpSynopsis: strings.TrimSpace(sysHelp["policy"][0]), - HelpDescription: strings.TrimSpace(sysHelp["policy"][1]), - }, - - &framework.Path{ - Pattern: "seal-status$", - HelpSynopsis: strings.TrimSpace(sysHelp["seal-status"][0]), - HelpDescription: strings.TrimSpace(sysHelp["seal-status"][1]), - }, - - &framework.Path{ - Pattern: "seal$", - HelpSynopsis: strings.TrimSpace(sysHelp["seal"][0]), - HelpDescription: strings.TrimSpace(sysHelp["seal"][1]), - }, - - &framework.Path{ - Pattern: "unseal$", - HelpSynopsis: strings.TrimSpace(sysHelp["unseal"][0]), - HelpDescription: strings.TrimSpace(sysHelp["unseal"][1]), - }, - - &framework.Path{ - Pattern: "audit-hash/(?P.+)", - - Fields: map[string]*framework.FieldSchema{ - "path": &framework.FieldSchema{ - Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["audit_path"][0]), - }, - - "input": &framework.FieldSchema{ - Type: framework.TypeString, - }, - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: b.handleAuditHash, - }, - - HelpSynopsis: strings.TrimSpace(sysHelp["audit-hash"][0]), - HelpDescription: strings.TrimSpace(sysHelp["audit-hash"][1]), - }, - - &framework.Path{ - Pattern: "audit$", - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ReadOperation: b.handleAuditTable, - }, - - HelpSynopsis: strings.TrimSpace(sysHelp["audit-table"][0]), - HelpDescription: strings.TrimSpace(sysHelp["audit-table"][1]), - }, - - &framework.Path{ - Pattern: "audit/(?P.+)", - - Fields: map[string]*framework.FieldSchema{ - "path": &framework.FieldSchema{ - Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["audit_path"][0]), - }, - "type": &framework.FieldSchema{ - Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["audit_type"][0]), - }, - "description": &framework.FieldSchema{ - Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["audit_desc"][0]), - }, - "options": &framework.FieldSchema{ - Type: framework.TypeKVPairs, - Description: strings.TrimSpace(sysHelp["audit_opts"][0]), - }, - "local": &framework.FieldSchema{ - Type: framework.TypeBool, - Default: false, - Description: strings.TrimSpace(sysHelp["mount_local"][0]), - }, - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: b.handleEnableAudit, - logical.DeleteOperation: b.handleDisableAudit, - }, - - HelpSynopsis: strings.TrimSpace(sysHelp["audit"][0]), - HelpDescription: strings.TrimSpace(sysHelp["audit"][1]), - }, - - &framework.Path{ - Pattern: "key-status$", - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ReadOperation: b.handleKeyStatus, - }, - - HelpSynopsis: strings.TrimSpace(sysHelp["key-status"][0]), - HelpDescription: strings.TrimSpace(sysHelp["key-status"][1]), - }, - - &framework.Path{ - Pattern: "rotate$", - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: b.handleRotate, - }, - - HelpSynopsis: strings.TrimSpace(sysHelp["rotate"][0]), - HelpDescription: strings.TrimSpace(sysHelp["rotate"][1]), - }, - - &framework.Path{ - Pattern: "wrapping/wrap$", - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: b.handleWrappingWrap, - }, - - HelpSynopsis: strings.TrimSpace(sysHelp["wrap"][0]), - HelpDescription: strings.TrimSpace(sysHelp["wrap"][1]), - }, - - &framework.Path{ - Pattern: "wrapping/unwrap$", - - Fields: map[string]*framework.FieldSchema{ - "token": &framework.FieldSchema{ - Type: framework.TypeString, - }, - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: b.handleWrappingUnwrap, - }, - - HelpSynopsis: strings.TrimSpace(sysHelp["unwrap"][0]), - HelpDescription: strings.TrimSpace(sysHelp["unwrap"][1]), - }, - - &framework.Path{ - Pattern: "wrapping/lookup$", - - Fields: map[string]*framework.FieldSchema{ - "token": &framework.FieldSchema{ - Type: framework.TypeString, - }, - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: b.handleWrappingLookup, - logical.ReadOperation: b.handleWrappingLookup, - }, - - HelpSynopsis: strings.TrimSpace(sysHelp["wraplookup"][0]), - HelpDescription: strings.TrimSpace(sysHelp["wraplookup"][1]), - }, - - &framework.Path{ - Pattern: "wrapping/rewrap$", - - Fields: map[string]*framework.FieldSchema{ - "token": &framework.FieldSchema{ - Type: framework.TypeString, - }, - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: b.handleWrappingRewrap, - }, - - HelpSynopsis: strings.TrimSpace(sysHelp["rewrap"][0]), - HelpDescription: strings.TrimSpace(sysHelp["rewrap"][1]), - }, - - &framework.Path{ - Pattern: "config/auditing/request-headers/(?P
.+)", - - Fields: map[string]*framework.FieldSchema{ - "header": &framework.FieldSchema{ - Type: framework.TypeString, - }, - "hmac": &framework.FieldSchema{ - Type: framework.TypeBool, - }, - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: b.handleAuditedHeaderUpdate, - logical.DeleteOperation: b.handleAuditedHeaderDelete, - logical.ReadOperation: b.handleAuditedHeaderRead, - }, - - HelpSynopsis: strings.TrimSpace(sysHelp["audited-headers-name"][0]), - HelpDescription: strings.TrimSpace(sysHelp["audited-headers-name"][1]), - }, - - &framework.Path{ - Pattern: "config/auditing/request-headers$", - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ReadOperation: b.handleAuditedHeadersRead, - }, - - HelpSynopsis: strings.TrimSpace(sysHelp["audited-headers"][0]), - HelpDescription: strings.TrimSpace(sysHelp["audited-headers"][1]), - }, - - &framework.Path{ - Pattern: "plugins/catalog/?$", - - Fields: map[string]*framework.FieldSchema{}, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ListOperation: b.handlePluginCatalogList, - }, - - HelpSynopsis: strings.TrimSpace(sysHelp["plugin-catalog"][0]), - HelpDescription: strings.TrimSpace(sysHelp["plugin-catalog"][1]), - }, - - &framework.Path{ - Pattern: "plugins/catalog/(?P.+)", - - Fields: map[string]*framework.FieldSchema{ - "name": &framework.FieldSchema{ - Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["plugin-catalog_name"][0]), - }, - "sha256": &framework.FieldSchema{ - Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["plugin-catalog_sha-256"][0]), - }, - "sha_256": &framework.FieldSchema{ - Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["plugin-catalog_sha-256"][0]), - }, - "command": &framework.FieldSchema{ - Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["plugin-catalog_command"][0]), - }, - "args": &framework.FieldSchema{ - Type: framework.TypeStringSlice, - Description: strings.TrimSpace(sysHelp["plugin-catalog_args"][0]), - }, - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: b.handlePluginCatalogUpdate, - logical.DeleteOperation: b.handlePluginCatalogDelete, - logical.ReadOperation: b.handlePluginCatalogRead, - }, - - HelpSynopsis: strings.TrimSpace(sysHelp["plugin-catalog"][0]), - HelpDescription: strings.TrimSpace(sysHelp["plugin-catalog"][1]), - }, - &framework.Path{ - Pattern: "plugins/reload/backend$", - - Fields: map[string]*framework.FieldSchema{ - "plugin": &framework.FieldSchema{ - Type: framework.TypeString, - Description: strings.TrimSpace(sysHelp["plugin-backend-reload-plugin"][0]), - }, - "mounts": &framework.FieldSchema{ - Type: framework.TypeCommaStringSlice, - Description: strings.TrimSpace(sysHelp["plugin-backend-reload-mounts"][0]), - }, - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: b.handlePluginReloadUpdate, - }, - - HelpSynopsis: strings.TrimSpace(sysHelp["plugin-reload"][0]), - HelpDescription: strings.TrimSpace(sysHelp["plugin-reload"][1]), - }, - &framework.Path{ - Pattern: "tools/hash" + framework.OptionalParamRegex("urlalgorithm"), - Fields: map[string]*framework.FieldSchema{ - "input": &framework.FieldSchema{ - Type: framework.TypeString, - Description: "The base64-encoded input data", - }, - - "algorithm": &framework.FieldSchema{ - Type: framework.TypeString, - Default: "sha2-256", - Description: `Algorithm to use (POST body parameter). Valid values are: - - * sha2-224 - * sha2-256 - * sha2-384 - * sha2-512 - - Defaults to "sha2-256".`, - }, - - "urlalgorithm": &framework.FieldSchema{ - Type: framework.TypeString, - Description: `Algorithm to use (POST URL parameter)`, - }, - - "format": &framework.FieldSchema{ - Type: framework.TypeString, - Default: "hex", - Description: `Encoding format to use. Can be "hex" or "base64". Defaults to "hex".`, - }, - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: b.pathHashWrite, - }, - - HelpSynopsis: strings.TrimSpace(sysHelp["hash"][0]), - HelpDescription: strings.TrimSpace(sysHelp["hash"][1]), - }, - - &framework.Path{ - Pattern: "tools/random" + framework.OptionalParamRegex("urlbytes"), - Fields: map[string]*framework.FieldSchema{ - "urlbytes": &framework.FieldSchema{ - Type: framework.TypeString, - Description: "The number of bytes to generate (POST URL parameter)", - }, - - "bytes": &framework.FieldSchema{ - Type: framework.TypeInt, - Default: 32, - Description: "The number of bytes to generate (POST body parameter). Defaults to 32 (256 bits).", - }, - - "format": &framework.FieldSchema{ - Type: framework.TypeString, - Default: "base64", - Description: `Encoding format to use. Can be "hex" or "base64". Defaults to "base64".`, - }, - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: b.pathRandomWrite, - }, - - HelpSynopsis: strings.TrimSpace(sysHelp["random"][0]), - HelpDescription: strings.TrimSpace(sysHelp["random"][1]), - }, - &framework.Path{ - Pattern: "internal/ui/mounts", - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ReadOperation: b.pathInternalUIMountsRead, - }, - HelpSynopsis: strings.TrimSpace(sysHelp["internal-ui-mounts"][0]), - HelpDescription: strings.TrimSpace(sysHelp["internal-ui-mounts"][1]), - }, - &framework.Path{ - Pattern: "internal/ui/mounts/(?P.+)", - Fields: map[string]*framework.FieldSchema{ - "path": &framework.FieldSchema{ - Type: framework.TypeString, - Description: "The path of the mount.", - }, - }, - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ReadOperation: b.pathInternalUIMountRead, - }, - HelpSynopsis: strings.TrimSpace(sysHelp["internal-ui-mounts"][0]), - HelpDescription: strings.TrimSpace(sysHelp["internal-ui-mounts"][1]), - }, - &framework.Path{ - Pattern: "internal/ui/resultant-acl", - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ReadOperation: b.pathInternalUIResultantACL, - }, - HelpSynopsis: strings.TrimSpace(sysHelp["internal-ui-resultant-acl"][0]), - HelpDescription: strings.TrimSpace(sysHelp["internal-ui-resultant-acl"][1]), + LocalStorage: []string{ + expirationSubPath, }, }, } - b.Backend.Paths = append(b.Backend.Paths, replicationPaths(b)...) + b.Backend.Paths = append(b.Backend.Paths, entPaths(b)...) + b.Backend.Paths = append(b.Backend.Paths, b.configPaths()...) + b.Backend.Paths = append(b.Backend.Paths, b.rekeyPaths()...) + b.Backend.Paths = append(b.Backend.Paths, b.sealPaths()...) + b.Backend.Paths = append(b.Backend.Paths, b.pluginsCatalogPath()) + b.Backend.Paths = append(b.Backend.Paths, b.pluginsCatalogListPath()) + b.Backend.Paths = append(b.Backend.Paths, b.pluginsReloadPath()) + b.Backend.Paths = append(b.Backend.Paths, b.auditPaths()...) + b.Backend.Paths = append(b.Backend.Paths, b.mountPaths()...) + b.Backend.Paths = append(b.Backend.Paths, b.authPaths()...) + b.Backend.Paths = append(b.Backend.Paths, b.leasePaths()...) + b.Backend.Paths = append(b.Backend.Paths, b.policyPaths()...) + b.Backend.Paths = append(b.Backend.Paths, b.wrappingPaths()...) + b.Backend.Paths = append(b.Backend.Paths, b.toolsPaths()...) + b.Backend.Paths = append(b.Backend.Paths, b.capabilitiesPaths()...) + b.Backend.Paths = append(b.Backend.Paths, b.internalUIPaths()...) + b.Backend.Paths = append(b.Backend.Paths, b.remountPath()) if core.rawEnabled { b.Backend.Paths = append(b.Backend.Paths, &framework.Path{ @@ -1140,8 +168,7 @@ func NewSystemBackend(core *Core, logger log.Logger) *SystemBackend { }) } - b.Backend.Invalidate = b.invalidate - + b.Backend.Invalidate = sysInvalidate(b) return b } @@ -1150,8 +177,11 @@ func NewSystemBackend(core *Core, logger log.Logger) *SystemBackend { // prefix. Conceptually it is similar to procfs on Linux. type SystemBackend struct { *framework.Backend - Core *Core - logger log.Logger + Core *Core + db *memdb.MemDB + mfaLock *sync.RWMutex + mfaLogger log.Logger + logger log.Logger } // handleCORSRead returns the current CORS configuration @@ -1192,8 +222,14 @@ func (b *SystemBackend) handleCORSDelete(ctx context.Context, req *logical.Reque } func (b *SystemBackend) handleTidyLeases(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } + go func() { - err := b.Core.expiration.Tidy() + tidyCtx := namespace.ContextWithNamespace(b.Core.activeContext, ns) + err := b.Core.expiration.Tidy(tidyCtx) if err != nil { b.Backend.Logger().Error("failed to tidy leases", "error", err) return @@ -1205,28 +241,6 @@ func (b *SystemBackend) handleTidyLeases(ctx context.Context, req *logical.Reque return logical.RespondWithStatusCode(resp, req, http.StatusAccepted) } -func (b *SystemBackend) invalidate(ctx context.Context, key string) { - /* - if b.Core.logger.IsTrace() { - b.Core.logger.Trace("invalidating key", "key", key) - } - */ - switch { - case strings.HasPrefix(key, policyACLSubPath): - b.Core.stateLock.RLock() - defer b.Core.stateLock.RUnlock() - if b.Core.policyStore != nil { - b.Core.policyStore.invalidate(ctx, strings.TrimPrefix(key, policyACLSubPath), PolicyTypeACL) - } - case strings.HasPrefix(key, tokenSubPath): - b.Core.stateLock.RLock() - defer b.Core.stateLock.RUnlock() - if b.Core.tokenStore != nil { - b.Core.tokenStore.Invalidate(ctx, key) - } - } -} - func (b *SystemBackend) handlePluginCatalogList(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { plugins, err := b.Core.pluginCatalog.List(ctx) if err != nil { @@ -1425,7 +439,7 @@ func (b *SystemBackend) handleCapabilitiesAccessor(ctx context.Context, req *log return logical.ErrorResponse("missing accessor"), nil } - aEntry, err := b.Core.tokenStore.lookupByAccessor(ctx, accessor, false) + aEntry, err := b.Core.tokenStore.lookupByAccessor(ctx, accessor, false, false) if err != nil { return nil, err } @@ -1593,6 +607,11 @@ func mountInfo(entry *MountEntry) map[string]interface{} { // handleMountTable handles the "mounts" endpoint to provide the mount table func (b *SystemBackend) handleMountTable(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } + b.Core.mountsLock.RLock() defer b.Core.mountsLock.RUnlock() @@ -1601,6 +620,19 @@ func (b *SystemBackend) handleMountTable(ctx context.Context, req *logical.Reque } for _, entry := range b.Core.mounts.Entries { + // Only show entries for current namespace + if entry.Namespace().Path != ns.Path { + continue + } + + cont, err := b.Core.checkReplicatedFiltering(ctx, entry, "") + if err != nil { + return nil, err + } + if cont { + continue + } + // Populate mount info info := mountInfo(entry) resp.Data[entry.Path] = info @@ -1801,25 +833,42 @@ func (b *SystemBackend) handleUnmount(ctx context.Context, req *logical.Request, path := data.Get("path").(string) path = sanitizeMountPath(path) + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } + repState := b.Core.ReplicationState() - entry := b.Core.router.MatchingMountEntry(path) + entry := b.Core.router.MatchingMountEntry(ctx, path) if entry != nil && !entry.Local && repState.HasState(consts.ReplicationPerformanceSecondary) { return logical.ErrorResponse("cannot unmount a non-local mount on a replication secondary"), nil } // We return success when the mount does not exists to not expose if the // mount existed or not - match := b.Core.router.MatchingMount(path) - if match == "" || path != match { + match := b.Core.router.MatchingMount(ctx, path) + if match == "" || ns.Path+path != match { return nil, nil } + prefix, found := b.Core.router.MatchingStoragePrefixByAPIPath(ctx, path) + if !found { + b.Backend.Logger().Error("unable to find storage for path", "path", path) + return handleError(fmt.Errorf("unable to find storage for path: %q", path)) + } + // Attempt unmount if err := b.Core.unmount(ctx, path); err != nil { b.Backend.Logger().Error("unmount failed", "path", path, "error", err) return handleError(err) } + // Remove from filtered mounts + if err := b.Core.removePrefixFromFilteredPaths(ctx, prefix); err != nil { + b.Backend.Logger().Error("filtered path removal failed", path, "error", err) + return handleError(err) + } + return nil, nil } @@ -1836,10 +885,7 @@ func (b *SystemBackend) handleRemount(ctx context.Context, req *logical.Request, logical.ErrInvalidRequest } - fromPath = sanitizeMountPath(fromPath) - toPath = sanitizeMountPath(toPath) - - entry := b.Core.router.MatchingMountEntry(fromPath) + entry := b.Core.router.MatchingMountEntry(ctx, fromPath) if entry != nil && !entry.Local && repState.HasState(consts.ReplicationPerformanceSecondary) { return logical.ErrorResponse("cannot remount a non-local mount on a replication secondary"), nil } @@ -1861,7 +907,7 @@ func (b *SystemBackend) handleAuthTuneRead(ctx context.Context, req *logical.Req "path must be specified as a string"), logical.ErrInvalidRequest } - return b.handleTuneReadCommon("auth/" + path) + return b.handleTuneReadCommon(ctx, "auth/"+path) } // handleMountTuneRead is used to get config settings on a backend @@ -1876,23 +922,23 @@ func (b *SystemBackend) handleMountTuneRead(ctx context.Context, req *logical.Re // This call will read both logical backend's configuration as well as auth methods'. // Retaining this behavior for backward compatibility. If this behavior is not desired, // an error can be returned if path has a prefix of "auth/". - return b.handleTuneReadCommon(path) + return b.handleTuneReadCommon(ctx, path) } // handleTuneReadCommon returns the config settings of a path -func (b *SystemBackend) handleTuneReadCommon(path string) (*logical.Response, error) { +func (b *SystemBackend) handleTuneReadCommon(ctx context.Context, path string) (*logical.Response, error) { path = sanitizeMountPath(path) - sysView := b.Core.router.MatchingSystemView(path) + sysView := b.Core.router.MatchingSystemView(ctx, path) if sysView == nil { b.Backend.Logger().Error("cannot fetch sysview", "path", path) - return handleError(fmt.Errorf("sys: cannot fetch sysview for path %q", path)) + return handleError(fmt.Errorf("cannot fetch sysview for path %q", path)) } - mountEntry := b.Core.router.MatchingMountEntry(path) + mountEntry := b.Core.router.MatchingMountEntry(ctx, path) if mountEntry == nil { b.Backend.Logger().Error("cannot fetch mount entry", "path", path) - return handleError(fmt.Errorf("sys: cannot fetch mount entry for path %q", path)) + return handleError(fmt.Errorf("cannot fetch mount entry for path %q", path)) } resp := &logical.Response{ @@ -1930,19 +976,25 @@ func (b *SystemBackend) handleTuneReadCommon(path string) (*logical.Response, er func (b *SystemBackend) handleAuthTuneWrite(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { path := data.Get("path").(string) if path == "" { - return logical.ErrorResponse("path must be specified as a string"), - logical.ErrInvalidRequest + return logical.ErrorResponse("missing path"), nil } - return b.handleTuneWriteCommon(ctx, "auth/"+path, data) + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } + + path = ns.Path + namespace.Canonicalize("auth/"+path) + + return b.handleTuneWriteCommon(ctx, path, data) } // handleMountTuneWrite is used to set config settings on a backend func (b *SystemBackend) handleMountTuneWrite(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { path := data.Get("path").(string) if path == "" { - return logical.ErrorResponse("path must be specified as a string"), - logical.ErrInvalidRequest + return logical.ErrorResponse("missing path"), nil } + // This call will write both logical backend's configuration as well as auth methods'. // Retaining this behavior for backward compatibility. If this behavior is not desired, // an error can be returned if path has a prefix of "auth/". @@ -1963,7 +1015,7 @@ func (b *SystemBackend) handleTuneWriteCommon(ctx context.Context, path string, } } - mountEntry := b.Core.router.MatchingMountEntry(path) + mountEntry := b.Core.router.MatchingMountEntry(ctx, path) if mountEntry == nil { b.Backend.Logger().Error("tune failed: no mount entry found", "path", path) return handleError(fmt.Errorf("tune of path %q failed: no mount entry found", path)) @@ -1984,7 +1036,7 @@ func (b *SystemBackend) handleTuneWriteCommon(ctx context.Context, path string, defer lock.Unlock() // Check again after grabbing the lock - mountEntry = b.Core.router.MatchingMountEntry(path) + mountEntry = b.Core.router.MatchingMountEntry(ctx, path) if mountEntry == nil { b.Backend.Logger().Error("tune failed: no mount entry found", "path", path) return handleError(fmt.Errorf("tune of path %q failed: no mount entry found", path)) @@ -2272,7 +1324,12 @@ func (b *SystemBackend) handleLeaseLookupList(ctx context.Context, req *logical. prefix = prefix + "/" } - keys, err := b.Core.expiration.idView.List(ctx, prefix) + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } + view := b.Core.expiration.leaseView(ns) + keys, err := view.List(ctx, prefix) if err != nil { b.Backend.Logger().Error("error listing leases", "prefix", prefix, "error", err) return handleErrorNoReadOnlyForward(err) @@ -2317,9 +1374,14 @@ func (b *SystemBackend) handleRevoke(ctx context.Context, req *logical.Request, logical.ErrInvalidRequest } + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } + revokeCtx := namespace.ContextWithNamespace(b.Core.activeContext, ns) if data.Get("sync").(bool) { // Invoke the expiration manager directly - if err := b.Core.expiration.Revoke(b.Core.activeContext, leaseID); err != nil { + if err := b.Core.expiration.Revoke(revokeCtx, leaseID); err != nil { b.Backend.Logger().Error("lease revocation failed", "lease_id", leaseID, "error", err) return handleErrorNoReadOnlyForward(err) } @@ -2327,7 +1389,7 @@ func (b *SystemBackend) handleRevoke(ctx context.Context, req *logical.Request, return nil, nil } - if err := b.Core.expiration.LazyRevoke(b.Core.activeContext, leaseID); err != nil { + if err := b.Core.expiration.LazyRevoke(revokeCtx, leaseID); err != nil { b.Backend.Logger().Error("lease revocation failed", "lease_id", leaseID, "error", err) return handleErrorNoReadOnlyForward(err) } @@ -2351,12 +1413,17 @@ func (b *SystemBackend) handleRevokePrefixCommon(ctx context.Context, // Get all the options prefix := data.Get("prefix").(string) + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } + // Invoke the expiration manager directly - var err error + revokeCtx := namespace.ContextWithNamespace(b.Core.activeContext, ns) if force { - err = b.Core.expiration.RevokeForce(b.Core.activeContext, prefix) + err = b.Core.expiration.RevokeForce(revokeCtx, prefix) } else { - err = b.Core.expiration.RevokePrefix(b.Core.activeContext, prefix, sync) + err = b.Core.expiration.RevokePrefix(revokeCtx, prefix, sync) } if err != nil { b.Backend.Logger().Error("revoke prefix failed", "prefix", prefix, "error", err) @@ -2372,13 +1439,32 @@ func (b *SystemBackend) handleRevokePrefixCommon(ctx context.Context, // handleAuthTable handles the "auth" endpoint to provide the auth table func (b *SystemBackend) handleAuthTable(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } + b.Core.authLock.RLock() defer b.Core.authLock.RUnlock() resp := &logical.Response{ Data: make(map[string]interface{}), } + for _, entry := range b.Core.auth.Entries { + // Only show entries for current namespace + if entry.Namespace().Path != ns.Path { + continue + } + + cont, err := b.Core.checkReplicatedFiltering(ctx, entry, credentialRoutePrefix) + if err != nil { + return nil, err + } + if cont { + continue + } + info := map[string]interface{}{ "type": entry.Type, "description": entry.Description, @@ -2548,32 +1634,53 @@ func (b *SystemBackend) handleDisableAuth(ctx context.Context, req *logical.Requ path := data.Get("path").(string) path = sanitizeMountPath(path) + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } fullPath := credentialRoutePrefix + path repState := b.Core.ReplicationState() - entry := b.Core.router.MatchingMountEntry(fullPath) + entry := b.Core.router.MatchingMountEntry(ctx, fullPath) if entry != nil && !entry.Local && repState.HasState(consts.ReplicationPerformanceSecondary) { return logical.ErrorResponse("cannot unmount a non-local mount on a replication secondary"), nil } // We return success when the mount does not exists to not expose if the // mount existed or not - match := b.Core.router.MatchingMount(fullPath) - if match == "" || fullPath != match { + match := b.Core.router.MatchingMount(ctx, fullPath) + if match == "" || ns.Path+fullPath != match { return nil, nil } + prefix, found := b.Core.router.MatchingStoragePrefixByAPIPath(ctx, fullPath) + if !found { + b.Backend.Logger().Error("unable to find storage for path", "path", fullPath) + return handleError(fmt.Errorf("unable to find storage for path: %q", fullPath)) + } + // Attempt disable if err := b.Core.disableCredential(ctx, path); err != nil { b.Backend.Logger().Error("disable auth mount failed", "path", path, "error", err) return handleError(err) } + + // Remove from filtered mounts + if err := b.Core.removePrefixFromFilteredPaths(ctx, prefix); err != nil { + b.Backend.Logger().Error("filtered path removal failed", path, "error", err) + return handleError(err) + } + return nil, nil } // handlePoliciesList handles /sys/policy/ and /sys/policies/ endpoints to provide the enabled policies func (b *SystemBackend) handlePoliciesList(policyType PolicyType) framework.OperationFunc { return func(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } policies, err := b.Core.policyStore.ListPolicies(ctx, policyType) if err != nil { return nil, err @@ -2581,16 +1688,29 @@ func (b *SystemBackend) handlePoliciesList(policyType PolicyType) framework.Oper switch policyType { case PolicyTypeACL: - // Add the special "root" policy if not egp - policies = append(policies, "root") + // Add the special "root" policy if not egp and we are at the root namespace + if ns.ID == namespace.RootNamespaceID { + policies = append(policies, "root") + } resp := logical.ListResponse(policies) // If the request is from sys/policy/ we handle backwards compatibility if strings.HasPrefix(req.Path, "policy") { resp.Data["policies"] = resp.Data["keys"] } - return resp, nil + + case PolicyTypeRGP: + return logical.ListResponse(policies), nil + + case PolicyTypeEGP: + nsScopedKeyInfo := getEGPListResponseKeyInfo(b, ns) + return &logical.Response{ + Data: map[string]interface{}{ + "keys": policies, + "key_info": nsScopedKeyInfo, + }, + }, nil } return logical.ErrorResponse("unknown policy type"), nil @@ -2626,6 +1746,11 @@ func (b *SystemBackend) handlePoliciesRead(policyType PolicyType) framework.Oper }, } + switch policy.Type { + case PolicyTypeRGP, PolicyTypeEGP: + addSentinelPolicyData(resp.Data, policy) + } + return resp, nil } } @@ -2635,9 +1760,15 @@ func (b *SystemBackend) handlePoliciesSet(policyType PolicyType) framework.Opera return func(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { var resp *logical.Response + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } + policy := &Policy{ - Name: strings.ToLower(data.Get("name").(string)), - Type: policyType, + Name: strings.ToLower(data.Get("name").(string)), + Type: policyType, + namespace: ns, } if policy.Name == "" { return logical.ErrorResponse("policy name must be provided in the URL"), nil @@ -2661,17 +1792,25 @@ func (b *SystemBackend) handlePoliciesSet(policyType PolicyType) framework.Opera switch policyType { case PolicyTypeACL: - p, err := ParseACLPolicy(policy.Raw) + p, err := ParseACLPolicy(ns, policy.Raw) if err != nil { return handleError(err) } policy.Paths = p.Paths policy.Templated = p.Templated + case PolicyTypeRGP, PolicyTypeEGP: + default: return logical.ErrorResponse("unknown policy type"), nil } + if policy.Type == PolicyTypeRGP || policy.Type == PolicyTypeEGP { + if errResp := inputSentinelPolicyData(data, policy); errResp != nil { + return errResp, nil + } + } + // Update the policy if err := b.Core.policyStore.SetPolicy(ctx, policy); err != nil { return handleError(err) @@ -2761,7 +1900,7 @@ func (b *SystemBackend) handleEnableAudit(ctx context.Context, req *logical.Requ } // Attempt enabling - if err := b.Core.enableAudit(ctx, me); err != nil { + if err := b.Core.enableAudit(ctx, me, true); err != nil { b.Backend.Logger().Error("enable audit mount failed", "path", me.Path, "error", err) return handleError(err) } @@ -2773,7 +1912,7 @@ func (b *SystemBackend) handleDisableAudit(ctx context.Context, req *logical.Req path := data.Get("path").(string) // Attempt disable - if existed, err := b.Core.disableAudit(ctx, path); existed && err != nil { + if existed, err := b.Core.disableAudit(ctx, path, true); existed && err != nil { b.Backend.Logger().Error("disable audit mount failed", "path", path, "error", err) return handleError(err) } @@ -3044,6 +2183,8 @@ func (b *SystemBackend) handleWrappingWrap(ctx context.Context, req *logical.Req }, nil } +// handleWrappingUnwrap will unwrap a response wrapping token or complete a +// request that required a control group. func (b *SystemBackend) handleWrappingUnwrap(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { // If a third party is unwrapping (rather than the calling token being the // wrapping token) we detect this so that we can revoke the original @@ -3069,16 +2210,24 @@ func (b *SystemBackend) handleWrappingUnwrap(ctx context.Context, req *logical.R return nil, err } if te == nil { - return nil, errors.New("could not find token") + return nil, nil } if len(te.Policies) != 1 { return nil, errors.New("token is not a valid unwrap token") } + unwrapNS, err := NamespaceByID(ctx, te.NamespaceID, b.Core) + if err != nil { + return nil, err + } + unwrapCtx := namespace.ContextWithNamespace(ctx, unwrapNS) + var response string switch te.Policies[0] { + case controlGroupPolicyName: + response, err = controlGroupUnwrap(unwrapCtx, b, token, thirdParty) case responseWrappingPolicyName: - response, err = b.responseWrappingUnwrap(ctx, token, thirdParty) + response, err = b.responseWrappingUnwrap(unwrapCtx, te, thirdParty) } if err != nil { var respErr *logical.Response @@ -3151,22 +2300,24 @@ func (b *SystemBackend) handleWrappingUnwrap(ctx context.Context, req *logical.R // responseWrappingUnwrap will read the stored response in the cubbyhole and // return the raw HTTP response. -func (b *SystemBackend) responseWrappingUnwrap(ctx context.Context, token string, thirdParty bool) (string, error) { +func (b *SystemBackend) responseWrappingUnwrap(ctx context.Context, te *logical.TokenEntry, thirdParty bool) (string, error) { + tokenID := te.ID if thirdParty { // Use the token to decrement the use count to avoid a second operation on the token. - _, err := b.Core.tokenStore.UseTokenByID(ctx, token) + _, err := b.Core.tokenStore.UseTokenByID(ctx, tokenID) if err != nil { return "", errwrap.Wrapf("error decrementing wrapping token's use-count: {{err}}", err) } - defer b.Core.tokenStore.revokeOrphan(ctx, token) + defer b.Core.tokenStore.revokeOrphan(ctx, tokenID) } cubbyReq := &logical.Request{ Operation: logical.ReadOperation, Path: "cubbyhole/response", - ClientToken: token, + ClientToken: tokenID, } + cubbyReq.SetTokenEntry(te) cubbyResp, err := b.Core.router.Route(ctx, cubbyReq) if err != nil { return "", errwrap.Wrapf("error looking up wrapping information: {{err}}", err) @@ -3204,11 +2355,23 @@ func (b *SystemBackend) handleWrappingLookup(ctx context.Context, req *logical.R } } + te, err := b.Core.tokenStore.lookupTainted(ctx, token) + if err != nil { + return nil, err + } + if te == nil { + return nil, nil + } + if len(te.Policies) != 1 { + return nil, errors.New("token is not a valid unwrap token") + } + cubbyReq := &logical.Request{ Operation: logical.ReadOperation, Path: "cubbyhole/wrapinfo", ClientToken: token, } + cubbyReq.SetTokenEntry(te) cubbyResp, err := b.Core.router.Route(ctx, cubbyReq) if err != nil { return nil, errwrap.Wrapf("error looking up wrapping information: {{err}}", err) @@ -3263,6 +2426,17 @@ func (b *SystemBackend) handleWrappingRewrap(ctx context.Context, req *logical.R token = req.ClientToken } + te, err := b.Core.tokenStore.lookupTainted(ctx, token) + if err != nil { + return nil, err + } + if te == nil { + return nil, nil + } + if len(te.Policies) != 1 { + return nil, errors.New("token is not a valid unwrap token") + } + if thirdParty { // Use the token to decrement the use count to avoid a second operation on the token. _, err := b.Core.tokenStore.UseTokenByID(ctx, token) @@ -3278,6 +2452,7 @@ func (b *SystemBackend) handleWrappingRewrap(ctx context.Context, req *logical.R Path: "cubbyhole/wrapinfo", ClientToken: token, } + cubbyReq.SetTokenEntry(te) cubbyResp, err := b.Core.router.Route(ctx, cubbyReq) if err != nil { return nil, errwrap.Wrapf("error looking up wrapping information: {{err}}", err) @@ -3315,6 +2490,7 @@ func (b *SystemBackend) handleWrappingRewrap(ctx context.Context, req *logical.R Path: "cubbyhole/response", ClientToken: token, } + cubbyReq.SetTokenEntry(te) cubbyResp, err = b.Core.router.Route(ctx, cubbyReq) if err != nil { return nil, errwrap.Wrapf("error looking up response: {{err}}", err) @@ -3447,10 +2623,15 @@ func (b *SystemBackend) pathRandomWrite(ctx context.Context, req *logical.Reques return resp, nil } -func hasMountAccess(acl *ACL, path string) bool { +func hasMountAccess(ctx context.Context, acl *ACL, path string) bool { + ns, err := namespace.FromContext(ctx) + if err != nil { + return false + } + // If an ealier policy is giving us access to the mount path then we can do // a fast return. - capabilities := acl.Capabilities(path) + capabilities := acl.Capabilities(ctx, ns.TrimmedPath(path)) if !strutil.StrListContains(capabilities, DenyCapability) { return true } @@ -3490,6 +2671,11 @@ func hasMountAccess(acl *ACL, path string) bool { } func (b *SystemBackend) pathInternalUIMountsRead(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } + resp := &logical.Response{ Data: make(map[string]interface{}), } @@ -3501,14 +2687,13 @@ func (b *SystemBackend) pathInternalUIMountsRead(ctx context.Context, req *logic var acl *ACL var isAuthed bool - var err error if req.ClientToken != "" { isAuthed = true var entity *identity.Entity var te *logical.TokenEntry // Load the ACL policies so we can walk the prefix for this mount - acl, te, entity, _, err = b.Core.fetchACLTokenEntryAndEntity(req) + acl, te, entity, _, err = b.Core.fetchACLTokenEntryAndEntity(ctx, req) if err != nil { if errwrap.ContainsType(err, new(TemplateError)) { b.Core.logger.Warn("permission denied due to a templated policy being invalid or containing directives not satisfied by the requestor", "error", err) @@ -3526,13 +2711,13 @@ func (b *SystemBackend) pathInternalUIMountsRead(ctx context.Context, req *logic } } - hasAccess := func(me *MountEntry) bool { + hasAccess := func(ctx context.Context, me *MountEntry) bool { if me.Config.ListingVisibility == ListingVisibilityUnauth { return true } if isAuthed { - return hasMountAccess(acl, me.Path) + return hasMountAccess(ctx, acl, ns.Path+me.Path) } return false @@ -3540,7 +2725,7 @@ func (b *SystemBackend) pathInternalUIMountsRead(ctx context.Context, req *logic b.Core.mountsLock.RLock() for _, entry := range b.Core.mounts.Entries { - if hasAccess(entry) { + if hasAccess(ctx, entry) && ns.ID == entry.NamespaceID { if isAuthed { // If this is an authed request return all the mount info secretMounts[entry.Path] = mountInfo(entry) @@ -3557,7 +2742,7 @@ func (b *SystemBackend) pathInternalUIMountsRead(ctx context.Context, req *logic b.Core.authLock.RLock() for _, entry := range b.Core.auth.Entries { - if hasAccess(entry) { + if hasAccess(ctx, entry) && ns.ID == entry.NamespaceID { if isAuthed { // If this is an authed request return all the mount info authMounts[entry.Path] = mountInfo(entry) @@ -3582,9 +2767,9 @@ func (b *SystemBackend) pathInternalUIMountRead(ctx context.Context, req *logica } path = sanitizeMountPath(path) - errResp := logical.ErrorResponse(fmt.Sprintf("Preflight capability check returned 403, please ensure client's policies grant access to path \"%s\"", path)) + errResp := logical.ErrorResponse(fmt.Sprintf("preflight capability check returned 403, please ensure client's policies grant access to path %q", path)) - me := b.Core.router.MatchingMountEntry(path) + me := b.Core.router.MatchingMountEntry(ctx, path) if me == nil { // Return a permission denied error here so this path cannot be used to // brute force a list of mounts. @@ -3597,7 +2782,7 @@ func (b *SystemBackend) pathInternalUIMountRead(ctx context.Context, req *logica resp.Data["path"] = me.Path // Load the ACL policies so we can walk the prefix for this mount - acl, te, entity, _, err := b.Core.fetchACLTokenEntryAndEntity(req) + acl, te, entity, _, err := b.Core.fetchACLTokenEntryAndEntity(ctx, req) if err != nil { if errwrap.ContainsType(err, new(TemplateError)) { b.Core.logger.Warn("permission denied due to a templated policy being invalid or containing directives not satisfied by the requestor", "error", err) @@ -3614,7 +2799,12 @@ func (b *SystemBackend) pathInternalUIMountRead(ctx context.Context, req *logica return nil, logical.ErrPermissionDenied } - if !hasMountAccess(acl, me.Path) { + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } + + if !hasMountAccess(ctx, acl, ns.Path+me.Path) { return errResp, logical.ErrPermissionDenied } @@ -3627,7 +2817,7 @@ func (b *SystemBackend) pathInternalUIResultantACL(ctx context.Context, req *log return nil, nil } - acl, te, entity, _, err := b.Core.fetchACLTokenEntryAndEntity(req) + acl, te, entity, _, err := b.Core.fetchACLTokenEntryAndEntity(ctx, req) if err != nil { if errwrap.ContainsType(err, new(TemplateError)) { b.Core.logger.Warn("permission denied due to a templated policy being invalid or containing directives not satisfied by the requestor", "error", err) @@ -3766,6 +2956,18 @@ as well as perform core operations. // sysHelp is all the help text for the sys backend. var sysHelp = map[string][2]string{ + "license": { + "Sets the license of the server.", + ` +The path responds to the following HTTP methods. + + GET / + Returns information on the installed license + + POST + Sets the license for the server + `, + }, "config/cors": { "Configures or returns the current configuration of CORS settings.", ` @@ -4132,7 +3334,17 @@ or delete a policy. }, "policy-rules": { - `The rules of the policy. Either given in HCL or JSON format.`, + `The rules of the policy.`, + "", + }, + + "policy-paths": { + `The paths on which the policy should be applied.`, + "", + }, + + "policy-enforcement-level": { + `The enforcement level to apply to the policy.`, "", }, @@ -4370,6 +3582,12 @@ This path responds to the following HTTP methods. "Information about mounts returned according to their tuned visibility. Internal API; its location, inputs, and outputs may change.", "", }, + "internal-ui-namespaces": { + "Information about visible child namespaces. Internal API; its location, inputs, and outputs may change.", + `Information about visible child namespaces returned starting from the request's + context namespace and filtered based on access from the client token. Internal API; + its location, inputs, and outputs may change.`, + }, "internal-ui-resultant-acl": { "Information about a token's resultant ACL. Internal API; its location, inputs, and outputs may change.", "", diff --git a/vault/logical_system_helpers.go b/vault/logical_system_helpers.go index d9fdb046b7..28fae41236 100644 --- a/vault/logical_system_helpers.go +++ b/vault/logical_system_helpers.go @@ -2,9 +2,88 @@ package vault import ( "context" + "errors" "fmt" "strings" "time" + + memdb "github.com/hashicorp/go-memdb" + "github.com/hashicorp/vault/helper/namespace" + "github.com/hashicorp/vault/logical" + "github.com/hashicorp/vault/logical/framework" +) + +var ( + invalidateMFAConfig = func(context.Context, *SystemBackend, string) {} + + sysInvalidate = func(b *SystemBackend) func(context.Context, string) { + return nil + } + + getSystemSchemas = func() []func() *memdb.TableSchema { return nil } + + getEGPListResponseKeyInfo = func(*SystemBackend, *namespace.Namespace) map[string]interface{} { return nil } + addSentinelPolicyData = func(map[string]interface{}, *Policy) {} + inputSentinelPolicyData = func(*framework.FieldData, *Policy) *logical.Response { return nil } + + controlGroupUnwrap = func(context.Context, *SystemBackend, string, bool) (string, error) { + return "", errors.New("control groups unavailable") + } + + pathInternalUINamespacesRead = func(b *SystemBackend) framework.OperationFunc { + return func(ctx context.Context, req *logical.Request, _ *framework.FieldData) (*logical.Response, error) { + // Short-circuit here if there's no client token provided + if req.ClientToken == "" { + return nil, fmt.Errorf("client token empty") + } + + // Load the ACL policies so we can check for access and filter namespaces + _, te, entity, _, err := b.Core.fetchACLTokenEntryAndEntity(ctx, req) + if err != nil { + return nil, err + } + if entity != nil && entity.Disabled { + b.logger.Warn("permission denied as the entity on the token is disabled") + return nil, logical.ErrPermissionDenied + } + if te != nil && te.EntityID != "" && entity == nil { + b.logger.Warn("permission denied as the entity on the token is invalid") + return nil, logical.ErrPermissionDenied + } + + return logical.ListResponse([]string{""}), nil + } + } + + pathLicenseRead = func(b *SystemBackend) framework.OperationFunc { + return func(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { + return nil, nil + } + } + + pathLicenseUpdate = func(b *SystemBackend) framework.OperationFunc { + return func(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { + return nil, nil + } + } + + entPaths = func(b *SystemBackend) []*framework.Path { + return []*framework.Path{ + { + Pattern: "replication/status", + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.ReadOperation: func(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { + resp := &logical.Response{ + Data: map[string]interface{}{ + "mode": "disabled", + }, + } + return resp, nil + }, + }, + }, + } + } ) // tuneMount is used to set config on a mount point diff --git a/vault/logical_system_integ_test.go b/vault/logical_system_integ_test.go index b929ef724f..6f0ae08261 100644 --- a/vault/logical_system_integ_test.go +++ b/vault/logical_system_integ_test.go @@ -1,7 +1,6 @@ package vault_test import ( - "context" "fmt" "io/ioutil" "os" @@ -12,6 +11,7 @@ import ( "github.com/go-test/deep" "github.com/hashicorp/vault/api" "github.com/hashicorp/vault/builtin/plugin" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/helper/pluginutil" vaulthttp "github.com/hashicorp/vault/http" "github.com/hashicorp/vault/logical" @@ -29,7 +29,7 @@ func TestSystemBackend_Plugin_secret(t *testing.T) { // Make a request to lazy load the plugin req := logical.TestRequest(t, logical.ReadOperation, "mock-0/internal") req.ClientToken = core.Client.Token() - resp, err := core.HandleRequest(context.Background(), req) + resp, err := core.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -67,7 +67,7 @@ func TestSystemBackend_Plugin_auth(t *testing.T) { // Make a request to lazy load the plugin req := logical.TestRequest(t, logical.ReadOperation, "auth/mock-0/internal") req.ClientToken = core.Client.Token() - resp, err := core.HandleRequest(context.Background(), req) + resp, err := core.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -109,7 +109,7 @@ func TestSystemBackend_Plugin_MismatchType(t *testing.T) { // and expect an error req := logical.TestRequest(t, logical.ReadOperation, "mock-0/internal") req.ClientToken = core.Client.Token() - _, err := core.HandleRequest(context.Background(), req) + _, err := core.HandleRequest(namespace.RootContext(nil), req) if err == nil { t.Fatalf("expected error due to mismatch on error type: %s", err) } @@ -145,7 +145,7 @@ func testPlugin_CatalogRemoved(t *testing.T, btype logical.BackendType, testMoun // Remove the plugin from the catalog req := logical.TestRequest(t, logical.DeleteOperation, "sys/plugins/catalog/mock-plugin") req.ClientToken = core.Client.Token() - resp, err := core.HandleRequest(context.Background(), req) + resp, err := core.HandleRequest(namespace.RootContext(nil), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } @@ -230,7 +230,7 @@ func testPlugin_continueOnError(t *testing.T, btype logical.BackendType, mismatc // Get the registered plugin req := logical.TestRequest(t, logical.ReadOperation, "sys/plugins/catalog/mock-plugin") req.ClientToken = core.Client.Token() - resp, err := core.HandleRequest(context.Background(), req) + resp, err := core.HandleRequest(namespace.RootContext(nil), req) if err != nil || resp == nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } @@ -248,7 +248,7 @@ func testPlugin_continueOnError(t *testing.T, btype logical.BackendType, mismatc "command": filepath.Base(command), } req.ClientToken = core.Client.Token() - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.RootContext(nil), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } @@ -294,7 +294,7 @@ func testPlugin_continueOnError(t *testing.T, btype logical.BackendType, mismatc "plugin": "mock-plugin", } req.ClientToken = core.Client.Token() - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.RootContext(nil), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%#v", err, resp) } @@ -310,7 +310,7 @@ func testPlugin_continueOnError(t *testing.T, btype logical.BackendType, mismatc req = logical.TestRequest(t, logical.ReadOperation, reqPath) req.ClientToken = core.Client.Token() - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -329,7 +329,7 @@ func TestSystemBackend_Plugin_autoReload(t *testing.T) { req := logical.TestRequest(t, logical.UpdateOperation, "mock-0/internal") req.ClientToken = core.Client.Token() req.Data["value"] = "baz" - resp, err := core.HandleRequest(context.Background(), req) + resp, err := core.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -340,7 +340,7 @@ func TestSystemBackend_Plugin_autoReload(t *testing.T) { // Call errors/rpc endpoint to trigger reload req = logical.TestRequest(t, logical.ReadOperation, "mock-0/errors/rpc") req.ClientToken = core.Client.Token() - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.RootContext(nil), req) if err == nil { t.Fatalf("expected error from error/rpc request") } @@ -348,7 +348,7 @@ func TestSystemBackend_Plugin_autoReload(t *testing.T) { // Check internal value to make sure it's reset req = logical.TestRequest(t, logical.ReadOperation, "mock-0/internal") req.ClientToken = core.Client.Token() - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } diff --git a/vault/logical_system_paths.go b/vault/logical_system_paths.go new file mode 100644 index 0000000000..1a33c5618f --- /dev/null +++ b/vault/logical_system_paths.go @@ -0,0 +1,1080 @@ +package vault + +import ( + "strings" + + "github.com/hashicorp/vault/logical" + "github.com/hashicorp/vault/logical/framework" +) + +func (b *SystemBackend) configPaths() []*framework.Path { + return []*framework.Path{ + { + Pattern: "config/cors$", + + Fields: map[string]*framework.FieldSchema{ + "enable": &framework.FieldSchema{ + Type: framework.TypeBool, + Description: "Enables or disables CORS headers on requests.", + }, + "allowed_origins": &framework.FieldSchema{ + Type: framework.TypeCommaStringSlice, + Description: "A comma-separated string or array of strings indicating origins that may make cross-origin requests.", + }, + "allowed_headers": &framework.FieldSchema{ + Type: framework.TypeCommaStringSlice, + Description: "A comma-separated string or array of strings indicating headers that are allowed on cross-origin requests.", + }, + }, + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.ReadOperation: b.handleCORSRead, + logical.UpdateOperation: b.handleCORSUpdate, + logical.DeleteOperation: b.handleCORSDelete, + }, + + HelpDescription: strings.TrimSpace(sysHelp["config/cors"][0]), + HelpSynopsis: strings.TrimSpace(sysHelp["config/cors"][1]), + }, + + { + Pattern: "config/ui/headers/" + framework.GenericNameRegex("header"), + + Fields: map[string]*framework.FieldSchema{ + "header": &framework.FieldSchema{ + Type: framework.TypeString, + Description: "The name of the header.", + }, + "values": &framework.FieldSchema{ + Type: framework.TypeStringSlice, + Description: "The values to set the header.", + }, + }, + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.ReadOperation: b.handleConfigUIHeadersRead, + logical.UpdateOperation: b.handleConfigUIHeadersUpdate, + logical.DeleteOperation: b.handleConfigUIHeadersDelete, + }, + + HelpDescription: strings.TrimSpace(sysHelp["config/ui/headers"][0]), + HelpSynopsis: strings.TrimSpace(sysHelp["config/ui/headers"][1]), + }, + + { + Pattern: "config/ui/headers/$", + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.ListOperation: b.handleConfigUIHeadersList, + }, + + HelpDescription: strings.TrimSpace(sysHelp["config/ui/headers"][0]), + HelpSynopsis: strings.TrimSpace(sysHelp["config/ui/headers"][1]), + }, + + { + Pattern: "generate-root(/attempt)?$", + HelpSynopsis: strings.TrimSpace(sysHelp["generate-root"][0]), + HelpDescription: strings.TrimSpace(sysHelp["generate-root"][1]), + }, + + { + Pattern: "init$", + HelpSynopsis: strings.TrimSpace(sysHelp["init"][0]), + HelpDescription: strings.TrimSpace(sysHelp["init"][1]), + }, + } +} + +func (b *SystemBackend) rekeyPaths() []*framework.Path { + return []*framework.Path{ + { + Pattern: "rekey/backup$", + + Fields: map[string]*framework.FieldSchema{}, + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.ReadOperation: b.handleRekeyRetrieveBarrier, + logical.DeleteOperation: b.handleRekeyDeleteBarrier, + }, + + HelpSynopsis: strings.TrimSpace(sysHelp["rekey_backup"][0]), + HelpDescription: strings.TrimSpace(sysHelp["rekey_backup"][0]), + }, + + { + Pattern: "rekey/recovery-key-backup$", + + Fields: map[string]*framework.FieldSchema{}, + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.ReadOperation: b.handleRekeyRetrieveRecovery, + logical.DeleteOperation: b.handleRekeyDeleteRecovery, + }, + + HelpSynopsis: strings.TrimSpace(sysHelp["rekey_backup"][0]), + HelpDescription: strings.TrimSpace(sysHelp["rekey_backup"][0]), + }, + + { + Pattern: "seal-status$", + HelpSynopsis: strings.TrimSpace(sysHelp["seal-status"][0]), + HelpDescription: strings.TrimSpace(sysHelp["seal-status"][1]), + }, + + { + Pattern: "seal$", + HelpSynopsis: strings.TrimSpace(sysHelp["seal"][0]), + HelpDescription: strings.TrimSpace(sysHelp["seal"][1]), + }, + + { + Pattern: "unseal$", + HelpSynopsis: strings.TrimSpace(sysHelp["unseal"][0]), + HelpDescription: strings.TrimSpace(sysHelp["unseal"][1]), + }, + } +} + +func (b *SystemBackend) auditPaths() []*framework.Path { + return []*framework.Path{ + { + Pattern: "audit-hash/(?P.+)", + + Fields: map[string]*framework.FieldSchema{ + "path": &framework.FieldSchema{ + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["audit_path"][0]), + }, + + "input": &framework.FieldSchema{ + Type: framework.TypeString, + }, + }, + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.UpdateOperation: b.handleAuditHash, + }, + + HelpSynopsis: strings.TrimSpace(sysHelp["audit-hash"][0]), + HelpDescription: strings.TrimSpace(sysHelp["audit-hash"][1]), + }, + + { + Pattern: "audit$", + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.ReadOperation: b.handleAuditTable, + }, + + HelpSynopsis: strings.TrimSpace(sysHelp["audit-table"][0]), + HelpDescription: strings.TrimSpace(sysHelp["audit-table"][1]), + }, + + { + Pattern: "audit/(?P.+)", + + Fields: map[string]*framework.FieldSchema{ + "path": &framework.FieldSchema{ + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["audit_path"][0]), + }, + "type": &framework.FieldSchema{ + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["audit_type"][0]), + }, + "description": &framework.FieldSchema{ + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["audit_desc"][0]), + }, + "options": &framework.FieldSchema{ + Type: framework.TypeKVPairs, + Description: strings.TrimSpace(sysHelp["audit_opts"][0]), + }, + "local": &framework.FieldSchema{ + Type: framework.TypeBool, + Default: false, + Description: strings.TrimSpace(sysHelp["mount_local"][0]), + }, + }, + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.UpdateOperation: b.handleEnableAudit, + logical.DeleteOperation: b.handleDisableAudit, + }, + + HelpSynopsis: strings.TrimSpace(sysHelp["audit"][0]), + HelpDescription: strings.TrimSpace(sysHelp["audit"][1]), + }, + + { + Pattern: "config/auditing/request-headers/(?P
.+)", + + Fields: map[string]*framework.FieldSchema{ + "header": &framework.FieldSchema{ + Type: framework.TypeString, + }, + "hmac": &framework.FieldSchema{ + Type: framework.TypeBool, + }, + }, + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.UpdateOperation: b.handleAuditedHeaderUpdate, + logical.DeleteOperation: b.handleAuditedHeaderDelete, + logical.ReadOperation: b.handleAuditedHeaderRead, + }, + + HelpSynopsis: strings.TrimSpace(sysHelp["audited-headers-name"][0]), + HelpDescription: strings.TrimSpace(sysHelp["audited-headers-name"][1]), + }, + + { + Pattern: "config/auditing/request-headers$", + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.ReadOperation: b.handleAuditedHeadersRead, + }, + + HelpSynopsis: strings.TrimSpace(sysHelp["audited-headers"][0]), + HelpDescription: strings.TrimSpace(sysHelp["audited-headers"][1]), + }, + } +} + +func (b *SystemBackend) sealPaths() []*framework.Path { + return []*framework.Path{ + { + Pattern: "key-status$", + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.ReadOperation: b.handleKeyStatus, + }, + + HelpSynopsis: strings.TrimSpace(sysHelp["key-status"][0]), + HelpDescription: strings.TrimSpace(sysHelp["key-status"][1]), + }, + + { + Pattern: "rotate$", + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.UpdateOperation: b.handleRotate, + }, + + HelpSynopsis: strings.TrimSpace(sysHelp["rotate"][0]), + HelpDescription: strings.TrimSpace(sysHelp["rotate"][1]), + }, + } +} + +func (b *SystemBackend) pluginsCatalogPath() *framework.Path { + return &framework.Path{ + Pattern: "plugins/catalog/(?P.+)", + + Fields: map[string]*framework.FieldSchema{ + "name": &framework.FieldSchema{ + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["plugin-catalog_name"][0]), + }, + "sha256": &framework.FieldSchema{ + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["plugin-catalog_sha-256"][0]), + }, + "sha_256": &framework.FieldSchema{ + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["plugin-catalog_sha-256"][0]), + }, + "command": &framework.FieldSchema{ + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["plugin-catalog_command"][0]), + }, + "args": &framework.FieldSchema{ + Type: framework.TypeStringSlice, + Description: strings.TrimSpace(sysHelp["plugin-catalog_args"][0]), + }, + }, + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.UpdateOperation: b.handlePluginCatalogUpdate, + logical.DeleteOperation: b.handlePluginCatalogDelete, + logical.ReadOperation: b.handlePluginCatalogRead, + }, + + HelpSynopsis: strings.TrimSpace(sysHelp["plugin-catalog"][0]), + HelpDescription: strings.TrimSpace(sysHelp["plugin-catalog"][1]), + } +} + +func (b *SystemBackend) pluginsReloadPath() *framework.Path { + return &framework.Path{ + Pattern: "plugins/reload/backend$", + + Fields: map[string]*framework.FieldSchema{ + "plugin": &framework.FieldSchema{ + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["plugin-backend-reload-plugin"][0]), + }, + "mounts": &framework.FieldSchema{ + Type: framework.TypeCommaStringSlice, + Description: strings.TrimSpace(sysHelp["plugin-backend-reload-mounts"][0]), + }, + }, + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.UpdateOperation: b.handlePluginReloadUpdate, + }, + + HelpSynopsis: strings.TrimSpace(sysHelp["plugin-reload"][0]), + HelpDescription: strings.TrimSpace(sysHelp["plugin-reload"][1]), + } +} + +func (b *SystemBackend) pluginsCatalogListPath() *framework.Path { + return &framework.Path{ + Pattern: "plugins/catalog/?$", + + Fields: map[string]*framework.FieldSchema{}, + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.ListOperation: b.handlePluginCatalogList, + }, + + HelpSynopsis: strings.TrimSpace(sysHelp["plugin-catalog"][0]), + HelpDescription: strings.TrimSpace(sysHelp["plugin-catalog"][1]), + } +} + +func (b *SystemBackend) toolsPaths() []*framework.Path { + return []*framework.Path{ + { + Pattern: "tools/hash" + framework.OptionalParamRegex("urlalgorithm"), + Fields: map[string]*framework.FieldSchema{ + "input": &framework.FieldSchema{ + Type: framework.TypeString, + Description: "The base64-encoded input data", + }, + + "algorithm": &framework.FieldSchema{ + Type: framework.TypeString, + Default: "sha2-256", + Description: `Algorithm to use (POST body parameter). Valid values are: + + * sha2-224 + * sha2-256 + * sha2-384 + * sha2-512 + + Defaults to "sha2-256".`, + }, + + "urlalgorithm": &framework.FieldSchema{ + Type: framework.TypeString, + Description: `Algorithm to use (POST URL parameter)`, + }, + + "format": &framework.FieldSchema{ + Type: framework.TypeString, + Default: "hex", + Description: `Encoding format to use. Can be "hex" or "base64". Defaults to "hex".`, + }, + }, + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.UpdateOperation: b.pathHashWrite, + }, + + HelpSynopsis: strings.TrimSpace(sysHelp["hash"][0]), + HelpDescription: strings.TrimSpace(sysHelp["hash"][1]), + }, + + { + Pattern: "tools/random" + framework.OptionalParamRegex("urlbytes"), + Fields: map[string]*framework.FieldSchema{ + "urlbytes": &framework.FieldSchema{ + Type: framework.TypeString, + Description: "The number of bytes to generate (POST URL parameter)", + }, + + "bytes": &framework.FieldSchema{ + Type: framework.TypeInt, + Default: 32, + Description: "The number of bytes to generate (POST body parameter). Defaults to 32 (256 bits).", + }, + + "format": &framework.FieldSchema{ + Type: framework.TypeString, + Default: "base64", + Description: `Encoding format to use. Can be "hex" or "base64". Defaults to "base64".`, + }, + }, + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.UpdateOperation: b.pathRandomWrite, + }, + + HelpSynopsis: strings.TrimSpace(sysHelp["random"][0]), + HelpDescription: strings.TrimSpace(sysHelp["random"][1]), + }, + } +} + +func (b *SystemBackend) internalUIPaths() []*framework.Path { + return []*framework.Path{ + { + Pattern: "internal/ui/mounts", + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.ReadOperation: b.pathInternalUIMountsRead, + }, + HelpSynopsis: strings.TrimSpace(sysHelp["internal-ui-mounts"][0]), + HelpDescription: strings.TrimSpace(sysHelp["internal-ui-mounts"][1]), + }, + { + Pattern: "internal/ui/mounts/(?P.+)", + Fields: map[string]*framework.FieldSchema{ + "path": &framework.FieldSchema{ + Type: framework.TypeString, + Description: "The path of the mount.", + }, + }, + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.ReadOperation: b.pathInternalUIMountRead, + }, + HelpSynopsis: strings.TrimSpace(sysHelp["internal-ui-mounts"][0]), + HelpDescription: strings.TrimSpace(sysHelp["internal-ui-mounts"][1]), + }, + { + Pattern: "internal/ui/namespaces", + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.ReadOperation: pathInternalUINamespacesRead(b), + }, + HelpSynopsis: strings.TrimSpace(sysHelp["internal-ui-namespaces"][0]), + HelpDescription: strings.TrimSpace(sysHelp["internal-ui-namespaces"][1]), + }, + { + Pattern: "internal/ui/resultant-acl", + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.ReadOperation: b.pathInternalUIResultantACL, + }, + HelpSynopsis: strings.TrimSpace(sysHelp["internal-ui-resultant-acl"][0]), + HelpDescription: strings.TrimSpace(sysHelp["internal-ui-resultant-acl"][1]), + }, + } +} + +func (b *SystemBackend) capabilitiesPaths() []*framework.Path { + return []*framework.Path{ + { + Pattern: "capabilities-accessor$", + + Fields: map[string]*framework.FieldSchema{ + "accessor": &framework.FieldSchema{ + Type: framework.TypeString, + Description: "Accessor of the token for which capabilities are being queried.", + }, + "path": &framework.FieldSchema{ + Type: framework.TypeCommaStringSlice, + Description: "(DEPRECATED) Path on which capabilities are being queried. Use 'paths' instead.", + }, + "paths": &framework.FieldSchema{ + Type: framework.TypeCommaStringSlice, + Description: "Paths on which capabilities are being queried.", + }, + }, + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.UpdateOperation: b.handleCapabilitiesAccessor, + }, + + HelpSynopsis: strings.TrimSpace(sysHelp["capabilities_accessor"][0]), + HelpDescription: strings.TrimSpace(sysHelp["capabilities_accessor"][1]), + }, + + { + Pattern: "capabilities$", + + Fields: map[string]*framework.FieldSchema{ + "token": &framework.FieldSchema{ + Type: framework.TypeString, + Description: "Token for which capabilities are being queried.", + }, + "path": &framework.FieldSchema{ + Type: framework.TypeCommaStringSlice, + Description: "(DEPRECATED) Path on which capabilities are being queried. Use 'paths' instead.", + }, + "paths": &framework.FieldSchema{ + Type: framework.TypeCommaStringSlice, + Description: "Paths on which capabilities are being queried.", + }, + }, + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.UpdateOperation: b.handleCapabilities, + }, + + HelpSynopsis: strings.TrimSpace(sysHelp["capabilities"][0]), + HelpDescription: strings.TrimSpace(sysHelp["capabilities"][1]), + }, + + { + Pattern: "capabilities-self$", + + Fields: map[string]*framework.FieldSchema{ + "token": &framework.FieldSchema{ + Type: framework.TypeString, + Description: "Token for which capabilities are being queried.", + }, + "path": &framework.FieldSchema{ + Type: framework.TypeCommaStringSlice, + Description: "(DEPRECATED) Path on which capabilities are being queried. Use 'paths' instead.", + }, + "paths": &framework.FieldSchema{ + Type: framework.TypeCommaStringSlice, + Description: "Paths on which capabilities are being queried.", + }, + }, + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.UpdateOperation: b.handleCapabilities, + }, + + HelpSynopsis: strings.TrimSpace(sysHelp["capabilities_self"][0]), + HelpDescription: strings.TrimSpace(sysHelp["capabilities_self"][1]), + }, + } +} + +func (b *SystemBackend) leasePaths() []*framework.Path { + return []*framework.Path{ + { + Pattern: "leases/lookup/(?P.+?)?", + + Fields: map[string]*framework.FieldSchema{ + "prefix": &framework.FieldSchema{ + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["leases-list-prefix"][0]), + }, + }, + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.ListOperation: b.handleLeaseLookupList, + }, + + HelpSynopsis: strings.TrimSpace(sysHelp["leases"][0]), + HelpDescription: strings.TrimSpace(sysHelp["leases"][1]), + }, + + { + Pattern: "leases/lookup", + + Fields: map[string]*framework.FieldSchema{ + "lease_id": &framework.FieldSchema{ + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["lease_id"][0]), + }, + }, + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.UpdateOperation: b.handleLeaseLookup, + }, + + HelpSynopsis: strings.TrimSpace(sysHelp["leases"][0]), + HelpDescription: strings.TrimSpace(sysHelp["leases"][1]), + }, + + { + Pattern: "(leases/)?renew" + framework.OptionalParamRegex("url_lease_id"), + + Fields: map[string]*framework.FieldSchema{ + "url_lease_id": &framework.FieldSchema{ + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["lease_id"][0]), + }, + "lease_id": &framework.FieldSchema{ + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["lease_id"][0]), + }, + "increment": &framework.FieldSchema{ + Type: framework.TypeDurationSecond, + Description: strings.TrimSpace(sysHelp["increment"][0]), + }, + }, + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.UpdateOperation: b.handleRenew, + }, + + HelpSynopsis: strings.TrimSpace(sysHelp["renew"][0]), + HelpDescription: strings.TrimSpace(sysHelp["renew"][1]), + }, + + { + Pattern: "(leases/)?revoke" + framework.OptionalParamRegex("url_lease_id"), + + Fields: map[string]*framework.FieldSchema{ + "url_lease_id": &framework.FieldSchema{ + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["lease_id"][0]), + }, + "lease_id": &framework.FieldSchema{ + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["lease_id"][0]), + }, + "sync": &framework.FieldSchema{ + Type: framework.TypeBool, + Default: true, + Description: strings.TrimSpace(sysHelp["revoke-sync"][0]), + }, + }, + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.UpdateOperation: b.handleRevoke, + }, + + HelpSynopsis: strings.TrimSpace(sysHelp["revoke"][0]), + HelpDescription: strings.TrimSpace(sysHelp["revoke"][1]), + }, + + { + Pattern: "(leases/)?revoke-force/(?P.+)", + + Fields: map[string]*framework.FieldSchema{ + "prefix": &framework.FieldSchema{ + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["revoke-force-path"][0]), + }, + }, + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.UpdateOperation: b.handleRevokeForce, + }, + + HelpSynopsis: strings.TrimSpace(sysHelp["revoke-force"][0]), + HelpDescription: strings.TrimSpace(sysHelp["revoke-force"][1]), + }, + + { + Pattern: "(leases/)?revoke-prefix/(?P.+)", + + Fields: map[string]*framework.FieldSchema{ + "prefix": &framework.FieldSchema{ + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["revoke-prefix-path"][0]), + }, + "sync": &framework.FieldSchema{ + Type: framework.TypeBool, + Default: true, + Description: strings.TrimSpace(sysHelp["revoke-sync"][0]), + }, + }, + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.UpdateOperation: b.handleRevokePrefix, + }, + + HelpSynopsis: strings.TrimSpace(sysHelp["revoke-prefix"][0]), + HelpDescription: strings.TrimSpace(sysHelp["revoke-prefix"][1]), + }, + + { + Pattern: "leases/tidy$", + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.UpdateOperation: b.handleTidyLeases, + }, + + HelpSynopsis: strings.TrimSpace(sysHelp["tidy_leases"][0]), + HelpDescription: strings.TrimSpace(sysHelp["tidy_leases"][1]), + }, + } +} + +func (b *SystemBackend) remountPath() *framework.Path { + return &framework.Path{ + Pattern: "remount", + + Fields: map[string]*framework.FieldSchema{ + "from": &framework.FieldSchema{ + Type: framework.TypeString, + Description: "The previous mount point.", + }, + "to": &framework.FieldSchema{ + Type: framework.TypeString, + Description: "The new mount point.", + }, + }, + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.UpdateOperation: b.handleRemount, + }, + + HelpSynopsis: strings.TrimSpace(sysHelp["remount"][0]), + HelpDescription: strings.TrimSpace(sysHelp["remount"][1]), + } +} + +func (b *SystemBackend) authPaths() []*framework.Path { + return []*framework.Path{ + { + Pattern: "auth$", + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.ReadOperation: b.handleAuthTable, + }, + HelpSynopsis: strings.TrimSpace(sysHelp["auth-table"][0]), + HelpDescription: strings.TrimSpace(sysHelp["auth-table"][1]), + }, + { + Pattern: "auth/(?P.+?)/tune$", + Fields: map[string]*framework.FieldSchema{ + "path": &framework.FieldSchema{ + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["auth_tune"][0]), + }, + "default_lease_ttl": &framework.FieldSchema{ + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["tune_default_lease_ttl"][0]), + }, + "max_lease_ttl": &framework.FieldSchema{ + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["tune_max_lease_ttl"][0]), + }, + "description": &framework.FieldSchema{ + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["auth_desc"][0]), + }, + "audit_non_hmac_request_keys": &framework.FieldSchema{ + Type: framework.TypeCommaStringSlice, + Description: strings.TrimSpace(sysHelp["tune_audit_non_hmac_request_keys"][0]), + }, + "audit_non_hmac_response_keys": &framework.FieldSchema{ + Type: framework.TypeCommaStringSlice, + Description: strings.TrimSpace(sysHelp["tune_audit_non_hmac_response_keys"][0]), + }, + "options": &framework.FieldSchema{ + Type: framework.TypeKVPairs, + Description: strings.TrimSpace(sysHelp["tune_mount_options"][0]), + }, + "listing_visibility": &framework.FieldSchema{ + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["listing_visibility"][0]), + }, + "passthrough_request_headers": &framework.FieldSchema{ + Type: framework.TypeCommaStringSlice, + Description: strings.TrimSpace(sysHelp["passthrough_request_headers"][0]), + }, + }, + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.ReadOperation: b.handleAuthTuneRead, + logical.UpdateOperation: b.handleAuthTuneWrite, + }, + HelpSynopsis: strings.TrimSpace(sysHelp["auth_tune"][0]), + HelpDescription: strings.TrimSpace(sysHelp["auth_tune"][1]), + }, + { + Pattern: "auth/(?P.+)", + Fields: map[string]*framework.FieldSchema{ + "path": &framework.FieldSchema{ + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["auth_path"][0]), + }, + "type": &framework.FieldSchema{ + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["auth_type"][0]), + }, + "description": &framework.FieldSchema{ + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["auth_desc"][0]), + }, + "config": &framework.FieldSchema{ + Type: framework.TypeMap, + Description: strings.TrimSpace(sysHelp["auth_config"][0]), + }, + "local": &framework.FieldSchema{ + Type: framework.TypeBool, + Default: false, + Description: strings.TrimSpace(sysHelp["mount_local"][0]), + }, + "seal_wrap": &framework.FieldSchema{ + Type: framework.TypeBool, + Default: false, + Description: strings.TrimSpace(sysHelp["seal_wrap"][0]), + }, + "plugin_name": &framework.FieldSchema{ + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["auth_plugin"][0]), + }, + "options": &framework.FieldSchema{ + Type: framework.TypeKVPairs, + Description: strings.TrimSpace(sysHelp["auth_options"][0]), + }, + }, + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.UpdateOperation: b.handleEnableAuth, + logical.DeleteOperation: b.handleDisableAuth, + }, + HelpSynopsis: strings.TrimSpace(sysHelp["auth"][0]), + HelpDescription: strings.TrimSpace(sysHelp["auth"][1]), + }, + } +} + +func (b *SystemBackend) policyPaths() []*framework.Path { + return []*framework.Path{ + { + Pattern: "policy/?$", + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.ReadOperation: b.handlePoliciesList(PolicyTypeACL), + logical.ListOperation: b.handlePoliciesList(PolicyTypeACL), + }, + + HelpSynopsis: strings.TrimSpace(sysHelp["policy-list"][0]), + HelpDescription: strings.TrimSpace(sysHelp["policy-list"][1]), + }, + + { + Pattern: "policy/(?P.+)", + + Fields: map[string]*framework.FieldSchema{ + "name": &framework.FieldSchema{ + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["policy-name"][0]), + }, + "rules": &framework.FieldSchema{ + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["policy-rules"][0]), + }, + "policy": &framework.FieldSchema{ + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["policy-rules"][0]), + }, + }, + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.ReadOperation: b.handlePoliciesRead(PolicyTypeACL), + logical.UpdateOperation: b.handlePoliciesSet(PolicyTypeACL), + logical.DeleteOperation: b.handlePoliciesDelete(PolicyTypeACL), + }, + + HelpSynopsis: strings.TrimSpace(sysHelp["policy"][0]), + HelpDescription: strings.TrimSpace(sysHelp["policy"][1]), + }, + + { + Pattern: "policies/acl/?$", + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.ListOperation: b.handlePoliciesList(PolicyTypeACL), + }, + + HelpSynopsis: strings.TrimSpace(sysHelp["policy-list"][0]), + HelpDescription: strings.TrimSpace(sysHelp["policy-list"][1]), + }, + + { + Pattern: "policies/acl/(?P.+)", + + Fields: map[string]*framework.FieldSchema{ + "name": &framework.FieldSchema{ + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["policy-name"][0]), + }, + "policy": &framework.FieldSchema{ + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["policy-rules"][0]), + }, + }, + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.ReadOperation: b.handlePoliciesRead(PolicyTypeACL), + logical.UpdateOperation: b.handlePoliciesSet(PolicyTypeACL), + logical.DeleteOperation: b.handlePoliciesDelete(PolicyTypeACL), + }, + + HelpSynopsis: strings.TrimSpace(sysHelp["policy"][0]), + HelpDescription: strings.TrimSpace(sysHelp["policy"][1]), + }, + } +} + +func (b *SystemBackend) wrappingPaths() []*framework.Path { + return []*framework.Path{ + { + Pattern: "wrapping/wrap$", + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.UpdateOperation: b.handleWrappingWrap, + }, + + HelpSynopsis: strings.TrimSpace(sysHelp["wrap"][0]), + HelpDescription: strings.TrimSpace(sysHelp["wrap"][1]), + }, + + { + Pattern: "wrapping/unwrap$", + + Fields: map[string]*framework.FieldSchema{ + "token": &framework.FieldSchema{ + Type: framework.TypeString, + }, + }, + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.UpdateOperation: b.handleWrappingUnwrap, + }, + + HelpSynopsis: strings.TrimSpace(sysHelp["unwrap"][0]), + HelpDescription: strings.TrimSpace(sysHelp["unwrap"][1]), + }, + + { + Pattern: "wrapping/lookup$", + + Fields: map[string]*framework.FieldSchema{ + "token": &framework.FieldSchema{ + Type: framework.TypeString, + }, + }, + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.UpdateOperation: b.handleWrappingLookup, + logical.ReadOperation: b.handleWrappingLookup, + }, + + HelpSynopsis: strings.TrimSpace(sysHelp["wraplookup"][0]), + HelpDescription: strings.TrimSpace(sysHelp["wraplookup"][1]), + }, + + { + Pattern: "wrapping/rewrap$", + + Fields: map[string]*framework.FieldSchema{ + "token": &framework.FieldSchema{ + Type: framework.TypeString, + }, + }, + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.UpdateOperation: b.handleWrappingRewrap, + }, + + HelpSynopsis: strings.TrimSpace(sysHelp["rewrap"][0]), + HelpDescription: strings.TrimSpace(sysHelp["rewrap"][1]), + }, + } +} + +func (b *SystemBackend) mountPaths() []*framework.Path { + return []*framework.Path{ + { + Pattern: "mounts/(?P.+?)/tune$", + + Fields: map[string]*framework.FieldSchema{ + "path": &framework.FieldSchema{ + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["mount_path"][0]), + }, + "default_lease_ttl": &framework.FieldSchema{ + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["tune_default_lease_ttl"][0]), + }, + "max_lease_ttl": &framework.FieldSchema{ + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["tune_max_lease_ttl"][0]), + }, + "description": &framework.FieldSchema{ + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["auth_desc"][0]), + }, + "audit_non_hmac_request_keys": &framework.FieldSchema{ + Type: framework.TypeCommaStringSlice, + Description: strings.TrimSpace(sysHelp["tune_audit_non_hmac_request_keys"][0]), + }, + "audit_non_hmac_response_keys": &framework.FieldSchema{ + Type: framework.TypeCommaStringSlice, + Description: strings.TrimSpace(sysHelp["tune_audit_non_hmac_response_keys"][0]), + }, + "options": &framework.FieldSchema{ + Type: framework.TypeKVPairs, + Description: strings.TrimSpace(sysHelp["tune_mount_options"][0]), + }, + "listing_visibility": &framework.FieldSchema{ + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["listing_visibility"][0]), + }, + "passthrough_request_headers": &framework.FieldSchema{ + Type: framework.TypeCommaStringSlice, + Description: strings.TrimSpace(sysHelp["passthrough_request_headers"][0]), + }, + }, + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.ReadOperation: b.handleMountTuneRead, + logical.UpdateOperation: b.handleMountTuneWrite, + }, + + HelpSynopsis: strings.TrimSpace(sysHelp["mount_tune"][0]), + HelpDescription: strings.TrimSpace(sysHelp["mount_tune"][1]), + }, + + { + Pattern: "mounts/(?P.+?)", + + Fields: map[string]*framework.FieldSchema{ + "path": &framework.FieldSchema{ + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["mount_path"][0]), + }, + "type": &framework.FieldSchema{ + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["mount_type"][0]), + }, + "description": &framework.FieldSchema{ + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["mount_desc"][0]), + }, + "config": &framework.FieldSchema{ + Type: framework.TypeMap, + Description: strings.TrimSpace(sysHelp["mount_config"][0]), + }, + "local": &framework.FieldSchema{ + Type: framework.TypeBool, + Default: false, + Description: strings.TrimSpace(sysHelp["mount_local"][0]), + }, + "seal_wrap": &framework.FieldSchema{ + Type: framework.TypeBool, + Default: false, + Description: strings.TrimSpace(sysHelp["seal_wrap"][0]), + }, + "plugin_name": &framework.FieldSchema{ + Type: framework.TypeString, + Description: strings.TrimSpace(sysHelp["mount_plugin_name"][0]), + }, + "options": &framework.FieldSchema{ + Type: framework.TypeKVPairs, + Description: strings.TrimSpace(sysHelp["mount_options"][0]), + }, + }, + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.UpdateOperation: b.handleMount, + logical.DeleteOperation: b.handleUnmount, + }, + + HelpSynopsis: strings.TrimSpace(sysHelp["mount"][0]), + HelpDescription: strings.TrimSpace(sysHelp["mount"][1]), + }, + + { + Pattern: "mounts$", + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.ReadOperation: b.handleMountTable, + }, + + HelpSynopsis: strings.TrimSpace(sysHelp["mounts"][0]), + HelpDescription: strings.TrimSpace(sysHelp["mounts"][1]), + }, + } +} diff --git a/vault/logical_system_test.go b/vault/logical_system_test.go index e6e0ed621c..8a1001e6bf 100644 --- a/vault/logical_system_test.go +++ b/vault/logical_system_test.go @@ -18,6 +18,7 @@ import ( hclog "github.com/hashicorp/go-hclog" "github.com/hashicorp/vault/audit" "github.com/hashicorp/vault/helper/builtinplugins" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/helper/salt" "github.com/hashicorp/vault/logical" "github.com/mitchellh/mapstructure" @@ -32,7 +33,11 @@ func TestSystemBackend_RootPaths(t *testing.T) { "raw", "raw/*", "replication/primary/secondary-token", + "replication/performance/primary/secondary-token", + "replication/dr/primary/secondary-token", "replication/reindex", + "replication/dr/reindex", + "replication/performance/reindex", "rotate", "config/cors", "config/auditing/*", @@ -61,7 +66,7 @@ func TestSystemConfigCORS(t *testing.T) { req := logical.TestRequest(t, logical.UpdateOperation, "config/cors") req.Data["allowed_origins"] = "http://www.example.com" req.Data["allowed_headers"] = "X-Custom-Header" - _, err := b.HandleRequest(context.Background(), req) + _, err := b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatal(err) } @@ -75,7 +80,7 @@ func TestSystemConfigCORS(t *testing.T) { } req = logical.TestRequest(t, logical.ReadOperation, "config/cors") - actual, err := b.HandleRequest(context.Background(), req) + actual, err := b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -85,13 +90,13 @@ func TestSystemConfigCORS(t *testing.T) { } req = logical.TestRequest(t, logical.DeleteOperation, "config/cors") - _, err = b.HandleRequest(context.Background(), req) + _, err = b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } req = logical.TestRequest(t, logical.ReadOperation, "config/cors") - actual, err = b.HandleRequest(context.Background(), req) + actual, err = b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -111,7 +116,7 @@ func TestSystemConfigCORS(t *testing.T) { func TestSystemBackend_mounts(t *testing.T) { b := testSystemBackend(t) req := logical.TestRequest(t, logical.ReadOperation, "mounts") - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -198,7 +203,7 @@ func TestSystemBackend_mount(t *testing.T) { "version": "1", } - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -207,7 +212,7 @@ func TestSystemBackend_mount(t *testing.T) { } req = logical.TestRequest(t, logical.ReadOperation, "mounts") - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -305,7 +310,7 @@ func TestSystemBackend_mount_force_no_cache(t *testing.T) { "force_no_cache": true, } - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -313,7 +318,7 @@ func TestSystemBackend_mount_force_no_cache(t *testing.T) { t.Fatalf("bad: %v", resp) } - mountEntry := core.router.MatchingMountEntry("prod/secret/") + mountEntry := core.router.MatchingMountEntry(namespace.TestContext(), "prod/secret/") if mountEntry == nil { t.Fatalf("missing mount entry") } @@ -327,7 +332,7 @@ func TestSystemBackend_mount_invalid(t *testing.T) { req := logical.TestRequest(t, logical.UpdateOperation, "mounts/prod/secret/") req.Data["type"] = "nope" - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != logical.ErrInvalidRequest { t.Fatalf("err: %v", err) } @@ -340,7 +345,7 @@ func TestSystemBackend_unmount(t *testing.T) { b := testSystemBackend(t) req := logical.TestRequest(t, logical.DeleteOperation, "mounts/secret/") - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -371,8 +376,8 @@ func TestSystemBackend_PathCapabilities(t *testing.T) { core, b, rootToken := testCoreSystemBackend(t) - policy, _ := ParseACLPolicy(capabilitiesPolicy) - err = core.policyStore.SetPolicy(context.Background(), policy) + policy, _ := ParseACLPolicy(namespace.RootNamespace, capabilitiesPolicy) + err = core.policyStore.SetPolicy(namespace.TestContext(), policy) if err != nil { t.Fatalf("err: %v", err) } @@ -394,7 +399,7 @@ func TestSystemBackend_PathCapabilities(t *testing.T) { } // Check the capabilities using the root token - resp, err = b.HandleRequest(context.Background(), &logical.Request{ + resp, err = b.HandleRequest(namespace.TestContext(), &logical.Request{ Path: "capabilities", Operation: logical.UpdateOperation, Data: map[string]interface{}{ @@ -408,7 +413,7 @@ func TestSystemBackend_PathCapabilities(t *testing.T) { rootCheckFunc(t, resp) // Check the capabilities using capabilities-self - resp, err = b.HandleRequest(context.Background(), &logical.Request{ + resp, err = b.HandleRequest(namespace.TestContext(), &logical.Request{ ClientToken: rootToken, Path: "capabilities-self", Operation: logical.UpdateOperation, @@ -422,13 +427,13 @@ func TestSystemBackend_PathCapabilities(t *testing.T) { rootCheckFunc(t, resp) // Lookup the accessor of the root token - te, err := core.tokenStore.Lookup(context.Background(), rootToken) + te, err := core.tokenStore.Lookup(namespace.TestContext(), rootToken) if err != nil { t.Fatal(err) } // Check the capabilities using capabilities-accessor endpoint - resp, err = b.HandleRequest(context.Background(), &logical.Request{ + resp, err = b.HandleRequest(namespace.TestContext(), &logical.Request{ Path: "capabilities-accessor", Operation: logical.UpdateOperation, Data: map[string]interface{}{ @@ -459,7 +464,7 @@ func TestSystemBackend_PathCapabilities(t *testing.T) { } // Check the capabilities using a non-root token - resp, err = b.HandleRequest(context.Background(), &logical.Request{ + resp, err = b.HandleRequest(namespace.TestContext(), &logical.Request{ Path: "capabilities", Operation: logical.UpdateOperation, Data: map[string]interface{}{ @@ -474,7 +479,7 @@ func TestSystemBackend_PathCapabilities(t *testing.T) { // Check the capabilities of a non-root token using capabilities-self // endpoint - resp, err = b.HandleRequest(context.Background(), &logical.Request{ + resp, err = b.HandleRequest(namespace.TestContext(), &logical.Request{ ClientToken: "tokenid", Path: "capabilities-self", Operation: logical.UpdateOperation, @@ -488,14 +493,14 @@ func TestSystemBackend_PathCapabilities(t *testing.T) { nonRootCheckFunc(t, resp) // Lookup the accessor of the non-root token - te, err = core.tokenStore.Lookup(context.Background(), "tokenid") + te, err = core.tokenStore.Lookup(namespace.TestContext(), "tokenid") if err != nil { t.Fatal(err) } // Check the capabilities using a non-root token using // capabilities-accessor endpoint - resp, err = b.HandleRequest(context.Background(), &logical.Request{ + resp, err = b.HandleRequest(namespace.TestContext(), &logical.Request{ Path: "capabilities-accessor", Operation: logical.UpdateOperation, Data: map[string]interface{}{ @@ -524,7 +529,7 @@ func testCapabilities(t *testing.T, endpoint string) { } req.Data["path"] = "any_path" - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatal(err) } @@ -538,8 +543,8 @@ func testCapabilities(t *testing.T, endpoint string) { t.Fatalf("bad: got\n%#v\nexpected\n%#v\n", actual, expected) } - policy, _ := ParseACLPolicy(capabilitiesPolicy) - err = core.policyStore.SetPolicy(context.Background(), policy) + policy, _ := ParseACLPolicy(namespace.RootNamespace, capabilitiesPolicy) + err = core.policyStore.SetPolicy(namespace.TestContext(), policy) if err != nil { t.Fatalf("err: %v", err) } @@ -553,7 +558,7 @@ func testCapabilities(t *testing.T, endpoint string) { } req.Data["path"] = "foo/bar" - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -570,7 +575,7 @@ func testCapabilities(t *testing.T, endpoint string) { func TestSystemBackend_CapabilitiesAccessor_BC(t *testing.T) { core, b, rootToken := testCoreSystemBackend(t) - te, err := core.tokenStore.Lookup(context.Background(), rootToken) + te, err := core.tokenStore.Lookup(namespace.TestContext(), rootToken) if err != nil { t.Fatal(err) } @@ -580,7 +585,7 @@ func TestSystemBackend_CapabilitiesAccessor_BC(t *testing.T) { req.Data["accessor"] = te.Accessor req.Data["path"] = "any_path" - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -594,15 +599,15 @@ func TestSystemBackend_CapabilitiesAccessor_BC(t *testing.T) { t.Fatalf("bad: got\n%#v\nexpected\n%#v\n", actual, expected) } - policy, _ := ParseACLPolicy(capabilitiesPolicy) - err = core.policyStore.SetPolicy(context.Background(), policy) + policy, _ := ParseACLPolicy(namespace.RootNamespace, capabilitiesPolicy) + err = core.policyStore.SetPolicy(namespace.TestContext(), policy) if err != nil { t.Fatalf("err: %v", err) } testMakeTokenViaBackend(t, core.tokenStore, rootToken, "tokenid", "", []string{"test"}) - te, err = core.tokenStore.Lookup(context.Background(), "tokenid") + te, err = core.tokenStore.Lookup(namespace.TestContext(), "tokenid") if err != nil { t.Fatal(err) } @@ -611,7 +616,7 @@ func TestSystemBackend_CapabilitiesAccessor_BC(t *testing.T) { req.Data["accessor"] = te.Accessor req.Data["path"] = "foo/bar" - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -633,7 +638,7 @@ func TestSystemBackend_remount(t *testing.T) { req.Data["from"] = "secret" req.Data["to"] = "foo" req.Data["config"] = structs.Map(MountConfig{}) - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -649,7 +654,7 @@ func TestSystemBackend_remount_invalid(t *testing.T) { req.Data["from"] = "unknown" req.Data["to"] = "foo" req.Data["config"] = structs.Map(MountConfig{}) - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != logical.ErrInvalidRequest { t.Fatalf("err: %v", err) } @@ -664,7 +669,7 @@ func TestSystemBackend_remount_system(t *testing.T) { req := logical.TestRequest(t, logical.UpdateOperation, "remount") req.Data["from"] = "sys" req.Data["to"] = "foo" - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != logical.ErrInvalidRequest { t.Fatalf("err: %v", err) } @@ -680,7 +685,7 @@ func TestSystemBackend_leases(t *testing.T) { req := logical.TestRequest(t, logical.UpdateOperation, "secret/foo") req.Data["foo"] = "bar" req.ClientToken = root - resp, err := core.HandleRequest(context.Background(), req) + resp, err := core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -691,7 +696,7 @@ func TestSystemBackend_leases(t *testing.T) { // Read a key with a LeaseID req = logical.TestRequest(t, logical.ReadOperation, "secret/foo") req.ClientToken = root - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -702,7 +707,7 @@ func TestSystemBackend_leases(t *testing.T) { // Read lease req = logical.TestRequest(t, logical.UpdateOperation, "leases/lookup") req.Data["lease_id"] = resp.Secret.LeaseID - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -713,7 +718,7 @@ func TestSystemBackend_leases(t *testing.T) { // Invalid lease req = logical.TestRequest(t, logical.UpdateOperation, "leases/lookup") req.Data["lease_id"] = "invalid" - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != logical.ErrInvalidRequest { t.Fatalf("expected invalid request, got err: %v", err) } @@ -726,7 +731,7 @@ func TestSystemBackend_leases_list(t *testing.T) { req := logical.TestRequest(t, logical.UpdateOperation, "secret/foo") req.Data["foo"] = "bar" req.ClientToken = root - resp, err := core.HandleRequest(context.Background(), req) + resp, err := core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -737,7 +742,7 @@ func TestSystemBackend_leases_list(t *testing.T) { // Read a key with a LeaseID req = logical.TestRequest(t, logical.ReadOperation, "secret/foo") req.ClientToken = root - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -747,7 +752,7 @@ func TestSystemBackend_leases_list(t *testing.T) { // List top level req = logical.TestRequest(t, logical.ListOperation, "leases/lookup/") - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -767,7 +772,7 @@ func TestSystemBackend_leases_list(t *testing.T) { // List lease req = logical.TestRequest(t, logical.ListOperation, "leases/lookup/secret/foo") - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -785,7 +790,7 @@ func TestSystemBackend_leases_list(t *testing.T) { // Generate multiple leases req = logical.TestRequest(t, logical.ReadOperation, "secret/foo") req.ClientToken = root - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -795,7 +800,7 @@ func TestSystemBackend_leases_list(t *testing.T) { req = logical.TestRequest(t, logical.ReadOperation, "secret/foo") req.ClientToken = root - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -804,7 +809,7 @@ func TestSystemBackend_leases_list(t *testing.T) { } req = logical.TestRequest(t, logical.ListOperation, "leases/lookup/secret/foo") - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -823,7 +828,7 @@ func TestSystemBackend_leases_list(t *testing.T) { req = logical.TestRequest(t, logical.UpdateOperation, "secret/bar") req.Data["foo"] = "bar" req.ClientToken = root - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -834,7 +839,7 @@ func TestSystemBackend_leases_list(t *testing.T) { // Read a key with a LeaseID req = logical.TestRequest(t, logical.ReadOperation, "secret/bar") req.ClientToken = root - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -843,7 +848,7 @@ func TestSystemBackend_leases_list(t *testing.T) { } req = logical.TestRequest(t, logical.ListOperation, "leases/lookup/secret") - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -870,7 +875,7 @@ func TestSystemBackend_renew(t *testing.T) { req := logical.TestRequest(t, logical.UpdateOperation, "secret/foo") req.Data["foo"] = "bar" req.ClientToken = root - resp, err := core.HandleRequest(context.Background(), req) + resp, err := core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -881,7 +886,7 @@ func TestSystemBackend_renew(t *testing.T) { // Read a key with a LeaseID req = logical.TestRequest(t, logical.ReadOperation, "secret/foo") req.ClientToken = root - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -891,7 +896,7 @@ func TestSystemBackend_renew(t *testing.T) { // Attempt renew req2 := logical.TestRequest(t, logical.UpdateOperation, "leases/renew/"+resp.Secret.LeaseID) - resp2, err := b.HandleRequest(context.Background(), req2) + resp2, err := b.HandleRequest(namespace.TestContext(), req2) if err != logical.ErrInvalidRequest { t.Fatalf("err: %v", err) } @@ -906,7 +911,7 @@ func TestSystemBackend_renew(t *testing.T) { req.Data["foo"] = "bar" req.Data["ttl"] = "180s" req.ClientToken = root - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -917,7 +922,7 @@ func TestSystemBackend_renew(t *testing.T) { // Read a key with a LeaseID req = logical.TestRequest(t, logical.ReadOperation, "secret/foo") req.ClientToken = root - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -927,7 +932,7 @@ func TestSystemBackend_renew(t *testing.T) { // Attempt renew req2 = logical.TestRequest(t, logical.UpdateOperation, "leases/renew/"+resp.Secret.LeaseID) - resp2, err = b.HandleRequest(context.Background(), req2) + resp2, err = b.HandleRequest(namespace.TestContext(), req2) if err != nil { t.Fatalf("err: %v", err) } @@ -944,7 +949,7 @@ func TestSystemBackend_renew(t *testing.T) { // Test the other route path req2 = logical.TestRequest(t, logical.UpdateOperation, "leases/renew") req2.Data["lease_id"] = resp.Secret.LeaseID - resp2, err = b.HandleRequest(context.Background(), req2) + resp2, err = b.HandleRequest(namespace.TestContext(), req2) if err != nil { t.Fatalf("err: %v", err) } @@ -961,7 +966,7 @@ func TestSystemBackend_renew(t *testing.T) { // Test orig path req2 = logical.TestRequest(t, logical.UpdateOperation, "renew") req2.Data["lease_id"] = resp.Secret.LeaseID - resp2, err = b.HandleRequest(context.Background(), req2) + resp2, err = b.HandleRequest(namespace.TestContext(), req2) if err != nil { t.Fatalf("err: %v", err) } @@ -981,7 +986,7 @@ func TestSystemBackend_renew_invalidID(t *testing.T) { // Attempt renew req := logical.TestRequest(t, logical.UpdateOperation, "leases/renew/foobarbaz") - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != logical.ErrInvalidRequest { t.Fatalf("err: %v", err) } @@ -992,7 +997,7 @@ func TestSystemBackend_renew_invalidID(t *testing.T) { // Attempt renew with other method req = logical.TestRequest(t, logical.UpdateOperation, "leases/renew") req.Data["lease_id"] = "foobarbaz" - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != logical.ErrInvalidRequest { t.Fatalf("err: %v", err) } @@ -1006,7 +1011,7 @@ func TestSystemBackend_renew_invalidID_origUrl(t *testing.T) { // Attempt renew req := logical.TestRequest(t, logical.UpdateOperation, "renew/foobarbaz") - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != logical.ErrInvalidRequest { t.Fatalf("err: %v", err) } @@ -1017,7 +1022,7 @@ func TestSystemBackend_renew_invalidID_origUrl(t *testing.T) { // Attempt renew with other method req = logical.TestRequest(t, logical.UpdateOperation, "renew") req.Data["lease_id"] = "foobarbaz" - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != logical.ErrInvalidRequest { t.Fatalf("err: %v", err) } @@ -1034,7 +1039,7 @@ func TestSystemBackend_revoke(t *testing.T) { req.Data["foo"] = "bar" req.Data["lease"] = "1h" req.ClientToken = root - resp, err := core.HandleRequest(context.Background(), req) + resp, err := core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1045,7 +1050,7 @@ func TestSystemBackend_revoke(t *testing.T) { // Read a key with a LeaseID req = logical.TestRequest(t, logical.ReadOperation, "secret/foo") req.ClientToken = root - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1055,7 +1060,7 @@ func TestSystemBackend_revoke(t *testing.T) { // Attempt revoke req2 := logical.TestRequest(t, logical.UpdateOperation, "revoke/"+resp.Secret.LeaseID) - resp2, err := b.HandleRequest(context.Background(), req2) + resp2, err := b.HandleRequest(namespace.TestContext(), req2) if err != nil { t.Fatalf("err: %v %#v", err, resp2) } @@ -1065,7 +1070,7 @@ func TestSystemBackend_revoke(t *testing.T) { // Attempt renew req3 := logical.TestRequest(t, logical.UpdateOperation, "renew/"+resp.Secret.LeaseID) - resp3, err := b.HandleRequest(context.Background(), req3) + resp3, err := b.HandleRequest(namespace.TestContext(), req3) if err != logical.ErrInvalidRequest { t.Fatalf("err: %v", err) } @@ -1076,7 +1081,7 @@ func TestSystemBackend_revoke(t *testing.T) { // Read a key with a LeaseID req = logical.TestRequest(t, logical.ReadOperation, "secret/foo") req.ClientToken = root - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1087,7 +1092,7 @@ func TestSystemBackend_revoke(t *testing.T) { // Test the other route path req2 = logical.TestRequest(t, logical.UpdateOperation, "revoke") req2.Data["lease_id"] = resp.Secret.LeaseID - resp2, err = b.HandleRequest(context.Background(), req2) + resp2, err = b.HandleRequest(namespace.TestContext(), req2) if err != nil { t.Fatalf("err: %v %#v", err, resp2) } @@ -1098,7 +1103,7 @@ func TestSystemBackend_revoke(t *testing.T) { // Read a key with a LeaseID req = logical.TestRequest(t, logical.ReadOperation, "secret/foo") req.ClientToken = root - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1109,7 +1114,7 @@ func TestSystemBackend_revoke(t *testing.T) { // Test the other route path req2 = logical.TestRequest(t, logical.UpdateOperation, "leases/revoke") req2.Data["lease_id"] = resp.Secret.LeaseID - resp2, err = b.HandleRequest(context.Background(), req2) + resp2, err = b.HandleRequest(namespace.TestContext(), req2) if err != nil { t.Fatalf("err: %v %#v", err, resp2) } @@ -1123,7 +1128,7 @@ func TestSystemBackend_revoke_invalidID(t *testing.T) { // Attempt revoke req := logical.TestRequest(t, logical.UpdateOperation, "leases/revoke/foobarbaz") - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1134,7 +1139,7 @@ func TestSystemBackend_revoke_invalidID(t *testing.T) { // Attempt revoke with other method req = logical.TestRequest(t, logical.UpdateOperation, "leases/revoke") req.Data["lease_id"] = "foobarbaz" - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1148,7 +1153,7 @@ func TestSystemBackend_revoke_invalidID_origUrl(t *testing.T) { // Attempt revoke req := logical.TestRequest(t, logical.UpdateOperation, "revoke/foobarbaz") - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1159,7 +1164,7 @@ func TestSystemBackend_revoke_invalidID_origUrl(t *testing.T) { // Attempt revoke with other method req = logical.TestRequest(t, logical.UpdateOperation, "revoke") req.Data["lease_id"] = "foobarbaz" - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1176,7 +1181,7 @@ func TestSystemBackend_revokePrefix(t *testing.T) { req.Data["foo"] = "bar" req.Data["lease"] = "1h" req.ClientToken = root - resp, err := core.HandleRequest(context.Background(), req) + resp, err := core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1187,7 +1192,7 @@ func TestSystemBackend_revokePrefix(t *testing.T) { // Read a key with a LeaseID req = logical.TestRequest(t, logical.ReadOperation, "secret/foo") req.ClientToken = root - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1197,7 +1202,7 @@ func TestSystemBackend_revokePrefix(t *testing.T) { // Attempt revoke req2 := logical.TestRequest(t, logical.UpdateOperation, "leases/revoke-prefix/secret/") - resp2, err := b.HandleRequest(context.Background(), req2) + resp2, err := b.HandleRequest(namespace.TestContext(), req2) if err != nil { t.Fatalf("err: %v %#v", err, resp2) } @@ -1207,7 +1212,7 @@ func TestSystemBackend_revokePrefix(t *testing.T) { // Attempt renew req3 := logical.TestRequest(t, logical.UpdateOperation, "leases/renew/"+resp.Secret.LeaseID) - resp3, err := b.HandleRequest(context.Background(), req3) + resp3, err := b.HandleRequest(namespace.TestContext(), req3) if err != logical.ErrInvalidRequest { t.Fatalf("err: %v", err) } @@ -1224,7 +1229,7 @@ func TestSystemBackend_revokePrefix_origUrl(t *testing.T) { req.Data["foo"] = "bar" req.Data["lease"] = "1h" req.ClientToken = root - resp, err := core.HandleRequest(context.Background(), req) + resp, err := core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1235,7 +1240,7 @@ func TestSystemBackend_revokePrefix_origUrl(t *testing.T) { // Read a key with a LeaseID req = logical.TestRequest(t, logical.ReadOperation, "secret/foo") req.ClientToken = root - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1245,7 +1250,7 @@ func TestSystemBackend_revokePrefix_origUrl(t *testing.T) { // Attempt revoke req2 := logical.TestRequest(t, logical.UpdateOperation, "revoke-prefix/secret/") - resp2, err := b.HandleRequest(context.Background(), req2) + resp2, err := b.HandleRequest(namespace.TestContext(), req2) if err != nil { t.Fatalf("err: %v %#v", err, resp2) } @@ -1255,7 +1260,7 @@ func TestSystemBackend_revokePrefix_origUrl(t *testing.T) { // Attempt renew req3 := logical.TestRequest(t, logical.UpdateOperation, "renew/"+resp.Secret.LeaseID) - resp3, err := b.HandleRequest(context.Background(), req3) + resp3, err := b.HandleRequest(namespace.TestContext(), req3) if err != logical.ErrInvalidRequest { t.Fatalf("err: %v", err) } @@ -1276,7 +1281,7 @@ func TestSystemBackend_revokePrefixAuth_newUrl(t *testing.T) { }, } b := NewSystemBackend(core, hclog.New(&hclog.LoggerOptions{})) - err := b.Backend.Setup(context.Background(), bc) + err := b.Backend.Setup(namespace.TestContext(), bc) if err != nil { t.Fatal(err) } @@ -1284,13 +1289,14 @@ func TestSystemBackend_revokePrefixAuth_newUrl(t *testing.T) { exp := ts.expiration te := &logical.TokenEntry{ - ID: "foo", - Path: "auth/github/login/bar", - TTL: time.Hour, + ID: "foo", + Path: "auth/github/login/bar", + TTL: time.Hour, + NamespaceID: namespace.RootNamespaceID, } testMakeTokenDirectly(t, ts, te) - te, err = ts.Lookup(context.Background(), "foo") + te, err = ts.Lookup(namespace.TestContext(), "foo") if err != nil { t.Fatal(err) } @@ -1305,13 +1311,13 @@ func TestSystemBackend_revokePrefixAuth_newUrl(t *testing.T) { TTL: time.Hour, }, } - err = exp.RegisterAuth(context.Background(), te.Path, auth) + err = exp.RegisterAuth(namespace.TestContext(), te, auth) if err != nil { t.Fatalf("err: %v", err) } req := logical.TestRequest(t, logical.UpdateOperation, "leases/revoke-prefix/auth/github/") - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v %v", err, resp) } @@ -1319,7 +1325,7 @@ func TestSystemBackend_revokePrefixAuth_newUrl(t *testing.T) { t.Fatalf("bad: %#v", resp) } - te, err = ts.Lookup(context.Background(), te.ID) + te, err = ts.Lookup(namespace.TestContext(), te.ID) if err != nil { t.Fatalf("err: %v", err) } @@ -1339,7 +1345,7 @@ func TestSystemBackend_revokePrefixAuth_origUrl(t *testing.T) { }, } b := NewSystemBackend(core, hclog.New(&hclog.LoggerOptions{})) - err := b.Backend.Setup(context.Background(), bc) + err := b.Backend.Setup(namespace.TestContext(), bc) if err != nil { t.Fatal(err) } @@ -1347,13 +1353,14 @@ func TestSystemBackend_revokePrefixAuth_origUrl(t *testing.T) { exp := ts.expiration te := &logical.TokenEntry{ - ID: "foo", - Path: "auth/github/login/bar", - TTL: time.Hour, + ID: "foo", + Path: "auth/github/login/bar", + TTL: time.Hour, + NamespaceID: namespace.RootNamespaceID, } testMakeTokenDirectly(t, ts, te) - te, err = ts.Lookup(context.Background(), "foo") + te, err = ts.Lookup(namespace.TestContext(), "foo") if err != nil { t.Fatal(err) } @@ -1368,13 +1375,13 @@ func TestSystemBackend_revokePrefixAuth_origUrl(t *testing.T) { TTL: time.Hour, }, } - err = exp.RegisterAuth(context.Background(), te.Path, auth) + err = exp.RegisterAuth(namespace.TestContext(), te, auth) if err != nil { t.Fatalf("err: %v", err) } req := logical.TestRequest(t, logical.UpdateOperation, "revoke-prefix/auth/github/") - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v %v", err, resp) } @@ -1382,7 +1389,7 @@ func TestSystemBackend_revokePrefixAuth_origUrl(t *testing.T) { t.Fatalf("bad: %#v", resp) } - te, err = ts.Lookup(context.Background(), te.ID) + te, err = ts.Lookup(namespace.TestContext(), te.ID) if err != nil { t.Fatalf("err: %v", err) } @@ -1394,7 +1401,7 @@ func TestSystemBackend_revokePrefixAuth_origUrl(t *testing.T) { func TestSystemBackend_authTable(t *testing.T) { b := testSystemBackend(t) req := logical.TestRequest(t, logical.ReadOperation, "auth") - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1434,7 +1441,7 @@ func TestSystemBackend_enableAuth(t *testing.T) { req.Data["local"] = true req.Data["seal_wrap"] = true - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1443,7 +1450,7 @@ func TestSystemBackend_enableAuth(t *testing.T) { } req = logical.TestRequest(t, logical.ReadOperation, "auth") - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1488,7 +1495,7 @@ func TestSystemBackend_enableAuth_invalid(t *testing.T) { b := testSystemBackend(t) req := logical.TestRequest(t, logical.UpdateOperation, "auth/foo") req.Data["type"] = "nope" - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != logical.ErrInvalidRequest { t.Fatalf("err: %v", err) } @@ -1506,11 +1513,11 @@ func TestSystemBackend_disableAuth(t *testing.T) { // Register the backend req := logical.TestRequest(t, logical.UpdateOperation, "auth/foo") req.Data["type"] = "noop" - b.HandleRequest(context.Background(), req) + b.HandleRequest(namespace.TestContext(), req) // Deregister it req = logical.TestRequest(t, logical.DeleteOperation, "auth/foo") - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1522,7 +1529,7 @@ func TestSystemBackend_disableAuth(t *testing.T) { func TestSystemBackend_policyList(t *testing.T) { b := testSystemBackend(t) req := logical.TestRequest(t, logical.ReadOperation, "policy") - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1543,7 +1550,7 @@ func TestSystemBackend_policyCRUD(t *testing.T) { rules := `path "foo/" { policy = "read" }` req := logical.TestRequest(t, logical.UpdateOperation, "policy/Foo") req.Data["rules"] = rules - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v %#v", err, resp) } @@ -1553,7 +1560,7 @@ func TestSystemBackend_policyCRUD(t *testing.T) { // Read the policy req = logical.TestRequest(t, logical.ReadOperation, "policy/foo") - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1568,7 +1575,7 @@ func TestSystemBackend_policyCRUD(t *testing.T) { // Read, and make sure that case has been normalized req = logical.TestRequest(t, logical.ReadOperation, "policy/Foo") - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1583,7 +1590,7 @@ func TestSystemBackend_policyCRUD(t *testing.T) { // List the policies req = logical.TestRequest(t, logical.ReadOperation, "policy") - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1598,7 +1605,7 @@ func TestSystemBackend_policyCRUD(t *testing.T) { // Delete the policy req = logical.TestRequest(t, logical.DeleteOperation, "policy/foo") - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1608,7 +1615,7 @@ func TestSystemBackend_policyCRUD(t *testing.T) { // Read the policy (deleted) req = logical.TestRequest(t, logical.ReadOperation, "policy/foo") - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1618,7 +1625,7 @@ func TestSystemBackend_policyCRUD(t *testing.T) { // List the policies (deleted) req = logical.TestRequest(t, logical.ReadOperation, "policy") - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1643,7 +1650,7 @@ func TestSystemBackend_enableAudit(t *testing.T) { req := logical.TestRequest(t, logical.UpdateOperation, "audit/foo") req.Data["type"] = "noop" - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1656,7 +1663,7 @@ func TestSystemBackend_auditHash(t *testing.T) { c, b, _ := testCoreSystemBackend(t) c.auditBackends["noop"] = func(ctx context.Context, config *audit.BackendConfig) (audit.Backend, error) { view := &logical.InmemStorage{} - view.Put(context.Background(), &logical.StorageEntry{ + view.Put(namespace.TestContext(), &logical.StorageEntry{ Key: "salt", Value: []byte("foo"), }) @@ -1674,7 +1681,7 @@ func TestSystemBackend_auditHash(t *testing.T) { req := logical.TestRequest(t, logical.UpdateOperation, "audit/foo") req.Data["type"] = "noop" - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1685,7 +1692,7 @@ func TestSystemBackend_auditHash(t *testing.T) { req = logical.TestRequest(t, logical.UpdateOperation, "audit-hash/foo") req.Data["input"] = "bar" - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1705,7 +1712,7 @@ func TestSystemBackend_enableAudit_invalid(t *testing.T) { b := testSystemBackend(t) req := logical.TestRequest(t, logical.UpdateOperation, "audit/foo") req.Data["type"] = "nope" - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != logical.ErrInvalidRequest { t.Fatalf("err: %v", err) } @@ -1729,10 +1736,10 @@ func TestSystemBackend_auditTable(t *testing.T) { "foo": "bar", } req.Data["local"] = true - b.HandleRequest(context.Background(), req) + b.HandleRequest(namespace.TestContext(), req) req = logical.TestRequest(t, logical.ReadOperation, "audit") - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1767,11 +1774,11 @@ func TestSystemBackend_disableAudit(t *testing.T) { req.Data["options"] = map[string]interface{}{ "foo": "bar", } - b.HandleRequest(context.Background(), req) + b.HandleRequest(namespace.TestContext(), req) // Deregister it req = logical.TestRequest(t, logical.DeleteOperation, "audit/foo") - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1784,7 +1791,7 @@ func TestSystemBackend_rawRead_Compressed(t *testing.T) { b := testSystemBackendRaw(t) req := logical.TestRequest(t, logical.ReadOperation, "raw/core/mounts") - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1797,7 +1804,7 @@ func TestSystemBackend_rawRead_Protected(t *testing.T) { b := testSystemBackendRaw(t) req := logical.TestRequest(t, logical.ReadOperation, "raw/"+keyringPath) - _, err := b.HandleRequest(context.Background(), req) + _, err := b.HandleRequest(namespace.TestContext(), req) if err != logical.ErrInvalidRequest { t.Fatalf("err: %v", err) } @@ -1807,7 +1814,7 @@ func TestSystemBackend_rawWrite_Protected(t *testing.T) { b := testSystemBackendRaw(t) req := logical.TestRequest(t, logical.UpdateOperation, "raw/"+keyringPath) - _, err := b.HandleRequest(context.Background(), req) + _, err := b.HandleRequest(namespace.TestContext(), req) if err != logical.ErrInvalidRequest { t.Fatalf("err: %v", err) } @@ -1818,7 +1825,7 @@ func TestSystemBackend_rawReadWrite(t *testing.T) { req := logical.TestRequest(t, logical.UpdateOperation, "raw/sys/policy/test") req.Data["value"] = `path "secret/" { policy = "read" }` - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1828,7 +1835,7 @@ func TestSystemBackend_rawReadWrite(t *testing.T) { // Read via raw API req = logical.TestRequest(t, logical.ReadOperation, "raw/sys/policy/test") - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1844,7 +1851,7 @@ func TestSystemBackend_rawDelete_Protected(t *testing.T) { b := testSystemBackendRaw(t) req := logical.TestRequest(t, logical.DeleteOperation, "raw/"+keyringPath) - _, err := b.HandleRequest(context.Background(), req) + _, err := b.HandleRequest(namespace.TestContext(), req) if err != logical.ErrInvalidRequest { t.Fatalf("err: %v", err) } @@ -1855,17 +1862,18 @@ func TestSystemBackend_rawDelete(t *testing.T) { // set the policy! p := &Policy{ - Name: "test", - Type: PolicyTypeACL, + Name: "test", + Type: PolicyTypeACL, + namespace: namespace.RootNamespace, } - err := c.policyStore.SetPolicy(context.Background(), p) + err := c.policyStore.SetPolicy(namespace.TestContext(), p) if err != nil { t.Fatalf("err: %v", err) } // Delete the policy req := logical.TestRequest(t, logical.DeleteOperation, "raw/sys/policy/test") - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1875,7 +1883,7 @@ func TestSystemBackend_rawDelete(t *testing.T) { // Policy should be gone c.policyStore.tokenPoliciesLRU.Purge() - out, err := c.policyStore.GetPolicy(context.Background(), "test", PolicyTypeToken) + out, err := c.policyStore.GetPolicy(namespace.TestContext(), "test", PolicyTypeToken) if err != nil { t.Fatalf("err: %v", err) } @@ -1887,7 +1895,7 @@ func TestSystemBackend_rawDelete(t *testing.T) { func TestSystemBackend_keyStatus(t *testing.T) { b := testSystemBackend(t) req := logical.TestRequest(t, logical.ReadOperation, "key-status") - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1905,7 +1913,7 @@ func TestSystemBackend_rotate(t *testing.T) { b := testSystemBackend(t) req := logical.TestRequest(t, logical.UpdateOperation, "rotate") - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1914,7 +1922,7 @@ func TestSystemBackend_rotate(t *testing.T) { } req = logical.TestRequest(t, logical.ReadOperation, "key-status") - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1948,24 +1956,6 @@ func testCoreSystemBackendRaw(t *testing.T) (*Core, logical.Backend, string) { return c, c.systemBackend, root } -func testSystemBackendInternal(t *testing.T, c *Core) logical.Backend { - bc := &logical.BackendConfig{ - Logger: c.logger, - System: logical.StaticSystemView{ - DefaultLeaseTTLVal: time.Hour * 24, - MaxLeaseTTLVal: time.Hour * 24 * 32, - }, - } - - b := NewSystemBackend(c, hclog.New(&hclog.LoggerOptions{})) - err := b.Backend.Setup(context.Background(), bc) - if err != nil { - t.Fatal(err) - } - - return b -} - func TestSystemBackend_PluginCatalog_CRUD(t *testing.T) { c, b, _ := testCoreSystemBackend(t) // Bootstrap the pluginCatalog @@ -1976,7 +1966,7 @@ func TestSystemBackend_PluginCatalog_CRUD(t *testing.T) { c.pluginCatalog.directory = sym req := logical.TestRequest(t, logical.ListOperation, "plugins/catalog/") - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -1986,7 +1976,7 @@ func TestSystemBackend_PluginCatalog_CRUD(t *testing.T) { } req = logical.TestRequest(t, logical.ReadOperation, "plugins/catalog/mysql-database-plugin") - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -2016,7 +2006,7 @@ func TestSystemBackend_PluginCatalog_CRUD(t *testing.T) { req.Data["args"] = []string{"--foo"} req.Data["sha_256"] = hex.EncodeToString([]byte{'1'}) req.Data["command"] = command - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -2025,13 +2015,13 @@ func TestSystemBackend_PluginCatalog_CRUD(t *testing.T) { } delete(req.Data, "args") - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil || resp.Error() != nil { t.Fatalf("err: %v %v", err, resp.Error()) } req = logical.TestRequest(t, logical.ReadOperation, "plugins/catalog/test-plugin") - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -2050,13 +2040,13 @@ func TestSystemBackend_PluginCatalog_CRUD(t *testing.T) { // Delete plugin req = logical.TestRequest(t, logical.DeleteOperation, "plugins/catalog/test-plugin") - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } req = logical.TestRequest(t, logical.ReadOperation, "plugins/catalog/test-plugin") - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if resp != nil || err != nil { t.Fatalf("expected nil response, plugin not deleted correctly got resp: %v, err: %v", resp, err) } @@ -2068,14 +2058,14 @@ func TestSystemBackend_ToolsHash(t *testing.T) { req.Data = map[string]interface{}{ "input": "dGhlIHF1aWNrIGJyb3duIGZveA==", } - _, err := b.HandleRequest(context.Background(), req) + _, err := b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } doRequest := func(req *logical.Request, errExpected bool, expected string) { t.Helper() - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != nil && !errExpected { t.Fatal(err) } @@ -2139,7 +2129,7 @@ func TestSystemBackend_ToolsRandom(t *testing.T) { b := testSystemBackend(t) req := logical.TestRequest(t, logical.UpdateOperation, "tools/random") - _, err := b.HandleRequest(context.Background(), req) + _, err := b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -2147,7 +2137,7 @@ func TestSystemBackend_ToolsRandom(t *testing.T) { doRequest := func(req *logical.Request, errExpected bool, format string, numBytes int) { t.Helper() getResponse := func() []byte { - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != nil && !errExpected { t.Fatal(err) } @@ -2221,7 +2211,7 @@ func TestSystemBackend_InternalUIMounts(t *testing.T) { // Ensure no entries are in the endpoint as a starting point req := logical.TestRequest(t, logical.ReadOperation, "internal/ui/mounts") - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -2236,7 +2226,7 @@ func TestSystemBackend_InternalUIMounts(t *testing.T) { req = logical.TestRequest(t, logical.ReadOperation, "internal/ui/mounts") req.ClientToken = rootToken - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -2326,15 +2316,21 @@ func TestSystemBackend_InternalUIMounts(t *testing.T) { // Mount-tune an auth mount req = logical.TestRequest(t, logical.UpdateOperation, "auth/token/tune") req.Data["listing_visibility"] = "unauth" - b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) + if resp.IsError() || err != nil { + t.Fatalf("resp.Error: %v, err:%v", resp.Error(), err) + } // Mount-tune a secret mount req = logical.TestRequest(t, logical.UpdateOperation, "mounts/secret/tune") req.Data["listing_visibility"] = "unauth" - b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) + if resp.IsError() || err != nil { + t.Fatalf("resp.Error: %v, err:%v", resp.Error(), err) + } req = logical.TestRequest(t, logical.ReadOperation, "internal/ui/mounts") - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -2370,7 +2366,7 @@ func TestSystemBackend_InternalUIMount(t *testing.T) { capabilities = ["create", "read", "update", "delete", "list"] }`, } - resp, err := b.HandleRequest(context.Background(), req) + resp, err := b.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("Bad %#v %#v", err, resp) } @@ -2380,14 +2376,14 @@ func TestSystemBackend_InternalUIMount(t *testing.T) { req.Data = map[string]interface{}{ "type": "kv", } - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("Bad %#v %#v", err, resp) } req = logical.TestRequest(t, logical.ReadOperation, "internal/ui/mounts/kv/bar") req.ClientToken = rootToken - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("Bad %#v %#v", err, resp) } @@ -2399,14 +2395,14 @@ func TestSystemBackend_InternalUIMount(t *testing.T) { req = logical.TestRequest(t, logical.ReadOperation, "internal/ui/mounts/kv") req.ClientToken = "tokenid" - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != logical.ErrPermissionDenied { t.Fatal("expected permission denied error") } req = logical.TestRequest(t, logical.ReadOperation, "internal/ui/mounts/secret") req.ClientToken = "tokenid" - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("Bad %#v %#v", err, resp) } @@ -2416,7 +2412,7 @@ func TestSystemBackend_InternalUIMount(t *testing.T) { req = logical.TestRequest(t, logical.ReadOperation, "internal/ui/mounts/sys") req.ClientToken = "tokenid" - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("Bad %#v %#v", err, resp) } @@ -2426,7 +2422,7 @@ func TestSystemBackend_InternalUIMount(t *testing.T) { req = logical.TestRequest(t, logical.ReadOperation, "internal/ui/mounts/non-existent") req.ClientToken = "tokenid" - resp, err = b.HandleRequest(context.Background(), req) + resp, err = b.HandleRequest(namespace.TestContext(), req) if err != logical.ErrPermissionDenied { t.Fatal("expected permission denied error") } diff --git a/vault/mount.go b/vault/mount.go index 2eba6bd6a7..4e3b3cb36a 100644 --- a/vault/mount.go +++ b/vault/mount.go @@ -13,6 +13,7 @@ import ( "github.com/hashicorp/go-uuid" "github.com/hashicorp/vault/helper/consts" "github.com/hashicorp/vault/helper/jsonutil" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/helper/strutil" "github.com/hashicorp/vault/logical" "github.com/mitchellh/copystructure" @@ -51,6 +52,17 @@ const ( ListingVisibilityHidden ListingVisibilityType = "hidden" // ListingVisibilityUnauth is the unauth type for listing visibility ListingVisibilityUnauth ListingVisibilityType = "unauth" + + systemMountPath = "sys/" + identityMountPath = "identity/" + cubbyholeMountPath = "cubbyhole/" + + systemMountType = "system" + identityMountType = "identity" + cubbyholeMountType = "cubbyhole" + + MountTableUpdateStorage = true + MountTableNoUpdateStorage = false ) var ( @@ -61,25 +73,25 @@ var ( protectedMounts = []string{ "audit/", "auth/", - "sys/", - "cubbyhole/", - "identity/", + systemMountPath, + cubbyholeMountPath, + identityMountPath, } untunableMounts = []string{ - "cubbyhole/", - "sys/", + cubbyholeMountPath, + systemMountPath, "audit/", - "identity/", + identityMountPath, } // singletonMounts can only exist in one location and are // loaded by default. These are types, not paths. singletonMounts = []string{ - "cubbyhole", - "system", + cubbyholeMountType, + systemMountType, "token", - "identity", + identityMountType, } // mountAliases maps old backend names to new backend names, allowing us @@ -87,19 +99,6 @@ var ( mountAliases = map[string]string{"generic": "kv"} ) -func collectBackendLocalPaths(backend logical.Backend, viewPath string) []string { - if backend == nil || backend.SpecialPaths() == nil || len(backend.SpecialPaths().LocalStorage) == 0 { - return nil - } - - var paths []string - for _, path := range backend.SpecialPaths().LocalStorage { - paths = append(paths, viewPath+path) - } - - return paths -} - func (c *Core) generateMountAccessor(entryType string) (string, error) { var accessor string for { @@ -137,30 +136,40 @@ func (t *MountTable) shallowClone() *MountTable { return mt } -// setTaint is used to set the taint on given entry -func (t *MountTable) setTaint(path string, value bool) *MountEntry { +// setTaint is used to set the taint on given entry Accepts either the mount +// entry's path or namespace + path, i.e. /secret/ or /token/ +func (t *MountTable) setTaint(ctx context.Context, path string, value bool) (*MountEntry, error) { n := len(t.Entries) + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } for i := 0; i < n; i++ { - if t.Entries[i].Path == path { + if entry := t.Entries[i]; entry.Path == path && entry.Namespace().ID == ns.ID { t.Entries[i].Tainted = value - return t.Entries[i] + return t.Entries[i], nil } } - return nil + return nil, nil } // remove is used to remove a given path entry; returns the entry that was // removed -func (t *MountTable) remove(path string) *MountEntry { +func (t *MountTable) remove(ctx context.Context, path string) (*MountEntry, error) { n := len(t.Entries) + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } + for i := 0; i < n; i++ { - if entry := t.Entries[i]; entry.Path == path { + if entry := t.Entries[i]; entry.Path == path && entry.Namespace().ID == ns.ID { t.Entries[i], t.Entries[n-1] = t.Entries[n-1], nil t.Entries = t.Entries[:n-1] - return entry + return entry, nil } } - return nil + return nil, nil } // sortEntriesByPath sorts the entries in the table by path and returns the @@ -172,6 +181,15 @@ func (t *MountTable) sortEntriesByPath() *MountTable { return t } +// sortEntriesByPath sorts the entries in the table by path and returns the +// table; this is useful for tests +func (t *MountTable) sortEntriesByPathDepth() *MountTable { + sort.Slice(t.Entries, func(i, j int) bool { + return len(strings.Split(t.Entries[i].Namespace().Path+t.Entries[i].Path, "/")) < len(strings.Split(t.Entries[j].Namespace().Path+t.Entries[j].Path, "/")) + }) + return t +} + // MountEntry is used to represent a mount table entry type MountEntry struct { Table string `json:"table"` // The table it belongs to @@ -186,6 +204,10 @@ type MountEntry struct { Local bool `json:"local"` // Local mounts are not replicated or affected by replication SealWrap bool `json:"seal_wrap"` // Whether to wrap CSPs Tainted bool `json:"tainted,omitempty"` // Set as a Write-Ahead flag for unmount/remount + NamespaceID string `json:"namespace_id"` + + // namespace contains the populated namespace + namespace *namespace.Namespace // synthesizedConfigCache is used to cache configuration values. These // particular values are cached since we want to get them at a point-in-time @@ -227,6 +249,20 @@ func (e *MountEntry) Clone() (*MountEntry, error) { return cp.(*MountEntry), nil } +// Namespace returns the namespace for the mount entry +func (e *MountEntry) Namespace() *namespace.Namespace { + return e.namespace +} + +// APIPath returns the full API Path for the given mount entry +func (e *MountEntry) APIPath() string { + path := e.Path + if e.Table == credentialTableType { + path = credentialRoutePrefix + path + } + return e.namespace.Path + path +} + // SyncCache syncs tunable configuration values to the cache. In the case of // cached values, they should be retrieved via synthesizedConfigCache.Load() // instead of accessing them directly through MountConfig. @@ -250,6 +286,38 @@ func (e *MountEntry) SyncCache() { } } +func (c *Core) decodeMountTable(ctx context.Context, raw []byte) (*MountTable, error) { + // Decode into mount table + mountTable := new(MountTable) + if err := jsonutil.DecodeJSON(raw, mountTable); err != nil { + return nil, err + } + + // Populate the namespace in memory + var mountEntries []*MountEntry + for _, entry := range mountTable.Entries { + if entry.NamespaceID == "" { + entry.NamespaceID = namespace.RootNamespaceID + } + ns, err := NamespaceByID(ctx, entry.NamespaceID, c) + if err != nil { + return nil, err + } + if ns == nil { + c.logger.Error("namespace on mount entry not found", "namespace_id", entry.NamespaceID, "mount_path", entry.Path, "mount_description", entry.Description) + continue + } + + entry.namespace = ns + mountEntries = append(mountEntries, entry) + } + + return &MountTable{ + Type: mountTable.Type, + Entries: mountEntries, + }, nil +} + // Mount is used to mount a new backend to the mount table. func (c *Core) mount(ctx context.Context, entry *MountEntry) error { // Ensure we end the path in a slash @@ -259,26 +327,41 @@ func (c *Core) mount(ctx context.Context, entry *MountEntry) error { // Prevent protected paths from being mounted for _, p := range protectedMounts { - if strings.HasPrefix(entry.Path, p) { - return logical.CodedError(403, fmt.Sprintf("cannot mount '%s'", entry.Path)) + if strings.HasPrefix(entry.Path, p) && entry.namespace == nil { + return logical.CodedError(403, fmt.Sprintf("cannot mount %q", entry.Path)) } } // Do not allow more than one instance of a singleton mount for _, p := range singletonMounts { if entry.Type == p { - return logical.CodedError(403, fmt.Sprintf("Cannot mount more than one instance of '%s'", entry.Type)) + return logical.CodedError(403, fmt.Sprintf("mount type of %q is not mountable", entry.Type)) } } - return c.mountInternal(ctx, entry) + return c.mountInternal(ctx, entry, MountTableUpdateStorage) } -func (c *Core) mountInternal(ctx context.Context, entry *MountEntry) error { +func (c *Core) mountInternal(ctx context.Context, entry *MountEntry, updateStorage bool) error { c.mountsLock.Lock() defer c.mountsLock.Unlock() + ns, err := namespace.FromContext(ctx) + if err != nil { + return err + } + + if err := verifyNamespace(c, ns, entry); err != nil { + return err + } + + entry.NamespaceID = ns.ID + entry.namespace = ns + + // Ensure the cache is populated, don't need the result + NamespaceByID(ctx, ns.ID, c) + // Verify there are no conflicting mounts - if match := c.router.MountConflict(entry.Path); match != "" { + if match := c.router.MountConflict(ctx, entry.Path); match != "" { return logical.CodedError(409, fmt.Sprintf("existing mount at %s", match)) } @@ -307,20 +390,35 @@ func (c *Core) mountInternal(ctx context.Context, entry *MountEntry) error { // Sync values to the cache entry.SyncCache() - viewPath := backendBarrierPrefix + entry.UUID + "/" + viewPath := entry.ViewPath() view := NewBarrierView(c.barrier, viewPath) + // Singleton mounts cannot be filtered on a per-secondary basis + // from replication + if strutil.StrListContains(singletonMounts, entry.Type) { + addFilterablePath(c, viewPath) + } + + nilMount, err := preprocessMount(c, entry, view) + if err != nil { + return err + } + origReadOnlyErr := view.getReadOnlyErr() + // Mark the view as read-only until the mounting is complete and // ensure that it is reset after. This ensures that there will be no // writes during the construction of the backend. view.setReadOnlyErr(logical.ErrSetupReadOnly) // We defer this because we're already up and running so we don't need to // time it for after postUnseal - defer view.setReadOnlyErr(nil) + defer view.setReadOnlyErr(origReadOnlyErr) var backend logical.Backend - var err error sysView := c.mountEntrySysView(entry) + conf := make(map[string]string) + if entry.Config.PluginName != "" { + conf["plugin_name"] = entry.Config.PluginName + } // Consider having plugin name under entry.Options backend, err = c.newLogicalBackend(ctx, entry, sysView, view) @@ -337,13 +435,29 @@ func (c *Core) mountInternal(ctx context.Context, entry *MountEntry) error { return fmt.Errorf("cannot mount %q of type %q as a logical backend", entry.Config.PluginName, backendType) } + addPathCheckers(c, entry, backend, viewPath) + c.setCoreBackend(entry, backend, view) + // If the mount is filtered or we are on a DR secondary we don't want to + // keep the actual backend running, so we clean it up and set it to nil + // so the router does not have a pointer to the object. + if nilMount { + backend.Cleanup(ctx) + backend = nil + } + newTable := c.mounts.shallowClone() newTable.Entries = append(newTable.Entries, entry) - if err := c.persistMounts(ctx, newTable, &entry.Local); err != nil { - c.logger.Error("failed to update mount table", "error", err) - return logical.CodedError(500, "failed to update mount table") + if updateStorage { + if err := c.persistMounts(ctx, newTable, &entry.Local); err != nil { + c.logger.Error("failed to update mount table", "error", err) + if err == logical.ErrReadOnly && c.perfStandby { + return err + } + + return logical.CodedError(500, "failed to update mount table") + } } c.mounts = newTable @@ -352,7 +466,7 @@ func (c *Core) mountInternal(ctx context.Context, entry *MountEntry) error { } if c.logger.IsInfo() { - c.logger.Info("successful mount", "path", entry.Path, "type", entry.Type) + c.logger.Info("successful mount", "namespace", entry.Namespace().Path, "path", entry.Path, "type", entry.Type) } return nil } @@ -371,47 +485,56 @@ func (c *Core) unmount(ctx context.Context, path string) error { return fmt.Errorf("cannot unmount %q", path) } } - return c.unmountInternal(ctx, path) + return c.unmountInternal(ctx, path, MountTableUpdateStorage) } -func (c *Core) unmountInternal(ctx context.Context, path string) error { +func (c *Core) unmountInternal(ctx context.Context, path string, updateStorage bool) error { + ns, err := namespace.FromContext(ctx) + if err != nil { + return err + } + // Verify exact match of the route - match := c.router.MatchingMount(path) - if match == "" || path != match { + match := c.router.MatchingMount(ctx, path) + if match == "" || ns.Path+path != match { return fmt.Errorf("no matching mount") } // Get the view for this backend - view := c.router.MatchingStorageByAPIPath(path) + view := c.router.MatchingStorageByAPIPath(ctx, path) // Get the backend/mount entry for this path, used to remove ignored // replication prefixes - backend := c.router.MatchingBackend(path) - entry := c.router.MatchingMountEntry(path) + backend := c.router.MatchingBackend(ctx, path) + entry := c.router.MatchingMountEntry(ctx, path) // Mark the entry as tainted - if err := c.taintMountEntry(ctx, path); err != nil { + if err := c.taintMountEntry(ctx, path, updateStorage); err != nil { c.logger.Error("failed to taint mount entry for path being unmounted", "error", err, "path", path) return err } // Taint the router path to prevent routing. Note that in-flight requests // are uncertain, right now. - if err := c.router.Taint(path); err != nil { + if err := c.router.Taint(ctx, path); err != nil { return err } - if backend != nil { + rCtx := namespace.ContextWithNamespace(c.activeContext, ns) + if backend != nil && c.rollback != nil { // Invoke the rollback manager a final time - if err := c.rollback.Rollback(path); err != nil { + if err := c.rollback.Rollback(rCtx, path); err != nil { return err } - + } + if backend != nil && c.expiration != nil && updateStorage { // Revoke all the dynamic keys - if err := c.expiration.RevokePrefix(c.activeContext, path, true); err != nil { + if err := c.expiration.RevokePrefix(rCtx, path, true); err != nil { return err } + } + if backend != nil { // Call cleanup function if it exists backend.Cleanup(ctx) } @@ -421,35 +544,48 @@ func (c *Core) unmountInternal(ctx context.Context, path string) error { return err } + viewPath := entry.ViewPath() switch { - case entry.Local, !c.ReplicationState().HasState(consts.ReplicationPerformanceSecondary): + case !updateStorage: + // Don't attempt to clear data, replication will handle this + case c.IsDRSecondary(), entry.Local, !c.ReplicationState().HasState(consts.ReplicationPerformanceSecondary): // Have writable storage, remove the whole thing if err := logical.ClearView(ctx, view); err != nil { c.logger.Error("failed to clear view for path being unmounted", "error", err, "path", path) return err } - } + case !entry.Local && c.ReplicationState().HasState(consts.ReplicationPerformanceSecondary): + if err := clearIgnoredPaths(ctx, c, backend, viewPath); err != nil { + return err + } + } // Remove the mount table entry - if err := c.removeMountEntry(ctx, path); err != nil { + if err := c.removeMountEntry(ctx, path, updateStorage); err != nil { c.logger.Error("failed to remove mount entry for path being unmounted", "error", err, "path", path) return err } + removePathCheckers(c, entry, viewPath) + if c.logger.IsInfo() { - c.logger.Info("successfully unmounted", "path", path) + c.logger.Info("successfully unmounted", "path", path, "namespace", ns.Path) } + return nil } // removeMountEntry is used to remove an entry from the mount table -func (c *Core) removeMountEntry(ctx context.Context, path string) error { +func (c *Core) removeMountEntry(ctx context.Context, path string, updateStorage bool) error { c.mountsLock.Lock() defer c.mountsLock.Unlock() // Remove the entry from the mount table newTable := c.mounts.shallowClone() - entry := newTable.remove(path) + entry, err := newTable.remove(ctx, path) + if err != nil { + return err + } if entry == nil { c.logger.Error("nil entry found removing entry in mounts table", "path", path) return logical.CodedError(500, "failed to remove entry in mounts table") @@ -461,10 +597,12 @@ func (c *Core) removeMountEntry(ctx context.Context, path string) error { newTable.Entries = nil } - // Update the mount table - if err := c.persistMounts(ctx, newTable, &entry.Local); err != nil { - c.logger.Error("failed to remove entry from mounts table", "error", err) - return logical.CodedError(500, "failed to remove entry from mounts table") + if updateStorage { + // Update the mount table + if err := c.persistMounts(ctx, newTable, &entry.Local); err != nil { + c.logger.Error("failed to remove entry from mounts table", "error", err) + return logical.CodedError(500, "failed to remove entry from mounts table") + } } c.mounts = newTable @@ -472,22 +610,31 @@ func (c *Core) removeMountEntry(ctx context.Context, path string) error { } // taintMountEntry is used to mark an entry in the mount table as tainted -func (c *Core) taintMountEntry(ctx context.Context, path string) error { +func (c *Core) taintMountEntry(ctx context.Context, path string, updateStorage bool) error { c.mountsLock.Lock() defer c.mountsLock.Unlock() // As modifying the taint of an entry affects shallow clones, // we simply use the original - entry := c.mounts.setTaint(path, true) + entry, err := c.mounts.setTaint(ctx, path, true) + if err != nil { + return err + } if entry == nil { c.logger.Error("nil entry found tainting entry in mounts table", "path", path) return logical.CodedError(500, "failed to taint entry in mounts table") } - // Update the mount table - if err := c.persistMounts(ctx, c.mounts, &entry.Local); err != nil { - c.logger.Error("failed to taint entry in mounts table", "error", err) - return logical.CodedError(500, "failed to taint entry in mounts table") + if updateStorage { + // Update the mount table + if err := c.persistMounts(ctx, c.mounts, &entry.Local); err != nil { + if err == logical.ErrReadOnly && c.perfStandby { + return err + } + + c.logger.Error("failed to taint entry in mounts table", "error", err) + return logical.CodedError(500, "failed to taint entry in mounts table") + } } return nil @@ -496,7 +643,7 @@ func (c *Core) taintMountEntry(ctx context.Context, path string) error { // remountForce takes a copy of the mount entry for the path and fully unmounts // and remounts the backend to pick up any changes, such as filtered paths func (c *Core) remountForce(ctx context.Context, path string) error { - me := c.router.MatchingMountEntry(path) + me := c.router.MatchingMountEntry(ctx, path) if me == nil { return fmt.Errorf("cannot find mount for path %q", path) } @@ -514,6 +661,11 @@ func (c *Core) remountForce(ctx context.Context, path string) error { // Remount is used to remount a path at a new mount point. func (c *Core) remount(ctx context.Context, src, dst string) error { + ns, err := namespace.FromContext(ctx) + if err != nil { + return err + } + // Ensure we end the path in a slash if !strings.HasSuffix(src, "/") { src += "/" @@ -530,33 +682,41 @@ func (c *Core) remount(ctx context.Context, src, dst string) error { } // Verify exact match of the route - match := c.router.MatchingMount(src) - if match == "" || src != match { + match := c.router.MatchingMount(ctx, src) + if match == "" || ns.Path+src != match { return fmt.Errorf("no matching mount at %q", src) } - if match := c.router.MatchingMount(dst); match != "" { + if match := c.router.MatchingMount(ctx, dst); match != "" { return fmt.Errorf("existing mount at %q", match) } // Mark the entry as tainted - if err := c.taintMountEntry(ctx, src); err != nil { + if err := c.taintMountEntry(ctx, src, true); err != nil { return err } // Taint the router path to prevent routing - if err := c.router.Taint(src); err != nil { + if err := c.router.Taint(ctx, src); err != nil { return err } - // Invoke the rollback manager a final time - if err := c.rollback.Rollback(src); err != nil { - return err - } + if !c.IsDRSecondary() { + // Invoke the rollback manager a final time + rCtx := namespace.ContextWithNamespace(c.activeContext, ns) + if err := c.rollback.Rollback(rCtx, src); err != nil { + return err + } - // Revoke all the dynamic keys - if err := c.expiration.RevokePrefix(c.activeContext, src, true); err != nil { - return err + entry := c.router.MatchingMountEntry(ctx, src) + if entry == nil { + return fmt.Errorf("no matching mount at %q", src) + } + + // Revoke all the dynamic keys + if err := c.expiration.RevokePrefix(rCtx, src, true); err != nil { + return err + } } c.mountsLock.Lock() @@ -580,18 +740,22 @@ func (c *Core) remount(ctx context.Context, src, dst string) error { entry.Path = src entry.Tainted = true c.mountsLock.Unlock() + if err == logical.ErrReadOnly && c.perfStandby { + return err + } + c.logger.Error("failed to update mounts table", "error", err) return logical.CodedError(500, "failed to update mounts table") } c.mountsLock.Unlock() // Remount the backend - if err := c.router.Remount(src, dst); err != nil { + if err := c.router.Remount(ctx, src, dst); err != nil { return err } // Un-taint the path - if err := c.router.Untaint(dst); err != nil { + if err := c.router.Untaint(ctx, dst); err != nil { return err } @@ -603,8 +767,6 @@ func (c *Core) remount(ctx context.Context, src, dst string) error { // loadMounts is invoked as part of postUnseal to load the mount table func (c *Core) loadMounts(ctx context.Context) error { - mountTable := &MountTable{} - localMountTable := &MountTable{} // Load the existing mount table raw, err := c.barrier.Get(ctx, coreMountConfigPath) if err != nil { @@ -624,7 +786,8 @@ func (c *Core) loadMounts(ctx context.Context) error { // Check if the persisted value has canary in the beginning. If // yes, decompress the table and then JSON decode it. If not, // simply JSON decode it. - if err := jsonutil.DecodeJSON(raw.Value, mountTable); err != nil { + mountTable, err := c.decodeMountTable(ctx, raw.Value) + if err != nil { c.logger.Error("failed to decompress and/or decode the mount table", "error", err) return err } @@ -639,7 +802,8 @@ func (c *Core) loadMounts(ctx context.Context) error { } if rawLocal != nil { - if err := jsonutil.DecodeJSON(rawLocal.Value, localMountTable); err != nil { + localMountTable, err := c.decodeMountTable(ctx, rawLocal.Value) + if err != nil { c.logger.Error("failed to decompress and/or decode the local mount table", "error", err) return err } @@ -681,7 +845,7 @@ func (c *Core) loadMounts(ctx context.Context) error { // Upgrade to table-scoped entries for _, entry := range c.mounts.Entries { - if entry.Type == "cubbyhole" && !entry.Local { + if entry.Type == cubbyholeMountType && !entry.Local { entry.Local = true needPersist = true } @@ -706,6 +870,19 @@ func (c *Core) loadMounts(ctx context.Context) error { needPersist = true } + if entry.NamespaceID == "" { + entry.NamespaceID = namespace.RootNamespaceID + needPersist = true + } + ns, err := NamespaceByID(ctx, entry.NamespaceID, c) + if err != nil { + return err + } + if ns == nil { + return namespace.ErrNoNamespace + } + entry.namespace = ns + // Sync values to the cache entry.SyncCache() } @@ -716,6 +893,7 @@ func (c *Core) loadMounts(ctx context.Context) error { return nil } + // Persist both mount tables if err := c.persistMounts(ctx, c.mounts, nil); err != nil { c.logger.Error("failed to persist mount table", "error", err) return errLoadMountsFailed @@ -772,7 +950,6 @@ func (c *Core) persistMounts(ctx context.Context, table *MountTable, local *bool c.logger.Error("failed to persist mount table", "error", err) return err } - return nil } @@ -807,36 +984,46 @@ func (c *Core) setupMounts(ctx context.Context) error { c.mountsLock.Lock() defer c.mountsLock.Unlock() - var backendType logical.BackendType - - for _, entry := range c.mounts.Entries { - + for _, entry := range c.mounts.sortEntriesByPathDepth().Entries { // Initialize the backend, special casing for system - barrierPath := backendBarrierPrefix + entry.UUID + "/" - if entry.Type == "system" { - barrierPath = systemBarrierPrefix - } + barrierPath := entry.ViewPath() // Create a barrier view using the UUID view := NewBarrierView(c.barrier, barrierPath) + // Singleton mounts cannot be filtered on a per-secondary basis + // from replication + if strutil.StrListContains(singletonMounts, entry.Type) { + addFilterablePath(c, barrierPath) + } + + // Determining the replicated state of the mount + nilMount, err := preprocessMount(c, entry, view) + if err != nil { + return err + } + origReadOnlyErr := view.getReadOnlyErr() + // Mark the view as read-only until the mounting is complete and // ensure that it is reset after. This ensures that there will be no // writes during the construction of the backend. view.setReadOnlyErr(logical.ErrSetupReadOnly) if strutil.StrListContains(singletonMounts, entry.Type) { - defer view.setReadOnlyErr(nil) + defer view.setReadOnlyErr(origReadOnlyErr) } else { c.postUnsealFuncs = append(c.postUnsealFuncs, func() { - view.setReadOnlyErr(nil) + view.setReadOnlyErr(origReadOnlyErr) }) } var backend logical.Backend - var err error - sysView := c.mountEntrySysView(entry) - // Create the new backend + sysView := c.mountEntrySysView(entry) + // Set up conf to pass in plugin_name + conf := make(map[string]string) + if entry.Config.PluginName != "" { + conf["plugin_name"] = entry.Config.PluginName + } backend, err = c.newLogicalBackend(ctx, entry, sysView, view) if err != nil { c.logger.Error("failed to create mount entry", "path", entry.Path, "error", err) @@ -853,13 +1040,25 @@ func (c *Core) setupMounts(ctx context.Context) error { return fmt.Errorf("created mount entry of type %q is nil", entry.Type) } - // Check for the correct backend type - backendType = backend.Type() - if entry.Type == "plugin" && backendType != logical.TypeLogical { - return fmt.Errorf("cannot mount %q of type %q as a logical backend", entry.Config.PluginName, backendType) + { + // Check for the correct backend type + backendType := backend.Type() + if entry.Type == "plugin" && backendType != logical.TypeLogical { + return fmt.Errorf("cannot mount %q of type %q as a logical backend", entry.Config.PluginName, backendType) + } + + addPathCheckers(c, entry, backend, barrierPath) + + c.setCoreBackend(entry, backend, view) } - c.setCoreBackend(entry, backend, view) + // If the mount is filtered or we are on a DR secondary we don't want to + // keep the actual backend running, so we clean it up and set it to nil + // so the router does not have a pointer to the object. + if nilMount { + backend.Cleanup(ctx) + backend = nil + } ROUTER_MOUNT: // Mount the backend @@ -875,8 +1074,11 @@ func (c *Core) setupMounts(ctx context.Context) error { // Ensure the path is tainted if set in the mount table if entry.Tainted { - c.router.Taint(entry.Path) + c.router.Taint(ctx, entry.Path) } + + // Ensure the cache is populated, don't need the result + NamespaceByID(ctx, entry.NamespaceID, c) } return nil } @@ -890,10 +1092,13 @@ func (c *Core) unloadMounts(ctx context.Context) error { if c.mounts != nil { mountTable := c.mounts.shallowClone() for _, e := range mountTable.Entries { - backend := c.router.MatchingBackend(e.Path) + backend := c.router.MatchingBackend(namespace.ContextWithNamespace(ctx, e.namespace), e.Path) if backend != nil { backend.Cleanup(ctx) } + + viewPath := e.ViewPath() + removePathCheckers(c, e, viewPath) } } @@ -1011,8 +1216,8 @@ func (c *Core) requiredMountTable() *MountTable { } cubbyholeMount := &MountEntry{ Table: mountTableType, - Path: "cubbyhole/", - Type: "cubbyhole", + Path: cubbyholeMountPath, + Type: cubbyholeMountType, Description: "per-token private secret storage", UUID: cubbyholeUUID, Accessor: cubbyholeAccessor, @@ -1035,7 +1240,7 @@ func (c *Core) requiredMountTable() *MountTable { sysMount := &MountEntry{ Table: mountTableType, Path: "sys/", - Type: "system", + Type: systemMountType, Description: "system endpoints used for control, policy and debugging", UUID: sysUUID, Accessor: sysAccessor, @@ -1083,7 +1288,7 @@ func (c *Core) singletonMountTables() (mounts, auth *MountTable) { c.mountsLock.RLock() for _, entry := range c.mounts.Entries { - if strutil.StrListContains(singletonMounts, entry.Type) && !entry.Local { + if strutil.StrListContains(singletonMounts, entry.Type) && !entry.Local && entry.Namespace().ID == namespace.RootNamespaceID { mounts.Entries = append(mounts.Entries, entry) } } @@ -1091,7 +1296,7 @@ func (c *Core) singletonMountTables() (mounts, auth *MountTable) { c.authLock.RLock() for _, entry := range c.auth.Entries { - if strutil.StrListContains(singletonMounts, entry.Type) && !entry.Local { + if strutil.StrListContains(singletonMounts, entry.Type) && !entry.Local && entry.Namespace().ID == namespace.RootNamespaceID { auth.Entries = append(auth.Entries, entry) } } @@ -1102,14 +1307,15 @@ func (c *Core) singletonMountTables() (mounts, auth *MountTable) { func (c *Core) setCoreBackend(entry *MountEntry, backend logical.Backend, view *BarrierView) { switch entry.Type { - case "system": + case systemMountType: c.systemBackend = backend.(*SystemBackend) c.systemBarrierView = view - case "cubbyhole": + case cubbyholeMountType: ch := backend.(*CubbyholeBackend) ch.saltUUID = entry.UUID ch.storageView = view - case "identity": + c.cubbyholeBackend = ch + case identityMountType: c.identityStore = backend.(*IdentityStore) } } diff --git a/vault/mount_test.go b/vault/mount_test.go index 880460fafd..d334e144fc 100644 --- a/vault/mount_test.go +++ b/vault/mount_test.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/vault/audit" "github.com/hashicorp/vault/helper/compressutil" "github.com/hashicorp/vault/helper/jsonutil" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/logical" ) @@ -32,7 +33,7 @@ func TestMount_ReadOnlyViewDuringMount(t *testing.T) { Path: "foo", Type: "noop", } - err := c.mount(context.Background(), me) + err := c.mount(namespace.TestContext(), me) if err != nil { t.Fatalf("err: %v", err) } @@ -74,12 +75,12 @@ func TestCore_Mount(t *testing.T) { Path: "foo", Type: "kv", } - err := c.mount(context.Background(), me) + err := c.mount(namespace.TestContext(), me) if err != nil { t.Fatalf("err: %v", err) } - match := c.router.MatchingMount("foo/bar") + match := c.router.MatchingMount(namespace.TestContext(), "foo/bar") if match != "foo/" { t.Fatalf("missing mount") } @@ -124,6 +125,8 @@ func TestCore_Mount_Local(t *testing.T) { UUID: "abcd", Accessor: "kv-abcd", BackendAwareUUID: "abcde", + NamespaceID: namespace.RootNamespaceID, + namespace: namespace.RootNamespace, }, &MountEntry{ Table: mountTableType, @@ -132,12 +135,14 @@ func TestCore_Mount_Local(t *testing.T) { UUID: "bcde", Accessor: "kv-bcde", BackendAwareUUID: "bcdea", + NamespaceID: namespace.RootNamespaceID, + namespace: namespace.RootNamespace, }, }, } // Both should set up successfully - err := c.setupMounts(context.Background()) + err := c.setupMounts(namespace.TestContext()) if err != nil { t.Fatal(err) } @@ -208,12 +213,12 @@ func TestCore_Mount_Local(t *testing.T) { func TestCore_Unmount(t *testing.T) { c, keys, _ := TestCoreUnsealed(t) - err := c.unmount(context.Background(), "secret") + err := c.unmount(namespace.TestContext(), "secret") if err != nil { t.Fatalf("err: %v", err) } - match := c.router.MatchingMount("secret/foo") + match := c.router.MatchingMount(namespace.TestContext(), "secret/foo") if match != "" { t.Fatalf("backend present") } @@ -255,12 +260,12 @@ func TestCore_Unmount_Cleanup(t *testing.T) { Path: "test/", Type: "noop", } - if err := c.mount(context.Background(), me); err != nil { + if err := c.mount(namespace.TestContext(), me); err != nil { t.Fatalf("err: %v", err) } // Store the view - view := c.router.MatchingStorageByAPIPath("test/") + view := c.router.MatchingStorageByAPIPath(namespace.TestContext(), "test/") // Inject data se := &logical.StorageEntry{ @@ -290,7 +295,7 @@ func TestCore_Unmount_Cleanup(t *testing.T) { Path: "test/foo", ClientToken: root, } - resp, err := c.HandleRequest(context.Background(), r) + resp, err := c.HandleRequest(namespace.TestContext(), r) if err != nil { t.Fatalf("err: %v", err) } @@ -299,7 +304,7 @@ func TestCore_Unmount_Cleanup(t *testing.T) { } // Unmount, this should cleanup - if err := c.unmount(context.Background(), "test/"); err != nil { + if err := c.unmount(namespace.TestContext(), "test/"); err != nil { t.Fatalf("err: %v", err) } @@ -328,12 +333,12 @@ func TestCore_Unmount_Cleanup(t *testing.T) { func TestCore_Remount(t *testing.T) { c, keys, _ := TestCoreUnsealed(t) - err := c.remount(context.Background(), "secret", "foo") + err := c.remount(namespace.TestContext(), "secret", "foo") if err != nil { t.Fatalf("err: %v", err) } - match := c.router.MatchingMount("foo/bar") + match := c.router.MatchingMount(namespace.TestContext(), "foo/bar") if match != "foo/" { t.Fatalf("failed remount") } @@ -375,12 +380,12 @@ func TestCore_Remount_Cleanup(t *testing.T) { Path: "test/", Type: "noop", } - if err := c.mount(context.Background(), me); err != nil { + if err := c.mount(namespace.TestContext(), me); err != nil { t.Fatalf("err: %v", err) } // Store the view - view := c.router.MatchingStorageByAPIPath("test/") + view := c.router.MatchingStorageByAPIPath(namespace.TestContext(), "test/") // Inject data se := &logical.StorageEntry{ @@ -410,7 +415,7 @@ func TestCore_Remount_Cleanup(t *testing.T) { Path: "test/foo", ClientToken: root, } - resp, err := c.HandleRequest(context.Background(), r) + resp, err := c.HandleRequest(namespace.TestContext(), r) if err != nil { t.Fatalf("err: %v", err) } @@ -419,7 +424,7 @@ func TestCore_Remount_Cleanup(t *testing.T) { } // Remount, this should cleanup - if err := c.remount(context.Background(), "test/", "new/"); err != nil { + if err := c.remount(namespace.TestContext(), "test/", "new/"); err != nil { t.Fatalf("err: %v", err) } @@ -448,7 +453,7 @@ func TestCore_Remount_Cleanup(t *testing.T) { func TestCore_Remount_Protected(t *testing.T) { c, _, _ := TestCoreUnsealed(t) - err := c.remount(context.Background(), "sys", "foo") + err := c.remount(namespace.TestContext(), "sys", "foo") if err.Error() != `cannot remount "sys/"` { t.Fatalf("err: %v", err) } @@ -474,7 +479,7 @@ func TestCore_MountTable_UpgradeToTyped(t *testing.T) { Path: "foo", Type: "noop", } - err := c.enableAudit(context.Background(), me) + err := c.enableAudit(namespace.TestContext(), me, true) if err != nil { t.Fatalf("err: %v", err) } @@ -488,7 +493,7 @@ func TestCore_MountTable_UpgradeToTyped(t *testing.T) { Path: "foo", Type: "noop", } - err = c.enableCredential(context.Background(), me) + err = c.enableCredential(namespace.TestContext(), me) if err != nil { t.Fatalf("err: %v", err) } diff --git a/vault/mount_util.go b/vault/mount_util.go new file mode 100644 index 0000000000..66ffb98e47 --- /dev/null +++ b/vault/mount_util.go @@ -0,0 +1,42 @@ +// +build !enterprise + +package vault + +import ( + "context" + "path" + + "github.com/hashicorp/vault/helper/namespace" + "github.com/hashicorp/vault/logical" +) + +func addPathCheckers(*Core, *MountEntry, logical.Backend, string) {} +func removePathCheckers(*Core, *MountEntry, string) {} +func addAuditPathChecker(*Core, *MountEntry, *BarrierView, string) {} +func removeAuditPathChecker(*Core, *MountEntry) {} +func addFilterablePath(*Core, string) {} +func preprocessMount(*Core, *MountEntry, *BarrierView) (bool, error) { return false, nil } +func clearIgnoredPaths(context.Context, *Core, logical.Backend, string) error { return nil } + +// ViewPath returns storage prefix for the view +func (e *MountEntry) ViewPath() string { + switch e.Type { + case systemMountType: + return systemBarrierPrefix + case "token": + return path.Join(systemBarrierPrefix, tokenSubPath) + "/" + } + + switch e.Table { + case mountTableType: + return backendBarrierPrefix + e.UUID + "/" + case credentialTableType: + return credentialBarrierPrefix + e.UUID + "/" + case auditTableType: + return auditBarrierPrefix + e.UUID + "/" + } + + panic("invalid mount entry") +} + +func verifyNamespace(*Core, *namespace.Namespace, *MountEntry) error { return nil } diff --git a/vault/namespaces.go b/vault/namespaces.go new file mode 100644 index 0000000000..5b9f31b94c --- /dev/null +++ b/vault/namespaces.go @@ -0,0 +1,18 @@ +package vault + +import ( + "context" + + "github.com/hashicorp/vault/helper/namespace" +) + +var ( + NamespaceByID func(context.Context, string, *Core) (*namespace.Namespace, error) = namespaceByID +) + +func namespaceByID(ctx context.Context, nsID string, c *Core) (*namespace.Namespace, error) { + if nsID == namespace.RootNamespaceID { + return namespace.RootNamespace, nil + } + return nil, namespace.ErrNoNamespace +} diff --git a/vault/plugin_reload.go b/vault/plugin_reload.go index e130df8ba9..c0fb715c3e 100644 --- a/vault/plugin_reload.go +++ b/vault/plugin_reload.go @@ -5,6 +5,8 @@ import ( "fmt" "strings" + "github.com/hashicorp/vault/helper/namespace" + "github.com/hashicorp/errwrap" multierror "github.com/hashicorp/go-multierror" "github.com/hashicorp/vault/helper/strutil" @@ -14,23 +16,35 @@ import ( // reloadPluginMounts reloads provided mounts, regardless of // plugin name, as long as the backend type is plugin. func (c *Core) reloadMatchingPluginMounts(ctx context.Context, mounts []string) error { - c.mountsLock.Lock() - defer c.mountsLock.Unlock() + c.mountsLock.RLock() + defer c.mountsLock.RUnlock() + c.authLock.RLock() + defer c.authLock.RUnlock() + + ns, err := namespace.FromContext(ctx) + if err != nil { + return err + } var errors error for _, mount := range mounts { - entry := c.router.MatchingMountEntry(mount) + entry := c.router.MatchingMountEntry(ctx, mount) if entry == nil { errors = multierror.Append(errors, fmt.Errorf("cannot fetch mount entry on %q", mount)) continue } var isAuth bool - fullPath := c.router.MatchingMount(mount) + fullPath := c.router.MatchingMount(ctx, mount) if strings.HasPrefix(fullPath, credentialRoutePrefix) { isAuth = true } + // We dont reload mounts that are not in the same namespace + if ns.ID != entry.Namespace().ID { + continue + } + if entry.Type == "plugin" { err := c.reloadBackendCommon(ctx, entry, isAuth) if err != nil { @@ -47,11 +61,23 @@ func (c *Core) reloadMatchingPluginMounts(ctx context.Context, mounts []string) // plugin pluginName (name of the plugin as registered in // the plugin catalog). func (c *Core) reloadMatchingPlugin(ctx context.Context, pluginName string) error { - c.mountsLock.Lock() - defer c.mountsLock.Unlock() + c.mountsLock.RLock() + defer c.mountsLock.RUnlock() + c.authLock.RLock() + defer c.authLock.RUnlock() + + ns, err := namespace.FromContext(ctx) + if err != nil { + return err + } // Filter mount entries that only matches the plugin name for _, entry := range c.mounts.Entries { + // We dont reload mounts that are not in the same namespace + if ns.ID != entry.Namespace().ID { + continue + } + if entry.Config.PluginName == pluginName && entry.Type == "plugin" { err := c.reloadBackendCommon(ctx, entry, false) if err != nil { @@ -63,6 +89,11 @@ func (c *Core) reloadMatchingPlugin(ctx context.Context, pluginName string) erro // Filter auth mount entries that ony matches the plugin name for _, entry := range c.auth.Entries { + // We dont reload mounts that are not in the same namespace + if ns.ID != entry.Namespace().ID { + continue + } + if entry.Config.PluginName == pluginName && entry.Type == "plugin" { err := c.reloadBackendCommon(ctx, entry, true) if err != nil { @@ -81,7 +112,7 @@ func (c *Core) reloadBackendCommon(ctx context.Context, entry *MountEntry, isAut // We don't want to reload the singleton mounts. They often have specific // inmemory elements and we don't want to touch them here. if strutil.StrListContains(singletonMounts, entry.Type) { - c.logger.Debug("Skipping reload of singleton mount", "type", entry.Type) + c.logger.Debug("skipping reload of singleton mount", "type", entry.Type) return nil } @@ -111,11 +142,24 @@ func (c *Core) reloadBackendCommon(ctx context.Context, entry *MountEntry, isAut } view := re.storageView + viewPath := entry.UUID + "/" + switch entry.Table { + case mountTableType: + viewPath = backendBarrierPrefix + viewPath + case credentialTableType: + viewPath = credentialBarrierPrefix + viewPath + } + + removePathCheckers(c, entry, viewPath) sysView := c.mountEntrySysView(entry) + nilMount, err := preprocessMount(c, entry, view.(*BarrierView)) + if err != nil { + return err + } + var backend logical.Backend - var err error if !isAuth { // Dispense a new backend backend, err = c.newLogicalBackend(ctx, entry, sysView, view) @@ -129,14 +173,23 @@ func (c *Core) reloadBackendCommon(ctx context.Context, entry *MountEntry, isAut return fmt.Errorf("nil backend of type %q returned from creation function", entry.Type) } + addPathCheckers(c, entry, backend, viewPath) + + if nilMount { + backend.Cleanup(ctx) + backend = nil + } + // Set the backend back re.backend = backend - // Set paths as well - paths := backend.SpecialPaths() - if paths != nil { - re.rootPaths.Store(pathsToRadix(paths.Root)) - re.loginPaths.Store(pathsToRadix(paths.Unauthenticated)) + if backend != nil { + // Set paths as well + paths := backend.SpecialPaths() + if paths != nil { + re.rootPaths.Store(pathsToRadix(paths.Root)) + re.loginPaths.Store(pathsToRadix(paths.Unauthenticated)) + } } return nil diff --git a/vault/policy.go b/vault/policy.go index 3eddc1b672..7bf448a352 100644 --- a/vault/policy.go +++ b/vault/policy.go @@ -12,6 +12,7 @@ import ( "github.com/hashicorp/hcl/hcl/ast" "github.com/hashicorp/vault/helper/hclutil" "github.com/hashicorp/vault/helper/identity" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/helper/parseutil" "github.com/mitchellh/copystructure" ) @@ -79,14 +80,33 @@ var ( } ) -// Policy is used to represent the policy specified by -// an ACL configuration. +type egpPath struct { + Path string `json:"path"` + Glob bool `json:"glob"` +} + +// Policy is used to represent the policy specified by an ACL configuration. type Policy struct { + sentinelPolicy Name string `hcl:"name"` Paths []*PathRules `hcl:"-"` Raw string Type PolicyType Templated bool + namespace *namespace.Namespace +} + +// ShallowClone returns a shallow clone of the policy. This should not be used +// if any of the reference-typed fields are going to be modified +func (p *Policy) ShallowClone() *Policy { + return &Policy{ + sentinelPolicy: p.sentinelPolicy, + Name: p.Name, + Paths: p.Paths, + Raw: p.Raw, + Type: p.Type, + namespace: p.namespace, + } } // PathRules represents a policy for a path in the namespace. @@ -104,6 +124,29 @@ type PathRules struct { AllowedParametersHCL map[string][]interface{} `hcl:"allowed_parameters"` DeniedParametersHCL map[string][]interface{} `hcl:"denied_parameters"` RequiredParametersHCL []string `hcl:"required_parameters"` + MFAMethodsHCL []string `hcl:"mfa_methods"` + ControlGroupHCL *ControlGroupHCL `hcl:"control_group"` +} + +type ControlGroupHCL struct { + TTL interface{} `hcl:"ttl"` + Factors map[string]*ControlGroupFactor `hcl:"factor"` +} + +type ControlGroup struct { + TTL time.Duration + Factors []*ControlGroupFactor +} + +type ControlGroupFactor struct { + Name string + Identity *IdentityFactor `hcl:"identity"` +} + +type IdentityFactor struct { + GroupIDs []string `hcl:"group_ids"` + GroupNames []string `hcl:"group_names"` + ApprovalsRequired int `hcl:"approvals"` } type ACLPermissions struct { @@ -113,6 +156,8 @@ type ACLPermissions struct { AllowedParameters map[string][]interface{} DeniedParameters map[string][]interface{} RequiredParameters []string + MFAMethods []string + ControlGroup *ControlGroup } func (p *ACLPermissions) Clone() (*ACLPermissions, error) { @@ -147,21 +192,43 @@ func (p *ACLPermissions) Clone() (*ACLPermissions, error) { ret.DeniedParameters = clonedDenied.(map[string][]interface{}) } + switch { + case p.MFAMethods == nil: + case len(p.MFAMethods) == 0: + ret.MFAMethods = []string{} + default: + clonedMFAMethods, err := copystructure.Copy(p.MFAMethods) + if err != nil { + return nil, err + } + ret.MFAMethods = clonedMFAMethods.([]string) + } + + switch { + case p.ControlGroup == nil: + default: + clonedControlGroup, err := copystructure.Copy(p.ControlGroup) + if err != nil { + return nil, err + } + ret.ControlGroup = clonedControlGroup.(*ControlGroup) + } + return ret, nil } // ParseACLPolicy is used to parse the specified ACL rules into an // intermediary set of policies, before being compiled into // the ACL -func ParseACLPolicy(rules string) (*Policy, error) { - return parseACLPolicyWithTemplating(rules, false, nil, nil) +func ParseACLPolicy(ns *namespace.Namespace, rules string) (*Policy, error) { + return parseACLPolicyWithTemplating(ns, rules, false, nil, nil) } // parseACLPolicyWithTemplating performs the actual work and checks whether we // should perform substitutions. If performTemplating is true we know that it // is templated so we don't check again, otherwise we check to see if it's a // templated policy. -func parseACLPolicyWithTemplating(rules string, performTemplating bool, entity *identity.Entity, groups []*identity.Group) (*Policy, error) { +func parseACLPolicyWithTemplating(ns *namespace.Namespace, rules string, performTemplating bool, entity *identity.Entity, groups []*identity.Group) (*Policy, error) { // Parse the rules root, err := hcl.Parse(rules) if err != nil { @@ -184,9 +251,11 @@ func parseACLPolicyWithTemplating(rules string, performTemplating bool, entity * } // Create the initial policy and store the raw text of the rules - var p Policy - p.Raw = rules - p.Type = PolicyTypeACL + p := Policy{ + Raw: rules, + Type: PolicyTypeACL, + namespace: ns, + } if err := hcl.DecodeObject(&p, list); err != nil { return nil, errwrap.Wrapf("failed to parse policy: {{err}}", err) } @@ -211,9 +280,10 @@ func parsePaths(result *Policy, list *ast.ObjectList, performTemplating bool, en // Check the path if performTemplating { _, templated, err := identity.PopulateString(&identity.PopulateStringInput{ - String: key, - Entity: entity, - Groups: groups, + String: key, + Entity: entity, + Groups: groups, + Namespace: result.namespace, }) if err != nil { continue @@ -241,6 +311,8 @@ func parsePaths(result *Policy, list *ast.ObjectList, performTemplating bool, en "required_parameters", "min_wrapping_ttl", "max_wrapping_ttl", + "mfa_methods", + "control_group", } if err := hclutil.CheckHCLKeys(item.Val, valid); err != nil { return multierror.Prefix(err, fmt.Sprintf("path %q:", key)) @@ -262,6 +334,9 @@ func parsePaths(result *Policy, list *ast.ObjectList, performTemplating bool, en pc.Prefix = pc.Prefix[1:] } + // Ensure we are using the full request path internally + pc.Prefix = result.namespace.Path + pc.Prefix + // Strip the glob character if found if strings.HasSuffix(pc.Prefix, "*") { pc.Prefix = strings.TrimSuffix(pc.Prefix, "*") @@ -327,6 +402,47 @@ func parsePaths(result *Policy, list *ast.ObjectList, performTemplating bool, en } pc.Permissions.MaxWrappingTTL = dur } + if pc.MFAMethodsHCL != nil { + pc.Permissions.MFAMethods = make([]string, len(pc.MFAMethodsHCL)) + for idx, item := range pc.MFAMethodsHCL { + pc.Permissions.MFAMethods[idx] = item + } + } + if pc.ControlGroupHCL != nil { + pc.Permissions.ControlGroup = new(ControlGroup) + if pc.ControlGroupHCL.TTL != nil { + dur, err := parseutil.ParseDurationSecond(pc.ControlGroupHCL.TTL) + if err != nil { + return errwrap.Wrapf("error parsing control group max ttl: {{err}}", err) + } + pc.Permissions.ControlGroup.TTL = dur + } + + var factors []*ControlGroupFactor + if pc.ControlGroupHCL.Factors != nil { + for key, factor := range pc.ControlGroupHCL.Factors { + // Although we only have one factor here, we need to check to make sure there is at least + // one factor defined in this factor block. + if factor.Identity == nil { + return errors.New("no control_group factor provided") + } + + if factor.Identity.ApprovalsRequired <= 0 || + (len(factor.Identity.GroupIDs) == 0 && len(factor.Identity.GroupNames) == 0) { + return errors.New("must provide more than one identity group and approvals > 0") + } + + factors = append(factors, &ControlGroupFactor{ + Name: key, + Identity: factor.Identity, + }) + } + } + if len(factors) == 0 { + return errors.New("no control group factors provided") + } + pc.Permissions.ControlGroup.Factors = factors + } if pc.Permissions.MinWrappingTTL != 0 && pc.Permissions.MaxWrappingTTL != 0 && pc.Permissions.MaxWrappingTTL < pc.Permissions.MinWrappingTTL { diff --git a/vault/policy_store.go b/vault/policy_store.go index b2d8cea032..2c7e12967b 100644 --- a/vault/policy_store.go +++ b/vault/policy_store.go @@ -3,6 +3,7 @@ package vault import ( "context" "fmt" + "path" "strings" "sync" "time" @@ -13,14 +14,18 @@ import ( "github.com/hashicorp/golang-lru" "github.com/hashicorp/vault/helper/consts" "github.com/hashicorp/vault/helper/identity" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/helper/strutil" "github.com/hashicorp/vault/logical" ) const ( - // policySubPath is the sub-path used for the policy store - // view. This is nested under the system view. + // policySubPath is the sub-path used for the policy store view. This is + // nested under the system view. policyRGPSubPath/policyEGPSubPath are + // similar but for RGPs/EGPs. policyACLSubPath = "policy/" + policyRGPSubPath = "policy-rgp/" + policyEGPSubPath = "policy-egp/" // policyCacheSize is the number of policies that are kept cached policyCacheSize = 1024 @@ -46,7 +51,17 @@ path "sys/wrapping/unwrap" { capabilities = ["update"] } ` + // controlGroupPolicy is the policy that ensures control group requests can + // commit themselves + controlGroupPolicy = ` +path "cubbyhole/control-group" { + capabilities = ["update", "create", "read"] +} +path "sys/wrapping/unwrap" { + capabilities = ["update"] +} +` // defaultPolicy is the "default" policy defaultPolicy = ` # Allow tokens to look up their own properties @@ -151,21 +166,32 @@ var ( // PolicyStore is used to provide durable storage of policy, and to // manage ACLs associated with them. type PolicyStore struct { - core *Core - aclView *BarrierView + entPolicyStore + + core *Core + aclView *BarrierView + rgpView *BarrierView + egpView *BarrierView + tokenPoliciesLRU *lru.TwoQueueCache + egpLRU *lru.TwoQueueCache + // This is used to ensure that writes to the store (acl/rgp) or to the egp // path tree don't happen concurrently. We are okay reading stale data so // long as there aren't concurrent writes. modifyLock *sync.RWMutex + // Stores whether a token policy is ACL or RGP policyTypeMap sync.Map + // logger is the server logger copied over from core logger log.Logger } // PolicyEntry is used to store a policy by name type PolicyEntry struct { + sentinelPolicy + Version int Raw string Templated bool @@ -174,39 +200,57 @@ 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, core *Core, 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, error) { ps := &PolicyStore{ aclView: baseView.SubView(policyACLSubPath), + rgpView: baseView.SubView(policyRGPSubPath), + egpView: baseView.SubView(policyEGPSubPath), modifyLock: new(sync.RWMutex), logger: logger, core: core, } + + ps.extraInit() + if !system.CachingDisabled() { cache, _ := lru.New2Q(policyCacheSize) ps.tokenPoliciesLRU = cache + cache, _ = lru.New2Q(policyCacheSize) + ps.egpLRU = cache } - keys, err := logical.CollectKeys(ctx, ps.aclView) + aclView := ps.getACLView(namespace.RootNamespace) + keys, err := logical.CollectKeys(namespace.RootContext(ctx), aclView) if err != nil { ps.logger.Error("error collecting acl policy keys", "error", err) - return nil + return nil, err } for _, key := range keys { - ps.policyTypeMap.Store(ps.sanitizeName(key), PolicyTypeACL) + index := ps.cacheKey(namespace.RootNamespace, ps.sanitizeName(key)) + ps.policyTypeMap.Store(index, PolicyTypeACL) } + + if err := ps.loadNamespacePolicies(ctx, core); err != nil { + return nil, err + } + // Special-case root; doesn't exist on disk but does need to be found - ps.policyTypeMap.Store("root", PolicyTypeACL) - return ps + ps.policyTypeMap.Store(ps.cacheKey(namespace.RootNamespace, "root"), PolicyTypeACL) + return ps, nil } // setupPolicyStore is used to initialize the policy store // when the vault is being unsealed. func (c *Core) setupPolicyStore(ctx context.Context) error { // Create the policy store + var err error sysView := &dynamicSystemView{core: c} psLogger := c.baseLogger.Named("policy") c.AddLogger(psLogger) - c.policyStore = NewPolicyStore(ctx, c, c.systemBarrierView, sysView, psLogger) + c.policyStore, err = NewPolicyStore(ctx, c, c.systemBarrierView, sysView, psLogger) + if err != nil { + return err + } if c.ReplicationState().HasState(consts.ReplicationPerformanceSecondary) { // Policies will sync from the primary @@ -221,6 +265,10 @@ func (c *Core) setupPolicyStore(ctx context.Context) error { if err := c.policyStore.loadACLPolicy(ctx, responseWrappingPolicyName, responseWrappingPolicy); err != nil { return err } + // Ensure that the control group policy exists + if err := c.policyStore.loadACLPolicy(ctx, controlGroupPolicyName, controlGroupPolicy); err != nil { + return err + } return nil } @@ -233,15 +281,30 @@ func (c *Core) teardownPolicyStore() error { } func (ps *PolicyStore) invalidate(ctx context.Context, name string, policyType PolicyType) { + ns, err := namespace.FromContext(ctx) + if err != nil { + ps.logger.Error("unable to invalidate key, no namespace info passed", "key", name) + return + } + // This may come with a prefixed "/" due to joining the file path saneName := strings.TrimPrefix(name, "/") + index := ps.cacheKey(ns, saneName) + + ps.modifyLock.Lock() + defer ps.modifyLock.Unlock() // We don't lock before removing from the LRU here because the worst that // can happen is we load again if something since added it switch policyType { - case PolicyTypeACL: + case PolicyTypeACL, PolicyTypeRGP: if ps.tokenPoliciesLRU != nil { - ps.tokenPoliciesLRU.Remove(saneName) + ps.tokenPoliciesLRU.Remove(index) + } + + case PolicyTypeEGP: + if ps.egpLRU != nil { + ps.egpLRU.Remove(index) } default: @@ -250,10 +313,20 @@ func (ps *PolicyStore) invalidate(ctx context.Context, name string, policyType P } // Force a reload - _, err := ps.GetPolicy(ctx, name, policyType) + out, err := ps.switchedGetPolicy(ctx, name, policyType, false) if err != nil { ps.logger.Error("error fetching policy after invalidation", "name", saneName) } + + // If true, the invalidation was actually a delete, so we may need to + // perform further deletion tasks. We skip the physical deletion just in + // case another process has re-written the policy; instead next time Get is + // called the values will be loaded back in. + if out == nil { + ps.switchedDeletePolicy(ctx, name, policyType, false) + } + + return } // SetPolicy is used to create or update the given policy @@ -277,26 +350,84 @@ func (ps *PolicyStore) SetPolicy(ctx context.Context, p *Policy) error { func (ps *PolicyStore) setPolicyInternal(ctx context.Context, p *Policy) error { ps.modifyLock.Lock() defer ps.modifyLock.Unlock() + + // Get the appropriate view based on policy type and namespace + view := ps.getBarrierView(p.namespace, p.Type) + if view == nil { + return fmt.Errorf("unable to get the barrier subview for policy type %q", p.Type) + } + + if err := ps.parseEGPPaths(p); err != nil { + return err + } + // Create the entry entry, err := logical.StorageEntryJSON(p.Name, &PolicyEntry{ - Version: 2, - Raw: p.Raw, - Type: p.Type, - Templated: p.Templated, + Version: 2, + Raw: p.Raw, + Type: p.Type, + Templated: p.Templated, + sentinelPolicy: p.sentinelPolicy, }) if err != nil { return errwrap.Wrapf("failed to create entry: {{err}}", err) } + + // Construct the cache key + index := ps.cacheKey(p.namespace, p.Name) + switch p.Type { case PolicyTypeACL: - if err := ps.aclView.Put(ctx, entry); err != nil { + rgpView := ps.getRGPView(p.namespace) + rgp, err := rgpView.Get(ctx, entry.Key) + if err != nil { + return errwrap.Wrapf("failed looking up conflicting policy: {{err}}", err) + } + if rgp != nil { + return fmt.Errorf("cannot reuse policy names between ACLs and RGPs") + } + + if err := view.Put(ctx, entry); err != nil { return errwrap.Wrapf("failed to persist policy: {{err}}", err) } - ps.policyTypeMap.Store(p.Name, PolicyTypeACL) + + ps.policyTypeMap.Store(index, PolicyTypeACL) if ps.tokenPoliciesLRU != nil { - // Update the LRU cache - ps.tokenPoliciesLRU.Add(p.Name, p) + ps.tokenPoliciesLRU.Add(index, p) + } + + case PolicyTypeRGP: + aclView := ps.getACLView(p.namespace) + acl, err := aclView.Get(ctx, entry.Key) + if err != nil { + return errwrap.Wrapf("failed looking up conflicting policy: {{err}}", err) + } + if acl != nil { + return fmt.Errorf("cannot reuse policy names between ACLs and RGPs") + } + + if err := ps.handleSentinelPolicy(ctx, p, view, entry); err != nil { + return err + } + + ps.policyTypeMap.Store(index, PolicyTypeRGP) + + // We load here after successfully loading into Sentinel so that on + // error we will try loading again on the next get + if ps.tokenPoliciesLRU != nil { + ps.tokenPoliciesLRU.Add(index, p) + } + + case PolicyTypeEGP: + if err := ps.handleSentinelPolicy(ctx, p, view, entry); err != nil { + return err + } + + // We load here after successfully loading into Sentinel so that on + // error we will try loading again on the next get + if ps.egpLRU != nil { + ps.egpLRU.Add(index, p) } default: @@ -308,20 +439,36 @@ func (ps *PolicyStore) setPolicyInternal(ctx context.Context, p *Policy) error { // GetPolicy is used to fetch the named policy func (ps *PolicyStore) GetPolicy(ctx context.Context, name string, policyType PolicyType) (*Policy, error) { + return ps.switchedGetPolicy(ctx, name, policyType, true) +} + +func (ps *PolicyStore) switchedGetPolicy(ctx context.Context, name string, policyType PolicyType, grabLock bool) (*Policy, error) { defer metrics.MeasureSince([]string{"policy", "get_policy"}, time.Now()) + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } // Policies are normalized to lower-case name = ps.sanitizeName(name) + index := ps.cacheKey(ns, name) var cache *lru.TwoQueueCache var view *BarrierView + switch policyType { case PolicyTypeACL: cache = ps.tokenPoliciesLRU - view = ps.aclView + view = ps.getACLView(ns) + case PolicyTypeRGP: + cache = ps.tokenPoliciesLRU + view = ps.getRGPView(ns) + case PolicyTypeEGP: + cache = ps.egpLRU + view = ps.getEGPView(ns) case PolicyTypeToken: cache = ps.tokenPoliciesLRU - val, ok := ps.policyTypeMap.Load(name) + val, ok := ps.policyTypeMap.Load(index) if !ok { // Doesn't exist return nil, nil @@ -329,7 +476,9 @@ func (ps *PolicyStore) GetPolicy(ctx context.Context, name string, policyType Po policyType = val.(PolicyType) switch policyType { case PolicyTypeACL: - view = ps.aclView + view = ps.getACLView(ns) + case PolicyTypeRGP: + view = ps.getRGPView(ns) default: return nil, fmt.Errorf("invalid type of policy in type map: %q", policyType) } @@ -337,30 +486,40 @@ func (ps *PolicyStore) GetPolicy(ctx context.Context, name string, policyType Po if cache != nil { // Check for cached policy - if raw, ok := cache.Get(name); ok { + if raw, ok := cache.Get(index); ok { return raw.(*Policy), nil } } // Special case the root policy - if policyType == PolicyTypeACL && name == "root" { - p := &Policy{Name: "root"} + if policyType == PolicyTypeACL && name == "root" && ns.ID == namespace.RootNamespaceID { + p := &Policy{ + Name: "root", + namespace: namespace.RootNamespace, + } if cache != nil { - cache.Add(p.Name, p) + cache.Add(index, p) } return p, nil } - ps.modifyLock.Lock() - defer ps.modifyLock.Unlock() + if grabLock { + ps.modifyLock.Lock() + defer ps.modifyLock.Unlock() + } // See if anything has added it since we got the lock if cache != nil { - if raw, ok := cache.Get(name); ok { + if raw, ok := cache.Get(index); ok { return raw.(*Policy), nil } } + // Nil-check on the view before proceeding to retrive from storage + if view == nil { + return nil, fmt.Errorf("unable to get the barrier subview for policy type %q", policyType) + } + out, err := view.Get(ctx, name) if err != nil { return nil, errwrap.Wrapf("failed to read policy: {{err}}", err) @@ -383,18 +542,33 @@ func (ps *PolicyStore) GetPolicy(ctx context.Context, name string, policyType Po policy.Raw = policyEntry.Raw policy.Type = policyEntry.Type policy.Templated = policyEntry.Templated + policy.sentinelPolicy = policyEntry.sentinelPolicy + policy.namespace = ns switch policyEntry.Type { case PolicyTypeACL: // Parse normally - p, err := ParseACLPolicy(policyEntry.Raw) + p, err := ParseACLPolicy(ns, policyEntry.Raw) if err != nil { return nil, errwrap.Wrapf("failed to parse policy: {{err}}", err) } policy.Paths = p.Paths + // Reset this in case they set the name in the policy itself policy.Name = name - ps.policyTypeMap.Store(name, PolicyTypeACL) + ps.policyTypeMap.Store(index, PolicyTypeACL) + + case PolicyTypeRGP: + if err := ps.handleSentinelPolicy(ctx, policy, nil, nil); err != nil { + return nil, err + } + + ps.policyTypeMap.Store(index, PolicyTypeRGP) + + case PolicyTypeEGP: + if err := ps.handleSentinelPolicy(ctx, policy, nil, nil); err != nil { + return nil, err + } default: return nil, fmt.Errorf("unknown policy type %q", policyEntry.Type.String()) @@ -402,7 +576,7 @@ func (ps *PolicyStore) GetPolicy(ctx context.Context, name string, policyType Po if cache != nil { // Update the LRU cache - cache.Add(name, policy) + cache.Add(index, policy) } return policy, nil @@ -411,13 +585,31 @@ func (ps *PolicyStore) GetPolicy(ctx context.Context, name string, policyType Po // ListPolicies is used to list the available policies func (ps *PolicyStore) ListPolicies(ctx context.Context, policyType PolicyType) ([]string, error) { defer metrics.MeasureSince([]string{"policy", "list_policies"}, time.Now()) + + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } + if ns == nil { + return nil, namespace.ErrNoNamespace + } + + // Get the appropriate view based on policy type and namespace + view := ps.getBarrierView(ns, policyType) + if view == nil { + return []string{}, fmt.Errorf("unable to get the barrier subview for policy type %q", policyType) + } + // Scan the view, since the policy names are the same as the // key names. var keys []string - var err error switch policyType { case PolicyTypeACL: - keys, err = logical.CollectKeys(ctx, ps.aclView) + keys, err = logical.CollectKeys(ctx, view) + case PolicyTypeRGP: + return logical.CollectKeys(ctx, view) + case PolicyTypeEGP: + return logical.CollectKeys(ctx, view) default: return nil, fmt.Errorf("unknown policy type %q", policyType) } @@ -425,7 +617,7 @@ func (ps *PolicyStore) ListPolicies(ctx context.Context, policyType PolicyType) // We only have non-assignable ACL policies at the moment for _, nonAssignable := range nonAssignablePolicies { deleteIndex := -1 - //Find indices of non-assignable policies in keys + // Find indices of non-assignable policies in keys for index, key := range keys { if key == nonAssignable { // Delete collection outside the loop @@ -444,13 +636,31 @@ func (ps *PolicyStore) ListPolicies(ctx context.Context, policyType PolicyType) // DeletePolicy is used to delete the named policy func (ps *PolicyStore) DeletePolicy(ctx context.Context, name string, policyType PolicyType) error { + return ps.switchedDeletePolicy(ctx, name, policyType, true) +} + +func (ps *PolicyStore) switchedDeletePolicy(ctx context.Context, name string, policyType PolicyType, physicalDeletion bool) error { defer metrics.MeasureSince([]string{"policy", "delete_policy"}, time.Now()) - ps.modifyLock.Lock() - defer ps.modifyLock.Unlock() + ns, err := namespace.FromContext(ctx) + if err != nil { + return err + } + // If not set, the call comes from invalidation, where we'll already have + // grabbed the lock + if physicalDeletion { + ps.modifyLock.Lock() + defer ps.modifyLock.Unlock() + } // Policies are normalized to lower-case name = ps.sanitizeName(name) + index := ps.cacheKey(ns, name) + + view := ps.getBarrierView(ns, policyType) + if view == nil { + return fmt.Errorf("unable to get the barrier subview for policy type %q", policyType) + } switch policyType { case PolicyTypeACL: @@ -461,19 +671,55 @@ func (ps *PolicyStore) DeletePolicy(ctx context.Context, name string, policyType return fmt.Errorf("cannot delete default policy") } - err := ps.aclView.Delete(ctx, name) - if err != nil { - return errwrap.Wrapf("failed to delete policy: {{err}}", err) + if physicalDeletion { + err := view.Delete(ctx, name) + if err != nil { + return errwrap.Wrapf("failed to delete policy: {{err}}", err) + } } if ps.tokenPoliciesLRU != nil { // Clear the cache - ps.tokenPoliciesLRU.Remove(name) + ps.tokenPoliciesLRU.Remove(index) } - ps.policyTypeMap.Delete(name) + ps.policyTypeMap.Delete(index) + case PolicyTypeRGP: + if physicalDeletion { + err := view.Delete(ctx, name) + if err != nil { + return errwrap.Wrapf("failed to delete policy: {{err}}", err) + } + } + + if ps.tokenPoliciesLRU != nil { + // Clear the cache + ps.tokenPoliciesLRU.Remove(index) + } + + ps.policyTypeMap.Delete(index) + + defer ps.core.invalidateSentinelPolicy(policyType, index) + + case PolicyTypeEGP: + if physicalDeletion { + err := view.Delete(ctx, name) + if err != nil { + return errwrap.Wrapf("failed to delete policy: {{err}}", err) + } + } + + if ps.egpLRU != nil { + // Clear the cache + ps.egpLRU.Remove(index) + } + + defer ps.core.invalidateSentinelPolicy(policyType, index) + + ps.invalidateEGPTreePath(index) } + return nil } @@ -491,16 +737,26 @@ func (t *TemplateError) Error() string { // ACL is used to return an ACL which is built using the // named policies. -func (ps *PolicyStore) ACL(ctx context.Context, entity *identity.Entity, names ...string) (*ACL, error) { - // Fetch the policies +func (ps *PolicyStore) ACL(ctx context.Context, entity *identity.Entity, policyNames map[string][]string) (*ACL, error) { var policies []*Policy - for _, name := range names { - p, err := ps.GetPolicy(ctx, name, PolicyTypeToken) + // Fetch the policies + for nsID, nsPolicyNames := range policyNames { + policyNS, err := NamespaceByID(ctx, nsID, ps.core) if err != nil { - return nil, errwrap.Wrapf("failed to get policy: {{err}}", err) + return nil, err } - if p != nil { - policies = append(policies, p) + if policyNS == nil { + return nil, namespace.ErrNoNamespace + } + policyCtx := namespace.ContextWithNamespace(ctx, policyNS) + for _, nsPolicyName := range nsPolicyNames { + p, err := ps.GetPolicy(policyCtx, nsPolicyName, PolicyTypeToken) + if err != nil { + return nil, errwrap.Wrapf("failed to get policy: {{err}}", err) + } + if p != nil { + policies = append(policies, p) + } } } @@ -518,37 +774,50 @@ func (ps *PolicyStore) ACL(ctx context.Context, entity *identity.Entity, names . groups = append(directGroups, inheritedGroups...) } } - p, err := parseACLPolicyWithTemplating(policy.Raw, true, entity, groups) + p, err := parseACLPolicyWithTemplating(policy.namespace, policy.Raw, true, entity, groups) if err != nil { return nil, errwrap.Wrapf(fmt.Sprintf("error parsing templated policy %q: {{err}}", policy.Name), err) } + p.Name = policy.Name policies[i] = p } } // Construct the ACL - acl, err := NewACL(policies) + acl, err := NewACL(ctx, policies) if err != nil { return nil, errwrap.Wrapf("failed to construct ACL: {{err}}", err) } + return acl, nil } +// loadACLPolicy is used to load default ACL policies. The default policies will +// be loaded to all namespaces. func (ps *PolicyStore) loadACLPolicy(ctx context.Context, policyName, policyText string) error { + return ps.loadACLPolicyNamespaces(ctx, policyName, policyText) +} + +// loadACLPolicyInternal is used to load default ACL policies in a specific +// namespace. +func (ps *PolicyStore) loadACLPolicyInternal(ctx context.Context, policyName, policyText string) error { + ns, err := namespace.FromContext(ctx) + if err != nil { + return err + } + // Check if the policy already exists policy, err := ps.GetPolicy(ctx, policyName, PolicyTypeACL) - if err != nil { return errwrap.Wrapf(fmt.Sprintf("error fetching %s policy from store: {{err}}", policyName), err) } - if policy != nil { if !strutil.StrListContains(immutablePolicies, policyName) || policyText == policy.Raw { return nil } } - policy, err = ParseACLPolicy(policyText) + policy, err = ParseACLPolicy(ns, policyText) if err != nil { return errwrap.Wrapf(fmt.Sprintf("error parsing %s policy: {{err}}", policyName), err) } @@ -565,3 +834,7 @@ func (ps *PolicyStore) loadACLPolicy(ctx context.Context, policyName, policyText func (ps *PolicyStore) sanitizeName(name string) string { return strings.ToLower(strings.TrimSpace(name)) } + +func (ps *PolicyStore) cacheKey(ns *namespace.Namespace, name string) string { + return path.Join(ns.ID, name) +} diff --git a/vault/policy_store_test.go b/vault/policy_store_test.go index f54e532421..df8ac8e815 100644 --- a/vault/policy_store_test.go +++ b/vault/policy_store_test.go @@ -7,13 +7,17 @@ import ( log "github.com/hashicorp/go-hclog" "github.com/hashicorp/vault/helper/logging" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/logical" ) func mockPolicyStore(t *testing.T) *PolicyStore { _, barrier, _ := mockBarrier(t) view := NewBarrierView(barrier, "foo/") - p := NewPolicyStore(context.Background(), nil, view, logical.TestSystemView(), logging.NewVaultLogger(log.Trace)) + p, err := NewPolicyStore(context.Background(), nil, view, logical.TestSystemView(), logging.NewVaultLogger(log.Trace)) + if err != nil { + t.Fatal(err) + } return p } @@ -22,49 +26,93 @@ func mockPolicyStoreNoCache(t *testing.T) *PolicyStore { sysView.CachingDisabledVal = true _, barrier, _ := mockBarrier(t) view := NewBarrierView(barrier, "foo/") - p := NewPolicyStore(context.Background(), nil, view, sysView, logging.NewVaultLogger(log.Trace)) + p, err := NewPolicyStore(context.Background(), nil, view, sysView, logging.NewVaultLogger(log.Trace)) + if err != nil { + t.Fatal(err) + } return p } -func TestPolicyStore_Root(t *testing.T) { - ps := mockPolicyStore(t) +func mockPolicyWithCore(t *testing.T, disableCache bool) (*Core, *PolicyStore) { + conf := &CoreConfig{ + DisableCache: disableCache, + } + core, _, _ := TestCoreUnsealedWithConfig(t, conf) + ps := core.policyStore + return core, ps +} + +func TestPolicyStore_Root(t *testing.T) { + t.Run("root", func(t *testing.T) { + t.Parallel() + + core, _, _ := TestCoreUnsealed(t) + ps := core.policyStore + testPolicyRoot(t, ps, namespace.RootNamespace, true) + }) +} + +func testPolicyRoot(t *testing.T, ps *PolicyStore, ns *namespace.Namespace, expectFound bool) { // Get should return a special policy - p, err := ps.GetPolicy(context.Background(), "root", PolicyTypeToken) + ctx := namespace.ContextWithNamespace(context.Background(), ns) + p, err := ps.GetPolicy(ctx, "root", PolicyTypeToken) if err != nil { t.Fatalf("err: %v", err) } - if p == nil { - t.Fatalf("bad: %v", p) - } - if p.Name != "root" { - t.Fatalf("bad: %v", p) + + // Handle whether a root token is expected + if expectFound { + if p == nil { + t.Fatalf("bad: %v", p) + } + if p.Name != "root" { + t.Fatalf("bad: %v", p) + } + } else { + if p != nil { + t.Fatal("expected nil root policy") + } + // Create root policy for subsequent modification and deletion failure + // tests + p = &Policy{ + Name: "root", + } } // Set should fail - err = ps.SetPolicy(context.Background(), p) + ctx = namespace.ContextWithNamespace(context.Background(), ns) + err = ps.SetPolicy(ctx, p) if err.Error() != `cannot update "root" policy` { t.Fatalf("err: %v", err) } // Delete should fail - err = ps.DeletePolicy(context.Background(), "root", PolicyTypeACL) + ctx = namespace.ContextWithNamespace(context.Background(), ns) + err = ps.DeletePolicy(ctx, "root", PolicyTypeACL) if err.Error() != `cannot delete "root" policy` { t.Fatalf("err: %v", err) } } func TestPolicyStore_CRUD(t *testing.T) { - ps := mockPolicyStore(t) - testPolicyStore_CRUD(t, ps) + t.Run("root-ns", func(t *testing.T) { + t.Run("cached", func(t *testing.T) { + _, ps := mockPolicyWithCore(t, false) + testPolicyStoreCRUD(t, ps, namespace.RootNamespace) + }) - ps = mockPolicyStoreNoCache(t) - testPolicyStore_CRUD(t, ps) + t.Run("no-cache", func(t *testing.T) { + _, ps := mockPolicyWithCore(t, true) + testPolicyStoreCRUD(t, ps, namespace.RootNamespace) + }) + }) } -func testPolicyStore_CRUD(t *testing.T, ps *PolicyStore) { +func testPolicyStoreCRUD(t *testing.T, ps *PolicyStore, ns *namespace.Namespace) { // Get should return nothing - p, err := ps.GetPolicy(context.Background(), "Dev", PolicyTypeToken) + ctx := namespace.ContextWithNamespace(context.Background(), ns) + p, err := ps.GetPolicy(ctx, "Dev", PolicyTypeToken) if err != nil { t.Fatalf("err: %v", err) } @@ -73,29 +121,33 @@ func testPolicyStore_CRUD(t *testing.T, ps *PolicyStore) { } // Delete should be no-op - err = ps.DeletePolicy(context.Background(), "deV", PolicyTypeACL) + ctx = namespace.ContextWithNamespace(context.Background(), ns) + err = ps.DeletePolicy(ctx, "deV", PolicyTypeACL) if err != nil { t.Fatalf("err: %v", err) } // List should be blank - out, err := ps.ListPolicies(context.Background(), PolicyTypeACL) + ctx = namespace.ContextWithNamespace(context.Background(), ns) + out, err := ps.ListPolicies(ctx, PolicyTypeACL) if err != nil { t.Fatalf("err: %v", err) } - if len(out) != 0 { + if len(out) != 1 { t.Fatalf("bad: %v", out) } // Set should work - policy, _ := ParseACLPolicy(aclPolicy) - err = ps.SetPolicy(context.Background(), policy) + ctx = namespace.ContextWithNamespace(context.Background(), ns) + policy, _ := ParseACLPolicy(ns, aclPolicy) + err = ps.SetPolicy(ctx, policy) if err != nil { t.Fatalf("err: %v", err) } // Get should work - p, err = ps.GetPolicy(context.Background(), "dEv", PolicyTypeToken) + ctx = namespace.ContextWithNamespace(context.Background(), ns) + p, err = ps.GetPolicy(ctx, "dEv", PolicyTypeToken) if err != nil { t.Fatalf("err: %v", err) } @@ -103,23 +155,41 @@ func testPolicyStore_CRUD(t *testing.T, ps *PolicyStore) { t.Fatalf("bad: %v", p) } - // List should be one element - out, err = ps.ListPolicies(context.Background(), PolicyTypeACL) + // List should contain two elements + ctx = namespace.ContextWithNamespace(context.Background(), ns) + out, err = ps.ListPolicies(ctx, PolicyTypeACL) if err != nil { t.Fatalf("err: %v", err) } - if len(out) != 1 || out[0] != "dev" { + if len(out) != 2 { t.Fatalf("bad: %v", out) } + expected := []string{"default", "dev"} + if !reflect.DeepEqual(expected, out) { + t.Fatalf("expected: %v\ngot: %v", expected, out) + } + // Delete should be clear the entry - err = ps.DeletePolicy(context.Background(), "Dev", PolicyTypeACL) + ctx = namespace.ContextWithNamespace(context.Background(), ns) + err = ps.DeletePolicy(ctx, "Dev", PolicyTypeACL) if err != nil { t.Fatalf("err: %v", err) } + // List should contain one element + ctx = namespace.ContextWithNamespace(context.Background(), ns) + out, err = ps.ListPolicies(ctx, PolicyTypeACL) + if err != nil { + t.Fatalf("err: %v", err) + } + if len(out) != 1 || out[0] != "default" { + t.Fatalf("bad: %v", out) + } + // Get should fail - p, err = ps.GetPolicy(context.Background(), "deV", PolicyTypeToken) + ctx = namespace.ContextWithNamespace(context.Background(), ns) + p, err = ps.GetPolicy(ctx, "deV", PolicyTypeToken) if err != nil { t.Fatalf("err: %v", err) } @@ -128,16 +198,18 @@ func testPolicyStore_CRUD(t *testing.T, ps *PolicyStore) { } } -// Test predefined policy handling func TestPolicyStore_Predefined(t *testing.T) { - core, _, _ := TestCoreUnsealed(t) - // Ensure both default policies are created - err := core.setupPolicyStore(context.Background()) - if err != nil { - t.Fatalf("err: %v", err) - } + t.Run("root-ns", func(t *testing.T) { + _, ps := mockPolicyWithCore(t, false) + testPolicyStorePredefined(t, ps, namespace.RootNamespace) + }) +} + +// Test predefined policy handling +func testPolicyStorePredefined(t *testing.T, ps *PolicyStore, ns *namespace.Namespace) { // List should be two elements - out, err := core.policyStore.ListPolicies(context.Background(), PolicyTypeACL) + ctx := namespace.ContextWithNamespace(context.Background(), ns) + out, err := ps.ListPolicies(ctx, PolicyTypeACL) if err != nil { t.Fatalf("err: %v", err) } @@ -146,7 +218,9 @@ func TestPolicyStore_Predefined(t *testing.T) { t.Fatalf("bad: %v", out) } - pCubby, err := core.policyStore.GetPolicy(context.Background(), "response-wrapping", PolicyTypeToken) + // Response-wrapping policy checks + ctx = namespace.ContextWithNamespace(context.Background(), ns) + pCubby, err := ps.GetPolicy(ctx, "response-wrapping", PolicyTypeToken) if err != nil { t.Fatalf("err: %v", err) } @@ -156,49 +230,72 @@ func TestPolicyStore_Predefined(t *testing.T) { if pCubby.Raw != responseWrappingPolicy { t.Fatalf("bad: expected\n%s\ngot\n%s\n", responseWrappingPolicy, pCubby.Raw) } - pRoot, err := core.policyStore.GetPolicy(context.Background(), "root", PolicyTypeToken) - if err != nil { - t.Fatalf("err: %v", err) - } - if pRoot == nil { - t.Fatal("nil root policy") - } - - err = core.policyStore.SetPolicy(context.Background(), pCubby) + ctx = namespace.ContextWithNamespace(context.Background(), ns) + err = ps.SetPolicy(ctx, pCubby) if err == nil { t.Fatalf("expected err setting %s", pCubby.Name) } - err = core.policyStore.SetPolicy(context.Background(), pRoot) - if err == nil { - t.Fatalf("expected err setting %s", pRoot.Name) - } - err = core.policyStore.DeletePolicy(context.Background(), pCubby.Name, PolicyTypeACL) + ctx = namespace.ContextWithNamespace(context.Background(), ns) + err = ps.DeletePolicy(ctx, pCubby.Name, PolicyTypeACL) if err == nil { t.Fatalf("expected err deleting %s", pCubby.Name) } - err = core.policyStore.DeletePolicy(context.Background(), pRoot.Name, PolicyTypeACL) + + // Root policy checks, behavior depending on namespace + ctx = namespace.ContextWithNamespace(context.Background(), ns) + pRoot, err := ps.GetPolicy(ctx, "root", PolicyTypeToken) + if err != nil { + t.Fatalf("err: %v", err) + } + if ns == namespace.RootNamespace { + if pRoot == nil { + t.Fatal("nil root policy") + } + } else { + if pRoot != nil { + t.Fatal("expected nil root policy") + } + pRoot = &Policy{ + Name: "root", + } + } + ctx = namespace.ContextWithNamespace(context.Background(), ns) + err = ps.SetPolicy(ctx, pRoot) + if err == nil { + t.Fatalf("expected err setting %s", pRoot.Name) + } + ctx = namespace.ContextWithNamespace(context.Background(), ns) + err = ps.DeletePolicy(ctx, pRoot.Name, PolicyTypeACL) if err == nil { t.Fatalf("expected err deleting %s", pRoot.Name) } } func TestPolicyStore_ACL(t *testing.T) { - ps := mockPolicyStore(t) - - policy, _ := ParseACLPolicy(aclPolicy) - err := ps.SetPolicy(context.Background(), policy) - if err != nil { - t.Fatalf("err: %v", err) - } - policy, _ = ParseACLPolicy(aclPolicy2) - err = ps.SetPolicy(context.Background(), policy) - if err != nil { - t.Fatalf("err: %v", err) - } - - acl, err := ps.ACL(context.Background(), nil, "dev", "ops") - if err != nil { - t.Fatalf("err: %v", err) - } - testLayeredACL(t, acl) + t.Run("root-ns", func(t *testing.T) { + _, ps := mockPolicyWithCore(t, false) + testPolicyStoreACL(t, ps, namespace.RootNamespace) + }) +} + +func testPolicyStoreACL(t *testing.T, ps *PolicyStore, ns *namespace.Namespace) { + ctx := namespace.ContextWithNamespace(context.Background(), ns) + policy, _ := ParseACLPolicy(ns, aclPolicy) + err := ps.SetPolicy(ctx, policy) + if err != nil { + t.Fatalf("err: %v", err) + } + ctx = namespace.ContextWithNamespace(context.Background(), ns) + policy, _ = ParseACLPolicy(ns, aclPolicy2) + err = ps.SetPolicy(ctx, policy) + if err != nil { + t.Fatalf("err: %v", err) + } + + ctx = namespace.ContextWithNamespace(context.Background(), ns) + acl, err := ps.ACL(ctx, nil, map[string][]string{ns.ID: []string{"dev", "ops"}}) + if err != nil { + t.Fatalf("err: %v", err) + } + testLayeredACL(t, acl, ns) } diff --git a/vault/policy_store_util.go b/vault/policy_store_util.go new file mode 100644 index 0000000000..c2c7a35a53 --- /dev/null +++ b/vault/policy_store_util.go @@ -0,0 +1,47 @@ +// +build !enterprise + +package vault + +import ( + "context" + + "github.com/hashicorp/vault/helper/namespace" + "github.com/hashicorp/vault/logical" +) + +type entPolicyStore struct{} + +func (ps *PolicyStore) extraInit() { +} + +func (ps *PolicyStore) loadNamespacePolicies(context.Context, *Core) error { return nil } + +func (ps *PolicyStore) getACLView(*namespace.Namespace) *BarrierView { + return ps.aclView +} + +func (ps *PolicyStore) getRGPView(ns *namespace.Namespace) *BarrierView { + return ps.rgpView +} + +func (ps *PolicyStore) getEGPView(ns *namespace.Namespace) *BarrierView { + return ps.egpView +} + +func (ps *PolicyStore) getBarrierView(ns *namespace.Namespace, _ PolicyType) *BarrierView { + return ps.getACLView(ns) +} + +func (ps *PolicyStore) handleSentinelPolicy(context.Context, *Policy, *BarrierView, *logical.StorageEntry) error { + return nil +} + +func (ps *PolicyStore) parseEGPPaths(*Policy) error { return nil } + +func (ps *PolicyStore) invalidateEGPTreePath(string) {} + +func (ps *PolicyStore) pathsToEGPPaths(*Policy) ([]*egpPath, error) { return nil, nil } + +func (ps *PolicyStore) loadACLPolicyNamespaces(ctx context.Context, policyName, policyText string) error { + return ps.loadACLPolicyInternal(namespace.RootContext(ctx), policyName, policyText) +} diff --git a/vault/policy_test.go b/vault/policy_test.go index ba40af3df2..65dcb43711 100644 --- a/vault/policy_test.go +++ b/vault/policy_test.go @@ -5,6 +5,8 @@ import ( "strings" "testing" "time" + + "github.com/hashicorp/vault/helper/namespace" ) var rawPolicy = strings.TrimSpace(` @@ -89,10 +91,14 @@ path "test/req" { capabilities = ["create", "sudo"] required_parameters = ["foo"] } +path "test/mfa" { + capabilities = ["create", "sudo"] + mfa_methods = ["my_totp", "my_totp2"] +} `) func TestPolicy_Parse(t *testing.T) { - p, err := ParseACLPolicy(rawPolicy) + p, err := ParseACLPolicy(namespace.RootNamespace, rawPolicy) if err != nil { t.Fatalf("err: %v", err) } @@ -243,14 +249,35 @@ func TestPolicy_Parse(t *testing.T) { }, Glob: false, }, + &PathRules{ + Prefix: "test/mfa", + Policy: "", + Capabilities: []string{ + "create", + "sudo", + }, + MFAMethodsHCL: []string{ + "my_totp", + "my_totp2", + }, + Permissions: &ACLPermissions{ + CapabilitiesBitmap: (CreateCapabilityInt | SudoCapabilityInt), + MFAMethods: []string{ + "my_totp", + "my_totp2", + }, + }, + Glob: false, + }, } + if !reflect.DeepEqual(p.Paths, expect) { t.Errorf("expected \n\n%#v\n\n to be \n\n%#v\n\n", p.Paths, expect) } } func TestPolicy_ParseBadRoot(t *testing.T) { - _, err := ParseACLPolicy(strings.TrimSpace(` + _, err := ParseACLPolicy(namespace.RootNamespace, strings.TrimSpace(` name = "test" bad = "foo" nope = "yes" @@ -269,7 +296,7 @@ nope = "yes" } func TestPolicy_ParseBadPath(t *testing.T) { - _, err := ParseACLPolicy(strings.TrimSpace(` + _, err := ParseACLPolicy(namespace.RootNamespace, strings.TrimSpace(` path "/" { capabilities = ["read"] capabilites = ["read"] @@ -285,7 +312,7 @@ path "/" { } func TestPolicy_ParseBadPolicy(t *testing.T) { - _, err := ParseACLPolicy(strings.TrimSpace(` + _, err := ParseACLPolicy(namespace.RootNamespace, strings.TrimSpace(` path "/" { policy = "banana" } @@ -300,7 +327,7 @@ path "/" { } func TestPolicy_ParseBadWrapping(t *testing.T) { - _, err := ParseACLPolicy(strings.TrimSpace(` + _, err := ParseACLPolicy(namespace.RootNamespace, strings.TrimSpace(` path "/" { policy = "read" min_wrapping_ttl = 400 @@ -317,7 +344,7 @@ path "/" { } func TestPolicy_ParseBadCapabilities(t *testing.T) { - _, err := ParseACLPolicy(strings.TrimSpace(` + _, err := ParseACLPolicy(namespace.RootNamespace, strings.TrimSpace(` path "/" { capabilities = ["read", "banana"] } diff --git a/vault/policy_util.go b/vault/policy_util.go new file mode 100644 index 0000000000..74b9263920 --- /dev/null +++ b/vault/policy_util.go @@ -0,0 +1,5 @@ +// +build !enterprise + +package vault + +type sentinelPolicy struct{} diff --git a/vault/rekey_test.go b/vault/rekey_test.go index 6b65ce5513..9d61b9b187 100644 --- a/vault/rekey_test.go +++ b/vault/rekey_test.go @@ -23,15 +23,6 @@ func TestCore_Rekey_Lifecycle(t *testing.T) { t.Fatalf("expected %d keys, got %d", bc.SecretShares-bc.StoredShares, len(masterKeys)) } testCore_Rekey_Lifecycle_Common(t, c, masterKeys, false) - - bc, _ = TestSealDefConfigs() - bc.SecretShares = 3 - bc.SecretThreshold = 3 - c, masterKeys, _, _ = TestCoreUnsealedWithConfigs(t, bc, nil) - if len(masterKeys) != 3 { - t.Fatalf("expected %d keys, got %d", bc.SecretShares-bc.StoredShares, len(masterKeys)) - } - testCore_Rekey_Lifecycle_Common(t, c, masterKeys, false) } func testCore_Rekey_Lifecycle_Common(t *testing.T, c *Core, masterKeys [][]byte, recovery bool) { @@ -97,8 +88,10 @@ func testCore_Rekey_Lifecycle_Common(t *testing.T, c *Core, masterKeys [][]byte, } func TestCore_Rekey_Init(t *testing.T) { - c, _, _ := TestCoreUnsealed(t) - testCore_Rekey_Init_Common(t, c, false) + t.Run("barrier-rekey-init", func(t *testing.T) { + c, _, _ := TestCoreUnsealed(t) + testCore_Rekey_Init_Common(t, c, false) + }) } func testCore_Rekey_Init_Common(t *testing.T, c *Core, recovery bool) { @@ -117,6 +110,13 @@ func testCore_Rekey_Init_Common(t *testing.T, c *Core, recovery bool) { SecretThreshold: 3, SecretShares: 5, } + + // If recovery key is supported, set newConf + // to be a recovery seal config + if c.seal.RecoveryKeySupported() { + newConf.Type = c.seal.RecoveryType() + } + err = c.RekeyInit(newConf, recovery) if err != nil { t.Fatalf("err: %v", err) @@ -133,6 +133,7 @@ func TestCore_Rekey_Update(t *testing.T) { bc, _ := TestSealDefConfigs() bc.SecretShares = 1 bc.SecretThreshold = 1 + bc.StoredShares = 0 c, masterKeys, _, root := TestCoreUnsealedWithConfigs(t, bc, nil) testCore_Rekey_Update_Common(t, c, masterKeys, root, false) } @@ -177,7 +178,14 @@ func testCore_Rekey_Update_Common(t *testing.T, c *Core, keys [][]byte, root str break } } - if result == nil || len(result.SecretShares) != newConf.SecretShares { + if result == nil { + t.Fatal("nil result after update") + } + if newConf.StoredShares > 0 { + if len(result.SecretShares) > 0 { + t.Fatal("got secret shares when should have been storing") + } + } else if len(result.SecretShares) != newConf.SecretShares { t.Fatalf("rekey update error: %#v", result) } @@ -214,6 +222,13 @@ func testCore_Rekey_Update_Common(t *testing.T, c *Core, keys [][]byte, root str t.Fatalf("\nexpected: %#v\nactual: %#v\nexpType: %s\nrecovery: %t", newConf, sealConf, expType, recovery) } + // At this point bail if we are rekeying the barrier key with recovery + // keys, since a new rekey should still be using the same set of recovery + // keys and we haven't been returned key shares in this mode. + if !recovery && c.seal.RecoveryKeySupported() { + return + } + // Attempt unseal if this was not recovery mode if !recovery { err = c.Seal(root) @@ -232,12 +247,6 @@ func testCore_Rekey_Update_Common(t *testing.T, c *Core, keys [][]byte, root str } // Start another rekey, this time we require a quorum! - // Skip this step if we are rekeying the barrier key with - // recovery keys, since a new rekey should still be using - // the same set of recovery keys. - if !recovery && c.seal.RecoveryKeySupported() { - return - } newConf = &SealConfig{ Type: expType, diff --git a/vault/replication_cluster_util.go b/vault/replication_cluster_util.go new file mode 100644 index 0000000000..013cc8f705 --- /dev/null +++ b/vault/replication_cluster_util.go @@ -0,0 +1,11 @@ +// +build !enterprise + +package vault + +import "github.com/hashicorp/vault/helper/consts" + +type ReplicatedCluster struct { + State consts.ReplicationState + ClusterID string + PrimaryClusterAddr string +} diff --git a/vault/request_forwarding.go b/vault/request_forwarding.go index 5732183c29..92cf8a2da3 100644 --- a/vault/request_forwarding.go +++ b/vault/request_forwarding.go @@ -9,11 +9,13 @@ import ( "net" "net/http" "net/url" - "runtime" "sync" "sync/atomic" "time" + cache "github.com/patrickmn/go-cache" + + uuid "github.com/hashicorp/go-uuid" "github.com/hashicorp/vault/helper/consts" "github.com/hashicorp/vault/helper/forwarding" "golang.org/x/net/http2" @@ -23,7 +25,18 @@ import ( const ( clusterListenerAcceptDeadline = 500 * time.Millisecond - requestForwardingALPN = "req_fw_sb-act_v1" + + // PerformanceReplicationALPN is the negotiated protocol used for + // performance replication. + PerformanceReplicationALPN = "replication_v1" + + // DRReplicationALPN is the negotiated protocol used for + // dr replication. + DRReplicationALPN = "replication_dr_v1" + + perfStandbyALPN = "perf_standby_v1" + + requestForwardingALPN = "req_fw_sb-act_v1" ) var ( @@ -31,6 +44,13 @@ var ( HeartbeatInterval = 5 * time.Second ) +type SecondaryConnsCacheVals struct { + ID string + Token string + Connection net.Conn + Mode consts.ReplicationState +} + // Starts the listeners and servers necessary to handle forwarded requests func (c *Core) startForwarding(ctx context.Context) error { c.logger.Debug("cluster listener setup function") @@ -44,15 +64,32 @@ func (c *Core) startForwarding(ctx context.Context) error { // Resolve locally to avoid races ha := c.ha != nil + var perfStandbyRepCluster *ReplicatedCluster + if ha { + id, err := uuid.GenerateUUID() + if err != nil { + return err + } + + perfStandbyRepCluster = &ReplicatedCluster{ + State: consts.ReplicationPerformanceStandby, + ClusterID: id, + PrimaryClusterAddr: c.clusterAddr, + } + if err = c.setupReplicatedClusterPrimary(perfStandbyRepCluster); err != nil { + return err + } + } + // Get our TLS config - tlsConfig, err := c.ClusterTLSConfig(ctx, nil) + tlsConfig, err := c.ClusterTLSConfig(ctx, nil, perfStandbyRepCluster) if err != nil { c.logger.Error("failed to get tls configuration when starting forwarding", "error", err) return err } // The server supports all of the possible protos - tlsConfig.NextProtos = []string{"h2", requestForwardingALPN} + tlsConfig.NextProtos = []string{"h2", requestForwardingALPN, perfStandbyALPN, PerformanceReplicationALPN, DRReplicationALPN} if !atomic.CompareAndSwapUint32(c.rpcServerActive, 0, 1) { c.logger.Warn("forwarding rpc server already running") @@ -67,10 +104,33 @@ func (c *Core) startForwarding(ctx context.Context) error { grpc.MaxSendMsgSize(math.MaxInt32), ) + // Setup performance standby RPC servers + perfStandbyCount := 0 + if !c.IsDRSecondary() && !c.disablePerfStandby { + perfStandbyCount = c.perfStandbyCount() + } + perfStandbySlots := make(chan struct{}, perfStandbyCount) + + perfStandbyCache := cache.New(2*HeartbeatInterval, 1*time.Second) + perfStandbyCache.OnEvicted(func(secondaryID string, _ interface{}) { + c.logger.Debug("removing performance standby", "id", secondaryID) + c.removePerfStandbySecondary(context.Background(), secondaryID) + select { + case <-perfStandbySlots: + default: + c.logger.Warn("perf secondary timeout hit but no slot to free") + } + }) + + perfStandbyReplicationRPCServer := perfStandbyRPCServer(c, perfStandbyCache) + if ha && c.clusterHandler != nil { RegisterRequestForwardingServer(fwRPCServer, &forwardedRequestRPCServer{ - core: c, - handler: c.clusterHandler, + core: c, + handler: c.clusterHandler, + perfStandbySlots: perfStandbySlots, + perfStandbyRepCluster: perfStandbyRepCluster, + perfStandbyCache: perfStandbyCache, }) } @@ -221,6 +281,8 @@ func (c *Core) startForwarding(ctx context.Context) error { shutdownWg.Done() }() + case PerformanceReplicationALPN, DRReplicationALPN, perfStandbyALPN: + handleReplicationConn(ctx, c, shutdownWg, closeCh, fws, perfStandbyReplicationRPCServer, perfStandbyCache, tlsConn) default: c.logger.Debug("unknown negotiated protocol on cluster port") tlsConn.Close() @@ -293,7 +355,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, nil)), + grpc.WithDialer(c.getGRPCDialer(ctx, requestForwardingALPN, "", nil, nil, nil)), grpc.WithInsecure(), // it's not, we handle it in the dialer grpc.WithKeepaliveParams(keepalive.ClientParameters{ Time: 2 * HeartbeatInterval, @@ -347,6 +409,13 @@ func (c *Core) ForwardRequest(req *http.Request) (int, http.Header, []byte, erro return 0, nil, nil, ErrCannotForward } + origPath := req.URL.Path + defer func() { + req.URL.Path = origPath + }() + + req.URL.Path = req.Context().Value("original_request_path").(string) + freq, err := forwarding.GenerateForwardedRequest(req) if err != nil { c.logger.Error("error creating forwarding RPC request", "error", err) @@ -370,15 +439,22 @@ func (c *Core) ForwardRequest(req *http.Request) (int, http.Header, []byte, erro } } + // If we are a perf standby and the request was forwarded to the active node + // we should attempt to wait for the WAL to ship to offer best effort read after + // write guarantees + if c.perfStandby && resp.LastRemoteWal > 0 { + WaitUntilWALShipped(req.Context(), c, resp.LastRemoteWal) + } + return int(resp.StatusCode), header, resp.Body, nil } // 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, repClusters *ReplicatedClusters) func(string, time.Duration) (net.Conn, error) { +func (c *Core) getGRPCDialer(ctx context.Context, alpnProto, serverName string, caCert *x509.Certificate, repClusters *ReplicatedClusters, perfStandbyCluster *ReplicatedCluster) func(string, time.Duration) (net.Conn, error) { return func(addr string, timeout time.Duration) (net.Conn, error) { - tlsConfig, err := c.ClusterTLSConfig(ctx, repClusters) + tlsConfig, err := c.ClusterTLSConfig(ctx, repClusters, perfStandbyCluster) if err != nil { c.logger.Error("failed to get tls configuration", "error", err) return nil, err @@ -401,121 +477,3 @@ func (c *Core) getGRPCDialer(ctx context.Context, alpnProto, serverName string, return tls.DialWithDialer(dialer, "tcp", addr, tlsConfig) } } - -type forwardedRequestRPCServer struct { - core *Core - handler http.Handler -} - -func (s *forwardedRequestRPCServer) ForwardRequest(ctx context.Context, freq *forwarding.Request) (*forwarding.Response, error) { - //s.core.logger.Debug("forwarding: serving rpc forwarded request") - - // Parse an http.Request out of it - req, err := forwarding.ParseForwardedRequest(freq) - if err != nil { - return nil, err - } - - // A very dummy response writer that doesn't follow normal semantics, just - // lets you write a status code (last written wins) and a body. But it - // meets the interface requirements. - w := forwarding.NewRPCResponseWriter() - - resp := &forwarding.Response{} - - runRequest := func() { - defer func() { - // Logic here comes mostly from the Go source code - if err := recover(); err != nil { - const size = 64 << 10 - buf := make([]byte, size) - buf = buf[:runtime.Stack(buf, false)] - s.core.logger.Error("forwarding: panic serving request", "path", req.URL.Path, "error", err, "stacktrace", string(buf)) - } - }() - s.handler.ServeHTTP(w, req) - } - runRequest() - resp.StatusCode = uint32(w.StatusCode()) - resp.Body = w.Body().Bytes() - - header := w.Header() - if header != nil { - resp.HeaderEntries = make(map[string]*forwarding.HeaderEntry, len(header)) - for k, v := range header { - resp.HeaderEntries[k] = &forwarding.HeaderEntry{ - Values: v, - } - } - } - - return resp, nil -} - -func (s *forwardedRequestRPCServer) Echo(ctx context.Context, in *EchoRequest) (*EchoReply, error) { - if in.ClusterAddr != "" { - s.core.clusterPeerClusterAddrsCache.Set(in.ClusterAddr, nil, 0) - } - return &EchoReply{ - Message: "pong", - ReplicationState: uint32(s.core.ReplicationState()), - }, nil -} - -type forwardingClient struct { - RequestForwardingClient - - core *Core - - echoTicker *time.Ticker - echoContext context.Context -} - -// NOTE: we also take advantage of gRPC's keepalive bits, but as we send data -// with these requests it's useful to keep this as well -func (c *forwardingClient) startHeartbeat() { - go func() { - tick := func() { - c.core.stateLock.RLock() - clusterAddr := c.core.clusterAddr - c.core.stateLock.RUnlock() - - ctx, cancel := context.WithTimeout(c.echoContext, 2*time.Second) - resp, err := c.RequestForwardingClient.Echo(ctx, &EchoRequest{ - Message: "ping", - ClusterAddr: clusterAddr, - }) - cancel() - if err != nil { - c.core.logger.Debug("forwarding: error sending echo request to active node", "error", err) - return - } - if resp == nil { - c.core.logger.Debug("forwarding: empty echo response from active node") - return - } - if resp.Message != "pong" { - c.core.logger.Debug("forwarding: unexpected echo response from active node", "message", resp.Message) - return - } - // Store the active node's replication state to display in - // sys/health calls - atomic.StoreUint32(c.core.activeNodeReplicationState, resp.ReplicationState) - //c.core.logger.Debug("forwarding: successful heartbeat") - } - - tick() - - for { - select { - case <-c.echoContext.Done(): - c.echoTicker.Stop() - c.core.logger.Debug("forwarding: stopping heartbeating") - atomic.StoreUint32(c.core.activeNodeReplicationState, uint32(consts.ReplicationUnknown)) - return - case <-c.echoTicker.C: - tick() - } - } - }() -} diff --git a/vault/request_forwarding_rpc.go b/vault/request_forwarding_rpc.go new file mode 100644 index 0000000000..de5b024a72 --- /dev/null +++ b/vault/request_forwarding_rpc.go @@ -0,0 +1,133 @@ +package vault + +import ( + "context" + "net/http" + "runtime" + "sync/atomic" + "time" + + "github.com/hashicorp/vault/helper/consts" + "github.com/hashicorp/vault/helper/forwarding" + cache "github.com/patrickmn/go-cache" +) + +type forwardedRequestRPCServer struct { + core *Core + handler http.Handler + perfStandbySlots chan struct{} + perfStandbyRepCluster *ReplicatedCluster + perfStandbyCache *cache.Cache +} + +func (s *forwardedRequestRPCServer) ForwardRequest(ctx context.Context, freq *forwarding.Request) (*forwarding.Response, error) { + // Parse an http.Request out of it + req, err := forwarding.ParseForwardedRequest(freq) + if err != nil { + return nil, err + } + + // A very dummy response writer that doesn't follow normal semantics, just + // lets you write a status code (last written wins) and a body. But it + // meets the interface requirements. + w := forwarding.NewRPCResponseWriter() + + resp := &forwarding.Response{} + + runRequest := func() { + defer func() { + // Logic here comes mostly from the Go source code + if err := recover(); err != nil { + const size = 64 << 10 + buf := make([]byte, size) + buf = buf[:runtime.Stack(buf, false)] + s.core.logger.Error("forwarding: panic serving request", "path", req.URL.Path, "error", err, "stacktrace", string(buf)) + } + }() + s.handler.ServeHTTP(w, req) + } + runRequest() + resp.StatusCode = uint32(w.StatusCode()) + resp.Body = w.Body().Bytes() + + header := w.Header() + if header != nil { + resp.HeaderEntries = make(map[string]*forwarding.HeaderEntry, len(header)) + for k, v := range header { + resp.HeaderEntries[k] = &forwarding.HeaderEntry{ + Values: v, + } + } + } + + resp.LastRemoteWal = LastRemoteWAL(s.core) + + return resp, nil +} + +func (s *forwardedRequestRPCServer) Echo(ctx context.Context, in *EchoRequest) (*EchoReply, error) { + if in.ClusterAddr != "" { + s.core.clusterPeerClusterAddrsCache.Set(in.ClusterAddr, nil, 0) + } + return &EchoReply{ + Message: "pong", + ReplicationState: uint32(s.core.ReplicationState()), + }, nil +} + +type forwardingClient struct { + RequestForwardingClient + + core *Core + + echoTicker *time.Ticker + echoContext context.Context +} + +// NOTE: we also take advantage of gRPC's keepalive bits, but as we send data +// with these requests it's useful to keep this as well +func (c *forwardingClient) startHeartbeat() { + go func() { + tick := func() { + c.core.stateLock.RLock() + clusterAddr := c.core.clusterAddr + c.core.stateLock.RUnlock() + + ctx, cancel := context.WithTimeout(c.echoContext, 2*time.Second) + resp, err := c.RequestForwardingClient.Echo(ctx, &EchoRequest{ + Message: "ping", + ClusterAddr: clusterAddr, + }) + cancel() + if err != nil { + c.core.logger.Debug("forwarding: error sending echo request to active node", "error", err) + return + } + if resp == nil { + c.core.logger.Debug("forwarding: empty echo response from active node") + return + } + if resp.Message != "pong" { + c.core.logger.Debug("forwarding: unexpected echo response from active node", "message", resp.Message) + return + } + // Store the active node's replication state to display in + // sys/health calls + atomic.StoreUint32(c.core.activeNodeReplicationState, resp.ReplicationState) + } + + tick() + + for { + select { + case <-c.echoContext.Done(): + c.echoTicker.Stop() + c.core.logger.Debug("forwarding: stopping heartbeating") + atomic.StoreUint32(c.core.activeNodeReplicationState, uint32(consts.ReplicationUnknown)) + return + case <-c.echoTicker.C: + tick() + } + } + }() +} diff --git a/vault/request_forwarding_rpc_util.go b/vault/request_forwarding_rpc_util.go new file mode 100644 index 0000000000..f4cd607df9 --- /dev/null +++ b/vault/request_forwarding_rpc_util.go @@ -0,0 +1,17 @@ +// +build !enterprise + +package vault + +import ( + "context" +) + +func (s *forwardedRequestRPCServer) PerformanceStandbyElectionRequest(in *PerfStandbyElectionInput, reqServ RequestForwarding_PerformanceStandbyElectionRequestServer) error { + return nil +} + +type ReplicationTokenInfo struct{} + +func (c *forwardingClient) PerformanceStandbyElection(ctx context.Context) (*ReplicationTokenInfo, error) { + return nil, nil +} diff --git a/vault/request_forwarding_service.pb.go b/vault/request_forwarding_service.pb.go index 1461fecede..b1411c1992 100644 --- a/vault/request_forwarding_service.pb.go +++ b/vault/request_forwarding_service.pb.go @@ -41,7 +41,7 @@ func (m *EchoRequest) Reset() { *m = EchoRequest{} } func (m *EchoRequest) String() string { return proto.CompactTextString(m) } func (*EchoRequest) ProtoMessage() {} func (*EchoRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_request_forwarding_service_9602fb43b1d0ebdf, []int{0} + return fileDescriptor_request_forwarding_service_b285da38926babcd, []int{0} } func (m *EchoRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_EchoRequest.Unmarshal(m, b) @@ -95,7 +95,7 @@ func (m *EchoReply) Reset() { *m = EchoReply{} } func (m *EchoReply) String() string { return proto.CompactTextString(m) } func (*EchoReply) ProtoMessage() {} func (*EchoReply) Descriptor() ([]byte, []int) { - return fileDescriptor_request_forwarding_service_9602fb43b1d0ebdf, []int{1} + return fileDescriptor_request_forwarding_service_b285da38926babcd, []int{1} } func (m *EchoReply) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_EchoReply.Unmarshal(m, b) @@ -136,9 +136,182 @@ func (m *EchoReply) GetReplicationState() uint32 { return 0 } +type ClientKey struct { + Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` + X []byte `protobuf:"bytes,2,opt,name=x,proto3" json:"x,omitempty"` + Y []byte `protobuf:"bytes,3,opt,name=y,proto3" json:"y,omitempty"` + D []byte `protobuf:"bytes,4,opt,name=d,proto3" json:"d,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ClientKey) Reset() { *m = ClientKey{} } +func (m *ClientKey) String() string { return proto.CompactTextString(m) } +func (*ClientKey) ProtoMessage() {} +func (*ClientKey) Descriptor() ([]byte, []int) { + return fileDescriptor_request_forwarding_service_b285da38926babcd, []int{2} +} +func (m *ClientKey) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ClientKey.Unmarshal(m, b) +} +func (m *ClientKey) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ClientKey.Marshal(b, m, deterministic) +} +func (dst *ClientKey) XXX_Merge(src proto.Message) { + xxx_messageInfo_ClientKey.Merge(dst, src) +} +func (m *ClientKey) XXX_Size() int { + return xxx_messageInfo_ClientKey.Size(m) +} +func (m *ClientKey) XXX_DiscardUnknown() { + xxx_messageInfo_ClientKey.DiscardUnknown(m) +} + +var xxx_messageInfo_ClientKey proto.InternalMessageInfo + +func (m *ClientKey) GetType() string { + if m != nil { + return m.Type + } + return "" +} + +func (m *ClientKey) GetX() []byte { + if m != nil { + return m.X + } + return nil +} + +func (m *ClientKey) GetY() []byte { + if m != nil { + return m.Y + } + return nil +} + +func (m *ClientKey) GetD() []byte { + if m != nil { + return m.D + } + return nil +} + +type PerfStandbyElectionInput struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *PerfStandbyElectionInput) Reset() { *m = PerfStandbyElectionInput{} } +func (m *PerfStandbyElectionInput) String() string { return proto.CompactTextString(m) } +func (*PerfStandbyElectionInput) ProtoMessage() {} +func (*PerfStandbyElectionInput) Descriptor() ([]byte, []int) { + return fileDescriptor_request_forwarding_service_b285da38926babcd, []int{3} +} +func (m *PerfStandbyElectionInput) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_PerfStandbyElectionInput.Unmarshal(m, b) +} +func (m *PerfStandbyElectionInput) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_PerfStandbyElectionInput.Marshal(b, m, deterministic) +} +func (dst *PerfStandbyElectionInput) XXX_Merge(src proto.Message) { + xxx_messageInfo_PerfStandbyElectionInput.Merge(dst, src) +} +func (m *PerfStandbyElectionInput) XXX_Size() int { + return xxx_messageInfo_PerfStandbyElectionInput.Size(m) +} +func (m *PerfStandbyElectionInput) XXX_DiscardUnknown() { + xxx_messageInfo_PerfStandbyElectionInput.DiscardUnknown(m) +} + +var xxx_messageInfo_PerfStandbyElectionInput proto.InternalMessageInfo + +type PerfStandbyElectionResponse struct { + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` + PrimaryClusterAddr string `protobuf:"bytes,3,opt,name=primary_cluster_addr,json=primaryClusterAddr,proto3" json:"primary_cluster_addr,omitempty"` + CaCert []byte `protobuf:"bytes,4,opt,name=ca_cert,json=caCert,proto3" json:"ca_cert,omitempty"` + ClientCert []byte `protobuf:"bytes,5,opt,name=client_cert,json=clientCert,proto3" json:"client_cert,omitempty"` + ClientKey *ClientKey `protobuf:"bytes,6,opt,name=client_key,json=clientKey,proto3" json:"client_key,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *PerfStandbyElectionResponse) Reset() { *m = PerfStandbyElectionResponse{} } +func (m *PerfStandbyElectionResponse) String() string { return proto.CompactTextString(m) } +func (*PerfStandbyElectionResponse) ProtoMessage() {} +func (*PerfStandbyElectionResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_request_forwarding_service_b285da38926babcd, []int{4} +} +func (m *PerfStandbyElectionResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_PerfStandbyElectionResponse.Unmarshal(m, b) +} +func (m *PerfStandbyElectionResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_PerfStandbyElectionResponse.Marshal(b, m, deterministic) +} +func (dst *PerfStandbyElectionResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_PerfStandbyElectionResponse.Merge(dst, src) +} +func (m *PerfStandbyElectionResponse) XXX_Size() int { + return xxx_messageInfo_PerfStandbyElectionResponse.Size(m) +} +func (m *PerfStandbyElectionResponse) XXX_DiscardUnknown() { + xxx_messageInfo_PerfStandbyElectionResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_PerfStandbyElectionResponse proto.InternalMessageInfo + +func (m *PerfStandbyElectionResponse) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +func (m *PerfStandbyElectionResponse) GetClusterId() string { + if m != nil { + return m.ClusterId + } + return "" +} + +func (m *PerfStandbyElectionResponse) GetPrimaryClusterAddr() string { + if m != nil { + return m.PrimaryClusterAddr + } + return "" +} + +func (m *PerfStandbyElectionResponse) GetCaCert() []byte { + if m != nil { + return m.CaCert + } + return nil +} + +func (m *PerfStandbyElectionResponse) GetClientCert() []byte { + if m != nil { + return m.ClientCert + } + return nil +} + +func (m *PerfStandbyElectionResponse) GetClientKey() *ClientKey { + if m != nil { + return m.ClientKey + } + return nil +} + func init() { proto.RegisterType((*EchoRequest)(nil), "vault.EchoRequest") proto.RegisterType((*EchoReply)(nil), "vault.EchoReply") + proto.RegisterType((*ClientKey)(nil), "vault.ClientKey") + proto.RegisterType((*PerfStandbyElectionInput)(nil), "vault.PerfStandbyElectionInput") + proto.RegisterType((*PerfStandbyElectionResponse)(nil), "vault.PerfStandbyElectionResponse") } // Reference imports to suppress errors if they are not otherwise used. @@ -155,6 +328,7 @@ const _ = grpc.SupportPackageIsVersion4 type RequestForwardingClient interface { ForwardRequest(ctx context.Context, in *forwarding.Request, opts ...grpc.CallOption) (*forwarding.Response, error) Echo(ctx context.Context, in *EchoRequest, opts ...grpc.CallOption) (*EchoReply, error) + PerformanceStandbyElectionRequest(ctx context.Context, in *PerfStandbyElectionInput, opts ...grpc.CallOption) (RequestForwarding_PerformanceStandbyElectionRequestClient, error) } type requestForwardingClient struct { @@ -183,10 +357,43 @@ func (c *requestForwardingClient) Echo(ctx context.Context, in *EchoRequest, opt return out, nil } +func (c *requestForwardingClient) PerformanceStandbyElectionRequest(ctx context.Context, in *PerfStandbyElectionInput, opts ...grpc.CallOption) (RequestForwarding_PerformanceStandbyElectionRequestClient, error) { + stream, err := c.cc.NewStream(ctx, &_RequestForwarding_serviceDesc.Streams[0], "/vault.RequestForwarding/PerformanceStandbyElectionRequest", opts...) + if err != nil { + return nil, err + } + x := &requestForwardingPerformanceStandbyElectionRequestClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type RequestForwarding_PerformanceStandbyElectionRequestClient interface { + Recv() (*PerfStandbyElectionResponse, error) + grpc.ClientStream +} + +type requestForwardingPerformanceStandbyElectionRequestClient struct { + grpc.ClientStream +} + +func (x *requestForwardingPerformanceStandbyElectionRequestClient) Recv() (*PerfStandbyElectionResponse, error) { + m := new(PerfStandbyElectionResponse) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + // RequestForwardingServer is the server API for RequestForwarding service. type RequestForwardingServer interface { ForwardRequest(context.Context, *forwarding.Request) (*forwarding.Response, error) Echo(context.Context, *EchoRequest) (*EchoReply, error) + PerformanceStandbyElectionRequest(*PerfStandbyElectionInput, RequestForwarding_PerformanceStandbyElectionRequestServer) error } func RegisterRequestForwardingServer(s *grpc.Server, srv RequestForwardingServer) { @@ -229,6 +436,27 @@ func _RequestForwarding_Echo_Handler(srv interface{}, ctx context.Context, dec f return interceptor(ctx, in, info, handler) } +func _RequestForwarding_PerformanceStandbyElectionRequest_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(PerfStandbyElectionInput) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(RequestForwardingServer).PerformanceStandbyElectionRequest(m, &requestForwardingPerformanceStandbyElectionRequestServer{stream}) +} + +type RequestForwarding_PerformanceStandbyElectionRequestServer interface { + Send(*PerfStandbyElectionResponse) error + grpc.ServerStream +} + +type requestForwardingPerformanceStandbyElectionRequestServer struct { + grpc.ServerStream +} + +func (x *requestForwardingPerformanceStandbyElectionRequestServer) Send(m *PerfStandbyElectionResponse) error { + return x.ServerStream.SendMsg(m) +} + var _RequestForwarding_serviceDesc = grpc.ServiceDesc{ ServiceName: "vault.RequestForwarding", HandlerType: (*RequestForwardingServer)(nil), @@ -242,33 +470,51 @@ var _RequestForwarding_serviceDesc = grpc.ServiceDesc{ Handler: _RequestForwarding_Echo_Handler, }, }, - Streams: []grpc.StreamDesc{}, + Streams: []grpc.StreamDesc{ + { + StreamName: "PerformanceStandbyElectionRequest", + Handler: _RequestForwarding_PerformanceStandbyElectionRequest_Handler, + ServerStreams: true, + }, + }, Metadata: "vault/request_forwarding_service.proto", } func init() { - proto.RegisterFile("vault/request_forwarding_service.proto", fileDescriptor_request_forwarding_service_9602fb43b1d0ebdf) + proto.RegisterFile("vault/request_forwarding_service.proto", fileDescriptor_request_forwarding_service_b285da38926babcd) } -var fileDescriptor_request_forwarding_service_9602fb43b1d0ebdf = []byte{ - // 297 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x91, 0x41, 0x4b, 0xfb, 0x40, - 0x10, 0xc5, 0x9b, 0xf6, 0xff, 0x57, 0xba, 0x6d, 0xa5, 0x5d, 0x3d, 0x84, 0x82, 0x10, 0x23, 0x48, - 0x40, 0xd8, 0x80, 0x9e, 0x3d, 0x28, 0xe8, 0x07, 0x88, 0x37, 0x2f, 0x61, 0xbb, 0x19, 0x93, 0x85, - 0x6d, 0x76, 0xdd, 0xd9, 0xb4, 0xe4, 0xea, 0x27, 0x97, 0x26, 0xa9, 0x4d, 0x29, 0x78, 0x19, 0x98, - 0x37, 0x8f, 0x37, 0xcc, 0x6f, 0xc8, 0xdd, 0x86, 0x57, 0xca, 0xc5, 0x16, 0xbe, 0x2a, 0x40, 0x97, - 0x7e, 0x6a, 0xbb, 0xe5, 0x36, 0x93, 0x65, 0x9e, 0x22, 0xd8, 0x8d, 0x14, 0xc0, 0x8c, 0xd5, 0x4e, - 0xd3, 0xff, 0x8d, 0x6f, 0x79, 0x5d, 0x80, 0x32, 0x60, 0xe3, 0x83, 0x2f, 0x76, 0xb5, 0x01, 0x6c, - 0x5d, 0xa1, 0x26, 0x93, 0x57, 0x51, 0xe8, 0xa4, 0x4d, 0xa3, 0x3e, 0x39, 0x5f, 0x03, 0x22, 0xcf, - 0xc1, 0xf7, 0x02, 0x2f, 0x1a, 0x27, 0xfb, 0x96, 0xde, 0x90, 0xa9, 0x50, 0x15, 0x3a, 0xb0, 0x29, - 0xcf, 0x32, 0xeb, 0x0f, 0x9b, 0xf1, 0xa4, 0xd3, 0x9e, 0xb3, 0xcc, 0xd2, 0x5b, 0x32, 0xeb, 0x5b, - 0xd0, 0x1f, 0x05, 0xa3, 0x68, 0x9c, 0x4c, 0x7b, 0x1e, 0x0c, 0xb7, 0x64, 0xdc, 0x2e, 0x34, 0xaa, - 0xfe, 0x63, 0xdd, 0x49, 0xd6, 0xf0, 0x34, 0x8b, 0xde, 0x93, 0x85, 0x05, 0xa3, 0xa4, 0xe0, 0x4e, - 0xea, 0x32, 0x45, 0xc7, 0x1d, 0xf8, 0xa3, 0xc0, 0x8b, 0x66, 0xc9, 0xbc, 0x37, 0x78, 0xdf, 0xe9, - 0x0f, 0xdf, 0x1e, 0x59, 0x74, 0x67, 0xbe, 0xfd, 0xb2, 0xa0, 0x4f, 0xe4, 0xa2, 0xeb, 0xf6, 0x08, - 0x2e, 0xd9, 0x01, 0x15, 0xeb, 0xc4, 0xe5, 0xd5, 0xb1, 0x88, 0x46, 0x97, 0x08, 0xe1, 0x80, 0x32, - 0xf2, 0x6f, 0x77, 0x0d, 0xa5, 0xac, 0xa1, 0xcd, 0x7a, 0x2c, 0x97, 0xf3, 0x23, 0xcd, 0xa8, 0x3a, - 0x1c, 0xbc, 0x84, 0x1f, 0x41, 0x2e, 0x5d, 0x51, 0xad, 0x98, 0xd0, 0xeb, 0xb8, 0xe0, 0x58, 0x48, - 0xa1, 0xad, 0x89, 0xdb, 0x9f, 0x36, 0x75, 0x75, 0xd6, 0x7c, 0xe6, 0xf1, 0x27, 0x00, 0x00, 0xff, - 0xff, 0xfe, 0x9f, 0x88, 0xc6, 0xe9, 0x01, 0x00, 0x00, +var fileDescriptor_request_forwarding_service_b285da38926babcd = []byte{ + // 493 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x53, 0x41, 0x6f, 0x1a, 0x3d, + 0x10, 0x8d, 0x81, 0x10, 0x31, 0x90, 0x88, 0xf8, 0x8b, 0xf4, 0xad, 0xa8, 0xa2, 0x90, 0xad, 0x54, + 0x21, 0x55, 0xda, 0x8d, 0xd2, 0x73, 0x0f, 0x2d, 0x4a, 0x25, 0xd4, 0x4b, 0xb5, 0xb9, 0xf5, 0xb2, + 0x32, 0xf6, 0x04, 0xac, 0x2e, 0x6b, 0xd7, 0x36, 0x49, 0xf6, 0x27, 0xf7, 0xd6, 0x9f, 0x50, 0xad, + 0xd7, 0x04, 0x10, 0x4d, 0x2f, 0x68, 0xe7, 0xcd, 0x63, 0xde, 0xf8, 0xf9, 0x19, 0xde, 0x3d, 0xb2, + 0x75, 0xe1, 0x52, 0x83, 0x3f, 0xd7, 0x68, 0x5d, 0xfe, 0xa0, 0xcc, 0x13, 0x33, 0x42, 0x96, 0x8b, + 0xdc, 0xa2, 0x79, 0x94, 0x1c, 0x13, 0x6d, 0x94, 0x53, 0xf4, 0xd8, 0xf3, 0x46, 0x97, 0x4b, 0x2c, + 0x34, 0x9a, 0x74, 0xcb, 0x4b, 0x5d, 0xa5, 0xd1, 0x36, 0xac, 0x58, 0x41, 0xff, 0x8e, 0x2f, 0x55, + 0xd6, 0x4c, 0xa3, 0x11, 0x9c, 0xac, 0xd0, 0x5a, 0xb6, 0xc0, 0x88, 0x8c, 0xc9, 0xa4, 0x97, 0x6d, + 0x4a, 0x7a, 0x0d, 0x03, 0x5e, 0xac, 0xad, 0x43, 0x93, 0x33, 0x21, 0x4c, 0xd4, 0xf2, 0xed, 0x7e, + 0xc0, 0x3e, 0x09, 0x61, 0xe8, 0x5b, 0x38, 0xdd, 0xa5, 0xd8, 0xa8, 0x3d, 0x6e, 0x4f, 0x7a, 0xd9, + 0x60, 0x87, 0x63, 0xe3, 0x27, 0xe8, 0x35, 0x82, 0xba, 0xa8, 0xfe, 0x21, 0x77, 0x30, 0xab, 0x75, + 0x38, 0x8b, 0xbe, 0x87, 0x73, 0x83, 0xba, 0x90, 0x9c, 0x39, 0xa9, 0xca, 0xdc, 0x3a, 0xe6, 0x30, + 0x6a, 0x8f, 0xc9, 0xe4, 0x34, 0x1b, 0xee, 0x34, 0xee, 0x6b, 0x3c, 0x9e, 0x41, 0x6f, 0x5a, 0x48, + 0x2c, 0xdd, 0x57, 0xac, 0x28, 0x85, 0x4e, 0xed, 0x42, 0x50, 0xf5, 0xdf, 0x74, 0x00, 0xe4, 0xd9, + 0x1f, 0x6b, 0x90, 0x91, 0xe7, 0xba, 0xaa, 0xfc, 0xac, 0x41, 0x46, 0xaa, 0xba, 0x12, 0x51, 0xa7, + 0xa9, 0x44, 0x3c, 0x82, 0xe8, 0x1b, 0x9a, 0x87, 0x7b, 0xc7, 0x4a, 0x31, 0xaf, 0xee, 0x0a, 0xe4, + 0xb5, 0xcc, 0xac, 0xd4, 0x6b, 0x17, 0xff, 0x22, 0xf0, 0xe6, 0x2f, 0xcd, 0x0c, 0xad, 0x56, 0xa5, + 0x45, 0x7a, 0x06, 0x2d, 0x29, 0x82, 0x6e, 0x4b, 0x0a, 0x7a, 0x09, 0xb0, 0x39, 0xa8, 0x14, 0xc1, + 0xd5, 0x5e, 0x40, 0x66, 0x82, 0xde, 0xc0, 0x85, 0x36, 0x72, 0xc5, 0x4c, 0x95, 0xef, 0xd9, 0xdf, + 0xf6, 0x44, 0x1a, 0x7a, 0xd3, 0x9d, 0x5b, 0xf8, 0x1f, 0x4e, 0x38, 0xcb, 0x39, 0x1a, 0x17, 0x16, + 0xee, 0x72, 0x36, 0x45, 0xe3, 0xe8, 0x15, 0xf4, 0xb9, 0x37, 0xa0, 0x69, 0x1e, 0xfb, 0x26, 0x34, + 0x90, 0x27, 0xa4, 0x10, 0xaa, 0xfc, 0x07, 0x56, 0x51, 0x77, 0x4c, 0x26, 0xfd, 0xdb, 0x61, 0xe2, + 0x63, 0x94, 0xbc, 0x58, 0x57, 0x2f, 0x17, 0x3e, 0x6f, 0x7f, 0x13, 0x38, 0x0f, 0xc9, 0xf9, 0xf2, + 0x12, 0x2f, 0xfa, 0x11, 0xce, 0x42, 0xb5, 0x49, 0xd5, 0x7f, 0xc9, 0x36, 0x7d, 0x49, 0x00, 0x47, + 0x17, 0xfb, 0x60, 0x63, 0x4f, 0x7c, 0x44, 0x13, 0xe8, 0xd4, 0x01, 0xa1, 0x34, 0x28, 0xef, 0xc4, + 0x73, 0x34, 0xdc, 0xc3, 0x74, 0x51, 0xc5, 0x47, 0xb4, 0x80, 0xeb, 0xda, 0x6f, 0x65, 0x56, 0xac, + 0xe4, 0x78, 0x60, 0x7b, 0xb3, 0xc1, 0x55, 0xf8, 0xe3, 0x6b, 0xd7, 0x36, 0x8a, 0x5f, 0x27, 0x6c, + 0x77, 0xbb, 0x21, 0x9f, 0xe3, 0xef, 0xe3, 0x85, 0x74, 0xcb, 0xf5, 0x3c, 0xe1, 0x6a, 0x95, 0x2e, + 0x99, 0x5d, 0x4a, 0xae, 0x8c, 0x4e, 0x9b, 0x47, 0xe9, 0x7f, 0xe7, 0x5d, 0xff, 0xb4, 0x3e, 0xfc, + 0x09, 0x00, 0x00, 0xff, 0xff, 0x03, 0x94, 0x0a, 0x17, 0xaa, 0x03, 0x00, 0x00, } diff --git a/vault/request_forwarding_service.proto b/vault/request_forwarding_service.proto index ba32f7dfb8..3429aaf5c3 100644 --- a/vault/request_forwarding_service.proto +++ b/vault/request_forwarding_service.proto @@ -22,7 +22,25 @@ message EchoReply { uint32 replication_state = 3; } +message ClientKey { + string type = 1; + bytes x = 2; + bytes y = 3; + bytes d = 4; +} + +message PerfStandbyElectionInput {} +message PerfStandbyElectionResponse { + string id = 1; + string cluster_id = 2; + string primary_cluster_addr = 3; + bytes ca_cert = 4; + bytes client_cert = 5; + ClientKey client_key = 6; +} + service RequestForwarding { rpc ForwardRequest(forwarding.Request) returns (forwarding.Response) {} rpc Echo(EchoRequest) returns (EchoReply) {} + rpc PerformanceStandbyElectionRequest(PerfStandbyElectionInput) returns (stream PerfStandbyElectionResponse) {} } diff --git a/vault/request_forwarding_util.go b/vault/request_forwarding_util.go new file mode 100644 index 0000000000..20fae15f0c --- /dev/null +++ b/vault/request_forwarding_util.go @@ -0,0 +1,18 @@ +// +build !enterprise + +package vault + +import ( + "context" + "crypto/tls" + "sync" + + cache "github.com/patrickmn/go-cache" + "golang.org/x/net/http2" + grpc "google.golang.org/grpc" +) + +func perfStandbyRPCServer(*Core, *cache.Cache) *grpc.Server { return nil } + +func handleReplicationConn(context.Context, *Core, *sync.WaitGroup, chan struct{}, *http2.Server, *grpc.Server, *cache.Cache, *tls.Conn) { +} diff --git a/vault/request_handling.go b/vault/request_handling.go index 8b5b8d5b13..90cc279ca2 100644 --- a/vault/request_handling.go +++ b/vault/request_handling.go @@ -16,6 +16,7 @@ import ( "github.com/hashicorp/vault/helper/errutil" "github.com/hashicorp/vault/helper/identity" "github.com/hashicorp/vault/helper/jsonutil" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/helper/policyutil" "github.com/hashicorp/vault/helper/strutil" "github.com/hashicorp/vault/helper/wrapping" @@ -31,6 +32,8 @@ var ( // DefaultMaxRequestDuration is the amount of time we'll wait for a request // to complete, unless overridden on a per-handler basis DefaultMaxRequestDuration = 90 * time.Second + + egpDebugLogging bool ) // HandlerProperties is used to seed configuration into a vaulthttp.Handler. @@ -48,7 +51,7 @@ type HandlerProperties struct { // also returns the cumulative list of policies that the entity is entitled to. // This list includes the policies from the entity itself and from all the // groups in which the given entity ID is a member of. -func (c *Core) fetchEntityAndDerivedPolicies(entityID string) (*identity.Entity, []string, error) { +func (c *Core) fetchEntityAndDerivedPolicies(ctx context.Context, tokenNS *namespace.Namespace, entityID string) (*identity.Entity, map[string][]string, error) { if entityID == "" || c.identityStore == nil { return nil, nil, nil } @@ -73,12 +76,14 @@ func (c *Core) fetchEntityAndDerivedPolicies(entityID string) (*identity.Entity, } } - var policies []string + policies := make(map[string][]string) if entity != nil { //c.logger.Debug("entity successfully fetched; adding entity policies to token's policies to create ACL") // Attach the policies on the entity - policies = append(policies, entity.Policies...) + if len(entity.Policies) != 0 { + policies[entity.NamespaceID] = append(policies[entity.NamespaceID], entity.Policies...) + } groupPolicies, err := c.identityStore.groupPoliciesByEntityID(entity.ID) if err != nil { @@ -86,14 +91,29 @@ func (c *Core) fetchEntityAndDerivedPolicies(entityID string) (*identity.Entity, return nil, nil, err } - // Attach the policies from all the groups - policies = append(policies, groupPolicies...) + // Filter and add the policies to the resultant set + for nsID, nsPolicies := range groupPolicies { + ns, err := NamespaceByID(ctx, nsID, c) + if err != nil { + return nil, nil, err + } + if ns == nil { + return nil, nil, namespace.ErrNoNamespace + } + if tokenNS.Path != ns.Path && !ns.HasParent(tokenNS) { + continue + } + nsPolicies = strutil.RemoveDuplicates(nsPolicies, false) + if len(nsPolicies) != 0 { + policies[nsID] = append(policies[nsID], nsPolicies...) + } + } } return entity, policies, err } -func (c *Core) fetchACLTokenEntryAndEntity(req *logical.Request) (*ACL, *logical.TokenEntry, *identity.Entity, []string, error) { +func (c *Core) fetchACLTokenEntryAndEntity(ctx context.Context, req *logical.Request) (*ACL, *logical.TokenEntry, *identity.Entity, map[string][]string, error) { defer metrics.MeasureSince([]string{"core", "fetch_acl_and_token"}, time.Now()) // Ensure there is a client token @@ -111,7 +131,7 @@ func (c *Core) fetchACLTokenEntryAndEntity(req *logical.Request) (*ACL, *logical switch req.TokenEntry() { case nil: var err error - te, err = c.tokenStore.Lookup(c.activeContext, req.ClientToken) + te, err = c.tokenStore.Lookup(ctx, req.ClientToken) if err != nil { c.logger.Error("failed to lookup token", "error", err) return nil, nil, nil, nil, ErrInternalError @@ -146,15 +166,50 @@ func (c *Core) fetchACLTokenEntryAndEntity(req *logical.Request) (*ACL, *logical } } - entity, identityPolicies, err := c.fetchEntityAndDerivedPolicies(te.EntityID) + policies := make(map[string][]string) + // Add tokens policies + policies[te.NamespaceID] = append(policies[te.NamespaceID], te.Policies...) + + tokenNS, err := NamespaceByID(ctx, te.NamespaceID, c) if err != nil { + c.logger.Error("failed to fetch token namespace", "error", err) + return nil, nil, nil, nil, ErrInternalError + } + if tokenNS == nil { + c.logger.Error("failed to fetch token namespace", "error", namespace.ErrNoNamespace) return nil, nil, nil, nil, ErrInternalError } - allPolicies := append(te.Policies, identityPolicies...) + // Add identity policies from all the namespaces + entity, identityPolicies, err := c.fetchEntityAndDerivedPolicies(ctx, tokenNS, te.EntityID) + if err != nil { + return nil, nil, nil, nil, ErrInternalError + } + for nsID, nsPolicies := range identityPolicies { + policies[nsID] = append(policies[nsID], nsPolicies...) + } - // Construct the corresponding ACL object - acl, err := c.policyStore.ACL(c.activeContext, entity, allPolicies...) + // Attach token's namespace information to the context. Wrapping tokens by + // should be able to be used anywhere, so we also special case behavior. + var tokenCtx context.Context + if len(policies) == 1 && + len(policies[te.NamespaceID]) == 1 && + (policies[te.NamespaceID][0] == responseWrappingPolicyName || + policies[te.NamespaceID][0] == controlGroupPolicyName) && + (strings.HasSuffix(req.Path, "sys/wrapping/unwrap") || + strings.HasSuffix(req.Path, "sys/wrapping/lookup") || + strings.HasSuffix(req.Path, "sys/wrapping/rewrap")) { + // Use the request namespace; will find the copy of the policy for the + // local namespace + tokenCtx = ctx + } else { + // Use the token's namespace for looking up policy + tokenCtx = namespace.ContextWithNamespace(ctx, tokenNS) + } + + // Construct the corresponding ACL object. ACL construction should be + // performed on the token's namespace. + acl, err := c.policyStore.ACL(tokenCtx, entity, policies) if err != nil { if errwrap.ContainsType(err, new(TemplateError)) { return nil, nil, nil, nil, err @@ -172,14 +227,14 @@ func (c *Core) checkToken(ctx context.Context, req *logical.Request, unauth bool var acl *ACL var te *logical.TokenEntry var entity *identity.Entity - var identityPolicies []string + var identityPolicies map[string][]string var err error // Even if unauth, if a token is provided, there's little reason not to // gather as much info as possible for the audit log and to e.g. control // trace mode for EGPs. if !unauth || (unauth && req.ClientToken != "") { - acl, te, entity, identityPolicies, err = c.fetchACLTokenEntryAndEntity(req) + acl, te, entity, identityPolicies, err = c.fetchACLTokenEntryAndEntity(ctx, req) // In the unauth case we don't want to fail the command, since it's // unauth, we just have no information to attach to the request, so // ignore errors...this was best-effort anyways @@ -202,7 +257,7 @@ func (c *Core) checkToken(ctx context.Context, req *logical.Request, unauth bool } // Check if this is a root protected path - rootPath := c.router.RootPath(req.Path) + rootPath := c.router.RootPath(ctx, req.Path) if rootPath && unauth { return nil, nil, errors.New("cannot access root path in unauthenticated request") @@ -246,18 +301,19 @@ func (c *Core) checkToken(ctx context.Context, req *logical.Request, unauth bool } // Create the auth response auth := &logical.Auth{ - ClientToken: req.ClientToken, - Accessor: req.ClientTokenAccessor, - Policies: identityPolicies, - IdentityPolicies: identityPolicies, + ClientToken: req.ClientToken, + Accessor: req.ClientTokenAccessor, } if te != nil { + auth.IdentityPolicies = identityPolicies[te.NamespaceID] auth.TokenPolicies = te.Policies - auth.Policies = append(te.Policies, identityPolicies...) + auth.Policies = append(te.Policies, identityPolicies[te.NamespaceID]...) auth.Metadata = te.Meta auth.DisplayName = te.DisplayName auth.EntityID = te.EntityID + delete(identityPolicies, te.NamespaceID) + auth.ExternalNamespacePolicies = identityPolicies // Store the entity ID in the request object req.EntityID = te.EntityID } @@ -268,6 +324,7 @@ func (c *Core) checkToken(ctx context.Context, req *logical.Request, unauth bool Unauth: unauth, RootPrivsRequired: rootPath, }) + if !authResults.Allowed { retErr := authResults.Error if authResults.Error.ErrorOrNil() == nil || authResults.DeniedError { @@ -286,7 +343,7 @@ func (c *Core) HandleRequest(httpCtx context.Context, req *logical.Request) (res if c.Sealed() { return nil, consts.ErrSealed } - if c.standby { + if c.standby && !c.perfStandby { return nil, consts.ErrStandby } @@ -313,8 +370,23 @@ func (c *Core) HandleRequest(httpCtx context.Context, req *logical.Request) (res return logical.ErrorResponse("cannot write to a path ending in '/'"), nil } + err = waitForReplicationState(ctx, c, req) + if err != nil { + return nil, err + } + + ns, err := namespace.FromContext(httpCtx) + if err != nil { + return nil, errwrap.Wrapf("could not parse namespace from http context: {{err}}", err) + } + ctx = namespace.ContextWithNamespace(ctx, ns) + + if !hasNamespaces(c) && ns.Path != "" { + return nil, logical.CodedError(403, "namespaces feature not enabled") + } + var auth *logical.Auth - if c.router.LoginPath(req.Path) { + if c.router.LoginPath(ctx, req.Path) { resp, auth, err = c.handleLoginRequest(ctx, req) } else { resp, auth, err = c.handleRequest(ctx, req) @@ -385,7 +457,7 @@ func (c *Core) HandleRequest(httpCtx context.Context, req *logical.Request) (res var nonHMACReqDataKeys []string var nonHMACRespDataKeys []string - entry := c.router.MatchingMountEntry(req.Path) + entry := c.router.MatchingMountEntry(ctx, req.Path) if entry != nil { // Get and set ignored HMAC'd value. Reset those back to empty afterwards. if rawVals, ok := entry.synthesizedConfigCache.Load("audit_non_hmac_request_keys"); ok { @@ -401,27 +473,33 @@ func (c *Core) HandleRequest(httpCtx context.Context, req *logical.Request) (res } // Create an audit trail of the response - logInput := &audit.LogInput{ - Auth: auth, - Request: req, - Response: auditResp, - OuterErr: err, - NonHMACReqDataKeys: nonHMACReqDataKeys, - NonHMACRespDataKeys: nonHMACRespDataKeys, - } - if auditErr := c.auditBroker.LogResponse(ctx, logInput, c.auditedHeaders); auditErr != nil { - c.logger.Error("failed to audit response", "request_path", req.Path, "error", auditErr) - return nil, ErrInternalError + if !isControlGroupRun(req) { + logInput := &audit.LogInput{ + Auth: auth, + Request: req, + Response: auditResp, + OuterErr: err, + NonHMACReqDataKeys: nonHMACReqDataKeys, + NonHMACRespDataKeys: nonHMACRespDataKeys, + } + if auditErr := c.auditBroker.LogResponse(ctx, logInput, c.auditedHeaders); auditErr != nil { + c.logger.Error("failed to audit response", "request_path", req.Path, "error", auditErr) + return nil, ErrInternalError + } } return } +func isControlGroupRun(req *logical.Request) bool { + return req.ControlGroup != nil +} + func (c *Core) handleRequest(ctx context.Context, req *logical.Request) (retResp *logical.Response, retAuth *logical.Auth, retErr error) { defer metrics.MeasureSince([]string{"core", "handle_request"}, time.Now()) var nonHMACReqDataKeys []string - entry := c.router.MatchingMountEntry(req.Path) + entry := c.router.MatchingMountEntry(ctx, req.Path) if entry != nil { // Get and set ignored HMAC'd value. if rawVals, ok := entry.synthesizedConfigCache.Load("audit_non_hmac_request_keys"); ok { @@ -429,10 +507,17 @@ func (c *Core) handleRequest(ctx context.Context, req *logical.Request) (retResp } } + ns, err := namespace.FromContext(ctx) + if err != nil { + c.logger.Error("failed to get namespace from context", "error", err) + retErr = multierror.Append(retErr, ErrInternalError) + return + } + // Validate the token auth, te, ctErr := c.checkToken(ctx, req, false) // We run this logic first because we want to decrement the use count even in the case of an error - if te != nil { + if te != nil && !isControlGroupRun(req) { // Attempt to use the token (decrement NumUses) var err error te, err = c.tokenStore.UseToken(ctx, te) @@ -451,7 +536,8 @@ func (c *Core) handleRequest(ctx context.Context, req *logical.Request) (retResp // valid request (this is the token's final use). We pass the ID in // directly just to be safe in case something else modifies te later. defer func(id string) { - leaseID, err := c.expiration.CreateOrFetchRevocationLeaseByToken(c.activeContext, te) + nsActiveCtx := namespace.ContextWithNamespace(c.activeContext, ns) + leaseID, err := c.expiration.CreateOrFetchRevocationLeaseByToken(nsActiveCtx, te) if err == nil { err = c.expiration.LazyRevoke(ctx, leaseID) } @@ -470,7 +556,19 @@ func (c *Core) handleRequest(ctx context.Context, req *logical.Request) (retResp }(te.ID) } } + if ctErr != nil { + newCtErr, cgResp, cgAuth, cgRetErr := checkNeedsCG(ctx, c, req, auth, ctErr, nonHMACReqDataKeys) + switch { + case newCtErr != nil: + ctErr = err + case cgResp != nil || cgAuth != nil: + if cgRetErr != nil { + retErr = multierror.Append(retErr, cgRetErr) + } + return cgResp, cgAuth, retErr + } + // If it is an internal error we return that, otherwise we // return invalid request so that the status codes can be correct switch { @@ -488,14 +586,16 @@ func (c *Core) handleRequest(ctx context.Context, req *logical.Request) (retResp retErr = multierror.Append(retErr, logical.ErrInvalidRequest) } - logInput := &audit.LogInput{ - Auth: auth, - Request: req, - OuterErr: ctErr, - NonHMACReqDataKeys: nonHMACReqDataKeys, - } - if err := c.auditBroker.LogRequest(ctx, logInput, c.auditedHeaders); err != nil { - c.logger.Error("failed to audit request", "path", req.Path, "error", err) + if !isControlGroupRun(req) { + logInput := &audit.LogInput{ + Auth: auth, + Request: req, + OuterErr: ctErr, + NonHMACReqDataKeys: nonHMACReqDataKeys, + } + if err := c.auditBroker.LogRequest(ctx, logInput, c.auditedHeaders); err != nil { + c.logger.Error("failed to audit request", "path", req.Path, "error", err) + } } if errwrap.Contains(retErr, ErrInternalError.Error()) { @@ -508,19 +608,25 @@ func (c *Core) handleRequest(ctx context.Context, req *logical.Request) (retResp req.DisplayName = auth.DisplayName // Create an audit trail of the request - logInput := &audit.LogInput{ - Auth: auth, - Request: req, - NonHMACReqDataKeys: nonHMACReqDataKeys, - } - if err := c.auditBroker.LogRequest(ctx, logInput, c.auditedHeaders); err != nil { - c.logger.Error("failed to audit request", "path", req.Path, "error", err) - retErr = multierror.Append(retErr, ErrInternalError) - return nil, auth, retErr + if !isControlGroupRun(req) { + logInput := &audit.LogInput{ + Auth: auth, + Request: req, + NonHMACReqDataKeys: nonHMACReqDataKeys, + } + if err := c.auditBroker.LogRequest(ctx, logInput, c.auditedHeaders); err != nil { + c.logger.Error("failed to audit request", "path", req.Path, "error", err) + retErr = multierror.Append(retErr, ErrInternalError) + return nil, auth, retErr + } } // Route the request resp, routeErr := c.router.Route(ctx, req) + // If we're replicating and we get a read-only error from a backend, need to forward to primary + if routeErr != nil { + resp, routeErr = possiblyForward(ctx, c, req, resp, routeErr) + } if resp != nil { // If wrapping is used, use the shortest between the request and response var wrapTTL time.Duration @@ -572,7 +678,7 @@ func (c *Core) handleRequest(ctx context.Context, req *logical.Request) (retResp // for a lease as this provides a massive slowdown registerLease := true - matchingMountEntry := c.router.MatchingMountEntry(req.Path) + matchingMountEntry := c.router.MatchingMountEntry(ctx, req.Path) if matchingMountEntry == nil { c.logger.Error("unable to retrieve kv mount entry from router") retErr = multierror.Append(retErr, ErrInternalError) @@ -583,7 +689,7 @@ func (c *Core) handleRequest(ctx context.Context, req *logical.Request) (retResp case "kv", "generic": // If we are kv type, first see if we are an older passthrough // backend, and otherwise check the mount entry options. - matchingBackend := c.router.MatchingBackend(req.Path) + matchingBackend := c.router.MatchingBackend(ctx, req.Path) if matchingBackend == nil { c.logger.Error("unable to retrieve kv backend from router") retErr = multierror.Append(retErr, ErrInternalError) @@ -610,7 +716,7 @@ func (c *Core) handleRequest(ctx context.Context, req *logical.Request) (retResp } if registerLease { - sysView := c.router.MatchingSystemView(req.Path) + sysView := c.router.MatchingSystemView(ctx, req.Path) if sysView == nil { c.logger.Error("unable to look up sys view for login path", "request_path", req.Path) return nil, nil, ErrInternalError @@ -625,7 +731,13 @@ func (c *Core) handleRequest(ctx context.Context, req *logical.Request) (retResp } resp.Secret.TTL = ttl - leaseID, err := c.expiration.Register(ctx, req, resp) + registerFunc, funcGetErr := getLeaseRegisterFunc(c) + if funcGetErr != nil { + retErr = multierror.Append(retErr, funcGetErr) + return nil, auth, retErr + } + + leaseID, err := registerFunc(ctx, req, resp) if err != nil { c.logger.Error("failed to register lease", "request_path", req.Path, "error", err) retErr = multierror.Append(retErr, ErrInternalError) @@ -645,14 +757,30 @@ func (c *Core) handleRequest(ctx context.Context, req *logical.Request) (retResp return nil, auth, retErr } - _, identityPolicies, err := c.fetchEntityAndDerivedPolicies(resp.Auth.EntityID) + // Fetch the namespace to which the token belongs + tokenNS, err := NamespaceByID(ctx, te.NamespaceID, c) + if err != nil { + c.logger.Error("failed to fetch token's namespace", "error", err) + retErr = multierror.Append(retErr, err) + return nil, auth, retErr + } + if tokenNS == nil { + c.logger.Error(namespace.ErrNoNamespace.Error()) + retErr = multierror.Append(retErr, namespace.ErrNoNamespace) + return nil, auth, retErr + } + + _, identityPolicies, err := c.fetchEntityAndDerivedPolicies(ctx, tokenNS, resp.Auth.EntityID) if err != nil { c.tokenStore.revokeOrphan(ctx, te.ID) return nil, nil, ErrInternalError } resp.Auth.TokenPolicies = policyutil.SanitizePolicies(resp.Auth.Policies, policyutil.DoNotAddDefaultPolicy) - if err := c.expiration.RegisterAuth(ctx, resp.Auth.CreationPath, resp.Auth); err != nil { + if err := c.expiration.RegisterAuth(ctx, &logical.TokenEntry{ + Path: resp.Auth.CreationPath, + NamespaceID: ns.ID, + }, resp.Auth); err != nil { c.tokenStore.revokeOrphan(ctx, te.ID) c.logger.Error("failed to register token lease", "request_path", req.Path, "error", err) retErr = multierror.Append(retErr, ErrInternalError) @@ -663,8 +791,10 @@ func (c *Core) handleRequest(ctx context.Context, req *logical.Request) (retResp // have what is purely a snapshot of current identity policies, and // plugins can be confused if they are checking contents of // Auth.Policies instead of Auth.TokenPolicies - resp.Auth.IdentityPolicies = policyutil.SanitizePolicies(identityPolicies, policyutil.DoNotAddDefaultPolicy) - resp.Auth.Policies = policyutil.SanitizePolicies(append(resp.Auth.Policies, identityPolicies...), policyutil.DoNotAddDefaultPolicy) + resp.Auth.Policies = policyutil.SanitizePolicies(append(resp.Auth.Policies, identityPolicies[te.NamespaceID]...), policyutil.DoNotAddDefaultPolicy) + resp.Auth.IdentityPolicies = policyutil.SanitizePolicies(identityPolicies[te.NamespaceID], policyutil.DoNotAddDefaultPolicy) + delete(identityPolicies, te.NamespaceID) + resp.Auth.ExternalNamespacePolicies = identityPolicies } if resp != nil && @@ -690,7 +820,50 @@ func (c *Core) handleLoginRequest(ctx context.Context, req *logical.Request) (re req.Unauthenticated = true var auth *logical.Auth - // Create an audit trail of the request, auth is not available on login requests + + // Do an unauth check. This will cause EGP policies to be checked + var ctErr error + auth, _, ctErr = c.checkToken(ctx, req, true) + if ctErr != nil { + // If it is an internal error we return that, otherwise we + // return invalid request so that the status codes can be correct + var errType error + switch ctErr { + case ErrInternalError, logical.ErrPermissionDenied: + errType = ctErr + default: + errType = logical.ErrInvalidRequest + } + + var nonHMACReqDataKeys []string + entry := c.router.MatchingMountEntry(ctx, req.Path) + if entry != nil { + // Get and set ignored HMAC'd value. + if rawVals, ok := entry.synthesizedConfigCache.Load("audit_non_hmac_request_keys"); ok { + nonHMACReqDataKeys = rawVals.([]string) + } + } + + logInput := &audit.LogInput{ + Auth: auth, + Request: req, + OuterErr: ctErr, + NonHMACReqDataKeys: nonHMACReqDataKeys, + } + if err := c.auditBroker.LogRequest(ctx, logInput, c.auditedHeaders); err != nil { + c.logger.Error("core: failed to audit request", "path", req.Path, "error", err) + return nil, nil, ErrInternalError + } + + if errType != nil { + retErr = multierror.Append(retErr, errType) + } + if ctErr == ErrInternalError { + return nil, auth, retErr + } + return logical.ErrorResponse(ctErr.Error()), auth, retErr + } + // Create an audit trail of the request. Attach auth if it was returned, // e.g. if a token was provided. logInput := &audit.LogInput{ @@ -711,6 +884,10 @@ func (c *Core) handleLoginRequest(ctx context.Context, req *logical.Request) (re // Route the request resp, routeErr := c.router.Route(ctx, req) + // If we're replicating and we get a read-only error from a backend, need to forward to primary + if routeErr != nil { + resp, routeErr = possiblyForward(ctx, c, req, resp, routeErr) + } if resp != nil { // If wrapping is used, use the shortest between the request and response var wrapTTL time.Duration @@ -764,7 +941,7 @@ func (c *Core) handleLoginRequest(ctx context.Context, req *logical.Request) (re var entity *identity.Entity auth = resp.Auth - mEntry := c.router.MatchingMountEntry(req.Path) + mEntry := c.router.MatchingMountEntry(ctx, req.Path) if auth.Alias != nil && mEntry != nil && @@ -783,11 +960,13 @@ func (c *Core) handleLoginRequest(ctx context.Context, req *logical.Request) (re // Fetch the entity for the alias, or create an entity if one // doesn't exist. - entity, err = c.identityStore.CreateOrFetchEntity(auth.Alias) + entity, err = c.identityStore.CreateOrFetchEntity(ctx, auth.Alias) + if err != nil { + entity, err = possiblyForwardAliasCreation(ctx, c, err, auth, entity) + } if err != nil { return nil, nil, err } - if entity == nil { return nil, nil, fmt.Errorf("failed to create an entity for the authenticated alias") } @@ -807,14 +986,14 @@ func (c *Core) handleLoginRequest(ctx context.Context, req *logical.Request) (re } // Determine the source of the login - source := c.router.MatchingMount(req.Path) + source := c.router.MatchingMount(ctx, req.Path) source = strings.TrimPrefix(source, credentialRoutePrefix) source = strings.Replace(source, "/", "-", -1) // Prepend the source to the display name auth.DisplayName = strings.TrimSuffix(source+auth.DisplayName, "-") - sysView := c.router.MatchingSystemView(req.Path) + sysView := c.router.MatchingSystemView(ctx, req.Path) if sysView == nil { c.logger.Error("unable to look up sys view for login path", "request_path", req.Path) return nil, nil, ErrInternalError @@ -828,33 +1007,17 @@ func (c *Core) handleLoginRequest(ctx context.Context, req *logical.Request) (re resp.AddWarning(warning) } - // We first assign token policies to what was returned from the backend - // via auth.Policies. Then, we get the full set of policies into - // auth.Policies from the backend + entity information -- this is not - // stored in the token, but we perform sanity checks on it and return - // that information to the user. - - // Generate a token - te := logical.TokenEntry{ - Path: req.Path, - Meta: auth.Metadata, - DisplayName: auth.DisplayName, - CreationTime: time.Now().Unix(), - TTL: tokenTTL, - NumUses: auth.NumUses, - EntityID: auth.EntityID, - BoundCIDRs: auth.BoundCIDRs, + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, nil, err } - - te.Policies = policyutil.SanitizePolicies(auth.Policies, policyutil.AddDefaultPolicy) - - _, identityPolicies, err := c.fetchEntityAndDerivedPolicies(auth.EntityID) + _, identityPolicies, err := c.fetchEntityAndDerivedPolicies(ctx, ns, auth.EntityID) if err != nil { return nil, nil, ErrInternalError } - auth.TokenPolicies = te.Policies - allPolicies := policyutil.SanitizePolicies(append(te.Policies, identityPolicies...), policyutil.DoNotAddDefaultPolicy) + auth.TokenPolicies = policyutil.SanitizePolicies(auth.Policies, policyutil.AddDefaultPolicy) + allPolicies := policyutil.SanitizePolicies(append(auth.TokenPolicies, identityPolicies[ns.ID]...), policyutil.DoNotAddDefaultPolicy) // Prevent internal policies from being assigned to tokens. We check // this on auth.Policies including derived ones from Identity before @@ -868,29 +1031,75 @@ func (c *Core) handleLoginRequest(ctx context.Context, req *logical.Request) (re } } - if err := c.tokenStore.create(ctx, &te); err != nil { - c.logger.Error("failed to create token", "error", err) - return nil, auth, ErrInternalError + registerFunc, funcGetErr := getAuthRegisterFunc(c) + if funcGetErr != nil { + retErr = multierror.Append(retErr, funcGetErr) + return nil, auth, retErr } - // Populate the client token, accessor, and TTL - auth.ClientToken = te.ID - auth.Accessor = te.Accessor - auth.TTL = te.TTL - - // Register with the expiration manager - if err := c.expiration.RegisterAuth(ctx, te.Path, auth); err != nil { - c.tokenStore.revokeOrphan(ctx, te.ID) - c.logger.Error("failed to register token lease", "request_path", req.Path, "error", err) - return nil, auth, ErrInternalError + err = registerFunc(ctx, tokenTTL, req.Path, auth) + switch { + case err == nil: + case err == ErrInternalError: + return nil, auth, err + default: + return logical.ErrorResponse(err.Error()), auth, logical.ErrInvalidRequest } - auth.IdentityPolicies = policyutil.SanitizePolicies(identityPolicies, policyutil.DoNotAddDefaultPolicy) + auth.IdentityPolicies = policyutil.SanitizePolicies(identityPolicies[ns.ID], policyutil.DoNotAddDefaultPolicy) + delete(identityPolicies, ns.ID) + auth.ExternalNamespacePolicies = identityPolicies auth.Policies = allPolicies // Attach the display name, might be used by audit backends req.DisplayName = auth.DisplayName + } return resp, auth, routeErr } + +func (c *Core) RegisterAuth(ctx context.Context, tokenTTL time.Duration, path string, auth *logical.Auth) error { + // We first assign token policies to what was returned from the backend + // via auth.Policies. Then, we get the full set of policies into + // auth.Policies from the backend + entity information -- this is not + // stored in the token, but we perform sanity checks on it and return + // that information to the user. + + // Generate a token + ns, err := namespace.FromContext(ctx) + if err != nil { + return err + } + te := logical.TokenEntry{ + Path: path, + Meta: auth.Metadata, + DisplayName: auth.DisplayName, + CreationTime: time.Now().Unix(), + TTL: tokenTTL, + NumUses: auth.NumUses, + EntityID: auth.EntityID, + BoundCIDRs: auth.BoundCIDRs, + Policies: auth.TokenPolicies, + NamespaceID: ns.ID, + } + + if err := c.tokenStore.create(ctx, &te); err != nil { + c.logger.Error("failed to create token", "error", err) + return ErrInternalError + } + + // Populate the client token, accessor, and TTL + auth.ClientToken = te.ID + auth.Accessor = te.Accessor + auth.TTL = te.TTL + + // Register with the expiration manager + if err := c.expiration.RegisterAuth(ctx, &te, auth); err != nil { + c.tokenStore.revokeOrphan(ctx, te.ID) + c.logger.Error("failed to register token lease", "request_path", path, "error", err) + return ErrInternalError + } + + return nil +} diff --git a/vault/request_handling_test.go b/vault/request_handling_test.go index 7be38c9155..c76bf5e83f 100644 --- a/vault/request_handling_test.go +++ b/vault/request_handling_test.go @@ -1,12 +1,12 @@ package vault import ( - "context" "testing" "time" "github.com/hashicorp/go-uuid" credUserpass "github.com/hashicorp/vault/builtin/credential/userpass" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/logical" ) @@ -16,7 +16,7 @@ func TestRequestHandling_Wrapping(t *testing.T) { core.logicalBackends["kv"] = PassthroughBackendFactory meUUID, _ := uuid.GenerateUUID() - err := core.mount(context.Background(), &MountEntry{ + err := core.mount(namespace.RootContext(nil), &MountEntry{ Table: mountTableType, UUID: meUUID, Path: "wraptest", @@ -35,7 +35,7 @@ func TestRequestHandling_Wrapping(t *testing.T) { "zip": "zap", }, } - resp, err := core.HandleRequest(context.Background(), req) + resp, err := core.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -51,7 +51,7 @@ func TestRequestHandling_Wrapping(t *testing.T) { TTL: time.Duration(15 * time.Second), }, } - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -66,7 +66,7 @@ func TestRequestHandling_Wrapping(t *testing.T) { func TestRequestHandling_LoginWrapping(t *testing.T) { core, _, root := TestCoreUnsealed(t) - if err := core.loadMounts(context.Background()); err != nil { + if err := core.loadMounts(namespace.RootContext(nil)); err != nil { t.Fatalf("err: %v", err) } @@ -82,7 +82,7 @@ func TestRequestHandling_LoginWrapping(t *testing.T) { }, Connection: &logical.Connection{}, } - resp, err := core.HandleRequest(context.Background(), req) + resp, err := core.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -95,7 +95,7 @@ func TestRequestHandling_LoginWrapping(t *testing.T) { "password": "foo", "policies": "default", } - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -111,7 +111,7 @@ func TestRequestHandling_LoginWrapping(t *testing.T) { }, Connection: &logical.Connection{}, } - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } @@ -133,7 +133,7 @@ func TestRequestHandling_LoginWrapping(t *testing.T) { }, Connection: &logical.Connection{}, } - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.RootContext(nil), req) if err != nil { t.Fatalf("err: %v", err) } diff --git a/vault/request_handling_util.go b/vault/request_handling_util.go new file mode 100644 index 0000000000..ffcc419a10 --- /dev/null +++ b/vault/request_handling_util.go @@ -0,0 +1,32 @@ +// +build !enterprise + +package vault + +import ( + "context" + + "github.com/hashicorp/vault/helper/identity" + "github.com/hashicorp/vault/logical" +) + +func waitForReplicationState(context.Context, *Core, *logical.Request) error { return nil } + +func checkNeedsCG(context.Context, *Core, *logical.Request, *logical.Auth, error, []string) (error, *logical.Response, *logical.Auth, error) { + return nil, nil, nil, nil +} + +func possiblyForward(ctx context.Context, c *Core, req *logical.Request, resp *logical.Response, routeErr error) (*logical.Response, error) { + return resp, routeErr +} + +func getLeaseRegisterFunc(c *Core) (func(context.Context, *logical.Request, *logical.Response) (string, error), error) { + return c.expiration.Register, nil +} + +func getAuthRegisterFunc(c *Core) (RegisterAuthFunc, error) { + return c.RegisterAuth, nil +} + +func possiblyForwardAliasCreation(ctx context.Context, c *Core, inErr error, auth *logical.Auth, entity *identity.Entity) (*identity.Entity, error) { + return entity, inErr +} diff --git a/vault/rollback.go b/vault/rollback.go index 7d9d873113..f9b4953990 100644 --- a/vault/rollback.go +++ b/vault/rollback.go @@ -2,6 +2,7 @@ package vault import ( "context" + "errors" "strings" "sync" "time" @@ -9,6 +10,7 @@ import ( log "github.com/hashicorp/go-hclog" "github.com/armon/go-metrics" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/logical" ) @@ -123,38 +125,40 @@ func (m *RollbackManager) triggerRollbacks() { } // When the mount is filtered, the backend will be nil - backend := m.router.MatchingBackend(path) + ctx := namespace.ContextWithNamespace(m.quitContext, e.namespace) + backend := m.router.MatchingBackend(ctx, path) if backend == nil { continue } + fullPath := e.namespace.Path + path m.inflightLock.RLock() - _, ok := m.inflight[path] + _, ok := m.inflight[fullPath] m.inflightLock.RUnlock() if !ok { - m.startRollback(path, true) + m.startRollback(ctx, fullPath, true) } } } // startRollback is used to start an async rollback attempt. // This must be called with the inflightLock held. -func (m *RollbackManager) startRollback(path string, grabStatelock bool) *rollbackState { +func (m *RollbackManager) startRollback(ctx context.Context, fullPath string, grabStatelock bool) *rollbackState { rs := &rollbackState{} rs.Add(1) m.inflightAll.Add(1) m.inflightLock.Lock() - m.inflight[path] = rs + m.inflight[fullPath] = rs m.inflightLock.Unlock() - go m.attemptRollback(m.quitContext, path, rs, grabStatelock) + go m.attemptRollback(ctx, fullPath, rs, grabStatelock) return rs } // attemptRollback invokes a RollbackOperation for the given path -func (m *RollbackManager) attemptRollback(ctx context.Context, path string, rs *rollbackState, grabStatelock bool) (err error) { - defer metrics.MeasureSince([]string{"rollback", "attempt", strings.Replace(path, "/", "-", -1)}, time.Now()) +func (m *RollbackManager) attemptRollback(ctx context.Context, fullPath string, rs *rollbackState, grabStatelock bool) (err error) { + defer metrics.MeasureSince([]string{"rollback", "attempt", strings.Replace(fullPath, "/", "-", -1)}, time.Now()) if m.logger.IsDebug() { - m.logger.Debug("attempting rollback", "path", path) + m.logger.Debug("attempting rollback", "path", fullPath) } defer func() { @@ -162,20 +166,33 @@ func (m *RollbackManager) attemptRollback(ctx context.Context, path string, rs * rs.Done() m.inflightAll.Done() m.inflightLock.Lock() - delete(m.inflight, path) + delete(m.inflight, fullPath) m.inflightLock.Unlock() }() + ns, err := namespace.FromContext(ctx) + if err != nil { + return err + } + if ns == nil { + return namespace.ErrNoNamespace + } + // Invoke a RollbackOperation req := &logical.Request{ Operation: logical.RollbackOperation, - Path: path, + Path: ns.TrimmedPath(fullPath), } + + if grabStatelock { + // Grab the statelock or stop + if stopped := grabLockOrStop(m.core.stateLock.RLock, m.core.stateLock.RUnlock, m.shutdownCh); stopped { + return errors.New("rollback shutting down") + } + } + var cancelFunc context.CancelFunc ctx, cancelFunc = context.WithTimeout(ctx, DefaultMaxRequestDuration) - if grabStatelock { - m.core.stateLock.RLock() - } _, err = m.router.Route(ctx, req) if grabStatelock { m.core.stateLock.RUnlock() @@ -192,7 +209,7 @@ func (m *RollbackManager) attemptRollback(ctx context.Context, path string, rs * err = nil } if err != nil { - m.logger.Error("error rolling back", "path", path, "error", err) + m.logger.Error("error rolling back", "path", fullPath, "error", err) } return } @@ -200,13 +217,19 @@ func (m *RollbackManager) attemptRollback(ctx context.Context, path string, rs * // Rollback is used to trigger an immediate rollback of the path, // or to join an existing rollback operation if in flight. Caller should have // core's statelock held -func (m *RollbackManager) Rollback(path string) error { +func (m *RollbackManager) Rollback(ctx context.Context, path string) error { + ns, err := namespace.FromContext(ctx) + if err != nil { + return err + } + fullPath := ns.Path + path + // Check for an existing attempt and start one if none m.inflightLock.RLock() - rs, ok := m.inflight[path] + rs, ok := m.inflight[fullPath] m.inflightLock.RUnlock() if !ok { - rs = m.startRollback(path, false) + rs = m.startRollback(ctx, fullPath, false) } // Wait for the attempt to finish diff --git a/vault/rollback_test.go b/vault/rollback_test.go index 1a4b0a81c5..a5d106fed9 100644 --- a/vault/rollback_test.go +++ b/vault/rollback_test.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/go-uuid" "github.com/hashicorp/vault/helper/logging" + "github.com/hashicorp/vault/helper/namespace" ) // mockRollback returns a mock rollback manager @@ -24,14 +25,17 @@ func mockRollback(t *testing.T) (*RollbackManager, *NoopBackend) { mounts.Entries = []*MountEntry{ &MountEntry{ - Path: "foo", + Path: "foo", + NamespaceID: namespace.RootNamespaceID, + namespace: namespace.TestNamespace(), }, } meUUID, err := uuid.GenerateUUID() if err != nil { t.Fatal(err) } - if err := router.Mount(backend, "foo", &MountEntry{UUID: meUUID, Accessor: "noopaccessor"}, view); err != nil { + + if err := router.Mount(backend, "foo", &MountEntry{UUID: meUUID, Accessor: "noopaccessor", NamespaceID: namespace.RootNamespaceID, namespace: namespace.TestNamespace()}, view); err != nil { t.Fatalf("err: %s", err) } @@ -86,7 +90,7 @@ func TestRollbackManager_Join(t *testing.T) { errCh := make(chan error, 3) go func() { defer wg.Done() - err := m.Rollback("foo") + err := m.Rollback(namespace.TestContext(), "foo") if err != nil { errCh <- err } @@ -94,7 +98,7 @@ func TestRollbackManager_Join(t *testing.T) { go func() { defer wg.Done() - err := m.Rollback("foo") + err := m.Rollback(namespace.TestContext(), "foo") if err != nil { errCh <- err } @@ -102,7 +106,7 @@ func TestRollbackManager_Join(t *testing.T) { go func() { defer wg.Done() - err := m.Rollback("foo") + err := m.Rollback(namespace.TestContext(), "foo") if err != nil { errCh <- err } diff --git a/vault/router.go b/vault/router.go index cced9b579f..69b9561bcc 100644 --- a/vault/router.go +++ b/vault/router.go @@ -10,6 +10,7 @@ import ( "github.com/armon/go-metrics" "github.com/armon/go-radix" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/helper/salt" "github.com/hashicorp/vault/logical" ) @@ -93,6 +94,9 @@ func (r *Router) Mount(backend logical.Backend, prefix string, mountEntry *Mount r.l.Lock() defer r.l.Unlock() + // prepend namespace + prefix = mountEntry.Namespace().Path + prefix + // Check if this is a nested mount if existing, _, ok := r.root.LongestPrefix(prefix); ok && existing != "" { return fmt.Errorf("cannot mount under existing mount %q", existing) @@ -139,6 +143,12 @@ func (r *Router) Mount(backend logical.Backend, prefix string, mountEntry *Mount // Unmount is used to remove a logical backend from a given prefix func (r *Router) Unmount(ctx context.Context, prefix string) error { + ns, err := namespace.FromContext(ctx) + if err != nil { + return err + } + prefix = ns.Path + prefix + r.l.Lock() defer r.l.Unlock() @@ -164,7 +174,14 @@ func (r *Router) Unmount(ctx context.Context, prefix string) error { } // Remount is used to change the mount location of a logical backend -func (r *Router) Remount(src, dst string) error { +func (r *Router) Remount(ctx context.Context, src, dst string) error { + ns, err := namespace.FromContext(ctx) + if err != nil { + return err + } + src = ns.Path + src + dst = ns.Path + dst + r.l.Lock() defer r.l.Unlock() @@ -182,7 +199,13 @@ func (r *Router) Remount(src, dst string) error { // Taint is used to mark a path as tainted. This means only RollbackOperation // RevokeOperation requests are allowed to proceed -func (r *Router) Taint(path string) error { +func (r *Router) Taint(ctx context.Context, path string) error { + ns, err := namespace.FromContext(ctx) + if err != nil { + return err + } + path = ns.Path + path + r.l.Lock() defer r.l.Unlock() _, raw, ok := r.root.LongestPrefix(path) @@ -193,7 +216,13 @@ func (r *Router) Taint(path string) error { } // Untaint is used to unmark a path as tainted. -func (r *Router) Untaint(path string) error { +func (r *Router) Untaint(ctx context.Context, path string) error { + ns, err := namespace.FromContext(ctx) + if err != nil { + return err + } + path = ns.Path + path + r.l.Lock() defer r.l.Unlock() _, raw, ok := r.root.LongestPrefix(path) @@ -239,14 +268,20 @@ func (r *Router) MatchingMountByAccessor(mountAccessor string) *MountEntry { } // MatchingMount returns the mount prefix that would be used for a path -func (r *Router) MatchingMount(path string) string { +func (r *Router) MatchingMount(ctx context.Context, path string) string { r.l.RLock() - mount := r.matchingMountInternal(path) + mount := r.matchingMountInternal(ctx, path) r.l.RUnlock() return mount } -func (r *Router) matchingMountInternal(path string) string { +func (r *Router) matchingMountInternal(ctx context.Context, path string) string { + ns, err := namespace.FromContext(ctx) + if err != nil { + return "" + } + path = ns.Path + path + mount, _, ok := r.root.LongestPrefix(path) if !ok { return "" @@ -255,11 +290,17 @@ func (r *Router) matchingMountInternal(path string) string { } // matchingPrefixInternal returns a mount prefix that a path may be a part of -func (r *Router) matchingPrefixInternal(path string) string { - var existing string = "" - fn := func(existing_path string, _v interface{}) bool { - if strings.HasPrefix(existing_path, path) { - existing = existing_path +func (r *Router) matchingPrefixInternal(ctx context.Context, path string) string { + ns, err := namespace.FromContext(ctx) + if err != nil { + return "" + } + path = ns.Path + path + + var existing string + fn := func(existingPath string, v interface{}) bool { + if strings.HasPrefix(existingPath, path) { + existing = existingPath return true } return false @@ -269,27 +310,33 @@ func (r *Router) matchingPrefixInternal(path string) string { } // MountConflict determines if there are potential path conflicts -func (r *Router) MountConflict(path string) string { +func (r *Router) MountConflict(ctx context.Context, path string) string { r.l.RLock() defer r.l.RUnlock() - if exact_match := r.matchingMountInternal(path); exact_match != "" { - return exact_match + if exactMatch := r.matchingMountInternal(ctx, path); exactMatch != "" { + return exactMatch } - if prefix_match := r.matchingPrefixInternal(path); prefix_match != "" { - return prefix_match + if prefixMatch := r.matchingPrefixInternal(ctx, path); prefixMatch != "" { + return prefixMatch } return "" } // MatchingStorageByAPIPath/StoragePath returns the storage used for // API/Storage paths respectively -func (r *Router) MatchingStorageByAPIPath(path string) logical.Storage { - return r.matchingStorage(path, true) +func (r *Router) MatchingStorageByAPIPath(ctx context.Context, path string) logical.Storage { + return r.matchingStorage(ctx, path, true) } -func (r *Router) MatchingStorageByStoragePath(path string) logical.Storage { - return r.matchingStorage(path, false) +func (r *Router) MatchingStorageByStoragePath(ctx context.Context, path string) logical.Storage { + return r.matchingStorage(ctx, path, false) } -func (r *Router) matchingStorage(path string, apiPath bool) logical.Storage { +func (r *Router) matchingStorage(ctx context.Context, path string, apiPath bool) logical.Storage { + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil + } + path = ns.Path + path + var raw interface{} var ok bool r.l.RLock() @@ -306,7 +353,13 @@ func (r *Router) matchingStorage(path string, apiPath bool) logical.Storage { } // MatchingMountEntry returns the MountEntry used for a path -func (r *Router) MatchingMountEntry(path string) *MountEntry { +func (r *Router) MatchingMountEntry(ctx context.Context, path string) *MountEntry { + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil + } + path = ns.Path + path + r.l.RLock() _, raw, ok := r.root.LongestPrefix(path) r.l.RUnlock() @@ -317,7 +370,13 @@ func (r *Router) MatchingMountEntry(path string) *MountEntry { } // MatchingBackend returns the backend used for a path -func (r *Router) MatchingBackend(path string) logical.Backend { +func (r *Router) MatchingBackend(ctx context.Context, path string) logical.Backend { + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil + } + path = ns.Path + path + r.l.RLock() _, raw, ok := r.root.LongestPrefix(path) r.l.RUnlock() @@ -328,7 +387,13 @@ func (r *Router) MatchingBackend(path string) logical.Backend { } // MatchingSystemView returns the SystemView used for a path -func (r *Router) MatchingSystemView(path string) logical.SystemView { +func (r *Router) MatchingSystemView(ctx context.Context, path string) logical.SystemView { + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil + } + path = ns.Path + path + r.l.RLock() _, raw, ok := r.root.LongestPrefix(path) r.l.RUnlock() @@ -338,15 +403,35 @@ func (r *Router) MatchingSystemView(path string) logical.SystemView { return raw.(*routeEntry).backend.System() } -// MatchingStoragePrefixByAPIPath/StoragePath returns the mount path matching -// and storage prefix matching the given API/Storage path respectively -func (r *Router) MatchingStoragePrefixByAPIPath(path string) (string, string, bool) { - return r.matchingStoragePrefix(path, true) +// MatchingStoragePrefixByAPIPath the storage prefix for the given api path +func (r *Router) MatchingStoragePrefixByAPIPath(ctx context.Context, path string) (string, bool) { + ns, err := namespace.FromContext(ctx) + if err != nil { + return "", false + } + path = ns.Path + path + + _, prefix, found := r.matchingMountEntryByPath(ctx, path, true) + return prefix, found } -func (r *Router) MatchingStoragePrefixByStoragePath(path string) (string, string, bool) { - return r.matchingStoragePrefix(path, false) + +// MatchingAPIPrefixByStoragePath the api path information for the given storage path +func (r *Router) MatchingAPIPrefixByStoragePath(ctx context.Context, path string) (*namespace.Namespace, string, string, bool) { + me, prefix, found := r.matchingMountEntryByPath(ctx, path, false) + if !found { + return nil, "", "", found + } + + mountPath := me.Path + // Add back the prefix for credential backends + if strings.HasPrefix(path, credentialBarrierPrefix) { + mountPath = credentialRoutePrefix + mountPath + } + + return me.Namespace(), mountPath, prefix, found } -func (r *Router) matchingStoragePrefix(path string, apiPath bool) (string, string, bool) { + +func (r *Router) matchingMountEntryByPath(ctx context.Context, path string, apiPath bool) (*MountEntry, string, bool) { var raw interface{} var ok bool r.l.RLock() @@ -357,20 +442,14 @@ func (r *Router) matchingStoragePrefix(path string, apiPath bool) (string, strin } r.l.RUnlock() if !ok { - return "", "", false + return nil, "", false } // Extract the mount path and storage prefix re := raw.(*routeEntry) - mountPath := re.mountEntry.Path prefix := re.storagePrefix - // Add back the prefix for credential backends - if !apiPath && strings.HasPrefix(path, credentialBarrierPrefix) { - mountPath = credentialRoutePrefix + mountPath - } - - return mountPath, prefix, true + return re.mountEntry, prefix, true } // Route is used to route a given request @@ -379,22 +458,27 @@ func (r *Router) Route(ctx context.Context, req *logical.Request) (*logical.Resp return resp, err } -// Route is used to route a given existence check request +// RouteExistenceCheck is used to route a given existence check request func (r *Router) RouteExistenceCheck(ctx context.Context, req *logical.Request) (bool, bool, error) { _, ok, exists, err := r.routeCommon(ctx, req, true) return ok, exists, err } func (r *Router) routeCommon(ctx context.Context, req *logical.Request, existenceCheck bool) (*logical.Response, bool, bool, error) { + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, false, false, err + } + // Find the mount point r.l.RLock() adjustedPath := req.Path - mount, raw, ok := r.root.LongestPrefix(adjustedPath) + mount, raw, ok := r.root.LongestPrefix(ns.Path + adjustedPath) if !ok && !strings.HasSuffix(adjustedPath, "/") { // Re-check for a backend by appending a slash. This lets "foo" mean // "foo/" at the root level which is almost always what we want. adjustedPath += "/" - mount, raw, ok = r.root.LongestPrefix(adjustedPath) + mount, raw, ok = r.root.LongestPrefix(ns.Path + adjustedPath) } r.l.RUnlock() if !ok { @@ -427,13 +511,15 @@ func (r *Router) routeCommon(ctx context.Context, req *logical.Request, existenc // Adjust the path to exclude the routing prefix originalPath := req.Path - req.Path = strings.TrimPrefix(req.Path, mount) + req.Path = strings.TrimPrefix(ns.Path+req.Path, mount) req.MountPoint = mount req.MountType = re.mountEntry.Type if req.Path == "/" { req.Path = "" } + originalEntReq := req.EntReq() + // Attach the storage view for the request req.Storage = re.storageView @@ -445,14 +531,34 @@ func (r *Router) routeCommon(ctx context.Context, req *logical.Request, existenc switch { case strings.HasPrefix(originalPath, "auth/token/"): case strings.HasPrefix(originalPath, "sys/"): - case strings.HasPrefix(originalPath, "cubbyhole/"): - // In order for the token store to revoke later, we need to have the same - // salted ID, so we double-salt what's going to the cubbyhole backend - salt, err := r.tokenStoreSaltFunc(ctx) - if err != nil { - return nil, false, false, err + case strings.HasPrefix(originalPath, cubbyholeMountPath): + if req.Operation == logical.RollbackOperation { + // Backend doesn't support this and it can't properly look up a + // cubbyhole ID so just return here + return nil, false, false, nil } - req.ClientToken = re.SaltID(salt.SaltID(req.ClientToken)) + + if req.TokenEntry() == nil { + return nil, false, false, fmt.Errorf("nil token entry") + } + + switch req.TokenEntry().NamespaceID { + case namespace.RootNamespaceID: + // In order for the token store to revoke later, we need to have the same + // salted ID, so we double-salt what's going to the cubbyhole backend + salt, err := r.tokenStoreSaltFunc(ctx) + if err != nil { + return nil, false, false, err + } + req.ClientToken = re.SaltID(salt.SaltID(req.ClientToken)) + + default: + if req.TokenEntry().CubbyholeID == "" { + return nil, false, false, fmt.Errorf("empty cubbyhole id") + } + req.ClientToken = req.TokenEntry().CubbyholeID + } + default: req.ClientToken = re.SaltID(req.ClientToken) } @@ -467,6 +573,9 @@ func (r *Router) routeCommon(ctx context.Context, req *logical.Request, existenc originalClientTokenRemainingUses := req.ClientTokenRemainingUses req.ClientTokenRemainingUses = 0 + origMFACreds := req.MFACreds + req.MFACreds = nil + // Cache the headers headers := req.Headers @@ -487,6 +596,7 @@ func (r *Router) routeCommon(ctx context.Context, req *logical.Request, existenc } } + originalPolicyOverride := req.PolicyOverride reqTokenEntry := req.TokenEntry() req.SetTokenEntry(nil) @@ -502,6 +612,7 @@ func (r *Router) routeCommon(ctx context.Context, req *logical.Request, existenc req.ClientTokenRemainingUses = originalClientTokenRemainingUses req.WrapInfo = wrapInfo req.Headers = headers + req.PolicyOverride = originalPolicyOverride // This is only set in one place, after routing, so should never be set // by a backend req.SetLastRemoteWAL(0) @@ -512,7 +623,10 @@ func (r *Router) routeCommon(ctx context.Context, req *logical.Request, existenc req.EntityID = originalEntityID + req.MFACreds = origMFACreds + req.SetTokenEntry(reqTokenEntry) + req.SetEntReq(originalEntReq) }() // Invoke the backend @@ -542,14 +656,22 @@ func (r *Router) routeCommon(ctx context.Context, req *logical.Request, existenc alias.MountAccessor = re.mountEntry.Accessor } } + return resp, false, false, err } } // RootPath checks if the given path requires root privileges -func (r *Router) RootPath(path string) bool { +func (r *Router) RootPath(ctx context.Context, path string) bool { + ns, err := namespace.FromContext(ctx) + if err != nil { + return false + } + + adjustedPath := ns.Path + path + r.l.RLock() - mount, raw, ok := r.root.LongestPrefix(path) + mount, raw, ok := r.root.LongestPrefix(adjustedPath) r.l.RUnlock() if !ok { return false @@ -557,7 +679,7 @@ func (r *Router) RootPath(path string) bool { re := raw.(*routeEntry) // Trim to get remaining path - remain := strings.TrimPrefix(path, mount) + remain := strings.TrimPrefix(adjustedPath, mount) // Check the rootPaths of this backend rootPaths := re.rootPaths.Load().(*radix.Tree) @@ -577,9 +699,16 @@ func (r *Router) RootPath(path string) bool { } // LoginPath checks if the given path is used for logins -func (r *Router) LoginPath(path string) bool { +func (r *Router) LoginPath(ctx context.Context, path string) bool { + ns, err := namespace.FromContext(ctx) + if err != nil { + return false + } + + adjustedPath := ns.Path + path + r.l.RLock() - mount, raw, ok := r.root.LongestPrefix(path) + mount, raw, ok := r.root.LongestPrefix(adjustedPath) r.l.RUnlock() if !ok { return false @@ -587,7 +716,7 @@ func (r *Router) LoginPath(path string) bool { re := raw.(*routeEntry) // Trim to get remaining path - remain := strings.TrimPrefix(path, mount) + remain := strings.TrimPrefix(adjustedPath, mount) // Check the loginPaths of this backend loginPaths := re.loginPaths.Load().(*radix.Tree) diff --git a/vault/router_access.go b/vault/router_access.go index fc6790ff6e..90335d7abc 100644 --- a/vault/router_access.go +++ b/vault/router_access.go @@ -1,5 +1,7 @@ package vault +import "context" + // RouterAccess provides access into some things necessary for testing type RouterAccess struct { c *Core @@ -9,6 +11,6 @@ func NewRouterAccess(c *Core) *RouterAccess { return &RouterAccess{c: c} } -func (r *RouterAccess) StoragePrefixByAPIPath(path string) (string, string, bool) { - return r.c.router.MatchingStoragePrefixByAPIPath(path) +func (r *RouterAccess) StoragePrefixByAPIPath(ctx context.Context, path string) (string, bool) { + return r.c.router.MatchingStoragePrefixByAPIPath(ctx, path) } diff --git a/vault/router_test.go b/vault/router_test.go index b992419f70..e72455220d 100644 --- a/vault/router_test.go +++ b/vault/router_test.go @@ -11,6 +11,7 @@ import ( log "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-uuid" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/logical" ) @@ -117,9 +118,11 @@ func TestRouter_Mount(t *testing.T) { } mountEntry := &MountEntry{ - Path: "prod/aws/", - UUID: meUUID, - Accessor: "awsaccessor", + Path: "prod/aws/", + UUID: meUUID, + Accessor: "awsaccessor", + NamespaceID: namespace.RootNamespaceID, + namespace: namespace.TestNamespace(), } n := &NoopBackend{} @@ -133,7 +136,7 @@ func TestRouter_Mount(t *testing.T) { t.Fatal(err) } - err = r.Mount(n, "prod/aws/", &MountEntry{UUID: meUUID}, view) + err = r.Mount(n, "prod/aws/", &MountEntry{UUID: meUUID, NamespaceID: namespace.RootNamespaceID, namespace: namespace.TestNamespace()}, view) if !strings.Contains(err.Error(), "cannot mount under existing mount") { t.Fatalf("err: %v", err) } @@ -143,19 +146,19 @@ func TestRouter_Mount(t *testing.T) { t.Fatal(err) } - if path := r.MatchingMount("prod/aws/foo"); path != "prod/aws/" { + if path := r.MatchingMount(namespace.TestContext(), "prod/aws/foo"); path != "prod/aws/" { t.Fatalf("bad: %s", path) } - if v := r.MatchingStorageByAPIPath("prod/aws/foo"); v.(*BarrierView) != view { + if v := r.MatchingStorageByAPIPath(namespace.TestContext(), "prod/aws/foo"); v.(*BarrierView) != view { t.Fatalf("bad: %v", v) } - if path := r.MatchingMount("stage/aws/foo"); path != "" { + if path := r.MatchingMount(namespace.TestContext(), "stage/aws/foo"); path != "" { t.Fatalf("bad: %s", path) } - if v := r.MatchingStorageByAPIPath("stage/aws/foo"); v != nil { + if v := r.MatchingStorageByAPIPath(namespace.TestContext(), "stage/aws/foo"); v != nil { t.Fatalf("bad: %v", v) } @@ -164,7 +167,7 @@ func TestRouter_Mount(t *testing.T) { t.Fatalf("failed to fetch mount entry using its ID; expected: %#v\n actual: %#v\n", mountEntry, mountEntryFetched) } - mount, prefix, ok := r.MatchingStoragePrefixByStoragePath("logical/foo") + _, mount, prefix, ok := r.MatchingAPIPrefixByStoragePath(namespace.TestContext(), "logical/foo") if !ok { t.Fatalf("missing storage prefix") } @@ -178,7 +181,7 @@ func TestRouter_Mount(t *testing.T) { req.SetTokenEntry(&logical.TokenEntry{ ID: "foo", }) - resp, err := r.Route(context.Background(), req) + resp, err := r.Route(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -195,12 +198,14 @@ func TestRouter_Mount(t *testing.T) { } subMountEntry := &MountEntry{ - Path: "prod/", - UUID: meUUID, - Accessor: "prodaccessor", + Path: "prod/", + UUID: meUUID, + Accessor: "prodaccessor", + NamespaceID: namespace.RootNamespaceID, + namespace: namespace.TestNamespace(), } - if r.MountConflict("prod/aws/") == "" { + if r.MountConflict(namespace.TestContext(), "prod/aws/") == "" { t.Fatalf("bad: prod/aws/") } @@ -209,7 +214,7 @@ func TestRouter_Mount(t *testing.T) { if err != nil { t.Fatalf("err: %v", err) } - if r.MountConflict("prod/test") == "" { + if r.MountConflict(namespace.TestContext(), "prod/test") == "" { t.Fatalf("bad: prod/test/") } } @@ -225,9 +230,11 @@ func TestRouter_MountCredential(t *testing.T) { } mountEntry := &MountEntry{ - Path: "aws", - UUID: meUUID, - Accessor: "awsaccessor", + Path: "aws", + UUID: meUUID, + Accessor: "awsaccessor", + NamespaceID: namespace.RootNamespaceID, + namespace: namespace.TestNamespace(), } n := &NoopBackend{} @@ -241,24 +248,24 @@ func TestRouter_MountCredential(t *testing.T) { t.Fatal(err) } - err = r.Mount(n, "auth/aws/", &MountEntry{UUID: meUUID}, view) + err = r.Mount(n, "auth/aws/", &MountEntry{UUID: meUUID, NamespaceID: namespace.RootNamespaceID, namespace: namespace.TestNamespace()}, view) if !strings.Contains(err.Error(), "cannot mount under existing mount") { t.Fatalf("err: %v", err) } - if path := r.MatchingMount("auth/aws/foo"); path != "auth/aws/" { + if path := r.MatchingMount(namespace.TestContext(), "auth/aws/foo"); path != "auth/aws/" { t.Fatalf("bad: %s", path) } - if v := r.MatchingStorageByAPIPath("auth/aws/foo"); v.(*BarrierView) != view { + if v := r.MatchingStorageByAPIPath(namespace.TestContext(), "auth/aws/foo"); v.(*BarrierView) != view { t.Fatalf("bad: %v", v) } - if path := r.MatchingMount("auth/stage/aws/foo"); path != "" { + if path := r.MatchingMount(namespace.TestContext(), "auth/stage/aws/foo"); path != "" { t.Fatalf("bad: %s", path) } - if v := r.MatchingStorageByAPIPath("auth/stage/aws/foo"); v != nil { + if v := r.MatchingStorageByAPIPath(namespace.TestContext(), "auth/stage/aws/foo"); v != nil { t.Fatalf("bad: %v", v) } @@ -267,7 +274,7 @@ func TestRouter_MountCredential(t *testing.T) { t.Fatalf("failed to fetch mount entry using its ID; expected: %#v\n actual: %#v\n", mountEntry, mountEntryFetched) } - mount, prefix, ok := r.MatchingStoragePrefixByStoragePath("auth/foo") + _, mount, prefix, ok := r.MatchingAPIPrefixByStoragePath(namespace.TestContext(), "auth/foo") if !ok { t.Fatalf("missing storage prefix") } @@ -278,7 +285,7 @@ func TestRouter_MountCredential(t *testing.T) { req := &logical.Request{ Path: "auth/aws/foo", } - resp, err := r.Route(context.Background(), req) + resp, err := r.Route(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -302,12 +309,12 @@ func TestRouter_Unmount(t *testing.T) { t.Fatal(err) } n := &NoopBackend{} - err = r.Mount(n, "prod/aws/", &MountEntry{Path: "prod/aws/", UUID: meUUID, Accessor: "awsaccessor"}, view) + err = r.Mount(n, "prod/aws/", &MountEntry{Path: "prod/aws/", UUID: meUUID, Accessor: "awsaccessor", NamespaceID: namespace.RootNamespaceID, namespace: namespace.TestNamespace()}, view) if err != nil { t.Fatalf("err: %v", err) } - err = r.Unmount(context.Background(), "prod/aws/") + err = r.Unmount(namespace.TestContext(), "prod/aws/") if err != nil { t.Fatalf("err: %v", err) } @@ -315,12 +322,12 @@ func TestRouter_Unmount(t *testing.T) { req := &logical.Request{ Path: "prod/aws/foo", } - _, err = r.Route(context.Background(), req) + _, err = r.Route(namespace.TestContext(), req) if !strings.Contains(err.Error(), "unsupported path") { t.Fatalf("err: %v", err) } - if _, _, ok := r.MatchingStoragePrefixByStoragePath("logical/foo"); ok { + if _, _, _, ok := r.MatchingAPIPrefixByStoragePath(namespace.TestContext(), "logical/foo"); ok { t.Fatalf("should not have matching storage prefix") } } @@ -335,19 +342,19 @@ func TestRouter_Remount(t *testing.T) { t.Fatal(err) } n := &NoopBackend{} - me := &MountEntry{Path: "prod/aws/", UUID: meUUID, Accessor: "awsaccessor"} + me := &MountEntry{Path: "prod/aws/", UUID: meUUID, Accessor: "awsaccessor", NamespaceID: namespace.RootNamespaceID, namespace: namespace.TestNamespace()} err = r.Mount(n, "prod/aws/", me, view) if err != nil { t.Fatalf("err: %v", err) } me.Path = "stage/aws/" - err = r.Remount("prod/aws/", "stage/aws/") + err = r.Remount(namespace.TestContext(), "prod/aws/", "stage/aws/") if err != nil { t.Fatalf("err: %v", err) } - err = r.Remount("prod/aws/", "stage/aws/") + err = r.Remount(namespace.TestContext(), "prod/aws/", "stage/aws/") if !strings.Contains(err.Error(), "no mount at") { t.Fatalf("err: %v", err) } @@ -355,7 +362,7 @@ func TestRouter_Remount(t *testing.T) { req := &logical.Request{ Path: "prod/aws/foo", } - _, err = r.Route(context.Background(), req) + _, err = r.Route(namespace.TestContext(), req) if !strings.Contains(err.Error(), "unsupported path") { t.Fatalf("err: %v", err) } @@ -363,7 +370,7 @@ func TestRouter_Remount(t *testing.T) { req = &logical.Request{ Path: "stage/aws/foo", } - _, err = r.Route(context.Background(), req) + _, err = r.Route(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -374,7 +381,7 @@ func TestRouter_Remount(t *testing.T) { } // Check the resolve from storage still works - mount, prefix, _ := r.MatchingStoragePrefixByStoragePath("logical/foobar") + _, mount, prefix, _ := r.MatchingAPIPrefixByStoragePath(namespace.TestContext(), "logical/foobar") if mount != "stage/aws/" { t.Fatalf("bad mount: %s", mount) } @@ -398,7 +405,7 @@ func TestRouter_RootPath(t *testing.T) { "policy/*", }, } - err = r.Mount(n, "prod/aws/", &MountEntry{UUID: meUUID, Accessor: "awsaccessor"}, view) + err = r.Mount(n, "prod/aws/", &MountEntry{UUID: meUUID, Accessor: "awsaccessor", NamespaceID: namespace.RootNamespaceID, namespace: namespace.TestNamespace()}, view) if err != nil { t.Fatalf("err: %v", err) } @@ -418,7 +425,7 @@ func TestRouter_RootPath(t *testing.T) { } for _, tc := range tcases { - out := r.RootPath(tc.path) + out := r.RootPath(namespace.TestContext(), tc.path) if out != tc.expect { t.Fatalf("bad: path: %s expect: %v got %v", tc.path, tc.expect, out) } @@ -440,7 +447,7 @@ func TestRouter_LoginPath(t *testing.T) { "oauth/*", }, } - err = r.Mount(n, "auth/foo/", &MountEntry{UUID: meUUID, Accessor: "authfooaccessor"}, view) + err = r.Mount(n, "auth/foo/", &MountEntry{UUID: meUUID, Accessor: "authfooaccessor", NamespaceID: namespace.RootNamespaceID, namespace: namespace.TestNamespace()}, view) if err != nil { t.Fatalf("err: %v", err) } @@ -458,7 +465,7 @@ func TestRouter_LoginPath(t *testing.T) { } for _, tc := range tcases { - out := r.LoginPath(tc.path) + out := r.LoginPath(namespace.TestContext(), tc.path) if out != tc.expect { t.Fatalf("bad: path: %s expect: %v got %v", tc.path, tc.expect, out) } @@ -475,12 +482,12 @@ func TestRouter_Taint(t *testing.T) { t.Fatal(err) } n := &NoopBackend{} - err = r.Mount(n, "prod/aws/", &MountEntry{UUID: meUUID, Accessor: "awsaccessor"}, view) + err = r.Mount(n, "prod/aws/", &MountEntry{UUID: meUUID, Accessor: "awsaccessor", NamespaceID: namespace.RootNamespaceID, namespace: namespace.TestNamespace()}, view) if err != nil { t.Fatalf("err: %v", err) } - err = r.Taint("prod/aws/") + err = r.Taint(namespace.TestContext(), "prod/aws/") if err != nil { t.Fatalf("err: %v", err) } @@ -489,20 +496,20 @@ func TestRouter_Taint(t *testing.T) { Operation: logical.ReadOperation, Path: "prod/aws/foo", } - _, err = r.Route(context.Background(), req) + _, err = r.Route(namespace.TestContext(), req) if err.Error() != "unsupported path" { t.Fatalf("err: %v", err) } // Rollback and Revoke should work req.Operation = logical.RollbackOperation - _, err = r.Route(context.Background(), req) + _, err = r.Route(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } req.Operation = logical.RevokeOperation - _, err = r.Route(context.Background(), req) + _, err = r.Route(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -518,17 +525,17 @@ func TestRouter_Untaint(t *testing.T) { t.Fatal(err) } n := &NoopBackend{} - err = r.Mount(n, "prod/aws/", &MountEntry{UUID: meUUID, Accessor: "awsaccessor"}, view) + err = r.Mount(n, "prod/aws/", &MountEntry{UUID: meUUID, Accessor: "awsaccessor", NamespaceID: namespace.RootNamespaceID, namespace: namespace.TestNamespace()}, view) if err != nil { t.Fatalf("err: %v", err) } - err = r.Taint("prod/aws/") + err = r.Taint(namespace.TestContext(), "prod/aws/") if err != nil { t.Fatalf("err: %v", err) } - err = r.Untaint("prod/aws/") + err = r.Untaint(namespace.TestContext(), "prod/aws/") if err != nil { t.Fatalf("err: %v", err) } @@ -537,7 +544,7 @@ func TestRouter_Untaint(t *testing.T) { Operation: logical.ReadOperation, Path: "prod/aws/foo", } - _, err = r.Route(context.Background(), req) + _, err = r.Route(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } diff --git a/vault/seal.go b/vault/seal.go index 363a47daf4..391cf88dce 100644 --- a/vault/seal.go +++ b/vault/seal.go @@ -12,6 +12,7 @@ import ( "github.com/hashicorp/errwrap" "github.com/hashicorp/vault/helper/jsonutil" "github.com/hashicorp/vault/physical" + "github.com/hashicorp/vault/vault/seal" "github.com/keybase/go-crypto/openpgp" "github.com/keybase/go-crypto/openpgp/packet" @@ -45,27 +46,10 @@ const ( ) const ( - SealTypeShamir = "shamir" - SealTypePKCS11 = "pkcs11" - SealTypeAWSKMS = "awskms" - SealTypeTest = "test-auto" - RecoveryTypeUnsupported = "unsupported" RecoveryTypeShamir = "shamir" ) -type KeyNotFoundError struct { - Err error -} - -func (e *KeyNotFoundError) WrappedErrors() []error { - return []error{e.Err} -} - -func (e *KeyNotFoundError) Error() string { - return e.Err.Error() -} - type Seal interface { SetCore(*Core) Init(context.Context) error @@ -121,7 +105,7 @@ func (d *defaultSeal) Finalize(ctx context.Context) error { } func (d *defaultSeal) BarrierType() string { - return SealTypeShamir + return seal.Shamir } func (d *defaultSeal) StoredKeysSupported() bool { diff --git a/vault/seal/seal.go b/vault/seal/seal.go new file mode 100644 index 0000000000..386cc6f771 --- /dev/null +++ b/vault/seal/seal.go @@ -0,0 +1,10 @@ +package seal + +const ( + Shamir = "shamir" + PKCS11 = "pkcs11" + AWSKMS = "awskms" + GCPCKMS = "gcpckms" + AzureKeyVault = "azurekeyvault" + Test = "test-auto" +) diff --git a/vault/seal_testing.go b/vault/seal_testing.go index 4f6b1d1258..d97281b36f 100644 --- a/vault/seal_testing.go +++ b/vault/seal_testing.go @@ -16,12 +16,16 @@ type TestSealOpts struct { RecoveryKeysDisabled bool } -func NewTestSeal(t testing.T, opts *TestSealOpts) Seal { - return NewDefaultSeal() -} - func testCoreUnsealedWithConfigs(t testing.T, barrierConf, recoveryConf *SealConfig) (*Core, [][]byte, [][]byte, string) { - seal := NewTestSeal(t, nil) + t.Helper() + var opts *TestSealOpts + if recoveryConf == nil { + opts = &TestSealOpts{ + StoredKeysDisabled: true, + RecoveryKeysDisabled: true, + } + } + seal := NewTestSeal(t, opts) core := TestCoreWithSeal(t, seal, false) result, err := core.Initialize(context.Background(), &InitParams{ BarrierConfig: barrierConf, @@ -66,14 +70,20 @@ func TestCoreUnsealedWithConfigSealOpts(t testing.T, barrierConf, recoveryConf * if err != nil { t.Fatalf("err: %s", err) } - for _, key := range result.SecretShares { - if _, err := core.Unseal(TestKeyCopy(key)); err != nil { - t.Fatalf("unseal err: %s", err) - } + err = core.UnsealWithStoredKeys(context.Background()) + if err != nil { + t.Fatalf("err: %s", err) } - if core.Sealed() { - t.Fatal("should not be sealed") + for _, key := range result.SecretShares { + if _, err := core.Unseal(TestKeyCopy(key)); err != nil { + t.Fatalf("unseal err: %s", err) + } + } + + if core.Sealed() { + t.Fatal("should not be sealed") + } } return core, result.SecretShares, result.RecoveryShares, result.RootToken diff --git a/vault/seal_testing_util.go b/vault/seal_testing_util.go new file mode 100644 index 0000000000..76568fad4d --- /dev/null +++ b/vault/seal_testing_util.go @@ -0,0 +1,9 @@ +// +build !enterprise + +package vault + +import "github.com/mitchellh/go-testing-interface" + +func NewTestSeal(testing.T, *TestSealOpts) Seal { + return NewDefaultSeal() +} diff --git a/vault/sealunwrapper.go b/vault/sealunwrapper.go index a7e6fc222b..5a96c4412f 100644 --- a/vault/sealunwrapper.go +++ b/vault/sealunwrapper.go @@ -1,7 +1,4 @@ -// +build !ent -// +build !prem -// +build !pro -// +build !hsm +// +build !enterprise package vault diff --git a/vault/sealunwrapper_test.go b/vault/sealunwrapper_test.go index 60857e64de..9eac41ba77 100644 --- a/vault/sealunwrapper_test.go +++ b/vault/sealunwrapper_test.go @@ -1,7 +1,4 @@ -// +build !ent -// +build !prem -// +build !pro -// +build !hsm +// +build !enterprise package vault @@ -85,7 +82,7 @@ func performTestSealUnwrapper(t *testing.T, phys physical.Backend, logger log.Lo t.Fatal(err) } if !bytes.Equal(entry.Value, origBytes) { - t.Fatalf("mismatched original bytes and unwrapped entry bytes: %v vs %v", entry.Value, origBytes) + t.Fatalf("mismatched original bytes and unwrapped entry bytes:\ngot:\n%v\nexpected:\n%v", entry.Value, origBytes) } underlyingEntry, err := phys.Get(ctx, "core/master") if err != nil { @@ -94,11 +91,11 @@ func performTestSealUnwrapper(t *testing.T, phys physical.Backend, logger log.Lo switch wrapped { case true: if !bytes.Equal(underlyingEntry.Value, pBytes) { - t.Fatalf("mismatched original bytes and proto entry bytes: %v vs %v", underlyingEntry.Value, pBytes) + t.Fatalf("mismatched original bytes and proto entry bytes:\ngot:\n%v\nexpected:\n%v", underlyingEntry.Value, pBytes) } default: if !bytes.Equal(underlyingEntry.Value, origBytes) { - t.Fatalf("mismatched original bytes and unwrapped entry bytes: %v vs %v", underlyingEntry.Value, origBytes) + t.Fatalf("mismatched original bytes and unwrapped entry bytes:\ngot:\n%v\nexpected:\n%v", underlyingEntry.Value, origBytes) } } } diff --git a/vault/testing.go b/vault/testing.go index 38dc423ca5..32c997ae79 100644 --- a/vault/testing.go +++ b/vault/testing.go @@ -24,6 +24,7 @@ import ( "os/exec" "path/filepath" "sync" + "sync/atomic" "time" log "github.com/hashicorp/go-hclog" @@ -102,24 +103,46 @@ func TestCoreNewSeal(t testing.T) *Core { return TestCoreWithSeal(t, seal, false) } +// TestCoreWithConfig returns a pure in-memory, uninitialized core with the +// specified core configurations overriden for testing. +func TestCoreWithConfig(t testing.T, conf *CoreConfig) *Core { + return TestCoreWithSealAndUI(t, conf) +} + // TestCoreWithSeal returns a pure in-memory, uninitialized core with the // specified seal for testing. func TestCoreWithSeal(t testing.T, testSeal Seal, enableRaw bool) *Core { + conf := &CoreConfig{ + Seal: testSeal, + EnableUI: false, + EnableRaw: enableRaw, + } + return TestCoreWithSealAndUI(t, conf) +} + +func TestCoreUI(t testing.T, enableUI bool) *Core { + conf := &CoreConfig{ + EnableUI: enableUI, + EnableRaw: true, + } + return TestCoreWithSealAndUI(t, conf) +} + +func TestCoreWithSealAndUI(t testing.T, opts *CoreConfig) *Core { logger := logging.NewVaultLogger(log.Trace) physicalBackend, err := physInmem.NewInmem(nil, logger) if err != nil { t.Fatal(err) } + // Start off with base test core config conf := testCoreConfig(t, physicalBackend, logger) - if enableRaw { - conf.EnableRaw = true - } - - if testSeal != nil { - conf.Seal = testSeal - } + // Override config values with ones that gets passed in + conf.EnableUI = opts.EnableUI + conf.EnableRaw = opts.EnableRaw + conf.Seal = opts.Seal + conf.LicensingConfig = opts.LicensingConfig c, err := NewCore(conf) if err != nil { @@ -251,6 +274,14 @@ func TestCoreUnsealedRaw(t testing.T) (*Core, [][]byte, string) { return testCoreUnsealed(t, core) } +// TestCoreUnsealedWithConfig returns a pure in-memory core that is already +// initialized, unsealed, with the any provided core config values overriden. +func TestCoreUnsealedWithConfig(t testing.T, conf *CoreConfig) (*Core, [][]byte, string) { + t.Helper() + core := TestCoreWithConfig(t, conf) + return testCoreUnsealed(t, core) +} + func testCoreUnsealed(t testing.T, core *Core) (*Core, [][]byte, string) { t.Helper() keys, token := TestCoreInit(t, core) @@ -774,18 +805,56 @@ func (c *TestCluster) EnsureCoresSealed(t testing.T) { } } +func CleanupClusters(clusters []*TestCluster) { + wg := &sync.WaitGroup{} + for _, cluster := range clusters { + wg.Add(1) + lc := cluster + go func() { + defer wg.Done() + lc.Cleanup() + }() + } + wg.Wait() +} + func (c *TestCluster) Cleanup() { // Close listeners + wg := &sync.WaitGroup{} for _, core := range c.Cores { - if core.Listeners != nil { - for _, ln := range core.Listeners { - ln.Close() + wg.Add(1) + lc := core + + go func() { + defer wg.Done() + if lc.Listeners != nil { + for _, ln := range lc.Listeners { + ln.Close() + } } - } + if lc.licensingStopCh != nil { + close(lc.licensingStopCh) + lc.licensingStopCh = nil + } + + if err := lc.Shutdown(); err != nil { + lc.Logger().Error("error during shutdown; abandoning sealing", "error", err) + } else { + timeout := time.Now().Add(60 * time.Second) + for { + if time.Now().After(timeout) { + lc.Logger().Error("timeout waiting for core to seal") + } + if lc.Sealed() { + break + } + time.Sleep(250 * time.Millisecond) + } + } + }() } - // Seal the cores - c.ensureCoresSealed() + wg.Wait() // Remove any temp dir that exists if c.TempDir != "" { @@ -835,6 +904,10 @@ func (c *TestCluster) UnsealWithStoredKeys(t testing.T) error { return nil } +func SetReplicationFailureMode(core *TestClusterCore, mode uint32) { + atomic.StoreUint32(core.Core.replicationFailure, mode) +} + type TestListener struct { net.Listener Address *net.TCPAddr @@ -1146,6 +1219,9 @@ func NewTestCluster(t testing.T, base *CoreConfig, opts *TestClusterOptions) *Te coreConfig.Seal = base.Seal coreConfig.DevToken = base.DevToken coreConfig.EnableRaw = base.EnableRaw + coreConfig.DisableSealWrap = base.DisableSealWrap + coreConfig.DevLicenseDuration = base.DevLicenseDuration + coreConfig.DisableCache = base.DisableCache if !coreConfig.DisableMlock { base.DisableMlock = false @@ -1208,6 +1284,11 @@ func NewTestCluster(t testing.T, base *CoreConfig, opts *TestClusterOptions) *Te coreConfig.HAPhysical = haPhys.(physical.HABackend) } + pubKey, priKey, err := testGenerateCoreKeys() + if err != nil { + t.Fatalf("err: %v", err) + } + cores := []*Core{} for i := 0; i < numCores; i++ { coreConfig.RedirectAddr = fmt.Sprintf("https://127.0.0.1:%d", listeners[i][0].Address.Port) @@ -1224,6 +1305,8 @@ func NewTestCluster(t testing.T, base *CoreConfig, opts *TestClusterOptions) *Te coreConfig.Logger = opts.Logger.Named(fmt.Sprintf("core%d", i)) } + coreConfig.LicensingConfig = testGetLicensingConfig(pubKey) + c, err := NewCore(coreConfig) if err != nil { t.Fatalf("err: %v", err) @@ -1408,9 +1491,15 @@ func NewTestCluster(t testing.T, base *CoreConfig, opts *TestClusterOptions) *Te tcc.ReloadFuncsLock.Lock() (*tcc.ReloadFuncs)["listener|tcp"] = []reload.ReloadFunc{certGetters[i].Reload} tcc.ReloadFuncsLock.Unlock() + + testAdjustTestCore(base, tcc) + ret = append(ret, tcc) } testCluster.Cores = ret + + testExtraClusterCoresTestSetup(t, priKey, testCluster.Cores) + return &testCluster } diff --git a/vault/testing_util.go b/vault/testing_util.go new file mode 100644 index 0000000000..3aff71e150 --- /dev/null +++ b/vault/testing_util.go @@ -0,0 +1,10 @@ +// +build !enterprise + +package vault + +import "github.com/mitchellh/go-testing-interface" + +func testGenerateCoreKeys() (interface{}, interface{}, error) { return nil, nil, nil } +func testGetLicensingConfig(interface{}) *LicensingConfig { return &LicensingConfig{} } +func testAdjustTestCore(*CoreConfig, *TestClusterCore) {} +func testExtraClusterCoresTestSetup(testing.T, interface{}, []*TestClusterCore) {} diff --git a/vault/token_store.go b/vault/token_store.go index fca288fa4a..14d0c601e2 100644 --- a/vault/token_store.go +++ b/vault/token_store.go @@ -19,11 +19,12 @@ import ( "github.com/armon/go-metrics" "github.com/hashicorp/go-multierror" - "github.com/hashicorp/go-uuid" + "github.com/hashicorp/vault/helper/base62" "github.com/hashicorp/vault/helper/consts" "github.com/hashicorp/vault/helper/identity" "github.com/hashicorp/vault/helper/jsonutil" "github.com/hashicorp/vault/helper/locksutil" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/helper/parseutil" "github.com/hashicorp/vault/helper/policyutil" "github.com/hashicorp/vault/helper/salt" @@ -34,9 +35,9 @@ import ( ) const ( - // lookupPrefix is the prefix used to store tokens for their + // idPrefix is the prefix used to store tokens for their // primary ID based index - lookupPrefix = "id/" + idPrefix = "id/" // accessorPrefix is the prefix used to store the index from // Accessor to Token ID @@ -61,30 +62,388 @@ const ( ) var ( + // TokenLength is the size of tokens we are currenlty generating, without + // any namespace information + TokenLength = 24 + // displayNameSanitize is used to sanitize a display name given to a token. displayNameSanitize = regexp.MustCompile("[^a-zA-Z0-9-]") // pathSuffixSanitize is used to ensure a path suffix in a role is valid. pathSuffixSanitize = regexp.MustCompile("\\w[\\w-.]+\\w") - destroyCubbyhole = func(ctx context.Context, ts *TokenStore, saltedID string) error { + destroyCubbyhole = func(ctx context.Context, ts *TokenStore, te *logical.TokenEntry) error { if ts.cubbyholeBackend == nil { // Should only ever happen in testing return nil } - return ts.cubbyholeBackend.revoke(ctx, salt.SaltID(ts.cubbyholeBackend.saltUUID, saltedID, salt.SHA1Hash)) + + if te == nil { + return errors.New("nil token entry") + } + + tokenNS, err := NamespaceByID(ctx, te.NamespaceID, ts.core) + if err != nil { + return err + } + if tokenNS == nil { + return namespace.ErrNoNamespace + } + + switch tokenNS.ID { + case namespace.RootNamespaceID: + saltedID, err := ts.SaltID(ctx, te.ID) + if err != nil { + return err + } + return ts.cubbyholeBackend.revoke(ctx, salt.SaltID(ts.cubbyholeBackend.saltUUID, saltedID, salt.SHA1Hash)) + + default: + if te.CubbyholeID == "" { + return fmt.Errorf("missing cubbyhole ID while destroying") + } + return ts.cubbyholeBackend.revoke(ctx, te.CubbyholeID) + } } ) +func (ts *TokenStore) paths() []*framework.Path { + return []*framework.Path{ + { + Pattern: "roles/?$", + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.ListOperation: ts.tokenStoreRoleList, + }, + + HelpSynopsis: tokenListRolesHelp, + HelpDescription: tokenListRolesHelp, + }, + + { + Pattern: "accessors/$", + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.ListOperation: ts.tokenStoreAccessorList, + }, + + HelpSynopsis: tokenListAccessorsHelp, + HelpDescription: tokenListAccessorsHelp, + }, + + { + Pattern: "roles/" + framework.GenericNameRegex("role_name"), + Fields: map[string]*framework.FieldSchema{ + "role_name": &framework.FieldSchema{ + Type: framework.TypeString, + Description: "Name of the role", + }, + + "allowed_policies": &framework.FieldSchema{ + Type: framework.TypeCommaStringSlice, + Description: tokenAllowedPoliciesHelp, + }, + + "disallowed_policies": &framework.FieldSchema{ + Type: framework.TypeCommaStringSlice, + Description: tokenDisallowedPoliciesHelp, + }, + + "orphan": &framework.FieldSchema{ + Type: framework.TypeBool, + Default: false, + Description: tokenOrphanHelp, + }, + + "period": &framework.FieldSchema{ + Type: framework.TypeDurationSecond, + Default: 0, + Description: tokenPeriodHelp, + }, + + "path_suffix": &framework.FieldSchema{ + Type: framework.TypeString, + Default: "", + Description: tokenPathSuffixHelp + pathSuffixSanitize.String(), + }, + + "explicit_max_ttl": &framework.FieldSchema{ + Type: framework.TypeDurationSecond, + Default: 0, + Description: tokenExplicitMaxTTLHelp, + }, + + "renewable": &framework.FieldSchema{ + Type: framework.TypeBool, + Default: true, + Description: tokenRenewableHelp, + }, + + "bound_cidrs": &framework.FieldSchema{ + Type: framework.TypeCommaStringSlice, + Description: `Comma separated string or JSON list of CIDR blocks. If set, specifies the blocks of IP addresses which are allowed to use the generated token.`, + }, + }, + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.ReadOperation: ts.tokenStoreRoleRead, + logical.CreateOperation: ts.tokenStoreRoleCreateUpdate, + logical.UpdateOperation: ts.tokenStoreRoleCreateUpdate, + logical.DeleteOperation: ts.tokenStoreRoleDelete, + }, + + ExistenceCheck: ts.tokenStoreRoleExistenceCheck, + }, + + { + Pattern: "create-orphan$", + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.UpdateOperation: ts.handleCreateOrphan, + }, + + HelpSynopsis: strings.TrimSpace(tokenCreateOrphanHelp), + HelpDescription: strings.TrimSpace(tokenCreateOrphanHelp), + }, + + { + Pattern: "create/" + framework.GenericNameRegex("role_name"), + + Fields: map[string]*framework.FieldSchema{ + "role_name": &framework.FieldSchema{ + Type: framework.TypeString, + Description: "Name of the role", + }, + }, + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.UpdateOperation: ts.handleCreateAgainstRole, + }, + + HelpSynopsis: strings.TrimSpace(tokenCreateRoleHelp), + HelpDescription: strings.TrimSpace(tokenCreateRoleHelp), + }, + + { + Pattern: "create$", + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.UpdateOperation: ts.handleCreate, + }, + + HelpSynopsis: strings.TrimSpace(tokenCreateHelp), + HelpDescription: strings.TrimSpace(tokenCreateHelp), + }, + + { + Pattern: "lookup" + framework.OptionalParamRegex("urltoken"), + + Fields: map[string]*framework.FieldSchema{ + "urltoken": &framework.FieldSchema{ + Type: framework.TypeString, + Description: "DEPRECATED: Token to lookup (URL parameter). Do not use this; use the POST version instead with the token in the body.", + }, + "token": &framework.FieldSchema{ + Type: framework.TypeString, + Description: "Token to lookup (POST request body)", + }, + }, + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.ReadOperation: ts.handleLookup, + logical.UpdateOperation: ts.handleLookup, + }, + + HelpSynopsis: strings.TrimSpace(tokenLookupHelp), + HelpDescription: strings.TrimSpace(tokenLookupHelp), + }, + + { + Pattern: "lookup-accessor" + framework.OptionalParamRegex("urlaccessor"), + + Fields: map[string]*framework.FieldSchema{ + "urlaccessor": &framework.FieldSchema{ + Type: framework.TypeString, + Description: "DEPRECATED: Accessor of the token to lookup (URL parameter). Do not use this; use the POST version instead with the accessor in the body.", + }, + "accessor": &framework.FieldSchema{ + Type: framework.TypeString, + Description: "Accessor of the token to look up (request body)", + }, + }, + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.UpdateOperation: ts.handleUpdateLookupAccessor, + }, + + HelpSynopsis: strings.TrimSpace(tokenLookupAccessorHelp), + HelpDescription: strings.TrimSpace(tokenLookupAccessorHelp), + }, + + { + Pattern: "lookup-self$", + + Fields: map[string]*framework.FieldSchema{ + "token": &framework.FieldSchema{ + Type: framework.TypeString, + Description: "Token to look up (unused, does not need to be set)", + }, + }, + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.UpdateOperation: ts.handleLookupSelf, + logical.ReadOperation: ts.handleLookupSelf, + }, + + HelpSynopsis: strings.TrimSpace(tokenLookupHelp), + HelpDescription: strings.TrimSpace(tokenLookupHelp), + }, + + { + Pattern: "revoke-accessor" + framework.OptionalParamRegex("urlaccessor"), + + Fields: map[string]*framework.FieldSchema{ + "urlaccessor": &framework.FieldSchema{ + Type: framework.TypeString, + Description: "DEPRECATED: Accessor of the token to revoke (URL parameter). Do not use this; use the POST version instead with the accessor in the body.", + }, + "accessor": &framework.FieldSchema{ + Type: framework.TypeString, + Description: "Accessor of the token (request body)", + }, + }, + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.UpdateOperation: ts.handleUpdateRevokeAccessor, + }, + + HelpSynopsis: strings.TrimSpace(tokenRevokeAccessorHelp), + HelpDescription: strings.TrimSpace(tokenRevokeAccessorHelp), + }, + + { + Pattern: "revoke-self$", + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.UpdateOperation: ts.handleRevokeSelf, + }, + + HelpSynopsis: strings.TrimSpace(tokenRevokeSelfHelp), + HelpDescription: strings.TrimSpace(tokenRevokeSelfHelp), + }, + + { + Pattern: "revoke" + framework.OptionalParamRegex("urltoken"), + + Fields: map[string]*framework.FieldSchema{ + "urltoken": &framework.FieldSchema{ + Type: framework.TypeString, + Description: "DEPRECATED: Token to revoke (URL parameter). Do not use this; use the POST version instead with the token in the body.", + }, + "token": &framework.FieldSchema{ + Type: framework.TypeString, + Description: "Token to revoke (request body)", + }, + }, + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.UpdateOperation: ts.handleRevokeTree, + }, + + HelpSynopsis: strings.TrimSpace(tokenRevokeHelp), + HelpDescription: strings.TrimSpace(tokenRevokeHelp), + }, + + { + Pattern: "revoke-orphan" + framework.OptionalParamRegex("urltoken"), + + Fields: map[string]*framework.FieldSchema{ + "urltoken": &framework.FieldSchema{ + Type: framework.TypeString, + Description: "DEPRECATED: Token to revoke (URL parameter). Do not use this; use the POST version instead with the token in the body.", + }, + "token": &framework.FieldSchema{ + Type: framework.TypeString, + Description: "Token to revoke (request body)", + }, + }, + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.UpdateOperation: ts.handleRevokeOrphan, + }, + + HelpSynopsis: strings.TrimSpace(tokenRevokeOrphanHelp), + HelpDescription: strings.TrimSpace(tokenRevokeOrphanHelp), + }, + + { + Pattern: "renew-self$", + + Fields: map[string]*framework.FieldSchema{ + "token": &framework.FieldSchema{ + Type: framework.TypeString, + Description: "Token to renew (unused, does not need to be set)", + }, + "increment": &framework.FieldSchema{ + Type: framework.TypeDurationSecond, + Default: 0, + Description: "The desired increment in seconds to the token expiration", + }, + }, + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.UpdateOperation: ts.handleRenewSelf, + }, + + HelpSynopsis: strings.TrimSpace(tokenRenewSelfHelp), + HelpDescription: strings.TrimSpace(tokenRenewSelfHelp), + }, + + { + Pattern: "renew" + framework.OptionalParamRegex("urltoken"), + + Fields: map[string]*framework.FieldSchema{ + "urltoken": &framework.FieldSchema{ + Type: framework.TypeString, + Description: "DEPRECATED: Token to renew (URL parameter). Do not use this; use the POST version instead with the token in the body.", + }, + "token": &framework.FieldSchema{ + Type: framework.TypeString, + Description: "Token to renew (request body)", + }, + "increment": &framework.FieldSchema{ + Type: framework.TypeDurationSecond, + Default: 0, + Description: "The desired increment in seconds to the token expiration", + }, + }, + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.UpdateOperation: ts.handleRenew, + }, + + HelpSynopsis: strings.TrimSpace(tokenRenewHelp), + HelpDescription: strings.TrimSpace(tokenRenewHelp), + }, + + { + Pattern: "tidy$", + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.UpdateOperation: ts.handleTidy, + }, + + HelpSynopsis: strings.TrimSpace(tokenTidyHelp), + HelpDescription: strings.TrimSpace(tokenTidyDesc), + }, + } +} + // LookupToken returns the properties of the token from the token store. This // is particularly useful to fetch the accessor of the client token and get it // populated in the logical request along with the client token. The accessor // of the client token can get audit logged. -func (c *Core) LookupToken(token string) (*logical.TokenEntry, error) { - if token == "" { - return nil, fmt.Errorf("missing client token") - } - +func (c *Core) LookupToken(ctx context.Context, token string) (*logical.TokenEntry, error) { if c.Sealed() { return nil, consts.ErrSealed } @@ -92,7 +451,7 @@ func (c *Core) LookupToken(token string) (*logical.TokenEntry, error) { c.stateLock.RLock() defer c.stateLock.RUnlock() - if c.standby { + if c.standby && !c.perfStandby { return nil, consts.ErrStandby } @@ -101,7 +460,7 @@ func (c *Core) LookupToken(token string) (*logical.TokenEntry, error) { return nil, nil } - return c.tokenStore.Lookup(c.activeContext, token) + return c.tokenStore.Lookup(ctx, token) } // TokenStore is used to manage client tokens. Tokens are used for @@ -110,14 +469,20 @@ func (c *Core) LookupToken(token string) (*logical.TokenEntry, error) { type TokenStore struct { *framework.Backend - view *BarrierView + activeContext context.Context + + core *Core + + baseBarrierView *BarrierView + idBarrierView *BarrierView + accessorBarrierView *BarrierView + parentBarrierView *BarrierView + rolesBarrierView *BarrierView expiration *ExpirationManager cubbyholeBackend *CubbyholeBackend - policyLookupFunc func(string) (*Policy, error) - tokenLocks []*locksutil.LockEntry // tokenPendingDeletion stores tokens that are being revoked. If the token is @@ -126,17 +491,15 @@ type TokenStore struct { // failed. Revocation needs to handle these states accordingly. tokensPendingDeletion *sync.Map - cubbyholeDestroyer func(context.Context, *TokenStore, string) error + cubbyholeDestroyer func(context.Context, *TokenStore, *logical.TokenEntry) error logger log.Logger saltLock sync.RWMutex - salt *salt.Salt + salts map[string]*salt.Salt tidyLock *uint32 - core *Core - identityPoliciesDeriverFunc func(string) (*identity.Entity, []string, error) quitContext context.Context @@ -144,28 +507,27 @@ type TokenStore struct { // NewTokenStore is used to construct a token store that is // backed by the given barrier view. -func NewTokenStore(ctx context.Context, logger log.Logger, c *Core, config *logical.BackendConfig) (*TokenStore, error) { +func NewTokenStore(ctx context.Context, logger log.Logger, core *Core, config *logical.BackendConfig) (*TokenStore, error) { // Create a sub-view - view := c.systemBarrierView.SubView(tokenSubPath) + view := core.systemBarrierView.SubView(tokenSubPath) // Initialize the store t := &TokenStore{ - view: view, - cubbyholeDestroyer: destroyCubbyhole, - logger: logger, - tokenLocks: locksutil.CreateLocks(), - tokensPendingDeletion: &sync.Map{}, - saltLock: sync.RWMutex{}, - core: c, - identityPoliciesDeriverFunc: c.fetchEntityAndDerivedPolicies, - tidyLock: new(uint32), - quitContext: c.activeContext, - } - - if c.policyStore != nil { - t.policyLookupFunc = func(name string) (*Policy, error) { - return c.policyStore.GetPolicy(ctx, name, PolicyTypeToken) - } + activeContext: ctx, + core: core, + baseBarrierView: view, + idBarrierView: view.SubView(idPrefix), + accessorBarrierView: view.SubView(accessorPrefix), + parentBarrierView: view.SubView(parentPrefix), + rolesBarrierView: view.SubView(rolesPrefix), + cubbyholeDestroyer: destroyCubbyhole, + logger: logger, + tokenLocks: locksutil.CreateLocks(), + tokensPendingDeletion: &sync.Map{}, + saltLock: sync.RWMutex{}, + tidyLock: new(uint32), + quitContext: core.activeContext, + salts: make(map[string]*salt.Salt), } // Setup the framework endpoints @@ -181,347 +543,16 @@ func NewTokenStore(ctx context.Context, logger log.Logger, c *Core, config *logi // Most token store items are local since tokens are local, but a // notable exception is roles LocalStorage: []string{ - lookupPrefix, + idPrefix, accessorPrefix, parentPrefix, salt.DefaultLocation, }, }, - - Paths: []*framework.Path{ - &framework.Path{ - Pattern: "roles/?$", - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ListOperation: t.tokenStoreRoleList, - }, - - HelpSynopsis: tokenListRolesHelp, - HelpDescription: tokenListRolesHelp, - }, - - &framework.Path{ - Pattern: "accessors/$", - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ListOperation: t.tokenStoreAccessorList, - }, - - HelpSynopsis: tokenListAccessorsHelp, - HelpDescription: tokenListAccessorsHelp, - }, - - &framework.Path{ - Pattern: "roles/" + framework.GenericNameRegex("role_name"), - Fields: map[string]*framework.FieldSchema{ - "role_name": &framework.FieldSchema{ - Type: framework.TypeString, - Description: "Name of the role", - }, - - "allowed_policies": &framework.FieldSchema{ - Type: framework.TypeCommaStringSlice, - Description: tokenAllowedPoliciesHelp, - }, - - "disallowed_policies": &framework.FieldSchema{ - Type: framework.TypeCommaStringSlice, - Description: tokenDisallowedPoliciesHelp, - }, - - "orphan": &framework.FieldSchema{ - Type: framework.TypeBool, - Default: false, - Description: tokenOrphanHelp, - }, - - "period": &framework.FieldSchema{ - Type: framework.TypeDurationSecond, - Default: 0, - Description: tokenPeriodHelp, - }, - - "path_suffix": &framework.FieldSchema{ - Type: framework.TypeString, - Default: "", - Description: tokenPathSuffixHelp + pathSuffixSanitize.String(), - }, - - "explicit_max_ttl": &framework.FieldSchema{ - Type: framework.TypeDurationSecond, - Default: 0, - Description: tokenExplicitMaxTTLHelp, - }, - - "renewable": &framework.FieldSchema{ - Type: framework.TypeBool, - Default: true, - Description: tokenRenewableHelp, - }, - - "bound_cidrs": &framework.FieldSchema{ - Type: framework.TypeCommaStringSlice, - Description: `Comma separated string or JSON list of CIDR blocks. If set, specifies the blocks of IP addresses which are allowed to use the generated token.`, - }, - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ReadOperation: t.tokenStoreRoleRead, - logical.CreateOperation: t.tokenStoreRoleCreateUpdate, - logical.UpdateOperation: t.tokenStoreRoleCreateUpdate, - logical.DeleteOperation: t.tokenStoreRoleDelete, - }, - - ExistenceCheck: t.tokenStoreRoleExistenceCheck, - - HelpSynopsis: tokenPathRolesHelp, - HelpDescription: tokenPathRolesHelp, - }, - - &framework.Path{ - Pattern: "create-orphan$", - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: t.handleCreateOrphan, - }, - - HelpSynopsis: strings.TrimSpace(tokenCreateOrphanHelp), - HelpDescription: strings.TrimSpace(tokenCreateOrphanHelp), - }, - - &framework.Path{ - Pattern: "create/" + framework.GenericNameRegex("role_name"), - - Fields: map[string]*framework.FieldSchema{ - "role_name": &framework.FieldSchema{ - Type: framework.TypeString, - Description: "Name of the role", - }, - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: t.handleCreateAgainstRole, - }, - - HelpSynopsis: strings.TrimSpace(tokenCreateRoleHelp), - HelpDescription: strings.TrimSpace(tokenCreateRoleHelp), - }, - - &framework.Path{ - Pattern: "create$", - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: t.handleCreate, - }, - - HelpSynopsis: strings.TrimSpace(tokenCreateHelp), - HelpDescription: strings.TrimSpace(tokenCreateHelp), - }, - - &framework.Path{ - Pattern: "lookup" + framework.OptionalParamRegex("urltoken"), - - Fields: map[string]*framework.FieldSchema{ - "urltoken": &framework.FieldSchema{ - Type: framework.TypeString, - Description: "DEPRECATED: Token to lookup (URL parameter). Do not use this; use the POST version instead with the token in the body.", - }, - "token": &framework.FieldSchema{ - Type: framework.TypeString, - Description: "Token to lookup (POST request body)", - }, - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.ReadOperation: t.handleLookup, - logical.UpdateOperation: t.handleLookup, - }, - - HelpSynopsis: strings.TrimSpace(tokenLookupHelp), - HelpDescription: strings.TrimSpace(tokenLookupHelp), - }, - - &framework.Path{ - Pattern: "lookup-accessor" + framework.OptionalParamRegex("urlaccessor"), - - Fields: map[string]*framework.FieldSchema{ - "urlaccessor": &framework.FieldSchema{ - Type: framework.TypeString, - Description: "DEPRECATED: Accessor of the token to lookup (URL parameter). Do not use this; use the POST version instead with the accessor in the body.", - }, - "accessor": &framework.FieldSchema{ - Type: framework.TypeString, - Description: "Accessor of the token to look up (request body)", - }, - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: t.handleUpdateLookupAccessor, - }, - - HelpSynopsis: strings.TrimSpace(tokenLookupAccessorHelp), - HelpDescription: strings.TrimSpace(tokenLookupAccessorHelp), - }, - - &framework.Path{ - Pattern: "lookup-self$", - - Fields: map[string]*framework.FieldSchema{ - "token": &framework.FieldSchema{ - Type: framework.TypeString, - Description: "Token to look up (unused, does not need to be set)", - }, - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: t.handleLookupSelf, - logical.ReadOperation: t.handleLookupSelf, - }, - - HelpSynopsis: strings.TrimSpace(tokenLookupHelp), - HelpDescription: strings.TrimSpace(tokenLookupHelp), - }, - - &framework.Path{ - Pattern: "revoke-accessor" + framework.OptionalParamRegex("urlaccessor"), - - Fields: map[string]*framework.FieldSchema{ - "urlaccessor": &framework.FieldSchema{ - Type: framework.TypeString, - Description: "DEPRECATED: Accessor of the token to revoke (URL parameter). Do not use this; use the POST version instead with the accessor in the body.", - }, - "accessor": &framework.FieldSchema{ - Type: framework.TypeString, - Description: "Accessor of the token (request body)", - }, - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: t.handleUpdateRevokeAccessor, - }, - - HelpSynopsis: strings.TrimSpace(tokenRevokeAccessorHelp), - HelpDescription: strings.TrimSpace(tokenRevokeAccessorHelp), - }, - - &framework.Path{ - Pattern: "revoke-self$", - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: t.handleRevokeSelf, - }, - - HelpSynopsis: strings.TrimSpace(tokenRevokeSelfHelp), - HelpDescription: strings.TrimSpace(tokenRevokeSelfHelp), - }, - - &framework.Path{ - Pattern: "revoke" + framework.OptionalParamRegex("urltoken"), - - Fields: map[string]*framework.FieldSchema{ - "urltoken": &framework.FieldSchema{ - Type: framework.TypeString, - Description: "DEPRECATED: Token to revoke (URL parameter). Do not use this; use the POST version instead with the token in the body.", - }, - "token": &framework.FieldSchema{ - Type: framework.TypeString, - Description: "Token to revoke (request body)", - }, - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: t.handleRevokeTree, - }, - - HelpSynopsis: strings.TrimSpace(tokenRevokeHelp), - HelpDescription: strings.TrimSpace(tokenRevokeHelp), - }, - - &framework.Path{ - Pattern: "revoke-orphan" + framework.OptionalParamRegex("urltoken"), - - Fields: map[string]*framework.FieldSchema{ - "urltoken": &framework.FieldSchema{ - Type: framework.TypeString, - Description: "DEPRECATED: Token to revoke (URL parameter). Do not use this; use the POST version instead with the token in the body.", - }, - "token": &framework.FieldSchema{ - Type: framework.TypeString, - Description: "Token to revoke (request body)", - }, - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: t.handleRevokeOrphan, - }, - - HelpSynopsis: strings.TrimSpace(tokenRevokeOrphanHelp), - HelpDescription: strings.TrimSpace(tokenRevokeOrphanHelp), - }, - - &framework.Path{ - Pattern: "renew-self$", - - Fields: map[string]*framework.FieldSchema{ - "token": &framework.FieldSchema{ - Type: framework.TypeString, - Description: "Token to renew (unused, does not need to be set)", - }, - "increment": &framework.FieldSchema{ - Type: framework.TypeDurationSecond, - Default: 0, - Description: "The desired increment in seconds to the token expiration", - }, - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: t.handleRenewSelf, - }, - - HelpSynopsis: strings.TrimSpace(tokenRenewSelfHelp), - HelpDescription: strings.TrimSpace(tokenRenewSelfHelp), - }, - - &framework.Path{ - Pattern: "renew" + framework.OptionalParamRegex("urltoken"), - - Fields: map[string]*framework.FieldSchema{ - "urltoken": &framework.FieldSchema{ - Type: framework.TypeString, - Description: "DEPRECATED: Token to renew (URL parameter). Do not use this; use the POST version instead with the token in the body.", - }, - "token": &framework.FieldSchema{ - Type: framework.TypeString, - Description: "Token to renew (request body)", - }, - "increment": &framework.FieldSchema{ - Type: framework.TypeDurationSecond, - Default: 0, - Description: "The desired increment in seconds to the token expiration", - }, - }, - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: t.handleRenew, - }, - - HelpSynopsis: strings.TrimSpace(tokenRenewHelp), - HelpDescription: strings.TrimSpace(tokenRenewHelp), - }, - - &framework.Path{ - Pattern: "tidy$", - - Callbacks: map[logical.Operation]framework.OperationFunc{ - logical.UpdateOperation: t.handleTidy, - }, - - HelpSynopsis: strings.TrimSpace(tokenTidyHelp), - HelpDescription: strings.TrimSpace(tokenTidyDesc), - }, - }, } + t.Backend.Paths = append(t.Backend.Paths, t.paths()...) + t.Backend.Setup(ctx, config) return t, nil @@ -533,31 +564,37 @@ func (ts *TokenStore) Invalidate(ctx context.Context, key string) { switch key { case tokenSubPath + salt.DefaultLocation: ts.saltLock.Lock() - ts.salt = nil + ts.salts = make(map[string]*salt.Salt) ts.saltLock.Unlock() } } func (ts *TokenStore) Salt(ctx context.Context) (*salt.Salt, error) { + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } + ts.saltLock.RLock() - if ts.salt != nil { + if salt, ok := ts.salts[ns.ID]; ok { defer ts.saltLock.RUnlock() - return ts.salt, nil + return salt, nil } ts.saltLock.RUnlock() ts.saltLock.Lock() defer ts.saltLock.Unlock() - if ts.salt != nil { - return ts.salt, nil + if salt, ok := ts.salts[ns.ID]; ok { + return salt, nil } - salt, err := salt.NewSalt(ctx, ts.view, &salt.Config{ + + salt, err := salt.NewSalt(ctx, ts.baseView(ns), &salt.Config{ HashFunc: salt.SHA1Hash, Location: salt.DefaultLocation, }) if err != nil { return nil, err } - ts.salt = salt + ts.salts[ns.ID] = salt return salt, nil } @@ -596,8 +633,9 @@ type tsRoleEntry struct { } type accessorEntry struct { - TokenID string `json:"token_id"` - AccessorID string `json:"accessor_id"` + TokenID string `json:"token_id"` + AccessorID string `json:"accessor_id"` + NamespaceID string `json:"namespace_id"` } // SetExpirationManager is used to provide the token store with @@ -609,21 +647,32 @@ func (ts *TokenStore) SetExpirationManager(exp *ExpirationManager) { // SaltID is used to apply a salt and hash to an ID to make sure its not reversible func (ts *TokenStore) SaltID(ctx context.Context, id string) (string, error) { + ns, err := namespace.FromContext(ctx) + if err != nil { + return "", namespace.ErrNoNamespace + } + s, err := ts.Salt(ctx) if err != nil { return "", err } + if ns.ID != namespace.RootNamespaceID { + return "h" + s.GetHMAC(id), nil + } + return s.SaltID(id), nil } -// RootToken is used to generate a new token with root privileges and no parent +// rootToken is used to generate a new token with root privileges and no parent func (ts *TokenStore) rootToken(ctx context.Context) (*logical.TokenEntry, error) { + ctx = namespace.ContextWithNamespace(ctx, namespace.RootNamespace) te := &logical.TokenEntry{ Policies: []string{"root"}, Path: "auth/token/root", DisplayName: "root", CreationTime: time.Now().Unix(), + NamespaceID: namespace.RootNamespaceID, } if err := ts.create(ctx, te); err != nil { return nil, err @@ -632,7 +681,13 @@ func (ts *TokenStore) rootToken(ctx context.Context) (*logical.TokenEntry, error } func (ts *TokenStore) tokenStoreAccessorList(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { - entries, err := ts.view.List(ctx, accessorPrefix) + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } + nsID := ns.ID + + entries, err := ts.accessorView(ns).List(ctx, "") if err != nil { return nil, err } @@ -641,14 +696,18 @@ func (ts *TokenStore) tokenStoreAccessorList(ctx context.Context, req *logical.R ret := make([]string, 0, len(entries)) for _, entry := range entries { - aEntry, err := ts.lookupBySaltedAccessor(ctx, entry, false) + aEntry, err := ts.lookupByAccessor(ctx, entry, true, false) if err != nil { resp.AddWarning("Found an accessor entry that could not be successfully decoded") continue } + if aEntry.TokenID == "" { resp.AddWarning(fmt.Sprintf("Found an accessor entry missing a token: %v", aEntry.AccessorID)) - } else { + continue + } + + if aEntry.NamespaceID == nsID { ret = append(ret, aEntry.AccessorID) } } @@ -664,31 +723,45 @@ func (ts *TokenStore) tokenStoreAccessorList(ctx context.Context, req *logical.R func (ts *TokenStore) createAccessor(ctx context.Context, entry *logical.TokenEntry) error { defer metrics.MeasureSince([]string{"token", "createAccessor"}, time.Now()) + var err error // Create a random accessor - accessorUUID, err := uuid.GenerateUUID() + entry.Accessor, err = base62.Random(TokenLength, true) if err != nil { return err } - entry.Accessor = accessorUUID + + tokenNS, err := NamespaceByID(ctx, entry.NamespaceID, ts.core) + if err != nil { + return err + } + if tokenNS == nil { + return namespace.ErrNoNamespace + } + + if tokenNS.ID != namespace.RootNamespaceID { + entry.Accessor = fmt.Sprintf("%s.%s", entry.Accessor, tokenNS.ID) + } // Create index entry, mapping the accessor to the token ID - saltID, err := ts.SaltID(ctx, entry.Accessor) + saltCtx := namespace.ContextWithNamespace(ctx, tokenNS) + saltID, err := ts.SaltID(saltCtx, entry.Accessor) if err != nil { return err } - path := accessorPrefix + saltID aEntry := &accessorEntry{ - TokenID: entry.ID, - AccessorID: entry.Accessor, + TokenID: entry.ID, + AccessorID: entry.Accessor, + NamespaceID: entry.NamespaceID, } + aEntryBytes, err := jsonutil.EncodeJSON(aEntry) if err != nil { return errwrap.Wrapf("failed to marshal accessor index entry: {{err}}", err) } - le := &logical.StorageEntry{Key: path, Value: aEntryBytes} - if err := ts.view.Put(ctx, le); err != nil { + le := &logical.StorageEntry{Key: saltID, Value: aEntryBytes} + if err := ts.accessorView(tokenNS).Put(ctx, le); err != nil { return errwrap.Wrapf("failed to persist accessor index entry: {{err}}", err) } return nil @@ -700,18 +773,33 @@ func (ts *TokenStore) create(ctx context.Context, entry *logical.TokenEntry) err defer metrics.MeasureSince([]string{"token", "create"}, time.Now()) // Generate an ID if necessary if entry.ID == "" { - entryUUID, err := uuid.GenerateUUID() + var err error + entry.ID, err = base62.Random(TokenLength, true) if err != nil { return err } - entry.ID = entryUUID } - saltedID, err := ts.SaltID(ctx, entry.ID) + tokenNS, err := NamespaceByID(ctx, entry.NamespaceID, ts.core) if err != nil { return err } - exist, _ := ts.lookupSalted(ctx, saltedID, true) + if tokenNS == nil { + return namespace.ErrNoNamespace + } + + if tokenNS.ID != namespace.RootNamespaceID { + entry.ID = fmt.Sprintf("%s.%s", entry.ID, tokenNS.ID) + if entry.CubbyholeID == "" { + cubbyholeID, err := base62.Random(TokenLength, true) + if err != nil { + return err + } + entry.CubbyholeID = cubbyholeID + } + } + + exist, _ := ts.lookupInternal(ctx, entry.ID, false, true) if exist != nil { return fmt.Errorf("cannot create a token with a duplicate ID") } @@ -736,7 +824,16 @@ func (ts *TokenStore) store(ctx context.Context, entry *logical.TokenEntry) erro // storeCommon handles the actual storage of an entry, possibly generating // secondary indexes func (ts *TokenStore) storeCommon(ctx context.Context, entry *logical.TokenEntry, writeSecondary bool) error { - saltedID, err := ts.SaltID(ctx, entry.ID) + tokenNS, err := NamespaceByID(ctx, entry.NamespaceID, ts.core) + if err != nil { + return err + } + if tokenNS == nil { + return namespace.ErrNoNamespace + } + + saltCtx := namespace.ContextWithNamespace(ctx, tokenNS) + saltedID, err := ts.SaltID(saltCtx, entry.ID) if err != nil { return err } @@ -762,26 +859,40 @@ func (ts *TokenStore) storeCommon(ctx context.Context, entry *logical.TokenEntry return fmt.Errorf("parent token not found") } - // Create the index entry - parentSaltedID, err := ts.SaltID(ctx, entry.Parent) + parentNS, err := NamespaceByID(ctx, parent.NamespaceID, ts.core) if err != nil { return err } - path := parentPrefix + parentSaltedID + "/" + saltedID + if parentNS == nil { + return namespace.ErrNoNamespace + } + + parentCtx := namespace.ContextWithNamespace(ctx, parentNS) + + // Create the index entry + parentSaltedID, err := ts.SaltID(parentCtx, entry.Parent) + if err != nil { + return err + } + + path := parentSaltedID + "/" + saltedID + if tokenNS.ID != namespace.RootNamespaceID { + path = fmt.Sprintf("%s.%s", path, tokenNS.ID) + } + le := &logical.StorageEntry{Key: path} - if err := ts.view.Put(ctx, le); err != nil { + if err := ts.parentView(parentNS).Put(ctx, le); err != nil { return errwrap.Wrapf("failed to persist entry: {{err}}", err) } } } // Write the primary ID - path := lookupPrefix + saltedID - le := &logical.StorageEntry{Key: path, Value: enc} + le := &logical.StorageEntry{Key: saltedID, Value: enc} if len(entry.Policies) == 1 && entry.Policies[0] == "root" { le.SealWrap = true } - if err := ts.view.Put(ctx, le); err != nil { + if err := ts.idView(tokenNS).Put(ctx, le); err != nil { return errwrap.Wrapf("failed to persist entry: {{err}}", err) } return nil @@ -815,13 +926,8 @@ func (ts *TokenStore) UseToken(ctx context.Context, te *logical.TokenEntry) (*lo lock.Lock() defer lock.Unlock() - // Call lookupSalted instead of Lookup to avoid deadlocking since Lookup grabs a read lock - saltedID, err := ts.SaltID(ctx, te.ID) - if err != nil { - return nil, err - } - - te, err = ts.lookupSalted(ctx, saltedID, false) + var err error + te, err = ts.lookupInternal(ctx, te.ID, false, false) if err != nil { return nil, errwrap.Wrapf("failed to refresh entry: {{err}}", err) } @@ -862,7 +968,7 @@ func (ts *TokenStore) UseTokenByID(ctx context.Context, id string) (*logical.Tok return ts.UseToken(ctx, te) } -// Lookup is used to find a token given its ID. It acquires a read lock, then calls lookupSalted. +// Lookup is used to find a token given its ID. It acquires a read lock, then calls lookupInternal. func (ts *TokenStore) Lookup(ctx context.Context, id string) (*logical.TokenEntry, error) { defer metrics.MeasureSince([]string{"token", "lookup"}, time.Now()) if id == "" { @@ -873,15 +979,11 @@ func (ts *TokenStore) Lookup(ctx context.Context, id string) (*logical.TokenEntr lock.RLock() defer lock.RUnlock() - saltedID, err := ts.SaltID(ctx, id) - if err != nil { - return nil, err - } - return ts.lookupSalted(ctx, saltedID, false) + return ts.lookupInternal(ctx, id, false, false) } // lookupTainted is used to find a token that may or maynot be tainted given its -// ID. It acquires a read lock, then calls lookupSalted. +// ID. It acquires a read lock, then calls lookupInternal. func (ts *TokenStore) lookupTainted(ctx context.Context, id string) (*logical.TokenEntry, error) { defer metrics.MeasureSince([]string{"token", "lookup"}, time.Now()) if id == "" { @@ -892,20 +994,51 @@ func (ts *TokenStore) lookupTainted(ctx context.Context, id string) (*logical.To lock.RLock() defer lock.RUnlock() - saltedID, err := ts.SaltID(ctx, id) - if err != nil { - return nil, err - } - return ts.lookupSalted(ctx, saltedID, true) + return ts.lookupInternal(ctx, id, false, true) } -// lookupSalted is used to find a token given its salted ID. If tainted is -// true, entries that are in some revocation state (currently, indicated by num -// uses < 0), the entry will be returned anyways -func (ts *TokenStore) lookupSalted(ctx context.Context, saltedID string, tainted bool) (*logical.TokenEntry, error) { - // Lookup token - path := lookupPrefix + saltedID - raw, err := ts.view.Get(ctx, path) +// lookupInternal is used to find a token given its (possibly salted) ID. If +// tainted is true, entries that are in some revocation state (currently, +// indicated by num uses < 0), the entry will be returned anyways +func (ts *TokenStore) lookupInternal(ctx context.Context, id string, salted, tainted bool) (*logical.TokenEntry, error) { + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, errwrap.Wrapf("failed to find namespace in context: {{err}}", err) + } + + var raw *logical.StorageEntry + lookupID := id + + if !salted { + // If possible, always use the token's namespace. If it doesn't match + // the request namespace, ensure the request namespace is a child + _, nsID := namespace.SplitIDFromString(id) + if nsID != "" { + tokenNS, err := NamespaceByID(ctx, nsID, ts.core) + if err != nil { + return nil, errwrap.Wrapf("failed to look up namespace from the token: {{err}}", err) + } + if tokenNS != nil { + if tokenNS.ID != ns.ID { + ns = tokenNS + ctx = namespace.ContextWithNamespace(ctx, tokenNS) + } + } + } else { + // Any non-root-ns token should have an accessor and child + // namespaces cannot have custom IDs. If someone omits or tampers + // with it, the lookup in the root namespace simply won't work. + ns = namespace.RootNamespace + ctx = namespace.ContextWithNamespace(ctx, ns) + } + + lookupID, err = ts.SaltID(ctx, id) + if err != nil { + return nil, err + } + } + + raw, err = ts.idView(ns).Get(ctx, lookupID) if err != nil { return nil, errwrap.Wrapf("failed to read entry: {{err}}", err) } @@ -926,6 +1059,10 @@ func (ts *TokenStore) lookupSalted(ctx context.Context, saltedID string, tainted return nil, nil } + if entry.NamespaceID == "" { + entry.NamespaceID = namespace.RootNamespaceID + } + persistNeeded := false // Upgrade the deprecated fields @@ -982,7 +1119,7 @@ func (ts *TokenStore) lookupSalted(ctx context.Context, saltedID string, tainted if ts.expiration == nil { return nil, errors.New("expiration manager is nil on tokenstore") } - le, err := ts.expiration.FetchLeaseTimesByToken(ctx, entry.Path, entry.ID) + le, err := ts.expiration.FetchLeaseTimesByToken(ctx, entry) if err != nil { return nil, errwrap.Wrapf("failed to fetch lease times: {{err}}", err) } @@ -992,12 +1129,21 @@ func (ts *TokenStore) lookupSalted(ctx context.Context, saltedID string, tainted switch { // It's any kind of expiring token with no lease, immediately delete it case le == nil: - leaseID, err := ts.expiration.CreateOrFetchRevocationLeaseByToken(ctx, entry) + tokenNS, err := NamespaceByID(ctx, entry.NamespaceID, ts.core) + if err != nil { + return nil, err + } + if tokenNS == nil { + return nil, namespace.ErrNoNamespace + } + + revokeCtx := namespace.ContextWithNamespace(ts.quitContext, tokenNS) + leaseID, err := ts.expiration.CreateOrFetchRevocationLeaseByToken(revokeCtx, entry) if err != nil { return nil, err } - err = ts.expiration.Revoke(ts.quitContext, leaseID) + err = ts.expiration.Revoke(revokeCtx, leaseID) if err != nil { return nil, err } @@ -1032,14 +1178,15 @@ func (ts *TokenStore) revokeOrphan(ctx context.Context, id string) error { if err != nil { return err } - return ts.revokeSalted(ctx, saltedID, false) + + return ts.revokeInternal(ctx, saltedID, false) } -// revokeSalted is used to invalidate a given salted token, any child tokens +// revokeInternal is used to invalidate a given salted token, any child tokens // will be orphaned unless otherwise specified. skipOrphan should be used // whenever we are revoking the entire tree starting from a particular parent -// (e.g. revokeTreeSalted). -func (ts *TokenStore) revokeSalted(ctx context.Context, saltedID string, skipOrphan bool) (ret error) { +// (e.g. revokeTreeInternal). +func (ts *TokenStore) revokeInternal(ctx context.Context, saltedID string, skipOrphan bool) (ret error) { // Check and set the token deletion state. We only proceed with the deletion // if we don't have a pending deletion (empty), or if the deletion previously // failed (state is false) @@ -1051,8 +1198,8 @@ func (ts *TokenStore) revokeSalted(ctx context.Context, saltedID string, skipOrp } // The map check above should protect use from any concurrent revocations, so - // doing a bare lookup here should be fine. - entry, err := ts.lookupSalted(ctx, saltedID, true) + // we do another lookup here to make sure we have the right state + entry, err := ts.lookupInternal(ctx, saltedID, true, true) if err != nil { return err } @@ -1068,17 +1215,24 @@ func (ts *TokenStore) revokeSalted(ctx context.Context, saltedID string, skipOrp // really work either. So we clear revocation state so the user can // try again. ts.logger.Error("failed to mark token as revoked") - ts.tokensPendingDeletion.Store(saltedID, false) + ts.tokensPendingDeletion.Store(entry.ID, false) return err } } + tokenNS, err := NamespaceByID(ctx, entry.NamespaceID, ts.core) + if err != nil { + return err + } + if tokenNS == nil { + return namespace.ErrNoNamespace + } + defer func() { // If we succeeded in all other revocation operations after this defer and // before we return, we can remove the token store entry if ret == nil { - path := lookupPrefix + saltedID - if err := ts.view.Delete(ctx, path); err != nil { + if err := ts.idView(tokenNS).Delete(ctx, saltedID); err != nil { ret = errwrap.Wrapf("failed to delete entry: {{err}}", err) } } @@ -1086,7 +1240,7 @@ func (ts *TokenStore) revokeSalted(ctx context.Context, saltedID string, skipOrp // Check on ret again and update the sync.Map accordingly if ret != nil { // If we failed on any of the calls within, we store the state as false - // so that the next call to revokeSalted will retry + // so that the next call to revokeInternal will retry ts.tokensPendingDeletion.Store(saltedID, false) } else { ts.tokensPendingDeletion.Delete(saltedID) @@ -1095,39 +1249,62 @@ func (ts *TokenStore) revokeSalted(ctx context.Context, saltedID string, skipOrp // Destroy the token's cubby. This should go first as it's a // security-sensitive item. - err = ts.cubbyholeDestroyer(ctx, ts, saltedID) + err = ts.cubbyholeDestroyer(ctx, ts, entry) if err != nil { return err } - // Revoke all secrets under this token. This should go first as it's a - // security-sensitive item. - if err := ts.expiration.RevokeByToken(ts.quitContext, entry); err != nil { + revokeCtx := namespace.ContextWithNamespace(ts.quitContext, tokenNS) + if err := ts.expiration.RevokeByToken(revokeCtx, entry); err != nil { return err } // Clear the secondary index if any if entry.Parent != "" { - parentSaltedID, err := ts.SaltID(ctx, entry.Parent) + _, parentNSID := namespace.SplitIDFromString(entry.Parent) + parentCtx := revokeCtx + parentNS := tokenNS + + if parentNSID != tokenNS.ID { + switch { + case parentNSID == "": + parentNS = namespace.RootNamespace + default: + parentNS, err = NamespaceByID(ctx, parentNSID, ts.core) + if err != nil { + return errwrap.Wrapf("failed to get parent namespace: {{err}}", err) + } + if parentNS == nil { + return namespace.ErrNoNamespace + } + } + + parentCtx = namespace.ContextWithNamespace(ctx, parentNS) + } + + parentSaltedID, err := ts.SaltID(parentCtx, entry.Parent) if err != nil { return err } - path := parentPrefix + parentSaltedID + "/" + saltedID - if err = ts.view.Delete(ctx, path); err != nil { + path := parentSaltedID + "/" + saltedID + if tokenNS.ID != namespace.RootNamespaceID { + path = fmt.Sprintf("%s.%s", path, tokenNS.ID) + } + + if err = ts.parentView(parentNS).Delete(ctx, path); err != nil { return errwrap.Wrapf("failed to delete entry: {{err}}", err) } } // Clear the accessor index if any if entry.Accessor != "" { - accessorSaltedID, err := ts.SaltID(ctx, entry.Accessor) + accessorSaltedID, err := ts.SaltID(revokeCtx, entry.Accessor) if err != nil { return err } - path := accessorPrefix + accessorSaltedID - if err = ts.view.Delete(ctx, path); err != nil { + if err = ts.accessorView(tokenNS).Delete(ctx, accessorSaltedID); err != nil { return errwrap.Wrapf("failed to delete entry: {{err}}", err) } } @@ -1137,24 +1314,47 @@ func (ts *TokenStore) revokeSalted(ctx context.Context, saltedID string, skipOrp // their parent index, and clear the parent entry. // // Marking the token as orphan should be skipped if it's called by - // revokeTreeSalted to avoid unnecessary view.List operations. Since + // revokeTreeInternal to avoid unnecessary view.List operations. Since // the deletion occurs in a DFS fashion we don't need to perform a delete // on child prefixes as there will be none (as saltedID entry is a leaf node). - parentPath := parentPrefix + saltedID + "/" - children, err := ts.view.List(ctx, parentPath) + children, err := ts.parentView(tokenNS).List(ctx, saltedID+"/") if err != nil { return errwrap.Wrapf("failed to scan for children: {{err}}", err) } for _, child := range children { - entry, err := ts.lookupSalted(ctx, child, true) + var childNSID string + childCtx := revokeCtx + child, childNSID = namespace.SplitIDFromString(child) + if childNSID != "" { + childNS, err := NamespaceByID(ctx, childNSID, ts.core) + if err != nil { + return errwrap.Wrapf("failed to get child token: {{err}}", err) + } + if childNS == nil { + return namespace.ErrNoNamespace + } + + childCtx = namespace.ContextWithNamespace(ctx, childNS) + } + + entry, err := ts.lookupInternal(childCtx, child, true, true) if err != nil { return errwrap.Wrapf("failed to get child token: {{err}}", err) } + if entry == nil { + // Seems it's already revoked, so nothing to do here except delete the index + err = ts.parentView(tokenNS).Delete(ctx, child) + if err != nil { + return errwrap.Wrapf("failed to delete child entry: {{err}}", err) + } + continue + } + lock := locksutil.LockForKey(ts.tokenLocks, entry.ID) lock.Lock() entry.Parent = "" - err = ts.store(ctx, entry) + err = ts.store(childCtx, entry) if err != nil { lock.Unlock() return errwrap.Wrapf("failed to update child token: {{err}}", err) @@ -1165,8 +1365,7 @@ func (ts *TokenStore) revokeSalted(ctx context.Context, saltedID string, skipOrp // paths are not deeply nested (i.e. they are simply // parenPrefix//), we can simply call view.Delete instead // of logical.ClearView - index := parentPath + child - err = ts.view.Delete(ctx, index) + err = ts.parentView(tokenNS).Delete(ctx, child) if err != nil { return errwrap.Wrapf("failed to delete child entry: {{err}}", err) } @@ -1178,48 +1377,87 @@ func (ts *TokenStore) revokeSalted(ctx context.Context, saltedID string, skipOrp // revokeTree is used to invalidate a given token and all // child tokens. -func (ts *TokenStore) revokeTree(ctx context.Context, id string) error { +func (ts *TokenStore) revokeTree(ctx context.Context, le *leaseEntry) error { defer metrics.MeasureSince([]string{"token", "revoke-tree"}, time.Now()) // Verify the token is not blank - if id == "" { + if le.ClientToken == "" { return fmt.Errorf("cannot tree-revoke blank token") } - // Get the salted ID - saltedID, err := ts.SaltID(ctx, id) + // In case lookup fails for some reason for the token itself, set the + // context for the next call from the lease entry's NS. This function is + // only called when a lease for a given token is expiring, so it should run + // in the context of the token namespace + revCtx := namespace.ContextWithNamespace(ctx, le.namespace) + + saltedID, err := ts.SaltID(revCtx, le.ClientToken) if err != nil { return err } // Nuke the entire tree recursively - return ts.revokeTreeSalted(ctx, saltedID) + return ts.revokeTreeInternal(revCtx, saltedID) } -// revokeTreeSalted is used to invalidate a given token and all -// child tokens using a saltedID. +// revokeTreeInternal is used to invalidate a given token and all +// child tokens. // Updated to be non-recursive and revoke child tokens // before parent tokens(DFS). -func (ts *TokenStore) revokeTreeSalted(ctx context.Context, saltedID string) error { +func (ts *TokenStore) revokeTreeInternal(ctx context.Context, id string) error { var dfs []string - dfs = append(dfs, saltedID) + dfs = append(dfs, id) + + var ns *namespace.Namespace + + te, err := ts.lookupInternal(ctx, id, true, true) + if err != nil { + return err + } + if te == nil { + ns, err = namespace.FromContext(ctx) + if err != nil { + return err + } + } else { + ns, err = NamespaceByID(ctx, te.NamespaceID, ts.core) + if err != nil { + return err + } + } + if ns == nil { + return fmt.Errorf("failed to find namespace for token revocation") + } for l := len(dfs); l > 0; l = len(dfs) { id := dfs[0] - path := parentPrefix + id + "/" - children, err := ts.view.List(ctx, path) + + saltedCtx := ctx + saltedNS := ns + saltedID, saltedNSID := namespace.SplitIDFromString(id) + if saltedNSID != "" { + saltedNS, err = NamespaceByID(ctx, saltedNSID, ts.core) + if err != nil { + return errwrap.Wrapf("failed to find namespace for token revocation: {{err}}", err) + } + + saltedCtx = namespace.ContextWithNamespace(ctx, saltedNS) + } + + path := saltedID + "/" + children, err := ts.parentView(saltedNS).List(saltedCtx, path) if err != nil { return errwrap.Wrapf("failed to scan for children: {{err}}", err) } + // If the length of the children array is zero, // then we are at a leaf node. if len(children) == 0 { - // Whenever revokeSalted is called, the token will be removed immediately and + // Whenever revokeInternal is called, the token will be removed immediately and // any underlying secrets will be handed off to the expiration manager which will // take care of expiring them. If Vault is restarted, any revoked tokens // would have been deleted, and any pending leases for deletion will be restored // by the expiration manager. - if err := ts.revokeSalted(ctx, id, true); err != nil { - + if err := ts.revokeInternal(saltedCtx, saltedID, true); err != nil { return errwrap.Wrapf("failed to revoke entry: {{err}}", err) } // If the length of l is equal to 1, then the last token has been deleted @@ -1251,18 +1489,44 @@ func (ts *TokenStore) handleCreateAgainstRole(ctx context.Context, req *logical. return ts.handleCreateCommon(ctx, req, d, false, roleEntry) } -func (ts *TokenStore) lookupByAccessor(ctx context.Context, accessor string, tainted bool) (accessorEntry, error) { - saltedID, err := ts.SaltID(ctx, accessor) - if err != nil { - return accessorEntry{}, err - } - return ts.lookupBySaltedAccessor(ctx, saltedID, tainted) -} - -func (ts *TokenStore) lookupBySaltedAccessor(ctx context.Context, saltedAccessor string, tainted bool) (accessorEntry, error) { - entry, err := ts.view.Get(ctx, accessorPrefix+saltedAccessor) +func (ts *TokenStore) lookupByAccessor(ctx context.Context, id string, salted, tainted bool) (accessorEntry, error) { var aEntry accessorEntry + ns, err := namespace.FromContext(ctx) + if err != nil { + return aEntry, err + } + + lookupID := id + if !salted { + _, nsID := namespace.SplitIDFromString(id) + if nsID != "" { + accessorNS, err := NamespaceByID(ctx, nsID, ts.core) + if err != nil { + return aEntry, err + } + if accessorNS != nil { + if accessorNS.ID != ns.ID { + ns = accessorNS + ctx = namespace.ContextWithNamespace(ctx, accessorNS) + } + } + } else { + // Any non-root-ns token should have an accessor and child + // namespaces cannot have custom IDs. If someone omits or tampers + // with it, the lookup in the root namespace simply won't work. + ns = namespace.RootNamespace + ctx = namespace.ContextWithNamespace(ctx, ns) + } + + lookupID, err = ts.SaltID(ctx, id) + if err != nil { + return aEntry, err + } + } + + entry, err := ts.accessorView(ns).Get(ctx, lookupID) + if err != nil { return aEntry, errwrap.Wrapf("failed to read index using accessor: {{err}}", err) } @@ -1273,26 +1537,26 @@ func (ts *TokenStore) lookupBySaltedAccessor(ctx context.Context, saltedAccessor err = jsonutil.DecodeJSON(entry.Value, &aEntry) // If we hit an error, assume it's a pre-struct straight token ID if err != nil { - saltedID, err := ts.SaltID(ctx, string(entry.Value)) - if err != nil { - return accessorEntry{}, err - } - - te, err := ts.lookupSalted(ctx, saltedID, tainted) + te, err := ts.lookupInternal(ctx, string(entry.Value), false, tainted) if err != nil { return accessorEntry{}, errwrap.Wrapf("failed to look up token using accessor index: {{err}}", err) } - // It's hard to reason about what to do here -- it may be that the - // token was revoked async, or that it's an old accessor index entry - // that was somehow not cleared up, or or or. A nonexistent token entry - // on lookup is nil, not an error, so we keep that behavior here to be - // safe...the token ID is simply not filled in. + // It's hard to reason about what to do here if te is nil -- it may be + // that the token was revoked async, or that it's an old accessor index + // entry that was somehow not cleared up, or or or. A nonexistent token + // entry on lookup is nil, not an error, so we keep that behavior here + // to be safe...the token ID is simply not filled in. if te != nil { aEntry.TokenID = te.ID aEntry.AccessorID = te.Accessor + aEntry.NamespaceID = te.NamespaceID } } + if aEntry.NamespaceID == "" { + aEntry.NamespaceID = namespace.RootNamespaceID + } + return aEntry, nil } @@ -1305,14 +1569,15 @@ func (ts *TokenStore) handleTidy(ctx context.Context, req *logical.Request, data return resp, nil } + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, errwrap.Wrapf("failed get namespace from context: {{err}}", err) + } + go func() { defer atomic.StoreUint32(ts.tidyLock, 0) - // Don't cancel when the original client request goes away - ctx = ts.quitContext - logger := ts.logger.Named("tidy") - ts.core.AddLogger(logger) var tidyErrors *multierror.Error @@ -1321,14 +1586,16 @@ func (ts *TokenStore) handleTidy(ctx context.Context, req *logical.Request, data ts.logger.Info("beginning tidy operation on tokens") defer ts.logger.Info("finished tidy operation on tokens") + quitCtx := namespace.ContextWithNamespace(ts.quitContext, ns) + // List out all the accessors - saltedAccessorList, err := ts.view.List(ctx, accessorPrefix) + saltedAccessorList, err := ts.accessorView(ns).List(quitCtx, "") if err != nil { return errwrap.Wrapf("failed to fetch accessor index entries: {{err}}", err) } // First, clean up secondary index entries that are no longer valid - parentList, err := ts.view.List(ctx, parentPrefix) + parentList, err := ts.parentView(ns).List(quitCtx, "") if err != nil { return errwrap.Wrapf("failed to fetch secondary index entries: {{err}}", err) } @@ -1341,7 +1608,7 @@ func (ts *TokenStore) handleTidy(ctx context.Context, req *logical.Request, data countParentEntries++ // Get the children - children, err := ts.view.List(ctx, parentPrefix+parent) + children, err := ts.parentView(ns).List(quitCtx, parent) if err != nil { tidyErrors = multierror.Append(tidyErrors, errwrap.Wrapf("failed to read secondary index: {{err}}", err)) continue @@ -1351,7 +1618,7 @@ func (ts *TokenStore) handleTidy(ctx context.Context, req *logical.Request, data // that deletion of children later with this loop below applies to all // children originalChildrenCount := int64(len(children)) - exists, _ := ts.lookupSalted(ctx, strings.TrimSuffix(parent, "/"), true) + exists, _ := ts.lookupInternal(quitCtx, strings.TrimSuffix(parent, "/"), true, true) if exists == nil { ts.logger.Debug("deleting invalid parent prefix entry", "index", parentPrefix+parent) } @@ -1367,7 +1634,7 @@ func (ts *TokenStore) handleTidy(ctx context.Context, req *logical.Request, data // found, it doesn't exist. Doing the following without locking // since appropriate locks cannot be held with salted token IDs. // Also perform deletion if the parent doesn't exist any more. - te, _ := ts.lookupSalted(ctx, child, true) + te, _ := ts.lookupInternal(quitCtx, child, true, true) // If the child entry is not nil, but the parent doesn't exist, then turn // that child token into an orphan token. Theres no deletion in this case. if te != nil && exists == nil { @@ -1375,7 +1642,7 @@ func (ts *TokenStore) handleTidy(ctx context.Context, req *logical.Request, data lock.Lock() te.Parent = "" - err = ts.store(ctx, te) + err = ts.store(quitCtx, te) if err != nil { tidyErrors = multierror.Append(tidyErrors, errwrap.Wrapf("failed to convert child token into an orphan token: {{err}}", err)) } @@ -1385,9 +1652,9 @@ func (ts *TokenStore) handleTidy(ctx context.Context, req *logical.Request, data // Otherwise, if the entry doesn't exist, or if the parent doesn't exist go // on with the delete on the secondary index if te == nil || exists == nil { - index := parentPrefix + parent + child + index := parent + child ts.logger.Debug("deleting invalid secondary index", "index", index) - err = ts.view.Delete(ctx, index) + err = ts.parentView(ns).Delete(quitCtx, index) if err != nil { tidyErrors = multierror.Append(tidyErrors, errwrap.Wrapf("failed to delete secondary index: {{err}}", err)) continue @@ -1419,7 +1686,7 @@ func (ts *TokenStore) handleTidy(ctx context.Context, req *logical.Request, data ts.logger.Info("checking if accessors contain valid tokens", "progress", countAccessorList) } - accessorEntry, err := ts.lookupBySaltedAccessor(ctx, saltedAccessor, true) + accessorEntry, err := ts.lookupByAccessor(quitCtx, saltedAccessor, true, true) if err != nil { tidyErrors = multierror.Append(tidyErrors, errwrap.Wrapf("failed to read the accessor index: {{err}}", err)) continue @@ -1429,10 +1696,9 @@ func (ts *TokenStore) handleTidy(ctx context.Context, req *logical.Request, data // in it. If not, it is an invalid accessor entry and needs to // be deleted. if accessorEntry.TokenID == "" { - index := accessorPrefix + saltedAccessor // If deletion of accessor fails, move on to the next // item since this is just a best-effort operation - err = ts.view.Delete(ctx, index) + err = ts.accessorView(ns).Delete(quitCtx, saltedAccessor) if err != nil { tidyErrors = multierror.Append(tidyErrors, errwrap.Wrapf("failed to delete the accessor index: {{err}}", err)) continue @@ -1445,13 +1711,7 @@ func (ts *TokenStore) handleTidy(ctx context.Context, req *logical.Request, data // Look up tainted variants so we only find entries that truly don't // exist - saltedID, err := ts.SaltID(ctx, accessorEntry.TokenID) - if err != nil { - tidyErrors = multierror.Append(tidyErrors, errwrap.Wrapf("failed to read salt id: {{err}}", err)) - lock.RUnlock() - continue - } - te, err := ts.lookupSalted(ctx, saltedID, true) + te, err := ts.lookupInternal(quitCtx, accessorEntry.TokenID, false, true) if err != nil { tidyErrors = multierror.Append(tidyErrors, errwrap.Wrapf("failed to lookup tainted ID: {{err}}", err)) lock.RUnlock() @@ -1464,31 +1724,30 @@ func (ts *TokenStore) handleTidy(ctx context.Context, req *logical.Request, data // more and conclude that accessor, leases, and secondary index entries // for this token should not exist as well. if te == nil { - ts.logger.Info("deleting token with nil entry", "salted_token", saltedID) + ts.logger.Info("deleting token with nil entry referenced by accessor", "salted_accessor", saltedAccessor) // RevokeByToken expects a '*logical.TokenEntry'. For the // purposes of tidying, it is sufficient if the token // entry only has ID set. tokenEntry := &logical.TokenEntry{ - ID: accessorEntry.TokenID, + ID: accessorEntry.TokenID, + NamespaceID: accessorEntry.NamespaceID, } // Attempt to revoke the token. This will also revoke // the leases associated with the token. - err := ts.expiration.RevokeByToken(ctx, tokenEntry) + err = ts.expiration.RevokeByToken(quitCtx, tokenEntry) if err != nil { tidyErrors = multierror.Append(tidyErrors, errwrap.Wrapf("failed to revoke leases of expired token: {{err}}", err)) continue } deletedCountInvalidTokenInAccessor++ - index := accessorPrefix + saltedAccessor - // If deletion of accessor fails, move on to the next item since // this is just a best-effort operation. We do this last so that on // next run if something above failed we still have the accessor // entry to try again. - err = ts.view.Delete(ctx, index) + err = ts.accessorView(ns).Delete(quitCtx, saltedAccessor) if err != nil { tidyErrors = multierror.Append(tidyErrors, errwrap.Wrapf("failed to delete accessor entry: {{err}}", err)) continue @@ -1533,7 +1792,7 @@ func (ts *TokenStore) handleUpdateLookupAccessor(ctx context.Context, req *logic urlaccessor = true } - aEntry, err := ts.lookupByAccessor(ctx, accessor, false) + aEntry, err := ts.lookupByAccessor(ctx, accessor, false, false) if err != nil { return nil, err } @@ -1587,7 +1846,7 @@ func (ts *TokenStore) handleUpdateRevokeAccessor(ctx context.Context, req *logic urlaccessor = true } - aEntry, err := ts.lookupByAccessor(ctx, accessor, true) + aEntry, err := ts.lookupByAccessor(ctx, accessor, false, true) if err != nil { return nil, err } @@ -1596,17 +1855,25 @@ func (ts *TokenStore) handleUpdateRevokeAccessor(ctx context.Context, req *logic if err != nil { return nil, err } - if te == nil { return logical.ErrorResponse("token not found"), logical.ErrInvalidRequest } - leaseID, err := ts.expiration.CreateOrFetchRevocationLeaseByToken(ctx, te) + tokenNS, err := NamespaceByID(ctx, te.NamespaceID, ts.core) + if err != nil { + return nil, err + } + if tokenNS == nil { + return nil, namespace.ErrNoNamespace + } + + revokeCtx := namespace.ContextWithNamespace(ts.quitContext, tokenNS) + leaseID, err := ts.expiration.CreateOrFetchRevocationLeaseByToken(revokeCtx, te) if err != nil { return nil, err } - err = ts.expiration.Revoke(ts.quitContext, leaseID) + err = ts.expiration.Revoke(revokeCtx, leaseID) if err != nil { return nil, err } @@ -1673,6 +1940,33 @@ func (ts *TokenStore) handleCreateCommon(ctx context.Context, req *logical.Reque "Error decoding request: %s", err)), logical.ErrInvalidRequest } + // If the context's namespace is different from the parent and this is an + // orphan token creation request, then this is an admin token generation for + // the namespace + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } + if ns.ID != parent.NamespaceID { + parentNS, err := NamespaceByID(ctx, parent.NamespaceID, ts.core) + if err != nil { + ts.logger.Error("error looking up parent namespace", "error", err, "parent_namespace", parent.NamespaceID) + return nil, ErrInternalError + } + if parentNS == nil { + ts.logger.Error("could not find information for parent namespace", "parent_namespace", parent.NamespaceID) + return nil, ErrInternalError + } + + if !isSudo { + return logical.ErrorResponse("root or sudo privileges required generate a namespace admin token"), logical.ErrInvalidRequest + } + + if strutil.StrListContains(data.Policies, "root") { + return logical.ErrorResponse("root tokens may not be created from a parent namespace"), logical.ErrInvalidRequest + } + } + // Verify the number of uses is positive if data.NumUses < 0 { return logical.ErrorResponse("number of uses cannot be negative"), @@ -1692,6 +1986,7 @@ func (ts *TokenStore) handleCreateCommon(ctx context.Context, req *logical.Reque DisplayName: "token", NumUses: data.NumUses, CreationTime: time.Now().Unix(), + NamespaceID: ns.ID, } renewable := true @@ -1732,6 +2027,10 @@ func (ts *TokenStore) handleCreateCommon(ctx context.Context, req *logical.Reque return logical.ErrorResponse("root or sudo privileges required to specify token id"), logical.ErrInvalidRequest } + if ns.ID != namespace.RootNamespaceID { + return logical.ErrorResponse("token IDs can only be manually specified in the root namespace"), + logical.ErrInvalidRequest + } te.ID = data.ID } @@ -1805,6 +2104,11 @@ func (ts *TokenStore) handleCreateCommon(ctx context.Context, req *logical.Reque data.Policies = finalPolicies + // We are creating a token from a parent namespace. We should only use the input + // policies. + case ns.ID != parent.NamespaceID: + addDefault = !data.NoDefaultPolicy + // No policies specified, inherit parent case len(data.Policies) == 0: // Only inherit "default" if the parent already has it, so don't touch addDefault here @@ -2028,15 +2332,13 @@ func (ts *TokenStore) handleCreateCommon(ctx context.Context, req *logical.Reque CreationPath: te.Path, } - if ts.policyLookupFunc != nil { - for _, p := range te.Policies { - policy, err := ts.policyLookupFunc(p) - if err != nil { - return logical.ErrorResponse(fmt.Sprintf("could not look up policy %s", p)), nil - } - if policy == nil { - resp.AddWarning(fmt.Sprintf("Policy %q does not exist", p)) - } + for _, p := range te.Policies { + policy, err := ts.core.policyStore.GetPolicy(ctx, p, PolicyTypeToken) + if err != nil { + return logical.ErrorResponse(fmt.Sprintf("could not look up policy %s", p)), nil + } + if policy == nil { + resp.AddWarning(fmt.Sprintf("Policy %q does not exist", p)) } } @@ -2051,17 +2353,25 @@ func (ts *TokenStore) handleRevokeSelf(ctx context.Context, req *logical.Request if err != nil { return nil, err } - if te == nil { - return logical.ErrorResponse("token not found"), logical.ErrInvalidRequest + return nil, nil } - leaseID, err := ts.expiration.CreateOrFetchRevocationLeaseByToken(ctx, te) + tokenNS, err := NamespaceByID(ctx, te.NamespaceID, ts.core) + if err != nil { + return nil, err + } + if tokenNS == nil { + return nil, namespace.ErrNoNamespace + } + + revokeCtx := namespace.ContextWithNamespace(ts.quitContext, tokenNS) + leaseID, err := ts.expiration.CreateOrFetchRevocationLeaseByToken(revokeCtx, te) if err != nil { return nil, err } - err = ts.expiration.Revoke(ts.quitContext, leaseID) + err = ts.expiration.Revoke(revokeCtx, leaseID) if err != nil { return nil, err } @@ -2087,17 +2397,25 @@ func (ts *TokenStore) handleRevokeTree(ctx context.Context, req *logical.Request if err != nil { return nil, err } - if te == nil { - return logical.ErrorResponse("token not found"), logical.ErrInvalidRequest + return nil, nil } - leaseID, err := ts.expiration.CreateOrFetchRevocationLeaseByToken(ctx, te) + tokenNS, err := NamespaceByID(ctx, te.NamespaceID, ts.core) + if err != nil { + return nil, err + } + if tokenNS == nil { + return nil, namespace.ErrNoNamespace + } + + revokeCtx := namespace.ContextWithNamespace(ts.quitContext, tokenNS) + leaseID, err := ts.expiration.CreateOrFetchRevocationLeaseByToken(revokeCtx, te) if err != nil { return nil, err } - err = ts.expiration.Revoke(ts.quitContext, leaseID) + err = ts.expiration.Revoke(revokeCtx, leaseID) if err != nil { return nil, err } @@ -2126,14 +2444,6 @@ func (ts *TokenStore) handleRevokeOrphan(ctx context.Context, req *logical.Reque urltoken = true } - parent, err := ts.Lookup(ctx, req.ClientToken) - if err != nil { - return nil, errwrap.Wrapf("parent token lookup failed: {{err}}", err) - } - if parent == nil { - return logical.ErrorResponse("parent token lookup failed: no parent found"), logical.ErrInvalidRequest - } - // Check if the client token has sudo/root privileges for the requested path isSudo := ts.System().SudoPrivilege(ctx, req.MountPoint+req.Path, req.ClientToken) @@ -2142,6 +2452,16 @@ func (ts *TokenStore) handleRevokeOrphan(ctx context.Context, req *logical.Reque logical.ErrInvalidRequest } + // Do a lookup. Among other things, that will ensure that this is either + // running in the same namespace or a parent. + te, err := ts.Lookup(ctx, id) + if err != nil { + return nil, errwrap.Wrapf("error when looking up token to revoke: {{err}}", err) + } + if te == nil { + return logical.ErrorResponse("token to revoke not found"), logical.ErrInvalidRequest + } + // Revoke and orphan if err := ts.revokeOrphan(ctx, id); err != nil { return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest @@ -2183,12 +2503,7 @@ func (ts *TokenStore) handleLookup(ctx context.Context, req *logical.Request, da lock.RLock() defer lock.RUnlock() - // Lookup the token - saltedID, err := ts.SaltID(ctx, id) - if err != nil { - return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest - } - out, err := ts.lookupSalted(ctx, saltedID, true) + out, err := ts.lookupInternal(ctx, id, false, true) if err != nil { return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest } @@ -2234,8 +2549,20 @@ func (ts *TokenStore) handleLookup(ctx context.Context, req *logical.Request, da resp.Data["bound_cidrs"] = out.BoundCIDRs } + tokenNS, err := NamespaceByID(ctx, out.NamespaceID, ts.core) + if err != nil { + return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest + } + if tokenNS == nil { + return nil, namespace.ErrNoNamespace + } + + if out.NamespaceID != namespace.RootNamespaceID { + resp.Data["namespace_path"] = tokenNS.Path + } + // Fetch the last renewal time - leaseTimes, err := ts.expiration.FetchLeaseTimesByToken(ctx, out.Path, out.ID) + leaseTimes, err := ts.expiration.FetchLeaseTimesByToken(ctx, out) if err != nil { return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest } @@ -2254,12 +2581,14 @@ func (ts *TokenStore) handleLookup(ctx context.Context, req *logical.Request, da } if out.EntityID != "" { - _, identityPolicies, err := ts.identityPoliciesDeriverFunc(out.EntityID) + _, identityPolicies, err := ts.core.fetchEntityAndDerivedPolicies(ctx, tokenNS, out.EntityID) if err != nil { return nil, err } if len(identityPolicies) != 0 { - resp.Data["identity_policies"] = identityPolicies + resp.Data["identity_policies"] = identityPolicies[out.NamespaceID] + delete(identityPolicies, out.NamespaceID) + resp.Data["external_namespace_policies"] = identityPolicies } } @@ -2295,16 +2624,14 @@ func (ts *TokenStore) handleRenew(ctx context.Context, req *logical.Request, dat // Lookup the token te, err := ts.Lookup(ctx, id) if err != nil { - return nil, errwrap.Wrapf("error looking up token: {{err}}", err) + return nil, errwrap.Wrapf("error looking up token to renew: {{err}}", err) } - - // Verify the token exists if te == nil { return logical.ErrorResponse("token not found"), logical.ErrInvalidRequest } // Renew the token and its children - resp, err := ts.expiration.RenewToken(ctx, req, te.Path, te.ID, increment) + resp, err := ts.expiration.RenewToken(ctx, req, te, increment) if urltoken { resp.AddWarning(`Using a token in the path is unsafe as the token can be logged in many places. Please use POST or PUT with the token passed in via the "token" parameter.`) @@ -2313,14 +2640,6 @@ func (ts *TokenStore) handleRenew(ctx context.Context, req *logical.Request, dat return resp, err } -func (ts *TokenStore) destroyCubbyhole(ctx context.Context, saltedID string) error { - if ts.cubbyholeBackend == nil { - // Should only ever happen in testing - return nil - } - return ts.cubbyholeBackend.revoke(ctx, salt.SaltID(ts.cubbyholeBackend.saltUUID, saltedID, salt.SHA1Hash)) -} - func (ts *TokenStore) authRenew(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { if req.Auth == nil { return nil, fmt.Errorf("request auth is nil") @@ -2354,7 +2673,12 @@ func (ts *TokenStore) authRenew(ctx context.Context, req *logical.Request, d *fr } func (ts *TokenStore) tokenStoreRole(ctx context.Context, name string) (*tsRoleEntry, error) { - entry, err := ts.view.Get(ctx, fmt.Sprintf("%s%s", rolesPrefix, name)) + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } + + entry, err := ts.rolesView(ns).Get(ctx, name) if err != nil { return nil, err } @@ -2371,7 +2695,12 @@ func (ts *TokenStore) tokenStoreRole(ctx context.Context, name string) (*tsRoleE } func (ts *TokenStore) tokenStoreRoleList(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { - entries, err := ts.view.List(ctx, rolesPrefix) + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } + + entries, err := ts.rolesView(ns).List(ctx, "") if err != nil { return nil, err } @@ -2385,7 +2714,12 @@ func (ts *TokenStore) tokenStoreRoleList(ctx context.Context, req *logical.Reque } func (ts *TokenStore) tokenStoreRoleDelete(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) { - err := ts.view.Delete(ctx, fmt.Sprintf("%s%s", rolesPrefix, data.Get("role_name").(string))) + ns, err := namespace.FromContext(ctx) + if err != nil { + return nil, err + } + + err = ts.rolesView(ns).Delete(ctx, data.Get("role_name").(string)) if err != nil { return nil, err } @@ -2549,12 +2883,17 @@ func (ts *TokenStore) tokenStoreRoleCreateUpdate(ctx context.Context, req *logic entry.DisallowedPolicies = strutil.RemoveDuplicates(data.Get("disallowed_policies").([]string), true) } - // Store it - jsonEntry, err := logical.StorageEntryJSON(fmt.Sprintf("%s%s", rolesPrefix, name), entry) + ns, err := namespace.FromContext(ctx) if err != nil { return nil, err } - if err := ts.view.Put(ctx, jsonEntry); err != nil { + + // Store it + jsonEntry, err := logical.StorageEntryJSON(name, entry) + if err != nil { + return nil, err + } + if err := ts.rolesView(ns).Put(ctx, jsonEntry); err != nil { return nil, err } diff --git a/vault/token_store_test.go b/vault/token_store_test.go index 0815612e23..49a7c7150b 100644 --- a/vault/token_store_test.go +++ b/vault/token_store_test.go @@ -16,6 +16,7 @@ import ( hclog "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-uuid" "github.com/hashicorp/vault/helper/locksutil" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/logical" ) @@ -60,17 +61,16 @@ func TestTokenStore_TokenEntryUpgrade(t *testing.T) { t.Fatal(err) } - saltedID, err := ts.SaltID(context.Background(), entry.ID) + saltedID, err := ts.SaltID(namespace.TestContext(), entry.ID) if err != nil { t.Fatal(err) } - path := lookupPrefix + saltedID le := &logical.StorageEntry{ - Key: path, + Key: saltedID, Value: enc, } - if err := ts.view.Put(context.Background(), le); err != nil { + if err := ts.idView(namespace.RootNamespace).Put(namespace.TestContext(), le); err != nil { t.Fatal(err) } @@ -86,11 +86,22 @@ func TestTokenStore_TokenEntryUpgrade(t *testing.T) { }, ClientToken: entry.ID, } - if err := ts.expiration.RegisterAuth(context.Background(), entry.Path, auth); err != nil { + // Same as entry from TokenEntryOld, but used for RegisterAuth + registryEntry := &logical.TokenEntry{ + DisplayName: entry.DisplayName, + Path: entry.Path, + Policies: entry.Policies, + CreationTime: entry.CreationTime, + ExplicitMaxTTL: entry.ExplicitMaxTTL, + NumUses: entry.NumUses, + NamespaceID: namespace.RootNamespaceID, + } + + if err := ts.expiration.RegisterAuth(namespace.TestContext(), registryEntry, auth); err != nil { t.Fatal(err) } - out, err := ts.Lookup(context.Background(), entry.ID) + out, err := ts.Lookup(namespace.TestContext(), entry.ID) if err != nil { t.Fatalf("err: %s", err) } @@ -115,8 +126,9 @@ func TestTokenStore_TokenEntryUpgrade(t *testing.T) { CreationTime: time.Now().Unix(), ExplicitMaxTTL: 100, NumUses: 10, + NamespaceID: namespace.RootNamespaceID, } - if err := ts.create(context.Background(), ent); err != nil { + if err := ts.create(namespace.TestContext(), ent); err != nil { t.Fatalf("err: %s", err) } auth = &logical.Auth{ @@ -130,11 +142,11 @@ func TestTokenStore_TokenEntryUpgrade(t *testing.T) { }, ClientToken: ent.ID, } - if err := ts.expiration.RegisterAuth(context.Background(), ent.Path, auth); err != nil { + if err := ts.expiration.RegisterAuth(namespace.TestContext(), ent, auth); err != nil { t.Fatal(err) } - out, err = ts.Lookup(context.Background(), ent.ID) + out, err = ts.Lookup(namespace.TestContext(), ent.ID) if err != nil { t.Fatalf("err: %s", err) } @@ -159,8 +171,9 @@ func TestTokenStore_TokenEntryUpgrade(t *testing.T) { CreationTimeDeprecated: time.Now().Unix(), ExplicitMaxTTLDeprecated: 100, NumUsesDeprecated: 10, + NamespaceID: namespace.RootNamespaceID, } - if err := ts.create(context.Background(), ent); err != nil { + if err := ts.create(namespace.TestContext(), ent); err != nil { t.Fatalf("err: %s", err) } auth = &logical.Auth{ @@ -174,11 +187,11 @@ func TestTokenStore_TokenEntryUpgrade(t *testing.T) { }, ClientToken: ent.ID, } - if err := ts.expiration.RegisterAuth(context.Background(), ent.Path, auth); err != nil { + if err := ts.expiration.RegisterAuth(namespace.TestContext(), ent, auth); err != nil { t.Fatal(err) } - out, err = ts.Lookup(context.Background(), ent.ID) + out, err = ts.Lookup(namespace.TestContext(), ent.ID) if err != nil { t.Fatalf("err: %s", err) } @@ -200,8 +213,9 @@ func TestTokenStore_TokenEntryUpgrade(t *testing.T) { Path: "test", NumUses: 5, NumUsesDeprecated: 10, + NamespaceID: namespace.RootNamespaceID, } - if err := ts.create(context.Background(), ent); err != nil { + if err := ts.create(namespace.TestContext(), ent); err != nil { t.Fatalf("err: %s", err) } auth = &logical.Auth{ @@ -215,11 +229,11 @@ func TestTokenStore_TokenEntryUpgrade(t *testing.T) { }, ClientToken: ent.ID, } - if err := ts.expiration.RegisterAuth(context.Background(), ent.Path, auth); err != nil { + if err := ts.expiration.RegisterAuth(namespace.TestContext(), ent, auth); err != nil { t.Fatal(err) } - out, err = ts.Lookup(context.Background(), ent.ID) + out, err = ts.Lookup(namespace.TestContext(), ent.ID) if err != nil { t.Fatalf("err: %s", err) } @@ -233,8 +247,9 @@ func TestTokenStore_TokenEntryUpgrade(t *testing.T) { Path: "test", NumUses: 10, NumUsesDeprecated: 5, + NamespaceID: namespace.RootNamespaceID, } - if err := ts.create(context.Background(), ent); err != nil { + if err := ts.create(namespace.TestContext(), ent); err != nil { t.Fatalf("err: %s", err) } auth = &logical.Auth{ @@ -248,11 +263,11 @@ func TestTokenStore_TokenEntryUpgrade(t *testing.T) { }, ClientToken: ent.ID, } - if err := ts.expiration.RegisterAuth(context.Background(), ent.Path, auth); err != nil { + if err := ts.expiration.RegisterAuth(namespace.TestContext(), ent, auth); err != nil { t.Fatal(err) } - out, err = ts.Lookup(context.Background(), ent.ID) + out, err = ts.Lookup(namespace.TestContext(), ent.ID) if err != nil { t.Fatalf("err: %s", err) } @@ -285,7 +300,7 @@ func testMakeTokenViaBackend(t testing.TB, ts *TokenStore, root, client, ttl str } func testMakeTokenViaRequest(t testing.TB, ts *TokenStore, req *logical.Request) *logical.Response { - resp, err := ts.HandleRequest(context.Background(), req) + resp, err := ts.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -294,15 +309,31 @@ func testMakeTokenViaRequest(t testing.TB, ts *TokenStore, req *logical.Request) t.Fatalf("got nil token from create call") } - if err := ts.expiration.RegisterAuth(context.Background(), resp.Auth.CreationPath, resp.Auth); err != nil { + te := &logical.TokenEntry{ + Path: resp.Auth.CreationPath, + NamespaceID: namespace.RootNamespaceID, + } + + if err := ts.expiration.RegisterAuth(namespace.TestContext(), te, resp.Auth); err != nil { t.Fatal(err) } + te, err = ts.Lookup(namespace.TestContext(), resp.Auth.ClientToken) + if err != nil { + t.Fatal(err) + } + if te == nil { + t.Fatal("token entry was nil") + } + return resp } func testMakeTokenDirectly(t testing.TB, ts *TokenStore, te *logical.TokenEntry) { - if err := ts.create(context.Background(), te); err != nil { + if te.NamespaceID == "" { + te.NamespaceID = namespace.RootNamespaceID + } + if err := ts.create(namespace.RootContext(nil), te); err != nil { t.Fatal(err) } auth := &logical.Auth{ @@ -321,7 +352,7 @@ func testMakeTokenDirectly(t testing.TB, ts *TokenStore, te *logical.TokenEntry) ExplicitMaxTTL: te.ExplicitMaxTTL, CreationPath: te.Path, } - if err := ts.expiration.RegisterAuth(context.Background(), te.Path, auth); err != nil { + if err := ts.expiration.RegisterAuth(namespace.TestContext(), te, auth); err != nil { t.Fatal(err) } } @@ -333,7 +364,7 @@ func testMakeTokenViaCore(t testing.TB, c *Core, root, client, ttl string, polic req.Data["policies"] = policy req.Data["ttl"] = ttl - resp, err := c.HandleRequest(context.Background(), req) + resp, err := c.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -347,13 +378,14 @@ func TestTokenStore_AccessorIndex(t *testing.T) { ts := c.tokenStore ent := &logical.TokenEntry{ - Path: "test", - Policies: []string{"dev", "ops"}, - TTL: time.Hour, + Path: "test", + Policies: []string{"dev", "ops"}, + TTL: time.Hour, + NamespaceID: namespace.RootNamespaceID, } testMakeTokenDirectly(t, ts, ent) - out, err := ts.Lookup(context.Background(), ent.ID) + out, err := ts.Lookup(namespace.TestContext(), ent.ID) if err != nil { t.Fatalf("err: %s", err) } @@ -363,7 +395,7 @@ func TestTokenStore_AccessorIndex(t *testing.T) { t.Fatalf("bad: %#v", out) } - aEntry, err := ts.lookupByAccessor(context.Background(), out.Accessor, false) + aEntry, err := ts.lookupByAccessor(namespace.TestContext(), out.Accessor, false, false) if err != nil { t.Fatalf("err: %s", err) } @@ -379,7 +411,7 @@ func TestTokenStore_HandleRequest_LookupAccessor(t *testing.T) { ts := c.tokenStore testMakeTokenViaBackend(t, ts, root, "tokenid", "", []string{"foo"}) - out, err := ts.Lookup(context.Background(), "tokenid") + out, err := ts.Lookup(namespace.TestContext(), "tokenid") if err != nil { t.Fatalf("err: %s", err) } @@ -392,7 +424,7 @@ func TestTokenStore_HandleRequest_LookupAccessor(t *testing.T) { "accessor": out.Accessor, } - resp, err := ts.HandleRequest(context.Background(), req) + resp, err := ts.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %s", err) } @@ -420,15 +452,15 @@ func TestTokenStore_HandleRequest_ListAccessors(t *testing.T) { } // Revoke root to make the number of accessors match - salted, err := ts.SaltID(context.Background(), root) + salted, err := ts.SaltID(namespace.TestContext(), root) if err != nil { t.Fatal(err) } - ts.revokeSalted(context.Background(), salted, false) + ts.revokeInternal(namespace.TestContext(), salted, false) req := logical.TestRequest(t, logical.ListOperation, "accessors/") - resp, err := ts.HandleRequest(context.Background(), req) + resp, err := ts.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %s", err) } @@ -448,26 +480,25 @@ func TestTokenStore_HandleRequest_ListAccessors(t *testing.T) { // Test upgrade from old struct method of accessor storage (of token id) for _, accessor := range keys { - aEntry, err := ts.lookupByAccessor(context.Background(), accessor, false) + aEntry, err := ts.lookupByAccessor(namespace.TestContext(), accessor, false, false) if err != nil { t.Fatal(err) } if aEntry.TokenID == "" || aEntry.AccessorID == "" { t.Fatalf("error, accessor entry looked up is empty, but no error thrown") } - salted, err := ts.SaltID(context.Background(), accessor) + saltID, err := ts.SaltID(namespace.TestContext(), accessor) if err != nil { t.Fatal(err) } - path := accessorPrefix + salted - le := &logical.StorageEntry{Key: path, Value: []byte(aEntry.TokenID)} - if err := ts.view.Put(context.Background(), le); err != nil { + le := &logical.StorageEntry{Key: saltID, Value: []byte(aEntry.TokenID)} + if err := ts.accessorView(namespace.RootNamespace).Put(namespace.TestContext(), le); err != nil { t.Fatalf("failed to persist accessor index entry: %v", err) } } // Do the lookup again, should get same result - resp, err = ts.HandleRequest(context.Background(), req) + resp, err = ts.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %s", err) } @@ -486,7 +517,7 @@ func TestTokenStore_HandleRequest_ListAccessors(t *testing.T) { } for _, accessor := range keys2 { - aEntry, err := ts.lookupByAccessor(context.Background(), accessor, false) + aEntry, err := ts.lookupByAccessor(namespace.TestContext(), accessor, false, false) if err != nil { t.Fatal(err) } @@ -500,7 +531,7 @@ func TestTokenStore_HandleRequest_RevokeAccessor(t *testing.T) { exp := mockExpiration(t) ts := exp.tokenStore - rootToken, err := ts.rootToken(context.Background()) + rootToken, err := ts.rootToken(namespace.TestContext()) root := rootToken.ID testMakeTokenViaBackend(t, ts, root, "tokenid", "", []string{"foo"}) @@ -512,12 +543,21 @@ func TestTokenStore_HandleRequest_RevokeAccessor(t *testing.T) { Renewable: true, }, } - err = exp.RegisterAuth(context.Background(), "auth/token/create", auth) + + te, err := ts.Lookup(namespace.TestContext(), "tokenid") + if err != nil { + t.Fatal(err) + } + if te == nil { + t.Fatal("token entry was nil") + } + + err = exp.RegisterAuth(namespace.TestContext(), te, auth) if err != nil { t.Fatalf("err: %v", err) } - out, err := ts.Lookup(context.Background(), "tokenid") + out, err := ts.Lookup(namespace.TestContext(), "tokenid") if err != nil { t.Fatalf("err: %s", err) } @@ -530,14 +570,14 @@ func TestTokenStore_HandleRequest_RevokeAccessor(t *testing.T) { "accessor": out.Accessor, } - _, err = ts.HandleRequest(context.Background(), req) + _, err = ts.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %s", err) } time.Sleep(200 * time.Millisecond) - out, err = ts.Lookup(context.Background(), "tokenid") + out, err = ts.Lookup(namespace.TestContext(), "tokenid") if err != nil { t.Fatalf("err: %s", err) } @@ -548,7 +588,7 @@ func TestTokenStore_HandleRequest_RevokeAccessor(t *testing.T) { // Now test without registering the token through the expiration manager testMakeTokenViaBackend(t, ts, root, "tokenid", "", []string{"foo"}) - out, err = ts.Lookup(context.Background(), "tokenid") + out, err = ts.Lookup(namespace.TestContext(), "tokenid") if err != nil { t.Fatalf("err: %s", err) } @@ -561,14 +601,14 @@ func TestTokenStore_HandleRequest_RevokeAccessor(t *testing.T) { "accessor": out.Accessor, } - _, err = ts.HandleRequest(context.Background(), req) + _, err = ts.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %s", err) } time.Sleep(200 * time.Millisecond) - out, err = ts.Lookup(context.Background(), "tokenid") + out, err = ts.Lookup(namespace.TestContext(), "tokenid") if err != nil { t.Fatalf("err: %s", err) } @@ -582,7 +622,7 @@ func TestTokenStore_RootToken(t *testing.T) { c, _, _ := TestCoreUnsealed(t) ts := c.tokenStore - te, err := ts.rootToken(context.Background()) + te, err := ts.rootToken(namespace.TestContext()) if err != nil { t.Fatalf("err: %v", err) } @@ -590,7 +630,7 @@ func TestTokenStore_RootToken(t *testing.T) { t.Fatalf("missing ID") } - out, err := ts.Lookup(context.Background(), te.ID) + out, err := ts.Lookup(namespace.TestContext(), te.ID) if err != nil { t.Fatalf("err: %v", err) } @@ -604,16 +644,17 @@ func TestTokenStore_CreateLookup(t *testing.T) { ts := c.tokenStore ent := &logical.TokenEntry{ - Path: "test", - Policies: []string{"dev", "ops"}, - TTL: time.Hour, + NamespaceID: namespace.RootNamespaceID, + Path: "test", + Policies: []string{"dev", "ops"}, + TTL: time.Hour, } testMakeTokenDirectly(t, ts, ent) if ent.ID == "" { t.Fatalf("missing ID") } - out, err := ts.Lookup(context.Background(), ent.ID) + out, err := ts.Lookup(namespace.TestContext(), ent.ID) if err != nil { t.Fatalf("err: %v", err) } @@ -622,14 +663,14 @@ func TestTokenStore_CreateLookup(t *testing.T) { } // New store should share the salt - ts2, err := NewTokenStore(context.Background(), hclog.New(&hclog.LoggerOptions{}), c, getBackendConfig(c)) + ts2, err := NewTokenStore(namespace.TestContext(), hclog.New(&hclog.LoggerOptions{}), c, getBackendConfig(c)) if err != nil { t.Fatalf("err: %v", err) } ts2.SetExpirationManager(c.expiration) // Should still match - out, err = ts2.Lookup(context.Background(), ent.ID) + out, err = ts2.Lookup(namespace.TestContext(), ent.ID) if err != nil { t.Fatalf("err: %v", err) } @@ -643,20 +684,21 @@ func TestTokenStore_CreateLookup_ProvidedID(t *testing.T) { ts := c.tokenStore ent := &logical.TokenEntry{ - ID: "foobarbaz", - Path: "test", - Policies: []string{"dev", "ops"}, - TTL: time.Hour, + ID: "foobarbaz", + NamespaceID: namespace.RootNamespaceID, + Path: "test", + Policies: []string{"dev", "ops"}, + TTL: time.Hour, } testMakeTokenDirectly(t, ts, ent) if ent.ID != "foobarbaz" { t.Fatalf("bad: ent.ID: expected:\"foobarbaz\"\n actual:%s", ent.ID) } - if err := ts.create(context.Background(), ent); err == nil { + if err := ts.create(namespace.TestContext(), ent); err == nil { t.Fatal("expected error creating token with the same ID") } - out, err := ts.Lookup(context.Background(), ent.ID) + out, err := ts.Lookup(namespace.TestContext(), ent.ID) if err != nil { t.Fatalf("err: %v", err) } @@ -665,14 +707,14 @@ func TestTokenStore_CreateLookup_ProvidedID(t *testing.T) { } // New store should share the salt - ts2, err := NewTokenStore(context.Background(), hclog.New(&hclog.LoggerOptions{}), c, getBackendConfig(c)) + ts2, err := NewTokenStore(namespace.TestContext(), hclog.New(&hclog.LoggerOptions{}), c, getBackendConfig(c)) if err != nil { t.Fatalf("err: %v", err) } ts2.SetExpirationManager(c.expiration) // Should still match - out, err = ts2.Lookup(context.Background(), ent.ID) + out, err = ts2.Lookup(namespace.TestContext(), ent.ID) if err != nil { t.Fatalf("err: %v", err) } @@ -685,8 +727,12 @@ func TestTokenStore_CreateLookup_ExpirationInRestoreMode(t *testing.T) { c, _, _ := TestCoreUnsealed(t) ts := c.tokenStore - ent := &logical.TokenEntry{Path: "test", Policies: []string{"dev", "ops"}} - if err := ts.create(context.Background(), ent); err != nil { + ent := &logical.TokenEntry{ + NamespaceID: namespace.RootNamespaceID, + Path: "test", + Policies: []string{"dev", "ops"}, + } + if err := ts.create(namespace.TestContext(), ent); err != nil { t.Fatalf("err: %v", err) } if ent.ID == "" { @@ -694,7 +740,7 @@ func TestTokenStore_CreateLookup_ExpirationInRestoreMode(t *testing.T) { } // Replace the lease with a lease with an expire time in the past - saltedID, err := ts.SaltID(context.Background(), ent.ID) + saltedID, err := ts.SaltID(namespace.TestContext(), ent.ID) if err != nil { t.Fatalf("err: %v", err) } @@ -707,12 +753,13 @@ func TestTokenStore_CreateLookup_ExpirationInRestoreMode(t *testing.T) { Path: ent.Path, IssueTime: time.Now(), ExpireTime: time.Now().Add(1 * time.Hour), + namespace: namespace.RootNamespace, } - if err := ts.expiration.persistEntry(context.Background(), le); err != nil { + if err := ts.expiration.persistEntry(namespace.TestContext(), le); err != nil { t.Fatalf("err: %v", err) } - out, err := ts.Lookup(context.Background(), ent.ID) + out, err := ts.Lookup(namespace.TestContext(), ent.ID) if err != nil { t.Fatalf("err: %v", err) } @@ -722,7 +769,7 @@ func TestTokenStore_CreateLookup_ExpirationInRestoreMode(t *testing.T) { // Set to expired lease time le.ExpireTime = time.Now().Add(-1 * time.Hour) - if err := ts.expiration.persistEntry(context.Background(), le); err != nil { + if err := ts.expiration.persistEntry(namespace.TestContext(), le); err != nil { t.Fatalf("err: %v", err) } @@ -739,7 +786,7 @@ func TestTokenStore_CreateLookup_ExpirationInRestoreMode(t *testing.T) { // Test that the token lookup does not return the token entry due to the // expired lease - out, err = ts.Lookup(context.Background(), ent.ID) + out, err = ts.Lookup(namespace.TestContext(), ent.ID) if err != nil { t.Fatalf("err: %v", err) } @@ -753,13 +800,13 @@ func TestTokenStore_UseToken(t *testing.T) { ts := c.tokenStore // Lookup the root token - ent, err := ts.Lookup(context.Background(), root) + ent, err := ts.Lookup(namespace.TestContext(), root) if err != nil { t.Fatalf("err: %v", err) } // Root is an unlimited use token, should be a no-op - te, err := ts.UseToken(context.Background(), ent) + te, err := ts.UseToken(namespace.TestContext(), ent) if err != nil { t.Fatalf("err: %v", err) } @@ -768,7 +815,7 @@ func TestTokenStore_UseToken(t *testing.T) { } // Lookup the root token again - ent2, err := ts.Lookup(context.Background(), root) + ent2, err := ts.Lookup(namespace.TestContext(), root) if err != nil { t.Fatalf("err: %v", err) } @@ -779,15 +826,16 @@ func TestTokenStore_UseToken(t *testing.T) { // Create a restricted token ent = &logical.TokenEntry{ - Path: "test", - Policies: []string{"dev", "ops"}, - NumUses: 2, - TTL: time.Hour, + Path: "test", + Policies: []string{"dev", "ops"}, + NumUses: 2, + TTL: time.Hour, + NamespaceID: namespace.RootNamespaceID, } testMakeTokenDirectly(t, ts, ent) // Use the token - te, err = ts.UseToken(context.Background(), ent) + te, err = ts.UseToken(namespace.TestContext(), ent) if err != nil { t.Fatalf("err: %v", err) } @@ -796,7 +844,7 @@ func TestTokenStore_UseToken(t *testing.T) { } // Lookup the token - ent2, err = ts.Lookup(context.Background(), ent.ID) + ent2, err = ts.Lookup(namespace.TestContext(), ent.ID) if err != nil { t.Fatalf("err: %v", err) } @@ -807,7 +855,7 @@ func TestTokenStore_UseToken(t *testing.T) { } // Use the token - te, err = ts.UseToken(context.Background(), ent) + te, err = ts.UseToken(namespace.TestContext(), ent) if err != nil { t.Fatalf("err: %v", err) } @@ -817,10 +865,10 @@ func TestTokenStore_UseToken(t *testing.T) { if te.NumUses != tokenRevocationPending { t.Fatalf("token entry after use #2 did not have revoke flag") } - ts.revokeOrphan(context.Background(), te.ID) + ts.revokeOrphan(namespace.TestContext(), te.ID) // Lookup the token - ent2, err = ts.Lookup(context.Background(), ent.ID) + ent2, err = ts.Lookup(namespace.TestContext(), ent.ID) if err != nil { t.Fatalf("err: %v", err) } @@ -835,21 +883,21 @@ func TestTokenStore_Revoke(t *testing.T) { c, _, _ := TestCoreUnsealed(t) ts := c.tokenStore - ent := &logical.TokenEntry{Path: "test", Policies: []string{"dev", "ops"}} - if err := ts.create(context.Background(), ent); err != nil { + ent := &logical.TokenEntry{Path: "test", Policies: []string{"dev", "ops"}, NamespaceID: namespace.RootNamespaceID} + if err := ts.create(namespace.TestContext(), ent); err != nil { t.Fatalf("err: %v", err) } - err := ts.revokeOrphan(context.Background(), "") + err := ts.revokeOrphan(namespace.TestContext(), "") if err.Error() != "cannot revoke blank token" { t.Fatalf("err: %v", err) } - err = ts.revokeOrphan(context.Background(), ent.ID) + err = ts.revokeOrphan(namespace.TestContext(), ent.ID) if err != nil { t.Fatalf("err: %v", err) } - out, err := ts.Lookup(context.Background(), ent.ID) + out, err := ts.Lookup(namespace.TestContext(), ent.ID) if err != nil { t.Fatalf("err: %v", err) } @@ -866,13 +914,13 @@ func TestTokenStore_Revoke_Leases(t *testing.T) { // Mount a noop backend noop := &NoopBackend{} - err := ts.expiration.router.Mount(noop, "noop/", &MountEntry{UUID: "noopuuid", Accessor: "noopaccessor"}, view) + err := ts.expiration.router.Mount(noop, "noop/", &MountEntry{UUID: "noopuuid", Accessor: "noopaccessor", namespace: namespace.RootNamespace}, view) if err != nil { t.Fatal(err) } - ent := &logical.TokenEntry{Path: "test", Policies: []string{"dev", "ops"}} - if err := ts.create(context.Background(), ent); err != nil { + ent := &logical.TokenEntry{Path: "test", Policies: []string{"dev", "ops"}, NamespaceID: namespace.RootNamespaceID} + if err := ts.create(namespace.TestContext(), ent); err != nil { t.Fatalf("err: %v", err) } @@ -893,13 +941,13 @@ func TestTokenStore_Revoke_Leases(t *testing.T) { "secret_key": "abcd", }, } - leaseID, err := ts.expiration.Register(context.Background(), req, resp) + leaseID, err := ts.expiration.Register(namespace.TestContext(), req, resp) if err != nil { t.Fatalf("err: %v", err) } // Revoke the token - err = ts.revokeOrphan(context.Background(), ent.ID) + err = ts.revokeOrphan(namespace.TestContext(), ent.ID) if err != nil { t.Fatalf("err: %v", err) } @@ -907,7 +955,7 @@ func TestTokenStore_Revoke_Leases(t *testing.T) { time.Sleep(200 * time.Millisecond) // Verify the lease is gone - out, err := ts.expiration.loadEntry(context.Background(), leaseID) + out, err := ts.expiration.loadEntry(namespace.TestContext(), leaseID) if err != nil { t.Fatalf("err: %v", err) } @@ -921,24 +969,26 @@ func TestTokenStore_Revoke_Orphan(t *testing.T) { ts := c.tokenStore ent := &logical.TokenEntry{ - Path: "test", - Policies: []string{"dev", "ops"}, - TTL: time.Hour, + NamespaceID: namespace.RootNamespaceID, + Path: "test", + Policies: []string{"dev", "ops"}, + TTL: time.Hour, } testMakeTokenDirectly(t, ts, ent) ent2 := &logical.TokenEntry{ - Parent: ent.ID, - TTL: time.Hour, + NamespaceID: namespace.RootNamespaceID, + Parent: ent.ID, + TTL: time.Hour, } testMakeTokenDirectly(t, ts, ent2) - err := ts.revokeOrphan(context.Background(), ent.ID) + err := ts.revokeOrphan(namespace.TestContext(), ent.ID) if err != nil { t.Fatalf("err: %v", err) } - out, err := ts.Lookup(context.Background(), ent2.ID) + out, err := ts.Lookup(namespace.TestContext(), ent2.ID) if err != nil { t.Fatalf("err: %v", err) } @@ -947,7 +997,7 @@ func TestTokenStore_Revoke_Orphan(t *testing.T) { ent2.Parent = "" if !reflect.DeepEqual(out, ent2) { - t.Fatalf("bad: expected:%#v\nactual:%#v", ent2, out) + t.Fatalf("bad:\nexpected:%#v\nactual:%#v", ent2, out) } } @@ -965,23 +1015,33 @@ func testTokenStore_RevokeTree_NonRecursive(t testing.TB, depth uint64) { c, _, _ := TestCoreUnsealed(t) ts := c.tokenStore root, children := buildTokenTree(t, ts, depth) - err := ts.revokeTree(context.Background(), "") - + err := ts.revokeTree(namespace.TestContext(), &leaseEntry{}) if err.Error() != "cannot tree-revoke blank token" { - t.Fatalf("err: %v", err) + t.Fatal(err) + } + + saltCtx := namespace.TestContext() + saltedID, err := c.tokenStore.SaltID(saltCtx, root.ID) + if err != nil { + t.Fatal(err) + } + tokenLeaseID := path.Join(root.Path, saltedID) + + tokenLease, err := ts.expiration.loadEntry(namespace.TestContext(), tokenLeaseID) + if err != nil || tokenLease == nil { + t.Fatalf("err: %v, tokenLease: %#v", err, tokenLease) } // Nuke tree non recursively. - err = ts.revokeTree(context.Background(), root.ID) - + err = ts.revokeTree(namespace.TestContext(), tokenLease) if err != nil { - t.Fatalf("err: %v", err) + t.Fatal(err) } // Append the root to ensure it was successfully // deleted. children = append(children, root) for _, entry := range children { - out, err := ts.Lookup(context.Background(), entry.ID) + out, err := ts.Lookup(namespace.TestContext(), entry.ID) if err != nil { t.Fatalf("err: %v", err) } @@ -1008,7 +1068,8 @@ func BenchmarkTokenStore_RevokeTree(b *testing.B) { // we may run revoke tests on it. func buildTokenTree(t testing.TB, ts *TokenStore, depth uint64) (root *logical.TokenEntry, children []*logical.TokenEntry) { root = &logical.TokenEntry{ - TTL: time.Hour, + NamespaceID: namespace.RootNamespaceID, + TTL: time.Hour, } testMakeTokenDirectly(t, ts, root) @@ -1018,14 +1079,16 @@ func buildTokenTree(t testing.TB, ts *TokenStore, depth uint64) (root *logical.T next := make([]*logical.TokenEntry, 0, 2*len(frontier)) for _, node := range frontier { left := &logical.TokenEntry{ - Parent: node.ID, - TTL: time.Hour, + Parent: node.ID, + TTL: time.Hour, + NamespaceID: namespace.RootNamespaceID, } testMakeTokenDirectly(t, ts, left) right := &logical.TokenEntry{ - Parent: node.ID, - TTL: time.Hour, + Parent: node.ID, + TTL: time.Hour, + NamespaceID: namespace.RootNamespaceID, } testMakeTokenDirectly(t, ts, right) @@ -1044,32 +1107,36 @@ func TestTokenStore_RevokeSelf(t *testing.T) { ts := exp.tokenStore ent1 := &logical.TokenEntry{ - TTL: time.Hour, + TTL: time.Hour, + NamespaceID: namespace.RootNamespaceID, } testMakeTokenDirectly(t, ts, ent1) ent2 := &logical.TokenEntry{ - Parent: ent1.ID, - TTL: time.Hour, + Parent: ent1.ID, + TTL: time.Hour, + NamespaceID: namespace.RootNamespaceID, } testMakeTokenDirectly(t, ts, ent2) ent3 := &logical.TokenEntry{ - Parent: ent2.ID, - TTL: time.Hour, + Parent: ent2.ID, + TTL: time.Hour, + NamespaceID: namespace.RootNamespaceID, } testMakeTokenDirectly(t, ts, ent3) ent4 := &logical.TokenEntry{ - Parent: ent2.ID, - TTL: time.Hour, + Parent: ent2.ID, + TTL: time.Hour, + NamespaceID: namespace.RootNamespaceID, } testMakeTokenDirectly(t, ts, ent4) req := logical.TestRequest(t, logical.UpdateOperation, "revoke-self") req.ClientToken = ent1.ID - resp, err := ts.HandleRequest(context.Background(), req) + resp, err := ts.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -1079,7 +1146,7 @@ func TestTokenStore_RevokeSelf(t *testing.T) { for _, id := range lookup { var found bool for i := 0; i < 10; i++ { - out, err = ts.Lookup(context.Background(), id) + out, err = ts.Lookup(namespace.TestContext(), id) if err != nil { t.Fatalf("err: %v", err) } @@ -1103,14 +1170,14 @@ func TestTokenStore_HandleRequest_NonAssignable(t *testing.T) { req.ClientToken = root req.Data["policies"] = []string{"default", "foo"} - resp, err := ts.HandleRequest(context.Background(), req) + resp, err := ts.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } req.Data["policies"] = []string{"default", "foo", responseWrappingPolicyName} - resp, err = ts.HandleRequest(context.Background(), req) + resp, err = ts.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatal(err) } @@ -1130,13 +1197,14 @@ func TestTokenStore_HandleRequest_CreateToken_DisplayName(t *testing.T) { req.ClientToken = root req.Data["display_name"] = "foo_bar.baz!" - resp, err := ts.HandleRequest(context.Background(), req) + resp, err := ts.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } expected := &logical.TokenEntry{ ID: resp.Auth.ClientToken, + NamespaceID: namespace.RootNamespaceID, Accessor: resp.Auth.Accessor, Parent: root, Policies: []string{"root"}, @@ -1144,7 +1212,7 @@ func TestTokenStore_HandleRequest_CreateToken_DisplayName(t *testing.T) { DisplayName: "token-foo-bar-baz", TTL: 0, } - out, err := ts.Lookup(context.Background(), resp.Auth.ClientToken) + out, err := ts.Lookup(namespace.TestContext(), resp.Auth.ClientToken) if err != nil { t.Fatalf("err: %v", err) } @@ -1162,13 +1230,14 @@ func TestTokenStore_HandleRequest_CreateToken_NumUses(t *testing.T) { req.ClientToken = root req.Data["num_uses"] = "1" - resp, err := ts.HandleRequest(context.Background(), req) + resp, err := ts.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } expected := &logical.TokenEntry{ ID: resp.Auth.ClientToken, + NamespaceID: namespace.RootNamespaceID, Accessor: resp.Auth.Accessor, Parent: root, Policies: []string{"root"}, @@ -1177,7 +1246,7 @@ func TestTokenStore_HandleRequest_CreateToken_NumUses(t *testing.T) { NumUses: 1, TTL: 0, } - out, err := ts.Lookup(context.Background(), resp.Auth.ClientToken) + out, err := ts.Lookup(namespace.TestContext(), resp.Auth.ClientToken) if err != nil { t.Fatalf("err: %v", err) } @@ -1195,7 +1264,7 @@ func TestTokenStore_HandleRequest_CreateToken_NumUses_Invalid(t *testing.T) { req.ClientToken = root req.Data["num_uses"] = "-1" - resp, err := ts.HandleRequest(context.Background(), req) + resp, err := ts.HandleRequest(namespace.TestContext(), req) if err != logical.ErrInvalidRequest { t.Fatalf("err: %v resp: %#v", err, resp) } @@ -1209,14 +1278,14 @@ func TestTokenStore_HandleRequest_CreateToken_NumUses_Restricted(t *testing.T) { req.ClientToken = root req.Data["num_uses"] = "1" - resp, err := ts.HandleRequest(context.Background(), req) + resp, err := ts.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } // We should NOT be able to use the restricted token to create a new token req.ClientToken = resp.Auth.ClientToken - _, err = ts.HandleRequest(context.Background(), req) + _, err = ts.HandleRequest(namespace.TestContext(), req) if err != logical.ErrInvalidRequest { t.Fatalf("err: %v resp: %#v", err, resp) } @@ -1229,13 +1298,14 @@ func TestTokenStore_HandleRequest_CreateToken_NoPolicy(t *testing.T) { req := logical.TestRequest(t, logical.UpdateOperation, "create") req.ClientToken = root - resp, err := ts.HandleRequest(context.Background(), req) + resp, err := ts.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } expected := &logical.TokenEntry{ ID: resp.Auth.ClientToken, + NamespaceID: namespace.RootNamespaceID, Accessor: resp.Auth.Accessor, Parent: root, Policies: []string{"root"}, @@ -1243,7 +1313,7 @@ func TestTokenStore_HandleRequest_CreateToken_NoPolicy(t *testing.T) { DisplayName: "token", TTL: 0, } - out, err := ts.Lookup(context.Background(), resp.Auth.ClientToken) + out, err := ts.Lookup(namespace.TestContext(), resp.Auth.ClientToken) if err != nil { t.Fatalf("err: %v", err) } @@ -1260,7 +1330,7 @@ func TestTokenStore_HandleRequest_CreateToken_BadParent(t *testing.T) { req := logical.TestRequest(t, logical.UpdateOperation, "create") req.ClientToken = "random" - resp, err := ts.HandleRequest(context.Background(), req) + resp, err := ts.HandleRequest(namespace.TestContext(), req) if err != logical.ErrInvalidRequest { t.Fatalf("err: %v resp: %#v", err, resp) } @@ -1277,7 +1347,7 @@ func TestTokenStore_HandleRequest_CreateToken(t *testing.T) { req.ClientToken = root req.Data["policies"] = []string{"foo"} - resp, err := ts.HandleRequest(context.Background(), req) + resp, err := ts.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -1295,7 +1365,7 @@ func TestTokenStore_HandleRequest_CreateToken_RootID(t *testing.T) { req.Data["id"] = "foobar" req.Data["policies"] = []string{"foo"} - resp, err := ts.HandleRequest(context.Background(), req) + resp, err := ts.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -1315,7 +1385,7 @@ func TestTokenStore_HandleRequest_CreateToken_NonRootID(t *testing.T) { req.Data["id"] = "foobar" req.Data["policies"] = []string{"foo"} - resp, err := ts.HandleRequest(context.Background(), req) + resp, err := ts.HandleRequest(namespace.TestContext(), req) if err != logical.ErrInvalidRequest { t.Fatalf("err: %v resp: %#v", err, resp) } @@ -1333,7 +1403,7 @@ func TestTokenStore_HandleRequest_CreateToken_NonRoot_Subset(t *testing.T) { req.ClientToken = "client" req.Data["policies"] = []string{"foo"} - resp, err := ts.HandleRequest(context.Background(), req) + resp, err := ts.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -1351,7 +1421,7 @@ func TestTokenStore_HandleRequest_CreateToken_NonRoot_InvalidSubset(t *testing.T req.ClientToken = "client" req.Data["policies"] = []string{"foo", "bar", "baz"} - resp, err := ts.HandleRequest(context.Background(), req) + resp, err := ts.HandleRequest(namespace.TestContext(), req) if err != logical.ErrInvalidRequest { t.Fatalf("err: %v resp: %#v", err, resp) } @@ -1365,9 +1435,9 @@ func TestTokenStore_HandleRequest_CreateToken_NonRoot_RootChild(t *testing.T) { ts := core.tokenStore ps := core.policyStore - policy, _ := ParseACLPolicy(tokenCreationPolicy) + policy, _ := ParseACLPolicy(namespace.RootNamespace, tokenCreationPolicy) policy.Name = "test1" - if err := ps.SetPolicy(context.Background(), policy); err != nil { + if err := ps.SetPolicy(namespace.TestContext(), policy); err != nil { t.Fatal(err) } @@ -1378,7 +1448,7 @@ func TestTokenStore_HandleRequest_CreateToken_NonRoot_RootChild(t *testing.T) { req.MountPoint = "auth/token/" req.Data["policies"] = []string{"root"} - resp, err := ts.HandleRequest(context.Background(), req) + resp, err := ts.HandleRequest(namespace.TestContext(), req) if err != logical.ErrInvalidRequest { t.Fatalf("err: %v; resp: %#v", err, resp) } @@ -1400,7 +1470,7 @@ func TestTokenStore_HandleRequest_CreateToken_Root_RootChild_NoExpiry_Expiry(t * "ttl": "5m", } - resp, err := ts.HandleRequest(context.Background(), req) + resp, err := ts.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v; resp: %#v", err, resp) } @@ -1418,7 +1488,7 @@ func TestTokenStore_HandleRequest_CreateToken_Root_RootChild_NoExpiry_Expiry(t * req.Data = map[string]interface{}{ "ttl": "0", } - resp, err = ts.HandleRequest(context.Background(), req) + resp, err = ts.HandleRequest(namespace.TestContext(), req) if err == nil { t.Fatalf("expected error") } @@ -1431,7 +1501,7 @@ func TestTokenStore_HandleRequest_CreateToken_Root_RootChild(t *testing.T) { req := logical.TestRequest(t, logical.UpdateOperation, "create") req.ClientToken = root - resp, err := ts.HandleRequest(context.Background(), req) + resp, err := ts.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v; resp: %#v", err, resp) } @@ -1453,7 +1523,7 @@ func TestTokenStore_HandleRequest_CreateToken_NonRoot_NoParent(t *testing.T) { req.Data["no_parent"] = true req.Data["policies"] = []string{"foo"} - resp, err := ts.HandleRequest(context.Background(), req) + resp, err := ts.HandleRequest(namespace.TestContext(), req) if err != logical.ErrInvalidRequest { t.Fatalf("err: %v resp: %#v", err, resp) } @@ -1476,7 +1546,7 @@ func TestTokenStore_HandleRequest_CreateToken_Root_NoParent(t *testing.T) { t.Fatalf("bad: %#v", resp) } - out, _ := ts.Lookup(context.Background(), resp.Auth.ClientToken) + out, _ := ts.Lookup(namespace.TestContext(), resp.Auth.ClientToken) if out.Parent != "" { t.Fatalf("bad: %#v", out) } @@ -1496,7 +1566,7 @@ func TestTokenStore_HandleRequest_CreateToken_PathBased_NoParent(t *testing.T) { t.Fatalf("bad: %#v", resp) } - out, _ := ts.Lookup(context.Background(), resp.Auth.ClientToken) + out, _ := ts.Lookup(namespace.TestContext(), resp.Auth.ClientToken) if out.Parent != "" { t.Fatalf("bad: %#v", out) } @@ -1520,7 +1590,7 @@ func TestTokenStore_HandleRequest_CreateToken_Metadata(t *testing.T) { t.Fatalf("bad: %#v", resp) } - out, _ := ts.Lookup(context.Background(), resp.Auth.ClientToken) + out, _ := ts.Lookup(namespace.TestContext(), resp.Auth.ClientToken) if !reflect.DeepEqual(out.Meta, meta) { t.Fatalf("bad: expected:%#v\nactual:%#v", meta, out.Meta) } @@ -1572,11 +1642,19 @@ func TestTokenStore_HandleRequest_Revoke(t *testing.T) { exp := mockExpiration(t) ts := exp.tokenStore - rootToken, err := ts.rootToken(context.Background()) + rootToken, err := ts.rootToken(namespace.TestContext()) root := rootToken.ID testMakeTokenViaBackend(t, ts, root, "child", "", []string{"root", "foo"}) + te, err := ts.Lookup(namespace.TestContext(), "child") + if err != nil { + t.Fatal(err) + } + if te == nil { + t.Fatal("token entry was nil") + } + auth := &logical.Auth{ ClientToken: "child", LeaseOptions: logical.LeaseOptions{ @@ -1584,13 +1662,21 @@ func TestTokenStore_HandleRequest_Revoke(t *testing.T) { Renewable: true, }, } - err = exp.RegisterAuth(context.Background(), "auth/token/create", auth) + err = exp.RegisterAuth(namespace.TestContext(), te, auth) if err != nil { t.Fatalf("err: %v", err) } testMakeTokenViaBackend(t, ts, "child", "sub-child", "", []string{"foo"}) + te, err = ts.Lookup(namespace.TestContext(), "sub-child") + if err != nil { + t.Fatal(err) + } + if te == nil { + t.Fatal("token entry was nil") + } + auth = &logical.Auth{ ClientToken: "sub-child", LeaseOptions: logical.LeaseOptions{ @@ -1598,7 +1684,7 @@ func TestTokenStore_HandleRequest_Revoke(t *testing.T) { Renewable: true, }, } - err = exp.RegisterAuth(context.Background(), "auth/token/create", auth) + err = exp.RegisterAuth(namespace.TestContext(), te, auth) if err != nil { t.Fatalf("err: %v", err) } @@ -1607,7 +1693,7 @@ func TestTokenStore_HandleRequest_Revoke(t *testing.T) { req.Data = map[string]interface{}{ "token": "child", } - resp, err := ts.HandleRequest(context.Background(), req) + resp, err := ts.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -1617,7 +1703,7 @@ func TestTokenStore_HandleRequest_Revoke(t *testing.T) { time.Sleep(200 * time.Millisecond) - out, err := ts.Lookup(context.Background(), "child") + out, err := ts.Lookup(namespace.TestContext(), "child") if err != nil { t.Fatalf("err: %v", err) } @@ -1626,7 +1712,7 @@ func TestTokenStore_HandleRequest_Revoke(t *testing.T) { } // Sub-child should not exist - out, err = ts.Lookup(context.Background(), "sub-child") + out, err = ts.Lookup(namespace.TestContext(), "sub-child") if err != nil { t.Fatalf("err: %v", err) } @@ -1642,7 +1728,7 @@ func TestTokenStore_HandleRequest_Revoke(t *testing.T) { req.Data = map[string]interface{}{ "token": "child", } - resp, err = ts.HandleRequest(context.Background(), req) + resp, err = ts.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -1652,7 +1738,7 @@ func TestTokenStore_HandleRequest_Revoke(t *testing.T) { time.Sleep(200 * time.Millisecond) - out, err = ts.Lookup(context.Background(), "child") + out, err = ts.Lookup(namespace.TestContext(), "child") if err != nil { t.Fatalf("err: %v", err) } @@ -1661,7 +1747,7 @@ func TestTokenStore_HandleRequest_Revoke(t *testing.T) { } // Sub-child should not exist - out, err = ts.Lookup(context.Background(), "sub-child") + out, err = ts.Lookup(namespace.TestContext(), "sub-child") if err != nil { t.Fatalf("err: %v", err) } @@ -1681,7 +1767,7 @@ func TestTokenStore_HandleRequest_RevokeOrphan(t *testing.T) { "token": "child", } req.ClientToken = root - resp, err := ts.HandleRequest(context.Background(), req) + resp, err := ts.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -1691,7 +1777,7 @@ func TestTokenStore_HandleRequest_RevokeOrphan(t *testing.T) { time.Sleep(200 * time.Millisecond) - out, err := ts.Lookup(context.Background(), "child") + out, err := ts.Lookup(namespace.TestContext(), "child") if err != nil { t.Fatalf("err: %v", err) } @@ -1700,11 +1786,11 @@ func TestTokenStore_HandleRequest_RevokeOrphan(t *testing.T) { } // Check that the parent entry is properly cleaned up - saltedID, err := ts.SaltID(context.Background(), "child") + saltedID, err := ts.SaltID(namespace.TestContext(), "child") if err != nil { t.Fatal(err) } - children, err := ts.view.List(context.Background(), parentPrefix+saltedID+"/") + children, err := ts.idView(namespace.RootNamespace).List(namespace.TestContext(), parentPrefix+saltedID+"/") if err != nil { t.Fatalf("err: %v", err) } @@ -1713,7 +1799,7 @@ func TestTokenStore_HandleRequest_RevokeOrphan(t *testing.T) { } // Sub-child should exist! - out, err = ts.Lookup(context.Background(), "sub-child") + out, err = ts.Lookup(namespace.TestContext(), "sub-child") if err != nil { t.Fatalf("err: %v", err) } @@ -1727,7 +1813,7 @@ func TestTokenStore_HandleRequest_RevokeOrphan_NonRoot(t *testing.T) { ts := c.tokenStore testMakeTokenViaBackend(t, ts, root, "child", "", []string{"foo"}) - out, err := ts.Lookup(context.Background(), "child") + out, err := ts.Lookup(namespace.TestContext(), "child") if err != nil { t.Fatalf("err: %v", err) } @@ -1740,7 +1826,7 @@ func TestTokenStore_HandleRequest_RevokeOrphan_NonRoot(t *testing.T) { "token": "child", } req.ClientToken = "child" - resp, err := ts.HandleRequest(context.Background(), req) + resp, err := ts.HandleRequest(namespace.TestContext(), req) if err != logical.ErrInvalidRequest { t.Fatalf("did not get error when non-root revoking itself with orphan flag; resp is %#v", resp) } @@ -1748,7 +1834,7 @@ func TestTokenStore_HandleRequest_RevokeOrphan_NonRoot(t *testing.T) { time.Sleep(200 * time.Millisecond) // Should still exist - out, err = ts.Lookup(context.Background(), "child") + out, err = ts.Lookup(namespace.TestContext(), "child") if err != nil { t.Fatalf("err: %v", err) } @@ -1764,7 +1850,7 @@ func TestTokenStore_HandleRequest_Lookup(t *testing.T) { req.Data = map[string]interface{}{ "token": root, } - resp, err := ts.HandleRequest(context.Background(), req) + resp, err := ts.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -1804,7 +1890,7 @@ func TestTokenStore_HandleRequest_Lookup(t *testing.T) { req.Data = map[string]interface{}{ "token": "client", } - resp, err = ts.HandleRequest(context.Background(), req) + resp, err = ts.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -1855,7 +1941,7 @@ func TestTokenStore_HandleRequest_Lookup(t *testing.T) { req.Data = map[string]interface{}{ "token": "client", } - resp, err = ts.HandleRequest(context.Background(), req) + resp, err = ts.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -1906,7 +1992,7 @@ func TestTokenStore_HandleRequest_Lookup(t *testing.T) { req.Data = map[string]interface{}{ "token": "client", } - resp, err = ts.HandleRequest(context.Background(), req) + resp, err = ts.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -1918,7 +2004,7 @@ func TestTokenStore_HandleRequest_Lookup(t *testing.T) { req.Data = map[string]interface{}{ "token": "client", } - resp, err = ts.HandleRequest(context.Background(), req) + resp, err = ts.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -1938,7 +2024,7 @@ func TestTokenStore_HandleRequest_LookupSelf(t *testing.T) { req := logical.TestRequest(t, logical.ReadOperation, "lookup-self") req.ClientToken = "client" - resp, err := ts.HandleRequest(context.Background(), req) + resp, err := ts.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -1990,7 +2076,7 @@ func TestTokenStore_HandleRequest_Renew(t *testing.T) { ts := exp.tokenStore // Create new token - root, err := ts.rootToken(context.Background()) + root, err := ts.rootToken(namespace.TestContext()) if err != nil { t.Fatalf("err: %v", err) } @@ -2003,7 +2089,7 @@ func TestTokenStore_HandleRequest_Renew(t *testing.T) { Renewable: true, }, } - err = exp.RegisterAuth(context.Background(), "auth/token/root", auth) + err = exp.RegisterAuth(namespace.TestContext(), root, auth) if err != nil { t.Fatalf("err: %v", err) } @@ -2019,7 +2105,7 @@ func TestTokenStore_HandleRequest_Renew(t *testing.T) { } req.Data["increment"] = "3600s" - resp, err := ts.HandleRequest(context.Background(), req) + resp, err := ts.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -2039,7 +2125,7 @@ func TestTokenStore_HandleRequest_RenewSelf(t *testing.T) { ts := exp.tokenStore // Create new token - root, err := ts.rootToken(context.Background()) + root, err := ts.rootToken(namespace.TestContext()) if err != nil { t.Fatalf("err: %v", err) } @@ -2052,7 +2138,7 @@ func TestTokenStore_HandleRequest_RenewSelf(t *testing.T) { Renewable: true, }, } - err = exp.RegisterAuth(context.Background(), "auth/token/root", auth) + err = exp.RegisterAuth(namespace.TestContext(), root, auth) if err != nil { t.Fatalf("err: %v", err) } @@ -2064,7 +2150,7 @@ func TestTokenStore_HandleRequest_RenewSelf(t *testing.T) { req := logical.TestRequest(t, logical.UpdateOperation, "renew-self") req.ClientToken = auth.ClientToken req.Data["increment"] = "3600s" - resp, err := ts.HandleRequest(context.Background(), req) + resp, err := ts.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -2085,7 +2171,7 @@ func TestTokenStore_RoleCRUD(t *testing.T) { req := logical.TestRequest(t, logical.ReadOperation, "auth/token/roles/test") req.ClientToken = root - resp, err := core.HandleRequest(context.Background(), req) + resp, err := core.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -2102,7 +2188,7 @@ func TestTokenStore_RoleCRUD(t *testing.T) { "path_suffix": "happenin", } - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -2114,7 +2200,7 @@ func TestTokenStore_RoleCRUD(t *testing.T) { req.Operation = logical.ReadOperation req.Data = map[string]interface{}{} - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -2148,7 +2234,7 @@ func TestTokenStore_RoleCRUD(t *testing.T) { "renewable": false, } - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -2160,7 +2246,7 @@ func TestTokenStore_RoleCRUD(t *testing.T) { req.Operation = logical.ReadOperation req.Data = map[string]interface{}{} - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -2189,7 +2275,7 @@ func TestTokenStore_RoleCRUD(t *testing.T) { "explicit_max_ttl": "5", "period": "0s", } - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -2197,7 +2283,7 @@ func TestTokenStore_RoleCRUD(t *testing.T) { req.Operation = logical.ReadOperation req.Data = map[string]interface{}{} - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -2223,7 +2309,7 @@ func TestTokenStore_RoleCRUD(t *testing.T) { req.Operation = logical.ListOperation req.Path = "auth/token/roles" req.Data = map[string]interface{}{} - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -2247,7 +2333,7 @@ func TestTokenStore_RoleCRUD(t *testing.T) { req.Operation = logical.DeleteOperation req.Path = "auth/token/roles/test" - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -2256,7 +2342,7 @@ func TestTokenStore_RoleCRUD(t *testing.T) { } req.Operation = logical.ReadOperation - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -2281,13 +2367,13 @@ func TestTokenStore_RoleDisallowedPoliciesWithRoot(t *testing.T) { }, ClientToken: root, } - resp, err = ts.HandleRequest(context.Background(), roleReq) + resp, err = ts.HandleRequest(namespace.TestContext(), roleReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%v", err, resp) } roleReq.Operation = logical.ReadOperation - resp, err = ts.HandleRequest(context.Background(), roleReq) + resp, err = ts.HandleRequest(namespace.TestContext(), roleReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%v", err, resp) } @@ -2308,21 +2394,21 @@ func TestTokenStore_RoleDisallowedPolicies(t *testing.T) { ps := core.policyStore // Create 3 different policies - policy, _ := ParseACLPolicy(tokenCreationPolicy) + policy, _ := ParseACLPolicy(namespace.RootNamespace, tokenCreationPolicy) policy.Name = "test1" - if err := ps.SetPolicy(context.Background(), policy); err != nil { + if err := ps.SetPolicy(namespace.TestContext(), policy); err != nil { t.Fatal(err) } - policy, _ = ParseACLPolicy(tokenCreationPolicy) + policy, _ = ParseACLPolicy(namespace.RootNamespace, tokenCreationPolicy) policy.Name = "test2" - if err := ps.SetPolicy(context.Background(), policy); err != nil { + if err := ps.SetPolicy(namespace.TestContext(), policy); err != nil { t.Fatal(err) } - policy, _ = ParseACLPolicy(tokenCreationPolicy) + policy, _ = ParseACLPolicy(namespace.RootNamespace, tokenCreationPolicy) policy.Name = "test3" - if err := ps.SetPolicy(context.Background(), policy); err != nil { + if err := ps.SetPolicy(namespace.TestContext(), policy); err != nil { t.Fatal(err) } @@ -2332,7 +2418,7 @@ func TestTokenStore_RoleDisallowedPolicies(t *testing.T) { req.Data = map[string]interface{}{ "disallowed_policies": "test1", } - resp, err = ts.HandleRequest(context.Background(), req) + resp, err = ts.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%v", err, resp) } @@ -2342,7 +2428,7 @@ func TestTokenStore_RoleDisallowedPolicies(t *testing.T) { req.Data = map[string]interface{}{ "disallowed_policies": "test2,test3", } - resp, err = ts.HandleRequest(context.Background(), req) + resp, err = ts.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%v", err, resp) } @@ -2352,7 +2438,7 @@ func TestTokenStore_RoleDisallowedPolicies(t *testing.T) { req.Data = map[string]interface{}{ "disallowed_policies": "test1,test2,test3", } - resp, err = ts.HandleRequest(context.Background(), req) + resp, err = ts.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%v", err, resp) } @@ -2372,21 +2458,21 @@ func TestTokenStore_RoleDisallowedPolicies(t *testing.T) { req = logical.TestRequest(t, logical.UpdateOperation, "create/test1") req.ClientToken = parentToken - resp, err = ts.HandleRequest(context.Background(), req) + resp, err = ts.HandleRequest(namespace.TestContext(), req) if err == nil || resp != nil && !resp.IsError() { t.Fatalf("expected an error response, got %#v", resp) } req = logical.TestRequest(t, logical.UpdateOperation, "create/test23") req.ClientToken = parentToken - resp, err = ts.HandleRequest(context.Background(), req) + resp, err = ts.HandleRequest(namespace.TestContext(), req) if err == nil || resp != nil && !resp.IsError() { t.Fatalf("expected an error response, got %#v", resp) } req = logical.TestRequest(t, logical.UpdateOperation, "create/test123") req.ClientToken = parentToken - resp, err = ts.HandleRequest(context.Background(), req) + resp, err = ts.HandleRequest(namespace.TestContext(), req) if err == nil || resp != nil && !resp.IsError() { t.Fatalf("expected an error response, got %#v", resp) } @@ -2404,14 +2490,14 @@ func TestTokenStore_RoleDisallowedPolicies(t *testing.T) { req.Data = map[string]interface{}{ "disallowed_policies": "default", } - resp, err = ts.HandleRequest(context.Background(), req) + resp, err = ts.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%v", err, resp) } req = logical.TestRequest(t, logical.UpdateOperation, "create/default") req.ClientToken = parentToken - resp, err = ts.HandleRequest(context.Background(), req) + resp, err = ts.HandleRequest(namespace.TestContext(), req) if err == nil || resp != nil && !resp.IsError() { t.Fatal("expected an error response") } @@ -2427,7 +2513,7 @@ func TestTokenStore_RoleAllowedPolicies(t *testing.T) { "allowed_policies": "test1,test2", } - resp, err := ts.HandleRequest(context.Background(), req) + resp, err := ts.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -2439,7 +2525,7 @@ func TestTokenStore_RoleAllowedPolicies(t *testing.T) { req.Path = "create/test" req.Data["policies"] = []string{"foo"} - resp, err = ts.HandleRequest(context.Background(), req) + resp, err = ts.HandleRequest(namespace.TestContext(), req) if err == nil { t.Fatalf("expected error") } @@ -2456,7 +2542,7 @@ func TestTokenStore_RoleAllowedPolicies(t *testing.T) { req.Data = map[string]interface{}{ "allowed_policies": "", } - resp, err = ts.HandleRequest(context.Background(), req) + resp, err = ts.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -2484,7 +2570,7 @@ func TestTokenStore_RoleAllowedPolicies(t *testing.T) { req.Path = "create/test" req.Data["policies"] = []string{"foo"} - resp, err = ts.HandleRequest(context.Background(), req) + resp, err = ts.HandleRequest(namespace.TestContext(), req) if err == nil { t.Fatalf("expected error") } @@ -2515,7 +2601,7 @@ func TestTokenStore_RoleOrphan(t *testing.T) { "orphan": true, } - resp, err := ts.HandleRequest(context.Background(), req) + resp, err := ts.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -2524,7 +2610,7 @@ func TestTokenStore_RoleOrphan(t *testing.T) { } req.Path = "create/test" - resp, err = ts.HandleRequest(context.Background(), req) + resp, err = ts.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -2532,7 +2618,7 @@ func TestTokenStore_RoleOrphan(t *testing.T) { t.Fatalf("bad: %#v", resp) } - out, err := ts.Lookup(context.Background(), resp.Auth.ClientToken) + out, err := ts.Lookup(namespace.TestContext(), resp.Auth.ClientToken) if err != nil { t.Fatalf("err: %v", err) } @@ -2556,7 +2642,7 @@ func TestTokenStore_RolePathSuffix(t *testing.T) { "path_suffix": "happenin", } - resp, err := ts.HandleRequest(context.Background(), req) + resp, err := ts.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -2565,7 +2651,7 @@ func TestTokenStore_RolePathSuffix(t *testing.T) { } req.Path = "create/test" - resp, err = ts.HandleRequest(context.Background(), req) + resp, err = ts.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -2573,7 +2659,7 @@ func TestTokenStore_RolePathSuffix(t *testing.T) { t.Fatalf("bad: %#v", resp) } - out, err := ts.Lookup(context.Background(), resp.Auth.ClientToken) + out, err := ts.Lookup(namespace.TestContext(), resp.Auth.ClientToken) if err != nil { t.Fatalf("err: %v", err) } @@ -2598,7 +2684,7 @@ func TestTokenStore_RolePeriod(t *testing.T) { "period": 5, } - resp, err := core.HandleRequest(context.Background(), req) + resp, err := core.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -2615,7 +2701,7 @@ func TestTokenStore_RolePeriod(t *testing.T) { req.Data = map[string]interface{}{ "policies": []string{"default"}, } - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -2626,7 +2712,7 @@ func TestTokenStore_RolePeriod(t *testing.T) { req.ClientToken = resp.Auth.ClientToken req.Operation = logical.ReadOperation req.Path = "auth/token/lookup-self" - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -2640,14 +2726,14 @@ func TestTokenStore_RolePeriod(t *testing.T) { req.Operation = logical.UpdateOperation req.Path = "auth/token/renew-self" - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } req.Operation = logical.ReadOperation req.Path = "auth/token/lookup-self" - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -2663,14 +2749,14 @@ func TestTokenStore_RolePeriod(t *testing.T) { req.Data = map[string]interface{}{ "increment": 1, } - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } req.Operation = logical.ReadOperation req.Path = "auth/token/lookup-self" - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -2686,7 +2772,7 @@ func TestTokenStore_RolePeriod(t *testing.T) { req.ClientToken = root req.Operation = logical.UpdateOperation req.Path = "auth/token/create/test" - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -2703,7 +2789,7 @@ func TestTokenStore_RolePeriod(t *testing.T) { req.ClientToken = resp.Auth.ClientToken req.Operation = logical.ReadOperation req.Path = "auth/token/lookup-self" - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -2720,14 +2806,14 @@ func TestTokenStore_RolePeriod(t *testing.T) { req.Data = map[string]interface{}{ "increment": 1, } - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } req.Operation = logical.ReadOperation req.Path = "auth/token/lookup-self" - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -2754,7 +2840,7 @@ func TestTokenStore_RoleExplicitMaxTTL(t *testing.T) { "explicit_max_ttl": "100h", } - resp, err := core.HandleRequest(context.Background(), req) + resp, err := core.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -2764,7 +2850,7 @@ func TestTokenStore_RoleExplicitMaxTTL(t *testing.T) { req.Operation = logical.UpdateOperation req.Path = "auth/token/create/test" - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("expected an error") } @@ -2779,7 +2865,7 @@ func TestTokenStore_RoleExplicitMaxTTL(t *testing.T) { "explicit_max_ttl": "10s", } - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -2795,7 +2881,7 @@ func TestTokenStore_RoleExplicitMaxTTL(t *testing.T) { req.Data = map[string]interface{}{ "policies": []string{"default"}, } - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -2806,7 +2892,7 @@ func TestTokenStore_RoleExplicitMaxTTL(t *testing.T) { req.ClientToken = resp.Auth.ClientToken req.Operation = logical.ReadOperation req.Path = "auth/token/lookup-self" - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -2820,14 +2906,14 @@ func TestTokenStore_RoleExplicitMaxTTL(t *testing.T) { req.Operation = logical.UpdateOperation req.Path = "auth/token/renew-self" - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } req.Operation = logical.ReadOperation req.Path = "auth/token/lookup-self" - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -2843,7 +2929,7 @@ func TestTokenStore_RoleExplicitMaxTTL(t *testing.T) { req.ClientToken = root req.Operation = logical.UpdateOperation req.Path = "auth/token/create/test" - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -2860,7 +2946,7 @@ func TestTokenStore_RoleExplicitMaxTTL(t *testing.T) { req.ClientToken = resp.Auth.ClientToken req.Operation = logical.ReadOperation req.Path = "auth/token/lookup-self" - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -2882,14 +2968,14 @@ func TestTokenStore_RoleExplicitMaxTTL(t *testing.T) { req.Data = map[string]interface{}{ "increment": 300, } - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } req.Operation = logical.ReadOperation req.Path = "auth/token/lookup-self" - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -2906,14 +2992,14 @@ func TestTokenStore_RoleExplicitMaxTTL(t *testing.T) { req.Data = map[string]interface{}{ "increment": 300, } - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } req.Operation = logical.ReadOperation req.Path = "auth/token/lookup-self" - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -2930,7 +3016,7 @@ func TestTokenStore_RoleExplicitMaxTTL(t *testing.T) { req.Data = map[string]interface{}{ "increment": 300, } - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err == nil { t.Fatalf("expected error") } @@ -2939,7 +3025,7 @@ func TestTokenStore_RoleExplicitMaxTTL(t *testing.T) { req.Operation = logical.ReadOperation req.Path = "auth/token/lookup-self" - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if resp != nil && err == nil { t.Fatalf("expected error, response is %#v", *resp) } @@ -2964,7 +3050,7 @@ func TestTokenStore_Periodic(t *testing.T) { "period": 5, } - resp, err := core.HandleRequest(context.Background(), req) + resp, err := core.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -2977,7 +3063,7 @@ func TestTokenStore_Periodic(t *testing.T) { req.ClientToken = root req.Operation = logical.UpdateOperation req.Path = "auth/token/create" - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatal(err) } @@ -2994,7 +3080,7 @@ func TestTokenStore_Periodic(t *testing.T) { req.ClientToken = resp.Auth.ClientToken req.Operation = logical.ReadOperation req.Path = "auth/token/lookup-self" - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -3011,14 +3097,14 @@ func TestTokenStore_Periodic(t *testing.T) { req.Data = map[string]interface{}{ "increment": 1, } - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } req.Operation = logical.ReadOperation req.Path = "auth/token/lookup-self" - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -3038,7 +3124,7 @@ func TestTokenStore_Periodic(t *testing.T) { req.Data = map[string]interface{}{ "period": 5, } - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -3055,7 +3141,7 @@ func TestTokenStore_Periodic(t *testing.T) { req.ClientToken = resp.Auth.ClientToken req.Operation = logical.ReadOperation req.Path = "auth/token/lookup-self" - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -3072,14 +3158,14 @@ func TestTokenStore_Periodic(t *testing.T) { req.Data = map[string]interface{}{ "increment": 1, } - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } req.Operation = logical.ReadOperation req.Path = "auth/token/lookup-self" - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -3105,7 +3191,7 @@ func TestTokenStore_Periodic_ExplicitMax(t *testing.T) { "period": 5, } - resp, err := core.HandleRequest(context.Background(), req) + resp, err := core.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -3122,7 +3208,7 @@ func TestTokenStore_Periodic_ExplicitMax(t *testing.T) { "period": 5, "explicit_max_ttl": 4, } - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatal(err) } @@ -3139,7 +3225,7 @@ func TestTokenStore_Periodic_ExplicitMax(t *testing.T) { req.ClientToken = resp.Auth.ClientToken req.Operation = logical.ReadOperation req.Path = "auth/token/lookup-self" - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -3156,14 +3242,14 @@ func TestTokenStore_Periodic_ExplicitMax(t *testing.T) { req.Data = map[string]interface{}{ "increment": 76, } - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } req.Operation = logical.ReadOperation req.Path = "auth/token/lookup-self" - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -3185,7 +3271,7 @@ func TestTokenStore_Periodic_ExplicitMax(t *testing.T) { "explicit_max_ttl": 4, } - resp, err := core.HandleRequest(context.Background(), req) + resp, err := core.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -3196,7 +3282,7 @@ func TestTokenStore_Periodic_ExplicitMax(t *testing.T) { req.ClientToken = root req.Operation = logical.UpdateOperation req.Path = "auth/token/create/test" - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } @@ -3213,7 +3299,7 @@ func TestTokenStore_Periodic_ExplicitMax(t *testing.T) { req.ClientToken = resp.Auth.ClientToken req.Operation = logical.ReadOperation req.Path = "auth/token/lookup-self" - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -3230,14 +3316,14 @@ func TestTokenStore_Periodic_ExplicitMax(t *testing.T) { req.Data = map[string]interface{}{ "increment": 1, } - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } req.Operation = logical.ReadOperation req.Path = "auth/token/lookup-self" - resp, err = core.HandleRequest(context.Background(), req) + resp, err = core.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %v", err) } @@ -3255,9 +3341,9 @@ func TestTokenStore_NoDefaultPolicy(t *testing.T) { core, _, root := TestCoreUnsealed(t) ts := core.tokenStore ps := core.policyStore - policy, _ := ParseACLPolicy(tokenCreationPolicy) + policy, _ := ParseACLPolicy(namespace.RootNamespace, tokenCreationPolicy) policy.Name = "policy1" - if err := ps.SetPolicy(context.Background(), policy); err != nil { + if err := ps.SetPolicy(namespace.TestContext(), policy); err != nil { t.Fatal(err) } @@ -3344,7 +3430,7 @@ func TestTokenStore_NoDefaultPolicy(t *testing.T) { Path: "roles/role1", Operation: logical.CreateOperation, } - resp, err = ts.HandleRequest(context.Background(), roleReq) + resp, err = ts.HandleRequest(namespace.TestContext(), roleReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v, resp: %v", err, resp) } @@ -3352,7 +3438,7 @@ func TestTokenStore_NoDefaultPolicy(t *testing.T) { tokenReq.Data = map[string]interface{}{ "policies": []string{"policy1"}, } - resp, err = ts.HandleRequest(context.Background(), tokenReq) + resp, err = ts.HandleRequest(namespace.TestContext(), tokenReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v, resp: %v", err, resp) } @@ -3367,7 +3453,7 @@ func TestTokenStore_NoDefaultPolicy(t *testing.T) { roleReq.Data = map[string]interface{}{ "allowed_policies": "policy1", } - resp, err = ts.HandleRequest(context.Background(), roleReq) + resp, err = ts.HandleRequest(namespace.TestContext(), roleReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v, resp: %v", err, resp) } @@ -3385,7 +3471,7 @@ func TestTokenStore_NoDefaultPolicy(t *testing.T) { "allowed_policies": "policy1", "disallowed_policies": "default", } - resp, err = ts.HandleRequest(context.Background(), roleReq) + resp, err = ts.HandleRequest(namespace.TestContext(), roleReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v, resp: %v", err, resp) } @@ -3399,7 +3485,7 @@ func TestTokenStore_NoDefaultPolicy(t *testing.T) { "allowed_policies": "", "disallowed_policies": "default", } - resp, err = ts.HandleRequest(context.Background(), roleReq) + resp, err = ts.HandleRequest(namespace.TestContext(), roleReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v, resp: %v", err, resp) } @@ -3414,13 +3500,13 @@ func TestTokenStore_NoDefaultPolicy(t *testing.T) { "allowed_policies": "default", "disallowed_policies": "default", } - resp, err = ts.HandleRequest(context.Background(), roleReq) + resp, err = ts.HandleRequest(namespace.TestContext(), roleReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v, resp: %v", err, resp) } delete(tokenReq.Data, "policies") - resp, err = ts.HandleRequest(context.Background(), tokenReq) + resp, err = ts.HandleRequest(namespace.TestContext(), tokenReq) if err == nil || (resp != nil && !resp.IsError()) { t.Fatalf("err: %v, resp: %v", err, resp) } @@ -3442,7 +3528,7 @@ func TestTokenStore_AllowedDisallowedPolicies(t *testing.T) { "disallowed_policies": "disallowed1,disallowed2", }, } - resp, err = ts.HandleRequest(context.Background(), roleReq) + resp, err = ts.HandleRequest(namespace.TestContext(), roleReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v, resp: %v", err, resp) } @@ -3455,7 +3541,7 @@ func TestTokenStore_AllowedDisallowedPolicies(t *testing.T) { "policies": []string{"allowed1"}, }, } - resp, err = ts.HandleRequest(context.Background(), tokenReq) + resp, err = ts.HandleRequest(namespace.TestContext(), tokenReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%v", err, resp) } @@ -3475,7 +3561,7 @@ func TestTokenStore_AllowedDisallowedPolicies(t *testing.T) { "no_default_policy": true, }, } - resp, err = ts.HandleRequest(context.Background(), tokenReq) + resp, err = ts.HandleRequest(namespace.TestContext(), tokenReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%v", err, resp) } @@ -3488,7 +3574,7 @@ func TestTokenStore_AllowedDisallowedPolicies(t *testing.T) { tokenReq.Data = map[string]interface{}{ "policies": []string{"disallowed1"}, } - resp, err = ts.HandleRequest(context.Background(), tokenReq) + resp, err = ts.HandleRequest(namespace.TestContext(), tokenReq) if err == nil { t.Fatalf("expected an error") } @@ -3498,7 +3584,7 @@ func TestTokenStore_AllowedDisallowedPolicies(t *testing.T) { "allowed_policies": "allowed1,common", "disallowed_policies": "disallowed1,common", } - resp, err = ts.HandleRequest(context.Background(), roleReq) + resp, err = ts.HandleRequest(namespace.TestContext(), roleReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v, resp: %v", err, resp) } @@ -3506,7 +3592,7 @@ func TestTokenStore_AllowedDisallowedPolicies(t *testing.T) { tokenReq.Data = map[string]interface{}{ "policies": []string{"allowed1", "common"}, } - resp, err = ts.HandleRequest(context.Background(), tokenReq) + resp, err = ts.HandleRequest(namespace.TestContext(), tokenReq) if err == nil { t.Fatalf("expected an error") } @@ -3521,7 +3607,7 @@ func TestTokenStore_RevokeUseCountToken(t *testing.T) { exp := mockExpiration(t) ts := exp.tokenStore - root, _ := exp.tokenStore.rootToken(context.Background()) + root, _ := exp.tokenStore.rootToken(namespace.TestContext()) tokenReq := &logical.Request{ Path: "create", @@ -3531,17 +3617,17 @@ func TestTokenStore_RevokeUseCountToken(t *testing.T) { "num_uses": 1, }, } - resp, err = ts.HandleRequest(context.Background(), tokenReq) + resp, err = ts.HandleRequest(namespace.TestContext(), tokenReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%v", err, resp) } tut := resp.Auth.ClientToken - saltTut, err := ts.SaltID(context.Background(), tut) + saltTut, err := ts.SaltID(namespace.TestContext(), tut) if err != nil { t.Fatal(err) } - te, err := ts.lookupSalted(context.Background(), saltTut, false) + te, err := ts.lookupInternal(namespace.TestContext(), saltTut, true, false) if err != nil { t.Fatal(err) } @@ -3552,7 +3638,7 @@ func TestTokenStore_RevokeUseCountToken(t *testing.T) { t.Fatalf("bad: %d", te.NumUses) } - te, err = ts.UseToken(context.Background(), te) + te, err = ts.UseToken(namespace.TestContext(), te) if err != nil { t.Fatal(err) } @@ -3564,7 +3650,7 @@ func TestTokenStore_RevokeUseCountToken(t *testing.T) { } // Should return no entry because it's tainted - te, err = ts.lookupSalted(context.Background(), saltTut, false) + te, err = ts.lookupInternal(namespace.TestContext(), saltTut, true, false) if err != nil { t.Fatal(err) } @@ -3578,7 +3664,7 @@ func TestTokenStore_RevokeUseCountToken(t *testing.T) { ClientToken: tut, Operation: logical.UpdateOperation, } - resp, err = ts.HandleRequest(context.Background(), req) + resp, err = ts.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatal(err) } @@ -3590,7 +3676,7 @@ func TestTokenStore_RevokeUseCountToken(t *testing.T) { } // Should return tainted entries - te, err = ts.lookupSalted(context.Background(), saltTut, true) + te, err = ts.lookupInternal(namespace.TestContext(), saltTut, true, true) if err != nil { t.Fatal(err) } @@ -3603,17 +3689,17 @@ func TestTokenStore_RevokeUseCountToken(t *testing.T) { origDestroyCubbyhole := ts.cubbyholeDestroyer - ts.cubbyholeDestroyer = func(context.Context, *TokenStore, string) error { + ts.cubbyholeDestroyer = func(context.Context, *TokenStore, *logical.TokenEntry) error { return fmt.Errorf("keep it frosty") } - err = ts.revokeSalted(context.Background(), saltTut, false) + err = ts.revokeInternal(namespace.TestContext(), saltTut, false) if err == nil { t.Fatalf("expected err") } // Since revocation failed we should still be able to get a token - te, err = ts.lookupSalted(context.Background(), saltTut, true) + te, err = ts.lookupInternal(namespace.TestContext(), saltTut, true, true) if err != nil { t.Fatal(err) } @@ -3622,7 +3708,7 @@ func TestTokenStore_RevokeUseCountToken(t *testing.T) { } // Check the race condition situation by making the process sleep - ts.cubbyholeDestroyer = func(context.Context, *TokenStore, string) error { + ts.cubbyholeDestroyer = func(context.Context, *TokenStore, *logical.TokenEntry) error { time.Sleep(1 * time.Second) return fmt.Errorf("keep it frosty") } @@ -3630,7 +3716,7 @@ func TestTokenStore_RevokeUseCountToken(t *testing.T) { go func() { cubbyFuncLock.RLock() - err := ts.revokeSalted(context.Background(), saltTut, false) + err := ts.revokeInternal(namespace.TestContext(), saltTut, false) cubbyFuncLock.RUnlock() if err == nil { t.Fatalf("expected error") @@ -3639,7 +3725,7 @@ func TestTokenStore_RevokeUseCountToken(t *testing.T) { // Give time for the function to start and grab locks time.Sleep(200 * time.Millisecond) - te, err = ts.lookupSalted(context.Background(), saltTut, true) + te, err = ts.lookupInternal(namespace.TestContext(), saltTut, true, true) if err != nil { t.Fatal(err) } @@ -3655,12 +3741,12 @@ func TestTokenStore_RevokeUseCountToken(t *testing.T) { defer cubbyFuncLock.Unlock() ts.cubbyholeDestroyer = origDestroyCubbyhole - err = ts.revokeSalted(context.Background(), saltTut, false) + err = ts.revokeInternal(namespace.TestContext(), saltTut, false) if err != nil { t.Fatal(err) } - te, err = ts.lookupSalted(context.Background(), saltTut, true) + te, err = ts.lookupInternal(namespace.TestContext(), saltTut, true, true) if err != nil { t.Fatal(err) } @@ -3685,7 +3771,7 @@ func TestTokenStore_HandleTidyCase1(t *testing.T) { Path: "accessors/", ClientToken: root, } - resp, err = ts.HandleRequest(context.Background(), accessorListReq) + resp, err = ts.HandleRequest(namespace.TestContext(), accessorListReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%v", err, resp) } @@ -3711,7 +3797,7 @@ func TestTokenStore_HandleTidyCase1(t *testing.T) { // Creation of another token should end up with incrementing // the number of accessors // the storage - resp, err = ts.HandleRequest(context.Background(), accessorListReq) + resp, err = ts.HandleRequest(namespace.TestContext(), accessorListReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%v", err, resp) } @@ -3727,30 +3813,29 @@ func TestTokenStore_HandleTidyCase1(t *testing.T) { // cubbyhole and by not deleting its secondary index, its accessor and // associated leases. - saltedTut, err := ts.SaltID(context.Background(), tut) + saltedTut, err := ts.SaltID(namespace.TestContext(), tut) if err != nil { t.Fatal(err) } - _, err = ts.lookupSalted(context.Background(), saltedTut, true) + te, err := ts.lookupInternal(namespace.TestContext(), saltedTut, true, true) if err != nil { t.Fatalf("failed to lookup token: %v", err) } // Destroy the token index - path := lookupPrefix + saltedTut - if ts.view.Delete(context.Background(), path); err != nil { + if ts.idView(namespace.RootNamespace).Delete(namespace.TestContext(), saltedTut); err != nil { t.Fatalf("failed to delete token entry: %v", err) } // Destroy the cubby space - err = ts.destroyCubbyhole(context.Background(), saltedTut) + err = ts.cubbyholeDestroyer(namespace.TestContext(), ts, te) if err != nil { t.Fatalf("failed to destroyCubbyhole: %v", err) } // Leaking of accessor should have resulted in no change to the number // of accessors - resp, err = ts.HandleRequest(context.Background(), accessorListReq) + resp, err = ts.HandleRequest(namespace.TestContext(), accessorListReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%v", err, resp) } @@ -3766,7 +3851,7 @@ func TestTokenStore_HandleTidyCase1(t *testing.T) { Operation: logical.UpdateOperation, ClientToken: root, } - resp, err = ts.HandleRequest(context.Background(), tidyReq) + resp, err = ts.HandleRequest(namespace.TestContext(), tidyReq) if err != nil { t.Fatal(err) } @@ -3781,7 +3866,7 @@ func TestTokenStore_HandleTidyCase1(t *testing.T) { time.Sleep(10 * time.Second) // Tidy should have removed all the dangling accessor entries - resp, err = ts.HandleRequest(context.Background(), accessorListReq) + resp, err = ts.HandleRequest(namespace.TestContext(), accessorListReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%v", err, resp) } @@ -3810,7 +3895,7 @@ func TestTokenStore_HandleTidy_parentCleanup(t *testing.T) { Path: "accessors/", ClientToken: root, } - resp, err = ts.HandleRequest(context.Background(), accessorListReq) + resp, err = ts.HandleRequest(namespace.TestContext(), accessorListReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%v", err, resp) } @@ -3846,7 +3931,7 @@ func TestTokenStore_HandleTidy_parentCleanup(t *testing.T) { // Creation of another token should end up with incrementing the number of // accessors the storage - resp, err = ts.HandleRequest(context.Background(), accessorListReq) + resp, err = ts.HandleRequest(namespace.TestContext(), accessorListReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%v", err, resp) } @@ -3862,30 +3947,29 @@ func TestTokenStore_HandleTidy_parentCleanup(t *testing.T) { // cubbyhole and by not deleting its secondary index, its accessor and // associated leases. - saltedTut, err := ts.SaltID(context.Background(), tut) + saltedTut, err := ts.SaltID(namespace.TestContext(), tut) if err != nil { t.Fatal(err) } - _, err = ts.lookupSalted(context.Background(), saltedTut, true) + te, err := ts.lookupInternal(namespace.TestContext(), saltedTut, true, true) if err != nil { t.Fatalf("failed to lookup token: %v", err) } // Destroy the token index - path := lookupPrefix + saltedTut - if ts.view.Delete(context.Background(), path); err != nil { + if ts.idView(namespace.RootNamespace).Delete(namespace.TestContext(), saltedTut); err != nil { t.Fatalf("failed to delete token entry: %v", err) } // Destroy the cubby space - err = ts.destroyCubbyhole(context.Background(), saltedTut) + err = ts.cubbyholeDestroyer(namespace.TestContext(), ts, te) if err != nil { t.Fatalf("failed to destroyCubbyhole: %v", err) } // Leaking of accessor should have resulted in no change to the number // of accessors - resp, err = ts.HandleRequest(context.Background(), accessorListReq) + resp, err = ts.HandleRequest(namespace.TestContext(), accessorListReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%v", err, resp) } @@ -3901,7 +3985,7 @@ func TestTokenStore_HandleTidy_parentCleanup(t *testing.T) { Operation: logical.UpdateOperation, ClientToken: root, } - resp, err = ts.HandleRequest(context.Background(), tidyReq) + resp, err = ts.HandleRequest(namespace.TestContext(), tidyReq) if err != nil { t.Fatal(err) } @@ -3916,7 +4000,7 @@ func TestTokenStore_HandleTidy_parentCleanup(t *testing.T) { time.Sleep(10 * time.Second) // Tidy should have removed all the dangling accessor entries - resp, err = ts.HandleRequest(context.Background(), accessorListReq) + resp, err = ts.HandleRequest(namespace.TestContext(), accessorListReq) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err:%v resp:%v", err, resp) } @@ -3936,7 +4020,7 @@ func TestTokenStore_HandleTidy_parentCleanup(t *testing.T) { "accessor": accessor, } - resp, err := ts.HandleRequest(context.Background(), req) + resp, err := ts.HandleRequest(namespace.TestContext(), req) if err != nil { t.Fatalf("err: %s", err) } @@ -3961,27 +4045,27 @@ func TestTokenStore_TidyLeaseRevocation(t *testing.T) { if err != nil { t.Fatal(err) } - err = exp.router.Mount(noop, "prod/aws/", &MountEntry{UUID: meUUID, Accessor: "awsaccessor"}, view) + err = exp.router.Mount(noop, "prod/aws/", &MountEntry{UUID: meUUID, Accessor: "awsaccessor", namespace: namespace.RootNamespace}, view) if err != nil { t.Fatal(err) } // Create new token - root, err := ts.rootToken(context.Background()) + root, err := ts.rootToken(namespace.TestContext()) if err != nil { t.Fatalf("err: %v", err) } + // Create a new token req := logical.TestRequest(t, logical.UpdateOperation, "create") req.ClientToken = root.ID req.Data["policies"] = []string{"default"} - resp, err := ts.HandleRequest(context.Background(), req) + resp, err := ts.HandleRequest(namespace.TestContext(), req) if err != nil || (resp != nil && resp.IsError()) { t.Fatalf("err: %v\nresp: %#v", err, resp) } - // Create a new token auth := &logical.Auth{ ClientToken: resp.Auth.ClientToken, LeaseOptions: logical.LeaseOptions{ @@ -3989,11 +4073,26 @@ func TestTokenStore_TidyLeaseRevocation(t *testing.T) { Renewable: true, }, } - err = exp.RegisterAuth(context.Background(), "auth/token/create", auth) + + te := &logical.TokenEntry{ + Path: resp.Auth.CreationPath, + NamespaceID: namespace.RootNamespaceID, + } + + err = exp.RegisterAuth(namespace.TestContext(), te, auth) if err != nil { t.Fatalf("err: %v", err) } + // Verify token entry through lookup + testTokenEntry, err := ts.Lookup(namespace.TestContext(), resp.Auth.ClientToken) + if err != nil { + t.Fatal(err) + } + if testTokenEntry == nil { + t.Fatal("token entry was nil") + } + tut := resp.Auth.ClientToken req = &logical.Request{ @@ -4011,7 +4110,7 @@ func TestTokenStore_TidyLeaseRevocation(t *testing.T) { leases := []string{} for i := 0; i < 10; i++ { - leaseID, err := exp.Register(context.Background(), req, resp) + leaseID, err := exp.Register(namespace.TestContext(), req, resp) if err != nil { t.Fatal(err) } @@ -4020,7 +4119,15 @@ func TestTokenStore_TidyLeaseRevocation(t *testing.T) { sort.Strings(leases) - storedLeases, err := exp.lookupLeasesByToken(context.Background(), tut) + te, err = ts.lookupInternal(namespace.TestContext(), tut, false, true) + if err != nil { + t.Fatalf("failed to lookup token: %v", err) + } + if te == nil { + t.Fatal("got nil token entry") + } + + storedLeases, err := exp.lookupLeasesByToken(namespace.TestContext(), te) if err != nil { t.Fatal(err) } @@ -4030,11 +4137,11 @@ func TestTokenStore_TidyLeaseRevocation(t *testing.T) { } // Now, delete the token entry. The leases should still exist. - saltedTut, err := ts.SaltID(context.Background(), tut) + saltedTut, err := ts.SaltID(namespace.TestContext(), tut) if err != nil { t.Fatal(err) } - te, err := ts.lookupSalted(context.Background(), saltedTut, true) + te, err = ts.lookupInternal(namespace.TestContext(), saltedTut, true, true) if err != nil { t.Fatalf("failed to lookup token: %v", err) } @@ -4043,11 +4150,10 @@ func TestTokenStore_TidyLeaseRevocation(t *testing.T) { } // Destroy the token index - path := lookupPrefix + saltedTut - if ts.view.Delete(context.Background(), path); err != nil { + if ts.idView(namespace.RootNamespace).Delete(namespace.TestContext(), saltedTut); err != nil { t.Fatalf("failed to delete token entry: %v", err) } - te, err = ts.lookupSalted(context.Background(), saltedTut, true) + te, err = ts.lookupInternal(namespace.TestContext(), saltedTut, true, true) if err != nil { t.Fatalf("failed to lookup token: %v", err) } @@ -4056,7 +4162,7 @@ func TestTokenStore_TidyLeaseRevocation(t *testing.T) { } // Verify leases still exist - storedLeases, err = exp.lookupLeasesByToken(context.Background(), tut) + storedLeases, err = exp.lookupLeasesByToken(namespace.TestContext(), testTokenEntry) if err != nil { t.Fatal(err) } @@ -4066,12 +4172,12 @@ func TestTokenStore_TidyLeaseRevocation(t *testing.T) { } // Call tidy - ts.handleTidy(context.Background(), &logical.Request{}, nil) + ts.handleTidy(namespace.TestContext(), &logical.Request{}, nil) time.Sleep(200 * time.Millisecond) // Verify leases are gone - storedLeases, err = exp.lookupLeasesByToken(context.Background(), tut) + storedLeases, err = exp.lookupLeasesByToken(namespace.TestContext(), testTokenEntry) if err != nil { t.Fatal(err) } diff --git a/vault/token_store_util.go b/vault/token_store_util.go new file mode 100644 index 0000000000..ca1f39a13d --- /dev/null +++ b/vault/token_store_util.go @@ -0,0 +1,27 @@ +// +build !enterprise + +package vault + +import ( + "github.com/hashicorp/vault/helper/namespace" +) + +func (ts *TokenStore) baseView(ns *namespace.Namespace) *BarrierView { + return ts.baseBarrierView +} + +func (ts *TokenStore) idView(ns *namespace.Namespace) *BarrierView { + return ts.idBarrierView +} + +func (ts *TokenStore) accessorView(ns *namespace.Namespace) *BarrierView { + return ts.accessorBarrierView +} + +func (ts *TokenStore) parentView(ns *namespace.Namespace) *BarrierView { + return ts.parentBarrierView +} + +func (ts *TokenStore) rolesView(ns *namespace.Namespace) *BarrierView { + return ts.rolesBarrierView +} diff --git a/vault/wrapping.go b/vault/wrapping.go index 177b7bbcc5..81c750a07d 100644 --- a/vault/wrapping.go +++ b/vault/wrapping.go @@ -16,6 +16,7 @@ import ( "github.com/hashicorp/errwrap" "github.com/hashicorp/vault/helper/consts" "github.com/hashicorp/vault/helper/jsonutil" + "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/logical" ) @@ -74,6 +75,10 @@ func (c *Core) ensureWrappingKey(ctx context.Context) error { } func (c *Core) wrapInCubbyhole(ctx context.Context, req *logical.Request, resp *logical.Response, auth *logical.Auth) (*logical.Response, error) { + if c.perfStandby { + return forwardWrapRequest(ctx, c, req, resp, auth) + } + // Before wrapping, obey special rules for listing: if no entries are // found, 404. This prevents unwrapping only to find empty data. if req.Operation == logical.ListOperation { @@ -104,6 +109,20 @@ DONELISTHANDLING: var err error sealWrap := resp.WrapInfo.SealWrap + var ns *namespace.Namespace + // If we are creating a JWT wrapping token we always want them to live in + // the root namespace. These are only used for replication and plugin setup. + switch resp.WrapInfo.Format { + case "jwt": + ns = namespace.RootNamespace + ctx = namespace.ContextWithNamespace(ctx, ns) + default: + ns, err = namespace.FromContext(ctx) + if err != nil { + return nil, err + } + } + // If we are wrapping, the first part (performed in this functions) happens // before auditing so that resp.WrapInfo.Token can contain the HMAC'd // wrapping token ID in the audit logs, so that it can be determined from @@ -116,6 +135,7 @@ DONELISTHANDLING: TTL: resp.WrapInfo.TTL, NumUses: 1, ExplicitMaxTTL: resp.WrapInfo.TTL, + NamespaceID: ns.ID, } if err := c.tokenStore.create(ctx, &te); err != nil { @@ -179,6 +199,7 @@ DONELISTHANDLING: SealWrap: true, } } + cubbyReq.SetTokenEntry(&te) // During a rewrap, store the original response, don't wrap it again. if req.Path == "sys/wrapping/rewrap" { @@ -259,7 +280,7 @@ DONELISTHANDLING: } // Register the wrapped token with the expiration manager - if err := c.expiration.RegisterAuth(ctx, te.Path, wAuth); err != nil { + if err := c.expiration.RegisterAuth(ctx, &te, wAuth); err != nil { // Revoke since it's not yet being tracked for expiration c.tokenStore.revokeOrphan(ctx, te.ID) c.logger.Error("failed to register cubbyhole wrapping token lease", "request_path", req.Path, "error", err) @@ -270,7 +291,7 @@ DONELISTHANDLING: } // ValidateWrappingToken checks whether a token is a wrapping token. -func (c *Core) ValidateWrappingToken(req *logical.Request) (bool, error) { +func (c *Core) ValidateWrappingToken(ctx context.Context, req *logical.Request) (bool, error) { if req == nil { return false, fmt.Errorf("invalid request") } @@ -325,11 +346,11 @@ func (c *Core) ValidateWrappingToken(req *logical.Request) (bool, error) { c.stateLock.RLock() defer c.stateLock.RUnlock() - if c.standby { + if c.standby && !c.perfStandby { return false, consts.ErrStandby } - te, err := c.tokenStore.Lookup(c.activeContext, token) + te, err := c.tokenStore.Lookup(ctx, token) if err != nil { return false, err } @@ -345,5 +366,11 @@ func (c *Core) ValidateWrappingToken(req *logical.Request) (bool, error) { return false, nil } + if !thirdParty { + req.ClientTokenAccessor = te.Accessor + req.ClientTokenRemainingUses = te.NumUses + req.SetTokenEntry(te) + } + return true, nil } diff --git a/vault/wrapping_util.go b/vault/wrapping_util.go new file mode 100644 index 0000000000..475fd35e1b --- /dev/null +++ b/vault/wrapping_util.go @@ -0,0 +1,13 @@ +// +build !enterprise + +package vault + +import ( + "context" + + "github.com/hashicorp/vault/logical" +) + +func forwardWrapRequest(context.Context, *Core, *logical.Request, *logical.Response, *logical.Auth) (*logical.Response, error) { + return nil, nil +} diff --git a/version/version_base.go b/version/version_base.go index 648aafc77b..94d2b26254 100644 --- a/version/version_base.go +++ b/version/version_base.go @@ -2,7 +2,7 @@ package version func init() { // The main version number that is being run at the moment. - Version = "0.11.1" + Version = "0.11.0" // A pre-release marker for the version. If this is "" (empty string) // then it means that it is a final release. Otherwise, this is a pre-release