Output user-friendly name for anonymous token (#15884)

This commit is contained in:
Chris S. Kim 2023-01-09 13:28:53 -05:00 committed by GitHub
parent 644cd864a5
commit a7b34d50fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 256 additions and 223 deletions

3
.changelog/15884.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:feature
acl: anonymous token is logged as 'anonymous token' instead of its accessor ID
```

View File

@ -2,6 +2,12 @@ package acl
const (
WildcardName = "*"
// AnonymousTokenID is the AccessorID of the anonymous token.
// When logging or displaying to users, use acl.AliasIfAnonymousToken
// to convert this to AnonymousTokenAlias.
AnonymousTokenID = "00000000-0000-0000-0000-000000000002"
AnonymousTokenAlias = "anonymous token"
)
// Config encapsulates all of the generic configuration parameters used for
@ -43,3 +49,13 @@ func (c *Config) Close() {
c.EnterpriseConfig.Close()
}
}
// AliasIfAnonymousToken returns the string "anonymous token" if
// accessorID is acl.AnonymousTokenID. Used for better
// UX when logging the accessorID.
func AliasIfAnonymousToken(accessorID string) string {
if accessorID == AnonymousTokenID {
return AnonymousTokenAlias
}
return accessorID
}

View File

@ -98,6 +98,8 @@ func (e PermissionDeniedError) Error() string {
if e.Accessor == "" {
message.WriteString(": provided token")
} else if e.Accessor == AnonymousTokenID {
message.WriteString(": anonymous token")
} else {
fmt.Fprintf(&message, ": token with AccessorID '%s'", e.Accessor)
}
@ -107,6 +109,10 @@ func (e PermissionDeniedError) Error() string {
if e.ResourceID.Name != "" {
fmt.Fprintf(&message, " on %s", e.ResourceID.ToString())
}
if e.Accessor == AnonymousTokenID {
message.WriteString(". The anonymous token is used implicitly when a request does not specify a token.")
}
return message.String()
}

View File

@ -2,9 +2,10 @@ package acl
import (
"fmt"
"github.com/stretchr/testify/require"
"regexp"
"testing"
"github.com/stretchr/testify/require"
)
func RequirePermissionDeniedError(t testing.TB, err error, authz Authorizer, _ *AuthorizerContext, resource Resource, accessLevel AccessLevel, resourceID string) {

View File

@ -169,7 +169,7 @@ func (a *Agent) filterMembers(token string, members *[]serf.Member) error {
continue
}
accessorID := authz.AccessorID()
a.logger.Debug("dropping node from result due to ACLs", "node", node, "accessorID", accessorID)
a.logger.Debug("dropping node from result due to ACLs", "node", node, "accessorID", acl.AliasIfAnonymousToken(accessorID))
m = append(m[:i], m[i+1:]...)
i--
}

View File

@ -17,8 +17,6 @@ import (
"sync"
"time"
"github.com/hashicorp/consul/proto/pboperator"
"github.com/armon/go-metrics"
"github.com/armon/go-metrics/prometheus"
"github.com/hashicorp/go-connlimit"
@ -66,6 +64,7 @@ import (
"github.com/hashicorp/consul/lib/mutex"
"github.com/hashicorp/consul/lib/routine"
"github.com/hashicorp/consul/logging"
"github.com/hashicorp/consul/proto/pboperator"
"github.com/hashicorp/consul/proto/pbpeering"
"github.com/hashicorp/consul/tlsutil"
"github.com/hashicorp/consul/types"
@ -1956,12 +1955,10 @@ OUTER:
WriteRequest: structs.WriteRequest{Token: agentToken},
}
var reply struct{}
// todo(kit) port all of these logger calls to hclog w/ loglevel configuration
// todo(kit) handle acl.ErrNotFound cases here in the future
if err := a.RPC(context.Background(), "Coordinate.Update", &req, &reply); err != nil {
if acl.IsErrPermissionDenied(err) {
accessorID := a.aclAccessorID(agentToken)
a.logger.Warn("Coordinate update blocked by ACLs", "accessorID", accessorID)
a.logger.Warn("Coordinate update blocked by ACLs", "accessorID", acl.AliasIfAnonymousToken(accessorID))
} else {
a.logger.Error("Coordinate update error", "error", err)
}

View File

@ -718,7 +718,7 @@ func (r *ACLResolver) collectPoliciesForIdentity(identity structs.ACLIdentity, p
} else {
r.logger.Warn("policy not found for identity",
"policy", policyID,
"accessorID", accessorID,
"accessorID", acl.AliasIfAnonymousToken(accessorID),
)
}
@ -819,7 +819,7 @@ func (r *ACLResolver) collectRolesForIdentity(identity structs.ACLIdentity, role
}
r.logger.Warn("role not found for identity",
"role", roleID,
"accessorID", accessorID,
"accessorID", acl.AliasIfAnonymousToken(accessorID),
)
}

View File

@ -574,7 +574,7 @@ func (a *ACL) TokenDelete(args *structs.ACLTokenDeleteRequest, reply *string) er
return fmt.Errorf("Accessor ID is missing or an invalid UUID")
}
if args.TokenID == structs.ACLTokenAnonymousID {
if args.TokenID == acl.AnonymousTokenID {
return fmt.Errorf("Delete operation not permitted on the anonymous token")
}

File diff suppressed because it is too large Load Diff

View File

@ -7,6 +7,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/hashicorp/consul/acl"
"github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/consul/testrpc"
)
@ -56,7 +57,7 @@ func testACLTokenReap_Primary(t *testing.T, local, global bool) {
codec := rpcClient(t, s1)
defer codec.Close()
acl := ACL{srv: s1}
aclEp := ACL{srv: s1}
initialManagementTokenAccessorID, err := retrieveTestTokenAccessorForSecret(codec, "root", "dc1", "root")
require.NoError(t, err)
@ -68,7 +69,7 @@ func testACLTokenReap_Primary(t *testing.T, local, global bool) {
}
var res structs.ACLTokenListResponse
err = acl.TokenList(&req, &res)
err = aclEp.TokenList(&req, &res)
if err != nil {
return nil, nil, err
}
@ -91,7 +92,7 @@ func testACLTokenReap_Primary(t *testing.T, local, global bool) {
// The initial management token and the anonymous token are always
// going to be present and global.
expectGlobal = append(expectGlobal, initialManagementTokenAccessorID)
expectGlobal = append(expectGlobal, structs.ACLTokenAnonymousID)
expectGlobal = append(expectGlobal, acl.AnonymousTokenID)
if local {
expectLocal = append(expectLocal, expect...)

View File

@ -176,11 +176,10 @@ func (s *Intention) computeApplyChangesLegacyCreate(
if !args.Intention.CanWrite(authz) {
sn := args.Intention.SourceServiceName()
dn := args.Intention.DestinationServiceName()
// todo(kit) Migrate intention access denial logging over to audit logging when we implement it
s.logger.Warn("Intention creation denied due to ACLs",
"source", sn.String(),
"destination", dn.String(),
"accessorID", accessorID)
"accessorID", acl.AliasIfAnonymousToken(accessorID))
return nil, acl.ErrPermissionDenied
}
@ -250,8 +249,9 @@ func (s *Intention) computeApplyChangesLegacyUpdate(
}
if !ixn.CanWrite(authz) {
// todo(kit) Migrate intention access denial logging over to audit logging when we implement it
s.logger.Warn("Update operation on intention denied due to ACLs", "intention", args.Intention.ID, "accessorID", accessorID)
s.logger.Warn("Update operation on intention denied due to ACLs",
"intention", args.Intention.ID,
"accessorID", acl.AliasIfAnonymousToken(accessorID))
return nil, acl.ErrPermissionDenied
}
@ -311,11 +311,10 @@ func (s *Intention) computeApplyChangesUpsert(
if !args.Intention.CanWrite(authz) {
sn := args.Intention.SourceServiceName()
dn := args.Intention.DestinationServiceName()
// todo(kit) Migrate intention access denial logging over to audit logging when we implement it
s.logger.Warn("Intention upsert denied due to ACLs",
"source", sn.String(),
"destination", dn.String(),
"accessorID", accessorID)
"accessorID", acl.AliasIfAnonymousToken(accessorID))
return nil, acl.ErrPermissionDenied
}
@ -371,8 +370,9 @@ func (s *Intention) computeApplyChangesLegacyDelete(
}
if !ixn.CanWrite(authz) {
// todo(kit) Migrate intention access denial logging over to audit logging when we implement it
s.logger.Warn("Deletion operation on intention denied due to ACLs", "intention", args.Intention.ID, "accessorID", accessorID)
s.logger.Warn("Deletion operation on intention denied due to ACLs",
"intention", args.Intention.ID,
"accessorID", acl.AliasIfAnonymousToken(accessorID))
return nil, acl.ErrPermissionDenied
}
@ -392,11 +392,10 @@ func (s *Intention) computeApplyChangesDelete(
if !args.Intention.CanWrite(authz) {
sn := args.Intention.SourceServiceName()
dn := args.Intention.DestinationServiceName()
// todo(kit) Migrate intention access denial logging over to audit logging when we implement it
s.logger.Warn("Intention delete denied due to ACLs",
"source", sn.String(),
"destination", dn.String(),
"accessorID", accessorID)
"accessorID", acl.AliasIfAnonymousToken(accessorID))
return nil, acl.ErrPermissionDenied
}
@ -483,8 +482,9 @@ func (s *Intention) Get(args *structs.IntentionQueryRequest, reply *structs.Inde
// If ACLs prevented any responses, error
if len(reply.Intentions) == 0 {
accessorID := authz.AccessorID()
// 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)
s.logger.Warn("Request to get intention denied due to ACLs",
"intention", args.IntentionID,
"accessorID", acl.AliasIfAnonymousToken(accessorID))
return acl.ErrPermissionDenied
}
@ -617,8 +617,9 @@ func (s *Intention) Match(args *structs.IntentionQueryRequest, reply *structs.In
if prefix := entry.Name; prefix != "" {
if err := authz.ToAllowAuthorizer().IntentionReadAllowed(prefix, &authzContext); err != nil {
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)
s.logger.Warn("Operation on intention prefix denied due to ACLs",
"prefix", prefix,
"accessorID", acl.AliasIfAnonymousToken(accessorID))
return err
}
}
@ -741,8 +742,9 @@ func (s *Intention) Check(args *structs.IntentionQueryRequest, reply *structs.In
query.FillAuthzContext(&authzContext)
if err := authz.ToAllowAuthorizer().ServiceReadAllowed(prefix, &authzContext); err != nil {
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)
s.logger.Warn("test on intention denied due to ACLs",
"prefix", prefix,
"accessorID", acl.AliasIfAnonymousToken(accessorID))
return err
}
}

View File

@ -755,7 +755,7 @@ func (m *Internal) EventFire(args *structs.EventFireRequest,
if err := authz.ToAllowAuthorizer().EventWriteAllowed(args.Name, nil); err != nil {
accessorID := authz.AccessorID()
m.logger.Warn("user event blocked by ACLs", "event", args.Name, "accessorID", accessorID)
m.logger.Warn("user event blocked by ACLs", "event", args.Name, "accessorID", acl.AliasIfAnonymousToken(accessorID))
return err
}

View File

@ -510,7 +510,7 @@ func (s *Server) initializeACLs(ctx context.Context) error {
// Ignoring expiration times to avoid an insertion collision.
if token == nil {
token = &structs.ACLToken{
AccessorID: structs.ACLTokenAnonymousID,
AccessorID: acl.AnonymousTokenID,
SecretID: anonymousToken,
Description: "Anonymous Token",
CreateTime: time.Now(),
@ -595,7 +595,7 @@ func (s *Server) legacyACLTokenUpgrade(ctx context.Context) error {
newToken := *token
if token.SecretID == anonymousToken {
newToken.AccessorID = structs.ACLTokenAnonymousID
newToken.AccessorID = acl.AnonymousTokenID
} else {
accessor, err := lib.GenerateUUID(s.checkTokenUUID)
if err != nil {

View File

@ -20,6 +20,7 @@ import (
msgpackrpc "github.com/hashicorp/consul-net-rpc/net-rpc-msgpackrpc"
"github.com/hashicorp/consul/acl"
"github.com/hashicorp/consul/agent/structs"
tokenStore "github.com/hashicorp/consul/agent/token"
"github.com/hashicorp/consul/api"
@ -2409,7 +2410,7 @@ func TestLeader_ACL_Initialization_AnonymousToken(t *testing.T) {
reqToken := structs.ACLTokenSetRequest{
Datacenter: "dc1",
ACLToken: structs.ACLToken{
AccessorID: structs.ACLTokenAnonymousID,
AccessorID: acl.AnonymousTokenID,
SecretID: anonymousToken,
Description: "Anonymous Token",
CreateTime: time.Now(),

View File

@ -1047,7 +1047,7 @@ func TestRPC_LocalTokenStrippedOnForward(t *testing.T) {
tokenUpsertReq := structs.ACLTokenSetRequest{
Datacenter: "dc1",
ACLToken: structs.ACLToken{
AccessorID: structs.ACLTokenAnonymousID,
AccessorID: acl.AnonymousTokenID,
Policies: []structs.ACLTokenPolicyLink{
{
ID: kvPolicy.ID,
@ -1225,7 +1225,7 @@ func TestRPC_LocalTokenStrippedOnForward_GRPC(t *testing.T) {
tokenUpsertReq := structs.ACLTokenSetRequest{
Datacenter: "dc1",
ACLToken: structs.ACLToken{
AccessorID: structs.ACLTokenAnonymousID,
AccessorID: acl.AnonymousTokenID,
Policies: []structs.ACLTokenPolicyLink{
{ID: policy.ID},
},

View File

@ -4,11 +4,11 @@ import (
"fmt"
"time"
memdb "github.com/hashicorp/go-memdb"
"github.com/hashicorp/go-memdb"
"github.com/hashicorp/consul/acl"
"github.com/hashicorp/consul/agent/structs"
pbacl "github.com/hashicorp/consul/proto/pbacl"
"github.com/hashicorp/consul/proto/pbacl"
)
// ACLTokens is used when saving a snapshot
@ -839,7 +839,7 @@ func aclTokenDeleteTxn(tx WriteTxn, idx uint64, value, index string, entMeta *ac
return nil
}
if token.(*structs.ACLToken).AccessorID == structs.ACLTokenAnonymousID {
if token.(*structs.ACLToken).AccessorID == acl.AnonymousTokenID {
return fmt.Errorf("Deletion of the builtin anonymous token is not permitted")
}

View File

@ -41,7 +41,7 @@ func setupGlobalManagement(t *testing.T, s *Store) {
func setupAnonymous(t *testing.T, s *Store) {
token := structs.ACLToken{
AccessorID: structs.ACLTokenAnonymousID,
AccessorID: acl.AnonymousTokenID,
SecretID: "anonymous",
Description: "Anonymous Token",
}
@ -979,7 +979,7 @@ func TestStateStore_ACLToken_List(t *testing.T) {
role: "",
methodName: "",
accessors: []string{
structs.ACLTokenAnonymousID,
acl.AnonymousTokenID,
"47eea4da-bda1-48a6-901c-3e36d2d9262f", // policy + global
"54866514-3cf2-4fec-8a8a-710583831834", // mgmt + global
"74277ae1-6a9b-4035-b444-2370fe6a2cb5", // authMethod + global
@ -1098,7 +1098,7 @@ func TestStateStore_ACLToken_List(t *testing.T) {
role: "",
methodName: "",
accessors: []string{
structs.ACLTokenAnonymousID,
acl.AnonymousTokenID,
"211f0360-ef53-41d3-9d4d-db84396eb6c0", // authMethod + local
"47eea4da-bda1-48a6-901c-3e36d2d9262f", // policy + global
"4915fc9d-3726-4171-b588-6c271f45eecd", // policy + local
@ -1476,7 +1476,7 @@ func TestStateStore_ACLToken_Delete(t *testing.T) {
t.Parallel()
s := testACLTokensStateStore(t)
require.Error(t, s.ACLTokenDeleteByAccessor(3, structs.ACLTokenAnonymousID, nil))
require.Error(t, s.ACLTokenDeleteByAccessor(3, acl.AnonymousTokenID, nil))
})
t.Run("Not Found", func(t *testing.T) {

View File

@ -18,7 +18,7 @@ func ACLAnonymous(t *testing.T) resolver.Result {
return resolver.Result{
Authorizer: acl.DenyAll(),
ACLIdentity: &structs.ACLToken{
AccessorID: structs.ACLTokenAnonymousID,
AccessorID: acl.AnonymousTokenID,
},
}
}

View File

@ -7,7 +7,6 @@ import (
"github.com/hashicorp/consul/acl"
"github.com/hashicorp/consul/acl/resolver"
"github.com/hashicorp/consul/agent/structs"
)
// We tag logs with a unique identifier to ease debugging. In the future this
@ -36,7 +35,7 @@ func RequireAnyValidACLToken(resolver ACLResolver, token string) error {
return status.Error(codes.Unauthenticated, err.Error())
}
if id := authz.ACLIdentity; id != nil && id.ID() == structs.ACLTokenAnonymousID {
if id := authz.ACLIdentity; id != nil && id.ID() == acl.AnonymousTokenID {
return status.Error(codes.Unauthenticated, "An ACL token must be provided (via the `x-consul-token` metadata field) to call this endpoint")
}

View File

@ -1301,7 +1301,9 @@ func (l *State) deleteService(key structs.ServiceID) error {
// todo(fs): some backoff strategy might be a better solution
l.services[key].InSync = true
accessorID := l.aclAccessorID(st)
l.logger.Warn("Service deregistration blocked by ACLs", "service", key.String(), "accessorID", accessorID)
l.logger.Warn("Service deregistration blocked by ACLs",
"service", key.String(),
"accessorID", acl.AliasIfAnonymousToken(accessorID))
metrics.IncrCounter([]string{"acl", "blocked", "service", "deregistration"}, 1)
return nil
@ -1341,7 +1343,9 @@ func (l *State) deleteCheck(key structs.CheckID) error {
// todo(fs): some backoff strategy might be a better solution
l.checks[key].InSync = true
accessorID := l.aclAccessorID(ct)
l.logger.Warn("Check deregistration blocked by ACLs", "check", key.String(), "accessorID", accessorID)
l.logger.Warn("Check deregistration blocked by ACLs",
"check", key.String(),
"accessorID", acl.AliasIfAnonymousToken(accessorID))
metrics.IncrCounter([]string{"acl", "blocked", "check", "deregistration"}, 1)
return nil
@ -1430,7 +1434,9 @@ func (l *State) syncService(key structs.ServiceID) error {
l.checks[checkKey].InSync = true
}
accessorID := l.aclAccessorID(st)
l.logger.Warn("Service registration blocked by ACLs", "service", key.String(), "accessorID", accessorID)
l.logger.Warn("Service registration blocked by ACLs",
"service", key.String(),
"accessorID", acl.AliasIfAnonymousToken(accessorID))
metrics.IncrCounter([]string{"acl", "blocked", "service", "registration"}, 1)
return nil
@ -1484,7 +1490,9 @@ func (l *State) syncCheck(key structs.CheckID) error {
// todo(fs): some backoff strategy might be a better solution
l.checks[key].InSync = true
accessorID := l.aclAccessorID(ct)
l.logger.Warn("Check registration blocked by ACLs", "check", key.String(), "accessorID", accessorID)
l.logger.Warn("Check registration blocked by ACLs",
"check", key.String(),
"accessorID", acl.AliasIfAnonymousToken(accessorID))
metrics.IncrCounter([]string{"acl", "blocked", "check", "registration"}, 1)
return nil
@ -1522,7 +1530,9 @@ func (l *State) syncNodeInfo() error {
// todo(fs): some backoff strategy might be a better solution
l.nodeInfoInSync = true
accessorID := l.aclAccessorID(at)
l.logger.Warn("Node info update blocked by ACLs", "node", l.config.NodeID, "accessorID", accessorID)
l.logger.Warn("Node info update blocked by ACLs",
"node", l.config.NodeID,
"accessorID", acl.AliasIfAnonymousToken(accessorID))
metrics.IncrCounter([]string{"acl", "blocked", "node", "registration"}, 1)
return nil

View File

@ -72,10 +72,6 @@ session_prefix "" {
policy = "write"
}` + EnterpriseACLPolicyGlobalManagement
// This is the policy ID for anonymous access. This is configurable by the
// user.
ACLTokenAnonymousID = "00000000-0000-0000-0000-000000000002"
ACLReservedPrefix = "00000000-0000-0000-0000-0000000000"
)

View File

@ -4,13 +4,14 @@ import (
"fmt"
"strings"
"github.com/hashicorp/consul/acl"
"github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/consul/api"
)
func GetTokenIDFromPartial(client *api.Client, partialID string) (string, error) {
if partialID == "anonymous" {
return structs.ACLTokenAnonymousID, nil
return acl.AnonymousTokenID, nil
}
// the full UUID string was given