mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-30 19:35:06 +00:00
fix: autogen not working correctly with cronjob conditions (#7571)
Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
This commit is contained in:
parent
a3bb168d9c
commit
8a62aaa6eb
7 changed files with 94 additions and 367 deletions
|
@ -2,12 +2,9 @@ package autogen
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||||
jsonutils "github.com/kyverno/kyverno/pkg/utils/json"
|
|
||||||
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
|
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
|
||||||
"golang.org/x/exp/slices"
|
"golang.org/x/exp/slices"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
@ -167,55 +164,6 @@ func GetControllers(meta *metav1.ObjectMeta, spec *kyvernov1.Spec) ([]string, []
|
||||||
// copy entire match / exclude block, it's users' responsibility to
|
// copy entire match / exclude block, it's users' responsibility to
|
||||||
// make sure all fields are applicable to pod controllers
|
// make sure all fields are applicable to pod controllers
|
||||||
|
|
||||||
// GenerateRulePatches generates rule for podControllers based on scenario A and C
|
|
||||||
func GenerateRulePatches(spec *kyvernov1.Spec, controllers string) (rulePatches [][]byte, errs []error) {
|
|
||||||
ruleIndex := make(map[string]int)
|
|
||||||
for index, rule := range spec.Rules {
|
|
||||||
ruleIndex[rule.Name] = index
|
|
||||||
}
|
|
||||||
insertIdx := len(spec.Rules)
|
|
||||||
genRules := generateRules(spec, controllers)
|
|
||||||
for i := range genRules {
|
|
||||||
patchPostion := insertIdx
|
|
||||||
convertToPatches := func(genRule kyvernoRule, patchPostion int) []byte {
|
|
||||||
operation := "add"
|
|
||||||
if existingIndex, alreadyExists := ruleIndex[genRule.Name]; alreadyExists {
|
|
||||||
operation = "replace"
|
|
||||||
patchPostion = existingIndex
|
|
||||||
}
|
|
||||||
patch := jsonutils.NewPatchOperation(fmt.Sprintf("/spec/rules/%s", strconv.Itoa(patchPostion)), operation, genRule)
|
|
||||||
pbytes, err := patch.Marshal()
|
|
||||||
if err != nil {
|
|
||||||
errs = append(errs, err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err := jsonutils.CheckPatch(pbytes); err != nil {
|
|
||||||
errs = append(errs, err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return pbytes
|
|
||||||
}
|
|
||||||
genRule := createRule(&genRules[i])
|
|
||||||
if genRule != nil {
|
|
||||||
pbytes := convertToPatches(*genRule, patchPostion)
|
|
||||||
if pbytes != nil {
|
|
||||||
rulePatches = append(rulePatches, pbytes)
|
|
||||||
}
|
|
||||||
insertIdx++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// podControllersKey annotation could be:
|
|
||||||
// scenario A: not exist, set default to "all", which generates on all pod controllers
|
|
||||||
// - if name / selector exist in resource description -> skip
|
|
||||||
// as these fields may not be applicable to pod controllers
|
|
||||||
// scenario B: "none", user explicitly disable this feature -> skip
|
|
||||||
// scenario C: some certain controllers that user set -> generate on defined controllers
|
|
||||||
// copy entire match / exclude block, it's users' responsibility to
|
|
||||||
// make sure all fields are applicable to pod controllers
|
|
||||||
|
|
||||||
// generateRules generates rule for podControllers based on scenario A and C
|
// generateRules generates rule for podControllers based on scenario A and C
|
||||||
func generateRules(spec *kyvernov1.Spec, controllers string) []kyvernov1.Rule {
|
func generateRules(spec *kyvernov1.Spec, controllers string) []kyvernov1.Rule {
|
||||||
var rules []kyvernov1.Rule
|
var rules []kyvernov1.Rule
|
||||||
|
|
|
@ -3,8 +3,6 @@ package autogen
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -310,250 +308,6 @@ func Test_GetRequestedControllers(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_Any(t *testing.T) {
|
|
||||||
dir, err := os.Getwd()
|
|
||||||
baseDir := filepath.Dir(filepath.Dir(dir))
|
|
||||||
assert.NilError(t, err)
|
|
||||||
file, err := os.ReadFile(baseDir + "/test/best_practices/disallow_bind_mounts.yaml")
|
|
||||||
if err != nil {
|
|
||||||
t.Log(err)
|
|
||||||
}
|
|
||||||
policies, _, err := yamlutils.GetPolicy(file)
|
|
||||||
if err != nil {
|
|
||||||
t.Log(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
policy := policies[0]
|
|
||||||
spec := policy.GetSpec()
|
|
||||||
spec.Rules[0].MatchResources.Any = kyverno.ResourceFilters{
|
|
||||||
{
|
|
||||||
ResourceDescription: kyverno.ResourceDescription{
|
|
||||||
Kinds: []string{"Pod"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
rulePatches, errs := GenerateRulePatches(spec, PodControllers)
|
|
||||||
if len(errs) != 0 {
|
|
||||||
t.Log(errs)
|
|
||||||
}
|
|
||||||
expectedPatches := [][]byte{
|
|
||||||
[]byte(`{"path":"/spec/rules/1","op":"add","value":{"name":"autogen-validate-hostPath","match":{"any":[{"resources":{"kinds":["DaemonSet","Deployment","Job","StatefulSet","ReplicaSet","ReplicationController"]}}],"resources":{"kinds":["Pod"]}},"validate":{"message":"Host path volumes are not allowed","pattern":{"spec":{"template":{"spec":{"=(volumes)":[{"X(hostPath)":"null"}]}}}}}}}`),
|
|
||||||
[]byte(`{"path":"/spec/rules/2","op":"add","value":{"name":"autogen-cronjob-validate-hostPath","match":{"any":[{"resources":{"kinds":["CronJob"]}}],"resources":{"kinds":["Pod"]}},"validate":{"message":"Host path volumes are not allowed","pattern":{"spec":{"jobTemplate":{"spec":{"template":{"spec":{"=(volumes)":[{"X(hostPath)":"null"}]}}}}}}}}}`),
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, ep := range expectedPatches {
|
|
||||||
assert.Equal(t, string(rulePatches[i]), string(ep),
|
|
||||||
fmt.Sprintf("unexpected patch: %s\nexpected: %s", rulePatches[i], ep))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_All(t *testing.T) {
|
|
||||||
dir, err := os.Getwd()
|
|
||||||
baseDir := filepath.Dir(filepath.Dir(dir))
|
|
||||||
assert.NilError(t, err)
|
|
||||||
file, err := os.ReadFile(baseDir + "/test/best_practices/disallow_bind_mounts.yaml")
|
|
||||||
if err != nil {
|
|
||||||
t.Log(err)
|
|
||||||
}
|
|
||||||
policies, _, err := yamlutils.GetPolicy(file)
|
|
||||||
if err != nil {
|
|
||||||
t.Log(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
policy := policies[0]
|
|
||||||
spec := policy.GetSpec()
|
|
||||||
spec.Rules[0].MatchResources.All = kyverno.ResourceFilters{
|
|
||||||
{
|
|
||||||
ResourceDescription: kyverno.ResourceDescription{
|
|
||||||
Kinds: []string{"Pod"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
rulePatches, errs := GenerateRulePatches(spec, PodControllers)
|
|
||||||
if len(errs) != 0 {
|
|
||||||
t.Log(errs)
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedPatches := [][]byte{
|
|
||||||
[]byte(`{"path":"/spec/rules/1","op":"add","value":{"name":"autogen-validate-hostPath","match":{"all":[{"resources":{"kinds":["DaemonSet","Deployment","Job","StatefulSet","ReplicaSet","ReplicationController"]}}],"resources":{"kinds":["Pod"]}},"validate":{"message":"Host path volumes are not allowed","pattern":{"spec":{"template":{"spec":{"=(volumes)":[{"X(hostPath)":"null"}]}}}}}}}`),
|
|
||||||
[]byte(`{"path":"/spec/rules/2","op":"add","value":{"name":"autogen-cronjob-validate-hostPath","match":{"all":[{"resources":{"kinds":["CronJob"]}}],"resources":{"kinds":["Pod"]}},"validate":{"message":"Host path volumes are not allowed","pattern":{"spec":{"jobTemplate":{"spec":{"template":{"spec":{"=(volumes)":[{"X(hostPath)":"null"}]}}}}}}}}}`),
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, ep := range expectedPatches {
|
|
||||||
assert.Equal(t, string(rulePatches[i]), string(ep),
|
|
||||||
fmt.Sprintf("unexpected patch: %s\nexpected: %s", rulePatches[i], ep))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Exclude(t *testing.T) {
|
|
||||||
dir, err := os.Getwd()
|
|
||||||
baseDir := filepath.Dir(filepath.Dir(dir))
|
|
||||||
assert.NilError(t, err)
|
|
||||||
file, err := os.ReadFile(baseDir + "/test/best_practices/disallow_bind_mounts.yaml")
|
|
||||||
if err != nil {
|
|
||||||
t.Log(err)
|
|
||||||
}
|
|
||||||
policies, _, err := yamlutils.GetPolicy(file)
|
|
||||||
if err != nil {
|
|
||||||
t.Log(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
policy := policies[0]
|
|
||||||
spec := policy.GetSpec()
|
|
||||||
spec.Rules[0].ExcludeResources.Namespaces = []string{"fake-namespce"}
|
|
||||||
|
|
||||||
rulePatches, errs := GenerateRulePatches(spec, PodControllers)
|
|
||||||
if len(errs) != 0 {
|
|
||||||
t.Log(errs)
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedPatches := [][]byte{
|
|
||||||
[]byte(`{"path":"/spec/rules/1","op":"add","value":{"name":"autogen-validate-hostPath","match":{"resources":{"kinds":["DaemonSet","Deployment","Job","StatefulSet","ReplicaSet","ReplicationController"]}},"exclude":{"resources":{"namespaces":["fake-namespce"]}},"validate":{"message":"Host path volumes are not allowed","pattern":{"spec":{"template":{"spec":{"=(volumes)":[{"X(hostPath)":"null"}]}}}}}}}`),
|
|
||||||
[]byte(`{"path":"/spec/rules/2","op":"add","value":{"name":"autogen-cronjob-validate-hostPath","match":{"resources":{"kinds":["CronJob"]}},"exclude":{"resources":{"namespaces":["fake-namespce"]}},"validate":{"message":"Host path volumes are not allowed","pattern":{"spec":{"jobTemplate":{"spec":{"template":{"spec":{"=(volumes)":[{"X(hostPath)":"null"}]}}}}}}}}}`),
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, ep := range expectedPatches {
|
|
||||||
assert.Equal(t, string(rulePatches[i]), string(ep),
|
|
||||||
fmt.Sprintf("unexpected patch: %s\nexpected: %s", rulePatches[i], ep))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_CronJobOnly(t *testing.T) {
|
|
||||||
controllers := PodControllerCronJob
|
|
||||||
dir, err := os.Getwd()
|
|
||||||
baseDir := filepath.Dir(filepath.Dir(dir))
|
|
||||||
assert.NilError(t, err)
|
|
||||||
file, err := os.ReadFile(baseDir + "/test/best_practices/disallow_bind_mounts.yaml")
|
|
||||||
if err != nil {
|
|
||||||
t.Log(err)
|
|
||||||
}
|
|
||||||
policies, _, err := yamlutils.GetPolicy(file)
|
|
||||||
if err != nil {
|
|
||||||
t.Log(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
policy := policies[0]
|
|
||||||
policy.SetAnnotations(map[string]string{
|
|
||||||
kyverno.PodControllersAnnotation: controllers,
|
|
||||||
})
|
|
||||||
|
|
||||||
rulePatches, errs := GenerateRulePatches(policy.GetSpec(), controllers)
|
|
||||||
if len(errs) != 0 {
|
|
||||||
t.Log(errs)
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedPatches := [][]byte{
|
|
||||||
[]byte(`{"path":"/spec/rules/1","op":"add","value":{"name":"autogen-cronjob-validate-hostPath","match":{"resources":{"kinds":["CronJob"]}},"validate":{"message":"Host path volumes are not allowed","pattern":{"spec":{"jobTemplate":{"spec":{"template":{"spec":{"=(volumes)":[{"X(hostPath)":"null"}]}}}}}}}}}`),
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.DeepEqual(t, rulePatches, expectedPatches)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_ForEachPod(t *testing.T) {
|
|
||||||
dir, err := os.Getwd()
|
|
||||||
baseDir := filepath.Dir(filepath.Dir(dir))
|
|
||||||
assert.NilError(t, err)
|
|
||||||
file, err := os.ReadFile(baseDir + "/test/policy/mutate/policy_mutate_pod_foreach_with_context.yaml")
|
|
||||||
if err != nil {
|
|
||||||
t.Log(err)
|
|
||||||
}
|
|
||||||
policies, _, err := yamlutils.GetPolicy(file)
|
|
||||||
if err != nil {
|
|
||||||
t.Log(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
policy := policies[0]
|
|
||||||
spec := policy.GetSpec()
|
|
||||||
spec.Rules[0].ExcludeResources.Namespaces = []string{"fake-namespce"}
|
|
||||||
|
|
||||||
rulePatches, errs := GenerateRulePatches(spec, PodControllers)
|
|
||||||
if len(errs) != 0 {
|
|
||||||
t.Log(errs)
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedPatches := [][]byte{
|
|
||||||
[]byte(`{"path":"/spec/rules/1","op":"add","value":{"name":"autogen-resolve-image-containers","match":{"resources":{"kinds":["DaemonSet","Deployment","Job","StatefulSet","ReplicaSet","ReplicationController"]}},"exclude":{"resources":{"namespaces":["fake-namespce"]}},"preconditions":{"all":[{"key":"{{request.operation}}","operator":"In","value":["CREATE","UPDATE"]}]},"mutate":{"foreach":[{"list":"request.object.spec.template.spec.containers","context":[{"name":"dictionary","configMap":{"name":"some-config-map","namespace":"some-namespace"}}],"patchStrategicMerge":{"spec":{"template":{"spec":{"containers":[{"image":"{{ dictionary.data.image }}","name":"{{ element.name }}"}]}}}}}]}}}`),
|
|
||||||
[]byte(`{"path":"/spec/rules/2","op":"add","value":{"name":"autogen-cronjob-resolve-image-containers","match":{"resources":{"kinds":["CronJob"]}},"exclude":{"resources":{"namespaces":["fake-namespce"]}},"preconditions":{"all":[{"key":"{{request.operation}}","operator":"In","value":["CREATE","UPDATE"]}]},"mutate":{"foreach":[{"list":"request.object.spec.jobTemplate.spec.template.spec.containers","context":[{"name":"dictionary","configMap":{"name":"some-config-map","namespace":"some-namespace"}}],"patchStrategicMerge":{"spec":{"jobTemplate":{"spec":{"template":{"spec":{"containers":[{"image":"{{ dictionary.data.image }}","name":"{{ element.name }}"}]}}}}}}}]}}}`),
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, ep := range expectedPatches {
|
|
||||||
assert.Equal(t, string(rulePatches[i]), string(ep),
|
|
||||||
fmt.Sprintf("unexpected patch: %s\nexpected: %s", rulePatches[i], ep))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_CronJob_hasExclude(t *testing.T) {
|
|
||||||
controllers := PodControllerCronJob
|
|
||||||
dir, err := os.Getwd()
|
|
||||||
baseDir := filepath.Dir(filepath.Dir(dir))
|
|
||||||
assert.NilError(t, err)
|
|
||||||
|
|
||||||
file, err := os.ReadFile(baseDir + "/test/best_practices/disallow_bind_mounts.yaml")
|
|
||||||
if err != nil {
|
|
||||||
t.Log(err)
|
|
||||||
}
|
|
||||||
policies, _, err := yamlutils.GetPolicy(file)
|
|
||||||
if err != nil {
|
|
||||||
t.Log(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
policy := policies[0]
|
|
||||||
policy.SetAnnotations(map[string]string{
|
|
||||||
kyverno.PodControllersAnnotation: controllers,
|
|
||||||
})
|
|
||||||
|
|
||||||
spec := policy.GetSpec()
|
|
||||||
rule := spec.Rules[0].DeepCopy()
|
|
||||||
rule.ExcludeResources.Kinds = []string{"Pod"}
|
|
||||||
rule.ExcludeResources.Namespaces = []string{"test"}
|
|
||||||
spec.Rules[0] = *rule
|
|
||||||
|
|
||||||
rulePatches, errs := GenerateRulePatches(spec, controllers)
|
|
||||||
if len(errs) != 0 {
|
|
||||||
t.Log(errs)
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedPatches := [][]byte{
|
|
||||||
[]byte(`{"path":"/spec/rules/1","op":"add","value":{"name":"autogen-cronjob-validate-hostPath","match":{"resources":{"kinds":["CronJob"]}},"exclude":{"resources":{"kinds":["CronJob"],"namespaces":["test"]}},"validate":{"message":"Host path volumes are not allowed","pattern":{"spec":{"jobTemplate":{"spec":{"template":{"spec":{"=(volumes)":[{"X(hostPath)":"null"}]}}}}}}}}}`),
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.DeepEqual(t, rulePatches, expectedPatches)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_CronJobAndDeployment(t *testing.T) {
|
|
||||||
controllers := strings.Join([]string{PodControllerCronJob, "Deployment"}, ",")
|
|
||||||
dir, err := os.Getwd()
|
|
||||||
baseDir := filepath.Dir(filepath.Dir(dir))
|
|
||||||
assert.NilError(t, err)
|
|
||||||
file, err := os.ReadFile(baseDir + "/test/best_practices/disallow_bind_mounts.yaml")
|
|
||||||
if err != nil {
|
|
||||||
t.Log(err)
|
|
||||||
}
|
|
||||||
policies, _, err := yamlutils.GetPolicy(file)
|
|
||||||
if err != nil {
|
|
||||||
t.Log(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
policy := policies[0]
|
|
||||||
policy.SetAnnotations(map[string]string{
|
|
||||||
kyverno.PodControllersAnnotation: controllers,
|
|
||||||
})
|
|
||||||
|
|
||||||
rulePatches, errs := GenerateRulePatches(policy.GetSpec(), controllers)
|
|
||||||
if len(errs) != 0 {
|
|
||||||
t.Log(errs)
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedPatches := [][]byte{
|
|
||||||
[]byte(`{"path":"/spec/rules/1","op":"add","value":{"name":"autogen-validate-hostPath","match":{"resources":{"kinds":["Deployment"]}},"validate":{"message":"Host path volumes are not allowed","pattern":{"spec":{"template":{"spec":{"=(volumes)":[{"X(hostPath)":"null"}]}}}}}}}`),
|
|
||||||
[]byte(`{"path":"/spec/rules/2","op":"add","value":{"name":"autogen-cronjob-validate-hostPath","match":{"resources":{"kinds":["CronJob"]}},"validate":{"message":"Host path volumes are not allowed","pattern":{"spec":{"jobTemplate":{"spec":{"template":{"spec":{"=(volumes)":[{"X(hostPath)":"null"}]}}}}}}}}}`),
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.DeepEqual(t, rulePatches, expectedPatches)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUpdateGenRuleByte(t *testing.T) {
|
func TestUpdateGenRuleByte(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
pbyte []byte
|
pbyte []byte
|
||||||
|
@ -595,74 +349,6 @@ func TestUpdateGenRuleByte(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_UpdateVariablePath(t *testing.T) {
|
|
||||||
dir, err := os.Getwd()
|
|
||||||
baseDir := filepath.Dir(filepath.Dir(dir))
|
|
||||||
assert.NilError(t, err)
|
|
||||||
file, err := os.ReadFile(baseDir + "/test/best_practices/select-secrets.yaml")
|
|
||||||
if err != nil {
|
|
||||||
t.Log(err)
|
|
||||||
}
|
|
||||||
policies, _, err := yamlutils.GetPolicy(file)
|
|
||||||
if err != nil {
|
|
||||||
t.Log(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
policy := policies[0]
|
|
||||||
|
|
||||||
rulePatches, errs := GenerateRulePatches(policy.GetSpec(), PodControllers)
|
|
||||||
if len(errs) != 0 {
|
|
||||||
t.Log(errs)
|
|
||||||
}
|
|
||||||
expectedPatches := [][]byte{
|
|
||||||
[]byte(`{"path":"/spec/rules/1","op":"add","value":{"name":"autogen-select-secrets-from-volumes","match":{"resources":{"kinds":["DaemonSet","Deployment","Job","StatefulSet","ReplicaSet","ReplicationController"]}},"context":[{"name":"volsecret","apiCall":{"urlPath":"/api/v1/namespaces/{{request.object.spec.template.metadata.namespace}}/secrets/{{request.object.spec.template.spec.volumes[0].secret.secretName}}","jmesPath":"metadata.labels.foo"}}],"preconditions":[{"key":"{{ request.operation }}","operator":"Equals","value":"CREATE"}],"validate":{"message":"The Secret named {{request.object.spec.template.spec.volumes[0].secret.secretName}} is restricted and may not be used.","pattern":{"spec":{"template":{"spec":{"containers":[{"image":"registry.domain.com/*"}]}}}}}}}`),
|
|
||||||
[]byte(`{"path":"/spec/rules/2","op":"add","value":{"name":"autogen-cronjob-select-secrets-from-volumes","match":{"resources":{"kinds":["CronJob"]}},"context":[{"name":"volsecret","apiCall":{"urlPath":"/api/v1/namespaces/{{request.object.spec.template.metadata.namespace}}/secrets/{{request.object.spec.jobTemplate.spec.template.spec.volumes[0].secret.secretName}}","jmesPath":"metadata.labels.foo"}}],"preconditions":[{"key":"{{ request.operation }}","operator":"Equals","value":"CREATE"}],"validate":{"message":"The Secret named {{request.object.spec.jobTemplate.spec.template.spec.volumes[0].secret.secretName}} is restricted and may not be used.","pattern":{"spec":{"jobTemplate":{"spec":{"template":{"spec":{"containers":[{"image":"registry.domain.com/*"}]}}}}}}}}}`),
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, ep := range expectedPatches {
|
|
||||||
assert.Equal(t, string(rulePatches[i]), string(ep),
|
|
||||||
fmt.Sprintf("unexpected patch: %s\nexpected: %s", rulePatches[i], ep))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Deny(t *testing.T) {
|
|
||||||
dir, err := os.Getwd()
|
|
||||||
baseDir := filepath.Dir(filepath.Dir(dir))
|
|
||||||
assert.NilError(t, err)
|
|
||||||
file, err := os.ReadFile(baseDir + "/test/policy/deny/policy.yaml")
|
|
||||||
if err != nil {
|
|
||||||
t.Log(err)
|
|
||||||
}
|
|
||||||
policies, _, err := yamlutils.GetPolicy(file)
|
|
||||||
if err != nil {
|
|
||||||
t.Log(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
policy := policies[0]
|
|
||||||
spec := policy.GetSpec()
|
|
||||||
spec.Rules[0].MatchResources.Any = kyverno.ResourceFilters{
|
|
||||||
{
|
|
||||||
ResourceDescription: kyverno.ResourceDescription{
|
|
||||||
Kinds: []string{"Pod"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
rulePatches, errs := GenerateRulePatches(spec, PodControllers)
|
|
||||||
if len(errs) != 0 {
|
|
||||||
t.Log(errs)
|
|
||||||
}
|
|
||||||
expectedPatches := [][]byte{
|
|
||||||
[]byte(`{"path":"/spec/rules/1","op":"add","value":{"name":"autogen-disallow-mount-containerd-sock","match":{"any":[{"resources":{"kinds":["DaemonSet","Deployment","Job","StatefulSet","ReplicaSet","ReplicationController"]}}],"resources":{"kinds":["Pod"]}},"validate":{"foreach":[{"list":"request.object.spec.template.spec.volumes[]","deny":{"conditions":{"any":[{"key":"{{ path_canonicalize(element.hostPath.path) }}","operator":"Equals","value":"/var/run/containerd/containerd.sock"},{"key":"{{ path_canonicalize(element.hostPath.path) }}","operator":"Equals","value":"/run/containerd/containerd.sock"},{"key":"{{ path_canonicalize(element.hostPath.path) }}","operator":"Equals","value":"\\var\\run\\containerd\\containerd.sock"}]}}}]}}}`),
|
|
||||||
[]byte(`{"path":"/spec/rules/2","op":"add","value":{"name":"autogen-cronjob-disallow-mount-containerd-sock","match":{"any":[{"resources":{"kinds":["CronJob"]}}],"resources":{"kinds":["Pod"]}},"validate":{"foreach":[{"list":"request.object.spec.jobTemplate.spec.template.spec.volumes[]","deny":{"conditions":{"any":[{"key":"{{ path_canonicalize(element.hostPath.path) }}","operator":"Equals","value":"/var/run/containerd/containerd.sock"},{"key":"{{ path_canonicalize(element.hostPath.path) }}","operator":"Equals","value":"/run/containerd/containerd.sock"},{"key":"{{ path_canonicalize(element.hostPath.path) }}","operator":"Equals","value":"\\var\\run\\containerd\\containerd.sock"}]}}}]}}}`),
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, ep := range expectedPatches {
|
|
||||||
assert.Equal(t, string(rulePatches[i]), string(ep),
|
|
||||||
fmt.Sprintf("unexpected patch: %s\nexpected: %s", rulePatches[i], ep))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_ComputeRules(t *testing.T) {
|
func Test_ComputeRules(t *testing.T) {
|
||||||
intPtr := func(i int) *int { return &i }
|
intPtr := func(i int) *int { return &i }
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
|
|
|
@ -304,12 +304,15 @@ func updateGenRuleByte(pbyte []byte, kind string) (obj []byte) {
|
||||||
if kind == "Pod" {
|
if kind == "Pod" {
|
||||||
obj = []byte(strings.ReplaceAll(string(pbyte), "request.object.spec", "request.object.spec.template.spec"))
|
obj = []byte(strings.ReplaceAll(string(pbyte), "request.object.spec", "request.object.spec.template.spec"))
|
||||||
obj = []byte(strings.ReplaceAll(string(obj), "request.oldObject.spec", "request.oldObject.spec.template.spec"))
|
obj = []byte(strings.ReplaceAll(string(obj), "request.oldObject.spec", "request.oldObject.spec.template.spec"))
|
||||||
|
obj = []byte(strings.ReplaceAll(string(obj), "request.object.metadata", "request.object.spec.template.metadata"))
|
||||||
|
obj = []byte(strings.ReplaceAll(string(obj), "request.oldObject.metadata", "request.oldObject.spec.template.metadata"))
|
||||||
}
|
}
|
||||||
if kind == "Cronjob" {
|
if kind == "Cronjob" {
|
||||||
obj = []byte(strings.ReplaceAll(string(pbyte), "request.object.spec", "request.object.spec.jobTemplate.spec.template.spec"))
|
obj = []byte(strings.ReplaceAll(string(pbyte), "request.object.spec", "request.object.spec.jobTemplate.spec.template.spec"))
|
||||||
obj = []byte(strings.ReplaceAll(string(obj), "request.oldObject.spec", "request.oldObject.spec.jobTemplate.spec.template.spec"))
|
obj = []byte(strings.ReplaceAll(string(obj), "request.oldObject.spec", "request.oldObject.spec.jobTemplate.spec.template.spec"))
|
||||||
|
obj = []byte(strings.ReplaceAll(string(obj), "request.object.metadata", "request.object.spec.jobTemplate.spec.template.metadata"))
|
||||||
|
obj = []byte(strings.ReplaceAll(string(obj), "request.oldObject.metadata", "request.oldObject.spec.jobTemplate.spec.template.metadata"))
|
||||||
}
|
}
|
||||||
obj = []byte(strings.ReplaceAll(string(obj), "request.object.metadata", "request.object.spec.template.metadata"))
|
|
||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
6
test/conformance/kuttl/autogen/conditions/01-policy.yaml
Normal file
6
test/conformance/kuttl/autogen/conditions/01-policy.yaml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
apiVersion: kuttl.dev/v1beta1
|
||||||
|
kind: TestStep
|
||||||
|
apply:
|
||||||
|
- policy.yaml
|
||||||
|
assert:
|
||||||
|
- policy-assert.yaml
|
11
test/conformance/kuttl/autogen/conditions/README.md
Normal file
11
test/conformance/kuttl/autogen/conditions/README.md
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
## Description
|
||||||
|
|
||||||
|
The policy should contain autogen rules with deny conditions correctly adjusted.
|
||||||
|
|
||||||
|
## Expected Behavior
|
||||||
|
|
||||||
|
The policy contains autogen rules with deny conditions correctly adjusted.
|
||||||
|
|
||||||
|
## Related Issue(s)
|
||||||
|
|
||||||
|
- https://github.com/kyverno/kyverno/issues/7566
|
49
test/conformance/kuttl/autogen/conditions/policy-assert.yaml
Normal file
49
test/conformance/kuttl/autogen/conditions/policy-assert.yaml
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
apiVersion: kyverno.io/v1
|
||||||
|
kind: ClusterPolicy
|
||||||
|
metadata:
|
||||||
|
name: allowed-annotations
|
||||||
|
spec: {}
|
||||||
|
status:
|
||||||
|
autogen:
|
||||||
|
rules:
|
||||||
|
- match:
|
||||||
|
any:
|
||||||
|
- resources:
|
||||||
|
kinds:
|
||||||
|
- DaemonSet
|
||||||
|
- Deployment
|
||||||
|
- Job
|
||||||
|
- StatefulSet
|
||||||
|
- ReplicaSet
|
||||||
|
- ReplicationController
|
||||||
|
name: autogen-allowed-fluxcd-annotations
|
||||||
|
validate:
|
||||||
|
deny:
|
||||||
|
conditions:
|
||||||
|
all:
|
||||||
|
- key: '{{ request.object.spec.template.metadata.annotations.keys(@)[?contains(@, ''fluxcd.io/'')] }}'
|
||||||
|
operator: AnyNotIn
|
||||||
|
value:
|
||||||
|
- fluxcd.io/cow
|
||||||
|
- fluxcd.io/dog
|
||||||
|
message: The only approved FluxCD annotations are `fluxcd.io/cow` and `fluxcd.io/dog`.
|
||||||
|
- match:
|
||||||
|
any:
|
||||||
|
- resources:
|
||||||
|
kinds:
|
||||||
|
- CronJob
|
||||||
|
name: autogen-cronjob-allowed-fluxcd-annotations
|
||||||
|
validate:
|
||||||
|
deny:
|
||||||
|
conditions:
|
||||||
|
all:
|
||||||
|
- key: '{{ request.object.spec.jobTemplate.spec.template.metadata.annotations.keys(@)[?contains(@, ''fluxcd.io/'')] }}'
|
||||||
|
operator: AnyNotIn
|
||||||
|
value:
|
||||||
|
- fluxcd.io/cow
|
||||||
|
- fluxcd.io/dog
|
||||||
|
message: The only approved FluxCD annotations are `fluxcd.io/cow` and `fluxcd.io/dog`.
|
||||||
|
conditions:
|
||||||
|
- reason: Succeeded
|
||||||
|
status: "True"
|
||||||
|
type: Ready
|
24
test/conformance/kuttl/autogen/conditions/policy.yaml
Normal file
24
test/conformance/kuttl/autogen/conditions/policy.yaml
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
apiVersion: kyverno.io/v1
|
||||||
|
kind: ClusterPolicy
|
||||||
|
metadata:
|
||||||
|
name: allowed-annotations
|
||||||
|
spec:
|
||||||
|
background: true
|
||||||
|
rules:
|
||||||
|
- match:
|
||||||
|
any:
|
||||||
|
- resources:
|
||||||
|
kinds:
|
||||||
|
- Pod
|
||||||
|
name: allowed-fluxcd-annotations
|
||||||
|
validate:
|
||||||
|
deny:
|
||||||
|
conditions:
|
||||||
|
all:
|
||||||
|
- key: '{{ request.object.metadata.annotations.keys(@)[?contains(@, ''fluxcd.io/'')] }}'
|
||||||
|
operator: AnyNotIn
|
||||||
|
value:
|
||||||
|
- fluxcd.io/cow
|
||||||
|
- fluxcd.io/dog
|
||||||
|
message: The only approved FluxCD annotations are `fluxcd.io/cow` and `fluxcd.io/dog`.
|
||||||
|
validationFailureAction: Enforce
|
Loading…
Add table
Reference in a new issue