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

feat(kuma-cp) User Token for API Server authentication #2892

Merged
merged 13 commits into from
Oct 8, 2021

Conversation

jakubdyszkiewicz
Copy link
Contributor

Summary

This PR introduces a new authentication mechanism in Kuma called User Token.
User Token is a JWT token that contains name and group.

Why?
Currently, our authentication mechanism is based on client certificates. We want to ditch it because

  • Managing client certs is painful. You need to pass them as a file to every instance of cp.
  • There is no notion of user. If you own client cert, you are admin
  • It works only with E2E TLS connections. Header-based auth is easier to pass through load balancers / reverse proxies.
  • User Token can be used for out of process integration with internal auth mechanism. For example, some system can authenticate user using whatever mechanism there is in place and then exchange this information for User Token with a short expiration date.

It is very similar to Dataplane Token that we use already, meaning there are similar endpoint and kumactl cli commands to generate a token.

Additionally, this token supports

  • Signing Key rotation
  • Token revocation list
  • Expiration

Those features will be carried to other tokens in a system for consistency.

In this PR, User Token auth is opt-in feature. I'll change it as a default and deprecate the current mechanism.

Implementation

It was implemented as a plugin. Once we try to carry rotation, revocation, and expiration to other tokens we can try to DRY the logic of Token Issuer.

TODO

In the next PR, I want to provide changes to kumactl to support configuring control plane with user token. Right now it's possible through --headers 'authorization=Bearer XYZ'

Documentation

  • I'd write docs after implementing bootstraping strategy of admin.

Testing

  • Unit tests
  • E2E tests
  • Manual testing on Universal
  • Manual testing on Kubernetes

Backwards compatibility

  • We don't want to backport it.

Signed-off-by: Jakub Dyszkiewicz <jakub.dyszkiewicz@gmail.com>
Signed-off-by: Jakub Dyszkiewicz <jakub.dyszkiewicz@gmail.com>
Signed-off-by: Jakub Dyszkiewicz <jakub.dyszkiewicz@gmail.com>
Signed-off-by: Jakub Dyszkiewicz <jakub.dyszkiewicz@gmail.com>
Signed-off-by: Jakub Dyszkiewicz <jakub.dyszkiewicz@gmail.com>
Signed-off-by: Jakub Dyszkiewicz <jakub.dyszkiewicz@gmail.com>
Signed-off-by: Jakub Dyszkiewicz <jakub.dyszkiewicz@gmail.com>
Signed-off-by: Jakub Dyszkiewicz <jakub.dyszkiewicz@gmail.com>
@jakubdyszkiewicz jakubdyszkiewicz requested a review from a team as a code owner October 5, 2021 10:55
@@ -12269,4 +12269,4 @@ spec:
name: prometheus-server
- name: storage-volume
persistentVolumeClaim:
claimName: prometheus-server
claimName: prometheus-server
Copy link
Contributor

Choose a reason for hiding this comment

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

hmm, why the last line disappeared from these 3 files?

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'm not sure. I regenerated everything using UPDATE_GOLDEN_FILES

Copy link
Contributor

Choose a reason for hiding this comment

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

I get the same if I update just on master:

 app/kumactl/cmd/install/testdata/install-gateway-enterprise.defaults.golden.yaml  | 2 +-
 app/kumactl/cmd/install/testdata/install-gateway-enterprise.overrides.golden.yaml | 2 +-
 app/kumactl/cmd/install/testdata/install-gateway.defaults.golden.yaml             | 2 +-
 app/kumactl/cmd/install/testdata/install-gateway.overrides.golden.yaml            | 2 +-
 app/kumactl/cmd/install/testdata/install-metrics.defaults.golden.yaml             | 2 +-
 app/kumactl/cmd/install/testdata/install-metrics.no-grafana.golden.yaml           | 2 +-
 app/kumactl/cmd/install/testdata/install-metrics.no-prometheus.golden.yaml        | 2 +-
 app/kumactl/cmd/install/testdata/install-metrics.overrides.golden.yaml            | 2 +-

Copy link
Contributor

@bartsmykla bartsmykla left a comment

Choose a reason for hiding this comment

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

lgtm

pkg/plugins/authn/api-server/tokens/ws/client/client.go Outdated Show resolved Hide resolved
Copy link
Contributor

@jpeach jpeach left a comment

Choose a reason for hiding this comment

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

I ran out of time on this review, planning to finish up tomorrow.

pkg/plugins/authn/api-server/tokens/ws/client/client.go Outdated Show resolved Hide resolved
pkg/plugins/authn/api-server/tokens/ws/client/client.go Outdated Show resolved Hide resolved
DefaultSerialNumber = 1
)

var SigningKeyNotFound = errors.New("there is no Signing Key in the Control Plane.")
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit, no full-stop in the error string

app/kumactl/cmd/generate/generate_signing_key.go Outdated Show resolved Hide resolved
app/kumactl/cmd/generate/context/context.go Show resolved Hide resolved
app/kumactl/cmd/generate/generate_signing_key.go Outdated Show resolved Hide resolved
cmd.Flags().StringVar(&args.name, "name", "", "name of the user")
_ = cmd.MarkFlagRequired("name")
cmd.Flags().StringVar(&args.group, "group", "", "group of the user")
cmd.Flags().DurationVar(&args.validFor, "valid-for", 0, `how long the token will be valid (for example "24h"). If 0, then token has no expiration time`)
Copy link
Contributor

Choose a reason for hiding this comment

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

"never" seems like a really long default ... IMHO better to make a short default so people have to either rotate or make a decision to make it long

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 think short implicit default is just asking for confusion. If we don't want to do infinity as default (which I get that it is not ideal), we should make this argument required and let the user decide always. WDYT?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think short implicit default is just asking for confusion. If we don't want to do infinity as default (which I get that it is not ideal), we should make this argument required and let the user decide always. WDYT?

Yeh, I can live with making it required. Let's also take an issue to support "infinity" to me an forever, rather than overloading "0" for that.

@jpeach
Copy link
Contributor

jpeach commented Oct 7, 2021

Something's up with codecov here?

Signed-off-by: Jakub Dyszkiewicz <jakub.dyszkiewicz@gmail.com>
Signed-off-by: Jakub Dyszkiewicz <jakub.dyszkiewicz@gmail.com>
@codecov-commenter
Copy link

Codecov Report

Merging #2892 (0cdf00b) into master (08f5c6e) will increase coverage by 0.10%.
The diff coverage is 58.95%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #2892      +/-   ##
==========================================
+ Coverage   52.32%   52.43%   +0.10%     
==========================================
  Files         900      912      +12     
  Lines       52296    52688     +392     
==========================================
+ Hits        27362    27625     +263     
- Misses      22756    22857     +101     
- Partials     2178     2206      +28     
Impacted Files Coverage Δ
app/kumactl/pkg/cmd/root_context.go 63.91% <ø> (+4.12%) ⬆️
...g/plugins/authn/api-server/tokens/authenticator.go 0.00% <0.00%> (ø)
test/e2e/auth/auth_universal.go 0.00% <0.00%> (ø)
test/framework/interface.go 0.00% <ø> (ø)
test/framework/k8s_controlplane.go 0.00% <0.00%> (ø)
test/framework/kumactl.go 0.00% <0.00%> (ø)
test/framework/universal_cluster.go 0.00% <0.00%> (ø)
test/framework/universal_controlplane.go 0.00% <0.00%> (ø)
pkg/core/rbac/static_role.go 87.50% <50.00%> (-12.50%) ⬇️
...ns/authn/api-server/tokens/ws/server/webservice.go 65.85% <65.85%> (ø)
... and 23 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 08f5c6e...0cdf00b. Read the comment docs.

app/kumactl/cmd/generate/generate_signing_key.go Outdated Show resolved Hide resolved
pkg/plugins/authn/api-server/tokens/cli/cli_suite_test.go Outdated Show resolved Hide resolved
pkg/plugins/authn/api-server/tokens/issuer/signing_key.go Outdated Show resolved Hide resolved
pkg/tokens/builtin/issuer/signing_key.go Outdated Show resolved Hide resolved
test/e2e/auth/auth_universal.go Outdated Show resolved Hide resolved
test/framework/kumactl.go Show resolved Hide resolved
test/framework/universal_cluster.go Outdated Show resolved Hide resolved
test/framework/universal_cluster.go Outdated Show resolved Hide resolved
ID: core.NewUUID(),
IssuedAt: jwt.NewNumericDate(now),
NotBefore: jwt.NewNumericDate(now.Add(time.Minute * -5)), // todo(jakubdyszkiewicz) parametrize via config and go through all clock skews in the project
},
Copy link
Contributor

Choose a reason for hiding this comment

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

Does it make sense to set the iss and aud claims here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

what would you put in both iss and aud in this case?

I was considering putting name into aud but then what about groups? should this be in payload as a separate field like we do right now?
What about iss? Should this be just Kuma User Token? Or should this contain username of user that issued the token?

Copy link
Contributor

Choose a reason for hiding this comment

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

The issuer would be the control plane that minted the token and the audience would be any control plane for that cluster. Larger question is whether those are actually concrete concepts that are useful here :)


if validFor != 0 {
c.RegisteredClaims.ExpiresAt = jwt.NewNumericDate(now.Add(validFor))
}
Copy link
Contributor

Choose a reason for hiding this comment

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

WDYT about a tracking issue for config to set default and maximum ticket lifetimes?

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 changed this to be required always and 0 do not mean infinite

Signed-off-by: Jakub Dyszkiewicz <jakub.dyszkiewicz@gmail.com>
Signed-off-by: Jakub Dyszkiewicz <jakub.dyszkiewicz@gmail.com>
Signed-off-by: Jakub Dyszkiewicz <jakub.dyszkiewicz@gmail.com>
@jakubdyszkiewicz jakubdyszkiewicz merged commit d373354 into master Oct 8, 2021
@jakubdyszkiewicz jakubdyszkiewicz deleted the user-token branch October 8, 2021 13:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants