diff --git a/.changelog/10013.txt b/.changelog/10013.txt new file mode 100644 index 0000000000..0d98ec9381 --- /dev/null +++ b/.changelog/10013.txt @@ -0,0 +1,3 @@ +```release-note:bug +audit-logging: (Enterprise only) Fixed an issue that resulted in usage of the agent master token or managed service provider tokens from being resolved properly. +``` diff --git a/agent/acl.go b/agent/acl.go index c9b42ba837..ad67f86c10 100644 --- a/agent/acl.go +++ b/agent/acl.go @@ -9,34 +9,6 @@ import ( "github.com/hashicorp/consul/agent/structs" ) -// resolveToken is the primary interface used by ACL-checkers in the agent -// endpoints, which is the one place where we do some ACL enforcement on -// 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.config.ACLsEnabled { - return nil, nil - } - - if acl.RootAuthorizer(id) != nil { - return nil, acl.ErrRootDenied - } - - if a.tokens.IsAgentMasterToken(id) { - return a.aclMasterAuthorizer, nil - } - - return a.delegate.ResolveTokenAndDefaultMeta(id, entMeta, authzContext) -} - // aclAccessorID is used to convert an ACLToken's secretID to its accessorID for non- // critical purposes, such as logging. Therefore we interpret all errors as empty-string // so we can safely log it without handling non-critical errors at the usage site. @@ -55,36 +27,11 @@ func (a *Agent) aclAccessorID(secretID string) string { return ident.ID() } -func initializeACLs(nodeName string) (acl.Authorizer, error) { - // Build a policy for the agent master token. - // The builtin agent master policy allows reading any node information - // and allows writes to the agent with the node name of the running agent - // only. This used to allow a prefix match on agent names but that seems - // entirely unnecessary so it is now using an exact match. - policy := &acl.Policy{ - PolicyRules: acl.PolicyRules{ - Agents: []*acl.AgentRule{ - { - Node: nodeName, - Policy: acl.PolicyWrite, - }, - }, - NodePrefixes: []*acl.NodeRule{ - { - Name: "", - Policy: acl.PolicyRead, - }, - }, - }, - } - return acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, nil) -} - // vetServiceRegister makes sure the service registration action is allowed by // the given token. func (a *Agent) vetServiceRegister(token string, service *structs.NodeService) error { // Resolve the token and bail if ACLs aren't enabled. - authz, err := a.resolveToken(token) + authz, err := a.delegate.ResolveTokenAndDefaultMeta(token, nil, nil) if err != nil { return err } @@ -130,7 +77,7 @@ func (a *Agent) vetServiceRegisterWithAuthorizer(authz acl.Authorizer, service * // token. func (a *Agent) vetServiceUpdate(token string, serviceID structs.ServiceID) error { // Resolve the token and bail if ACLs aren't enabled. - authz, err := a.resolveToken(token) + authz, err := a.delegate.ResolveTokenAndDefaultMeta(token, nil, nil) if err != nil { return err } @@ -163,7 +110,7 @@ func (a *Agent) vetServiceUpdateWithAuthorizer(authz acl.Authorizer, serviceID s // given token. func (a *Agent) vetCheckRegister(token string, check *structs.HealthCheck) error { // Resolve the token and bail if ACLs aren't enabled. - authz, err := a.resolveToken(token) + authz, err := a.delegate.ResolveTokenAndDefaultMeta(token, nil, nil) if err != nil { return err } @@ -208,7 +155,7 @@ func (a *Agent) vetCheckRegisterWithAuthorizer(authz acl.Authorizer, check *stru // 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. - authz, err := a.resolveToken(token) + authz, err := a.delegate.ResolveTokenAndDefaultMeta(token, nil, nil) if err != nil { return err } @@ -245,7 +192,7 @@ func (a *Agent) vetCheckUpdateWithAuthorizer(authz acl.Authorizer, checkID struc // filterMembers redacts members that the token doesn't have access to. func (a *Agent) filterMembers(token string, members *[]serf.Member) error { // Resolve the token and bail if ACLs aren't enabled. - rule, err := a.resolveToken(token) + rule, err := a.delegate.ResolveTokenAndDefaultMeta(token, nil, nil) if err != nil { return err } @@ -274,7 +221,7 @@ 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. - authz, err := a.resolveToken(token) + authz, err := a.delegate.ResolveTokenAndDefaultMeta(token, nil, nil) if err != nil { return err } @@ -302,7 +249,7 @@ func (a *Agent) filterServicesWithAuthorizer(authz acl.Authorizer, services *map // 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. - authz, err := a.resolveToken(token) + authz, err := a.delegate.ResolveTokenAndDefaultMeta(token, nil, nil) if err != nil { return err } diff --git a/agent/acl_endpoint.go b/agent/acl_endpoint.go index e35f33a75a..5adf5cbad6 100644 --- a/agent/acl_endpoint.go +++ b/agent/acl_endpoint.go @@ -104,7 +104,7 @@ func (s *HTTPHandlers) ACLRulesTranslate(resp http.ResponseWriter, req *http.Req var token string s.parseToken(req, &token) - rule, err := s.agent.resolveToken(token) + rule, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, nil, nil) if err != nil { return nil, err } @@ -1153,7 +1153,7 @@ func (s *HTTPHandlers) ACLAuthorize(resp http.ResponseWriter, req *http.Request) return nil, err } } else { - authz, err := s.agent.resolveToken(request.Token) + authz, err := s.agent.delegate.ResolveTokenAndDefaultMeta(request.Token, nil, nil) if err != nil { return nil, err } else if authz == nil { diff --git a/agent/acl_test.go b/agent/acl_test.go index 2f41412af4..a1cf461f18 100644 --- a/agent/acl_test.go +++ b/agent/acl_test.go @@ -177,46 +177,11 @@ func TestACL_Version8EnabledByDefault(t *testing.T) { } a := NewTestACLAgent(t, t.Name(), TestACLConfig(), resolveFn, nil) - _, err := a.resolveToken("nope") + _, err := a.delegate.ResolveTokenAndDefaultMeta("nope", nil, nil) require.Error(t, err) require.True(t, called) } -func TestACL_AgentMasterToken(t *testing.T) { - t.Parallel() - - a := NewTestACLAgent(t, t.Name(), TestACLConfig(), nil, nil) - err := a.tokens.Load(a.config.ACLTokens, a.logger) - require.NoError(t, err) - - authz, err := a.resolveToken("towel") - require.NotNil(t, authz) - require.Nil(t, err) - - require.Equal(t, acl.Allow, authz.AgentRead(a.config.NodeName, nil)) - require.Equal(t, acl.Allow, authz.AgentWrite(a.config.NodeName, nil)) - require.Equal(t, acl.Allow, authz.NodeRead("foobarbaz", nil)) - require.Equal(t, acl.Deny, authz.NodeWrite("foobarbaz", nil)) -} - -func TestACL_RootAuthorizersDenied(t *testing.T) { - t.Parallel() - - a := NewTestACLAgent(t, t.Name(), TestACLConfig(), nil, nil) - authz, err := a.resolveToken("deny") - require.Nil(t, authz) - require.Error(t, err) - require.True(t, acl.IsErrRootDenied(err)) - authz, err = a.resolveToken("allow") - require.Nil(t, authz) - require.Error(t, err) - require.True(t, acl.IsErrRootDenied(err)) - authz, err = a.resolveToken("manage") - require.Nil(t, authz) - require.Error(t, err) - require.True(t, acl.IsErrRootDenied(err)) -} - func authzFromPolicy(policy *acl.Policy, cfg *acl.Config) (acl.Authorizer, error) { return acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, cfg) } @@ -535,7 +500,7 @@ func TestACL_ResolveIdentity(t *testing.T) { // just double checkingto ensure if we had used the wrong function // that an error would be produced - _, err = a.resolveToken(nodeROSecret) + _, err = a.delegate.ResolveTokenAndDefaultMeta(nodeROSecret, nil, nil) require.Error(t, err) } diff --git a/agent/agent.go b/agent/agent.go index 53e4fb2a3c..c6615af4be 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -386,13 +386,6 @@ func New(bd BaseDeps) (*Agent, error) { a.serviceManager = NewServiceManager(&a) - // TODO: do this somewhere else, maybe move to newBaseDeps - var err error - a.aclMasterAuthorizer, err = initializeACLs(bd.RuntimeConfig.NodeName) - if err != nil { - return nil, err - } - // We used to do this in the Start method. However it doesn't need to go // there any longer. Originally it did because we passed the agent // delegate to some of the cache registrations. Now we just @@ -656,9 +649,11 @@ func (a *Agent) listenAndServeGRPC() error { } xdsServer := &xds.Server{ - Logger: a.logger.Named(logging.Envoy), - CfgMgr: a.proxyConfig, - ResolveToken: a.resolveToken, + Logger: a.logger.Named(logging.Envoy), + CfgMgr: a.proxyConfig, + ResolveToken: func(id string) (acl.Authorizer, error) { + return a.delegate.ResolveTokenAndDefaultMeta(id, nil, nil) + }, CheckFetcher: a, CfgFetcher: a, AuthCheckFrequency: xds.DefaultAuthCheckFrequency, diff --git a/agent/agent_endpoint.go b/agent/agent_endpoint.go index 10fe70c6d4..babb906f09 100644 --- a/agent/agent_endpoint.go +++ b/agent/agent_endpoint.go @@ -48,7 +48,7 @@ func (s *HTTPHandlers) AgentSelf(resp http.ResponseWriter, req *http.Request) (i // Fetch the ACL token, if any, and enforce agent policy. var token string s.parseToken(req, &token) - rule, err := s.agent.resolveToken(token) + rule, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, nil, nil) if err != nil { return nil, err } @@ -129,7 +129,7 @@ func (s *HTTPHandlers) AgentMetrics(resp http.ResponseWriter, req *http.Request) // Fetch the ACL token, if any, and enforce agent policy. var token string s.parseToken(req, &token) - rule, err := s.agent.resolveToken(token) + rule, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, nil, nil) if err != nil { return nil, err } @@ -160,7 +160,7 @@ func (s *HTTPHandlers) AgentReload(resp http.ResponseWriter, req *http.Request) // Fetch the ACL token, if any, and enforce agent policy. var token string s.parseToken(req, &token) - rule, err := s.agent.resolveToken(token) + rule, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, nil, nil) if err != nil { return nil, err } @@ -239,7 +239,7 @@ func (s *HTTPHandlers) AgentServices(resp http.ResponseWriter, req *http.Request var filterExpression string s.parseFilter(req, &filterExpression) - authz, err := s.agent.resolveTokenAndDefaultMeta(token, &entMeta, nil) + authz, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, &entMeta, nil) if err != nil { return nil, err } @@ -296,7 +296,7 @@ func (s *HTTPHandlers) AgentService(resp http.ResponseWriter, req *http.Request) } // need to resolve to default the meta - _, err := s.agent.resolveTokenAndDefaultMeta(token, &entMeta, nil) + _, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, &entMeta, nil) if err != nil { return nil, err } @@ -325,7 +325,7 @@ func (s *HTTPHandlers) AgentService(resp http.ResponseWriter, req *http.Request) ws.Add(svcState.WatchCh) // Check ACLs. - authz, err := s.agent.resolveToken(token) + authz, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, nil, nil) if err != nil { return "", nil, err } @@ -366,7 +366,7 @@ func (s *HTTPHandlers) AgentChecks(resp http.ResponseWriter, req *http.Request) return nil, err } - authz, err := s.agent.resolveTokenAndDefaultMeta(token, &entMeta, nil) + authz, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, &entMeta, nil) if err != nil { return nil, err } @@ -448,7 +448,7 @@ func (s *HTTPHandlers) AgentJoin(resp http.ResponseWriter, req *http.Request) (i // Fetch the ACL token, if any, and enforce agent policy. var token string s.parseToken(req, &token) - rule, err := s.agent.resolveToken(token) + rule, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, nil, nil) if err != nil { return nil, err } @@ -480,7 +480,7 @@ func (s *HTTPHandlers) AgentLeave(resp http.ResponseWriter, req *http.Request) ( // Fetch the ACL token, if any, and enforce agent policy. var token string s.parseToken(req, &token) - rule, err := s.agent.resolveToken(token) + rule, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, nil, nil) if err != nil { return nil, err } @@ -498,7 +498,7 @@ func (s *HTTPHandlers) AgentForceLeave(resp http.ResponseWriter, req *http.Reque // Fetch the ACL token, if any, and enforce agent policy. var token string s.parseToken(req, &token) - rule, err := s.agent.resolveToken(token) + rule, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, nil, nil) if err != nil { return nil, err } @@ -550,7 +550,7 @@ func (s *HTTPHandlers) AgentRegisterCheck(resp http.ResponseWriter, req *http.Re return nil, nil } - authz, err := s.agent.resolveTokenAndDefaultMeta(token, &args.EnterpriseMeta, nil) + authz, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, &args.EnterpriseMeta, nil) if err != nil { return nil, err } @@ -602,7 +602,7 @@ func (s *HTTPHandlers) AgentDeregisterCheck(resp http.ResponseWriter, req *http. return nil, err } - authz, err := s.agent.resolveTokenAndDefaultMeta(token, &checkID.EnterpriseMeta, nil) + authz, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, &checkID.EnterpriseMeta, nil) if err != nil { return nil, err } @@ -690,7 +690,7 @@ func (s *HTTPHandlers) agentCheckUpdate(_resp http.ResponseWriter, req *http.Req return nil, err } - authz, err := s.agent.resolveTokenAndDefaultMeta(token, &cid.EnterpriseMeta, nil) + authz, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, &cid.EnterpriseMeta, nil) if err != nil { return nil, err } @@ -767,7 +767,7 @@ func (s *HTTPHandlers) AgentHealthServiceByID(resp http.ResponseWriter, req *htt // need to resolve to default the meta var authzContext acl.AuthorizerContext - authz, err := s.agent.resolveTokenAndDefaultMeta(token, &entMeta, &authzContext) + authz, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, &entMeta, &authzContext) if err != nil { return nil, err } @@ -821,7 +821,7 @@ func (s *HTTPHandlers) AgentHealthServiceByName(resp http.ResponseWriter, req *h // need to resolve to default the meta var authzContext acl.AuthorizerContext - authz, err := s.agent.resolveTokenAndDefaultMeta(token, &entMeta, &authzContext) + authz, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, &entMeta, &authzContext) if err != nil { return nil, err } @@ -899,7 +899,7 @@ func (s *HTTPHandlers) AgentRegisterService(resp http.ResponseWriter, req *http. var token string s.parseToken(req, &token) - authz, err := s.agent.resolveTokenAndDefaultMeta(token, &args.EnterpriseMeta, nil) + authz, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, &args.EnterpriseMeta, nil) if err != nil { return nil, err } @@ -1032,7 +1032,7 @@ func (s *HTTPHandlers) AgentDeregisterService(resp http.ResponseWriter, req *htt return nil, err } - authz, err := s.agent.resolveTokenAndDefaultMeta(token, &sid.EnterpriseMeta, nil) + authz, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, &sid.EnterpriseMeta, nil) if err != nil { return nil, err } @@ -1085,7 +1085,7 @@ func (s *HTTPHandlers) AgentServiceMaintenance(resp http.ResponseWriter, req *ht return nil, err } - authz, err := s.agent.resolveTokenAndDefaultMeta(token, &sid.EnterpriseMeta, nil) + authz, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, &sid.EnterpriseMeta, nil) if err != nil { return nil, err } @@ -1134,7 +1134,7 @@ func (s *HTTPHandlers) AgentNodeMaintenance(resp http.ResponseWriter, req *http. // Get the provided token, if any, and vet against any ACL policies. var token string s.parseToken(req, &token) - rule, err := s.agent.resolveToken(token) + rule, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, nil, nil) if err != nil { return nil, err } @@ -1155,7 +1155,7 @@ func (s *HTTPHandlers) AgentMonitor(resp http.ResponseWriter, req *http.Request) // Fetch the ACL token, if any, and enforce agent policy. var token string s.parseToken(req, &token) - rule, err := s.agent.resolveToken(token) + rule, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, nil, nil) if err != nil { return nil, err } @@ -1227,7 +1227,7 @@ func (s *HTTPHandlers) AgentToken(resp http.ResponseWriter, req *http.Request) ( // Fetch the ACL token, if any, and enforce agent policy. var token string s.parseToken(req, &token) - rule, err := s.agent.resolveToken(token) + rule, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, nil, nil) if err != nil { return nil, err } @@ -1404,7 +1404,7 @@ func (s *HTTPHandlers) AgentHost(resp http.ResponseWriter, req *http.Request) (i // Fetch the ACL token, if any, and enforce agent policy. var token string s.parseToken(req, &token) - rule, err := s.agent.resolveToken(token) + rule, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, nil, nil) if err != nil { return nil, err } diff --git a/agent/connect_auth.go b/agent/connect_auth.go index 9b50ef183e..71f14b3194 100644 --- a/agent/connect_auth.go +++ b/agent/connect_auth.go @@ -59,7 +59,7 @@ func (a *Agent) ConnectAuthorize(token string, // We do this manually here since the RPC request below only verifies // service:read. var authzContext acl.AuthorizerContext - authz, err := a.resolveTokenAndDefaultMeta(token, &req.EnterpriseMeta, &authzContext) + authz, err := a.delegate.ResolveTokenAndDefaultMeta(token, &req.EnterpriseMeta, &authzContext) if err != nil { return returnErr(err) } diff --git a/agent/consul/acl.go b/agent/consul/acl.go index 65be4787c0..c0b0436a1b 100644 --- a/agent/consul/acl.go +++ b/agent/consul/acl.go @@ -14,6 +14,7 @@ import ( "github.com/hashicorp/consul/acl" "github.com/hashicorp/consul/agent/structs" + "github.com/hashicorp/consul/agent/token" "github.com/hashicorp/consul/logging" ) @@ -205,6 +206,9 @@ type ACLResolverConfig struct { // ACLConfig is the configuration necessary to pass through to the acl package when creating authorizers // and when authorizing access ACLConfig *acl.Config + + // Tokens is the token store of locally managed tokens + Tokens *token.Store } // ACLResolver is the type to handle all your token and policy resolution needs. @@ -239,6 +243,8 @@ type ACLResolver struct { delegate ACLResolverDelegate aclConf *acl.Config + tokens *token.Store + cache *structs.ACLCaches identityGroup singleflight.Group policyGroup singleflight.Group @@ -250,6 +256,33 @@ type ACLResolver struct { autoDisable bool disabled time.Time disabledLock sync.RWMutex + + agentMasterAuthz acl.Authorizer +} + +func agentMasterAuthorizer(nodeName string) (acl.Authorizer, error) { + // Build a policy for the agent master token. + // The builtin agent master policy allows reading any node information + // and allows writes to the agent with the node name of the running agent + // only. This used to allow a prefix match on agent names but that seems + // entirely unnecessary so it is now using an exact match. + policy := &acl.Policy{ + PolicyRules: acl.PolicyRules{ + Agents: []*acl.AgentRule{ + { + Node: nodeName, + Policy: acl.PolicyWrite, + }, + }, + NodePrefixes: []*acl.NodeRule{ + { + Name: "", + Policy: acl.PolicyRead, + }, + }, + }, + } + return acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, nil) } func NewACLResolver(config *ACLResolverConfig) (*ACLResolver, error) { @@ -286,14 +319,21 @@ func NewACLResolver(config *ACLResolverConfig) (*ACLResolver, error) { return nil, fmt.Errorf("invalid ACL down policy %q", config.Config.ACLDownPolicy) } + authz, err := agentMasterAuthorizer(config.Config.NodeName) + if err != nil { + return nil, fmt.Errorf("failed to initialize the agent master authorizer") + } + return &ACLResolver{ - config: config.Config, - logger: config.Logger.Named(logging.ACL), - delegate: config.Delegate, - aclConf: config.ACLConfig, - cache: cache, - autoDisable: config.AutoDisable, - down: down, + config: config.Config, + logger: config.Logger.Named(logging.ACL), + delegate: config.Delegate, + aclConf: config.ACLConfig, + cache: cache, + autoDisable: config.AutoDisable, + down: down, + tokens: config.Tokens, + agentMasterAuthz: authz, }, nil } @@ -1131,6 +1171,19 @@ func (r *ACLResolver) disableACLsWhenUpstreamDisabled(err error) error { return err } +func (r *ACLResolver) resolveLocallyManagedToken(token string) (structs.ACLIdentity, acl.Authorizer, bool) { + // can only resolve local tokens if we were given a token store + if r.tokens == nil { + return nil, nil, false + } + + if r.tokens.IsAgentMasterToken(token) { + return structs.NewAgentMasterTokenIdentity(r.config.NodeName, token), r.agentMasterAuthz, true + } + + return r.resolveLocallyManagedEnterpriseToken(token) +} + func (r *ACLResolver) ResolveTokenToIdentityAndAuthorizer(token string) (structs.ACLIdentity, acl.Authorizer, error) { if !r.ACLsEnabled() { return nil, nil, nil @@ -1145,6 +1198,10 @@ func (r *ACLResolver) ResolveTokenToIdentityAndAuthorizer(token string) (structs token = anonymousToken } + if ident, authz, ok := r.resolveLocallyManagedToken(token); ok { + return ident, authz, nil + } + if r.delegate.UseLegacyACLs() { identity, authorizer, err := r.resolveTokenLegacy(token) return identity, authorizer, r.disableACLsWhenUpstreamDisabled(err) @@ -1201,6 +1258,10 @@ func (r *ACLResolver) ResolveTokenToIdentity(token string) (structs.ACLIdentity, token = anonymousToken } + if ident, _, ok := r.resolveLocallyManagedToken(token); ok { + return ident, nil + } + if r.delegate.UseLegacyACLs() { identity, _, err := r.resolveTokenLegacy(token) return identity, r.disableACLsWhenUpstreamDisabled(err) diff --git a/agent/consul/acl_oss.go b/agent/consul/acl_oss.go index 591c596e11..8b0417b413 100644 --- a/agent/consul/acl_oss.go +++ b/agent/consul/acl_oss.go @@ -36,3 +36,8 @@ func (_ *ACLResolver) resolveEnterpriseIdentityAndPolicies(_ structs.ACLIdentity // this function does nothing in OSS return nil, nil, nil } + +// resolveLocallyManagedEnterpriseToken will resolve a managed service provider token to an identity and authorizer +func (_ *ACLResolver) resolveLocallyManagedEnterpriseToken(_ string) (structs.ACLIdentity, acl.Authorizer, bool) { + return nil, nil, false +} diff --git a/agent/consul/acl_server.go b/agent/consul/acl_server.go index 56627a4874..e59e9ede36 100644 --- a/agent/consul/acl_server.go +++ b/agent/consul/acl_server.go @@ -236,9 +236,6 @@ func (s *Server) ResolveTokenToIdentity(token string) (structs.ACLIdentity, erro } func (s *Server) ResolveTokenToIdentityAndAuthorizer(token string) (structs.ACLIdentity, acl.Authorizer, error) { - if id, authz := s.ResolveEntTokenToIdentityAndAuthorizer(token); id != nil && authz != nil { - return id, authz, nil - } return s.acls.ResolveTokenToIdentityAndAuthorizer(token) } @@ -271,9 +268,6 @@ func (s *Server) ResolveTokenAndDefaultMeta(token string, entMeta *structs.Enter } func (s *Server) filterACL(token string, subj interface{}) error { - if id, authz := s.ResolveEntTokenToIdentityAndAuthorizer(token); id != nil && authz != nil { - return s.acls.filterACLWithAuthorizer(authz, subj) - } return s.acls.filterACL(token, subj) } diff --git a/agent/consul/acl_server_oss.go b/agent/consul/acl_server_oss.go index 7c378143bb..97aa4e4390 100644 --- a/agent/consul/acl_server_oss.go +++ b/agent/consul/acl_server_oss.go @@ -3,15 +3,9 @@ package consul import ( - "github.com/hashicorp/consul/acl" "github.com/hashicorp/consul/agent/structs" ) -// Consul-enterprise only -func (s *Server) ResolveEntTokenToIdentityAndAuthorizer(token string) (structs.ACLIdentity, acl.Authorizer) { - return nil, nil -} - // Consul-enterprise only func (s *Server) validateEnterpriseToken(identity structs.ACLIdentity) error { return nil diff --git a/agent/consul/acl_test.go b/agent/consul/acl_test.go index 99653efdf0..3cf6b71dc5 100644 --- a/agent/consul/acl_test.go +++ b/agent/consul/acl_test.go @@ -15,6 +15,7 @@ import ( "github.com/hashicorp/consul/acl" "github.com/hashicorp/consul/agent/structs" + "github.com/hashicorp/consul/agent/token" "github.com/hashicorp/consul/api" "github.com/hashicorp/consul/sdk/testutil" "github.com/hashicorp/consul/sdk/testutil/retry" @@ -4024,3 +4025,29 @@ func TestACL_LocalToken(t *testing.T) { require.Equal(t, acl.PermissionDeniedError{Cause: "This is a local token in datacenter \"remote\""}, err) }) } + +func TestACLResolver_AgentMaster(t *testing.T) { + var tokens token.Store + + d := &ACLResolverTestDelegate{ + datacenter: "dc1", + enabled: true, + } + r := newTestACLResolver(t, d, func(cfg *ACLResolverConfig) { + cfg.Tokens = &tokens + cfg.Config.NodeName = "foo" + cfg.AutoDisable = false + }) + + tokens.UpdateAgentMasterToken("9a184a11-5599-459e-b71a-550e5f9a5a23", token.TokenSourceConfig) + + ident, authz, err := r.ResolveTokenToIdentityAndAuthorizer("9a184a11-5599-459e-b71a-550e5f9a5a23") + require.NoError(t, err) + require.NotNil(t, ident) + require.Equal(t, "agent-master:foo", ident.ID()) + require.NotNil(t, authz) + require.Equal(t, r.agentMasterAuthz, authz) + require.Equal(t, acl.Allow, authz.AgentWrite("foo", nil)) + require.Equal(t, acl.Allow, authz.NodeRead("bar", nil)) + require.Equal(t, acl.Deny, authz.NodeWrite("bar", nil)) +} diff --git a/agent/consul/client.go b/agent/consul/client.go index 7b55dfaec0..bff2565be3 100644 --- a/agent/consul/client.go +++ b/agent/consul/client.go @@ -128,6 +128,7 @@ func NewClient(config *Config, deps Deps) (*Client, error) { AutoDisable: true, CacheConfig: clientACLCacheConfig, ACLConfig: newACLConfig(c.logger), + Tokens: deps.Tokens, } var err error if c.acls, err = NewACLResolver(&aclConfig); err != nil { diff --git a/agent/consul/server.go b/agent/consul/server.go index cbb33dc082..7c694b3f18 100644 --- a/agent/consul/server.go +++ b/agent/consul/server.go @@ -443,6 +443,7 @@ func NewServer(config *Config, flat Deps) (*Server, error) { AutoDisable: false, Logger: logger, ACLConfig: s.aclConfig, + Tokens: flat.Tokens, } // Initialize the ACL resolver. if s.acls, err = NewACLResolver(&aclConfig); err != nil { diff --git a/agent/event_endpoint.go b/agent/event_endpoint.go index 1d48d3a30d..ca0583f410 100644 --- a/agent/event_endpoint.go +++ b/agent/event_endpoint.go @@ -78,7 +78,7 @@ func (s *HTTPHandlers) EventList(resp http.ResponseWriter, req *http.Request) (i // Fetch the ACL token, if any. var token string s.parseToken(req, &token) - authz, err := s.agent.resolveToken(token) + authz, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, nil, nil) if err != nil { return nil, err } diff --git a/agent/http.go b/agent/http.go index 186366cc49..d5215a2524 100644 --- a/agent/http.go +++ b/agent/http.go @@ -238,7 +238,7 @@ func (s *HTTPHandlers) handler(enableDebug bool) http.Handler { var token string s.parseToken(req, &token) - rule, err := s.agent.resolveToken(token) + rule, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, nil, nil) if err != nil { resp.WriteHeader(http.StatusForbidden) return diff --git a/agent/structs/acl.go b/agent/structs/acl.go index 64dc428212..ed2b0791ae 100644 --- a/agent/structs/acl.go +++ b/agent/structs/acl.go @@ -1798,3 +1798,55 @@ func CreateACLAuthorizationResponses(authz acl.Authorizer, requests []ACLAuthori return responses, nil } + +type AgentMasterTokenIdentity struct { + agent string + secretID string +} + +func NewAgentMasterTokenIdentity(agent string, secretID string) *AgentMasterTokenIdentity { + return &AgentMasterTokenIdentity{ + agent: agent, + secretID: secretID, + } +} + +func (id *AgentMasterTokenIdentity) ID() string { + return fmt.Sprintf("agent-master:%s", id.agent) +} + +func (id *AgentMasterTokenIdentity) SecretToken() string { + return id.secretID +} + +func (id *AgentMasterTokenIdentity) PolicyIDs() []string { + return nil +} + +func (id *AgentMasterTokenIdentity) RoleIDs() []string { + return nil +} + +func (id *AgentMasterTokenIdentity) EmbeddedPolicy() *ACLPolicy { + return nil +} + +func (id *AgentMasterTokenIdentity) ServiceIdentityList() []*ACLServiceIdentity { + return nil +} + +func (id *AgentMasterTokenIdentity) NodeIdentityList() []*ACLNodeIdentity { + return nil +} + +func (id *AgentMasterTokenIdentity) IsExpired(asOf time.Time) bool { + return false +} + +func (id *AgentMasterTokenIdentity) IsLocal() bool { + return true +} + +func (id *AgentMasterTokenIdentity) EnterpriseMetadata() *EnterpriseMeta { + return nil +} diff --git a/agent/ui_endpoint.go b/agent/ui_endpoint.go index 6be13b3845..6cfa349819 100644 --- a/agent/ui_endpoint.go +++ b/agent/ui_endpoint.go @@ -584,7 +584,7 @@ func (s *HTTPHandlers) UIMetricsProxy(resp http.ResponseWriter, req *http.Reques s.clearTokenFromHeaders(req) var entMeta structs.EnterpriseMeta - authz, err := s.agent.resolveTokenAndDefaultMeta(token, &entMeta, nil) + authz, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, &entMeta, nil) if err != nil { return nil, err }