Move static token resolution into the ACLResolver (#10013)

This commit is contained in:
Matt Keeler 2021-04-14 12:39:35 -04:00 committed by GitHub
parent 0470d9ec25
commit aa0eb60f57
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 199 additions and 154 deletions

3
.changelog/10013.txt Normal file
View File

@ -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.
```

View File

@ -9,34 +9,6 @@ import (
"github.com/hashicorp/consul/agent/structs" "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- // 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 // 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. // 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() 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 // vetServiceRegister makes sure the service registration action is allowed by
// the given token. // the given token.
func (a *Agent) vetServiceRegister(token string, service *structs.NodeService) error { func (a *Agent) vetServiceRegister(token string, service *structs.NodeService) error {
// Resolve the token and bail if ACLs aren't enabled. // 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 { if err != nil {
return err return err
} }
@ -130,7 +77,7 @@ func (a *Agent) vetServiceRegisterWithAuthorizer(authz acl.Authorizer, service *
// token. // token.
func (a *Agent) vetServiceUpdate(token string, serviceID structs.ServiceID) error { func (a *Agent) vetServiceUpdate(token string, serviceID structs.ServiceID) error {
// Resolve the token and bail if ACLs aren't enabled. // 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 { if err != nil {
return err return err
} }
@ -163,7 +110,7 @@ func (a *Agent) vetServiceUpdateWithAuthorizer(authz acl.Authorizer, serviceID s
// given token. // given token.
func (a *Agent) vetCheckRegister(token string, check *structs.HealthCheck) error { func (a *Agent) vetCheckRegister(token string, check *structs.HealthCheck) error {
// Resolve the token and bail if ACLs aren't enabled. // 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 { if err != nil {
return err 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. // vetCheckUpdate makes sure that a check update is allowed by the given token.
func (a *Agent) vetCheckUpdate(token string, checkID structs.CheckID) error { func (a *Agent) vetCheckUpdate(token string, checkID structs.CheckID) error {
// Resolve the token and bail if ACLs aren't enabled. // 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 { if err != nil {
return err 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. // filterMembers redacts members that the token doesn't have access to.
func (a *Agent) filterMembers(token string, members *[]serf.Member) error { func (a *Agent) filterMembers(token string, members *[]serf.Member) error {
// Resolve the token and bail if ACLs aren't enabled. // 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 { if err != nil {
return err 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. // filterServices redacts services that the token doesn't have access to.
func (a *Agent) filterServices(token string, services *map[structs.ServiceID]*structs.NodeService) error { func (a *Agent) filterServices(token string, services *map[structs.ServiceID]*structs.NodeService) error {
// Resolve the token and bail if ACLs aren't enabled. // 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 { if err != nil {
return err 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. // filterChecks redacts checks that the token doesn't have access to.
func (a *Agent) filterChecks(token string, checks *map[structs.CheckID]*structs.HealthCheck) error { func (a *Agent) filterChecks(token string, checks *map[structs.CheckID]*structs.HealthCheck) error {
// Resolve the token and bail if ACLs aren't enabled. // 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 { if err != nil {
return err return err
} }

View File

@ -104,7 +104,7 @@ func (s *HTTPHandlers) ACLRulesTranslate(resp http.ResponseWriter, req *http.Req
var token string var token string
s.parseToken(req, &token) s.parseToken(req, &token)
rule, err := s.agent.resolveToken(token) rule, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, nil, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1153,7 +1153,7 @@ func (s *HTTPHandlers) ACLAuthorize(resp http.ResponseWriter, req *http.Request)
return nil, err return nil, err
} }
} else { } else {
authz, err := s.agent.resolveToken(request.Token) authz, err := s.agent.delegate.ResolveTokenAndDefaultMeta(request.Token, nil, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} else if authz == nil { } else if authz == nil {

View File

@ -177,46 +177,11 @@ func TestACL_Version8EnabledByDefault(t *testing.T) {
} }
a := NewTestACLAgent(t, t.Name(), TestACLConfig(), resolveFn, nil) a := NewTestACLAgent(t, t.Name(), TestACLConfig(), resolveFn, nil)
_, err := a.resolveToken("nope") _, err := a.delegate.ResolveTokenAndDefaultMeta("nope", nil, nil)
require.Error(t, err) require.Error(t, err)
require.True(t, called) 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) { func authzFromPolicy(policy *acl.Policy, cfg *acl.Config) (acl.Authorizer, error) {
return acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, cfg) 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 // just double checkingto ensure if we had used the wrong function
// that an error would be produced // that an error would be produced
_, err = a.resolveToken(nodeROSecret) _, err = a.delegate.ResolveTokenAndDefaultMeta(nodeROSecret, nil, nil)
require.Error(t, err) require.Error(t, err)
} }

View File

@ -386,13 +386,6 @@ func New(bd BaseDeps) (*Agent, error) {
a.serviceManager = NewServiceManager(&a) 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 // 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 // there any longer. Originally it did because we passed the agent
// delegate to some of the cache registrations. Now we just // delegate to some of the cache registrations. Now we just
@ -658,7 +651,9 @@ func (a *Agent) listenAndServeGRPC() error {
xdsServer := &xds.Server{ xdsServer := &xds.Server{
Logger: a.logger.Named(logging.Envoy), Logger: a.logger.Named(logging.Envoy),
CfgMgr: a.proxyConfig, CfgMgr: a.proxyConfig,
ResolveToken: a.resolveToken, ResolveToken: func(id string) (acl.Authorizer, error) {
return a.delegate.ResolveTokenAndDefaultMeta(id, nil, nil)
},
CheckFetcher: a, CheckFetcher: a,
CfgFetcher: a, CfgFetcher: a,
AuthCheckFrequency: xds.DefaultAuthCheckFrequency, AuthCheckFrequency: xds.DefaultAuthCheckFrequency,

View File

@ -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. // Fetch the ACL token, if any, and enforce agent policy.
var token string var token string
s.parseToken(req, &token) s.parseToken(req, &token)
rule, err := s.agent.resolveToken(token) rule, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, nil, nil)
if err != nil { if err != nil {
return nil, err 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. // Fetch the ACL token, if any, and enforce agent policy.
var token string var token string
s.parseToken(req, &token) s.parseToken(req, &token)
rule, err := s.agent.resolveToken(token) rule, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, nil, nil)
if err != nil { if err != nil {
return nil, err 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. // Fetch the ACL token, if any, and enforce agent policy.
var token string var token string
s.parseToken(req, &token) s.parseToken(req, &token)
rule, err := s.agent.resolveToken(token) rule, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, nil, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -239,7 +239,7 @@ func (s *HTTPHandlers) AgentServices(resp http.ResponseWriter, req *http.Request
var filterExpression string var filterExpression string
s.parseFilter(req, &filterExpression) s.parseFilter(req, &filterExpression)
authz, err := s.agent.resolveTokenAndDefaultMeta(token, &entMeta, nil) authz, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, &entMeta, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -296,7 +296,7 @@ func (s *HTTPHandlers) AgentService(resp http.ResponseWriter, req *http.Request)
} }
// need to resolve to default the meta // 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 { if err != nil {
return nil, err return nil, err
} }
@ -325,7 +325,7 @@ func (s *HTTPHandlers) AgentService(resp http.ResponseWriter, req *http.Request)
ws.Add(svcState.WatchCh) ws.Add(svcState.WatchCh)
// Check ACLs. // Check ACLs.
authz, err := s.agent.resolveToken(token) authz, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, nil, nil)
if err != nil { if err != nil {
return "", nil, err return "", nil, err
} }
@ -366,7 +366,7 @@ func (s *HTTPHandlers) AgentChecks(resp http.ResponseWriter, req *http.Request)
return nil, err return nil, err
} }
authz, err := s.agent.resolveTokenAndDefaultMeta(token, &entMeta, nil) authz, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, &entMeta, nil)
if err != nil { if err != nil {
return nil, err 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. // Fetch the ACL token, if any, and enforce agent policy.
var token string var token string
s.parseToken(req, &token) s.parseToken(req, &token)
rule, err := s.agent.resolveToken(token) rule, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, nil, nil)
if err != nil { if err != nil {
return nil, err 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. // Fetch the ACL token, if any, and enforce agent policy.
var token string var token string
s.parseToken(req, &token) s.parseToken(req, &token)
rule, err := s.agent.resolveToken(token) rule, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, nil, nil)
if err != nil { if err != nil {
return nil, err 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. // Fetch the ACL token, if any, and enforce agent policy.
var token string var token string
s.parseToken(req, &token) s.parseToken(req, &token)
rule, err := s.agent.resolveToken(token) rule, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, nil, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -550,7 +550,7 @@ func (s *HTTPHandlers) AgentRegisterCheck(resp http.ResponseWriter, req *http.Re
return nil, nil 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 { if err != nil {
return nil, err return nil, err
} }
@ -602,7 +602,7 @@ func (s *HTTPHandlers) AgentDeregisterCheck(resp http.ResponseWriter, req *http.
return nil, err 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 { if err != nil {
return nil, err return nil, err
} }
@ -690,7 +690,7 @@ func (s *HTTPHandlers) agentCheckUpdate(_resp http.ResponseWriter, req *http.Req
return nil, err 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 { if err != nil {
return nil, err return nil, err
} }
@ -767,7 +767,7 @@ func (s *HTTPHandlers) AgentHealthServiceByID(resp http.ResponseWriter, req *htt
// need to resolve to default the meta // need to resolve to default the meta
var authzContext acl.AuthorizerContext var authzContext acl.AuthorizerContext
authz, err := s.agent.resolveTokenAndDefaultMeta(token, &entMeta, &authzContext) authz, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, &entMeta, &authzContext)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -821,7 +821,7 @@ func (s *HTTPHandlers) AgentHealthServiceByName(resp http.ResponseWriter, req *h
// need to resolve to default the meta // need to resolve to default the meta
var authzContext acl.AuthorizerContext var authzContext acl.AuthorizerContext
authz, err := s.agent.resolveTokenAndDefaultMeta(token, &entMeta, &authzContext) authz, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, &entMeta, &authzContext)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -899,7 +899,7 @@ func (s *HTTPHandlers) AgentRegisterService(resp http.ResponseWriter, req *http.
var token string var token string
s.parseToken(req, &token) 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 { if err != nil {
return nil, err return nil, err
} }
@ -1032,7 +1032,7 @@ func (s *HTTPHandlers) AgentDeregisterService(resp http.ResponseWriter, req *htt
return nil, err 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 { if err != nil {
return nil, err return nil, err
} }
@ -1085,7 +1085,7 @@ func (s *HTTPHandlers) AgentServiceMaintenance(resp http.ResponseWriter, req *ht
return nil, err 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 { if err != nil {
return nil, err 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. // Get the provided token, if any, and vet against any ACL policies.
var token string var token string
s.parseToken(req, &token) s.parseToken(req, &token)
rule, err := s.agent.resolveToken(token) rule, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, nil, nil)
if err != nil { if err != nil {
return nil, err 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. // Fetch the ACL token, if any, and enforce agent policy.
var token string var token string
s.parseToken(req, &token) s.parseToken(req, &token)
rule, err := s.agent.resolveToken(token) rule, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, nil, nil)
if err != nil { if err != nil {
return nil, err 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. // Fetch the ACL token, if any, and enforce agent policy.
var token string var token string
s.parseToken(req, &token) s.parseToken(req, &token)
rule, err := s.agent.resolveToken(token) rule, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, nil, nil)
if err != nil { if err != nil {
return nil, err 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. // Fetch the ACL token, if any, and enforce agent policy.
var token string var token string
s.parseToken(req, &token) s.parseToken(req, &token)
rule, err := s.agent.resolveToken(token) rule, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, nil, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -59,7 +59,7 @@ func (a *Agent) ConnectAuthorize(token string,
// We do this manually here since the RPC request below only verifies // We do this manually here since the RPC request below only verifies
// service:read. // service:read.
var authzContext acl.AuthorizerContext var authzContext acl.AuthorizerContext
authz, err := a.resolveTokenAndDefaultMeta(token, &req.EnterpriseMeta, &authzContext) authz, err := a.delegate.ResolveTokenAndDefaultMeta(token, &req.EnterpriseMeta, &authzContext)
if err != nil { if err != nil {
return returnErr(err) return returnErr(err)
} }

View File

@ -14,6 +14,7 @@ import (
"github.com/hashicorp/consul/acl" "github.com/hashicorp/consul/acl"
"github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/consul/agent/token"
"github.com/hashicorp/consul/logging" "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 // ACLConfig is the configuration necessary to pass through to the acl package when creating authorizers
// and when authorizing access // and when authorizing access
ACLConfig *acl.Config 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. // ACLResolver is the type to handle all your token and policy resolution needs.
@ -239,6 +243,8 @@ type ACLResolver struct {
delegate ACLResolverDelegate delegate ACLResolverDelegate
aclConf *acl.Config aclConf *acl.Config
tokens *token.Store
cache *structs.ACLCaches cache *structs.ACLCaches
identityGroup singleflight.Group identityGroup singleflight.Group
policyGroup singleflight.Group policyGroup singleflight.Group
@ -250,6 +256,33 @@ type ACLResolver struct {
autoDisable bool autoDisable bool
disabled time.Time disabled time.Time
disabledLock sync.RWMutex 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) { func NewACLResolver(config *ACLResolverConfig) (*ACLResolver, error) {
@ -286,6 +319,11 @@ func NewACLResolver(config *ACLResolverConfig) (*ACLResolver, error) {
return nil, fmt.Errorf("invalid ACL down policy %q", config.Config.ACLDownPolicy) 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{ return &ACLResolver{
config: config.Config, config: config.Config,
logger: config.Logger.Named(logging.ACL), logger: config.Logger.Named(logging.ACL),
@ -294,6 +332,8 @@ func NewACLResolver(config *ACLResolverConfig) (*ACLResolver, error) {
cache: cache, cache: cache,
autoDisable: config.AutoDisable, autoDisable: config.AutoDisable,
down: down, down: down,
tokens: config.Tokens,
agentMasterAuthz: authz,
}, nil }, nil
} }
@ -1131,6 +1171,19 @@ func (r *ACLResolver) disableACLsWhenUpstreamDisabled(err error) error {
return err 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) { func (r *ACLResolver) ResolveTokenToIdentityAndAuthorizer(token string) (structs.ACLIdentity, acl.Authorizer, error) {
if !r.ACLsEnabled() { if !r.ACLsEnabled() {
return nil, nil, nil return nil, nil, nil
@ -1145,6 +1198,10 @@ func (r *ACLResolver) ResolveTokenToIdentityAndAuthorizer(token string) (structs
token = anonymousToken token = anonymousToken
} }
if ident, authz, ok := r.resolveLocallyManagedToken(token); ok {
return ident, authz, nil
}
if r.delegate.UseLegacyACLs() { if r.delegate.UseLegacyACLs() {
identity, authorizer, err := r.resolveTokenLegacy(token) identity, authorizer, err := r.resolveTokenLegacy(token)
return identity, authorizer, r.disableACLsWhenUpstreamDisabled(err) return identity, authorizer, r.disableACLsWhenUpstreamDisabled(err)
@ -1201,6 +1258,10 @@ func (r *ACLResolver) ResolveTokenToIdentity(token string) (structs.ACLIdentity,
token = anonymousToken token = anonymousToken
} }
if ident, _, ok := r.resolveLocallyManagedToken(token); ok {
return ident, nil
}
if r.delegate.UseLegacyACLs() { if r.delegate.UseLegacyACLs() {
identity, _, err := r.resolveTokenLegacy(token) identity, _, err := r.resolveTokenLegacy(token)
return identity, r.disableACLsWhenUpstreamDisabled(err) return identity, r.disableACLsWhenUpstreamDisabled(err)

View File

@ -36,3 +36,8 @@ func (_ *ACLResolver) resolveEnterpriseIdentityAndPolicies(_ structs.ACLIdentity
// this function does nothing in OSS // this function does nothing in OSS
return nil, nil, nil 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
}

View File

@ -236,9 +236,6 @@ func (s *Server) ResolveTokenToIdentity(token string) (structs.ACLIdentity, erro
} }
func (s *Server) ResolveTokenToIdentityAndAuthorizer(token string) (structs.ACLIdentity, acl.Authorizer, error) { 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) 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 { 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) return s.acls.filterACL(token, subj)
} }

View File

@ -3,15 +3,9 @@
package consul package consul
import ( import (
"github.com/hashicorp/consul/acl"
"github.com/hashicorp/consul/agent/structs" "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 // Consul-enterprise only
func (s *Server) validateEnterpriseToken(identity structs.ACLIdentity) error { func (s *Server) validateEnterpriseToken(identity structs.ACLIdentity) error {
return nil return nil

View File

@ -15,6 +15,7 @@ import (
"github.com/hashicorp/consul/acl" "github.com/hashicorp/consul/acl"
"github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/consul/agent/token"
"github.com/hashicorp/consul/api" "github.com/hashicorp/consul/api"
"github.com/hashicorp/consul/sdk/testutil" "github.com/hashicorp/consul/sdk/testutil"
"github.com/hashicorp/consul/sdk/testutil/retry" "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) 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))
}

View File

@ -128,6 +128,7 @@ func NewClient(config *Config, deps Deps) (*Client, error) {
AutoDisable: true, AutoDisable: true,
CacheConfig: clientACLCacheConfig, CacheConfig: clientACLCacheConfig,
ACLConfig: newACLConfig(c.logger), ACLConfig: newACLConfig(c.logger),
Tokens: deps.Tokens,
} }
var err error var err error
if c.acls, err = NewACLResolver(&aclConfig); err != nil { if c.acls, err = NewACLResolver(&aclConfig); err != nil {

View File

@ -443,6 +443,7 @@ func NewServer(config *Config, flat Deps) (*Server, error) {
AutoDisable: false, AutoDisable: false,
Logger: logger, Logger: logger,
ACLConfig: s.aclConfig, ACLConfig: s.aclConfig,
Tokens: flat.Tokens,
} }
// Initialize the ACL resolver. // Initialize the ACL resolver.
if s.acls, err = NewACLResolver(&aclConfig); err != nil { if s.acls, err = NewACLResolver(&aclConfig); err != nil {

View File

@ -78,7 +78,7 @@ func (s *HTTPHandlers) EventList(resp http.ResponseWriter, req *http.Request) (i
// Fetch the ACL token, if any. // Fetch the ACL token, if any.
var token string var token string
s.parseToken(req, &token) s.parseToken(req, &token)
authz, err := s.agent.resolveToken(token) authz, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, nil, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -238,7 +238,7 @@ func (s *HTTPHandlers) handler(enableDebug bool) http.Handler {
var token string var token string
s.parseToken(req, &token) s.parseToken(req, &token)
rule, err := s.agent.resolveToken(token) rule, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, nil, nil)
if err != nil { if err != nil {
resp.WriteHeader(http.StatusForbidden) resp.WriteHeader(http.StatusForbidden)
return return

View File

@ -1798,3 +1798,55 @@ func CreateACLAuthorizationResponses(authz acl.Authorizer, requests []ACLAuthori
return responses, nil 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
}

View File

@ -584,7 +584,7 @@ func (s *HTTPHandlers) UIMetricsProxy(resp http.ResponseWriter, req *http.Reques
s.clearTokenFromHeaders(req) s.clearTokenFromHeaders(req)
var entMeta structs.EnterpriseMeta var entMeta structs.EnterpriseMeta
authz, err := s.agent.resolveTokenAndDefaultMeta(token, &entMeta, nil) authz, err := s.agent.delegate.ResolveTokenAndDefaultMeta(token, &entMeta, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }