Skip to content

Commit

Permalink
feat(kuma-cp) prefix system users and groups with mesh-system (#3013)
Browse files Browse the repository at this point in the history
Signed-off-by: Jakub Dyszkiewicz <jakub.dyszkiewicz@gmail.com>
  • Loading branch information
jakubdyszkiewicz authored Oct 27, 2021
1 parent feeb8ec commit 1194575
Show file tree
Hide file tree
Showing 20 changed files with 75 additions and 74 deletions.
4 changes: 2 additions & 2 deletions pkg/api-server/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ var _ = Describe("Auth test", func() {
body, err := ioutil.ReadAll(resp.Body)
Expect(err).ToNot(HaveOccurred())
Expect(resp.Body.Close()).To(Succeed())
Expect(string(body)).To(MatchJSON(`{"title": "Access Denied", "details": "user did not authenticate"}`))
Expect(string(body)).To(MatchJSON(`{"title": "Access Denied", "details": "user \"mesh-system:anonymous/mesh-system:unauthenticated\" cannot access the resource of type \"Secret\""}`))
})

It("should be block an access to admin endpoints from other machine using HTTPS without proper client certs", func() {
Expand All @@ -126,7 +126,7 @@ var _ = Describe("Auth test", func() {
body, err := ioutil.ReadAll(resp.Body)
Expect(err).ToNot(HaveOccurred())
Expect(resp.Body.Close()).To(Succeed())
Expect(string(body)).To(MatchJSON(`{"title": "Access Denied", "details": "user did not authenticate"}`))
Expect(string(body)).To(MatchJSON(`{"title": "Access Denied", "details": "user \"mesh-system:anonymous/mesh-system:unauthenticated\" cannot access the resource of type \"Secret\""}`))
})
})

Expand Down
2 changes: 1 addition & 1 deletion pkg/api-server/authn/localhost.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func LocalhostAuthenticator(request *restful.Request, response *restful.Response
}
if host == "127.0.0.1" || host == "::1" {
log.V(1).Info("authenticated as admin because requests originates from the same machine")
request.Request = request.Request.WithContext(user.Ctx(request.Request.Context(), user.Admin))
request.Request = request.Request.WithContext(user.Ctx(request.Request.Context(), user.Admin.Authenticated()))
}
chain.ProcessFilter(request, response)
}
12 changes: 6 additions & 6 deletions pkg/api-server/config_ws_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -323,16 +323,16 @@ var _ = Describe("Config WS", func() {
"type": "static",
"static": {
"adminResources": {
"users": ["admin"],
"groups": ["admin"]
"users": ["mesh-system:admin"],
"groups": ["mesh-system:admin"]
},
"generateDpToken": {
"users": ["admin"],
"groups": ["admin"]
"users": ["mesh-system:admin"],
"groups": ["mesh-system:admin"]
},
"generateUserToken": {
"users": ["admin"],
"groups": ["admin"]
"users": ["mesh-system:admin"],
"groups": ["mesh-system:admin"]
}
}
}
Expand Down
12 changes: 6 additions & 6 deletions pkg/config/app/kuma-cp/kuma-cp.defaults.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -400,18 +400,18 @@ rbac:
# AdminResources defines an access to admin resources (Secret/GlobalSecret)
adminResources:
# List of users that are allowed to access admin resources
users: ["admin"] # ENV: KUMA_RBAC_STATIC_ADMIN_RESOURCES_USERS
users: ["mesh-system:admin"] # ENV: KUMA_RBAC_STATIC_ADMIN_RESOURCES_USERS
# List of groups that are allowed to access admin resources
groups: ["admin"] # ENV: KUMA_RBAC_STATIC_ADMIN_RESOURCES_GROUPS
groups: ["mesh-system:admin"] # ENV: KUMA_RBAC_STATIC_ADMIN_RESOURCES_GROUPS
# GenerateDPToken defines an access to generating dataplane token
generateDpToken:
# List of users that are allowed to generate dataplane token
users: ["admin"] # ENV: KUMA_RBAC_STATIC_GENERATE_DP_TOKEN_USERS
users: ["mesh-system:admin"] # ENV: KUMA_RBAC_STATIC_GENERATE_DP_TOKEN_USERS
# List of groups that are allowed to generate dataplane token
groups: ["admin"] # ENV: KUMA_RBAC_STATIC_GENERATE_DP_TOKEN_GROUPS
groups: ["mesh-system:admin"] # ENV: KUMA_RBAC_STATIC_GENERATE_DP_TOKEN_GROUPS
# GenerateDPToken defines an access to generating user token
generateUserToken:
# List of users that are allowed to generate user token
users: ["admin"] # ENV: KUMA_RBAC_STATIC_GENERATE_USER_TOKEN_USERS
users: ["mesh-system:admin"] # ENV: KUMA_RBAC_STATIC_GENERATE_USER_TOKEN_USERS
# List of groups that are allowed to generate user token
groups: ["admin"] # ENV: KUMA_RBAC_STATIC_GENERATE_USER_TOKEN_GROUPS
groups: ["mesh-system:admin"] # ENV: KUMA_RBAC_STATIC_GENERATE_USER_TOKEN_GROUPS
12 changes: 6 additions & 6 deletions pkg/config/rbac/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@ func DefaultRBACConfig() RBACConfig {
Type: StaticType,
Static: RBACStaticConfig{
AdminResources: AdminResourcesRBACStaticConfig{
Users: []string{"admin"},
Groups: []string{"admin"},
Users: []string{"mesh-system:admin"},
Groups: []string{"mesh-system:admin"},
},
GenerateDPToken: GenerateDPTokenRBACStaticConfig{
Users: []string{"admin"},
Groups: []string{"admin"},
Users: []string{"mesh-system:admin"},
Groups: []string{"mesh-system:admin"},
},
GenerateUserToken: GenerateUserTokenRBACStaticConfig{
Users: []string{"admin"},
Groups: []string{"admin"},
Users: []string{"mesh-system:admin"},
Groups: []string{"mesh-system:admin"},
},
},
}
Expand Down
17 changes: 6 additions & 11 deletions pkg/core/resources/rbac/admin_resource_access.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,35 +30,30 @@ func NewAdminResourceAccess(cfg config_rbac.AdminResourcesRBACStaticConfig) Reso

var _ ResourceAccess = &adminResourceAccess{}

func (a *adminResourceAccess) ValidateCreate(key model.ResourceKey, spec model.ResourceSpec, descriptor model.ResourceTypeDescriptor, user *user.User) error {
func (a *adminResourceAccess) ValidateCreate(key model.ResourceKey, spec model.ResourceSpec, descriptor model.ResourceTypeDescriptor, user user.User) error {
return a.validateAdminAccess(user, descriptor)
}

func (a *adminResourceAccess) ValidateUpdate(key model.ResourceKey, spec model.ResourceSpec, descriptor model.ResourceTypeDescriptor, user *user.User) error {
func (a *adminResourceAccess) ValidateUpdate(key model.ResourceKey, spec model.ResourceSpec, descriptor model.ResourceTypeDescriptor, user user.User) error {
return a.validateAdminAccess(user, descriptor)
}

func (a *adminResourceAccess) ValidateDelete(key model.ResourceKey, spec model.ResourceSpec, descriptor model.ResourceTypeDescriptor, user *user.User) error {
func (a *adminResourceAccess) ValidateDelete(key model.ResourceKey, spec model.ResourceSpec, descriptor model.ResourceTypeDescriptor, user user.User) error {
return a.validateAdminAccess(user, descriptor)
}

func (a *adminResourceAccess) ValidateList(descriptor model.ResourceTypeDescriptor, user *user.User) error {
func (a *adminResourceAccess) ValidateList(descriptor model.ResourceTypeDescriptor, user user.User) error {
return a.validateAdminAccess(user, descriptor)
}

func (a *adminResourceAccess) ValidateGet(key model.ResourceKey, descriptor model.ResourceTypeDescriptor, user *user.User) error {
func (a *adminResourceAccess) ValidateGet(key model.ResourceKey, descriptor model.ResourceTypeDescriptor, user user.User) error {
return a.validateAdminAccess(user, descriptor)
}

func (r *adminResourceAccess) validateAdminAccess(u *user.User, descriptor model.ResourceTypeDescriptor) error {
func (r *adminResourceAccess) validateAdminAccess(u user.User, descriptor model.ResourceTypeDescriptor) error {
if !descriptor.AdminOnly {
return nil
}
if u == nil {
return &rbac.AccessDeniedError{
Reason: "user did not authenticate",
}
}
allowed := r.usernames[u.Name]
for _, group := range u.Groups {
if r.groups[group] {
Expand Down
24 changes: 12 additions & 12 deletions pkg/core/resources/rbac/admin_resource_access_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ import (

var _ = Describe("Admin Resource Access", func() {
resourceAccess := resources_rbac.NewAdminResourceAccess(config_rbac.AdminResourcesRBACStaticConfig{
Users: []string{"admin"},
Users: []string{user.Admin.Name},
})

It("should allow regular user to access non admin resource", func() {
err := resourceAccess.ValidateCreate(
model.ResourceKey{Name: "xyz", Mesh: "demo"},
&mesh_proto.CircuitBreaker{},
mesh.NewCircuitBreakerResource().Descriptor(),
nil,
user.Anonymous,
)

// then
Expand All @@ -37,7 +37,7 @@ var _ = Describe("Admin Resource Access", func() {
model.ResourceKey{Name: "xyz"},
&system_proto.Secret{},
system.NewSecretResource().Descriptor(),
&user.Admin,
user.Admin,
)

// then
Expand All @@ -50,7 +50,7 @@ var _ = Describe("Admin Resource Access", func() {
model.ResourceKey{Name: "xyz"},
&system_proto.Secret{},
system.NewSecretResource().Descriptor(),
&user.User{Name: "john doe", Groups: []string{"users"}},
user.User{Name: "john doe", Groups: []string{"users"}},
)

// then
Expand All @@ -63,11 +63,11 @@ var _ = Describe("Admin Resource Access", func() {
model.ResourceKey{Name: "xyz"},
&system_proto.Secret{},
system.NewSecretResource().Descriptor(),
nil,
user.Anonymous,
)

// then
Expect(err).To(MatchError(`access denied: user did not authenticate`))
Expect(err).To(MatchError(`access denied: user "mesh-system:anonymous/mesh-system:unauthenticated" cannot access the resource of type "Secret"`))
})

It("should allow admin to access Update", func() {
Expand All @@ -76,7 +76,7 @@ var _ = Describe("Admin Resource Access", func() {
model.ResourceKey{Name: "xyz"},
&system_proto.Secret{},
system.NewSecretResource().Descriptor(),
&user.Admin,
user.Admin,
)

// then
Expand All @@ -89,7 +89,7 @@ var _ = Describe("Admin Resource Access", func() {
model.ResourceKey{Name: "xyz"},
&system_proto.Secret{},
system.NewSecretResource().Descriptor(),
&user.User{Name: "john doe", Groups: []string{"users"}},
user.User{Name: "john doe", Groups: []string{"users"}},
)

// then
Expand All @@ -101,7 +101,7 @@ var _ = Describe("Admin Resource Access", func() {
err := resourceAccess.ValidateGet(
model.ResourceKey{Name: "xyz"},
system.NewSecretResource().Descriptor(),
&user.Admin,
user.Admin,
)

// then
Expand All @@ -113,7 +113,7 @@ var _ = Describe("Admin Resource Access", func() {
err := resourceAccess.ValidateGet(
model.ResourceKey{Name: "xyz"},
system.NewSecretResource().Descriptor(),
&user.User{Name: "john doe", Groups: []string{"users"}},
user.User{Name: "john doe", Groups: []string{"users"}},
)

// then
Expand All @@ -124,7 +124,7 @@ var _ = Describe("Admin Resource Access", func() {
// when
err := resourceAccess.ValidateList(
system.NewSecretResource().Descriptor(),
&user.Admin,
user.Admin,
)

// then
Expand All @@ -135,7 +135,7 @@ var _ = Describe("Admin Resource Access", func() {
// when
err := resourceAccess.ValidateList(
system.NewSecretResource().Descriptor(),
&user.User{Name: "john doe", Groups: []string{"users"}},
user.User{Name: "john doe", Groups: []string{"users"}},
)

// then
Expand Down
10 changes: 5 additions & 5 deletions pkg/core/resources/rbac/resource_access.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import (
)

type ResourceAccess interface {
ValidateCreate(key model.ResourceKey, spec model.ResourceSpec, desc model.ResourceTypeDescriptor, user *user.User) error
ValidateUpdate(key model.ResourceKey, spec model.ResourceSpec, desc model.ResourceTypeDescriptor, user *user.User) error
ValidateDelete(key model.ResourceKey, spec model.ResourceSpec, desc model.ResourceTypeDescriptor, user *user.User) error
ValidateList(desc model.ResourceTypeDescriptor, user *user.User) error
ValidateGet(key model.ResourceKey, desc model.ResourceTypeDescriptor, user *user.User) error
ValidateCreate(key model.ResourceKey, spec model.ResourceSpec, desc model.ResourceTypeDescriptor, user user.User) error
ValidateUpdate(key model.ResourceKey, spec model.ResourceSpec, desc model.ResourceTypeDescriptor, user user.User) error
ValidateDelete(key model.ResourceKey, spec model.ResourceSpec, desc model.ResourceTypeDescriptor, user user.User) error
ValidateList(desc model.ResourceTypeDescriptor, user user.User) error
ValidateGet(key model.ResourceKey, desc model.ResourceTypeDescriptor, user user.User) error
}
8 changes: 4 additions & 4 deletions pkg/core/user/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import "context"
type userCtx struct{}

func Ctx(ctx context.Context, user User) context.Context {
return context.WithValue(ctx, userCtx{}, &user)
return context.WithValue(ctx, userCtx{}, user)
}

func FromCtx(ctx context.Context) *User {
if value, ok := ctx.Value(userCtx{}).(*User); ok {
func FromCtx(ctx context.Context) User {
if value, ok := ctx.Value(userCtx{}).(User); ok {
return value
}
return nil
return Anonymous
}
16 changes: 14 additions & 2 deletions pkg/core/user/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package user

import "strings"

const AuthenticatedGroup = "mesh-system:authenticated"

type User struct {
Name string
Groups []string
Expand All @@ -11,9 +13,19 @@ func (u User) String() string {
return u.Name + "/" + strings.Join(u.Groups, ",")
}

func (u User) Authenticated() User {
u.Groups = append(u.Groups, AuthenticatedGroup)
return u
}

// Admin is a static user that can be used when authn mechanism does not authenticate to specific user,
// but authenticate to admin without giving credential (ex. authenticate as localhost, authenticate via legacy client certs).
var Admin = User{
Name: "admin",
Groups: []string{"admin"},
Name: "mesh-system:admin",
Groups: []string{"mesh-system:admin"},
}

var Anonymous = User{
Name: "mesh-system:anonymous",
Groups: []string{"mesh-system:unauthenticated"},
}
4 changes: 2 additions & 2 deletions pkg/plugins/authn/api-server/certs/authenticator.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import (

// backwards compatibility with Kuma 1.3.x
func ClientCertAuthenticator(request *restful.Request, response *restful.Response, chain *restful.FilterChain) {
if user.FromCtx(request.Request.Context()) == nil && // do not overwrite existing user
if user.FromCtx(request.Request.Context()).Name == user.Anonymous.Name && // do not overwrite existing user
request.Request.TLS != nil &&
request.Request.TLS.HandshakeComplete &&
len(request.Request.TLS.PeerCertificates) > 0 {
request.Request = request.Request.WithContext(user.Ctx(request.Request.Context(), user.Admin))
request.Request = request.Request.WithContext(user.Ctx(request.Request.Context(), user.Admin.Authenticated()))
}
chain.ProcessFilter(request, response)
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ var _ = Describe("Admin Token Bootstrap", func() {
g.Expect(err).ToNot(HaveOccurred())
user, err := tokenIssuer.Validate(string(globalSecret.Spec.Data.Value))
g.Expect(err).ToNot(HaveOccurred())
g.Expect(user.Name).To(Equal("admin"))
g.Expect(user.Groups).To(Equal([]string{"admin"}))
g.Expect(user.Name).To(Equal("mesh-system:admin"))
g.Expect(user.Groups).To(Equal([]string{"mesh-system:admin"}))
}).Should(Succeed())
})
})
4 changes: 2 additions & 2 deletions pkg/plugins/authn/api-server/tokens/authenticator.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const bearerPrefix = "Bearer "
func UserTokenAuthenticator(issuer issuer.UserTokenIssuer) authn.Authenticator {
return func(request *restful.Request, response *restful.Response, chain *restful.FilterChain) {
authnHeader := request.Request.Header.Get("authorization")
if user.FromCtx(request.Request.Context()) == nil && // do not overwrite existing user
if user.FromCtx(request.Request.Context()).Name == user.Anonymous.Name && // do not overwrite existing user
authnHeader != "" &&
strings.HasPrefix(authnHeader, bearerPrefix) {
token := strings.TrimPrefix(authnHeader, bearerPrefix)
Expand All @@ -25,7 +25,7 @@ func UserTokenAuthenticator(issuer issuer.UserTokenIssuer) authn.Authenticator {
rest_errors.HandleError(response, &rest_errors.Unauthenticated{}, "invalid authentication data: "+err.Error())
return
}
request.Request = request.Request.WithContext(user.Ctx(request.Request.Context(), u))
request.Request = request.Request.WithContext(user.Ctx(request.Request.Context(), u.Authenticated()))
}
chain.ProcessFilter(request, response)
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/plugins/authn/api-server/tokens/rbac/access.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ package rbac
import "github.com/kumahq/kuma/pkg/core/user"

type GenerateUserTokenAccess interface {
ValidateGenerate(user *user.User) error
ValidateGenerate(user user.User) error
}
5 changes: 1 addition & 4 deletions pkg/plugins/authn/api-server/tokens/rbac/static.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,7 @@ func NewStaticGenerateUserTokenAccess(cfg config_rbac.GenerateUserTokenRBACStati
return s
}

func (s *staticGenerateUserTokenAccess) ValidateGenerate(user *user.User) error {
if user == nil {
return &rbac.AccessDeniedError{Reason: "authentication required"}
}
func (s *staticGenerateUserTokenAccess) ValidateGenerate(user user.User) error {
allowed := s.usernames[user.Name]
for _, group := range user.Groups {
if s.groups[group] {
Expand Down
2 changes: 1 addition & 1 deletion pkg/plugins/authn/api-server/tokens/ws/ws_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import (
type noopGenerateUserTokenAccess struct {
}

func (n *noopGenerateUserTokenAccess) ValidateGenerate(*user.User) error {
func (n *noopGenerateUserTokenAccess) ValidateGenerate(user.User) error {
return nil
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/tokens/builtin/rbac/access.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ import (
)

type GenerateDataplaneTokenAccess interface {
ValidateGenerate(name string, mesh string, tags map[string][]string, tokenType string, user *user.User) error
ValidateGenerate(name string, mesh string, tags map[string][]string, tokenType string, user user.User) error
}
2 changes: 1 addition & 1 deletion pkg/tokens/builtin/rbac/noop.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ type NoopGenerateDpTokenAccess struct {

var _ GenerateDataplaneTokenAccess = NoopGenerateDpTokenAccess{}

func (n NoopGenerateDpTokenAccess) ValidateGenerate(name string, mesh string, tags map[string][]string, tokenType string, user *user.User) error {
func (n NoopGenerateDpTokenAccess) ValidateGenerate(name string, mesh string, tags map[string][]string, tokenType string, user user.User) error {
return nil
}
Loading

0 comments on commit 1194575

Please sign in to comment.