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

fix: generate policy validation to prevent endless loop (#7026)

* refactor policy validation

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

* add loop check for generate

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

* add kuttl tests

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

* linter fixes

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

* linter fixes

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

---------

Signed-off-by: ShutingZhao <shuting@nirmata.com>
This commit is contained in:
shuting 2023-04-28 21:54:17 +08:00 committed by GitHub
parent 0edbb3038a
commit f87b0204e6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 304 additions and 223 deletions

View file

@ -21,8 +21,8 @@ import (
"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"
gitutils "github.com/kyverno/kyverno/pkg/utils/git"
policyvalidation "github.com/kyverno/kyverno/pkg/validation/policy"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/client-go/dynamic"
@ -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, config.KyvernoUserName(config.KyvernoServiceAccountName()))
_, err := policyvalidation.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

@ -14,8 +14,8 @@ import (
"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"
policyvalidation "github.com/kyverno/kyverno/pkg/validation/policy"
"github.com/spf13/cobra"
"go.uber.org/multierr"
"sigs.k8s.io/controller-runtime/pkg/log"

View file

@ -22,7 +22,7 @@ import (
"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"
policyvalidation "github.com/kyverno/kyverno/pkg/validation/policy"
"golang.org/x/exp/slices"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@ -179,7 +179,7 @@ func applyPoliciesFromPath(
}
for _, policy := range policies {
_, err := policy2.Validate(policy, nil, nil, true, openApiManager, config.KyvernoUserName(config.KyvernoServiceAccountName()))
_, err := policyvalidation.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

@ -5,8 +5,8 @@ import (
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
kyvernov1alpha2 "github.com/kyverno/kyverno/api/kyverno/v1alpha2"
"github.com/kyverno/kyverno/pkg/autogen"
"github.com/kyverno/kyverno/pkg/policy"
datautils "github.com/kyverno/kyverno/pkg/utils/data"
policyvalidation "github.com/kyverno/kyverno/pkg/validation/policy"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/sets"
)
@ -15,7 +15,7 @@ func CanBackgroundProcess(p kyvernov1.PolicyInterface) bool {
if !p.BackgroundProcessingEnabled() {
return false
}
if err := policy.ValidateVariables(p, true); err != nil {
if err := policyvalidation.ValidateVariables(p, true); err != nil {
return false
}
return true

View file

@ -24,6 +24,7 @@ import (
datautils "github.com/kyverno/kyverno/pkg/utils/data"
engineutils "github.com/kyverno/kyverno/pkg/utils/engine"
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
policyvalidation "github.com/kyverno/kyverno/pkg/validation/policy"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -147,7 +148,7 @@ func (pc *policyController) canBackgroundProcess(p kyvernov1.PolicyInterface) bo
}
}
if err := ValidateVariables(p, true); err != nil {
if err := policyvalidation.ValidateVariables(p, true); err != nil {
logger.V(4).Info("policy cannot be processed in the background")
return false
}

View file

@ -16,7 +16,7 @@ var forbidden = []*regexp.Regexp{
regexp.MustCompile(`[^\.](request.clusterRoles)\b`),
}
// ContainsUserVariables returns error if variable that does not start from request.object
// containsUserVariables returns error if variable that does not start from request.object
func containsUserVariables(policy kyvernov1.PolicyInterface, vars [][]string) error {
rules := autogen.ComputeRules(policy)
for idx := range rules {

View file

@ -0,0 +1,140 @@
package policy
import (
"crypto/md5" //nolint:gosec
"encoding/hex"
"encoding/json"
"errors"
"fmt"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/utils/strings/slices"
)
func immutableGenerateFields(new, old kyvernov1.PolicyInterface) error {
if new == nil || old == nil {
return nil
}
if !new.GetSpec().HasGenerate() {
return nil
}
oldRuleHashes, err := buildHashes(old.GetSpec().Rules)
if err != nil {
return err
}
newRuleHashes, err := buildHashes(new.GetSpec().Rules)
if err != nil {
return err
}
switch len(old.GetSpec().Rules) <= len(new.GetSpec().Rules) {
case true:
if newRuleHashes.IsSuperset(oldRuleHashes) {
return nil
} else {
return errors.New("change of immutable fields for a generate rule is disallowed")
}
case false:
if oldRuleHashes.IsSuperset(newRuleHashes) {
return nil
} else {
return errors.New("rule deletion - change of immutable fields for a generate rule is disallowed")
}
}
return nil
}
// checkClusterResourceInMatchAndExclude returns false if namespaced ClusterPolicy contains cluster wide resources in
// Match and Exclude block
func checkClusterResourceInMatchAndExclude(rule kyvernov1.Rule, clusterResources sets.Set[string], policyNamespace string, mock bool, res []*metav1.APIResourceList) error {
if !mock {
// Check for generate policy
// - if resource to be generated is namespaced resource then the namespace field
// should be mentioned
// - if resource to be generated is non namespaced resource then the namespace field
// should not be mentioned
if rule.HasGenerate() {
generateResourceKind := rule.Generation.Kind
for _, resList := range res {
for _, r := range resList.APIResources {
if r.Kind == generateResourceKind {
if r.Namespaced {
if rule.Generation.Namespace == "" {
return fmt.Errorf("path: spec.rules[%v]: please mention the namespace to generate a namespaced resource", rule.Name)
}
if rule.Generation.Namespace != policyNamespace {
return fmt.Errorf("path: spec.rules[%v]: a namespaced policy cannot generate resources in other namespaces, expected: %v, received: %v", rule.Name, policyNamespace, rule.Generation.Namespace)
}
if rule.Generation.Clone.Name != "" {
if rule.Generation.Clone.Namespace != policyNamespace {
return fmt.Errorf("path: spec.rules[%v]: a namespaced policy cannot clone resources to or from other namespaces, expected: %v, received: %v", rule.Name, policyNamespace, rule.Generation.Clone.Namespace)
}
}
} else {
if rule.Generation.Namespace != "" {
return fmt.Errorf("path: spec.rules[%v]: do not mention the namespace to generate a non namespaced resource", rule.Name)
}
if policyNamespace != "" {
return fmt.Errorf("path: spec.rules[%v]: a namespaced policy cannot generate cluster-wide resources", rule.Name)
}
}
} else if len(rule.Generation.CloneList.Kinds) != 0 {
for _, kind := range rule.Generation.CloneList.Kinds {
_, splitkind := kubeutils.GetKindFromGVK(kind)
if r.Kind == splitkind {
if r.Namespaced {
if rule.Generation.CloneList.Namespace != policyNamespace {
return fmt.Errorf("path: spec.rules[%v]: a namespaced policy cannot clone resource in other namespace, expected: %v, received: %v", rule.Name, policyNamespace, rule.Generation.Namespace)
}
} else {
if policyNamespace != "" {
return fmt.Errorf("path: spec.rules[%v]: a namespaced policy cannot generate cluster-wide resources", rule.Name)
}
}
}
}
}
}
}
}
}
return nil
}
func loopInGenerate(rule kyvernov1.Rule) error {
if !rule.HasGenerate() {
return nil
}
if slices.Contains(rule.MatchResources.GetKinds(), rule.Generation.Kind) {
return fmt.Errorf("the rule would result in an endless loop, the trigger and the target resources are the same kind: %s", rule.Generation.Kind)
}
return nil
}
func resetMutableFields(rule kyvernov1.Rule) *kyvernov1.Rule {
new := new(kyvernov1.Rule)
rule.DeepCopyInto(new)
new.Generation.Synchronize = true
new.Generation.SetData(nil)
return new
}
func buildHashes(rules []kyvernov1.Rule) (sets.Set[string], error) {
ruleHashes := sets.New[string]()
for _, rule := range rules {
r := resetMutableFields(rule)
data, err := json.Marshal(r)
if err != nil {
return ruleHashes, fmt.Errorf("failed to create hash from the generate rule %v", err)
}
hash := md5.Sum(data) //nolint:gosec
ruleHashes.Insert(hex.EncodeToString(hash[:]))
}
return ruleHashes, nil
}

View file

@ -2,8 +2,6 @@ package policy
import (
"context"
"crypto/md5" //nolint:gosec
"encoding/hex"
"encoding/json"
"errors"
"fmt"
@ -185,79 +183,47 @@ func Validate(policy, oldPolicy kyvernov1.PolicyInterface, client dclient.Interf
}
}
if err := immutableGenerateFields(policy, oldPolicy); err != nil {
return warnings, err
}
rules := autogen.ComputeRules(policy)
rulesPath := specPath.Child("rules")
for _, rule := range rules {
// Validate Kind with match resource kinds
for i, rule := range rules {
match := rule.MatchResources
exclude := rule.ExcludeResources
for _, value := range match.Any {
wildcardErr := validateWildcard(value.ResourceDescription.Kinds, spec, rule)
if wildcardErr != nil {
return warnings, wildcardErr
}
if !slices.Contains(value.ResourceDescription.Kinds, "*") {
err := validateKinds(value.ResourceDescription.Kinds, mock, background, rule.HasValidate(), client)
if err != nil {
return warnings, fmt.Errorf("the kind defined in the any match resource is invalid: %w", err)
}
for j, value := range match.Any {
if err := validateKinds(value.ResourceDescription.Kinds, rule, mock, background, client); err != nil {
return warnings, fmt.Errorf("path: spec.rules[%d].match.any[%d].kinds: %v", i, j, err)
}
}
for _, value := range match.All {
wildcardErr := validateWildcard(value.ResourceDescription.Kinds, spec, rule)
if wildcardErr != nil {
return warnings, wildcardErr
}
if !slices.Contains(value.ResourceDescription.Kinds, "*") {
err := validateKinds(value.ResourceDescription.Kinds, mock, background, rule.HasValidate(), client)
if err != nil {
return warnings, fmt.Errorf("the kind defined in the all match resource is invalid: %w", err)
}
for j, value := range match.All {
if err := validateKinds(value.ResourceDescription.Kinds, rule, mock, background, client); err != nil {
return warnings, fmt.Errorf("path: spec.rules[%d].match.all[%d].kinds: %v", i, j, err)
}
}
for _, value := range exclude.Any {
wildcardErr := validateWildcard(value.ResourceDescription.Kinds, spec, rule)
if wildcardErr != nil {
return warnings, wildcardErr
}
if !slices.Contains(value.ResourceDescription.Kinds, "*") {
err := validateKinds(value.ResourceDescription.Kinds, mock, background, rule.HasValidate(), client)
if err != nil {
return warnings, fmt.Errorf("the kind defined in the any exclude resource is invalid: %w", err)
}
for j, value := range exclude.Any {
if err := validateKinds(value.ResourceDescription.Kinds, rule, mock, background, client); err != nil {
return warnings, fmt.Errorf("path: spec.rules[%d].exclude.any[%d].kinds: %v", i, j, err)
}
}
for _, value := range exclude.All {
wildcardErr := validateWildcard(value.ResourceDescription.Kinds, spec, rule)
if wildcardErr != nil {
return warnings, wildcardErr
}
if !slices.Contains(value.ResourceDescription.Kinds, "*") {
err := validateKinds(value.ResourceDescription.Kinds, mock, background, rule.HasValidate(), client)
if err != nil {
return warnings, fmt.Errorf("the kind defined in the all exclude resource is invalid: %w", err)
}
for j, value := range exclude.All {
if err := validateKinds(value.ResourceDescription.Kinds, rule, mock, background, client); err != nil {
return warnings, fmt.Errorf("path: spec.rules[%d].exclude.all[%d].kinds: %v", i, j, err)
}
}
if !slices.Contains(rule.MatchResources.Kinds, "*") {
err := validateKinds(rule.MatchResources.Kinds, mock, background, rule.HasValidate(), client)
if err != nil {
return warnings, fmt.Errorf("match resource kind is invalid: %w", err)
}
err = validateKinds(rule.ExcludeResources.Kinds, mock, background, rule.HasValidate(), client)
if err != nil {
return warnings, fmt.Errorf("exclude resource kind is invalid: %w", err)
}
} else {
wildcardErr := validateWildcard(rule.MatchResources.Kinds, spec, rule)
if wildcardErr != nil {
return warnings, wildcardErr
}
wildcardErr = validateWildcard(rule.ExcludeResources.Kinds, spec, rule)
if wildcardErr != nil {
return warnings, wildcardErr
}
if err := validateKinds(rule.MatchResources.Kinds, rule, mock, background, client); err != nil {
return warnings, fmt.Errorf("path: spec.rules[%d].match.kinds: %v", i, err)
}
if err := validateKinds(rule.ExcludeResources.Kinds, rule, mock, background, client); err != nil {
return warnings, fmt.Errorf("path: spec.rules[%d].exclude.kinds: %v", i, err)
}
if err := loopInGenerate(rule); err != nil {
return warnings, fmt.Errorf("path: spec.rules[%d]: %v", i, err)
}
}
@ -316,10 +282,6 @@ func Validate(policy, oldPolicy kyvernov1.PolicyInterface, client dclient.Interf
}
}
if err := immutableGenerateFields(policy, oldPolicy); err != nil {
return warnings, err
}
// validate Cluster Resources in namespaced policy
// For namespaced policy, ClusterResource type field and values are not allowed in match and exclude
if namespaced {
@ -1133,63 +1095,6 @@ func validateMatchedResourceDescription(rd kyvernov1.ResourceDescription) (strin
return "", nil
}
// checkClusterResourceInMatchAndExclude returns false if namespaced ClusterPolicy contains cluster wide resources in
// Match and Exclude block
func checkClusterResourceInMatchAndExclude(rule kyvernov1.Rule, clusterResources sets.Set[string], policyNamespace string, mock bool, res []*metav1.APIResourceList) error {
if !mock {
// Check for generate policy
// - if resource to be generated is namespaced resource then the namespace field
// should be mentioned
// - if resource to be generated is non namespaced resource then the namespace field
// should not be mentioned
if rule.HasGenerate() {
generateResourceKind := rule.Generation.Kind
for _, resList := range res {
for _, r := range resList.APIResources {
if r.Kind == generateResourceKind {
if r.Namespaced {
if rule.Generation.Namespace == "" {
return fmt.Errorf("path: spec.rules[%v]: please mention the namespace to generate a namespaced resource", rule.Name)
}
if rule.Generation.Namespace != policyNamespace {
return fmt.Errorf("path: spec.rules[%v]: a namespaced policy cannot generate resources in other namespaces, expected: %v, received: %v", rule.Name, policyNamespace, rule.Generation.Namespace)
}
if rule.Generation.Clone.Name != "" {
if rule.Generation.Clone.Namespace != policyNamespace {
return fmt.Errorf("path: spec.rules[%v]: a namespaced policy cannot clone resources to or from other namespaces, expected: %v, received: %v", rule.Name, policyNamespace, rule.Generation.Clone.Namespace)
}
}
} else {
if rule.Generation.Namespace != "" {
return fmt.Errorf("path: spec.rules[%v]: do not mention the namespace to generate a non namespaced resource", rule.Name)
}
if policyNamespace != "" {
return fmt.Errorf("path: spec.rules[%v]: a namespaced policy cannot generate cluster-wide resources", rule.Name)
}
}
} else if len(rule.Generation.CloneList.Kinds) != 0 {
for _, kind := range rule.Generation.CloneList.Kinds {
_, splitkind := kubeutils.GetKindFromGVK(kind)
if r.Kind == splitkind {
if r.Namespaced {
if rule.Generation.CloneList.Namespace != policyNamespace {
return fmt.Errorf("path: spec.rules[%v]: a namespaced policy cannot clone resource in other namespace, expected: %v, received: %v", rule.Name, policyNamespace, rule.Generation.Namespace)
}
} else {
if policyNamespace != "" {
return fmt.Errorf("path: spec.rules[%v]: a namespaced policy cannot generate cluster-wide resources", rule.Name)
}
}
}
}
}
}
}
}
}
return nil
}
// jsonPatchOnPod checks if a rule applies JSON patches to Pod
func jsonPatchOnPod(rule kyvernov1.Rule) bool {
if !rule.HasMutate() {
@ -1218,13 +1123,28 @@ func podControllerAutoGenExclusion(policy kyvernov1.PolicyInterface) bool {
return false
}
func validateKinds(kinds []string, rule kyvernov1.Rule, mock, background bool, client dclient.Interface) error {
if err := validateWildcard(kinds, background, rule); err != nil {
return err
}
if slices.Contains(kinds, "*") {
return nil
}
if err := validKinds(kinds, mock, background, rule.HasValidate(), client); err != nil {
return fmt.Errorf("the kind defined in the all match resource is invalid: %w", err)
}
return nil
}
// validateWildcard check for an Match/Exclude block contains "*"
func validateWildcard(kinds []string, spec *kyvernov1.Spec, rule kyvernov1.Rule) error {
if slices.Contains(kinds, "*") && spec.BackgroundProcessingEnabled() {
func validateWildcard(kinds []string, background bool, rule kyvernov1.Rule) error {
if slices.Contains(kinds, "*") && background {
return fmt.Errorf("wildcard policy not allowed in background mode. Set spec.background=false to disable background mode for this policy rule ")
}
if slices.Contains(kinds, "*") && len(kinds) > 1 {
return fmt.Errorf("wildard policy can not deal more than one kind")
return fmt.Errorf("wildcard policy can not deal with more than one kind")
}
if slices.Contains(kinds, "*") {
if rule.HasGenerate() || rule.HasVerifyImages() || rule.Validation.ForEachValidation != nil {
@ -1264,10 +1184,10 @@ func validateWildcard(kinds []string, spec *kyvernov1.Spec, rule kyvernov1.Rule)
return nil
}
// validateKinds verifies if an API resource that matches 'kind' is valid kind
// validKinds verifies if an API resource that matches 'kind' is valid kind
// and found in the cache, returns error if not found. It also returns an error if background scanning
// is enabled for a subresource.
func validateKinds(kinds []string, mock, backgroundScanningEnabled, isValidationPolicy bool, client dclient.Interface) error {
func validKinds(kinds []string, mock, backgroundScanningEnabled, isValidationPolicy bool, client dclient.Interface) error {
if !mock {
for _, k := range kinds {
group, version, kind, subresource := kubeutils.ParseKindSelector(k)
@ -1373,60 +1293,3 @@ func checkForStatusSubresource(ruleTypeJson []byte, allKinds []string, warnings
*warnings = append(*warnings, msg)
}
}
func immutableGenerateFields(new, old kyvernov1.PolicyInterface) error {
if new == nil || old == nil {
return nil
}
if !new.GetSpec().HasGenerate() {
return nil
}
oldRuleHashes, err := buildHashes(old.GetSpec().Rules)
if err != nil {
return err
}
newRuleHashes, err := buildHashes(new.GetSpec().Rules)
if err != nil {
return err
}
switch len(old.GetSpec().Rules) <= len(new.GetSpec().Rules) {
case true:
if newRuleHashes.IsSuperset(oldRuleHashes) {
return nil
} else {
return errors.New("change of immutable fields for a generate rule is disallowed")
}
case false:
if oldRuleHashes.IsSuperset(newRuleHashes) {
return nil
} else {
return errors.New("rule deletion - change of immutable fields for a generate rule is disallowed")
}
}
return nil
}
func resetMutableFields(rule kyvernov1.Rule) *kyvernov1.Rule {
new := new(kyvernov1.Rule)
rule.DeepCopyInto(new)
new.Generation.Synchronize = true
new.Generation.SetData(nil)
return new
}
func buildHashes(rules []kyvernov1.Rule) (sets.Set[string], error) {
ruleHashes := sets.New[string]()
for _, rule := range rules {
r := resetMutableFields(rule)
data, err := json.Marshal(r)
if err != nil {
return ruleHashes, fmt.Errorf("failed to create hash from the generate rule %v", err)
}
hash := md5.Sum(data) //nolint:gosec
ruleHashes.Insert(hex.EncodeToString(hash[:]))
}
return ruleHashes, nil
}

View file

@ -7,8 +7,8 @@ import (
"github.com/go-logr/logr"
"github.com/kyverno/kyverno/pkg/clients/dclient"
"github.com/kyverno/kyverno/pkg/openapi"
policyvalidate "github.com/kyverno/kyverno/pkg/policy"
admissionutils "github.com/kyverno/kyverno/pkg/utils/admission"
policyvalidate "github.com/kyverno/kyverno/pkg/validation/policy"
"github.com/kyverno/kyverno/pkg/webhooks"
"github.com/kyverno/kyverno/pkg/webhooks/handlers"
)

View file

@ -33,7 +33,7 @@ spec:
data:
ZK_ADDRESS: "192.168.10.10:2181,192.168.10.11:2181,192.168.10.12:2181"
KAFKA_ADDRESS: "192.168.10.13:9092,192.168.10.14:9092,192.168.10.15:9092"
- name: super-secret
- name: super-configmap
match:
any:
- resources:
@ -44,14 +44,13 @@ spec:
generate:
synchronize: true
apiVersion: v1
kind: Secret
name: supersecret
kind: ConfigMap
name: superconfigmap
namespace: pol-data-sync-delete-rule
data:
kind: Secret
type: Opaque
kind: ConfigMap
metadata:
labels:
somekey: somesecretvalue
somekey: somevalue
data:
mysupersecretkey: bXlzdXBlcnNlY3JldHZhbHVl
key: superconfigmap

View file

@ -1,5 +1,5 @@
apiVersion: kuttl.dev/v1beta1
kind: TestStep
assert:
- secret.yaml
- configmap.yaml
- configmap.yaml
- configmap-remain.yaml

View file

@ -6,7 +6,7 @@ metadata:
spec:
generateExisting: false
rules:
- name: super-secret
- name: super-configmap
match:
any:
- resources:
@ -17,14 +17,13 @@ spec:
generate:
synchronize: true
apiVersion: v1
kind: Secret
name: supersecret
kind: ConfigMap
name: superconfigmap
namespace: pol-data-sync-delete-rule
data:
kind: Secret
type: Opaque
kind: ConfigMap
metadata:
labels:
somekey: somesecretvalue
somekey: somevalue
data:
mysupersecretkey: bXlzdXBlcnNlY3JldHZhbHVl
key: superconfigmap

View file

@ -2,6 +2,6 @@ apiVersion: kuttl.dev/v1beta1
kind: TestStep
apply:
assert:
- secret.yaml
- configmap-remain.yaml
error:
- configmap.yaml

View file

@ -0,0 +1,9 @@
apiVersion: v1
data:
key: superconfigmap
kind: ConfigMap
metadata:
labels:
somekey: somevalue
name: superconfigmap
namespace: pol-data-sync-delete-rule

View file

@ -1,10 +0,0 @@
apiVersion: v1
data:
mysupersecretkey: bXlzdXBlcnNlY3JldHZhbHVl
kind: Secret
metadata:
labels:
somekey: somesecretvalue
name: supersecret
namespace: pol-data-sync-delete-rule
type: Opaque

View file

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

View file

@ -0,0 +1,12 @@
## Description
This test ensures that a generate policy cannot have the same kind defined in the trigger and the target resources. Otherwise it would result in an endless loop.
## Expected Behavior
The test fails if the policy creation is allowed, otherwise passes.
## Reference Issue(s)
https://github.com/kyverno/kyverno/issues/7017

View file

@ -0,0 +1,23 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: cpol-generate-prevent-loop
spec:
rules:
- name: cpol-generate-prevent-loop
match:
any:
- resources:
kinds:
- ConfigMap
operations:
- CREATE
generate:
apiVersion: v1
kind: ConfigMap
name: corp-{{ random('[0-9a-z]{8}') }}
namespace: "{{request.namespace}}"
synchronize: false
data:
data:
foo: bar

View file

@ -11,7 +11,7 @@ spec:
any:
- resources:
kinds:
- ConfigMap
- Secret
generate:
synchronize: true
apiVersion: v1

View file

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

View file

@ -0,0 +1,12 @@
## Description
This test ensures that a generate policy cannot have the same kind defined in the trigger and the target resources. Otherwise it would result in an endless loop.
## Expected Behavior
The test fails if the policy creation is allowed, otherwise passes.
## Reference Issue(s)
https://github.com/kyverno/kyverno/issues/7017

View file

@ -0,0 +1,23 @@
apiVersion: kyverno.io/v1
kind: Policy
metadata:
name: pol-generate-prevent-loop
spec:
rules:
- name: pol-generate-prevent-loop
match:
any:
- resources:
kinds:
- ConfigMap
operations:
- CREATE
generate:
apiVersion: v1
kind: ConfigMap
name: corp-{{ random('[0-9a-z]{8}') }}
namespace: "{{request.namespace}}"
synchronize: false
data:
data:
foo: bar