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): use zone token to auth zone ingress #5103

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
8 changes: 8 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ Make sure your certificates contain a valid CN or SANs.
This component has been removed
after [a long period of deprecation](https://github.com/kumahq/kuma/issues/2851).

### Zone Ingress Token migration

This is only relevant to Multizone deployment with Universal zones.
Zone Token that was previously used for authenticating Zone Egress, can now be used to authenticate Zone Ingress.
Please regenerate Zone Ingress token using `kumactl generate zone-token --scope=ingress`.
For the time being you can still use the old Zone Ingress token and Zone Token with scope ingress.
However, Zone Ingress Token is now deprecated and will be removed in the future.

### Helm

`ingress.annotations` and `egress.annotations` are deprecated in favour of `ingress.podAnnotations` and `egress.podAnnotations` which is a better name and aligne with the existing `controlPlane.podAnnoations`.
Expand Down
15 changes: 7 additions & 8 deletions app/kumactl/cmd/generate/generate_zone_token.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package generate

import (
"fmt"
"time"

"github.com/pkg/errors"
Expand Down Expand Up @@ -29,12 +30,12 @@ func NewGenerateZoneTokenCmd(pctx *kumactl_cmd.RootContext) *cobra.Command {
cmd := &cobra.Command{
Use: "zone-token",
Short: "Generate Zone Token",
// TODO (bartsmykla): update descriptions when this token will be able to
// be used to prove identities of zone dataplanes and ingresses as well
Long: `Generate Zone Token that is used to prove identity of Zone egresses.`,
Long: `Generate Zone Token that is used to prove identity of zone components (Zone Ingress, Zone Egress).`,
Example: `Generate token bound by zone
$ kumactl generate zone-token --zone zone-1 --valid-for 24h
$ kumactl generate zone-token --zone zone-1 --valid-for 24h --scope egress`,
$ kumactl generate zone-token --zone zone-1 --valid-for 24h --scope egress
$ kumactl generate zone-token --zone zone-1 --valid-for 24h --scope ingress
$ kumactl generate zone-token --zone zone-1 --valid-for 24h --scope ingress --scope egress`,
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, _ []string) error {
if err := validateArgs(ctx.args); err != nil {
Expand All @@ -58,8 +59,7 @@ $ kumactl generate zone-token --zone zone-1 --valid-for 24h --scope egress`,
}

cmd.Flags().StringVar(&ctx.args.zone, "zone", "", "name of the zone where resides")
// TODO (bartsmykla): update when Zone Token will be available for dataplanes and ingresses
cmd.Flags().StringSliceVar(&ctx.args.scope, "scope", zone.FullScope, "scope of resources which the token will be able to identify (can be 'egress')")
cmd.Flags().StringSliceVar(&ctx.args.scope, "scope", zone.FullScope, fmt.Sprintf("scope of resources which the token will be able to identify (can be: %v)", zone.FullScope))
cmd.Flags().DurationVar(&ctx.args.validFor, "valid-for", 0, `how long the token will be valid (for example "24h")`)

_ = cmd.MarkFlagRequired("valid-for")
Expand All @@ -71,8 +71,7 @@ func validateArgs(args *generateZoneTokenContextArgs) error {
var unsupportedScopes []string

for _, s := range args.scope {
// TODO (bartsmykla): update when Zone Token will be available for dataplanes and ingresses
if s != zone.EgressScope {
if !zone.InScope(zone.FullScope, s) {
unsupportedScopes = append(unsupportedScopes, s)
}
}
Expand Down
5 changes: 4 additions & 1 deletion app/kumactl/cmd/generate/generate_zoneingress_token.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ func NewGenerateZoneIngressTokenCmd(pctx *kumactl_cmd.RootContext) *cobra.Comman
cmd := &cobra.Command{
Use: "zone-ingress-token",
Short: "Generate Zone Ingress Token",
Long: `Generate Zone Ingress Token that is used to prove Zone Ingress identity.`,
Long: `Generate Zone Ingress Token that is used to prove Zone Ingress identity.

DEPRECATED: Use kumactl generate zone-token --scope=ingress instead.
jakubdyszkiewicz marked this conversation as resolved.
Show resolved Hide resolved
`,
Example: `
Generate token bound by zone
$ kumactl generate zone-ingress-token --zone zone-1 --valid-for 30d
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ Generate Zone Ingress Token

Generate Zone Ingress Token that is used to prove Zone Ingress identity.

DEPRECATED: Use kumactl generate zone-token --scope=ingress instead.


```
kumactl generate zone-ingress-token [flags]
```
Expand Down
6 changes: 4 additions & 2 deletions docs/generated/cmd/kumactl/kumactl_generate_zone-token.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Generate Zone Token

### Synopsis

Generate Zone Token that is used to prove identity of Zone egresses.
Generate Zone Token that is used to prove identity of zone components (Zone Ingress, Zone Egress).

```
kumactl generate zone-token [flags]
Expand All @@ -16,13 +16,15 @@ kumactl generate zone-token [flags]
Generate token bound by zone
$ kumactl generate zone-token --zone zone-1 --valid-for 24h
$ kumactl generate zone-token --zone zone-1 --valid-for 24h --scope egress
$ kumactl generate zone-token --zone zone-1 --valid-for 24h --scope ingress
$ kumactl generate zone-token --zone zone-1 --valid-for 24h --scope ingress --scope egress
```

### Options

```
-h, --help help for zone-token
--scope strings scope of resources which the token will be able to identify (can be 'egress') (default [egress])
--scope strings scope of resources which the token will be able to identify (can be: [ingress egress]) (default [ingress,egress])
--valid-for duration how long the token will be valid (for example "24h")
--zone string name of the zone where resides
```
Expand Down
8 changes: 5 additions & 3 deletions pkg/core/xds/rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,10 +227,12 @@ func (c *SubsetIter) next() bool {
}

// simplified returns copy of c.current and deletes redundant tags, for example:
// * env: dev
// * env: !prod
// - env: dev
// - env: !prod
//
// could be simplified to:
// * env: dev
// - env: dev
//
// If tags are contradicted (same keys have different positive value) then the function
// returns nil.
func (c *SubsetIter) simplified() Subset {
Expand Down
12 changes: 3 additions & 9 deletions pkg/tokens/builtin/zone/scope.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
package zone

const (
// TODO (bartsmykla): uncomment when Zone Token will be available for dataplanes
// and ingresses
// DataplaneScope string = "dataplane
// IngressScope string = "ingress"
EgressScope string = "egress"
IngressScope string = "ingress"
EgressScope string = "egress"
)

var FullScope = []string{
// TODO (bartsmykla): uncomment when Zone Token will be available for dataplanes
// and ingresses
// DataplaneScope,
// IngressScope,
IngressScope,
EgressScope,
}

Expand Down
50 changes: 50 additions & 0 deletions pkg/tokens/builtin/zoneingress/zone_validator_adapter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package zoneingress

import (
"context"

"github.com/golang-jwt/jwt/v4"

"github.com/kumahq/kuma/pkg/tokens/builtin/zone"
)

type zoneValidatorAdapter struct {
zoneIngressValidator Validator
zoneValidator zone.Validator
}

var _ zone.Validator = &zoneValidatorAdapter{}

// NewZoneValidatorAdapter returns Zone Token Validator that has a fallback on Zone Ingress Validator
// This is used for backwards compatibility to still support old ingress token.
// This should be deleted if we delete zone ingress token.
func NewZoneValidatorAdapter(zoneIngressValidator Validator, zoneValidator zone.Validator) zone.Validator {
return &zoneValidatorAdapter{
zoneIngressValidator: zoneIngressValidator,
zoneValidator: zoneValidator,
}
}

func (z *zoneValidatorAdapter) Validate(ctx context.Context, token zone.Token) (zone.Identity, error) {
if isZoneToken(token) {
return z.zoneValidator.Validate(ctx, token)
}
id, err := z.zoneIngressValidator.Validate(ctx, token)
if err != nil {
return zone.Identity{}, err
}
return zone.Identity{
Zone: id.Zone,
Scope: []string{zone.IngressScope},
}, nil
}

func isZoneToken(token zone.Token) bool {
parser := jwt.Parser{}
claims := zone.ZoneClaims{}
_, _, err := parser.ParseUnverified(token, &claims)
if err != nil {
return false
}
return len(claims.Scope) > 0 // zone token has to contain Scope
}
4 changes: 3 additions & 1 deletion pkg/xds/auth/components/components.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
core_manager "github.com/kumahq/kuma/pkg/core/resources/manager"
k8s_extensions "github.com/kumahq/kuma/pkg/plugins/extensions/k8s"
"github.com/kumahq/kuma/pkg/tokens/builtin"
"github.com/kumahq/kuma/pkg/tokens/builtin/zoneingress"
"github.com/kumahq/kuma/pkg/xds/auth"
k8s_auth "github.com/kumahq/kuma/pkg/xds/auth/k8s"
universal_auth "github.com/kumahq/kuma/pkg/xds/auth/universal"
Expand All @@ -35,8 +36,9 @@ func NewUniversalAuthenticator(rt Context) (auth.Authenticator, error) {
dataplaneValidator := builtin.NewDataplaneTokenValidator(rt.ReadOnlyResourceManager(), config.Store.Type)
zoneIngressValidator := builtin.NewZoneIngressTokenValidator(rt.ReadOnlyResourceManager(), config.Store.Type)
zoneTokenValidator := builtin.NewZoneTokenValidator(rt.ReadOnlyResourceManager(), config.Mode, config.Store.Type)
adaptedValidator := zoneingress.NewZoneValidatorAdapter(zoneIngressValidator, zoneTokenValidator)

return universal_auth.NewAuthenticator(dataplaneValidator, zoneIngressValidator, zoneTokenValidator, config.Multizone.Zone.Name), nil
return universal_auth.NewAuthenticator(dataplaneValidator, adaptedValidator, config.Multizone.Zone.Name), nil
}

func DefaultAuthenticator(rt Context) (auth.Authenticator, error) {
Expand Down
78 changes: 71 additions & 7 deletions pkg/xds/auth/universal/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@ import (
test_model "github.com/kumahq/kuma/pkg/test/resources/model"
"github.com/kumahq/kuma/pkg/tokens/builtin"
builtin_issuer "github.com/kumahq/kuma/pkg/tokens/builtin/issuer"
"github.com/kumahq/kuma/pkg/tokens/builtin/zone"
"github.com/kumahq/kuma/pkg/tokens/builtin/zoneingress"
"github.com/kumahq/kuma/pkg/xds/auth"
"github.com/kumahq/kuma/pkg/xds/auth/universal"
)

var _ = Describe("Authentication flow", func() {
var issuer builtin_issuer.DataplaneTokenIssuer
var dpTokenIssuer builtin_issuer.DataplaneTokenIssuer
var authenticator auth.Authenticator
var resStore core_store.ResourceStore
var resManager manager.ResourceManager
Expand Down Expand Up @@ -70,11 +72,12 @@ var _ = Describe("Authentication flow", func() {
Expect(resManager.Create(context.Background(), core_mesh.NewMeshResource(), core_store.CreateByKey("demo", model.NoMesh))).To(Succeed())
Expect(resManager.Create(context.Background(), core_mesh.NewMeshResource(), core_store.CreateByKey("demo-2", model.NoMesh))).To(Succeed())

dpTokenIssuer = builtin.NewDataplaneTokenIssuer(resManager)
dataplaneValidator := builtin.NewDataplaneTokenValidator(resManager, store_config.MemoryStore)
zoneIngressValidator := builtin.NewZoneIngressTokenValidator(resManager, store_config.MemoryStore)
zoneTokenValidator := builtin.NewZoneTokenValidator(resManager, config_core.Global, store_config.MemoryStore)
issuer = builtin.NewDataplaneTokenIssuer(resManager)
authenticator = universal.NewAuthenticator(dataplaneValidator, zoneIngressValidator, zoneTokenValidator, "zone-1")
adaptedValidator := zoneingress.NewZoneValidatorAdapter(zoneIngressValidator, zoneTokenValidator)
authenticator = universal.NewAuthenticator(dataplaneValidator, adaptedValidator, "zone-1")

signingKeyManager := tokens.NewMeshedSigningKeyManager(resManager, builtin_issuer.DataplaneTokenSigningKeyPrefix("default"), "default")
Expect(signingKeyManager.CreateDefaultSigningKey(ctx)).To(Succeed())
Expand All @@ -93,7 +96,7 @@ var _ = Describe("Authentication flow", func() {
DescribeTable("should correctly authenticate dataplane",
func(given testCase) {
// when
credential, err := issuer.Generate(ctx, given.id, 24*time.Hour)
credential, err := dpTokenIssuer.Generate(ctx, given.id, 24*time.Hour)

// then
Expect(err).ToNot(HaveOccurred())
Expand Down Expand Up @@ -134,7 +137,7 @@ var _ = Describe("Authentication flow", func() {
DescribeTable("should fail auth",
func(given testCase) {
// when
token, err := issuer.Generate(ctx, given.id, 24*time.Hour)
token, err := dpTokenIssuer.Generate(ctx, given.id, 24*time.Hour)

// then
Expect(err).ToNot(HaveOccurred())
Expand Down Expand Up @@ -212,7 +215,7 @@ var _ = Describe("Authentication flow", func() {

It("should throw an error when signing key used for validation is different than for generation", func() {
// given
credential, err := issuer.Generate(ctx, builtin_issuer.DataplaneIdentity{
credential, err := dpTokenIssuer.Generate(ctx, builtin_issuer.DataplaneIdentity{
Name: "dp-1",
Mesh: "default",
}, 24*time.Hour)
Expand All @@ -233,11 +236,72 @@ var _ = Describe("Authentication flow", func() {

It("should throw an error when signing key is not found", func() {
// when
_, err := issuer.Generate(ctx, builtin_issuer.DataplaneIdentity{
_, err := dpTokenIssuer.Generate(ctx, builtin_issuer.DataplaneIdentity{
Mesh: "demo-2",
}, 24*time.Hour)

// then
Expect(err.Error()).To(ContainSubstring(`there is no signing key`))
})

Context("Zone Ingress", func() {
ziRes := core_mesh.ZoneIngressResource{
Meta: &test_model.ResourceMeta{
Mesh: "zi-1",
},
Spec: &mesh_proto.ZoneIngress{
Networking: &mesh_proto.ZoneIngress_Networking{
Address: "127.0.0.1",
},
},
}

var zoneTokenIssuer zone.TokenIssuer
var zoneIngressTokenIssuer zoneingress.TokenIssuer

BeforeEach(func() {
err := resStore.Create(context.Background(), &ziRes, core_store.CreateByKey("zi-1", model.NoMesh))
Expect(err).ToNot(HaveOccurred())

zoneKeyManager := tokens.NewSigningKeyManager(resManager, zone.SigningKeyPrefix)
Expect(zoneKeyManager.CreateDefaultSigningKey(ctx)).To(Succeed())
zoneTokenIssuer = builtin.NewZoneTokenIssuer(resManager)

ziKeyManager := tokens.NewSigningKeyManager(resManager, zoneingress.ZoneIngressSigningKeyPrefix)
Expect(ziKeyManager.CreateDefaultSigningKey(ctx)).To(Succeed())
zoneIngressTokenIssuer = builtin.NewZoneIngressTokenIssuer(resManager)
})

It("should authenticate zone ingress with zone token", func() {
// given
identity := zone.Identity{
Zone: "zone-1",
Scope: []string{zone.IngressScope},
}
token, err := zoneTokenIssuer.Generate(ctx, identity, time.Hour)
Expect(err).ToNot(HaveOccurred())

// when
err = authenticator.Authenticate(ctx, &ziRes, token)

// then
Expect(err).ToNot(HaveOccurred())
})

It("should authenticate zone ingress with zoneingress token", func() { // backwards compatibility
// given
identity := zoneingress.Identity{
Zone: "zone-1",
}
token, err := zoneIngressTokenIssuer.Generate(ctx, identity, time.Hour)
Expect(err).ToNot(HaveOccurred())

// when
err = authenticator.Authenticate(ctx, &ziRes, token)

// then
Expect(err).ToNot(HaveOccurred())
})
})

})
Loading