Move static token resolution into the ACLResolver (#10013)
This commit is contained in:
parent
0470d9ec25
commit
aa0eb60f57
|
@ -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.
|
||||
```
|
67
agent/acl.go
67
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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue