Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

acl: remove ResolveTokenToIdentity #12166

Merged
merged 5 commits into from
Feb 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/12166.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:deprecation
acl: The `consul.acl.ResolveTokenToIdentity` metric is no longer reported. The values that were previous reported as part of this metric will now be part of the `consul.acl.ResolveToken` metric.
```
9 changes: 3 additions & 6 deletions agent/acl.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,15 @@ import (
// 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.
func (a *Agent) aclAccessorID(secretID string) string {
ident, err := a.delegate.ResolveTokenToIdentity(secretID)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same concern here

Copy link
Contributor Author

@dnephin dnephin Jan 26, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see this method being called in two places: Agent.filterMembers, and Agent.sendCoordinate.

Agent.sendCoordinate is similar to the other case in agent/local. It's only done when we receive a permission denied error, and we will be included the accessorID in that error soon, so we should be able to remove that call entirely.

The second call Agent.filterMembers already has an acl.Authorizer. I missed this on the first pass! I'll push a commit which removes this call, and uses authz.AccessorID() to get the value. (edit: Done in the latest commit)

ident, err := a.delegate.ResolveTokenAndDefaultMeta(secretID, nil, nil)
if acl.IsErrNotFound(err) {
return ""
}
if err != nil {
a.logger.Debug("non-critical error resolving acl token accessor for logging", "error", err)
return ""
}
if ident == nil {
return ""
}
return ident.ID()
return ident.AccessorID()
}

// vetServiceRegister makes sure the service registration action is allowed by
Expand Down Expand Up @@ -174,7 +171,7 @@ func (a *Agent) filterMembers(token string, members *[]serf.Member) error {
if authz.NodeRead(node, &authzContext) == acl.Allow {
continue
}
accessorID := a.aclAccessorID(token)
accessorID := authz.AccessorID()
a.logger.Debug("dropping node from result due to ACLs", "node", node, "accessorID", accessorID)
m = append(m[:i], m[i+1:]...)
i--
Expand Down
33 changes: 3 additions & 30 deletions agent/acl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,18 +94,10 @@ func (a *TestACLAgent) ResolveTokenToIdentityAndAuthorizer(secretID string) (str
return a.resolveAuthzFn(secretID)
}

func (a *TestACLAgent) ResolveTokenToIdentity(secretID string) (structs.ACLIdentity, error) {
if a.resolveIdentFn == nil {
return nil, fmt.Errorf("ResolveTokenToIdentity call is unexpected - no ident resolver callback set")
}

return a.resolveIdentFn(secretID)
}

func (a *TestACLAgent) ResolveTokenAndDefaultMeta(secretID string, entMeta *structs.EnterpriseMeta, authzContext *acl.AuthorizerContext) (acl.Authorizer, error) {
func (a *TestACLAgent) ResolveTokenAndDefaultMeta(secretID string, entMeta *structs.EnterpriseMeta, authzContext *acl.AuthorizerContext) (consul.ACLResolveResult, error) {
identity, authz, err := a.ResolveTokenToIdentityAndAuthorizer(secretID)
if err != nil {
return nil, err
return consul.ACLResolveResult{}, err
}

// Default the EnterpriseMeta based on the Tokens meta or actual defaults
Expand All @@ -119,7 +111,7 @@ func (a *TestACLAgent) ResolveTokenAndDefaultMeta(secretID string, entMeta *stru
// Use the meta to fill in the ACL authorization context
entMeta.FillAuthzContext(authzContext)

return authz, err
return consul.ACLResolveResult{Authorizer: authz, ACLIdentity: identity}, err
}

// All of these are stubs to satisfy the interface
Expand Down Expand Up @@ -523,22 +515,3 @@ func TestACL_filterChecksWithAuthorizer(t *testing.T) {
_, ok = checks["my-other"]
require.False(t, ok)
}

// TODO: remove?
func TestACL_ResolveIdentity(t *testing.T) {
t.Parallel()
a := NewTestACLAgent(t, t.Name(), TestACLConfig(), nil, catalogIdent)

// this test is meant to ensure we are calling the correct function
// which is ResolveTokenToIdentity on the Agent delegate. Our
// nil authz resolver will cause it to emit an error if used
ident, err := a.delegate.ResolveTokenToIdentity(nodeROSecret)
require.NoError(t, err)
require.NotNil(t, ident)

// just double checkingto ensure if we had used the wrong function
// that an error would be produced
_, err = a.delegate.ResolveTokenAndDefaultMeta(nodeROSecret, nil, nil)
require.Error(t, err)

}
5 changes: 1 addition & 4 deletions agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,14 +167,11 @@ type delegate interface {
// RemoveFailedNode is used to remove a failed node from the cluster.
RemoveFailedNode(node string, prune bool, entMeta *structs.EnterpriseMeta) error

// TODO: replace this method with consul.ACLResolver
ResolveTokenToIdentity(token string) (structs.ACLIdentity, error)

// ResolveTokenAndDefaultMeta returns an acl.Authorizer which authorizes
// actions based on the permissions granted to the token.
// If either entMeta or authzContext are non-nil they will be populated with the
// default partition and namespace from the token.
ResolveTokenAndDefaultMeta(token string, entMeta *structs.EnterpriseMeta, authzContext *acl.AuthorizerContext) (acl.Authorizer, error)
ResolveTokenAndDefaultMeta(token string, entMeta *structs.EnterpriseMeta, authzContext *acl.AuthorizerContext) (consul.ACLResolveResult, error)

RPC(method string, args interface{}, reply interface{}) error
SnapshotRPC(args *structs.SnapshotRequest, in io.Reader, out io.Writer, replyFn structs.SnapshotReplyFn) error
Expand Down
4 changes: 2 additions & 2 deletions agent/agent_endpoint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1640,8 +1640,8 @@ type fakeResolveTokenDelegate struct {
authorizer acl.Authorizer
}

func (f fakeResolveTokenDelegate) ResolveTokenAndDefaultMeta(_ string, _ *structs.EnterpriseMeta, _ *acl.AuthorizerContext) (acl.Authorizer, error) {
return f.authorizer, nil
func (f fakeResolveTokenDelegate) ResolveTokenAndDefaultMeta(_ string, _ *structs.EnterpriseMeta, _ *acl.AuthorizerContext) (consul.ACLResolveResult, error) {
return consul.ACLResolveResult{Authorizer: f.authorizer}, nil
}

func TestAgent_Reload(t *testing.T) {
Expand Down
42 changes: 12 additions & 30 deletions agent/consul/acl.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,6 @@ var ACLSummaries = []prometheus.SummaryDefinition{
Name: []string{"acl", "ResolveToken"},
Help: "This measures the time it takes to resolve an ACL token.",
},
{
Name: []string{"acl", "ResolveTokenToIdentity"},
Help: "This measures the time it takes to resolve an ACL token to an Identity.",
},
}

// These must be kept in sync with the constants in command/agent/acl.go.
Expand Down Expand Up @@ -1115,31 +1111,17 @@ func (r *ACLResolver) ResolveTokenToIdentityAndAuthorizer(token string) (structs
return identity, acl.NewChainedAuthorizer(chain), nil
}

// TODO: rename to AccessorIDFromToken. This method is only used to retrieve the
// ACLIdentity.ID, so we don't need to return a full ACLIdentity. We could
// return a much smaller type (instad of just a string) to allow for changes
// in the future.
func (r *ACLResolver) ResolveTokenToIdentity(token string) (structs.ACLIdentity, error) {
if !r.ACLsEnabled() {
return nil, nil
}

if acl.RootAuthorizer(token) != nil {
return nil, acl.ErrRootDenied
}

// handle the anonymous token
if token == "" {
token = anonymousToken
}
type ACLResolveResult struct {
acl.Authorizer
// TODO: likely we can reduce this interface
structs.ACLIdentity
}

if ident, _, ok := r.resolveLocallyManagedToken(token); ok {
return ident, nil
func (a ACLResolveResult) AccessorID() string {
if a.ACLIdentity == nil {
return ""
}

defer metrics.MeasureSince([]string{"acl", "ResolveTokenToIdentity"}, time.Now())
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just noticed I am removing this metric. I'm going to push one more commit that documents that this metric is being removed. I'll add a changelog as well.


return r.resolveIdentityFromToken(token)
return a.ACLIdentity.ID()
}

func (r *ACLResolver) ACLsEnabled() bool {
Expand All @@ -1158,10 +1140,10 @@ func (r *ACLResolver) ACLsEnabled() bool {
return true
}

func (r *ACLResolver) ResolveTokenAndDefaultMeta(token string, entMeta *structs.EnterpriseMeta, authzContext *acl.AuthorizerContext) (acl.Authorizer, error) {
func (r *ACLResolver) ResolveTokenAndDefaultMeta(token string, entMeta *structs.EnterpriseMeta, authzContext *acl.AuthorizerContext) (ACLResolveResult, error) {
identity, authz, err := r.ResolveTokenToIdentityAndAuthorizer(token)
if err != nil {
return nil, err
return ACLResolveResult{}, err
}

if entMeta == nil {
Expand All @@ -1179,7 +1161,7 @@ func (r *ACLResolver) ResolveTokenAndDefaultMeta(token string, entMeta *structs.
// Use the meta to fill in the ACL authorization context
entMeta.FillAuthzContext(authzContext)

return authz, err
return ACLResolveResult{Authorizer: authz, ACLIdentity: identity}, err
}

// aclFilter is used to filter results from our state store based on ACL rules
Expand Down
3 changes: 1 addition & 2 deletions agent/consul/acl_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,13 +159,12 @@ func (s *Server) ResolveRoleFromID(roleID string) (bool, *structs.ACLRole, error
return s.InPrimaryDatacenter() || index > 0, role, acl.ErrNotFound
}

// TODO: remove
func (s *Server) ResolveToken(token string) (acl.Authorizer, error) {
Comment on lines +162 to 163
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is done in #12167

_, authz, err := s.ACLResolver.ResolveTokenToIdentityAndAuthorizer(token)
return authz, err
}

// TODO: Client has an identical implementation, remove duplication

func (s *Server) filterACL(token string, subj interface{}) error {
return filterACL(s.ACLResolver, token, subj)
}
Expand Down
30 changes: 0 additions & 30 deletions agent/consul/acl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1534,36 +1534,6 @@ func TestACLResolver_Client(t *testing.T) {
require.Equal(t, policyResolves, int32(3))
})

t.Run("Resolve-Identity", func(t *testing.T) {
t.Parallel()

delegate := &ACLResolverTestDelegate{
enabled: true,
datacenter: "dc1",
legacy: false,
localTokens: false,
localPolicies: false,
}

delegate.tokenReadFn = delegate.plainTokenReadFn
delegate.policyResolveFn = delegate.plainPolicyResolveFn
delegate.roleResolveFn = delegate.plainRoleResolveFn

r := newTestACLResolver(t, delegate, nil)

ident, err := r.ResolveTokenToIdentity("found-policy-and-role")
require.NoError(t, err)
require.NotNil(t, ident)
require.Equal(t, "5f57c1f6-6a89-4186-9445-531b316e01df", ident.ID())
require.EqualValues(t, 0, delegate.localTokenResolutions)
require.EqualValues(t, 1, delegate.remoteTokenResolutions)
require.EqualValues(t, 0, delegate.localPolicyResolutions)
require.EqualValues(t, 0, delegate.remotePolicyResolutions)
require.EqualValues(t, 0, delegate.localRoleResolutions)
require.EqualValues(t, 0, delegate.remoteRoleResolutions)
require.EqualValues(t, 0, delegate.remoteLegacyResolutions)
})

t.Run("Concurrent-Token-Resolve", func(t *testing.T) {
t.Parallel()

Expand Down
32 changes: 7 additions & 25 deletions agent/consul/intention_endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ func (s *Intention) Apply(args *structs.IntentionRequest, reply *string) error {
}

// Get the ACL token for the request for the checks below.
// TODO: use ResolveTokenAndDefaultMeta
identity, authz, err := s.srv.ACLResolver.ResolveTokenToIdentityAndAuthorizer(args.Token)
if err != nil {
return err
Expand Down Expand Up @@ -432,7 +433,8 @@ func (s *Intention) Get(args *structs.IntentionQueryRequest, reply *structs.Inde

// Get the ACL token for the request for the checks below.
var entMeta structs.EnterpriseMeta
if _, err := s.srv.ResolveTokenAndDefaultMeta(args.Token, &entMeta, nil); err != nil {
authz, err := s.srv.ResolveTokenAndDefaultMeta(args.Token, &entMeta, nil)
if err != nil {
return err
}

Expand Down Expand Up @@ -479,13 +481,11 @@ func (s *Intention) Get(args *structs.IntentionQueryRequest, reply *structs.Inde
reply.Intentions = structs.Intentions{ixn}

// Filter
if err := s.srv.filterACL(args.Token, reply); err != nil {
return err
}
s.srv.filterACLWithAuthorizer(authz, reply)

// If ACLs prevented any responses, error
if len(reply.Intentions) == 0 {
accessorID := s.aclAccessorID(args.Token)
accessorID := authz.AccessorID()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great, love that this avoids re-resolving the token

// todo(kit) Migrate intention access denial logging over to audit logging when we implement it
s.logger.Warn("Request to get intention denied due to ACLs", "intention", args.IntentionID, "accessorID", accessorID)
return acl.ErrPermissionDenied
Expand Down Expand Up @@ -618,7 +618,7 @@ func (s *Intention) Match(args *structs.IntentionQueryRequest, reply *structs.In
for _, entry := range args.Match.Entries {
entry.FillAuthzContext(&authzContext)
if prefix := entry.Name; prefix != "" && authz.IntentionRead(prefix, &authzContext) != acl.Allow {
accessorID := s.aclAccessorID(args.Token)
accessorID := authz.AccessorID()
// todo(kit) Migrate intention access denial logging over to audit logging when we implement it
s.logger.Warn("Operation on intention prefix denied due to ACLs", "prefix", prefix, "accessorID", accessorID)
return acl.ErrPermissionDenied
Expand Down Expand Up @@ -708,7 +708,7 @@ func (s *Intention) Check(args *structs.IntentionQueryRequest, reply *structs.In
var authzContext acl.AuthorizerContext
query.FillAuthzContext(&authzContext)
if authz.ServiceRead(prefix, &authzContext) != acl.Allow {
accessorID := s.aclAccessorID(args.Token)
accessorID := authz.AccessorID()
// todo(kit) Migrate intention access denial logging over to audit logging when we implement it
s.logger.Warn("test on intention denied due to ACLs", "prefix", prefix, "accessorID", accessorID)
return acl.ErrPermissionDenied
Expand Down Expand Up @@ -760,24 +760,6 @@ func (s *Intention) Check(args *structs.IntentionQueryRequest, reply *structs.In
return nil
}

// 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.
func (s *Intention) aclAccessorID(secretID string) string {
_, ident, err := s.srv.ResolveIdentityFromToken(secretID)
if acl.IsErrNotFound(err) {
return ""
}
if err != nil {
s.logger.Debug("non-critical error resolving acl token accessor for logging", "error", err)
return ""
}
if ident == nil {
return ""
}
return ident.ID()
}

func (s *Intention) validateEnterpriseIntention(ixn *structs.Intention) error {
if err := s.srv.validateEnterpriseIntentionPartition(ixn.SourcePartition); err != nil {
return fmt.Errorf("Invalid source partition %q: %v", ixn.SourcePartition, err)
Expand Down
22 changes: 2 additions & 20 deletions agent/consul/internal_endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -401,13 +401,13 @@ func (m *Internal) EventFire(args *structs.EventFireRequest,
}

// Check ACLs
authz, err := m.srv.ResolveToken(args.Token)
authz, err := m.srv.ResolveTokenAndDefaultMeta(args.Token, nil, nil)
if err != nil {
return err
}

if authz.EventWrite(args.Name, nil) != acl.Allow {
accessorID := m.aclAccessorID(args.Token)
accessorID := authz.AccessorID()
m.logger.Warn("user event blocked by ACLs", "event", args.Name, "accessorID", accessorID)
return acl.ErrPermissionDenied
}
Expand Down Expand Up @@ -545,21 +545,3 @@ func (m *Internal) executeKeyringOpMgr(

return serfResp, err
}

// 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.
func (m *Internal) aclAccessorID(secretID string) string {
_, ident, err := m.srv.ResolveIdentityFromToken(secretID)
if acl.IsErrNotFound(err) {
return ""
}
if err != nil {
m.logger.Debug("non-critical error resolving acl token accessor for logging", "error", err)
return ""
}
if ident == nil {
return ""
}
return ident.ID()
}
5 changes: 0 additions & 5 deletions agent/delegate_mock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,6 @@ func (m *delegateMock) RemoveFailedNode(node string, prune bool, entMeta *struct
return m.Called(node, prune, entMeta).Error(0)
}

func (m *delegateMock) ResolveTokenToIdentity(token string) (structs.ACLIdentity, error) {
ret := m.Called(token)
return ret.Get(0).(structs.ACLIdentity), ret.Error(1)
}

func (m *delegateMock) ResolveTokenAndDefaultMeta(token string, entMeta *structs.EnterpriseMeta, authzContext *acl.AuthorizerContext) (acl.Authorizer, error) {
ret := m.Called(token, entMeta, authzContext)
return ret.Get(0).(acl.Authorizer), ret.Error(1)
Expand Down
Loading