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

fix: block generate policies when lack of permission to operate downstream resources (#6610)

* debug

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

* return on errors only

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

* update clusterrolebinding

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

* update clusterrolebinding

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

* remove debug

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

* add kuttl tests

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

* fix ns

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

---------

Signed-off-by: ShutingZhao <shuting@nirmata.com>
This commit is contained in:
shuting 2023-03-22 21:14:57 +08:00 committed by GitHub
parent f5e96e6ef2
commit 6249ab70e8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
58 changed files with 587 additions and 64 deletions

View file

@ -14,5 +14,8 @@ 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

@ -1,6 +1,7 @@
package policy
import (
"context"
"fmt"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
@ -14,7 +15,7 @@ import (
// Validation provides methods to validate a rule
type Validation interface {
Validate() (string, error)
Validate(ctx context.Context) (string, error)
}
// validateAction performs validation on the rule actions
@ -27,11 +28,10 @@ func validateActions(idx int, rule *kyvernov1.Rule, client dclient.Interface, mo
}
var checker Validation
// Mutate
if rule.HasMutate() {
checker = mutate.NewMutateFactory(rule.Mutation)
if path, err := checker.Validate(); err != nil {
checker = mutate.NewMutateFactory(rule.Mutation, client)
if path, err := checker.Validate(context.TODO()); err != nil {
return fmt.Errorf("path: spec.rules[%d].mutate.%s.: %v", idx, path, err)
}
}
@ -39,7 +39,7 @@ func validateActions(idx int, rule *kyvernov1.Rule, client dclient.Interface, mo
// Validate
if rule.HasValidate() {
checker = validate.NewValidateFactory(&rule.Validation)
if path, err := checker.Validate(); err != nil {
if path, err := checker.Validate(context.TODO()); err != nil {
return fmt.Errorf("path: spec.rules[%d].validate.%s.: %v", idx, path, err)
}
}
@ -51,12 +51,12 @@ func validateActions(idx int, rule *kyvernov1.Rule, client dclient.Interface, mo
// this need to modified to use different implementation for online and offline mode
if mock {
checker = generate.NewFakeGenerate(rule.Generation)
if path, err := checker.Validate(); err != nil {
if path, err := checker.Validate(context.TODO()); err != nil {
return fmt.Errorf("path: spec.rules[%d].generate.%s.: %v", idx, path, err)
}
} else {
checker = generate.NewGenerateFactory(client, rule.Generation, logging.GlobalLogger())
if path, err := checker.Validate(); err != nil {
if path, err := checker.Validate(context.TODO()); err != nil {
return fmt.Errorf("path: spec.rules[%d].generate.%s.: %v", idx, path, err)
}
}

View file

@ -36,7 +36,7 @@ func NewGenerateFactory(client dclient.Interface, rule kyvernov1.Generation, log
}
// Validate validates the 'generate' rule
func (g *Generate) Validate() (string, error) {
func (g *Generate) Validate(ctx context.Context) (string, error) {
rule := g.rule
if rule.GetData() != nil && rule.Clone != (kyvernov1.CloneFrom{}) {
return "", fmt.Errorf("only one of data or clone can be specified")
@ -91,12 +91,12 @@ func (g *Generate) Validate() (string, error) {
if len(rule.CloneList.Kinds) != 0 {
for _, kind = range rule.CloneList.Kinds {
_, kind = kubeutils.GetKindFromGVK(kind)
if err := g.canIGenerate(kind, namespace); err != nil {
if err := g.canIGenerate(ctx, kind, namespace); err != nil {
return "", err
}
}
} else {
if err := g.canIGenerate(kind, namespace); err != nil {
if err := g.canIGenerate(ctx, kind, namespace); err != nil {
return "", err
}
}
@ -119,7 +119,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:generate'", kind, namespace)
return "", fmt.Errorf("kyverno does not have permissions to 'get' resource %s/%s. Update permissions in ClusterRole 'kyverno:background-controller:additional'", kind, namespace)
}
} else {
g.log.V(4).Info("name & namespace uses variables, so cannot be resolved. Skipping Auth Checks.")
@ -128,46 +128,46 @@ func (g *Generate) validateClone(c kyvernov1.CloneFrom, cl kyvernov1.CloneList,
}
// canIGenerate returns a error if kyverno cannot perform operations
func (g *Generate) canIGenerate(kind, namespace string) error {
func (g *Generate) canIGenerate(ctx context.Context, kind, namespace string) error {
// Skip if there is variable defined
authCheck := g.authCheck
if !regex.IsVariable(kind) && !regex.IsVariable(namespace) {
// CREATE
ok, err := authCheck.CanICreate(context.TODO(), kind, namespace)
ok, err := authCheck.CanICreate(ctx, kind, namespace)
if err != nil {
// machinery error
return err
}
if !ok {
return fmt.Errorf("kyverno does not have permissions to 'create' resource %s/%s. Update permissions in ClusterRole 'kyverno:generate'", kind, namespace)
return fmt.Errorf("kyverno does not have permissions to 'create' resource %s/%s. Update permissions in ClusterRole 'kyverno:background-controller:additional'", kind, namespace)
}
// UPDATE
ok, err = authCheck.CanIUpdate(context.TODO(), kind, namespace)
ok, err = authCheck.CanIUpdate(ctx, kind, namespace)
if err != nil {
// machinery error
return err
}
if !ok {
return fmt.Errorf("kyverno does not have permissions to 'update' resource %s/%s. Update permissions in ClusterRole 'kyverno:generate'", kind, namespace)
return fmt.Errorf("kyverno does not have permissions to 'update' resource %s/%s. Update permissions in ClusterRole 'kyverno:background-controller:additional'", kind, namespace)
}
// GET
ok, err = authCheck.CanIGet(context.TODO(), kind, namespace)
ok, err = authCheck.CanIGet(ctx, kind, namespace)
if err != nil {
// machinery error
return err
}
if !ok {
return fmt.Errorf("kyverno does not have permissions to 'get' resource %s/%s. Update permissions in ClusterRole 'kyverno:generate'", kind, namespace)
return fmt.Errorf("kyverno does not have permissions to 'get' resource %s/%s. Update permissions in ClusterRole 'kyverno:background-controller:additional'", kind, namespace)
}
// DELETE
ok, err = authCheck.CanIDelete(context.TODO(), kind, namespace)
ok, err = authCheck.CanIDelete(ctx, kind, namespace)
if err != nil {
// machinery error
return err
}
if !ok {
return fmt.Errorf("kyverno does not have permissions to 'delete' resource %s/%s. Update permissions in ClusterRole 'kyverno:generate'", kind, namespace)
return fmt.Errorf("kyverno does not have permissions to 'delete' resource %s/%s. Update permissions in ClusterRole 'kyverno:background-controller:additional'", kind, namespace)
}
} else {
g.log.V(4).Info("name & namespace uses variables, so cannot be resolved. Skipping Auth Checks.")

View file

@ -1,6 +1,7 @@
package generate
import (
"context"
"encoding/json"
"testing"
@ -34,7 +35,7 @@ func Test_Validate_Generate(t *testing.T) {
err := json.Unmarshal(rawGenerate, &genRule)
assert.NilError(t, err)
checker := NewFakeGenerate(genRule)
if _, err := checker.Validate(); err != nil {
if _, err := checker.Validate(context.TODO()); err != nil {
assert.Assert(t, err != nil)
}
}
@ -66,7 +67,7 @@ func Test_Validate_Generate_HasAnchors(t *testing.T) {
err = json.Unmarshal(rawGenerate, &genRule)
assert.NilError(t, err)
checker := NewFakeGenerate(genRule)
if _, err := checker.Validate(); err != nil {
if _, err := checker.Validate(context.TODO()); err != nil {
assert.Assert(t, err != nil)
}
@ -83,7 +84,7 @@ func Test_Validate_Generate_HasAnchors(t *testing.T) {
err = json.Unmarshal(rawGenerate, &genRule)
assert.NilError(t, err)
checker = NewFakeGenerate(genRule)
if _, err := checker.Validate(); err != nil {
if _, err := checker.Validate(context.TODO()); err != nil {
assert.Assert(t, err != nil)
}
}

37
pkg/policy/mutate/auth.go Normal file
View file

@ -0,0 +1,37 @@
package mutate
import (
"context"
"github.com/kyverno/kyverno/pkg/auth"
"github.com/kyverno/kyverno/pkg/clients/dclient"
)
type authChecker struct {
client dclient.Interface
}
type AuthChecker interface {
CanICreate(ctx context.Context, kind, namespace, subresource string) (bool, error)
CanIUpdate(ctx context.Context, kind, namespace, subresource string) (bool, error)
CanIGet(ctx context.Context, kind, namespace, subresource string) (bool, error)
}
func newAuthChecker(client dclient.Interface) AuthChecker {
return &authChecker{client: client}
}
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)
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)
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)
return checker.RunAccessCheck(ctx)
}

View file

@ -1,27 +1,34 @@
package mutate
import (
"context"
"fmt"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/clients/dclient"
"github.com/kyverno/kyverno/pkg/engine/variables/regex"
"github.com/kyverno/kyverno/pkg/utils/api"
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
"go.uber.org/multierr"
v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
)
// Mutate provides implementation to validate 'mutate' rule
type Mutate struct {
mutation kyvernov1.Mutation
mutation kyvernov1.Mutation
authChecker AuthChecker
}
// NewMutateFactory returns a new instance of Mutate validation checker
func NewMutateFactory(m kyvernov1.Mutation) *Mutate {
func NewMutateFactory(m kyvernov1.Mutation, client dclient.Interface) *Mutate {
return &Mutate{
mutation: m,
mutation: m,
authChecker: newAuthChecker(client),
}
}
// Validate validates the 'mutate' rule
func (m *Mutate) Validate() (string, error) {
func (m *Mutate) Validate(ctx context.Context) (string, error) {
if m.hasForEach() {
if m.hasPatchStrategicMerge() || m.hasPatchesJSON6902() {
return "foreach", fmt.Errorf("only one of `foreach`, `patchStrategicMerge`, or `patchesJson6902` is allowed")
@ -34,6 +41,11 @@ func (m *Mutate) Validate() (string, error) {
return "foreach", fmt.Errorf("only one of `patchStrategicMerge` or `patchesJson6902` is allowed")
}
if m.mutation.Targets != nil {
if err := m.validateAuth(ctx, m.mutation.Targets); err != nil {
return "targets", fmt.Errorf("auth check fails, require additional privileges, update the ClusterRole 'kyverno:background-controller:additional':%v", err)
}
}
return "", nil
}
@ -77,3 +89,30 @@ func (m *Mutate) hasPatchStrategicMerge() bool {
func (m *Mutate) hasPatchesJSON6902() bool {
return m.mutation.PatchesJSON6902 != ""
}
func (m *Mutate) validateAuth(ctx context.Context, targets []kyvernov1.ResourceSpec) error {
var errs []error
for _, target := range targets {
if !regex.IsVariable(target.Namespace) {
_, _, k, sub := kubeutils.ParseKindSelector(target.Kind)
if ok, err := m.authChecker.CanICreate(ctx, k, target.Namespace, sub); err != nil {
errs = append(errs, err)
} else if !ok {
errs = append(errs, fmt.Errorf("cannot %s %s/%s in namespace %s", "create", k, sub, target.Namespace))
}
if ok, err := m.authChecker.CanIUpdate(ctx, k, target.Namespace, sub); err != nil {
errs = append(errs, err)
} else if !ok {
errs = append(errs, fmt.Errorf("cannot %s %s/%s in namespace %s", "update", k, sub, target.Namespace))
}
if ok, err := m.authChecker.CanIGet(ctx, k, target.Namespace, sub); err != nil {
errs = append(errs, err)
} else if !ok {
errs = append(errs, fmt.Errorf("cannot %s %s/%s in namespace %s", "get", k, sub, target.Namespace))
}
}
}
return multierr.Combine(errs...)
}

View file

@ -252,7 +252,9 @@ func Validate(policy, oldPolicy kyvernov1.PolicyInterface, client dclient.Interf
// validate Cluster Resources in namespaced policy
// For namespaced policy, ClusterResource type field and values are not allowed in match and exclude
if namespaced {
return warnings, checkClusterResourceInMatchAndExclude(rule, clusterResources, policy.GetNamespace(), mock, res)
if err := checkClusterResourceInMatchAndExclude(rule, clusterResources, policy.GetNamespace(), mock, res); err != nil {
return warnings, err
}
}
if err := validateActions(i, &rules[i], client, mock); err != nil {

View file

@ -1,6 +1,7 @@
package validate
import (
"context"
"fmt"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
@ -24,7 +25,7 @@ func NewValidateFactory(rule *kyvernov1.Validation) *Validate {
}
// Validate validates the 'validate' rule
func (v *Validate) Validate() (string, error) {
func (v *Validate) Validate(ctx context.Context) (string, error) {
if err := v.validateElements(); err != nil {
return "", err
}

View file

@ -1,6 +1,7 @@
package validate
import (
"context"
"encoding/json"
"testing"
@ -17,7 +18,7 @@ func Test_Validate_OverlayPattern_Empty(t *testing.T) {
assert.NilError(t, err)
checker := NewValidateFactory(&validation)
if _, err := checker.Validate(); err != nil {
if _, err := checker.Validate(context.TODO()); err != nil {
assert.Assert(t, err != nil)
}
}
@ -31,7 +32,7 @@ func Test_Validate_OverlayPattern_Nil_PatternAnypattern(t *testing.T) {
err := json.Unmarshal(rawValidation, &validation)
assert.NilError(t, err)
checker := NewValidateFactory(&validation)
if _, err := checker.Validate(); err != nil {
if _, err := checker.Validate(context.TODO()); err != nil {
assert.Assert(t, err != nil)
}
}
@ -69,7 +70,7 @@ func Test_Validate_OverlayPattern_Exist_PatternAnypattern(t *testing.T) {
err := json.Unmarshal(rawValidation, &validation)
assert.NilError(t, err)
checker := NewValidateFactory(&validation)
if _, err := checker.Validate(); err != nil {
if _, err := checker.Validate(context.TODO()); err != nil {
assert.Assert(t, err != nil)
}
}
@ -107,7 +108,7 @@ func Test_Validate_OverlayPattern_Valid(t *testing.T) {
err := json.Unmarshal(rawValidation, &validation)
assert.NilError(t, err)
checker := NewValidateFactory(&validation)
if _, err := checker.Validate(); err != nil {
if _, err := checker.Validate(context.TODO()); err != nil {
assert.NilError(t, err)
}
}
@ -140,7 +141,7 @@ func Test_Validate_ExistingAnchor_AnchorOnMap(t *testing.T) {
err := json.Unmarshal(rawValidation, &validation)
assert.NilError(t, err)
checker := NewValidateFactory(&validation)
if _, err := checker.Validate(); err != nil {
if _, err := checker.Validate(context.TODO()); err != nil {
assert.Assert(t, err != nil)
}
}
@ -170,7 +171,7 @@ func Test_Validate_ExistingAnchor_AnchorOnString(t *testing.T) {
err := json.Unmarshal(rawValidation, &validation)
assert.NilError(t, err)
checker := NewValidateFactory(&validation)
if _, err := checker.Validate(); err != nil {
if _, err := checker.Validate(context.TODO()); err != nil {
assert.Assert(t, err != nil)
}
}
@ -203,7 +204,7 @@ func Test_Validate_ExistingAnchor_Valid(t *testing.T) {
err = json.Unmarshal(rawValidation, &validation)
assert.NilError(t, err)
checker := NewValidateFactory(&validation)
if _, err := checker.Validate(); err != nil {
if _, err := checker.Validate(context.TODO()); err != nil {
assert.Assert(t, err != nil)
}
rawValidation = []byte(`
@ -228,7 +229,7 @@ func Test_Validate_ExistingAnchor_Valid(t *testing.T) {
err = json.Unmarshal(rawValidation, &validation)
assert.NilError(t, err)
checker = NewValidateFactory(&validation)
if _, err := checker.Validate(); err != nil {
if _, err := checker.Validate(context.TODO()); err != nil {
assert.Assert(t, err != nil)
}
@ -269,7 +270,7 @@ func Test_Validate_Validate_ValidAnchor(t *testing.T) {
assert.NilError(t, err)
checker := NewValidateFactory(&validate)
if _, err := checker.Validate(); err != nil {
if _, err := checker.Validate(context.TODO()); err != nil {
assert.NilError(t, err)
}
@ -291,7 +292,7 @@ func Test_Validate_Validate_ValidAnchor(t *testing.T) {
assert.NilError(t, err)
checker = NewValidateFactory(&validate)
if _, err := checker.Validate(); err != nil {
if _, err := checker.Validate(context.TODO()); err != nil {
assert.NilError(t, err)
}
}
@ -318,7 +319,7 @@ func Test_Validate_Validate_Mismatched(t *testing.T) {
err := json.Unmarshal(rawValidate, &validate)
assert.NilError(t, err)
checker := NewValidateFactory(&validate)
if _, err := checker.Validate(); err != nil {
if _, err := checker.Validate(context.TODO()); err != nil {
assert.Assert(t, err != nil)
}
}
@ -348,7 +349,7 @@ func Test_Validate_Validate_Unsupported(t *testing.T) {
err = json.Unmarshal(rawValidate, &validate)
assert.NilError(t, err)
checker := NewValidateFactory(&validate)
if _, err := checker.Validate(); err != nil {
if _, err := checker.Validate(context.TODO()); err != nil {
assert.Assert(t, err != nil)
}
@ -374,7 +375,7 @@ func Test_Validate_Validate_Unsupported(t *testing.T) {
assert.NilError(t, err)
checker = NewValidateFactory(&validate)
if _, err := checker.Validate(); err != nil {
if _, err := checker.Validate(context.TODO()); err != nil {
assert.Assert(t, err != nil)
}

View file

@ -32,11 +32,10 @@ spec:
example.com/sm-sync: "true"
generate:
apiVersion: v1
kind: Secret
kind: ConfigMap
name: "{{request.object.metadata.name}}-modify"
namespace: match-trigger-namespace-ns
synchronize: true
data:
type: Opaque
data:
modify: Zm9v

View file

@ -1,8 +1,7 @@
apiVersion: v1
data:
modify: Zm9v
kind: Secret
kind: ConfigMap
metadata:
name: regcred-modify
namespace: match-trigger-namespace-ns
type: Opaque
namespace: match-trigger-namespace-ns

View file

@ -37,11 +37,10 @@ spec:
example.com/sm-sync: "true"
generate:
apiVersion: v1
kind: Secret
kind: ConfigMap
name: "{{request.object.metadata.name}}-modify"
namespace: non-match-trigger-namespace-ns
synchronize: true
data:
type: Opaque
data:
modify: Zm9v

View file

@ -1,8 +1,7 @@
apiVersion: v1
data:
modify: Zm9v
kind: Secret
kind: ConfigMap
metadata:
name: regcred-modify
namespace: non-match-trigger-namespace-ns
type: Opaque
namespace: non-match-trigger-namespace-ns

View file

@ -3,7 +3,7 @@ kind: ClusterPolicy
metadata:
name: generate-update-rule-spec
spec:
generateExistingOnPolicyUpdate: false
generateExisting: false
rules:
- name: k-kafka-address
match:

View file

@ -3,7 +3,7 @@ kind: ClusterPolicy
metadata:
name: generate-update-rule-spec
spec:
generateExistingOnPolicyUpdate: false
generateExisting: false
rules:
- name: k-kafka-address
match:

View file

@ -3,7 +3,7 @@ kind: ClusterPolicy
metadata:
name: generate-update-rule-spec
spec:
generateExistingOnPolicyUpdate: false
generateExisting: false
rules:
- name: k-kafka-address
match:

View file

@ -3,7 +3,7 @@ kind: ClusterPolicy
metadata:
name: generate-update-rule-spec
spec:
generateExistingOnPolicyUpdate: false
generateExisting: false
rules:
- name: k-kafka-address
match:

View file

@ -3,7 +3,7 @@ kind: ClusterPolicy
metadata:
name: generate-update-rule-spec
spec:
generateExistingOnPolicyUpdate: false
generateExisting: false
rules:
- name: i-changed-this
match:

View file

@ -3,7 +3,7 @@ kind: ClusterPolicy
metadata:
name: generate-update-rule-spec
spec:
generateExistingOnPolicyUpdate: false
generateExisting: false
rules:
- name: k-kafka-address
match:

View file

@ -0,0 +1,5 @@
apiVersion: kuttl.dev/v1beta1
kind: TestStep
apply:
- file: policy.yaml
shouldFail: true

View file

@ -0,0 +1,16 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: kyverno:background-controller:additional
rules:
- apiGroups:
- '*'
resources:
- serviceaccounts
verbs:
- create
- update
- patch
- delete
- get
- list

View file

@ -0,0 +1,6 @@
apiVersion: kuttl.dev/v1beta1
kind: TestStep
apply:
- policy.yaml
assert:
- policy-assert.yaml

View file

@ -0,0 +1,27 @@
## reset changed clusterrole for the rest of the tests
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: kyverno:background-controller:additional
rules:
- apiGroups:
- '*'
resources:
- configmaps
- networkpolicies
- resourcequotas
- secrets
- roles
- rolebindings
- limitranges
- namespaces
- nodes
- nodes/status
- pods
verbs:
- create
- update
- patch
- delete
- get
- list

View file

@ -0,0 +1,12 @@
## Description
This test ensures that a generate policy is denied when it does not have corresponding permissions to generate the downstream resource.
## Expected Behavior
The test fails if the policy creation is allowed, otherwise passes.
## Reference Issue(s)
https://github.com/kyverno/kyverno/issues/6584

View file

@ -0,0 +1,9 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: cpol-validate-create-sa-permission
status:
conditions:
- reason: Succeeded
status: "True"
type: Ready

View file

@ -0,0 +1,21 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: cpol-validate-create-sa-permission
spec:
rules:
- name: clone-secret
match:
any:
- resources:
kinds:
- ConfigMap
generate:
apiVersion: v1
kind: ServiceAccount
name: cpol-validate-create-sa-permission-sa
namespace: default
synchronize: true
clone:
namespace: default
name: regcred

View file

@ -4,7 +4,7 @@ metadata:
name: generate-update-rule-spec
namespace: default
spec:
generateExistingOnPolicyUpdate: false
generateExisting: false
rules:
- name: k-kafka-address
match:

View file

@ -4,7 +4,7 @@ metadata:
name: generate-update-rule-spec
namespace: default
spec:
generateExistingOnPolicyUpdate: false
generateExisting: false
rules:
- name: k-kafka-address
match:

View file

@ -4,7 +4,7 @@ metadata:
name: generate-update-rule-spec
namespace: default
spec:
generateExistingOnPolicyUpdate: false
generateExisting: false
rules:
- name: k-kafka-address
match:

View file

@ -4,7 +4,7 @@ metadata:
name: generate-update-rule-spec
namespace: default
spec:
generateExistingOnPolicyUpdate: false
generateExisting: false
rules:
- name: k-kafka-address
match:

View file

@ -4,7 +4,7 @@ metadata:
name: generate-update-rule-spec
namespace: default
spec:
generateExistingOnPolicyUpdate: false
generateExisting: false
rules:
- name: i-changed-this
match:

View file

@ -4,7 +4,7 @@ metadata:
name: generate-update-rule-spec
namespace: default
spec:
generateExistingOnPolicyUpdate: false
generateExisting: false
rules:
- name: k-kafka-address
match:

View file

@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: pol-validate-create-sa-permission-ns

View file

@ -0,0 +1,5 @@
apiVersion: kuttl.dev/v1beta1
kind: TestStep
apply:
- file: policy.yaml
shouldFail: true

View file

@ -0,0 +1,16 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: kyverno:background-controller:additional
rules:
- apiGroups:
- '*'
resources:
- serviceaccounts
verbs:
- create
- update
- patch
- delete
- get
- list

View file

@ -0,0 +1,6 @@
apiVersion: kuttl.dev/v1beta1
kind: TestStep
apply:
- policy.yaml
assert:
- policy-assert.yaml

View file

@ -0,0 +1,27 @@
## reset changed clusterrole for the rest of the tests
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: kyverno:background-controller:additional
rules:
- apiGroups:
- '*'
resources:
- configmaps
- networkpolicies
- resourcequotas
- secrets
- roles
- rolebindings
- limitranges
- namespaces
- nodes
- nodes/status
- pods
verbs:
- create
- update
- patch
- delete
- get
- list

View file

@ -0,0 +1,12 @@
## Description
This test ensures that a generate policy is denied when it does not have corresponding permissions to generate the downstream resource.
## Expected Behavior
The test fails if the policy creation is allowed, otherwise passes.
## Reference Issue(s)
https://github.com/kyverno/kyverno/issues/6584

View file

@ -0,0 +1,10 @@
apiVersion: kyverno.io/v1
kind: Policy
metadata:
name: pol-validate-create-sa-permission
namespace: pol-validate-create-sa-permission-ns
status:
conditions:
- reason: Succeeded
status: "True"
type: Ready

View file

@ -0,0 +1,22 @@
apiVersion: kyverno.io/v1
kind: Policy
metadata:
name: pol-validate-create-sa-permission
namespace: pol-validate-create-sa-permission-ns
spec:
rules:
- name: clone-secret
match:
any:
- resources:
kinds:
- ConfigMap
generate:
apiVersion: v1
kind: ServiceAccount
name: cpol-validate-create-sa-permission-sa
namespace: pol-validate-create-sa-permission-ns
synchronize: true
clone:
namespace: pol-validate-create-sa-permission-ns
name: regcred

View file

@ -0,0 +1,5 @@
apiVersion: kuttl.dev/v1beta1
kind: TestStep
apply:
- file: policy.yaml
shouldFail: true

View file

@ -0,0 +1,16 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: kyverno:background-controller:additional
rules:
- apiGroups:
- '*'
resources:
- serviceaccounts
verbs:
- create
- update
- patch
- delete
- get
- list

View file

@ -0,0 +1,6 @@
apiVersion: kuttl.dev/v1beta1
kind: TestStep
apply:
- policy.yaml
assert:
- policy-assert.yaml

View file

@ -0,0 +1,27 @@
## reset changed clusterrole for the rest of the tests
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: kyverno:background-controller:additional
rules:
- apiGroups:
- '*'
resources:
- configmaps
- networkpolicies
- resourcequotas
- secrets
- roles
- rolebindings
- limitranges
- namespaces
- nodes
- nodes/status
- pods
verbs:
- create
- update
- patch
- delete
- get
- list

View file

@ -0,0 +1,12 @@
## Description
This test ensures that a mutate existing policy is denied when it does not have corresponding permissions to generate the downstream resource.
## Expected Behavior
The test fails if the policy creation is allowed, otherwise passes.
## Reference Issue(s)
https://github.com/kyverno/kyverno/issues/6584

View file

@ -0,0 +1,9 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: cpol-mutate-existing-auth-check
status:
conditions:
- reason: Succeeded
status: "True"
type: Ready

View file

@ -0,0 +1,22 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: cpol-mutate-existing-auth-check
spec:
mutateExistingOnPolicyUpdate: true
background: false
rules:
- name: label-privileged-namespaces
match:
any:
- resources:
kinds:
- Namespace
mutate:
targets:
- apiVersion: v1
kind: ServiceAccount
patchStrategicMerge:
metadata:
labels:
foo: bar

View file

@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: pol-mutate-existing-auth-check-ns

View file

@ -0,0 +1,5 @@
apiVersion: kuttl.dev/v1beta1
kind: TestStep
apply:
- file: policy.yaml
shouldFail: true

View file

@ -0,0 +1,16 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: kyverno:background-controller:additional
rules:
- apiGroups:
- '*'
resources:
- serviceaccounts
verbs:
- create
- update
- patch
- delete
- get
- list

View file

@ -0,0 +1,6 @@
apiVersion: kuttl.dev/v1beta1
kind: TestStep
apply:
- policy.yaml
assert:
- policy-assert.yaml

View file

@ -0,0 +1,27 @@
## reset changed clusterrole for the rest of the tests
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: kyverno:background-controller:additional
rules:
- apiGroups:
- '*'
resources:
- configmaps
- networkpolicies
- resourcequotas
- secrets
- roles
- rolebindings
- limitranges
- namespaces
- nodes
- nodes/status
- pods
verbs:
- create
- update
- patch
- delete
- get
- list

View file

@ -0,0 +1,12 @@
## Description
This test ensures that a mutate existing policy is denied when it does not have corresponding permissions to generate the downstream resource.
## Expected Behavior
The test fails if the policy creation is allowed, otherwise passes.
## Reference Issue(s)
https://github.com/kyverno/kyverno/issues/6584

View file

@ -0,0 +1,10 @@
apiVersion: kyverno.io/v1
kind: Policy
metadata:
name: pol-mutate-existing-auth-check
namespace: pol-mutate-existing-auth-check-ns
status:
conditions:
- reason: Succeeded
status: "True"
type: Ready

View file

@ -0,0 +1,24 @@
apiVersion: kyverno.io/v1
kind: Policy
metadata:
name: pol-mutate-existing-auth-check
namespace: pol-mutate-existing-auth-check-ns
spec:
mutateExistingOnPolicyUpdate: true
background: false
rules:
- name: label-privileged-namespaces
match:
any:
- resources:
kinds:
- ConfigMap
mutate:
targets:
- apiVersion: v1
kind: ServiceAccount
namespace: pol-mutate-existing-auth-check-ns
patchStrategicMerge:
metadata:
labels:
foo: bar

View file

@ -0,0 +1,16 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: kyverno:background-controller:additional
rules:
- apiGroups:
- '*'
resources:
- deployments
verbs:
- create
- update
- patch
- delete
- get
- list

View file

@ -0,0 +1,28 @@
## reset changed clusterrole for the rest of the tests
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: kyverno:background-controller:additional
rules:
- apiGroups:
- '*'
resources:
- configmaps
- networkpolicies
- resourcequotas
- secrets
- roles
- rolebindings
- limitranges
- namespaces
- nodes
- nodes/status
- pods
- deployments
verbs:
- create
- update
- patch
- delete
- get
- list