From a78f7d7a348147eab251ec7ef6538335074f960a Mon Sep 17 00:00:00 2001 From: Matt Keeler Date: Wed, 18 Dec 2019 13:46:53 -0500 Subject: [PATCH] OSS changes for implementing token based namespace inferencing remove debug log --- agent/acl.go | 91 +++++++++----- agent/acl_test.go | 197 +++++++++++++++++++------------ agent/agent.go | 5 +- agent/agent_endpoint.go | 78 +++++++++--- agent/checks/check.go | 34 +++--- agent/consul/acl.go | 90 ++++++++++---- agent/consul/acl_client.go | 24 ++++ agent/consul/acl_endpoint.go | 186 +++++++++++++---------------- agent/consul/acl_server.go | 24 ++++ agent/consul/catalog_endpoint.go | 115 ++++++++++-------- agent/consul/health_endpoint.go | 61 ++++++---- agent/consul/kvs_endpoint.go | 76 ++++++------ agent/consul/session_endpoint.go | 82 ++++++------- agent/structs/structs_oss.go | 4 + 14 files changed, 644 insertions(+), 423 deletions(-) diff --git a/agent/acl.go b/agent/acl.go index 0491cff931..30788b96a2 100644 --- a/agent/acl.go +++ b/agent/acl.go @@ -13,6 +13,13 @@ import ( // clients. Some of the enforcement is normative (e.g. self and monitor) // and some is informative (e.g. catalog and health). func (a *Agent) resolveToken(id string) (acl.Authorizer, error) { + return a.resolveTokenAndDefaultMeta(id, nil, nil) +} + +// resolveTokenAndDefaultMeta is used to resolve an ACL token secret to an +// acl.Authorizer and to default any enterprise specific metadata for the request. +// The defaulted metadata is then used to fill in an acl.AuthorizationContext. +func (a *Agent) resolveTokenAndDefaultMeta(id string, entMeta *structs.EnterpriseMeta, authzContext *acl.AuthorizerContext) (acl.Authorizer, error) { // ACLs are disabled if !a.delegate.ACLsEnabled() { return nil, nil @@ -30,7 +37,8 @@ func (a *Agent) resolveToken(id string) (acl.Authorizer, error) { if a.tokens.IsAgentMasterToken(id) { return a.aclMasterAuthorizer, nil } - return a.delegate.ResolveToken(id) + + return a.delegate.ResolveTokenAndDefaultMeta(id, entMeta, authzContext) } func (a *Agent) initializeACLs() error { @@ -67,25 +75,30 @@ func (a *Agent) initializeACLs() error { // the given token. func (a *Agent) vetServiceRegister(token string, service *structs.NodeService) error { // Resolve the token and bail if ACLs aren't enabled. - rule, err := a.resolveToken(token) + authz, err := a.resolveToken(token) if err != nil { return err } - if rule == nil { + + return a.vetServiceRegisterWithAuthorizer(authz, service) +} + +func (a *Agent) vetServiceRegisterWithAuthorizer(authz acl.Authorizer, service *structs.NodeService) error { + if authz == nil { return nil } var authzContext acl.AuthorizerContext service.FillAuthzContext(&authzContext) // Vet the service itself. - if rule.ServiceWrite(service.Service, &authzContext) != acl.Allow { + if authz.ServiceWrite(service.Service, &authzContext) != acl.Allow { return acl.ErrPermissionDenied } // Vet any service that might be getting overwritten. if existing := a.State.Service(service.CompoundServiceID()); existing != nil { existing.FillAuthzContext(&authzContext) - if rule.ServiceWrite(existing.Service, &authzContext) != acl.Allow { + if authz.ServiceWrite(existing.Service, &authzContext) != acl.Allow { return acl.ErrPermissionDenied } } @@ -94,7 +107,7 @@ func (a *Agent) vetServiceRegister(token string, service *structs.NodeService) e // since it can be discovered as an instance of that service. if service.Kind == structs.ServiceKindConnectProxy { service.FillAuthzContext(&authzContext) - if rule.ServiceWrite(service.Proxy.DestinationServiceName, &authzContext) != acl.Allow { + if authz.ServiceWrite(service.Proxy.DestinationServiceName, &authzContext) != acl.Allow { return acl.ErrPermissionDenied } } @@ -106,11 +119,16 @@ func (a *Agent) vetServiceRegister(token string, service *structs.NodeService) e // token. func (a *Agent) vetServiceUpdate(token string, serviceID structs.ServiceID) error { // Resolve the token and bail if ACLs aren't enabled. - rule, err := a.resolveToken(token) + authz, err := a.resolveToken(token) if err != nil { return err } - if rule == nil { + + return a.vetServiceUpdateWithAuthorizer(authz, serviceID) +} + +func (a *Agent) vetServiceUpdateWithAuthorizer(authz acl.Authorizer, serviceID structs.ServiceID) error { + if authz == nil { return nil } @@ -119,7 +137,7 @@ func (a *Agent) vetServiceUpdate(token string, serviceID structs.ServiceID) erro // Vet any changes based on the existing services's info. if existing := a.State.Service(serviceID); existing != nil { existing.FillAuthzContext(&authzContext) - if rule.ServiceWrite(existing.Service, &authzContext) != acl.Allow { + if authz.ServiceWrite(existing.Service, &authzContext) != acl.Allow { return acl.ErrPermissionDenied } } else { @@ -133,11 +151,16 @@ func (a *Agent) vetServiceUpdate(token string, serviceID structs.ServiceID) erro // given token. func (a *Agent) vetCheckRegister(token string, check *structs.HealthCheck) error { // Resolve the token and bail if ACLs aren't enabled. - rule, err := a.resolveToken(token) + authz, err := a.resolveToken(token) if err != nil { return err } - if rule == nil { + + return a.vetCheckRegisterWithAuthorizer(authz, check) +} + +func (a *Agent) vetCheckRegisterWithAuthorizer(authz acl.Authorizer, check *structs.HealthCheck) error { + if authz == nil { return nil } @@ -145,11 +168,11 @@ func (a *Agent) vetCheckRegister(token string, check *structs.HealthCheck) error check.FillAuthzContext(&authzContext) // Vet the check itself. if len(check.ServiceName) > 0 { - if rule.ServiceWrite(check.ServiceName, &authzContext) != acl.Allow { + if authz.ServiceWrite(check.ServiceName, &authzContext) != acl.Allow { return acl.ErrPermissionDenied } } else { - if rule.NodeWrite(a.config.NodeName, &authzContext) != acl.Allow { + if authz.NodeWrite(a.config.NodeName, &authzContext) != acl.Allow { return acl.ErrPermissionDenied } } @@ -157,11 +180,11 @@ func (a *Agent) vetCheckRegister(token string, check *structs.HealthCheck) error // Vet any check that might be getting overwritten. if existing := a.State.Check(check.CompoundCheckID()); existing != nil { if len(existing.ServiceName) > 0 { - if rule.ServiceWrite(existing.ServiceName, &authzContext) != acl.Allow { + if authz.ServiceWrite(existing.ServiceName, &authzContext) != acl.Allow { return acl.ErrPermissionDenied } } else { - if rule.NodeWrite(a.config.NodeName, &authzContext) != acl.Allow { + if authz.NodeWrite(a.config.NodeName, &authzContext) != acl.Allow { return acl.ErrPermissionDenied } } @@ -173,11 +196,16 @@ func (a *Agent) vetCheckRegister(token string, check *structs.HealthCheck) error // vetCheckUpdate makes sure that a check update is allowed by the given token. func (a *Agent) vetCheckUpdate(token string, checkID structs.CheckID) error { // Resolve the token and bail if ACLs aren't enabled. - rule, err := a.resolveToken(token) + authz, err := a.resolveToken(token) if err != nil { return err } - if rule == nil { + + return a.vetCheckUpdateWithAuthorizer(authz, checkID) +} + +func (a *Agent) vetCheckUpdateWithAuthorizer(authz acl.Authorizer, checkID structs.CheckID) error { + if authz == nil { return nil } @@ -187,11 +215,11 @@ func (a *Agent) vetCheckUpdate(token string, checkID structs.CheckID) error { // Vet any changes based on the existing check's info. if existing := a.State.Check(checkID); existing != nil { if len(existing.ServiceName) > 0 { - if rule.ServiceWrite(existing.ServiceName, &authzContext) != acl.Allow { + if authz.ServiceWrite(existing.ServiceName, &authzContext) != acl.Allow { return acl.ErrPermissionDenied } } else { - if rule.NodeWrite(a.config.NodeName, &authzContext) != acl.Allow { + if authz.NodeWrite(a.config.NodeName, &authzContext) != acl.Allow { return acl.ErrPermissionDenied } } @@ -233,19 +261,23 @@ func (a *Agent) filterMembers(token string, members *[]serf.Member) error { // filterServices redacts services that the token doesn't have access to. func (a *Agent) filterServices(token string, services *map[structs.ServiceID]*structs.NodeService) error { // Resolve the token and bail if ACLs aren't enabled. - rule, err := a.resolveToken(token) + authz, err := a.resolveToken(token) if err != nil { return err } - if rule == nil { + + return a.filterServicesWithAuthorizer(authz, services) +} + +func (a *Agent) filterServicesWithAuthorizer(authz acl.Authorizer, services *map[structs.ServiceID]*structs.NodeService) error { + if authz == nil { return nil } - var authzContext acl.AuthorizerContext // Filter out services based on the service policy. for id, service := range *services { service.FillAuthzContext(&authzContext) - if rule.ServiceRead(service.Service, &authzContext) == acl.Allow { + if authz.ServiceRead(service.Service, &authzContext) == acl.Allow { continue } a.logger.Printf("[DEBUG] agent: dropping service %q from result due to ACLs", id.String()) @@ -257,11 +289,16 @@ func (a *Agent) filterServices(token string, services *map[structs.ServiceID]*st // filterChecks redacts checks that the token doesn't have access to. func (a *Agent) filterChecks(token string, checks *map[structs.CheckID]*structs.HealthCheck) error { // Resolve the token and bail if ACLs aren't enabled. - rule, err := a.resolveToken(token) + authz, err := a.resolveToken(token) if err != nil { return err } - if rule == nil { + + return a.filterChecksWithAuthorizer(authz, checks) +} + +func (a *Agent) filterChecksWithAuthorizer(authz acl.Authorizer, checks *map[structs.CheckID]*structs.HealthCheck) error { + if authz == nil { return nil } @@ -270,12 +307,12 @@ func (a *Agent) filterChecks(token string, checks *map[structs.CheckID]*structs. for id, check := range *checks { if len(check.ServiceName) > 0 { check.FillAuthzContext(&authzContext) - if rule.ServiceRead(check.ServiceName, &authzContext) == acl.Allow { + if authz.ServiceRead(check.ServiceName, &authzContext) == acl.Allow { continue } } else { structs.DefaultEnterpriseMeta().FillAuthzContext(&authzContext) - if rule.NodeRead(a.config.NodeName, &authzContext) == acl.Allow { + if authz.NodeRead(a.config.NodeName, &authzContext) == acl.Allow { continue } } diff --git a/agent/acl_test.go b/agent/acl_test.go index d7c10c2c35..97635208bd 100644 --- a/agent/acl_test.go +++ b/agent/acl_test.go @@ -47,7 +47,7 @@ type TestACLAgent struct { // Shutdown() is called. DataDir string - resolveTokenFn func(string) (acl.Authorizer, error) + resolveTokenFn func(string) (structs.ACLIdentity, acl.Authorizer, error) *Agent } @@ -55,7 +55,7 @@ type TestACLAgent struct { // NewTestACLAGent does just enough so that all the code within agent/acl.go can work // Basically it needs a local state for some of the vet* functions, a logger and a delegate. // The key is that we are the delegate so we can control the ResolveToken responses -func NewTestACLAgent(t *testing.T, name string, hcl string, resolveFn func(string) (acl.Authorizer, error)) *TestACLAgent { +func NewTestACLAgent(t *testing.T, name string, hcl string, resolveFn func(string) (structs.ACLIdentity, acl.Authorizer, error)) *TestACLAgent { a := &TestACLAgent{Name: name, HCL: hcl, resolveTokenFn: resolveFn} hclDataDir := `data_dir = "acl-agent"` @@ -98,9 +98,38 @@ func (a *TestACLAgent) ResolveToken(secretID string) (acl.Authorizer, error) { panic("This agent is useless without providing a token resolution function") } + _, authz, err := a.resolveTokenFn(secretID) + return authz, err +} + +func (a *TestACLAgent) ResolveTokenToIdentityAndAuthorizer(secretID string) (structs.ACLIdentity, acl.Authorizer, error) { + if a.resolveTokenFn == nil { + panic("This agent is useless without providing a token resolution function") + } + return a.resolveTokenFn(secretID) } +func (a *TestACLAgent) ResolveTokenAndDefaultMeta(secretID string, entMeta *structs.EnterpriseMeta, authzContext *acl.AuthorizerContext) (acl.Authorizer, error) { + identity, authz, err := a.ResolveTokenToIdentityAndAuthorizer(secretID) + if err != nil { + return nil, err + } + + // Default the EnterpriseMeta based on the Tokens meta or actual defaults + // in the case of unknown identity + if identity != nil { + entMeta.Merge(identity.EnterpriseMetadata()) + } else { + entMeta.Merge(structs.DefaultEnterpriseMeta()) + } + + // Use the meta to fill in the ACL authorization context + entMeta.FillAuthzContext(authzContext) + + return authz, err +} + // All of these are stubs to satisfy the interface func (a *TestACLAgent) Encrypted() bool { return false @@ -150,9 +179,9 @@ func TestACL_Version8(t *testing.T) { t.Parallel() t.Run("version 8 disabled", func(t *testing.T) { - resolveFn := func(string) (acl.Authorizer, error) { + resolveFn := func(string) (structs.ACLIdentity, acl.Authorizer, error) { require.Fail(t, "should not have called delegate.ResolveToken") - return nil, fmt.Errorf("should not have called delegate.ResolveToken") + return nil, nil, fmt.Errorf("should not have called delegate.ResolveToken") } a := NewTestACLAgent(t, t.Name(), TestACLConfig()+` @@ -166,9 +195,9 @@ func TestACL_Version8(t *testing.T) { t.Run("version 8 enabled", func(t *testing.T) { called := false - resolveFn := func(string) (acl.Authorizer, error) { + resolveFn := func(string) (structs.ACLIdentity, acl.Authorizer, error) { called = true - return nil, acl.ErrNotFound + return nil, nil, acl.ErrNotFound } a := NewTestACLAgent(t, t.Name(), TestACLConfig()+` acl_enforce_version_8 = true @@ -183,9 +212,9 @@ func TestACL_Version8(t *testing.T) { func TestACL_AgentMasterToken(t *testing.T) { t.Parallel() - resolveFn := func(string) (acl.Authorizer, error) { + resolveFn := func(string) (structs.ACLIdentity, acl.Authorizer, error) { require.Fail(t, "should not have called delegate.ResolveToken") - return nil, fmt.Errorf("should not have called delegate.ResolveToken") + return nil, nil, fmt.Errorf("should not have called delegate.ResolveToken") } a := NewTestACLAgent(t, t.Name(), TestACLConfig(), resolveFn) @@ -203,9 +232,9 @@ func TestACL_AgentMasterToken(t *testing.T) { func TestACL_RootAuthorizersDenied(t *testing.T) { t.Parallel() - resolveFn := func(string) (acl.Authorizer, error) { + resolveFn := func(string) (structs.ACLIdentity, acl.Authorizer, error) { require.Fail(t, "should not have called delegate.ResolveToken") - return nil, fmt.Errorf("should not have called delegate.ResolveToken") + return nil, nil, fmt.Errorf("should not have called delegate.ResolveToken") } a := NewTestACLAgent(t, t.Name(), TestACLConfig(), resolveFn) @@ -227,54 +256,72 @@ func authzFromPolicy(policy *acl.Policy, cfg *acl.Config) (acl.Authorizer, error return acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, cfg) } -// catalogPolicy supplies some standard policies to help with testing the -// catalog-related vet and filter functions. -func catalogPolicy(token string) (acl.Authorizer, error) { - switch token { +type testToken struct { + token structs.ACLToken + // yes the rules can exist on the token itself but that is legacy behavior + // that I would prefer these tests not rely on + rules string +} - case "node-ro": - return authzFromPolicy(&acl.Policy{ - PolicyRules: acl.PolicyRules{ - NodePrefixes: []*acl.NodeRule{ - &acl.NodeRule{Name: "Node", Policy: "read"}, - }, +var ( + nodeROSecret = "7e80d017-bccc-492f-8dec-65f03aeaebf3" + nodeRWSecret = "e3586ee5-02a2-4bf4-9ec3-9c4be7606e8c" + serviceROSecret = "3d2c8552-df3b-4da7-9890-36885cbf56ac" + serviceRWSecret = "4a1017a2-f788-4be3-93f2-90566f1340bb" + otherRWSecret = "a38e8016-91b6-4876-b3e7-a307abbb2002" + + testTokens = map[string]testToken{ + nodeROSecret: testToken{ + token: structs.ACLToken{ + AccessorID: "9df2d1a4-2d07-414e-8ead-6053f56ed2eb", + SecretID: nodeROSecret, }, - }, nil) - case "node-rw": - return authzFromPolicy(&acl.Policy{ - PolicyRules: acl.PolicyRules{ - NodePrefixes: []*acl.NodeRule{ - &acl.NodeRule{Name: "Node", Policy: "write"}, - }, + rules: `node_prefix "Node" { policy = "read" }`, + }, + nodeRWSecret: testToken{ + token: structs.ACLToken{ + AccessorID: "efb6b7d5-d343-47c1-b4cb-aa6b94d2f490", + SecretID: nodeROSecret, }, - }, nil) - case "service-ro": - return authzFromPolicy(&acl.Policy{ - PolicyRules: acl.PolicyRules{ - ServicePrefixes: []*acl.ServiceRule{ - &acl.ServiceRule{Name: "service", Policy: "read"}, - }, + rules: `node_prefix "Node" { policy = "write" }`, + }, + serviceROSecret: testToken{ + token: structs.ACLToken{ + AccessorID: "0da53edb-36e5-4603-9c31-79965bad45f5", + SecretID: serviceROSecret, }, - }, nil) - case "service-rw": - return authzFromPolicy(&acl.Policy{ - PolicyRules: acl.PolicyRules{ - ServicePrefixes: []*acl.ServiceRule{ - &acl.ServiceRule{Name: "service", Policy: "write"}, - }, + rules: `service_prefix "service" { policy = "read" }`, + }, + serviceRWSecret: testToken{ + token: structs.ACLToken{ + AccessorID: "52504258-137a-41e6-9326-01f40e80872e", + SecretID: serviceRWSecret, }, - }, nil) - case "other-rw": - return authzFromPolicy(&acl.Policy{ - PolicyRules: acl.PolicyRules{ - ServicePrefixes: []*acl.ServiceRule{ - &acl.ServiceRule{Name: "other", Policy: "write"}, - }, + rules: `service_prefix "service" { policy = "write" }`, + }, + otherRWSecret: testToken{ + token: structs.ACLToken{ + AccessorID: "5e032c5b-c39e-4552-b5ad-8a9365b099c4", + SecretID: otherRWSecret, }, - }, nil) - default: - return nil, fmt.Errorf("unknown token %q", token) + rules: `service_prefix "other" { policy = "write" }`, + }, } +) + +func catalogPolicy(token string) (structs.ACLIdentity, acl.Authorizer, error) { + tok, ok := testTokens[token] + if !ok { + return nil, nil, acl.ErrNotFound + } + + policy, err := acl.NewPolicyFromSource("", 0, tok.rules, acl.SyntaxCurrent, nil, nil) + if err != nil { + return nil, nil, err + } + + authz, err := authzFromPolicy(policy, nil) + return &tok.token, authz, err } func TestACL_vetServiceRegister(t *testing.T) { @@ -282,14 +329,14 @@ func TestACL_vetServiceRegister(t *testing.T) { a := NewTestACLAgent(t, t.Name(), TestACLConfig(), catalogPolicy) // Register a new service, with permission. - err := a.vetServiceRegister("service-rw", &structs.NodeService{ + err := a.vetServiceRegister(serviceRWSecret, &structs.NodeService{ ID: "my-service", Service: "service", }) require.NoError(t, err) // Register a new service without write privs. - err = a.vetServiceRegister("service-ro", &structs.NodeService{ + err = a.vetServiceRegister(serviceROSecret, &structs.NodeService{ ID: "my-service", Service: "service", }) @@ -301,7 +348,7 @@ func TestACL_vetServiceRegister(t *testing.T) { ID: "my-service", Service: "other", }, "") - err = a.vetServiceRegister("service-rw", &structs.NodeService{ + err = a.vetServiceRegister(serviceRWSecret, &structs.NodeService{ ID: "my-service", Service: "service", }) @@ -313,7 +360,7 @@ func TestACL_vetServiceUpdate(t *testing.T) { a := NewTestACLAgent(t, t.Name(), TestACLConfig(), catalogPolicy) // Update a service that doesn't exist. - err := a.vetServiceUpdate("service-rw", structs.NewServiceID("my-service", nil)) + err := a.vetServiceUpdate(serviceRWSecret, structs.NewServiceID("my-service", nil)) require.Error(t, err) require.Contains(t, err.Error(), "Unknown service") @@ -322,11 +369,11 @@ func TestACL_vetServiceUpdate(t *testing.T) { ID: "my-service", Service: "service", }, "") - err = a.vetServiceUpdate("service-rw", structs.NewServiceID("my-service", nil)) + err = a.vetServiceUpdate(serviceRWSecret, structs.NewServiceID("my-service", nil)) require.NoError(t, err) // Update without write privs. - err = a.vetServiceUpdate("service-ro", structs.NewServiceID("my-service", nil)) + err = a.vetServiceUpdate(serviceROSecret, structs.NewServiceID("my-service", nil)) require.Error(t, err) require.True(t, acl.IsErrPermissionDenied(err)) } @@ -336,7 +383,7 @@ func TestACL_vetCheckRegister(t *testing.T) { a := NewTestACLAgent(t, t.Name(), TestACLConfig(), catalogPolicy) // Register a new service check with write privs. - err := a.vetCheckRegister("service-rw", &structs.HealthCheck{ + err := a.vetCheckRegister(serviceRWSecret, &structs.HealthCheck{ CheckID: types.CheckID("my-check"), ServiceID: "my-service", ServiceName: "service", @@ -344,7 +391,7 @@ func TestACL_vetCheckRegister(t *testing.T) { require.NoError(t, err) // Register a new service check without write privs. - err = a.vetCheckRegister("service-ro", &structs.HealthCheck{ + err = a.vetCheckRegister(serviceROSecret, &structs.HealthCheck{ CheckID: types.CheckID("my-check"), ServiceID: "my-service", ServiceName: "service", @@ -353,13 +400,13 @@ func TestACL_vetCheckRegister(t *testing.T) { require.True(t, acl.IsErrPermissionDenied(err)) // Register a new node check with write privs. - err = a.vetCheckRegister("node-rw", &structs.HealthCheck{ + err = a.vetCheckRegister(nodeRWSecret, &structs.HealthCheck{ CheckID: types.CheckID("my-check"), }) require.NoError(t, err) // Register a new node check without write privs. - err = a.vetCheckRegister("node-ro", &structs.HealthCheck{ + err = a.vetCheckRegister(nodeROSecret, &structs.HealthCheck{ CheckID: types.CheckID("my-check"), }) require.Error(t, err) @@ -376,7 +423,7 @@ func TestACL_vetCheckRegister(t *testing.T) { ServiceID: "my-service", ServiceName: "other", }, "") - err = a.vetCheckRegister("service-rw", &structs.HealthCheck{ + err = a.vetCheckRegister(serviceRWSecret, &structs.HealthCheck{ CheckID: types.CheckID("my-check"), ServiceID: "my-service", ServiceName: "service", @@ -388,7 +435,7 @@ func TestACL_vetCheckRegister(t *testing.T) { a.State.AddCheck(&structs.HealthCheck{ CheckID: types.CheckID("my-node-check"), }, "") - err = a.vetCheckRegister("service-rw", &structs.HealthCheck{ + err = a.vetCheckRegister(serviceRWSecret, &structs.HealthCheck{ CheckID: types.CheckID("my-node-check"), ServiceID: "my-service", ServiceName: "service", @@ -402,7 +449,7 @@ func TestACL_vetCheckUpdate(t *testing.T) { a := NewTestACLAgent(t, t.Name(), TestACLConfig(), catalogPolicy) // Update a check that doesn't exist. - err := a.vetCheckUpdate("node-rw", structs.NewCheckID("my-check", nil)) + err := a.vetCheckUpdate(nodeRWSecret, structs.NewCheckID("my-check", nil)) require.Error(t, err) require.Contains(t, err.Error(), "Unknown check") @@ -416,11 +463,11 @@ func TestACL_vetCheckUpdate(t *testing.T) { ServiceID: "my-service", ServiceName: "service", }, "") - err = a.vetCheckUpdate("service-rw", structs.NewCheckID("my-service-check", nil)) + err = a.vetCheckUpdate(serviceRWSecret, structs.NewCheckID("my-service-check", nil)) require.NoError(t, err) // Update service check without write privs. - err = a.vetCheckUpdate("service-ro", structs.NewCheckID("my-service-check", nil)) + err = a.vetCheckUpdate(serviceROSecret, structs.NewCheckID("my-service-check", nil)) require.Error(t, err) require.True(t, acl.IsErrPermissionDenied(err), "not permission denied: %s", err.Error()) @@ -428,11 +475,11 @@ func TestACL_vetCheckUpdate(t *testing.T) { a.State.AddCheck(&structs.HealthCheck{ CheckID: types.CheckID("my-node-check"), }, "") - err = a.vetCheckUpdate("node-rw", structs.NewCheckID("my-node-check", nil)) + err = a.vetCheckUpdate(nodeRWSecret, structs.NewCheckID("my-node-check", nil)) require.NoError(t, err) // Update without write privs. - err = a.vetCheckUpdate("node-ro", structs.NewCheckID("my-node-check", nil)) + err = a.vetCheckUpdate(nodeROSecret, structs.NewCheckID("my-node-check", nil)) require.Error(t, err) require.True(t, acl.IsErrPermissionDenied(err)) } @@ -442,7 +489,7 @@ func TestACL_filterMembers(t *testing.T) { a := NewTestACLAgent(t, t.Name(), TestACLConfig(), catalogPolicy) var members []serf.Member - require.NoError(t, a.filterMembers("node-ro", &members)) + require.NoError(t, a.filterMembers(nodeROSecret, &members)) require.Len(t, members, 0) members = []serf.Member{ @@ -450,7 +497,7 @@ func TestACL_filterMembers(t *testing.T) { serf.Member{Name: "Nope"}, serf.Member{Name: "Node 2"}, } - require.NoError(t, a.filterMembers("node-ro", &members)) + require.NoError(t, a.filterMembers(nodeROSecret, &members)) require.Len(t, members, 2) require.Equal(t, members[0].Name, "Node 1") require.Equal(t, members[1].Name, "Node 2") @@ -461,11 +508,11 @@ func TestACL_filterServices(t *testing.T) { a := NewTestACLAgent(t, t.Name(), TestACLConfig(), catalogPolicy) services := make(map[structs.ServiceID]*structs.NodeService) - require.NoError(t, a.filterServices("node-ro", &services)) + require.NoError(t, a.filterServices(nodeROSecret, &services)) services[structs.NewServiceID("my-service", nil)] = &structs.NodeService{ID: "my-service", Service: "service"} services[structs.NewServiceID("my-other", nil)] = &structs.NodeService{ID: "my-other", Service: "other"} - require.NoError(t, a.filterServices("service-ro", &services)) + require.NoError(t, a.filterServices(serviceROSecret, &services)) require.Contains(t, services, structs.NewServiceID("my-service", nil)) require.NotContains(t, services, structs.NewServiceID("my-other", nil)) } @@ -475,12 +522,12 @@ func TestACL_filterChecks(t *testing.T) { a := NewTestACLAgent(t, t.Name(), TestACLConfig(), catalogPolicy) checks := make(map[structs.CheckID]*structs.HealthCheck) - require.NoError(t, a.filterChecks("node-ro", &checks)) + require.NoError(t, a.filterChecks(nodeROSecret, &checks)) checks[structs.NewCheckID("my-node", nil)] = &structs.HealthCheck{} checks[structs.NewCheckID("my-service", nil)] = &structs.HealthCheck{ServiceName: "service"} checks[structs.NewCheckID("my-other", nil)] = &structs.HealthCheck{ServiceName: "other"} - require.NoError(t, a.filterChecks("service-ro", &checks)) + require.NoError(t, a.filterChecks(serviceROSecret, &checks)) _, ok := checks[structs.NewCheckID("my-node", nil)] require.False(t, ok) _, ok = checks[structs.NewCheckID("my-service", nil)] @@ -491,7 +538,7 @@ func TestACL_filterChecks(t *testing.T) { checks[structs.NewCheckID("my-node", nil)] = &structs.HealthCheck{} checks[structs.NewCheckID("my-service", nil)] = &structs.HealthCheck{ServiceName: "service"} checks[structs.NewCheckID("my-other", nil)] = &structs.HealthCheck{ServiceName: "other"} - require.NoError(t, a.filterChecks("node-ro", &checks)) + require.NoError(t, a.filterChecks(nodeROSecret, &checks)) _, ok = checks[structs.NewCheckID("my-node", nil)] require.True(t, ok) _, ok = checks[structs.NewCheckID("my-service", nil)] diff --git a/agent/agent.go b/agent/agent.go index 8f35ddfdea..2a1acaa1d0 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -136,6 +136,7 @@ type delegate interface { JoinLAN(addrs []string) (n int, err error) RemoveFailedNode(node string, prune bool) error ResolveToken(secretID string) (acl.Authorizer, error) + ResolveTokenAndDefaultMeta(secretID string, entMeta *structs.EnterpriseMeta, authzContext *acl.AuthorizerContext) (acl.Authorizer, error) RPC(method string, args interface{}, reply interface{}) error ACLsEnabled() bool UseLegacyACLs() bool @@ -2700,8 +2701,6 @@ func (a *Agent) addCheck(check *structs.HealthCheck, chkType *structs.CheckType, ttl.Start() a.checkTTLs[cid] = ttl - a.logger.Printf("[DEBUG] ttl checks: %+v", a.checkTTLs) - case chkType.IsHTTP(): if existing, ok := a.checkHTTPs[cid]; ok { existing.Stop() @@ -3160,7 +3159,7 @@ func (a *Agent) loadCheckState(check *structs.HealthCheck) error { // Check if the state has expired if time.Now().Unix() >= p.Expires { - a.logger.Printf("[DEBUG] agent: check state expired for %q, not restoring", cid) + a.logger.Printf("[DEBUG] agent: check state expired for %q, not restoring", cid.String()) return a.purgeCheckState(cid) } diff --git a/agent/agent_endpoint.go b/agent/agent_endpoint.go index ade9ee7ca5..f791a90e95 100644 --- a/agent/agent_endpoint.go +++ b/agent/agent_endpoint.go @@ -220,8 +220,13 @@ func (s *HTTPServer) AgentServices(resp http.ResponseWriter, req *http.Request) var filterExpression string s.parseFilter(req, &filterExpression) + authz, err := s.agent.resolveTokenAndDefaultMeta(token, &entMeta, nil) + if err != nil { + return nil, err + } + services := s.agent.State.Services(&entMeta) - if err := s.agent.filterServices(token, &services); err != nil { + if err := s.agent.filterServicesWithAuthorizer(authz, &services); err != nil { return nil, err } @@ -269,6 +274,12 @@ func (s *HTTPServer) AgentService(resp http.ResponseWriter, req *http.Request) ( return nil, err } + // need to resolve to default the meta + _, err := s.agent.resolveTokenAndDefaultMeta(token, &entMeta, nil) + if err != nil { + return nil, err + } + // Parse hash specially. Eventually this should happen in parseWait and end up // in QueryOptions but I didn't want to make very general changes right away. hash := req.URL.Query().Get("hash") @@ -291,13 +302,13 @@ func (s *HTTPServer) AgentService(resp http.ResponseWriter, req *http.Request) ( ws.Add(svcState.WatchCh) // Check ACLs. - rule, err := s.agent.resolveToken(token) + authz, err := s.agent.resolveToken(token) if err != nil { return "", nil, err } var authzContext acl.AuthorizerContext svc.FillAuthzContext(&authzContext) - if rule != nil && rule.ServiceRead(svc.Service, &authzContext) != acl.Allow { + if authz != nil && authz.ServiceRead(svc.Service, &authzContext) != acl.Allow { return "", nil, acl.ErrPermissionDenied } @@ -332,6 +343,11 @@ func (s *HTTPServer) AgentChecks(resp http.ResponseWriter, req *http.Request) (i return nil, err } + authz, err := s.agent.resolveTokenAndDefaultMeta(token, &entMeta, nil) + if err != nil { + return nil, err + } + var filterExpression string s.parseFilter(req, &filterExpression) filter, err := bexpr.CreateFilter(filterExpression, nil, nil) @@ -340,7 +356,7 @@ func (s *HTTPServer) AgentChecks(resp http.ResponseWriter, req *http.Request) (i } checks := s.agent.State.Checks(&entMeta) - if err := s.agent.filterChecks(token, &checks); err != nil { + if err := s.agent.filterChecksWithAuthorizer(authz, &checks); err != nil { return nil, err } @@ -480,6 +496,9 @@ func (s *HTTPServer) syncChanges() { } func (s *HTTPServer) AgentRegisterCheck(resp http.ResponseWriter, req *http.Request) (interface{}, error) { + var token string + s.parseToken(req, &token) + var args structs.CheckDefinition if err := s.parseEntMetaNoWildcard(req, &args.EnterpriseMeta); err != nil { return nil, err @@ -504,12 +523,17 @@ func (s *HTTPServer) AgentRegisterCheck(resp http.ResponseWriter, req *http.Requ return nil, nil } + authz, err := s.agent.resolveTokenAndDefaultMeta(token, &args.EnterpriseMeta, nil) + if err != nil { + return nil, err + } + // Construct the health check. health := args.HealthCheck(s.agent.config.NodeName) // Verify the check type. chkType := args.CheckType() - err := chkType.Validate() + err = chkType.Validate() if err != nil { resp.WriteHeader(http.StatusBadRequest) fmt.Fprint(resp, fmt.Errorf("Invalid check: %v", err)) @@ -528,9 +552,7 @@ func (s *HTTPServer) AgentRegisterCheck(resp http.ResponseWriter, req *http.Requ } // Get the provided token, if any, and vet against any ACL policies. - var token string - s.parseToken(req, &token) - if err := s.agent.vetCheckRegister(token, health); err != nil { + if err := s.agent.vetCheckRegisterWithAuthorizer(authz, health); err != nil { return nil, err } @@ -553,9 +575,14 @@ func (s *HTTPServer) AgentDeregisterCheck(resp http.ResponseWriter, req *http.Re return nil, err } + authz, err := s.agent.resolveTokenAndDefaultMeta(token, &checkID.EnterpriseMeta, nil) + if err != nil { + return nil, err + } + checkID.Normalize() - if err := s.agent.vetCheckUpdate(token, checkID); err != nil { + if err := s.agent.vetCheckUpdateWithAuthorizer(authz, checkID); err != nil { return nil, err } @@ -636,9 +663,14 @@ func (s *HTTPServer) agentCheckUpdate(resp http.ResponseWriter, req *http.Reques return nil, err } + authz, err := s.agent.resolveTokenAndDefaultMeta(token, &cid.EnterpriseMeta, nil) + if err != nil { + return nil, err + } + cid.Normalize() - if err := s.agent.vetCheckUpdate(token, cid); err != nil { + if err := s.agent.vetCheckUpdateWithAuthorizer(authz, cid); err != nil { return nil, err } @@ -808,6 +840,14 @@ func (s *HTTPServer) AgentRegisterService(resp http.ResponseWriter, req *http.Re return nil, nil } + var token string + s.parseToken(req, &token) + + authz, err := s.agent.resolveTokenAndDefaultMeta(token, &args.EnterpriseMeta, nil) + if err != nil { + return nil, err + } + // Get the node service. ns := args.NodeService() if ns.Weights != nil { @@ -864,9 +904,7 @@ func (s *HTTPServer) AgentRegisterService(resp http.ResponseWriter, req *http.Re } // Get the provided token, if any, and vet against any ACL policies. - var token string - s.parseToken(req, &token) - if err := s.agent.vetServiceRegister(token, ns); err != nil { + if err := s.agent.vetServiceRegisterWithAuthorizer(authz, ns); err != nil { return nil, err } @@ -934,9 +972,14 @@ func (s *HTTPServer) AgentDeregisterService(resp http.ResponseWriter, req *http. return nil, err } + authz, err := s.agent.resolveTokenAndDefaultMeta(token, &sid.EnterpriseMeta, nil) + if err != nil { + return nil, err + } + sid.Normalize() - if err := s.agent.vetServiceUpdate(token, sid); err != nil { + if err := s.agent.vetServiceUpdateWithAuthorizer(authz, sid); err != nil { return nil, err } @@ -982,9 +1025,14 @@ func (s *HTTPServer) AgentServiceMaintenance(resp http.ResponseWriter, req *http return nil, err } + authz, err := s.agent.resolveTokenAndDefaultMeta(token, &sid.EnterpriseMeta, nil) + if err != nil { + return nil, err + } + sid.Normalize() - if err := s.agent.vetServiceUpdate(token, sid); err != nil { + if err := s.agent.vetServiceUpdateWithAuthorizer(authz, sid); err != nil { return nil, err } diff --git a/agent/checks/check.go b/agent/checks/check.go index a656051737..8040ffb08f 100644 --- a/agent/checks/check.go +++ b/agent/checks/check.go @@ -121,7 +121,7 @@ func (c *CheckMonitor) check() { cmd, err = exec.Script(c.Script) } if err != nil { - c.Logger.Printf("[ERR] agent: Check %q failed to setup: %s", c.CheckID, err) + c.Logger.Printf("[ERR] agent: Check %q failed to setup: %s", c.CheckID.String(), err) c.Notify.UpdateCheck(c.CheckID, api.HealthCritical, err.Error()) return } @@ -138,13 +138,13 @@ func (c *CheckMonitor) check() { outputStr = fmt.Sprintf("Captured %d of %d bytes\n...\n%s", output.Size(), output.TotalWritten(), outputStr) } - c.Logger.Printf("[TRACE] agent: Check %q output: %s", c.CheckID, outputStr) + c.Logger.Printf("[TRACE] agent: Check %q output: %s", c.CheckID.String(), outputStr) return outputStr } // Start the check if err := cmd.Start(); err != nil { - c.Logger.Printf("[ERR] agent: Check %q failed to invoke: %s", c.CheckID, err) + c.Logger.Printf("[ERR] agent: Check %q failed to invoke: %s", c.CheckID.String(), err) c.Notify.UpdateCheck(c.CheckID, api.HealthCritical, err.Error()) return } @@ -162,11 +162,11 @@ func (c *CheckMonitor) check() { select { case <-time.After(timeout): if err := exec.KillCommandSubtree(cmd); err != nil { - c.Logger.Printf("[WARN] agent: Check %q failed to kill after timeout: %s", c.CheckID, err) + c.Logger.Printf("[WARN] agent: Check %q failed to kill after timeout: %s", c.CheckID.String(), err) } msg := fmt.Sprintf("Timed out (%s) running check", timeout.String()) - c.Logger.Printf("[WARN] agent: Check %q: %s", c.CheckID, msg) + c.Logger.Printf("[WARN] agent: Check %q: %s", c.CheckID.String(), msg) outputStr := truncateAndLogOutput() if len(outputStr) > 0 { @@ -259,7 +259,7 @@ func (c *CheckTTL) run() { select { case <-c.timer.C: c.Logger.Printf("[WARN] agent: Check %q missed TTL, is now critical", - c.CheckID) + c.CheckID.String()) c.Notify.UpdateCheck(c.CheckID, api.HealthCritical, c.getExpiredOutput()) case <-c.stopCh: @@ -285,7 +285,7 @@ func (c *CheckTTL) getExpiredOutput() string { // and to renew the TTL. If expired, TTL is restarted. // output is returned (might be truncated) func (c *CheckTTL) SetStatus(status, output string) string { - c.Logger.Printf("[DEBUG] agent: Check %q status is now %s", c.CheckID, status) + c.Logger.Printf("[DEBUG] agent: Check %q status is now %s", c.CheckID.String(), status) total := len(output) if total > c.OutputMaxSize { output = fmt.Sprintf("%s ... (captured %d of %d bytes)", @@ -450,7 +450,7 @@ func (c *CheckHTTP) check() { // Read the response into a circular buffer to limit the size output, _ := circbuf.NewBuffer(int64(c.OutputMaxSize)) if _, err := io.Copy(output, resp.Body); err != nil { - c.Logger.Printf("[WARN] agent: Check %q error while reading body: %s", c.CheckID, err) + c.Logger.Printf("[WARN] agent: Check %q error while reading body: %s", c.CheckID.String(), err) } // Format the response body @@ -542,7 +542,7 @@ func (c *CheckTCP) run() { func (c *CheckTCP) check() { conn, err := c.dialer.Dial(`tcp`, c.TCP) if err != nil { - c.Logger.Printf("[WARN] agent: Check %q socket connection failed: %s", c.CheckID, err) + c.Logger.Printf("[WARN] agent: Check %q socket connection failed: %s", c.CheckID.String(), err) c.StatusHandler.updateCheck(c.CheckID, api.HealthCritical, err.Error()) return } @@ -615,7 +615,7 @@ func (c *CheckDocker) check() { var out string status, b, err := c.doCheck() if err != nil { - c.Logger.Printf("[DEBUG] agent: Check %q: %s", c.CheckID, err) + c.Logger.Printf("[DEBUG] agent: Check %q: %s", c.CheckID.String(), err) out = err.Error() } else { // out is already limited to CheckBufSize since we're getting a @@ -625,7 +625,7 @@ func (c *CheckDocker) check() { if int(b.TotalWritten()) > len(out) { out = fmt.Sprintf("Captured %d of %d bytes\n...\n%s", len(out), b.TotalWritten(), out) } - c.Logger.Printf("[TRACE] agent: Check %q output: %s", c.CheckID, out) + c.Logger.Printf("[TRACE] agent: Check %q output: %s", c.CheckID.String(), out) } c.StatusHandler.updateCheck(c.CheckID, status, out) } @@ -657,10 +657,10 @@ func (c *CheckDocker) doCheck() (string, *circbuf.Buffer, error) { case 0: return api.HealthPassing, buf, nil case 1: - c.Logger.Printf("[DEBUG] agent: Check %q failed with exit code: %d", c.CheckID, exitCode) + c.Logger.Printf("[DEBUG] agent: Check %q failed with exit code: %d", c.CheckID.String(), exitCode) return api.HealthWarning, buf, nil default: - c.Logger.Printf("[DEBUG] agent: Check %q failed with exit code: %d", c.CheckID, exitCode) + c.Logger.Printf("[DEBUG] agent: Check %q failed with exit code: %d", c.CheckID.String(), exitCode) return api.HealthCritical, buf, nil } } @@ -782,19 +782,19 @@ func (s *StatusHandler) updateCheck(checkID structs.CheckID, status, output stri s.successCounter++ s.failuresCounter = 0 if s.successCounter >= s.successBeforePassing { - s.logger.Printf("[DEBUG] agent: Check %q is %q", checkID, status) + s.logger.Printf("[DEBUG] agent: Check %q is %q", checkID.String(), status) s.inner.UpdateCheck(checkID, status, output) return } - s.logger.Printf("[WARN] agent: Check %q was %q but has not reached success threshold %d/%d", checkID, status, s.successCounter, s.successBeforePassing) + s.logger.Printf("[WARN] agent: Check %q was %q but has not reached success threshold %d/%d", checkID.String(), status, s.successCounter, s.successBeforePassing) } else { s.failuresCounter++ s.successCounter = 0 if s.failuresCounter >= s.failuresBeforeCritical { - s.logger.Printf("[WARN] agent: Check %q is now critical", checkID) + s.logger.Printf("[WARN] agent: Check %q is now critical", checkID.String()) s.inner.UpdateCheck(checkID, status, output) return } - s.logger.Printf("[WARN] agent: Check %q failed but has not reached failure threshold %d/%d", checkID, s.failuresCounter, s.failuresBeforeCritical) + s.logger.Printf("[WARN] agent: Check %q failed but has not reached failure threshold %d/%d", checkID.String(), s.failuresCounter, s.failuresBeforeCritical) } } diff --git a/agent/consul/acl.go b/agent/consul/acl.go index 43ce8be434..e2b0b194a5 100644 --- a/agent/consul/acl.go +++ b/agent/consul/acl.go @@ -69,6 +69,44 @@ const ( tokenRoleResolutionMaxRetries = 5 ) +// missingIdentity is used to return some identity in the event that the real identity cannot be ascertained +type missingIdentity struct { + reason string + token string +} + +func (id *missingIdentity) ID() string { + return id.reason +} + +func (id *missingIdentity) SecretToken() string { + return id.token +} + +func (id *missingIdentity) PolicyIDs() []string { + return nil +} + +func (id *missingIdentity) RoleIDs() []string { + return nil +} + +func (id *missingIdentity) EmbeddedPolicy() *structs.ACLPolicy { + return nil +} + +func (id *missingIdentity) ServiceIdentityList() []*structs.ACLServiceIdentity { + return nil +} + +func (id *missingIdentity) IsExpired(asOf time.Time) bool { + return false +} + +func (id *missingIdentity) EnterpriseMetadata() *structs.EnterpriseMeta { + return structs.DefaultEnterpriseMeta() +} + func minTTL(a time.Duration, b time.Duration) time.Duration { if a < b { return a @@ -287,7 +325,7 @@ func (r *ACLResolver) fetchAndCacheTokenLegacy(token string, cached *structs.Aut } } -func (r *ACLResolver) resolveTokenLegacy(token string) (acl.Authorizer, error) { +func (r *ACLResolver) resolveTokenLegacy(token string) (structs.ACLIdentity, acl.Authorizer, error) { defer metrics.MeasureSince([]string{"acl", "resolveTokenLegacy"}, time.Now()) // Attempt to resolve locally first (local results are not cached) @@ -297,18 +335,23 @@ func (r *ACLResolver) resolveTokenLegacy(token string) (acl.Authorizer, error) { if err == nil && identity != nil { policies, err := r.resolvePoliciesForIdentity(identity) if err != nil { - return nil, err + return identity, nil, err } authz, err := policies.Compile(r.cache, r.entConf) if err != nil { - return nil, err + return identity, nil, err } - return acl.NewChainedAuthorizer([]acl.Authorizer{authz, acl.RootAuthorizer(r.config.ACLDefaultPolicy)}), nil + return identity, acl.NewChainedAuthorizer([]acl.Authorizer{authz, acl.RootAuthorizer(r.config.ACLDefaultPolicy)}), nil } - return nil, err + return nil, nil, err + } + + identity := &missingIdentity{ + reason: "legacy-token", + token: token, } // Look in the cache prior to making a RPC request @@ -317,9 +360,9 @@ func (r *ACLResolver) resolveTokenLegacy(token string) (acl.Authorizer, error) { if entry != nil && entry.Age() <= minTTL(entry.TTL, r.config.ACLTokenTTL) { metrics.IncrCounter([]string{"acl", "token", "cache_hit"}, 1) if entry.Authorizer != nil { - return entry.Authorizer, nil + return identity, entry.Authorizer, nil } - return nil, acl.ErrNotFound + return identity, nil, acl.ErrNotFound } metrics.IncrCounter([]string{"acl", "token", "cache_miss"}, 1) @@ -334,9 +377,9 @@ func (r *ACLResolver) resolveTokenLegacy(token string) (acl.Authorizer, error) { if !waitForResult { // waitForResult being false requires the cacheEntry to not be nil if entry.Authorizer != nil { - return entry.Authorizer, nil + return identity, entry.Authorizer, nil } - return nil, acl.ErrNotFound + return identity, nil, acl.ErrNotFound } // block waiting for the async RPC to finish. @@ -347,7 +390,7 @@ func (r *ACLResolver) resolveTokenLegacy(token string) (acl.Authorizer, error) { authorizer = res.Val.(acl.Authorizer) } - return authorizer, res.Err + return identity, authorizer, res.Err } func (r *ACLResolver) fetchAndCacheIdentityFromToken(token string, cached *structs.IdentityCacheEntry) (structs.ACLIdentity, error) { @@ -987,13 +1030,13 @@ func (r *ACLResolver) disableACLsWhenUpstreamDisabled(err error) error { return err } -func (r *ACLResolver) ResolveToken(token string) (acl.Authorizer, error) { +func (r *ACLResolver) ResolveTokenToIdentityAndAuthorizer(token string) (structs.ACLIdentity, acl.Authorizer, error) { if !r.ACLsEnabled() { - return nil, nil + return nil, nil, nil } if acl.RootAuthorizer(token) != nil { - return nil, acl.ErrRootDenied + return nil, nil, acl.ErrRootDenied } // handle the anonymous token @@ -1002,8 +1045,8 @@ func (r *ACLResolver) ResolveToken(token string) (acl.Authorizer, error) { } if r.delegate.UseLegacyACLs() { - authorizer, err := r.resolveTokenLegacy(token) - return authorizer, r.disableACLsWhenUpstreamDisabled(err) + identity, authorizer, err := r.resolveTokenLegacy(token) + return identity, authorizer, r.disableACLsWhenUpstreamDisabled(err) } defer metrics.MeasureSince([]string{"acl", "ResolveToken"}, time.Now()) @@ -1013,10 +1056,10 @@ func (r *ACLResolver) ResolveToken(token string) (acl.Authorizer, error) { r.disableACLsWhenUpstreamDisabled(err) if IsACLRemoteError(err) { r.logger.Printf("[ERR] consul.acl: %v", err) - return r.down, nil + return &missingIdentity{reason: "primary-dc-down", token: token}, r.down, nil } - return nil, err + return nil, nil, err } // Build the Authorizer @@ -1024,7 +1067,7 @@ func (r *ACLResolver) ResolveToken(token string) (acl.Authorizer, error) { authz, err := policies.Compile(r.cache, r.entConf) if err != nil { - return nil, err + return nil, nil, err } chain = append(chain, authz) @@ -1032,15 +1075,20 @@ func (r *ACLResolver) ResolveToken(token string) (acl.Authorizer, error) { if err != nil { if IsACLRemoteError(err) { r.logger.Printf("[ERR] consul.acl: %v", err) - return r.down, nil + return identity, r.down, nil } - return nil, err + return nil, nil, err } else if authz != nil { chain = append(chain, authz) } chain = append(chain, acl.RootAuthorizer(r.config.ACLDefaultPolicy)) - return acl.NewChainedAuthorizer(chain), nil + return identity, acl.NewChainedAuthorizer(chain), nil +} + +func (r *ACLResolver) ResolveToken(token string) (acl.Authorizer, error) { + _, authz, err := r.ResolveTokenToIdentityAndAuthorizer(token) + return authz, err } func (r *ACLResolver) ACLsEnabled() bool { diff --git a/agent/consul/acl_client.go b/agent/consul/acl_client.go index 1951d43412..cfc970e584 100644 --- a/agent/consul/acl_client.go +++ b/agent/consul/acl_client.go @@ -106,3 +106,27 @@ func (c *Client) ResolveRoleFromID(roleID string) (bool, *structs.ACLRole, error func (c *Client) ResolveToken(token string) (acl.Authorizer, error) { return c.acls.ResolveToken(token) } + +func (c *Client) ResolveTokenToIdentityAndAuthorizer(token string) (structs.ACLIdentity, acl.Authorizer, error) { + return c.acls.ResolveTokenToIdentityAndAuthorizer(token) +} + +func (c *Client) ResolveTokenAndDefaultMeta(token string, entMeta *structs.EnterpriseMeta, authzContext *acl.AuthorizerContext) (acl.Authorizer, error) { + identity, authz, err := c.acls.ResolveTokenToIdentityAndAuthorizer(token) + if err != nil { + return nil, err + } + + // Default the EnterpriseMeta based on the Tokens meta or actual defaults + // in the case of unknown identity + if identity != nil { + entMeta.Merge(identity.EnterpriseMetadata()) + } else { + entMeta.Merge(structs.DefaultEnterpriseMeta()) + } + + // Use the meta to fill in the ACL authorization context + entMeta.FillAuthzContext(authzContext) + + return authz, err +} diff --git a/agent/consul/acl_endpoint.go b/agent/consul/acl_endpoint.go index efe83783b6..04295ee5db 100644 --- a/agent/consul/acl_endpoint.go +++ b/agent/consul/acl_endpoint.go @@ -205,19 +205,17 @@ func (a *ACL) TokenRead(args *structs.ACLTokenGetRequest, reply *structs.ACLToke return err } - var rule acl.Authorizer + var authz acl.Authorizer if args.TokenIDType == structs.ACLTokenAccessor { - var entCtx acl.AuthorizerContext - args.FillAuthzContext(&entCtx) - var err error + var authzContext acl.AuthorizerContext // Only ACLRead privileges are required to list tokens // However if you do not have ACLWrite as well the token // secrets will be redacted - if rule, err = a.srv.ResolveToken(args.Token); err != nil { + if authz, err = a.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, &authzContext); err != nil { return err - } else if rule == nil || rule.ACLRead(&entCtx) != acl.Allow { + } else if authz == nil || authz.ACLRead(&authzContext) != acl.Allow { return acl.ErrPermissionDenied } } @@ -231,7 +229,7 @@ func (a *ACL) TokenRead(args *structs.ACLTokenGetRequest, reply *structs.ACLToke if args.TokenIDType == structs.ACLTokenAccessor { index, token, err = state.ACLTokenGetByAccessor(ws, args.TokenID, &args.EnterpriseMeta) if token != nil { - a.srv.filterACLWithAuthorizer(rule, &token) + a.srv.filterACLWithAuthorizer(authz, &token) // token secret was redacted if token.SecretID == redactedToken { @@ -277,12 +275,11 @@ func (a *ACL) TokenClone(args *structs.ACLTokenSetRequest, reply *structs.ACLTok defer metrics.MeasureSince([]string{"acl", "token", "clone"}, time.Now()) - var entCtx acl.AuthorizerContext - args.ACLToken.FillAuthzContext(&entCtx) - - if rule, err := a.srv.ResolveToken(args.Token); err != nil { + var authzContext acl.AuthorizerContext + authz, err := a.srv.ResolveTokenAndDefaultMeta(args.Token, &args.ACLToken.EnterpriseMeta, &authzContext) + if err != nil { return err - } else if rule == nil || rule.ACLWrite(&entCtx) != acl.Allow { + } else if authz == nil || authz.ACLWrite(&authzContext) != acl.Allow { return acl.ErrPermissionDenied } @@ -348,11 +345,10 @@ func (a *ACL) TokenSet(args *structs.ACLTokenSetRequest, reply *structs.ACLToken defer metrics.MeasureSince([]string{"acl", "token", "upsert"}, time.Now()) // Verify token is permitted to modify ACLs - var entCtx acl.AuthorizerContext - args.ACLToken.FillAuthzContext(&entCtx) - if rule, err := a.srv.ResolveToken(args.Token); err != nil { + var authzContext acl.AuthorizerContext + if authz, err := a.srv.ResolveTokenAndDefaultMeta(args.Token, &args.ACLToken.EnterpriseMeta, &authzContext); err != nil { return err - } else if rule == nil || rule.ACLWrite(&entCtx) != acl.Allow { + } else if authz == nil || authz.ACLWrite(&authzContext) != acl.Allow { return acl.ErrPermissionDenied } @@ -737,12 +733,10 @@ func (a *ACL) TokenDelete(args *structs.ACLTokenDeleteRequest, reply *string) er defer metrics.MeasureSince([]string{"acl", "token", "delete"}, time.Now()) // Verify token is permitted to modify ACLs - var entCtx acl.AuthorizerContext - args.FillAuthzContext(&entCtx) - - if rule, err := a.srv.ResolveToken(args.Token); err != nil { + var authzContext acl.AuthorizerContext + if authz, err := a.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, &authzContext); err != nil { return err - } else if rule == nil || rule.ACLWrite(&entCtx) != acl.Allow { + } else if authz == nil || authz.ACLWrite(&authzContext) != acl.Allow { return acl.ErrPermissionDenied } @@ -826,13 +820,11 @@ func (a *ACL) TokenList(args *structs.ACLTokenListRequest, reply *structs.ACLTok return err } - var entCtx acl.AuthorizerContext - args.FillAuthzContext(&entCtx) - - rule, err := a.srv.ResolveToken(args.Token) + var authzContext acl.AuthorizerContext + authz, err := a.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, &authzContext) if err != nil { return err - } else if rule == nil || rule.ACLRead(&entCtx) != acl.Allow { + } else if authz == nil || authz.ACLRead(&authzContext) != acl.Allow { return acl.ErrPermissionDenied } @@ -854,7 +846,7 @@ func (a *ACL) TokenList(args *structs.ACLTokenListRequest, reply *structs.ACLTok } // filter down to just the tokens that the requester has permissions to read - if err := a.srv.filterACLWithAuthorizer(rule, &stubs); err != nil { + if err := a.srv.filterACLWithAuthorizer(authz, &stubs); err != nil { return err } @@ -876,10 +868,10 @@ func (a *ACL) TokenBatchRead(args *structs.ACLTokenBatchGetRequest, reply *struc return err } - rule, err := a.srv.ResolveToken(args.Token) + authz, err := a.srv.ResolveToken(args.Token) if err != nil { return err - } else if rule == nil { + } else if authz == nil { return acl.ErrPermissionDenied } @@ -901,7 +893,7 @@ func (a *ACL) TokenBatchRead(args *structs.ACLTokenBatchGetRequest, reply *struc ret := make(structs.ACLTokens, 0, len(tokens)) for _, token := range tokens { final := token - a.srv.filterACLWithAuthorizer(rule, &final) + a.srv.filterACLWithAuthorizer(authz, &final) if final != nil { ret = append(ret, final) if final.SecretID == redactedToken { @@ -930,12 +922,10 @@ func (a *ACL) PolicyRead(args *structs.ACLPolicyGetRequest, reply *structs.ACLPo return err } - var entCtx acl.AuthorizerContext - args.FillAuthzContext(&entCtx) - - if rule, err := a.srv.ResolveToken(args.Token); err != nil { + var authzContext acl.AuthorizerContext + if authz, err := a.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, &authzContext); err != nil { return err - } else if rule == nil || rule.ACLRead(&entCtx) != acl.Allow { + } else if authz == nil || authz.ACLRead(&authzContext) != acl.Allow { return acl.ErrPermissionDenied } @@ -961,10 +951,10 @@ func (a *ACL) PolicyBatchRead(args *structs.ACLPolicyBatchGetRequest, reply *str return err } - rule, err := a.srv.ResolveToken(args.Token) + authz, err := a.srv.ResolveToken(args.Token) if err != nil { return err - } else if rule == nil { + } else if authz == nil { return acl.ErrPermissionDenied } @@ -975,7 +965,7 @@ func (a *ACL) PolicyBatchRead(args *structs.ACLPolicyBatchGetRequest, reply *str return err } - a.srv.filterACLWithAuthorizer(rule, &policies) + a.srv.filterACLWithAuthorizer(authz, &policies) reply.Index, reply.Policies = index, policies return nil @@ -1002,12 +992,11 @@ func (a *ACL) PolicySet(args *structs.ACLPolicySetRequest, reply *structs.ACLPol defer metrics.MeasureSince([]string{"acl", "policy", "upsert"}, time.Now()) // Verify token is permitted to modify ACLs - var entCtx acl.AuthorizerContext - args.Policy.FillAuthzContext(&entCtx) + var authzContext acl.AuthorizerContext - if rule, err := a.srv.ResolveToken(args.Token); err != nil { + if authz, err := a.srv.ResolveTokenAndDefaultMeta(args.Token, &args.Policy.EnterpriseMeta, &authzContext); err != nil { return err - } else if rule == nil || rule.ACLWrite(&entCtx) != acl.Allow { + } else if authz == nil || authz.ACLWrite(&authzContext) != acl.Allow { return acl.ErrPermissionDenied } @@ -1138,12 +1127,11 @@ func (a *ACL) PolicyDelete(args *structs.ACLPolicyDeleteRequest, reply *string) defer metrics.MeasureSince([]string{"acl", "policy", "delete"}, time.Now()) // Verify token is permitted to modify ACLs - var entCtx acl.AuthorizerContext - args.FillAuthzContext(&entCtx) + var authzContext acl.AuthorizerContext - if rule, err := a.srv.ResolveToken(args.Token); err != nil { + if authz, err := a.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, &authzContext); err != nil { return err - } else if rule == nil || rule.ACLWrite(&entCtx) != acl.Allow { + } else if authz == nil || authz.ACLWrite(&authzContext) != acl.Allow { return acl.ErrPermissionDenied } @@ -1195,13 +1183,12 @@ func (a *ACL) PolicyList(args *structs.ACLPolicyListRequest, reply *structs.ACLP return err } - var entCtx acl.AuthorizerContext - args.FillAuthzContext(&entCtx) + var authzContext acl.AuthorizerContext - rule, err := a.srv.ResolveToken(args.Token) + authz, err := a.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, &authzContext) if err != nil { return err - } else if rule == nil || rule.ACLRead(&entCtx) != acl.Allow { + } else if authz == nil || authz.ACLRead(&authzContext) != acl.Allow { return acl.ErrPermissionDenied } @@ -1213,7 +1200,7 @@ func (a *ACL) PolicyList(args *structs.ACLPolicyListRequest, reply *structs.ACLP } // filter down to just what the requester has permissions to see - a.srv.filterACLWithAuthorizer(rule, &policies) + a.srv.filterACLWithAuthorizer(authz, &policies) var stubs structs.ACLPolicyListStubs for _, policy := range policies { @@ -1351,12 +1338,11 @@ func (a *ACL) RoleRead(args *structs.ACLRoleGetRequest, reply *structs.ACLRoleRe return err } - var entCtx acl.AuthorizerContext - args.FillAuthzContext(&entCtx) + var authzContext acl.AuthorizerContext - if rule, err := a.srv.ResolveToken(args.Token); err != nil { + if authz, err := a.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, &authzContext); err != nil { return err - } else if rule == nil || rule.ACLRead(&entCtx) != acl.Allow { + } else if authz == nil || authz.ACLRead(&authzContext) != acl.Allow { return acl.ErrPermissionDenied } @@ -1391,10 +1377,10 @@ func (a *ACL) RoleBatchRead(args *structs.ACLRoleBatchGetRequest, reply *structs return err } - rule, err := a.srv.ResolveToken(args.Token) + authz, err := a.srv.ResolveToken(args.Token) if err != nil { return err - } else if rule == nil { + } else if authz == nil { return acl.ErrPermissionDenied } @@ -1405,7 +1391,7 @@ func (a *ACL) RoleBatchRead(args *structs.ACLRoleBatchGetRequest, reply *structs return err } - a.srv.filterACLWithAuthorizer(rule, &roles) + a.srv.filterACLWithAuthorizer(authz, &roles) reply.Index, reply.Roles = index, roles return nil @@ -1432,12 +1418,11 @@ func (a *ACL) RoleSet(args *structs.ACLRoleSetRequest, reply *structs.ACLRole) e defer metrics.MeasureSince([]string{"acl", "role", "upsert"}, time.Now()) // Verify token is permitted to modify ACLs - var entCtx acl.AuthorizerContext - args.Role.FillAuthzContext(&entCtx) + var authzContext acl.AuthorizerContext - if rule, err := a.srv.ResolveToken(args.Token); err != nil { + if authz, err := a.srv.ResolveTokenAndDefaultMeta(args.Token, &args.Role.EnterpriseMeta, &authzContext); err != nil { return err - } else if rule == nil || rule.ACLWrite(&entCtx) != acl.Allow { + } else if authz == nil || authz.ACLWrite(&authzContext) != acl.Allow { return acl.ErrPermissionDenied } @@ -1582,12 +1567,11 @@ func (a *ACL) RoleDelete(args *structs.ACLRoleDeleteRequest, reply *string) erro defer metrics.MeasureSince([]string{"acl", "role", "delete"}, time.Now()) // Verify token is permitted to modify ACLs - var entCtx acl.AuthorizerContext - args.FillAuthzContext(&entCtx) + var authzContext acl.AuthorizerContext - if rule, err := a.srv.ResolveToken(args.Token); err != nil { + if authz, err := a.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, &authzContext); err != nil { return err - } else if rule == nil || rule.ACLWrite(&entCtx) != acl.Allow { + } else if authz == nil || authz.ACLWrite(&authzContext) != acl.Allow { return acl.ErrPermissionDenied } @@ -1635,13 +1619,12 @@ func (a *ACL) RoleList(args *structs.ACLRoleListRequest, reply *structs.ACLRoleL return err } - var entCtx acl.AuthorizerContext - args.FillAuthzContext(&entCtx) + var authzContext acl.AuthorizerContext - rule, err := a.srv.ResolveToken(args.Token) + authz, err := a.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, &authzContext) if err != nil { return err - } else if rule == nil || rule.ACLRead(&entCtx) != acl.Allow { + } else if authz == nil || authz.ACLRead(&authzContext) != acl.Allow { return acl.ErrPermissionDenied } @@ -1652,7 +1635,7 @@ func (a *ACL) RoleList(args *structs.ACLRoleListRequest, reply *structs.ACLRoleL return err } - a.srv.filterACLWithAuthorizer(rule, &roles) + a.srv.filterACLWithAuthorizer(authz, &roles) reply.Index, reply.Roles = index, roles return nil @@ -1721,13 +1704,12 @@ func (a *ACL) BindingRuleRead(args *structs.ACLBindingRuleGetRequest, reply *str return err } - var entCtx acl.AuthorizerContext - args.FillAuthzContext(&entCtx) + var authzContext acl.AuthorizerContext - rule, err := a.srv.ResolveToken(args.Token) + authz, err := a.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, &authzContext) if err != nil { return err - } else if rule == nil || rule.ACLRead(&entCtx) != acl.Allow { + } else if authz == nil || authz.ACLRead(&authzContext) != acl.Allow { return acl.ErrPermissionDenied } @@ -1763,13 +1745,12 @@ func (a *ACL) BindingRuleSet(args *structs.ACLBindingRuleSetRequest, reply *stru defer metrics.MeasureSince([]string{"acl", "bindingrule", "upsert"}, time.Now()) - var entCtx acl.AuthorizerContext - args.BindingRule.FillAuthzContext(&entCtx) + var authzContext acl.AuthorizerContext // Verify token is permitted to modify ACLs - if rule, err := a.srv.ResolveToken(args.Token); err != nil { + if authz, err := a.srv.ResolveTokenAndDefaultMeta(args.Token, &args.BindingRule.EnterpriseMeta, &authzContext); err != nil { return err - } else if rule == nil || rule.ACLWrite(&entCtx) != acl.Allow { + } else if authz == nil || authz.ACLWrite(&authzContext) != acl.Allow { return acl.ErrPermissionDenied } @@ -1792,6 +1773,7 @@ func (a *ACL) BindingRuleSet(args *structs.ACLBindingRuleSetRequest, reply *stru // Verify the role exists var err error + // specifically disregarding the enterprise meta here _, existing, err = state.ACLBindingRuleGetByID(nil, rule.ID, nil) if err != nil { return fmt.Errorf("acl binding rule lookup failed: %v", err) @@ -1894,13 +1876,12 @@ func (a *ACL) BindingRuleDelete(args *structs.ACLBindingRuleDeleteRequest, reply defer metrics.MeasureSince([]string{"acl", "bindingrule", "delete"}, time.Now()) - var entCtx acl.AuthorizerContext - args.FillAuthzContext(&entCtx) + var authzContext acl.AuthorizerContext // Verify token is permitted to modify ACLs - if rule, err := a.srv.ResolveToken(args.Token); err != nil { + if authz, err := a.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, &authzContext); err != nil { return err - } else if rule == nil || rule.ACLWrite(&entCtx) != acl.Allow { + } else if authz == nil || authz.ACLWrite(&authzContext) != acl.Allow { return acl.ErrPermissionDenied } @@ -1948,13 +1929,12 @@ func (a *ACL) BindingRuleList(args *structs.ACLBindingRuleListRequest, reply *st return err } - var entCtx acl.AuthorizerContext - args.FillAuthzContext(&entCtx) + var authzContext acl.AuthorizerContext - rule, err := a.srv.ResolveToken(args.Token) + authz, err := a.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, &authzContext) if err != nil { return err - } else if rule == nil || rule.ACLRead(&entCtx) != acl.Allow { + } else if authz == nil || authz.ACLRead(&authzContext) != acl.Allow { return acl.ErrPermissionDenied } @@ -1965,7 +1945,7 @@ func (a *ACL) BindingRuleList(args *structs.ACLBindingRuleListRequest, reply *st return err } - a.srv.filterACLWithAuthorizer(rule, &rules) + a.srv.filterACLWithAuthorizer(authz, &rules) reply.Index, reply.BindingRules = index, rules return nil @@ -1989,12 +1969,11 @@ func (a *ACL) AuthMethodRead(args *structs.ACLAuthMethodGetRequest, reply *struc return err } - var entCtx acl.AuthorizerContext - args.FillAuthzContext(&entCtx) + var authzContext acl.AuthorizerContext - if rule, err := a.srv.ResolveToken(args.Token); err != nil { + if authz, err := a.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, &authzContext); err != nil { return err - } else if rule == nil || rule.ACLRead(&entCtx) != acl.Allow { + } else if authz == nil || authz.ACLRead(&authzContext) != acl.Allow { return acl.ErrPermissionDenied } @@ -2031,12 +2010,11 @@ func (a *ACL) AuthMethodSet(args *structs.ACLAuthMethodSetRequest, reply *struct defer metrics.MeasureSince([]string{"acl", "authmethod", "upsert"}, time.Now()) // Verify token is permitted to modify ACLs - var entCtx acl.AuthorizerContext - args.AuthMethod.FillAuthzContext(&entCtx) + var authzContext acl.AuthorizerContext - if rule, err := a.srv.ResolveToken(args.Token); err != nil { + if authz, err := a.srv.ResolveTokenAndDefaultMeta(args.Token, &args.AuthMethod.EnterpriseMeta, &authzContext); err != nil { return err - } else if rule == nil || rule.ACLWrite(&entCtx) != acl.Allow { + } else if authz == nil || authz.ACLWrite(&authzContext) != acl.Allow { return acl.ErrPermissionDenied } @@ -2119,12 +2097,11 @@ func (a *ACL) AuthMethodDelete(args *structs.ACLAuthMethodDeleteRequest, reply * defer metrics.MeasureSince([]string{"acl", "authmethod", "delete"}, time.Now()) // Verify token is permitted to modify ACLs - var entCtx acl.AuthorizerContext - args.FillAuthzContext(&entCtx) + var authzContext acl.AuthorizerContext - if rule, err := a.srv.ResolveToken(args.Token); err != nil { + if authz, err := a.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, &authzContext); err != nil { return err - } else if rule == nil || rule.ACLWrite(&entCtx) != acl.Allow { + } else if authz == nil || authz.ACLWrite(&authzContext) != acl.Allow { return acl.ErrPermissionDenied } @@ -2173,13 +2150,12 @@ func (a *ACL) AuthMethodList(args *structs.ACLAuthMethodListRequest, reply *stru return err } - var entCtx acl.AuthorizerContext - args.FillAuthzContext(&entCtx) + var authzContext acl.AuthorizerContext - rule, err := a.srv.ResolveToken(args.Token) + authz, err := a.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, &authzContext) if err != nil { return err - } else if rule == nil || rule.ACLRead(&entCtx) != acl.Allow { + } else if authz == nil || authz.ACLRead(&authzContext) != acl.Allow { return acl.ErrPermissionDenied } @@ -2190,7 +2166,7 @@ func (a *ACL) AuthMethodList(args *structs.ACLAuthMethodListRequest, reply *stru return err } - a.srv.filterACLWithAuthorizer(rule, &methods) + a.srv.filterACLWithAuthorizer(authz, &methods) var stubs structs.ACLAuthMethodListStubs for _, method := range methods { diff --git a/agent/consul/acl_server.go b/agent/consul/acl_server.go index 463d82a32a..65292e1af5 100644 --- a/agent/consul/acl_server.go +++ b/agent/consul/acl_server.go @@ -213,6 +213,30 @@ func (s *Server) ResolveToken(token string) (acl.Authorizer, error) { return s.acls.ResolveToken(token) } +func (s *Server) ResolveTokenToIdentityAndAuthorizer(token string) (structs.ACLIdentity, acl.Authorizer, error) { + return s.acls.ResolveTokenToIdentityAndAuthorizer(token) +} + +func (s *Server) ResolveTokenAndDefaultMeta(token string, entMeta *structs.EnterpriseMeta, authzContext *acl.AuthorizerContext) (acl.Authorizer, error) { + identity, authz, err := s.acls.ResolveTokenToIdentityAndAuthorizer(token) + if err != nil { + return nil, err + } + + // Default the EnterpriseMeta based on the Tokens meta or actual defaults + // in the case of unknown identity + if identity != nil { + entMeta.Merge(identity.EnterpriseMetadata()) + } else { + entMeta.Merge(structs.DefaultEnterpriseMeta()) + } + + // Use the meta to fill in the ACL authorization context + entMeta.FillAuthzContext(authzContext) + + return authz, err +} + func (s *Server) filterACL(token string, subj interface{}) error { return s.acls.filterACL(token, subj) } diff --git a/agent/consul/catalog_endpoint.go b/agent/consul/catalog_endpoint.go index 9251851cb4..8019ed93a6 100644 --- a/agent/consul/catalog_endpoint.go +++ b/agent/consul/catalog_endpoint.go @@ -35,7 +35,7 @@ func nodePreApply(nodeName, nodeID string) error { return nil } -func servicePreApply(service *structs.NodeService, rule acl.Authorizer) error { +func servicePreApply(service *structs.NodeService, authz acl.Authorizer) error { // Validate the service. This is in addition to the below since // the above just hasn't been moved over yet. We should move it over // in time. @@ -68,14 +68,14 @@ func servicePreApply(service *structs.NodeService, rule acl.Authorizer) error { // later if version 0.8 is enabled, so we can eventually just // delete this and do all the ACL checks down there. if service.Service != structs.ConsulServiceName { - if rule != nil && rule.ServiceWrite(service.Service, &authzContext) != acl.Allow { + if authz != nil && authz.ServiceWrite(service.Service, &authzContext) != acl.Allow { return acl.ErrPermissionDenied } } // Proxies must have write permission on their destination if service.Kind == structs.ServiceKindConnectProxy { - if rule != nil && rule.ServiceWrite(service.Proxy.DestinationServiceName, &authzContext) != acl.Allow { + if authz != nil && authz.ServiceWrite(service.Proxy.DestinationServiceName, &authzContext) != acl.Allow { return acl.ErrPermissionDenied } } @@ -92,17 +92,25 @@ func checkPreApply(check *structs.HealthCheck) { // Register is used register that a node is providing a given service. func (c *Catalog) Register(args *structs.RegisterRequest, reply *struct{}) error { - if err := c.srv.validateEnterpriseRequest(args.GetEnterpriseMeta(), true); err != nil { - return err - } - if done, err := c.srv.forward("Catalog.Register", args, args, reply); done { return err } defer metrics.MeasureSince([]string{"catalog", "register"}, time.Now()) // Fetch the ACL token, if any. - rule, err := c.srv.ResolveToken(args.Token) + authz, err := c.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, nil) + if err != nil { + return err + } + + if err := c.srv.validateEnterpriseRequest(args.GetEnterpriseMeta(), true); err != nil { + return err + } + + // This needs to happen before the other preapply checks as it will fixup some of the + // internal enterprise metas on the services and checks + state := c.srv.fsm.State() + entMeta, err := state.ValidateRegisterRequest(args) if err != nil { return err } @@ -117,7 +125,7 @@ func (c *Catalog) Register(args *structs.RegisterRequest, reply *struct{}) error // Handle a service registration. if args.Service != nil { - if err := servicePreApply(args.Service, rule); err != nil { + if err := servicePreApply(args.Service, authz); err != nil { return err } } @@ -141,20 +149,14 @@ func (c *Catalog) Register(args *structs.RegisterRequest, reply *struct{}) error } } - state := c.srv.fsm.State() - entMeta, err := state.ValidateRegisterRequest(args) - if err != nil { - return err - } - // Check the complete register request against the given ACL policy. - if rule != nil && c.srv.config.ACLEnforceVersion8 { + if authz != nil && c.srv.config.ACLEnforceVersion8 { state := c.srv.fsm.State() _, ns, err := state.NodeServices(nil, args.Node, entMeta) if err != nil { return fmt.Errorf("Node lookup failed: %v", err) } - if err := vetRegisterWithACL(rule, args, ns); err != nil { + if err := vetRegisterWithACL(authz, args, ns); err != nil { return err } } @@ -171,10 +173,6 @@ func (c *Catalog) Register(args *structs.RegisterRequest, reply *struct{}) error // Deregister is used to remove a service registration for a given node. func (c *Catalog) Deregister(args *structs.DeregisterRequest, reply *struct{}) error { - if err := c.srv.validateEnterpriseRequest(&args.EnterpriseMeta, true); err != nil { - return err - } - if done, err := c.srv.forward("Catalog.Deregister", args, args, reply); done { return err } @@ -186,13 +184,17 @@ func (c *Catalog) Deregister(args *structs.DeregisterRequest, reply *struct{}) e } // Fetch the ACL token, if any. - rule, err := c.srv.ResolveToken(args.Token) + authz, err := c.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, nil) if err != nil { return err } + if err := c.srv.validateEnterpriseRequest(&args.EnterpriseMeta, true); err != nil { + return err + } + // Check the complete deregister request against the given ACL policy. - if rule != nil && c.srv.config.ACLEnforceVersion8 { + if authz != nil && c.srv.config.ACLEnforceVersion8 { state := c.srv.fsm.State() var ns *structs.NodeService @@ -211,7 +213,7 @@ func (c *Catalog) Deregister(args *structs.DeregisterRequest, reply *struct{}) e } } - if err := vetDeregisterWithACL(rule, args, ns, nc); err != nil { + if err := vetDeregisterWithACL(authz, args, ns, nc); err != nil { return err } @@ -282,16 +284,21 @@ func (c *Catalog) ListNodes(args *structs.DCSpecificRequest, reply *structs.Inde // ListServices is used to query the services in a DC func (c *Catalog) ListServices(args *structs.DCSpecificRequest, reply *structs.IndexedServices) error { - if err := c.srv.validateEnterpriseRequest(&args.EnterpriseMeta, false); err != nil { - return err - } - if done, err := c.srv.forward("Catalog.ListServices", args, args, reply); done { return err } (*reply).EnterpriseMeta = args.EnterpriseMeta + authz, err := c.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, nil) + if err != nil { + return err + } + + if err := c.srv.validateEnterpriseRequest(&args.EnterpriseMeta, false); err != nil { + return err + } + return c.srv.blockingQuery( &args.QueryOptions, &reply.QueryMeta, @@ -308,17 +315,13 @@ func (c *Catalog) ListServices(args *structs.DCSpecificRequest, reply *structs.I return err } - reply.Index, reply.Services = index, services - return c.srv.filterACL(args.Token, reply) + reply.Index, reply.Services, reply.EnterpriseMeta = index, services, args.EnterpriseMeta + return c.srv.filterACLWithAuthorizer(authz, reply) }) } // ServiceNodes returns all the nodes registered as part of a service func (c *Catalog) ServiceNodes(args *structs.ServiceSpecificRequest, reply *structs.IndexedServiceNodes) error { - if err := c.srv.validateEnterpriseRequest(&args.EnterpriseMeta, false); err != nil { - return err - } - if done, err := c.srv.forward("Catalog.ServiceNodes", args, args, reply); done { return err } @@ -360,17 +363,19 @@ func (c *Catalog) ServiceNodes(args *structs.ServiceSpecificRequest, reply *stru } var authzContext acl.AuthorizerContext - args.FillAuthzContext(&authzContext) + authz, err := c.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, &authzContext) + if err != nil { + return err + } + + if err := c.srv.validateEnterpriseRequest(&args.EnterpriseMeta, false); err != nil { + return err + } + // If we're doing a connect query, we need read access to the service // we're trying to find proxies for, so check that. if args.Connect { - // Fetch the ACL token, if any. - rule, err := c.srv.ResolveToken(args.Token) - if err != nil { - return err - } - - if rule != nil && rule.ServiceRead(args.ServiceName, &authzContext) != acl.Allow { + if authz != nil && authz.ServiceRead(args.ServiceName, &authzContext) != acl.Allow { // Just return nil, which will return an empty response (tested) return nil } @@ -455,10 +460,6 @@ func (c *Catalog) ServiceNodes(args *structs.ServiceSpecificRequest, reply *stru // NodeServices returns all the services registered as part of a node func (c *Catalog) NodeServices(args *structs.NodeSpecificRequest, reply *structs.IndexedNodeServices) error { - if err := c.srv.validateEnterpriseRequest(&args.EnterpriseMeta, false); err != nil { - return err - } - if done, err := c.srv.forward("Catalog.NodeServices", args, args, reply); done { return err } @@ -474,6 +475,15 @@ func (c *Catalog) NodeServices(args *structs.NodeSpecificRequest, reply *structs return err } + _, err = c.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, nil) + if err != nil { + return err + } + + if err := c.srv.validateEnterpriseRequest(&args.EnterpriseMeta, false); err != nil { + return err + } + return c.srv.blockingQuery( &args.QueryOptions, &reply.QueryMeta, @@ -501,10 +511,6 @@ func (c *Catalog) NodeServices(args *structs.NodeSpecificRequest, reply *structs } func (c *Catalog) NodeServiceList(args *structs.NodeSpecificRequest, reply *structs.IndexedNodeServiceList) error { - if err := c.srv.validateEnterpriseRequest(&args.EnterpriseMeta, false); err != nil { - return err - } - if done, err := c.srv.forward("Catalog.NodeServiceList", args, args, reply); done { return err } @@ -520,6 +526,15 @@ func (c *Catalog) NodeServiceList(args *structs.NodeSpecificRequest, reply *stru return err } + _, err = c.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, nil) + if err != nil { + return err + } + + if err := c.srv.validateEnterpriseRequest(&args.EnterpriseMeta, false); err != nil { + return err + } + return c.srv.blockingQuery( &args.QueryOptions, &reply.QueryMeta, diff --git a/agent/consul/health_endpoint.go b/agent/consul/health_endpoint.go index b0bcd0c45c..bd3d2e487d 100644 --- a/agent/consul/health_endpoint.go +++ b/agent/consul/health_endpoint.go @@ -20,10 +20,6 @@ type Health struct { // ChecksInState is used to get all the checks in a given state func (h *Health) ChecksInState(args *structs.ChecksInStateRequest, reply *structs.IndexedHealthChecks) error { - if err := h.srv.validateEnterpriseRequest(&args.EnterpriseMeta, false); err != nil { - return err - } - if done, err := h.srv.forward("Health.ChecksInState", args, args, reply); done { return err } @@ -33,6 +29,15 @@ func (h *Health) ChecksInState(args *structs.ChecksInStateRequest, return err } + _, err = h.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, nil) + if err != nil { + return err + } + + if err := h.srv.validateEnterpriseRequest(&args.EnterpriseMeta, false); err != nil { + return err + } + return h.srv.blockingQuery( &args.QueryOptions, &reply.QueryMeta, @@ -66,10 +71,6 @@ func (h *Health) ChecksInState(args *structs.ChecksInStateRequest, // NodeChecks is used to get all the checks for a node func (h *Health) NodeChecks(args *structs.NodeSpecificRequest, reply *structs.IndexedHealthChecks) error { - if err := h.srv.validateEnterpriseRequest(&args.EnterpriseMeta, false); err != nil { - return err - } - if done, err := h.srv.forward("Health.NodeChecks", args, args, reply); done { return err } @@ -79,6 +80,15 @@ func (h *Health) NodeChecks(args *structs.NodeSpecificRequest, return err } + _, err = h.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, nil) + if err != nil { + return err + } + + if err := h.srv.validateEnterpriseRequest(&args.EnterpriseMeta, false); err != nil { + return err + } + return h.srv.blockingQuery( &args.QueryOptions, &reply.QueryMeta, @@ -105,10 +115,6 @@ func (h *Health) NodeChecks(args *structs.NodeSpecificRequest, func (h *Health) ServiceChecks(args *structs.ServiceSpecificRequest, reply *structs.IndexedHealthChecks) error { - if err := h.srv.validateEnterpriseRequest(&args.EnterpriseMeta, false); err != nil { - return err - } - // Reject if tag filtering is on if args.TagFilter { return fmt.Errorf("Tag filtering is not supported") @@ -124,6 +130,15 @@ func (h *Health) ServiceChecks(args *structs.ServiceSpecificRequest, return err } + _, err = h.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, nil) + if err != nil { + return err + } + + if err := h.srv.validateEnterpriseRequest(&args.EnterpriseMeta, false); err != nil { + return err + } + return h.srv.blockingQuery( &args.QueryOptions, &reply.QueryMeta, @@ -156,10 +171,6 @@ func (h *Health) ServiceChecks(args *structs.ServiceSpecificRequest, // ServiceNodes returns all the nodes registered as part of a service including health info func (h *Health) ServiceNodes(args *structs.ServiceSpecificRequest, reply *structs.IndexedCheckServiceNodes) error { - if err := h.srv.validateEnterpriseRequest(&args.EnterpriseMeta, false); err != nil { - return err - } - if done, err := h.srv.forward("Health.ServiceNodes", args, args, reply); done { return err } @@ -180,16 +191,20 @@ func (h *Health) ServiceNodes(args *structs.ServiceSpecificRequest, reply *struc f = h.serviceNodesDefault } + var authzContext acl.AuthorizerContext + authz, err := h.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, &authzContext) + if err != nil { + return err + } + + if err := h.srv.validateEnterpriseRequest(&args.EnterpriseMeta, false); err != nil { + return err + } + // If we're doing a connect query, we need read access to the service // we're trying to find proxies for, so check that. if args.Connect { - // Fetch the ACL token, if any. - rule, err := h.srv.ResolveToken(args.Token) - if err != nil { - return err - } - - if rule != nil && rule.ServiceRead(args.ServiceName, nil) != acl.Allow { + if authz != nil && authz.ServiceRead(args.ServiceName, &authzContext) != acl.Allow { // Just return nil, which will return an empty response (tested) return nil } diff --git a/agent/consul/kvs_endpoint.go b/agent/consul/kvs_endpoint.go index 4ff6fd7512..a8a8e73572 100644 --- a/agent/consul/kvs_endpoint.go +++ b/agent/consul/kvs_endpoint.go @@ -21,20 +21,20 @@ type KVS struct { // preApply does all the verification of a KVS update that is performed BEFORE // we submit as a Raft log entry. This includes enforcing the lock delay which // must only be done on the leader. -func kvsPreApply(srv *Server, rule acl.Authorizer, op api.KVOp, dirEnt *structs.DirEntry) (bool, error) { +func kvsPreApply(srv *Server, authz acl.Authorizer, op api.KVOp, dirEnt *structs.DirEntry) (bool, error) { // Verify the entry. if dirEnt.Key == "" && op != api.KVDeleteTree { return false, fmt.Errorf("Must provide key") } // Apply the ACL policy if any. - if rule != nil { + if authz != nil { switch op { case api.KVDeleteTree: var authzContext acl.AuthorizerContext dirEnt.FillAuthzContext(&authzContext) - if rule.KeyWritePrefix(dirEnt.Key, &authzContext) != acl.Allow { + if authz.KeyWritePrefix(dirEnt.Key, &authzContext) != acl.Allow { return false, acl.ErrPermissionDenied } @@ -48,7 +48,7 @@ func kvsPreApply(srv *Server, rule acl.Authorizer, op api.KVOp, dirEnt *structs. var authzContext acl.AuthorizerContext dirEnt.FillAuthzContext(&authzContext) - if rule.KeyRead(dirEnt.Key, &authzContext) != acl.Allow { + if authz.KeyRead(dirEnt.Key, &authzContext) != acl.Allow { return false, acl.ErrPermissionDenied } @@ -56,7 +56,7 @@ func kvsPreApply(srv *Server, rule acl.Authorizer, op api.KVOp, dirEnt *structs. var authzContext acl.AuthorizerContext dirEnt.FillAuthzContext(&authzContext) - if rule.KeyWrite(dirEnt.Key, &authzContext) != acl.Allow { + if authz.KeyWrite(dirEnt.Key, &authzContext) != acl.Allow { return false, acl.ErrPermissionDenied } } @@ -88,16 +88,17 @@ func (k *KVS) Apply(args *structs.KVSRequest, reply *bool) error { } defer metrics.MeasureSince([]string{"kvs", "apply"}, time.Now()) + // Perform the pre-apply checks. + authz, err := k.srv.ResolveTokenAndDefaultMeta(args.Token, &args.DirEnt.EnterpriseMeta, nil) + if err != nil { + return err + } + if err := k.srv.validateEnterpriseRequest(&args.DirEnt.EnterpriseMeta, true); err != nil { return err } - // Perform the pre-apply checks. - rule, err := k.srv.ResolveToken(args.Token) - if err != nil { - return err - } - ok, err := kvsPreApply(k.srv, rule, args.Op, &args.DirEnt) + ok, err := kvsPreApply(k.srv, authz, args.Op, &args.DirEnt) if err != nil { return err } @@ -128,15 +129,14 @@ func (k *KVS) Get(args *structs.KeyRequest, reply *structs.IndexedDirEntries) er if done, err := k.srv.forward("KVS.Get", args, args, reply); done { return err } - if err := k.srv.validateEnterpriseRequest(&args.EnterpriseMeta, false); err != nil { + + var authzContext acl.AuthorizerContext + authz, err := k.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, &authzContext) + if err != nil { return err } - var entCtx acl.AuthorizerContext - args.FillAuthzContext(&entCtx) - - rule, err := k.srv.ResolveToken(args.Token) - if err != nil { + if err := k.srv.validateEnterpriseRequest(&args.EnterpriseMeta, false); err != nil { return err } @@ -148,7 +148,7 @@ func (k *KVS) Get(args *structs.KeyRequest, reply *structs.IndexedDirEntries) er if err != nil { return err } - if rule != nil && rule.KeyRead(args.Key, &entCtx) != acl.Allow { + if authz != nil && authz.KeyRead(args.Key, &authzContext) != acl.Allow { return acl.ErrPermissionDenied } @@ -174,18 +174,18 @@ func (k *KVS) List(args *structs.KeyRequest, reply *structs.IndexedDirEntries) e if done, err := k.srv.forward("KVS.List", args, args, reply); done { return err } + + var authzContext acl.AuthorizerContext + authz, err := k.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, &authzContext) + if err != nil { + return err + } + if err := k.srv.validateEnterpriseRequest(&args.EnterpriseMeta, false); err != nil { return err } - var entCtx acl.AuthorizerContext - args.FillAuthzContext(&entCtx) - - rule, err := k.srv.ResolveToken(args.Token) - if err != nil { - return err - } - if rule != nil && k.srv.config.ACLEnableKeyListPolicy && rule.KeyList(args.Key, &entCtx) != acl.Allow { + if authz != nil && k.srv.config.ACLEnableKeyListPolicy && authz.KeyList(args.Key, &authzContext) != acl.Allow { return acl.ErrPermissionDenied } @@ -197,8 +197,8 @@ func (k *KVS) List(args *structs.KeyRequest, reply *structs.IndexedDirEntries) e if err != nil { return err } - if rule != nil { - ent = FilterDirEnt(rule, ent) + if authz != nil { + ent = FilterDirEnt(authz, ent) } if len(ent) == 0 { @@ -226,18 +226,18 @@ func (k *KVS) ListKeys(args *structs.KeyListRequest, reply *structs.IndexedKeyLi if done, err := k.srv.forward("KVS.ListKeys", args, args, reply); done { return err } + + var authzContext acl.AuthorizerContext + authz, err := k.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, &authzContext) + if err != nil { + return err + } + if err := k.srv.validateEnterpriseRequest(&args.EnterpriseMeta, false); err != nil { return err } - var entCtx acl.AuthorizerContext - args.FillAuthzContext(&entCtx) - - rule, err := k.srv.ResolveToken(args.Token) - if err != nil { - return err - } - if rule != nil && k.srv.config.ACLEnableKeyListPolicy && rule.KeyList(args.Prefix, &entCtx) != acl.Allow { + if authz != nil && k.srv.config.ACLEnableKeyListPolicy && authz.KeyList(args.Prefix, &authzContext) != acl.Allow { return acl.ErrPermissionDenied } @@ -258,8 +258,8 @@ func (k *KVS) ListKeys(args *structs.KeyListRequest, reply *structs.IndexedKeyLi reply.Index = index } - if rule != nil { - entries = FilterDirEnt(rule, entries) + if authz != nil { + entries = FilterDirEnt(authz, entries) } // Collect the keys from the filtered entries diff --git a/agent/consul/session_endpoint.go b/agent/consul/session_endpoint.go index 311fc4e216..b627ee104c 100644 --- a/agent/consul/session_endpoint.go +++ b/agent/consul/session_endpoint.go @@ -25,10 +25,6 @@ func (s *Session) Apply(args *structs.SessionRequest, reply *string) error { } defer metrics.MeasureSince([]string{"session", "apply"}, time.Now()) - if err := s.srv.validateEnterpriseRequest(&args.Session.EnterpriseMeta, true); err != nil { - return err - } - // Verify the args if args.Session.ID == "" && args.Op == structs.SessionDestroy { return fmt.Errorf("Must provide ID") @@ -37,20 +33,21 @@ func (s *Session) Apply(args *structs.SessionRequest, reply *string) error { return fmt.Errorf("Must provide Node") } - // TODO (namespaces) (acls) infer entmeta if not provided. - // The entMeta to populate is the one in the Session struct, not SessionRequest // This is because the Session is what is passed to downstream functions like raftApply - var entCtx acl.AuthorizerContext - args.Session.FillAuthzContext(&entCtx) + var authzContext acl.AuthorizerContext // Fetch the ACL token, if any, and apply the policy. - rule, err := s.srv.ResolveToken(args.Token) + authz, err := s.srv.ResolveTokenAndDefaultMeta(args.Token, &args.Session.EnterpriseMeta, &authzContext) if err != nil { return err } - if rule != nil && s.srv.config.ACLEnforceVersion8 { + if err := s.srv.validateEnterpriseRequest(&args.Session.EnterpriseMeta, true); err != nil { + return err + } + + if authz != nil && s.srv.config.ACLEnforceVersion8 { switch args.Op { case structs.SessionDestroy: state := s.srv.fsm.State() @@ -61,12 +58,12 @@ func (s *Session) Apply(args *structs.SessionRequest, reply *string) error { if existing == nil { return fmt.Errorf("Unknown session %q", args.Session.ID) } - if rule.SessionWrite(existing.Node, &entCtx) != acl.Allow { + if authz.SessionWrite(existing.Node, &authzContext) != acl.Allow { return acl.ErrPermissionDenied } case structs.SessionCreate: - if rule.SessionWrite(args.Session.Node, &entCtx) != acl.Allow { + if authz.SessionWrite(args.Session.Node, &authzContext) != acl.Allow { return acl.ErrPermissionDenied } @@ -157,16 +154,13 @@ func (s *Session) Get(args *structs.SessionSpecificRequest, return err } - if err := s.srv.validateEnterpriseRequest(&args.EnterpriseMeta, false); err != nil { + var authzContext acl.AuthorizerContext + authz, err := s.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, &authzContext) + if err != nil { return err } - // TODO (namespaces) TODO (acls) infer args.entmeta if not provided - var entCtx acl.AuthorizerContext - args.FillAuthzContext(&entCtx) - - rule, err := s.srv.ResolveToken(args.Token) - if err != nil { + if err := s.srv.validateEnterpriseRequest(&args.EnterpriseMeta, false); err != nil { return err } @@ -185,7 +179,7 @@ func (s *Session) Get(args *structs.SessionSpecificRequest, } else { reply.Sessions = nil } - if err := s.srv.filterACLWithAuthorizer(rule, reply); err != nil { + if err := s.srv.filterACLWithAuthorizer(authz, reply); err != nil { return err } return nil @@ -199,16 +193,13 @@ func (s *Session) List(args *structs.SessionSpecificRequest, return err } - if err := s.srv.validateEnterpriseRequest(&args.EnterpriseMeta, false); err != nil { + var authzContext acl.AuthorizerContext + authz, err := s.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, &authzContext) + if err != nil { return err } - // TODO (namespaces) TODO (acls) infer args.entmeta if not provided - var entCtx acl.AuthorizerContext - args.FillAuthzContext(&entCtx) - - rule, err := s.srv.ResolveToken(args.Token) - if err != nil { + if err := s.srv.validateEnterpriseRequest(&args.EnterpriseMeta, false); err != nil { return err } @@ -222,7 +213,7 @@ func (s *Session) List(args *structs.SessionSpecificRequest, } reply.Index, reply.Sessions = index, sessions - if err := s.srv.filterACLWithAuthorizer(rule, reply); err != nil { + if err := s.srv.filterACLWithAuthorizer(authz, reply); err != nil { return err } return nil @@ -236,16 +227,13 @@ func (s *Session) NodeSessions(args *structs.NodeSpecificRequest, return err } - if err := s.srv.validateEnterpriseRequest(&args.EnterpriseMeta, false); err != nil { + var authzContext acl.AuthorizerContext + authz, err := s.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, &authzContext) + if err != nil { return err } - // TODO (namespaces) TODO (acls) infer args.entmeta if not provided - var entCtx acl.AuthorizerContext - args.FillAuthzContext(&entCtx) - - rule, err := s.srv.ResolveToken(args.Token) - if err != nil { + if err := s.srv.validateEnterpriseRequest(&args.EnterpriseMeta, false); err != nil { return err } @@ -259,7 +247,7 @@ func (s *Session) NodeSessions(args *structs.NodeSpecificRequest, } reply.Index, reply.Sessions = index, sessions - if err := s.srv.filterACLWithAuthorizer(rule, reply); err != nil { + if err := s.srv.filterACLWithAuthorizer(authz, reply); err != nil { return err } return nil @@ -274,6 +262,13 @@ func (s *Session) Renew(args *structs.SessionSpecificRequest, } defer metrics.MeasureSince([]string{"session", "renew"}, time.Now()) + // Fetch the ACL token, if any, and apply the policy. + var authzContext acl.AuthorizerContext + authz, err := s.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, &authzContext) + if err != nil { + return err + } + if err := s.srv.validateEnterpriseRequest(&args.EnterpriseMeta, true); err != nil { return err } @@ -290,19 +285,8 @@ func (s *Session) Renew(args *structs.SessionSpecificRequest, return nil } - // TODO (namespaces) (freddy):infer args.entmeta if not provided - // Fetch the ACL token, if any, and apply the policy. - var entCtx acl.AuthorizerContext - args.FillAuthzContext(&entCtx) - - rule, err := s.srv.ResolveToken(args.Token) - if err != nil { - return err - } - if rule != nil && s.srv.config.ACLEnforceVersion8 { - if rule.SessionWrite(session.Node, &entCtx) != acl.Allow { - return acl.ErrPermissionDenied - } + if authz != nil && s.srv.config.ACLEnforceVersion8 && authz.SessionWrite(session.Node, &authzContext) != acl.Allow { + return acl.ErrPermissionDenied } // Reset the session TTL timer. diff --git a/agent/structs/structs_oss.go b/agent/structs/structs_oss.go index 2524c3e633..7bdac5f6d2 100644 --- a/agent/structs/structs_oss.go +++ b/agent/structs/structs_oss.go @@ -21,6 +21,10 @@ func (m *EnterpriseMeta) addToHash(_ hash.Hash, _ bool) { // do nothing } +func (m *EnterpriseMeta) Merge(_ *EnterpriseMeta) { + // do nothing +} + func (m *EnterpriseMeta) Matches(_ *EnterpriseMeta) bool { return true }