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

Output user-friendly name for anonymous token #15884

Merged
merged 14 commits into from
Jan 9, 2023

Conversation

kisunji
Copy link
Contributor

@kisunji kisunji commented Jan 3, 2023

Description

When ACLs are enabled but an operator forgets to pass a token, a potential ACL error may refer to the anonymous token which appears in the logs as 00000000-0000-0000-0000-000000000002.

We should avoid the accessorID being a source of confusion for users by simply aliasing it to anonymous token whenever we output it in logs.

structs.ACLTokenAnonymousID -> acl.AnonymousTokenID refactor was done partly to resolve an import cycle but a broader objective is to fold more ACL-related domain objects into acl package.

Testing & Reproduction steps

  • No behavioral changes

PR Checklist

  • updated test coverage
  • external facing docs updated
  • not a security concern

Sorry, something went wrong.

@github-actions github-actions bot added theme/acls ACL and token generation theme/cli Flags and documentation for the CLI interface labels Jan 3, 2023
@kisunji kisunji added the theme/operator-usability Replaces UX. Anything related to making things easier for the practitioner label Jan 3, 2023
@kisunji kisunji force-pushed the kisunji/NET-1766-user-friendly-anonymous-token-oss branch from f1839c2 to 1f5f294 Compare January 3, 2023 16:49
@kisunji kisunji requested review from a team, rboyer and pglass and removed request for a team, rboyer and pglass January 4, 2023 15:16
Chris S. Kim added 2 commits January 4, 2023 10:16

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
@kisunji kisunji force-pushed the kisunji/NET-1766-user-friendly-anonymous-token-oss branch from 1f5f294 to c087380 Compare January 4, 2023 15:16
boxofrad and others added 7 commits January 5, 2023 09:51

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
…15564)

Adds automation for generating the map of `gRPC Method Name → Rate Limit Type`
used by the middleware introduced in #15550, and will ensure we don't forget
to add new endpoints.

Engineers must annotate their RPCs in the proto file like so:

```
rpc Foo(FooRequest) returns (FooResponse) {
  option (consul.internal.ratelimit.spec) = {
    operation_type: READ,
  };
}
```

When they run `make proto` a protoc plugin `protoc-gen-consul-rate-limit` will
be installed that writes rate-limit specs as a JSON array to a file called
`.ratelimit.tmp` (one per protobuf package/directory).

After running Buf, `make proto` will execute a post-process script that will
ingest all of the `.ratelimit.tmp` files and generate a Go file containing the
mappings in the `agent/grpc-middleware` package. In the enterprise repository,
it will write an additional file with the enterprise-only endpoints.

If an engineer forgets to add the annotation to a new RPC, the plugin will
return an error like so:

```
RPC Foo is missing rate-limit specification, fix it with:

	import "proto-public/annotations/ratelimit/ratelimit.proto";

	service Bar {
	  rpc Foo(...) returns (...) {
	    option (hashicorp.consul.internal.ratelimit.spec) = {
	      operation_type: OPERATION_READ | OPERATION_WRITE | OPERATION_EXEMPT,
	    };
	  }
	}
```

In the future, this annotation can be extended to support rate-limit
category (e.g. KV vs Catalog) and to determine the retry policy.
Add support to provide an initial token via the bootstrap HTTP API, similar to hashicorp/nomad#12520
#15885)

* Refactoring the peering integ test to accommodate coming changes of other upgrade scenarios.

- Add a utils package under test that contains methods to set up various test scenarios.
- Deduplication: have a single CreatingPeeringClusterAndSetup replace
  CreatingAcceptingClusterAndSetup and CreateDialingClusterAndSetup.
- Separate peering cluster creation and server registration.

* Apply suggestions from code review

Co-authored-by: Dan Stough <dan.stough@hashicorp.com>
* Update api gateway version to latest

* Update website/content/docs/api-gateway/install.mdx

* update to latest apigw version 0.5.1

* update consul and helm version
@kisunji kisunji requested a review from a team as a code owner January 5, 2023 14:54
@vercel
Copy link

vercel bot commented Jan 5, 2023

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Updated
consul 🔄 Building (Inspect) Jan 5, 2023 at 2:55PM (UTC)
consul-ui-staging 🔄 Building (Inspect) Jan 5, 2023 at 2:55PM (UTC)

Copy link
Contributor

@cthain cthain left a comment

Choose a reason for hiding this comment

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

This is looking good Chris. I just had a couple of minor comments. I like that you also did some housekeeping around comments and formatting 👍

acl/acl.go Outdated
// CheckAnonymous returns the string "anonymous token" if
// accessorID is acl.AnonymousTokenID. Used for better
// UX when logging the accessorID.
func CheckAnonymous(accessorID string) string {
Copy link
Contributor

Choose a reason for hiding this comment

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

What do you think about making the name of this func more generic and using a LUT to find the name for the token ID? I could envision that in the future we may want to use this mechanism for other well-known tokens. Something like:

// maybe this could have package scope?
wellKnownTokens := map[string]string{AnonymousTokenID: "anonymous token"}

func ResolveToken(accessorID string) string {
  if name, ok := wellKnownTokens[accessorID]; ok {
    return name
  }
  return accessorID
}

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 agree that this UX improvement would be useful for other well-known tokens but my understanding based on this doc is that the anonymous token is the only static one in our system.

Every other "well-known" token is configured and generated at runtime, making it harder to maintain a lookup table.

Not impossible, but I'd like to consider it out of scope for this PR.

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree with deferring that generalization (YAGNI principle). Once we have >1 such token (if ever), we can make it more general as suggested.

@@ -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"
Copy link
Contributor

Choose a reason for hiding this comment

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

This PR only migrates the anonymous token definition for ACLs.. I don't disagree with this at all but just want to point out that now we have ACL definitions in two places, acl/ and structs/.. Is it worth adding a note in the code indicating that we intend to move the rest of the ACL defs to the acl/ package at some point?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah it was originally an initiative started in core before Mark A left; the intent had always been to migrate things over. I'll add a todo

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As I was looking over the file I realized that there are some import cycles that might make a general migration difficult. Unsure if a general TODO would be helpful (or if it will grow stale and unaddressed). I think just an ad-hoc refactoring over time might be better?

Copy link
Contributor

Choose a reason for hiding this comment

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

Ya, I'm fine with whatever you think is best. I suspect that we will be thinking about and revisiting the ACLs system over the coming months and this is not likely to fall off our radar.

@@ -161,7 +161,7 @@ func TestACLEndpoint_TokenRead(t *testing.T) {

waitForLeaderEstablishment(t, srv)

acl := ACL{srv: srv}
aclEp := ACL{srv: srv}
Copy link
Contributor

Choose a reason for hiding this comment

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

This change creates a lot of noise in this file, but I think this is the right thing to do given that the consul/acl package was already imported and these acl vars where hiding that package in the tests. I really like the disambiguation here.

@jkirschner-hashicorp
Copy link
Contributor

@kisunji : My understanding is that most ACL permission denied error messages are centrally defined here:

consul/acl/errors.go

Lines 85 to 111 in ee2d47d

func (e PermissionDeniedError) Error() string {
var message strings.Builder
message.WriteString(errPermissionDenied)
// Type 4)
if e.Cause != "" {
fmt.Fprintf(&message, ": %s", e.Cause)
return message.String()
}
// Should only be empty when default struct is used.
if e.Resource == "" {
return message.String()
}
if e.Accessor == "" {
message.WriteString(": provided token")
} else {
fmt.Fprintf(&message, ": token with AccessorID '%s'", e.Accessor)
}
fmt.Fprintf(&message, " lacks permission '%s:%s'", e.Resource, e.AccessLevel.String())
if e.ResourceID.Name != "" {
fmt.Fprintf(&message, " on %s", e.ResourceID.ToString())
}
return message.String()
}

I don't see that error message currently modified in this PR.

And when we do modify that error message, we might want to take a different approach than is done here.

Currently, the error message is:

Permission denied: token with AccessorID 00000000-0000-0000-0000-000000000002 lacks permission node:write on node-1

I'm not sure that replacing the accessor ID with "accessor ID" is the clearest way to communicate what's happening in this case (though I agree it's a good solution for structured logging entries like those currently included in this PR). That would look like:

Permission denied: token with AccessorID 'anonymous token' lacks permission node:write on node-1

Perhaps it could instead be something like...

Permission denied: the anonymous token with AccessorID 00000000-0000-0000-0000-000000000002 lacks permission node:write on node-1. The anonymous token is used implicitly when a request does not specify a token.

Or ...

Permission denied: token with AccessorID "anonymous token" lacks permission node:write on node-1. The anonymous token is used implicitly when a request does not specify a token.


... I feel a bit weird about saying that the accessor ID is "anonymous token" when that isn't actually accurate, though I think it's preferable to the alternative in the case of structure logging (leaving as ID ....002). It's not like we want to encourage the lookup and modification of the anonymous token to have broader privileges ... What we want to communicate is: you didn't specify a token, but probably should. And I think making the accessor ID display as "anonymous token" is a reasonable way to accomplish that.

@kisunji kisunji merged commit a7b34d5 into main Jan 9, 2023
@kisunji kisunji deleted the kisunji/NET-1766-user-friendly-anonymous-token-oss branch January 9, 2023 18:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
theme/acls ACL and token generation theme/cli Flags and documentation for the CLI interface theme/operator-usability Replaces UX. Anything related to making things easier for the practitioner
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

9 participants