1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-14 11:57:48 +00:00

feat: new access checks for background policies (#6970)

* switch to use sar for access checks

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* fix unit tests

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* update helm config

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* fix username

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* update msg

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* fix sa name

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* update install.yaml

Signed-off-by: ShutingZhao <shuting@nirmata.com>

---------

Signed-off-by: ShutingZhao <shuting@nirmata.com>
This commit is contained in:
shuting 2023-04-24 18:31:42 +08:00 committed by GitHub
parent f9578ed582
commit e14fe847bc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 122 additions and 90 deletions

View file

@ -88,6 +88,12 @@ rules:
- create
- update
- patch
- apiGroups:
- authorization.k8s.io
resources:
- subjectaccessreviews
verbs:
- create
- apiGroups:
- '*'
resources:

View file

@ -120,7 +120,7 @@ spec:
image: {{ include "kyverno.image" (dict "image" .Values.admissionController.container.image "defaultTag" .Chart.AppVersion) | quote }}
imagePullPolicy: {{ .Values.admissionController.container.image.pullPolicy }}
args:
- --backgroundServiceAccountName='system:serviceaccount:{{ include "kyverno.namespace" . }}:{{ include "kyverno.background-controller.serviceAccountName" . }}'
- --backgroundServiceAccountName=system:serviceaccount:{{ include "kyverno.namespace" . }}:{{ include "kyverno.background-controller.serviceAccountName" . }}
- --servicePort={{ .Values.admissionController.service.port }}
- --loggingFormat={{ .Values.admissionController.logging.format }}
- --v={{ .Values.admissionController.logging.verbosity }}

View file

@ -14,8 +14,5 @@ subjects:
- kind: ServiceAccount
name: {{ template "kyverno.background-controller.serviceAccountName" . }}
namespace: {{ template "kyverno.namespace" . }}
- kind: ServiceAccount
name: {{ template "kyverno.admission-controller.serviceAccountName" . }}
namespace: {{ template "kyverno.namespace" . }}
{{- end -}}
{{- end -}}

View file

@ -73,6 +73,12 @@ rules:
- create
- patch
- update
- apiGroups:
- authorization.k8s.io
resources:
- subjectaccessreviews
verbs:
- create
{{- with .Values.cleanupController.rbac.clusterRole.extraResources }}
---
apiVersion: rbac.authorization.k8s.io/v1

View file

@ -113,6 +113,8 @@ spec:
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: KYVERNO_SERVICEACCOUNT_NAME
value: {{ template "kyverno.cleanup-controller.serviceAccountName" . }}
- name: KYVERNO_NAMESPACE
valueFrom:
fieldRef:

View file

@ -388,7 +388,7 @@ func (c *ApplyCommandConfig) applyCommandHelper() (rc *common.ResultCounts, reso
skipInvalidPolicies.invalid = make([]string, 0)
for _, policy := range policies {
_, err := policy2.Validate(policy, nil, nil, true, openApiManager)
_, err := policy2.Validate(policy, nil, nil, true, openApiManager, config.KyvernoUserName(config.KyvernoServiceAccountName()))
if err != nil {
log.Log.Error(err, "policy validation error")
if strings.HasPrefix(err.Error(), "variable 'element.name'") {

View file

@ -12,6 +12,7 @@ import (
"github.com/google/go-containerregistry/pkg/v1/static"
"github.com/google/go-containerregistry/pkg/v1/types"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/common"
"github.com/kyverno/kyverno/pkg/config"
"github.com/kyverno/kyverno/pkg/openapi"
policyvalidation "github.com/kyverno/kyverno/pkg/policy"
policyutils "github.com/kyverno/kyverno/pkg/utils/policy"
@ -47,7 +48,7 @@ kyverno oci push -p policies. -i <imgref>`,
return fmt.Errorf("creating openapi manager: %v", err)
}
for _, policy := range policies {
if _, err := policyvalidation.Validate(policy, nil, nil, true, openApiManager); err != nil {
if _, err := policyvalidation.Validate(policy, nil, nil, true, openApiManager, config.KyvernoUserName(config.KyvernoServiceAccountName())); err != nil {
return fmt.Errorf("validating policy %s: %v", policy.GetName(), err)
}
}

View file

@ -19,6 +19,7 @@ import (
"github.com/kyverno/kyverno/pkg/autogen"
"github.com/kyverno/kyverno/pkg/background/generate"
"github.com/kyverno/kyverno/pkg/clients/dclient"
"github.com/kyverno/kyverno/pkg/config"
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
"github.com/kyverno/kyverno/pkg/openapi"
policy2 "github.com/kyverno/kyverno/pkg/policy"
@ -178,7 +179,7 @@ func applyPoliciesFromPath(
}
for _, policy := range policies {
_, err := policy2.Validate(policy, nil, nil, true, openApiManager)
_, err := policy2.Validate(policy, nil, nil, true, openApiManager, config.KyvernoUserName(config.KyvernoServiceAccountName()))
if err != nil {
log.Log.Error(err, "skipping invalid policy", "name", policy.GetName())
continue

View file

@ -408,6 +408,7 @@ func main() {
policyHandlers := webhookspolicy.NewHandlers(
setup.KyvernoDynamicClient,
openApiManager,
backgroundServiceAccountName,
)
resourceHandlers := webhooksresource.NewHandlers(
engine,

View file

@ -33962,6 +33962,12 @@ rules:
- create
- update
- patch
- apiGroups:
- authorization.k8s.io
resources:
- subjectaccessreviews
verbs:
- create
- apiGroups:
- '*'
resources:
@ -34143,6 +34149,12 @@ rules:
- create
- patch
- update
- apiGroups:
- authorization.k8s.io
resources:
- subjectaccessreviews
verbs:
- create
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
@ -34444,9 +34456,6 @@ subjects:
- kind: ServiceAccount
name: kyverno-background-controller
namespace: kyverno
- kind: ServiceAccount
name: kyverno-admission-controller
namespace: kyverno
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
@ -34937,7 +34946,7 @@ spec:
image: "ghcr.io/kyverno/kyverno:latest"
imagePullPolicy: IfNotPresent
args:
- --backgroundServiceAccountName='system:serviceaccount:kyverno:kyverno-background-controller'
- --backgroundServiceAccountName=system:serviceaccount:kyverno:kyverno-background-controller
- --servicePort=443
- --loggingFormat=text
- --v=2
@ -35179,6 +35188,8 @@ spec:
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: KYVERNO_SERVICEACCOUNT_NAME
value: kyverno-cleanup-controller
- name: KYVERNO_NAMESPACE
valueFrom:
fieldRef:

View file

@ -23,7 +23,7 @@ type CanIOptions interface {
// - can only evaluate a single verb
// - group version resource is determined from the kind using the discovery client REST mapper
// - If disallowed, the reason and evaluationError is available in the logs
// - each can generates a SelfSubjectAccessReview resource and response is evaluated for permissions
// - each can generates a SubjectAccessReview resource and response is evaluated for permissions
RunAccessCheck(context.Context) (bool, error)
}
@ -32,19 +32,21 @@ type canIOptions struct {
verb string
kind string
subresource string
user string
discovery Discovery
ssarClient authorizationv1client.SelfSubjectAccessReviewInterface
sarClient authorizationv1client.SubjectAccessReviewInterface
}
// NewCanI returns a new instance of operation access controller evaluator
func NewCanI(discovery Discovery, ssarClient authorizationv1client.SelfSubjectAccessReviewInterface, kind, namespace, verb, subresource string) CanIOptions {
func NewCanI(discovery Discovery, sarClient authorizationv1client.SubjectAccessReviewInterface, kind, namespace, verb, subresource string, user string) CanIOptions {
return &canIOptions{
namespace: namespace,
verb: verb,
kind: kind,
subresource: subresource,
user: user,
discovery: discovery,
ssarClient: ssarClient,
sarClient: sarClient,
}
}
@ -72,8 +74,8 @@ func (o *canIOptions) RunAccessCheck(ctx context.Context) (bool, error) {
return false, fmt.Errorf("failed to get the Group Version Resource for kind %s", o.kind)
}
sar := &authorizationv1.SelfSubjectAccessReview{
Spec: authorizationv1.SelfSubjectAccessReviewSpec{
sar := &authorizationv1.SubjectAccessReview{
Spec: authorizationv1.SubjectAccessReviewSpec{
ResourceAttributes: &authorizationv1.ResourceAttributes{
Namespace: o.namespace,
Verb: o.verb,
@ -81,17 +83,12 @@ func (o *canIOptions) RunAccessCheck(ctx context.Context) (bool, error) {
Resource: gvr.Resource,
Subresource: o.subresource,
},
User: o.user,
},
}
// Set self subject access review
// - namespace
// - verb
// - resource
// - subresource
logger := logger.WithValues("kind", sar.Kind, "namespace", sar.Namespace, "name", sar.Name)
// Create the Resource
resp, err := o.ssarClient.Create(ctx, sar, metav1.CreateOptions{})
logger := logger.WithValues("kind", sar.Kind, "namespace", sar.Namespace, "name", sar.Name, "gvr", gvr.String())
resp, err := o.sarClient.Create(ctx, sar, metav1.CreateOptions{})
if err != nil {
logger.Error(err, "failed to create resource")
return false, err
@ -100,7 +97,6 @@ func (o *canIOptions) RunAccessCheck(ctx context.Context) (bool, error) {
if !resp.Status.Allowed {
reason := resp.Status.Reason
evaluationError := resp.Status.EvaluationError
// Reporting ? (just logs)
logger.Info("disallowed operation", "reason", reason, "evaluationError", evaluationError)
}

View file

@ -34,7 +34,7 @@ func TestNewCanI(t *testing.T) {
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := NewCanI(tt.args.client.Discovery(), tt.args.client.GetKubeClient().AuthorizationV1().SelfSubjectAccessReviews(), tt.args.kind, tt.args.namespace, tt.args.verb, "")
got := NewCanI(tt.args.client.Discovery(), tt.args.client.GetKubeClient().AuthorizationV1().SubjectAccessReviews(), tt.args.kind, tt.args.namespace, tt.args.verb, "", "admin")
assert.NotNil(t, got)
})
}
@ -71,7 +71,7 @@ func TestCanIOptions_DiscoveryError(t *testing.T) {
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
o := NewCanI(tt.fields.discovery, nil, tt.fields.kind, tt.fields.namespace, tt.fields.verb, "")
o := NewCanI(tt.fields.discovery, nil, tt.fields.kind, tt.fields.namespace, tt.fields.verb, "", "admin")
got, err := o.RunAccessCheck(context.TODO())
if tt.wantErr {
assert.Error(t, err)
@ -83,19 +83,19 @@ func TestCanIOptions_DiscoveryError(t *testing.T) {
}
}
type ssar struct{}
type sar struct{}
func (d *ssar) Create(_ context.Context, _ *v1.SelfSubjectAccessReview, _ metav1.CreateOptions) (*v1.SelfSubjectAccessReview, error) {
func (d *sar) Create(_ context.Context, _ *v1.SubjectAccessReview, _ metav1.CreateOptions) (*v1.SubjectAccessReview, error) {
return nil, errors.New("dummy")
}
func TestCanIOptions_SsarError(t *testing.T) {
type fields struct {
namespace string
verb string
kind string
discovery Discovery
ssarClient authorizationv1client.SelfSubjectAccessReviewInterface
namespace string
verb string
kind string
discovery Discovery
sarClient authorizationv1client.SubjectAccessReviewInterface
}
tests := []struct {
name string
@ -105,18 +105,18 @@ func TestCanIOptions_SsarError(t *testing.T) {
}{{
name: "deployments",
fields: fields{
discovery: dclient.NewEmptyFakeClient().Discovery(),
ssarClient: &ssar{},
kind: "Deployment",
namespace: "default",
verb: "test",
discovery: dclient.NewEmptyFakeClient().Discovery(),
sarClient: &sar{},
kind: "Deployment",
namespace: "default",
verb: "test",
},
want: false,
wantErr: true,
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
o := NewCanI(tt.fields.discovery, tt.fields.ssarClient, tt.fields.kind, tt.fields.namespace, tt.fields.verb, "")
o := NewCanI(tt.fields.discovery, tt.fields.sarClient, tt.fields.kind, tt.fields.namespace, tt.fields.verb, "", "admin")
got, err := o.RunAccessCheck(context.TODO())
if tt.wantErr {
assert.Error(t, err)
@ -173,7 +173,7 @@ func TestCanIOptions_RunAccessCheck(t *testing.T) {
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
o := NewCanI(tt.fields.client.Discovery(), tt.fields.client.GetKubeClient().AuthorizationV1().SelfSubjectAccessReviews(), tt.fields.kind, tt.fields.namespace, tt.fields.verb, "")
o := NewCanI(tt.fields.client.Discovery(), tt.fields.client.GetKubeClient().AuthorizationV1().SubjectAccessReviews(), tt.fields.kind, tt.fields.namespace, tt.fields.verb, "", "admin")
got, err := o.RunAccessCheck(context.TODO())
if tt.wantErr {
assert.Error(t, err)

View file

@ -2,6 +2,7 @@ package config
import (
"errors"
"fmt"
"strconv"
"sync"
@ -135,6 +136,10 @@ func KyvernoMetricsConfigMapName() string {
return kyvernoMetricsConfigMapName
}
func KyvernoUserName(serviceaccount string) string {
return fmt.Sprintf("system:serviceaccount:%s:%s", kyvernoNamespace, serviceaccount)
}
// Configuration to be used by consumer to check filters
type Configuration interface {
// GetDefaultRegistry return default image registry

View file

@ -173,7 +173,7 @@ func (h validateManifestHandler) verifyManifest(
}
func (h validateManifestHandler) checkDryRunPermission(ctx context.Context, kind, namespace string) (bool, error) {
canI := auth.NewCanI(h.client.Discovery(), h.client.GetKubeClient().AuthorizationV1().SelfSubjectAccessReviews(), kind, namespace, "create", "")
canI := auth.NewCanI(h.client.Discovery(), h.client.GetKubeClient().AuthorizationV1().SubjectAccessReviews(), kind, namespace, "create", "", config.KyvernoServiceAccountName())
ok, err := canI.RunAccessCheck(ctx)
if err != nil {
return false, err

View file

@ -22,7 +22,7 @@ type Validation interface {
// - Mutate
// - Validation
// - Generate
func validateActions(idx int, rule *kyvernov1.Rule, client dclient.Interface, mock bool) error {
func validateActions(idx int, rule *kyvernov1.Rule, client dclient.Interface, mock bool, username string) error {
if rule == nil {
return nil
}
@ -30,7 +30,7 @@ func validateActions(idx int, rule *kyvernov1.Rule, client dclient.Interface, mo
var checker Validation
// Mutate
if rule.HasMutate() {
checker = mutate.NewMutateFactory(rule.Mutation, client)
checker = mutate.NewMutateFactory(rule.Mutation, client, username)
if path, err := checker.Validate(context.TODO()); err != nil {
return fmt.Errorf("path: spec.rules[%d].mutate.%s.: %v", idx, path, err)
}
@ -55,7 +55,7 @@ func validateActions(idx int, rule *kyvernov1.Rule, client dclient.Interface, mo
return fmt.Errorf("path: spec.rules[%d].generate.%s.: %v", idx, path, err)
}
} else {
checker = generate.NewGenerateFactory(client, rule.Generation, logging.GlobalLogger())
checker = generate.NewGenerateFactory(client, rule.Generation, username, logging.GlobalLogger())
if path, err := checker.Validate(context.TODO()); err != nil {
return fmt.Errorf("path: spec.rules[%d].generate.%s.: %v", idx, path, err)
}

View file

@ -23,13 +23,15 @@ type Operations interface {
// Auth provides implementation to check if caller/self/kyverno has access to perofrm operations
type Auth struct {
client dclient.Interface
user string
log logr.Logger
}
// NewAuth returns a new instance of Auth for operations
func NewAuth(client dclient.Interface, log logr.Logger) *Auth {
func NewAuth(client dclient.Interface, user string, log logr.Logger) *Auth {
a := Auth{
client: client,
user: user,
log: log,
}
return &a
@ -37,7 +39,7 @@ func NewAuth(client dclient.Interface, log logr.Logger) *Auth {
// CanICreate returns 'true' if self can 'create' resource
func (a *Auth) CanICreate(ctx context.Context, kind, namespace string) (bool, error) {
canI := auth.NewCanI(a.client.Discovery(), a.client.GetKubeClient().AuthorizationV1().SelfSubjectAccessReviews(), kind, namespace, "create", "")
canI := auth.NewCanI(a.client.Discovery(), a.client.GetKubeClient().AuthorizationV1().SubjectAccessReviews(), kind, namespace, "create", "", a.user)
ok, err := canI.RunAccessCheck(ctx)
if err != nil {
return false, err
@ -47,7 +49,7 @@ func (a *Auth) CanICreate(ctx context.Context, kind, namespace string) (bool, er
// CanIUpdate returns 'true' if self can 'update' resource
func (a *Auth) CanIUpdate(ctx context.Context, kind, namespace string) (bool, error) {
canI := auth.NewCanI(a.client.Discovery(), a.client.GetKubeClient().AuthorizationV1().SelfSubjectAccessReviews(), kind, namespace, "update", "")
canI := auth.NewCanI(a.client.Discovery(), a.client.GetKubeClient().AuthorizationV1().SubjectAccessReviews(), kind, namespace, "update", "", a.user)
ok, err := canI.RunAccessCheck(ctx)
if err != nil {
return false, err
@ -57,7 +59,7 @@ func (a *Auth) CanIUpdate(ctx context.Context, kind, namespace string) (bool, er
// CanIDelete returns 'true' if self can 'delete' resource
func (a *Auth) CanIDelete(ctx context.Context, kind, namespace string) (bool, error) {
canI := auth.NewCanI(a.client.Discovery(), a.client.GetKubeClient().AuthorizationV1().SelfSubjectAccessReviews(), kind, namespace, "delete", "")
canI := auth.NewCanI(a.client.Discovery(), a.client.GetKubeClient().AuthorizationV1().SubjectAccessReviews(), kind, namespace, "delete", "", a.user)
ok, err := canI.RunAccessCheck(ctx)
if err != nil {
return false, err
@ -67,7 +69,7 @@ func (a *Auth) CanIDelete(ctx context.Context, kind, namespace string) (bool, er
// CanIGet returns 'true' if self can 'get' resource
func (a *Auth) CanIGet(ctx context.Context, kind, namespace string) (bool, error) {
canI := auth.NewCanI(a.client.Discovery(), a.client.GetKubeClient().AuthorizationV1().SelfSubjectAccessReviews(), kind, namespace, "get", "")
canI := auth.NewCanI(a.client.Discovery(), a.client.GetKubeClient().AuthorizationV1().SubjectAccessReviews(), kind, namespace, "get", "", a.user)
ok, err := canI.RunAccessCheck(ctx)
if err != nil {
return false, err

View file

@ -25,10 +25,10 @@ type Generate struct {
}
// NewGenerateFactory returns a new instance of Generate validation checker
func NewGenerateFactory(client dclient.Interface, rule kyvernov1.Generation, log logr.Logger) *Generate {
func NewGenerateFactory(client dclient.Interface, rule kyvernov1.Generation, user string, log logr.Logger) *Generate {
g := Generate{
rule: rule,
authCheck: NewAuth(client, log),
authCheck: NewAuth(client, user, log),
log: log,
}
@ -122,7 +122,7 @@ func (g *Generate) validateClone(c kyvernov1.CloneFrom, cl kyvernov1.CloneList,
return "", err
}
if !ok {
return "", fmt.Errorf("kyverno does not have permissions to 'get' resource %s/%s. Update permissions in ClusterRole 'kyverno:background-controller:additional'", kind, namespace)
return "", fmt.Errorf("kyverno does not have permissions to 'get' resource %s/%s. Grant proper permissions to the background controller", kind, namespace)
}
} else {
g.log.V(2).Info("resource Kind uses variables, so cannot be resolved. Skipping Auth Checks.")
@ -145,7 +145,7 @@ func (g *Generate) canIGenerate(ctx context.Context, kind, namespace string) err
return err
}
if !ok {
return fmt.Errorf("kyverno does not have permissions to 'create' resource %s/%s. Update permissions in ClusterRole 'kyverno:background-controller:additional'", kind, namespace)
return fmt.Errorf("kyverno does not have permissions to 'create' resource %s/%s. Grant proper permissions to the background controller", kind, namespace)
}
// UPDATE
ok, err = authCheck.CanIUpdate(ctx, kind, namespace)
@ -154,7 +154,7 @@ func (g *Generate) canIGenerate(ctx context.Context, kind, namespace string) err
return err
}
if !ok {
return fmt.Errorf("kyverno does not have permissions to 'update' resource %s/%s. Update permissions in ClusterRole 'kyverno:background-controller:additional'", kind, namespace)
return fmt.Errorf("kyverno does not have permissions to 'update' resource %s/%s. Grant proper permissions to the background controller", kind, namespace)
}
// GET
ok, err = authCheck.CanIGet(ctx, kind, namespace)
@ -163,7 +163,7 @@ func (g *Generate) canIGenerate(ctx context.Context, kind, namespace string) err
return err
}
if !ok {
return fmt.Errorf("kyverno does not have permissions to 'get' resource %s/%s. Update permissions in ClusterRole 'kyverno:background-controller:additional'", kind, namespace)
return fmt.Errorf("kyverno does not have permissions to 'get' resource %s/%s. Grant proper permissions to the background controller", kind, namespace)
}
// DELETE
@ -173,7 +173,7 @@ func (g *Generate) canIGenerate(ctx context.Context, kind, namespace string) err
return err
}
if !ok {
return fmt.Errorf("kyverno does not have permissions to 'delete' resource %s/%s. Update permissions in ClusterRole 'kyverno:background-controller:additional'", kind, namespace)
return fmt.Errorf("kyverno does not have permissions to 'delete' resource %s/%s. Grant proper permissions to the background controller", kind, namespace)
}
} else {
g.log.V(2).Info("resource Kind uses variables, so cannot be resolved. Skipping Auth Checks.")

View file

@ -9,6 +9,7 @@ import (
type authChecker struct {
client dclient.Interface
user string
}
type AuthChecker interface {
@ -17,21 +18,21 @@ type AuthChecker interface {
CanIGet(ctx context.Context, kind, namespace, subresource string) (bool, error)
}
func newAuthChecker(client dclient.Interface) AuthChecker {
return &authChecker{client: client}
func newAuthChecker(client dclient.Interface, user string) AuthChecker {
return &authChecker{client: client, user: user}
}
func (a *authChecker) CanICreate(ctx context.Context, kind, namespace, subresource string) (bool, error) {
checker := auth.NewCanI(a.client.Discovery(), a.client.GetKubeClient().AuthorizationV1().SelfSubjectAccessReviews(), kind, namespace, "create", subresource)
checker := auth.NewCanI(a.client.Discovery(), a.client.GetKubeClient().AuthorizationV1().SubjectAccessReviews(), kind, namespace, "create", subresource, a.user)
return checker.RunAccessCheck(ctx)
}
func (a *authChecker) CanIUpdate(ctx context.Context, kind, namespace, subresource string) (bool, error) {
checker := auth.NewCanI(a.client.Discovery(), a.client.GetKubeClient().AuthorizationV1().SelfSubjectAccessReviews(), kind, namespace, "update", subresource)
checker := auth.NewCanI(a.client.Discovery(), a.client.GetKubeClient().AuthorizationV1().SubjectAccessReviews(), kind, namespace, "update", subresource, a.user)
return checker.RunAccessCheck(ctx)
}
func (a *authChecker) CanIGet(ctx context.Context, kind, namespace, subresource string) (bool, error) {
checker := auth.NewCanI(a.client.Discovery(), a.client.GetKubeClient().AuthorizationV1().SelfSubjectAccessReviews(), kind, namespace, "get", subresource)
checker := auth.NewCanI(a.client.Discovery(), a.client.GetKubeClient().AuthorizationV1().SubjectAccessReviews(), kind, namespace, "get", subresource, a.user)
return checker.RunAccessCheck(ctx)
}

View file

@ -20,10 +20,10 @@ type Mutate struct {
}
// NewMutateFactory returns a new instance of Mutate validation checker
func NewMutateFactory(m kyvernov1.Mutation, client dclient.Interface) *Mutate {
func NewMutateFactory(m kyvernov1.Mutation, client dclient.Interface, user string) *Mutate {
return &Mutate{
mutation: m,
authChecker: newAuthChecker(client),
authChecker: newAuthChecker(client, user),
}
}

View file

@ -124,7 +124,7 @@ func checkValidationFailureAction(spec *kyvernov1.Spec) []string {
}
// Validate checks the policy and rules declarations for required configurations
func Validate(policy, oldPolicy kyvernov1.PolicyInterface, client dclient.Interface, mock bool, openApiManager openapi.Manager) ([]string, error) {
func Validate(policy, oldPolicy kyvernov1.PolicyInterface, client dclient.Interface, mock bool, openApiManager openapi.Manager, username string) ([]string, error) {
var warnings []string
namespaced := policy.IsNamespaced()
spec := policy.GetSpec()
@ -328,7 +328,7 @@ func Validate(policy, oldPolicy kyvernov1.PolicyInterface, client dclient.Interf
}
}
if err := validateActions(i, &rules[i], client, mock); err != nil {
if err := validateActions(i, &rules[i], client, mock, username); err != nil {
return warnings, err
}

View file

@ -352,7 +352,7 @@ func Test_Validate_Policy(t *testing.T) {
err := json.Unmarshal(rawPolicy, &policy)
assert.NilError(t, err)
_, err = Validate(policy, nil, nil, true, openApiManager)
_, err = Validate(policy, nil, nil, true, openApiManager, "admin")
assert.NilError(t, err)
}
@ -499,7 +499,7 @@ func Test_Validate_ErrorFormat(t *testing.T) {
assert.NilError(t, err)
openApiManager, _ := openapi.NewManager(logr.Discard())
_, err = Validate(policy, nil, nil, true, openApiManager)
_, err = Validate(policy, nil, nil, true, openApiManager, "admin")
assert.Assert(t, err != nil)
}
@ -901,7 +901,7 @@ func Test_Validate_Kind(t *testing.T) {
assert.NilError(t, err)
openApiManager, _ := openapi.NewManager(logr.Discard())
_, err = Validate(policy, nil, nil, true, openApiManager)
_, err = Validate(policy, nil, nil, true, openApiManager, "admin")
assert.Assert(t, err != nil)
}
@ -950,7 +950,7 @@ func Test_Validate_Any_Kind(t *testing.T) {
assert.NilError(t, err)
openApiManager, _ := openapi.NewManager(logr.Discard())
_, err = Validate(policy, nil, nil, true, openApiManager)
_, err = Validate(policy, nil, nil, true, openApiManager, "admin")
assert.Assert(t, err != nil)
}
@ -1040,7 +1040,7 @@ func Test_Wildcards_Kind(t *testing.T) {
assert.NilError(t, err)
openApiManager, _ := openapi.NewManager(logr.Discard())
_, err = Validate(policy, nil, nil, true, openApiManager)
_, err = Validate(policy, nil, nil, true, openApiManager, "admin")
assert.Assert(t, err != nil)
}
@ -1090,7 +1090,7 @@ func Test_Namespced_Policy(t *testing.T) {
assert.NilError(t, err)
openApiManager, _ := openapi.NewManager(logr.Discard())
_, err = Validate(policy, nil, nil, true, openApiManager)
_, err = Validate(policy, nil, nil, true, openApiManager, "admin")
assert.Assert(t, err != nil)
}
@ -1268,7 +1268,7 @@ func Test_patchesJson6902_Policy(t *testing.T) {
assert.NilError(t, err)
openApiManager, _ := openapi.NewManager(logr.Discard())
_, err = Validate(policy, nil, nil, true, openApiManager)
_, err = Validate(policy, nil, nil, true, openApiManager, "admin")
assert.NilError(t, err)
}
@ -1316,7 +1316,7 @@ func Test_deny_exec(t *testing.T) {
assert.NilError(t, err)
openApiManager, _ := openapi.NewManager(logr.Discard())
_, err = Validate(policy, nil, nil, true, openApiManager)
_, err = Validate(policy, nil, nil, true, openApiManager, "admin")
assert.NilError(t, err)
}
@ -1464,7 +1464,7 @@ func Test_SignatureAlgorithm(t *testing.T) {
assert.NilError(t, err)
openApiManager, _ := openapi.NewManager(logr.Discard())
_, err = Validate(policy, nil, nil, true, openApiManager)
_, err = Validate(policy, nil, nil, true, openApiManager, "admin")
if testcase.expectedOutput {
assert.NilError(t, err)
} else {
@ -1514,7 +1514,7 @@ func Test_existing_resource_policy(t *testing.T) {
assert.NilError(t, err)
openApiManager, _ := openapi.NewManager(logr.Discard())
_, err = Validate(policy, nil, nil, true, openApiManager)
_, err = Validate(policy, nil, nil, true, openApiManager, "admin")
assert.NilError(t, err)
}
@ -1570,7 +1570,7 @@ func Test_PodControllerAutoGenExclusion_All_Controllers_Policy(t *testing.T) {
assert.NilError(t, err)
openApiManager, _ := openapi.NewManager(logr.Discard())
res, err := Validate(policy, nil, nil, true, openApiManager)
res, err := Validate(policy, nil, nil, true, openApiManager, "admin")
assert.NilError(t, err)
assert.Assert(t, res == nil)
}
@ -1627,7 +1627,7 @@ func Test_PodControllerAutoGenExclusion_Not_All_Controllers_Policy(t *testing.T)
assert.NilError(t, err)
openApiManager, _ := openapi.NewManager(logr.Discard())
warnings, err := Validate(policy, nil, nil, true, openApiManager)
warnings, err := Validate(policy, nil, nil, true, openApiManager, "admin")
assert.Assert(t, warnings != nil)
assert.NilError(t, err)
}
@ -1684,7 +1684,7 @@ func Test_PodControllerAutoGenExclusion_None_Policy(t *testing.T) {
assert.NilError(t, err)
openApiManager, _ := openapi.NewManager(logr.Discard())
warnings, err := Validate(policy, nil, nil, true, openApiManager)
warnings, err := Validate(policy, nil, nil, true, openApiManager, "admin")
assert.Assert(t, warnings == nil)
assert.NilError(t, err)
}
@ -2226,7 +2226,7 @@ func Test_Any_wildcard_policy(t *testing.T) {
assert.NilError(t, err)
openApiManager, _ := openapi.NewManager(logr.Discard())
_, err = Validate(policy, nil, nil, true, openApiManager)
_, err = Validate(policy, nil, nil, true, openApiManager, "admin")
assert.Assert(t, err != nil)
}
@ -2284,7 +2284,7 @@ func Test_Validate_RuleImageExtractorsJMESPath(t *testing.T) {
expectedErr := fmt.Errorf("path: spec.rules[0]: jmespath may not be used in an image extractor when mutating digests with verify images")
openApiManager, _ := openapi.NewManager(logr.Discard())
_, actualErr := Validate(policy, nil, nil, true, openApiManager)
_, actualErr := Validate(policy, nil, nil, true, openApiManager, "admin")
assert.Equal(t, expectedErr.Error(), actualErr.Error())
}

View file

@ -9,6 +9,7 @@ import (
kyvernov2alpha1 "github.com/kyverno/kyverno/api/kyverno/v2alpha1"
"github.com/kyverno/kyverno/pkg/auth"
"github.com/kyverno/kyverno/pkg/clients/dclient"
"github.com/kyverno/kyverno/pkg/config"
enginecontext "github.com/kyverno/kyverno/pkg/engine/context"
"github.com/kyverno/kyverno/pkg/engine/variables"
"k8s.io/apimachinery/pkg/util/sets"
@ -70,7 +71,7 @@ func validateAuth(ctx context.Context, client dclient.Interface, policy kyvernov
spec := policy.GetSpec()
kinds := sets.New(spec.MatchResources.GetKinds()...)
for kind := range kinds {
checker := auth.NewCanI(client.Discovery(), client.GetKubeClient().AuthorizationV1().SelfSubjectAccessReviews(), kind, namespace, "delete", "")
checker := auth.NewCanI(client.Discovery(), client.GetKubeClient().AuthorizationV1().SubjectAccessReviews(), kind, namespace, "delete", "", config.KyvernoUserName(config.KyvernoServiceAccountName()))
allowedDeletion, err := checker.RunAccessCheck(ctx)
if err != nil {
return err
@ -79,7 +80,7 @@ func validateAuth(ctx context.Context, client dclient.Interface, policy kyvernov
return fmt.Errorf("cleanup controller has no permission to delete kind %s", kind)
}
checker = auth.NewCanI(client.Discovery(), client.GetKubeClient().AuthorizationV1().SelfSubjectAccessReviews(), kind, namespace, "list", "")
checker = auth.NewCanI(client.Discovery(), client.GetKubeClient().AuthorizationV1().SubjectAccessReviews(), kind, namespace, "list", "", config.KyvernoUserName(config.KyvernoServiceAccountName()))
allowedList, err := checker.RunAccessCheck(ctx)
if err != nil {
return err

View file

@ -14,14 +14,16 @@ import (
)
type policyHandlers struct {
client dclient.Interface
openApiManager openapi.Manager
client dclient.Interface
openApiManager openapi.Manager
backgroungServiceAccountName string
}
func NewHandlers(client dclient.Interface, openApiManager openapi.Manager) webhooks.PolicyHandlers {
func NewHandlers(client dclient.Interface, openApiManager openapi.Manager, serviceaccount string) webhooks.PolicyHandlers {
return &policyHandlers{
client: client,
openApiManager: openApiManager,
client: client,
openApiManager: openApiManager,
backgroungServiceAccountName: serviceaccount,
}
}
@ -31,7 +33,7 @@ func (h *policyHandlers) Validate(ctx context.Context, logger logr.Logger, reque
logger.Error(err, "failed to unmarshal policies from admission request")
return admissionutils.Response(request.UID, err)
}
warnings, err := policyvalidate.Validate(policy, oldPolicy, h.client, false, h.openApiManager)
warnings, err := policyvalidate.Validate(policy, oldPolicy, h.client, false, h.openApiManager, h.backgroungServiceAccountName)
if err != nil {
logger.Error(err, "policy validation errors")
}