2022-03-02 00:19:31 +01:00
package autogen
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
"testing"
kyverno "github.com/kyverno/kyverno/api/kyverno/v1"
2022-09-06 17:16:44 +02:00
yamlutils "github.com/kyverno/kyverno/pkg/utils/yaml"
2022-03-02 00:19:31 +01:00
"gotest.tools/assert"
2022-03-10 16:51:29 +01:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2022-03-02 00:19:31 +01:00
)
2022-04-05 17:12:22 +02:00
func Test_getAutogenRuleName ( t * testing . T ) {
testCases := [ ] struct {
name string
ruleName string
prefix string
expected string
} {
{ "valid" , "valid-rule-name" , "autogen" , "autogen-valid-rule-name" } ,
{ "truncated" , "too-long-this-rule-name-will-be-truncated-to-63-characters" , "autogen" , "autogen-too-long-this-rule-name-will-be-truncated-to-63-charact" } ,
{ "valid-cronjob" , "valid-rule-name" , "autogen-cronjob" , "autogen-cronjob-valid-rule-name" } ,
{ "truncated-cronjob" , "too-long-this-rule-name-will-be-truncated-to-63-characters" , "autogen-cronjob" , "autogen-cronjob-too-long-this-rule-name-will-be-truncated-to-63" } ,
}
for _ , test := range testCases {
res := getAutogenRuleName ( test . prefix , test . ruleName )
assert . Equal ( t , test . expected , res )
}
}
func Test_isAutogenRule ( t * testing . T ) {
testCases := [ ] struct {
name string
ruleName string
expected bool
} {
{ "normal" , "valid-rule-name" , false } ,
{ "simple" , "autogen-simple" , true } ,
{ "simple-cronjob" , "autogen-cronjob-simple" , true } ,
{ "truncated" , "autogen-too-long-this-rule-name-will-be-truncated-to-63-charact" , true } ,
{ "truncated-cronjob" , "autogen-cronjob-too-long-this-rule-name-will-be-truncated-to-63" , true } ,
}
for _ , test := range testCases {
res := isAutogenRuleName ( test . ruleName )
assert . Equal ( t , test . expected , res )
}
}
2022-03-10 16:51:29 +01:00
func Test_CanAutoGen ( t * testing . T ) {
2022-03-02 00:19:31 +01:00
testCases := [ ] struct {
name string
policy [ ] byte
expectedControllers string
} {
{
name : "rule-with-match-name" ,
policy : [ ] byte ( ` { "apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata": { "name":"test"},"spec": { "rules":[ { "name":"test","match": { "resources": { "kinds":["Namespace"],"name":"*"}}}]}} ` ) ,
expectedControllers : "none" ,
} ,
{
name : "rule-with-match-selector" ,
policy : [ ] byte ( ` { "apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata": { "name":"test-getcontrollers"},"spec": { "background":false,"rules":[ { "name":"test-getcontrollers","match": { "resources": { "kinds":["Pod"],"selector": { "matchLabels": { "foo":"bar"}}}}}]}} ` ) ,
expectedControllers : "none" ,
} ,
{
name : "rule-with-exclude-name" ,
policy : [ ] byte ( ` { "apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata": { "name":"test-getcontrollers"},"spec": { "background":false,"rules":[ { "name":"test-getcontrollers","match": { "resources": { "kinds":["Pod"]}},"exclude": { "resources": { "name":"test"}}}]}} ` ) ,
expectedControllers : "none" ,
} ,
2022-10-12 08:09:02 +02:00
{
name : "rule-with-exclude-names" ,
policy : [ ] byte ( ` { "apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata": { "name":"test-getcontrollers"},"spec": { "background":false,"rules":[ { "name":"test-getcontrollers","match": { "resources": { "kinds":["Pod"]}},"exclude": { "resources": { "names":["test"]}}}]}} ` ) ,
expectedControllers : "none" ,
} ,
2022-03-02 00:19:31 +01:00
{
name : "rule-with-exclude-selector" ,
policy : [ ] byte ( ` { "apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata": { "name":"test-getcontrollers"},"spec": { "background":false,"rules":[ { "name":"test-getcontrollers","match": { "resources": { "kinds":["Pod"]}},"exclude": { "resources": { "selector": { "matchLabels": { "foo":"bar"}}}}}]}} ` ) ,
expectedControllers : "none" ,
} ,
{
name : "rule-with-deny" ,
policy : [ ] byte ( ` { "apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata": { "name":"test"},"spec": { "rules":[ { "name":"require-network-policy","match": { "resources": { "kinds":["Pod"]}},"validate": { "message":"testpolicy","deny": { "conditions":[ { "key":" {{ request .object .metadata .labels .foo }} ","operator":"Equals","value":"bar"}]}}}]}} ` ) ,
2022-03-09 14:48:04 +01:00
expectedControllers : PodControllers ,
2022-03-02 00:19:31 +01:00
} ,
{
name : "rule-with-match-mixed-kinds-pod-podcontrollers" ,
policy : [ ] byte ( ` { "apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata": { "name":"set-service-labels-env"},"spec": { "background":false,"rules":[ { "name":"set-service-label","match": { "resources": { "kinds":["Pod","Deployment"]}},"preconditions": { "any":[ { "key":" {{ request .operation }} ","operator":"Equals","value":"CREATE"}]},"mutate": { "patchStrategicMerge": { "metadata": { "labels": { "+(service)":" {{ request .object .spec .template .metadata .labels .app }} "}}}}}]}} ` ) ,
expectedControllers : "none" ,
} ,
{
name : "rule-with-exclude-mixed-kinds-pod-podcontrollers" ,
policy : [ ] byte ( ` { "apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata": { "name":"set-service-labels-env"},"spec": { "background":false,"rules":[ { "name":"set-service-label","match": { "resources": { "kinds":["Pod"]}},"exclude": { "resources": { "kinds":["Pod","Deployment"]}},"mutate": { "patchStrategicMerge": { "metadata": { "labels": { "+(service)":" {{ request .object .spec .template .metadata .labels .app }} "}}}}}]}} ` ) ,
expectedControllers : "none" ,
} ,
{
name : "rule-with-match-kinds-pod-only" ,
policy : [ ] byte ( ` { "apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata": { "name":"test"},"spec": { "rules":[ { "name":"require-network-policy","match": { "resources": { "kinds":["Pod"]}},"validate": { "message":"testpolicy","pattern": { "metadata": { "labels": { "foo":"bar"}}}}}]}} ` ) ,
2022-03-09 14:48:04 +01:00
expectedControllers : PodControllers ,
2022-03-02 00:19:31 +01:00
} ,
{
name : "rule-with-exclude-kinds-pod-only" ,
policy : [ ] byte ( ` { "apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata": { "name":"test"},"spec": { "rules":[ { "name":"require-network-policy","match": { "resources": { "kinds":["Pod"]}},"exclude": { "resources": { "kinds":["Pod"],"namespaces":["test"]}},"validate": { "message":"testpolicy","pattern": { "metadata": { "labels": { "foo":"bar"}}}}}]}} ` ) ,
2022-03-09 14:48:04 +01:00
expectedControllers : PodControllers ,
2022-03-02 00:19:31 +01:00
} ,
{
name : "rule-with-mutate-patches" ,
policy : [ ] byte ( ` { "apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata": { "name":"test"},"spec": { "rules":[ { "name":"test","match": { "resources": { "kinds":["Pod"]}},"mutate": { "patchesJson6902":"-op:add\npath:/spec/containers/0/env/-1\nvalue: { \"name\":\"SERVICE\",\"value\": {{ request .object .spec .template .metadata .labels .app }} }"}}]}} ` ) ,
expectedControllers : "none" ,
} ,
{
name : "rule-with-generate" ,
policy : [ ] byte ( ` { "apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata": { "name":"add-networkpolicy"},"spec": { "rules":[ { "name":"default-deny-ingress","match": { "resources": { "kinds":["Namespace"],"name":"*"}},"exclude": { "resources": { "namespaces":["kube-system","default","kube-public","kyverno"]}},"generate": { "kind":"NetworkPolicy","name":"default-deny-ingress","namespace":" {{ request .object .metadata .name }} ","synchronize":true,"data": { "spec": { "podSelector": { },"policyTypes":["Ingress"]}}}}]}} ` ) ,
expectedControllers : "none" ,
} ,
{
name : "rule-with-predefined-invalid-controllers" ,
policy : [ ] byte ( ` { "apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata": { "name":"set-service-labels-env"},"annotations":null,"pod-policies.kyverno.io/autogen-controllers":"DaemonSet,Deployment,StatefulSet","spec": { "background":false,"rules":[ { "name":"set-service-label","match": { "resources": { "kinds":["Pod","Deployment"]}},"mutate": { "patchStrategicMerge": { "metadata": { "labels": { "+(service)":" {{ request .object .spec .template .metadata .labels .app }} "}}}}}]}} ` ) ,
expectedControllers : "none" ,
} ,
{
name : "rule-with-predefined-valid-controllers" ,
policy : [ ] byte ( ` { "apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata": { "name":"set-service-labels-env"},"annotations":null,"pod-policies.kyverno.io/autogen-controllers":"none","spec": { "background":false,"rules":[ { "name":"set-service-label","match": { "resources": { "kinds":["Pod","Deployment"]}},"mutate": { "patchStrategicMerge": { "metadata": { "labels": { "+(service)":" {{ request .object .spec .template .metadata .labels .app }} "}}}}}]}} ` ) ,
expectedControllers : "none" ,
} ,
{
name : "rule-with-only-predefined-valid-controllers" ,
policy : [ ] byte ( ` { "apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata": { "name":"set-service-labels-env"},"annotations":null,"pod-policies.kyverno.io/autogen-controllers":"none","spec": { "background":false,"rules":[ { "name":"set-service-label","match": { "resources": { "kinds":["Namespace"]}},"mutate": { "patchStrategicMerge": { "metadata": { "labels": { "+(service)":" {{ request .object .spec .template .metadata .labels .app }} "}}}}}]}} ` ) ,
expectedControllers : "none" ,
} ,
Extend Pod Security Admission (#4364)
* init commit for pss
Signed-off-by: ShutingZhao <shuting@nirmata.com>
* add test for Volume Type control
* add test for App Armor control except ExemptProfile. Fix PSS profile check in EvaluatePSS()
* remove unused code, still a JMESPATH problem with app armor ExemptProfile()
* test for Host Process / Host Namespaces controls
* test for Privileged containers controls
* test for HostPathVolume control
* test for HostPorts control
* test for HostPorts control
* test for SELinux control
* test for Proc mount type control
* Set to baseline
* test for Seccomp control
* test for Sysctl control
* test for Privilege escalation control
* test for Run as non root control
* test for Restricted Seccomp control
* Add problems to address
* add solutions to problems
* Add validate rule for PSA
* api.Version --> string. latest by default
* Exclude all values for a restrictedField
* add tests for kyverno engine
* code to be used to match kyverno rule's namespace
* Refacto pkg/pss
* fix multiple problems: not matching containers, add contains methods, select the right container when we have the same exclude.RestrictedField for multiple containers:
* EvaluatePod
* Use EvaluatePod in kyverno engine
* Set pod instead of container in context to use full Jmespath. e.g.: securityContext.capabilities.add --> spec.containers[*].securityContext.capabilities.add
* Check if PSSCheckResult matched at least one exclude value
* add tests for engine
* fix engine validation test
* config
* update go.mod and go.sum
* crds
* Check validate value: add PodSecurity
* exclude all restrictedFields when we only specify the controlName
* ExemptProfile(): check if exclud.RestrictedField matches at least one restrictedField.path
* handle containers, initContainers, ephemeralContainers when we only specify the controlName (all restrictedFields are excluded)
* refacto pks/pss/evaluate.go and add pkg/engine/validation_test.go
* add all controls with containers in restrictedFields as comments
* add tests for capabilities and privileged containers and fix some errors
* add tests for host ports control
* add tests for proc mount control
* add tests for privilege escalation control
* add tests for capabilities control
* remove comments
* new algo
* refacto algo, working. Add test for hostProcess control
* remove unused code
* fix getPodWithNotMatchingContainers(), add tests for host namespaces control
* refacto ExemptProfile()
* get values for a specific container. add test for SELinuxOptions control
* fix allowedValues for SELinuxOptions
* add tests for seccompProfile_baseline control
* refacto checkContainers(), add test for seccomp control
* add test for running as non root control
* add some tests for runAsUser control, have to update current PSA version
* add sysctls control
* add allowed values for restrictedVolumes control
* add some tests for appArmor, volume types controls
* add tests for volume types control
* add tests for hostPath volume control
* finish merge conflicts and add tests for runAsUser
* update charts and crds
* exclude.images optional
* change volume types control exclude values
* add appAmor control
* fix: did not match any exclude value for pod-level restrictedFields
* create autogen for validate.PodSecurity
* clean code, remove logs
* fix sonatype lift errors
* fix sonatype lift errors: duplication
* fix crash in pkg/policy/validate/ tests and unmarshall errors for pkg/engine tests
* beginning of autogen implement for validate.exclude
* Autogen for validation.PodSecurity
* working autogen with simple tests
* change validate.PodSecurity failure response format
* make codegen
* fix lint errors, remove debug prints
* fix tags
* fix tags
* fix crash when deleting pods matching validate.podSecurity rule. Only check validatePodSecurity() when it's not a delete request
* Changes requested
* Changes requested 2
* Changes requested 3
* Changes requested 4
* Changes requested and make codegen
* fix host namespaces control
* fix lint
* fix codegen error
* update docs/crd/v1/index.html
Signed-off-by: ShutingZhao <shuting@nirmata.com>
* fix path
Signed-off-by: ShutingZhao <shuting@nirmata.com>
* update crd schema
Signed-off-by: ShutingZhao <shuting@nirmata.com>
* update charts/kyverno/templates/crds.yaml
Signed-off-by: ShutingZhao <shuting@nirmata.com>
Signed-off-by: ShutingZhao <shuting@nirmata.com>
Co-authored-by: ShutingZhao <shuting@nirmata.com>
2022-08-31 11:16:31 +02:00
{
name : "rule-with-match-kinds-pod-only-validate-exclude" ,
policy : [ ] byte ( ` { "apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata": { "name":"test"},"spec": { "rules":[ { "name":"require-network-policy","match": { "resources": { "kinds":["Pod"]}},"validate": { "message":"testpolicy","podSecurity": { "level": "baseline","version":"v1.24","exclude":[ { "controlName":"SELinux","restrictedField":"spec.containers[*].securityContext.seLinuxOptions.role","images":["nginx"],"values":["baz"]}, { "controlName":"SELinux","restrictedField":"spec.initContainers[*].securityContext.seLinuxOptions.role","images":["nodejs"],"values":["init-baz"]}]}}}]}} ` ) ,
expectedControllers : PodControllers ,
} ,
2022-03-02 00:19:31 +01:00
}
for _ , test := range testCases {
var policy kyverno . ClusterPolicy
err := json . Unmarshal ( test . policy , & policy )
assert . NilError ( t , err )
2022-04-29 11:12:21 +02:00
applyAutoGen , controllers := CanAutoGen ( & policy . Spec )
2022-03-02 00:19:31 +01:00
if ! applyAutoGen {
controllers = "none"
}
assert . Equal ( t , test . expectedControllers , controllers , fmt . Sprintf ( "test %s failed" , test . name ) )
}
}
2022-03-10 16:51:29 +01:00
func Test_GetSupportedControllers ( t * testing . T ) {
testCases := [ ] struct {
name string
policy [ ] byte
expectedControllers string
} {
{
name : "rule-with-match-name" ,
policy : [ ] byte ( ` { "apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata": { "name":"test"},"spec": { "rules":[ { "name":"test","match": { "resources": { "kinds":["Namespace"],"name":"*"}}}]}} ` ) ,
expectedControllers : "none" ,
} ,
{
name : "rule-with-match-selector" ,
policy : [ ] byte ( ` { "apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata": { "name":"test-getcontrollers"},"spec": { "background":false,"rules":[ { "name":"test-getcontrollers","match": { "resources": { "kinds":["Pod"],"selector": { "matchLabels": { "foo":"bar"}}}}}]}} ` ) ,
expectedControllers : "none" ,
} ,
{
name : "rule-with-exclude-name" ,
policy : [ ] byte ( ` { "apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata": { "name":"test-getcontrollers"},"spec": { "background":false,"rules":[ { "name":"test-getcontrollers","match": { "resources": { "kinds":["Pod"]}},"exclude": { "resources": { "name":"test"}}}]}} ` ) ,
expectedControllers : "none" ,
} ,
{
name : "rule-with-exclude-selector" ,
policy : [ ] byte ( ` { "apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata": { "name":"test-getcontrollers"},"spec": { "background":false,"rules":[ { "name":"test-getcontrollers","match": { "resources": { "kinds":["Pod"]}},"exclude": { "resources": { "selector": { "matchLabels": { "foo":"bar"}}}}}]}} ` ) ,
expectedControllers : "none" ,
} ,
{
name : "rule-with-deny" ,
policy : [ ] byte ( ` { "apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata": { "name":"test"},"spec": { "rules":[ { "name":"require-network-policy","match": { "resources": { "kinds":["Pod"]}},"validate": { "message":"testpolicy","deny": { "conditions":[ { "key":" {{ request .object .metadata .labels .foo }} ","operator":"Equals","value":"bar"}]}}}]}} ` ) ,
expectedControllers : PodControllers ,
} ,
{
name : "rule-with-match-mixed-kinds-pod-podcontrollers" ,
policy : [ ] byte ( ` { "apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata": { "name":"set-service-labels-env"},"spec": { "background":false,"rules":[ { "name":"set-service-label","match": { "resources": { "kinds":["Pod","Deployment"]}},"preconditions": { "any":[ { "key":" {{ request .operation }} ","operator":"Equals","value":"CREATE"}]},"mutate": { "patchStrategicMerge": { "metadata": { "labels": { "+(service)":" {{ request .object .spec .template .metadata .labels .app }} "}}}}}]}} ` ) ,
expectedControllers : "none" ,
} ,
{
name : "rule-with-exclude-mixed-kinds-pod-podcontrollers" ,
policy : [ ] byte ( ` { "apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata": { "name":"set-service-labels-env"},"spec": { "background":false,"rules":[ { "name":"set-service-label","match": { "resources": { "kinds":["Pod"]}},"exclude": { "resources": { "kinds":["Pod","Deployment"]}},"mutate": { "patchStrategicMerge": { "metadata": { "labels": { "+(service)":" {{ request .object .spec .template .metadata .labels .app }} "}}}}}]}} ` ) ,
expectedControllers : "none" ,
} ,
{
name : "rule-with-match-kinds-pod-only" ,
policy : [ ] byte ( ` { "apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata": { "name":"test"},"spec": { "rules":[ { "name":"require-network-policy","match": { "resources": { "kinds":["Pod"]}},"validate": { "message":"testpolicy","pattern": { "metadata": { "labels": { "foo":"bar"}}}}}]}} ` ) ,
expectedControllers : PodControllers ,
} ,
{
name : "rule-with-exclude-kinds-pod-only" ,
policy : [ ] byte ( ` { "apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata": { "name":"test"},"spec": { "rules":[ { "name":"require-network-policy","match": { "resources": { "kinds":["Pod"]}},"exclude": { "resources": { "kinds":["Pod"],"namespaces":["test"]}},"validate": { "message":"testpolicy","pattern": { "metadata": { "labels": { "foo":"bar"}}}}}]}} ` ) ,
expectedControllers : PodControllers ,
} ,
{
name : "rule-with-mutate-patches" ,
policy : [ ] byte ( ` { "apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata": { "name":"test"},"spec": { "rules":[ { "name":"test","match": { "resources": { "kinds":["Pod"]}},"mutate": { "patchesJson6902":"-op:add\npath:/spec/containers/0/env/-1\nvalue: { \"name\":\"SERVICE\",\"value\": {{ request .object .spec .template .metadata .labels .app }} }"}}]}} ` ) ,
expectedControllers : "none" ,
} ,
{
name : "rule-with-generate" ,
policy : [ ] byte ( ` { "apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata": { "name":"add-networkpolicy"},"spec": { "rules":[ { "name":"default-deny-ingress","match": { "resources": { "kinds":["Namespace"],"name":"*"}},"exclude": { "resources": { "namespaces":["kube-system","default","kube-public","kyverno"]}},"generate": { "kind":"NetworkPolicy","name":"default-deny-ingress","namespace":" {{ request .object .metadata .name }} ","synchronize":true,"data": { "spec": { "podSelector": { },"policyTypes":["Ingress"]}}}}]}} ` ) ,
expectedControllers : "none" ,
} ,
{
name : "rule-with-predefined-invalid-controllers" ,
policy : [ ] byte ( ` { "apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata": { "name":"set-service-labels-env"},"annotations":null,"pod-policies.kyverno.io/autogen-controllers":"DaemonSet,Deployment,StatefulSet","spec": { "background":false,"rules":[ { "name":"set-service-label","match": { "resources": { "kinds":["Pod","Deployment"]}},"mutate": { "patchStrategicMerge": { "metadata": { "labels": { "+(service)":" {{ request .object .spec .template .metadata .labels .app }} "}}}}}]}} ` ) ,
expectedControllers : "none" ,
} ,
{
name : "rule-with-predefined-valid-controllers" ,
policy : [ ] byte ( ` { "apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata": { "name":"set-service-labels-env"},"annotations":null,"pod-policies.kyverno.io/autogen-controllers":"none","spec": { "background":false,"rules":[ { "name":"set-service-label","match": { "resources": { "kinds":["Pod","Deployment"]}},"mutate": { "patchStrategicMerge": { "metadata": { "labels": { "+(service)":" {{ request .object .spec .template .metadata .labels .app }} "}}}}}]}} ` ) ,
expectedControllers : "none" ,
} ,
{
name : "rule-with-only-predefined-valid-controllers" ,
policy : [ ] byte ( ` { "apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata": { "name":"set-service-labels-env"},"annotations":null,"pod-policies.kyverno.io/autogen-controllers":"none","spec": { "background":false,"rules":[ { "name":"set-service-label","match": { "resources": { "kinds":["Namespace"]}},"mutate": { "patchStrategicMerge": { "metadata": { "labels": { "+(service)":" {{ request .object .spec .template .metadata .labels .app }} "}}}}}]}} ` ) ,
expectedControllers : "none" ,
} ,
Extend Pod Security Admission (#4364)
* init commit for pss
Signed-off-by: ShutingZhao <shuting@nirmata.com>
* add test for Volume Type control
* add test for App Armor control except ExemptProfile. Fix PSS profile check in EvaluatePSS()
* remove unused code, still a JMESPATH problem with app armor ExemptProfile()
* test for Host Process / Host Namespaces controls
* test for Privileged containers controls
* test for HostPathVolume control
* test for HostPorts control
* test for HostPorts control
* test for SELinux control
* test for Proc mount type control
* Set to baseline
* test for Seccomp control
* test for Sysctl control
* test for Privilege escalation control
* test for Run as non root control
* test for Restricted Seccomp control
* Add problems to address
* add solutions to problems
* Add validate rule for PSA
* api.Version --> string. latest by default
* Exclude all values for a restrictedField
* add tests for kyverno engine
* code to be used to match kyverno rule's namespace
* Refacto pkg/pss
* fix multiple problems: not matching containers, add contains methods, select the right container when we have the same exclude.RestrictedField for multiple containers:
* EvaluatePod
* Use EvaluatePod in kyverno engine
* Set pod instead of container in context to use full Jmespath. e.g.: securityContext.capabilities.add --> spec.containers[*].securityContext.capabilities.add
* Check if PSSCheckResult matched at least one exclude value
* add tests for engine
* fix engine validation test
* config
* update go.mod and go.sum
* crds
* Check validate value: add PodSecurity
* exclude all restrictedFields when we only specify the controlName
* ExemptProfile(): check if exclud.RestrictedField matches at least one restrictedField.path
* handle containers, initContainers, ephemeralContainers when we only specify the controlName (all restrictedFields are excluded)
* refacto pks/pss/evaluate.go and add pkg/engine/validation_test.go
* add all controls with containers in restrictedFields as comments
* add tests for capabilities and privileged containers and fix some errors
* add tests for host ports control
* add tests for proc mount control
* add tests for privilege escalation control
* add tests for capabilities control
* remove comments
* new algo
* refacto algo, working. Add test for hostProcess control
* remove unused code
* fix getPodWithNotMatchingContainers(), add tests for host namespaces control
* refacto ExemptProfile()
* get values for a specific container. add test for SELinuxOptions control
* fix allowedValues for SELinuxOptions
* add tests for seccompProfile_baseline control
* refacto checkContainers(), add test for seccomp control
* add test for running as non root control
* add some tests for runAsUser control, have to update current PSA version
* add sysctls control
* add allowed values for restrictedVolumes control
* add some tests for appArmor, volume types controls
* add tests for volume types control
* add tests for hostPath volume control
* finish merge conflicts and add tests for runAsUser
* update charts and crds
* exclude.images optional
* change volume types control exclude values
* add appAmor control
* fix: did not match any exclude value for pod-level restrictedFields
* create autogen for validate.PodSecurity
* clean code, remove logs
* fix sonatype lift errors
* fix sonatype lift errors: duplication
* fix crash in pkg/policy/validate/ tests and unmarshall errors for pkg/engine tests
* beginning of autogen implement for validate.exclude
* Autogen for validation.PodSecurity
* working autogen with simple tests
* change validate.PodSecurity failure response format
* make codegen
* fix lint errors, remove debug prints
* fix tags
* fix tags
* fix crash when deleting pods matching validate.podSecurity rule. Only check validatePodSecurity() when it's not a delete request
* Changes requested
* Changes requested 2
* Changes requested 3
* Changes requested 4
* Changes requested and make codegen
* fix host namespaces control
* fix lint
* fix codegen error
* update docs/crd/v1/index.html
Signed-off-by: ShutingZhao <shuting@nirmata.com>
* fix path
Signed-off-by: ShutingZhao <shuting@nirmata.com>
* update crd schema
Signed-off-by: ShutingZhao <shuting@nirmata.com>
* update charts/kyverno/templates/crds.yaml
Signed-off-by: ShutingZhao <shuting@nirmata.com>
Signed-off-by: ShutingZhao <shuting@nirmata.com>
Co-authored-by: ShutingZhao <shuting@nirmata.com>
2022-08-31 11:16:31 +02:00
{
name : "rule-with-match-kinds-pod-only-validate-exclude" ,
policy : [ ] byte ( ` { "apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata": { "name":"test"},"spec": { "rules":[ { "name":"require-network-policy","match": { "resources": { "kinds":["Pod"]}},"validate": { "message":"testpolicy","podSecurity": { "level": "baseline","version":"v1.24","exclude":[ { "controlName":"SELinux","restrictedField":"spec.containers[*].securityContext.seLinuxOptions.role","images":["nginx"],"values":["baz"]}, { "controlName":"SELinux","restrictedField":"spec.initContainers[*].securityContext.seLinuxOptions.role","images":["nodejs"],"values":["init-baz"]}]}}}]}} ` ) ,
expectedControllers : PodControllers ,
} ,
2022-09-12 00:03:35 -07:00
{
name : "rule-with-validate-podsecurity" ,
policy : [ ] byte ( ` { "apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata": { "name":"pod-security"},"spec": { "validationFailureAction":"enforce","rules":[ { "name":"restricted","match": { "all":[ { "resources": { "kinds":["Pod"]}}]},"validate": { "podSecurity": { "level":"restricted","version":"v1.24"}}}]}} ` ) ,
expectedControllers : PodControllers ,
} ,
2022-03-10 16:51:29 +01:00
}
for _ , test := range testCases {
var policy kyverno . ClusterPolicy
err := json . Unmarshal ( test . policy , & policy )
assert . NilError ( t , err )
2022-04-29 11:12:21 +02:00
controllers := GetSupportedControllers ( & policy . Spec )
2022-03-10 16:51:29 +01:00
var expectedControllers [ ] string
if test . expectedControllers != "none" {
expectedControllers = strings . Split ( test . expectedControllers , "," )
}
assert . DeepEqual ( t , expectedControllers , controllers )
}
}
func Test_GetRequestedControllers ( t * testing . T ) {
testCases := [ ] struct {
name string
meta metav1 . ObjectMeta
expectedControllers [ ] string
} {
{
name : "annotations-nil" ,
meta : metav1 . ObjectMeta { } ,
expectedControllers : nil ,
} ,
{
name : "annotation-not-set" ,
meta : metav1 . ObjectMeta { Annotations : map [ string ] string { } } ,
expectedControllers : nil ,
} ,
{
name : "annotation-empty" ,
2022-03-22 21:43:19 +01:00
meta : metav1 . ObjectMeta { Annotations : map [ string ] string { kyverno . PodControllersAnnotation : "" } } ,
2022-03-10 16:51:29 +01:00
expectedControllers : nil ,
} ,
{
name : "annotation-none" ,
2022-03-22 21:43:19 +01:00
meta : metav1 . ObjectMeta { Annotations : map [ string ] string { kyverno . PodControllersAnnotation : "none" } } ,
2022-03-10 16:51:29 +01:00
expectedControllers : [ ] string { } ,
} ,
{
name : "annotation-job" ,
2022-03-22 21:43:19 +01:00
meta : metav1 . ObjectMeta { Annotations : map [ string ] string { kyverno . PodControllersAnnotation : "Job" } } ,
2022-03-10 16:51:29 +01:00
expectedControllers : [ ] string { "Job" } ,
} ,
{
name : "annotation-job-deployment" ,
2022-03-22 21:43:19 +01:00
meta : metav1 . ObjectMeta { Annotations : map [ string ] string { kyverno . PodControllersAnnotation : "Job,Deployment" } } ,
2022-03-10 16:51:29 +01:00
expectedControllers : [ ] string { "Job" , "Deployment" } ,
} ,
}
for _ , test := range testCases {
2022-03-25 15:43:47 +01:00
controllers := GetRequestedControllers ( & test . meta )
2022-03-10 16:51:29 +01:00
assert . DeepEqual ( t , test . expectedControllers , controllers )
}
}
2022-03-02 00:19:31 +01:00
func Test_Any ( t * testing . T ) {
dir , err := os . Getwd ( )
baseDir := filepath . Dir ( filepath . Dir ( dir ) )
assert . NilError ( t , err )
2022-09-30 15:25:19 +08:00
file , err := os . ReadFile ( baseDir + "/test/best_practices/disallow_bind_mounts.yaml" )
2022-03-02 00:19:31 +01:00
if err != nil {
t . Log ( err )
}
2022-09-06 17:16:44 +02:00
policies , err := yamlutils . GetPolicy ( file )
2022-03-02 00:19:31 +01:00
if err != nil {
t . Log ( err )
}
policy := policies [ 0 ]
2022-03-31 08:44:00 +02:00
spec := policy . GetSpec ( )
spec . Rules [ 0 ] . MatchResources . Any = kyverno . ResourceFilters {
2022-03-02 00:19:31 +01:00
{
ResourceDescription : kyverno . ResourceDescription {
Kinds : [ ] string { "Pod" } ,
} ,
} ,
}
2022-04-29 11:12:21 +02:00
rulePatches , errs := GenerateRulePatches ( spec , PodControllers )
2022-03-02 00:19:31 +01:00
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"]}}],"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 )
2022-09-30 15:25:19 +08:00
file , err := os . ReadFile ( baseDir + "/test/best_practices/disallow_bind_mounts.yaml" )
2022-03-02 00:19:31 +01:00
if err != nil {
t . Log ( err )
}
2022-09-06 17:16:44 +02:00
policies , err := yamlutils . GetPolicy ( file )
2022-03-02 00:19:31 +01:00
if err != nil {
t . Log ( err )
}
policy := policies [ 0 ]
2022-03-31 08:44:00 +02:00
spec := policy . GetSpec ( )
spec . Rules [ 0 ] . MatchResources . All = kyverno . ResourceFilters {
2022-03-02 00:19:31 +01:00
{
ResourceDescription : kyverno . ResourceDescription {
Kinds : [ ] string { "Pod" } ,
} ,
} ,
}
2022-04-29 11:12:21 +02:00
rulePatches , errs := GenerateRulePatches ( spec , PodControllers )
2022-03-02 00:19:31 +01:00
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"]}}],"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 )
2022-09-30 15:25:19 +08:00
file , err := os . ReadFile ( baseDir + "/test/best_practices/disallow_bind_mounts.yaml" )
2022-03-02 00:19:31 +01:00
if err != nil {
t . Log ( err )
}
2022-09-06 17:16:44 +02:00
policies , err := yamlutils . GetPolicy ( file )
2022-03-02 00:19:31 +01:00
if err != nil {
t . Log ( err )
}
policy := policies [ 0 ]
2022-03-31 08:44:00 +02:00
spec := policy . GetSpec ( )
spec . Rules [ 0 ] . ExcludeResources . Namespaces = [ ] string { "fake-namespce" }
2022-03-02 00:19:31 +01:00
2022-04-29 11:12:21 +02:00
rulePatches , errs := GenerateRulePatches ( spec , PodControllers )
2022-03-02 00:19:31 +01:00
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"]}},"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 ) {
2022-03-09 14:48:04 +01:00
controllers := PodControllerCronJob
2022-03-02 00:19:31 +01:00
dir , err := os . Getwd ( )
baseDir := filepath . Dir ( filepath . Dir ( dir ) )
assert . NilError ( t , err )
2022-09-30 15:25:19 +08:00
file , err := os . ReadFile ( baseDir + "/test/best_practices/disallow_bind_mounts.yaml" )
2022-03-02 00:19:31 +01:00
if err != nil {
t . Log ( err )
}
2022-09-06 17:16:44 +02:00
policies , err := yamlutils . GetPolicy ( file )
2022-03-02 00:19:31 +01:00
if err != nil {
t . Log ( err )
}
policy := policies [ 0 ]
policy . SetAnnotations ( map [ string ] string {
2022-03-09 14:48:04 +01:00
kyverno . PodControllersAnnotation : controllers ,
2022-03-02 00:19:31 +01:00
} )
2022-04-29 11:12:21 +02:00
rulePatches , errs := GenerateRulePatches ( policy . GetSpec ( ) , controllers )
2022-03-02 00:19:31 +01:00
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 )
2022-09-30 15:25:19 +08:00
file , err := os . ReadFile ( baseDir + "/test/policy/mutate/policy_mutate_pod_foreach_with_context.yaml" )
2022-03-02 00:19:31 +01:00
if err != nil {
t . Log ( err )
}
2022-09-06 17:16:44 +02:00
policies , err := yamlutils . GetPolicy ( file )
2022-03-02 00:19:31 +01:00
if err != nil {
t . Log ( err )
}
policy := policies [ 0 ]
2022-03-31 08:44:00 +02:00
spec := policy . GetSpec ( )
spec . Rules [ 0 ] . ExcludeResources . Namespaces = [ ] string { "fake-namespce" }
2022-03-02 00:19:31 +01:00
2022-04-29 11:12:21 +02:00
rulePatches , errs := GenerateRulePatches ( spec , PodControllers )
2022-03-02 00:19:31 +01:00
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"]}},"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 ) {
2022-03-09 14:48:04 +01:00
controllers := PodControllerCronJob
2022-03-02 00:19:31 +01:00
dir , err := os . Getwd ( )
baseDir := filepath . Dir ( filepath . Dir ( dir ) )
assert . NilError ( t , err )
2022-09-30 15:25:19 +08:00
file , err := os . ReadFile ( baseDir + "/test/best_practices/disallow_bind_mounts.yaml" )
2022-03-02 00:19:31 +01:00
if err != nil {
t . Log ( err )
}
2022-09-06 17:16:44 +02:00
policies , err := yamlutils . GetPolicy ( file )
2022-03-02 00:19:31 +01:00
if err != nil {
t . Log ( err )
}
policy := policies [ 0 ]
policy . SetAnnotations ( map [ string ] string {
2022-03-09 14:48:04 +01:00
kyverno . PodControllersAnnotation : controllers ,
2022-03-02 00:19:31 +01:00
} )
2022-03-31 08:44:00 +02:00
spec := policy . GetSpec ( )
rule := spec . Rules [ 0 ] . DeepCopy ( )
2022-03-02 00:19:31 +01:00
rule . ExcludeResources . Kinds = [ ] string { "Pod" }
rule . ExcludeResources . Namespaces = [ ] string { "test" }
2022-03-31 08:44:00 +02:00
spec . Rules [ 0 ] = * rule
2022-03-02 00:19:31 +01:00
2022-04-29 11:12:21 +02:00
rulePatches , errs := GenerateRulePatches ( spec , controllers )
2022-03-02 00:19:31 +01:00
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 ) {
2022-03-09 14:48:04 +01:00
controllers := strings . Join ( [ ] string { PodControllerCronJob , "Deployment" } , "," )
2022-03-02 00:19:31 +01:00
dir , err := os . Getwd ( )
baseDir := filepath . Dir ( filepath . Dir ( dir ) )
assert . NilError ( t , err )
2022-09-30 15:25:19 +08:00
file , err := os . ReadFile ( baseDir + "/test/best_practices/disallow_bind_mounts.yaml" )
2022-03-02 00:19:31 +01:00
if err != nil {
t . Log ( err )
}
2022-09-06 17:16:44 +02:00
policies , err := yamlutils . GetPolicy ( file )
2022-03-02 00:19:31 +01:00
if err != nil {
t . Log ( err )
}
policy := policies [ 0 ]
policy . SetAnnotations ( map [ string ] string {
2022-03-09 14:48:04 +01:00
kyverno . PodControllersAnnotation : controllers ,
2022-03-02 00:19:31 +01:00
} )
2022-04-29 11:12:21 +02:00
rulePatches , errs := GenerateRulePatches ( policy . GetSpec ( ) , controllers )
2022-03-02 00:19:31 +01:00
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 Test_UpdateVariablePath ( t * testing . T ) {
dir , err := os . Getwd ( )
baseDir := filepath . Dir ( filepath . Dir ( dir ) )
assert . NilError ( t , err )
2022-09-30 15:25:19 +08:00
file , err := os . ReadFile ( baseDir + "/test/best_practices/select-secrets.yaml" )
2022-03-02 00:19:31 +01:00
if err != nil {
t . Log ( err )
}
2022-09-06 17:16:44 +02:00
policies , err := yamlutils . GetPolicy ( file )
2022-03-02 00:19:31 +01:00
if err != nil {
t . Log ( err )
}
policy := policies [ 0 ]
2022-04-29 11:12:21 +02:00
rulePatches , errs := GenerateRulePatches ( policy . GetSpec ( ) , PodControllers )
2022-03-02 00:19:31 +01:00
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"]}},"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/*"}]}}}}}}}}} ` ) ,
}
2022-04-05 17:12:22 +02:00
for i , ep := range expectedPatches {
assert . Equal ( t , string ( rulePatches [ i ] ) , string ( ep ) ,
fmt . Sprintf ( "unexpected patch: %s\nexpected: %s" , rulePatches [ i ] , ep ) )
}
2022-03-02 00:19:31 +01:00
}
func Test_Deny ( t * testing . T ) {
dir , err := os . Getwd ( )
baseDir := filepath . Dir ( filepath . Dir ( dir ) )
assert . NilError ( t , err )
2022-09-30 15:25:19 +08:00
file , err := os . ReadFile ( baseDir + "/test/policy/deny/policy.yaml" )
2022-03-02 00:19:31 +01:00
if err != nil {
t . Log ( err )
}
2022-09-06 17:16:44 +02:00
policies , err := yamlutils . GetPolicy ( file )
2022-03-02 00:19:31 +01:00
if err != nil {
t . Log ( err )
}
policy := policies [ 0 ]
2022-03-31 08:44:00 +02:00
spec := policy . GetSpec ( )
spec . Rules [ 0 ] . MatchResources . Any = kyverno . ResourceFilters {
2022-03-02 00:19:31 +01:00
{
ResourceDescription : kyverno . ResourceDescription {
Kinds : [ ] string { "Pod" } ,
} ,
} ,
}
2022-04-29 11:12:21 +02:00
rulePatches , errs := GenerateRulePatches ( spec , PodControllers )
2022-03-02 00:19:31 +01:00
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"]}}],"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 ) )
}
}
2022-04-29 13:48:22 +02:00
func Test_ComputeRules ( t * testing . T ) {
intPtr := func ( i int ) * int { return & i }
testCases := [ ] struct {
name string
policy string
expectedRules [ ] kyverno . Rule
} {
{
name : "rule-with-match-name" ,
policy : `
apiVersion : kyverno . io / v1
kind : ClusterPolicy
metadata :
name : check - image
spec :
validationFailureAction : enforce
background : false
webhookTimeoutSeconds : 30
failurePolicy : Fail
rules :
- name : check - image
match :
resources :
kinds :
- Pod
verifyImages :
- imageReferences :
- "*"
attestors :
- count : 1
entries :
- keyless :
roots : | -
-- -- - BEGIN CERTIFICATE -- -- -
MIIDjTCCAnWgAwIBAgIQb8yUrbw3aYZAubIjOJkFBjANBgkqhkiG9w0BAQsFADBZ
MRMwEQYKCZImiZPyLGQBGRYDY29tMRowGAYKCZImiZPyLGQBGRYKdmVuYWZpZGVt
bzEmMCQGA1UEAxMddmVuYWZpZGVtby1FQzJBTUFaLVFOSVI4OUktQ0EwHhcNMjAx
MjE0MjEzNzAzWhcNMjUxMjE0MjE0NzAzWjBZMRMwEQYKCZImiZPyLGQBGRYDY29t
MRowGAYKCZImiZPyLGQBGRYKdmVuYWZpZGVtbzEmMCQGA1UEAxMddmVuYWZpZGVt
by1FQzJBTUFaLVFOSVI4OUktQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQC5CTVQczGnh77yNxq + BGh5ff0qNcRTkFll + y8lJbMPHevebF7JLWBQTGS7
9 aHIqUQLjy9sPOkdMrDh / vOZNVhVrHon9uwepF81dUMJ9lMbfQSI / tytp78f0z6b
DVRHYZr / taYSkqNPT2FuHOijc7Y + oB3Q1DzPSoBc3a6I5DM6ET6O2GZWo3mqpImG
J8 + dNllYgjVKEuxuPqQjT7VD4fB2GqJbwwL0E8bSyfsgMV9Y + qHdznkm8v + TbYoc
9 uS83f1fjjp98D7VtWpSC4O / 27 JWgEED / BB58sOipUQHiECr6dD5VWGJ9fnVOV2i
vHqj9cKS6BGMkAh99ss0Bu / 3 DEBxAgMBAAGjUTBPMAsGA1UdDwQEAwIBhjAPBgNV
HRMBAf8EBTADAQH / MB0GA1UdDgQWBBTuZecNgrj3Gdv9XpekFZuIkYtu9jAQBgkr
BgEEAYI3FQEEAwIBADANBgkqhkiG9w0BAQsFAAOCAQEADPNrGypaKliXJ + H7gt6b
NJSBdWB9EV63CdvxjLOuqvp3IUu8KIV2mMsulEjxjAb5kya0SURJVFvr9rrLVxvR
e6B2SJUGUKJkX1Cq4nIthwGfJTEnypYhqMKkfUYjqfszU + 1 CerRD2ZTJHeKZsc7M
GdxLXeocztZ220idf6uDYeNLnGLBfkodEgFV0RmrlnHQYQdRqj3hjClLAkNqKVrz
rxNyyQvgaswK + 4 kHAPQhv + ipx4Q0eeROpp3prJ + dD0hhk8niQSKWQWZHyElhzIKv
FlDw3fzPhtberBblY4Y9u525ev999SogMBTXoSkfajRR2ol10xUxY60kVbqoEUln
kA ==
-- -- - END CERTIFICATE -- -- - ` ,
expectedRules : [ ] kyverno . Rule { {
Name : "check-image" ,
MatchResources : kyverno . MatchResources {
ResourceDescription : kyverno . ResourceDescription {
Kinds : [ ] string { "Pod" } ,
} ,
} ,
VerifyImages : [ ] kyverno . ImageVerification { {
ImageReferences : [ ] string { "*" } ,
Attestors : [ ] kyverno . AttestorSet { {
Count : intPtr ( 1 ) ,
Entries : [ ] kyverno . Attestor { {
Keyless : & kyverno . KeylessAttestor {
Roots : ` -- -- - BEGIN CERTIFICATE -- -- -
MIIDjTCCAnWgAwIBAgIQb8yUrbw3aYZAubIjOJkFBjANBgkqhkiG9w0BAQsFADBZ
MRMwEQYKCZImiZPyLGQBGRYDY29tMRowGAYKCZImiZPyLGQBGRYKdmVuYWZpZGVt
bzEmMCQGA1UEAxMddmVuYWZpZGVtby1FQzJBTUFaLVFOSVI4OUktQ0EwHhcNMjAx
MjE0MjEzNzAzWhcNMjUxMjE0MjE0NzAzWjBZMRMwEQYKCZImiZPyLGQBGRYDY29t
MRowGAYKCZImiZPyLGQBGRYKdmVuYWZpZGVtbzEmMCQGA1UEAxMddmVuYWZpZGVt
by1FQzJBTUFaLVFOSVI4OUktQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQC5CTVQczGnh77yNxq + BGh5ff0qNcRTkFll + y8lJbMPHevebF7JLWBQTGS7
9 aHIqUQLjy9sPOkdMrDh / vOZNVhVrHon9uwepF81dUMJ9lMbfQSI / tytp78f0z6b
DVRHYZr / taYSkqNPT2FuHOijc7Y + oB3Q1DzPSoBc3a6I5DM6ET6O2GZWo3mqpImG
J8 + dNllYgjVKEuxuPqQjT7VD4fB2GqJbwwL0E8bSyfsgMV9Y + qHdznkm8v + TbYoc
9 uS83f1fjjp98D7VtWpSC4O / 27 JWgEED / BB58sOipUQHiECr6dD5VWGJ9fnVOV2i
vHqj9cKS6BGMkAh99ss0Bu / 3 DEBxAgMBAAGjUTBPMAsGA1UdDwQEAwIBhjAPBgNV
HRMBAf8EBTADAQH / MB0GA1UdDgQWBBTuZecNgrj3Gdv9XpekFZuIkYtu9jAQBgkr
BgEEAYI3FQEEAwIBADANBgkqhkiG9w0BAQsFAAOCAQEADPNrGypaKliXJ + H7gt6b
NJSBdWB9EV63CdvxjLOuqvp3IUu8KIV2mMsulEjxjAb5kya0SURJVFvr9rrLVxvR
e6B2SJUGUKJkX1Cq4nIthwGfJTEnypYhqMKkfUYjqfszU + 1 CerRD2ZTJHeKZsc7M
GdxLXeocztZ220idf6uDYeNLnGLBfkodEgFV0RmrlnHQYQdRqj3hjClLAkNqKVrz
rxNyyQvgaswK + 4 kHAPQhv + ipx4Q0eeROpp3prJ + dD0hhk8niQSKWQWZHyElhzIKv
FlDw3fzPhtberBblY4Y9u525ev999SogMBTXoSkfajRR2ol10xUxY60kVbqoEUln
kA ==
-- -- - END CERTIFICATE -- -- - ` ,
} ,
} } ,
} } ,
} } ,
} , {
Name : "autogen-check-image" ,
MatchResources : kyverno . MatchResources {
ResourceDescription : kyverno . ResourceDescription {
Kinds : [ ] string { "DaemonSet" , "Deployment" , "Job" , "StatefulSet" } ,
} ,
} ,
VerifyImages : [ ] kyverno . ImageVerification { {
ImageReferences : [ ] string { "*" } ,
Attestors : [ ] kyverno . AttestorSet { {
Count : intPtr ( 1 ) ,
Entries : [ ] kyverno . Attestor { {
Keyless : & kyverno . KeylessAttestor {
Roots : ` -- -- - BEGIN CERTIFICATE -- -- -
MIIDjTCCAnWgAwIBAgIQb8yUrbw3aYZAubIjOJkFBjANBgkqhkiG9w0BAQsFADBZ
MRMwEQYKCZImiZPyLGQBGRYDY29tMRowGAYKCZImiZPyLGQBGRYKdmVuYWZpZGVt
bzEmMCQGA1UEAxMddmVuYWZpZGVtby1FQzJBTUFaLVFOSVI4OUktQ0EwHhcNMjAx
MjE0MjEzNzAzWhcNMjUxMjE0MjE0NzAzWjBZMRMwEQYKCZImiZPyLGQBGRYDY29t
MRowGAYKCZImiZPyLGQBGRYKdmVuYWZpZGVtbzEmMCQGA1UEAxMddmVuYWZpZGVt
by1FQzJBTUFaLVFOSVI4OUktQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQC5CTVQczGnh77yNxq + BGh5ff0qNcRTkFll + y8lJbMPHevebF7JLWBQTGS7
9 aHIqUQLjy9sPOkdMrDh / vOZNVhVrHon9uwepF81dUMJ9lMbfQSI / tytp78f0z6b
DVRHYZr / taYSkqNPT2FuHOijc7Y + oB3Q1DzPSoBc3a6I5DM6ET6O2GZWo3mqpImG
J8 + dNllYgjVKEuxuPqQjT7VD4fB2GqJbwwL0E8bSyfsgMV9Y + qHdznkm8v + TbYoc
9 uS83f1fjjp98D7VtWpSC4O / 27 JWgEED / BB58sOipUQHiECr6dD5VWGJ9fnVOV2i
vHqj9cKS6BGMkAh99ss0Bu / 3 DEBxAgMBAAGjUTBPMAsGA1UdDwQEAwIBhjAPBgNV
HRMBAf8EBTADAQH / MB0GA1UdDgQWBBTuZecNgrj3Gdv9XpekFZuIkYtu9jAQBgkr
BgEEAYI3FQEEAwIBADANBgkqhkiG9w0BAQsFAAOCAQEADPNrGypaKliXJ + H7gt6b
NJSBdWB9EV63CdvxjLOuqvp3IUu8KIV2mMsulEjxjAb5kya0SURJVFvr9rrLVxvR
e6B2SJUGUKJkX1Cq4nIthwGfJTEnypYhqMKkfUYjqfszU + 1 CerRD2ZTJHeKZsc7M
GdxLXeocztZ220idf6uDYeNLnGLBfkodEgFV0RmrlnHQYQdRqj3hjClLAkNqKVrz
rxNyyQvgaswK + 4 kHAPQhv + ipx4Q0eeROpp3prJ + dD0hhk8niQSKWQWZHyElhzIKv
FlDw3fzPhtberBblY4Y9u525ev999SogMBTXoSkfajRR2ol10xUxY60kVbqoEUln
kA ==
-- -- - END CERTIFICATE -- -- - ` ,
} ,
} } ,
} } ,
} } ,
} , {
Name : "autogen-cronjob-check-image" ,
MatchResources : kyverno . MatchResources {
ResourceDescription : kyverno . ResourceDescription {
Kinds : [ ] string { "CronJob" } ,
} ,
} ,
VerifyImages : [ ] kyverno . ImageVerification { {
ImageReferences : [ ] string { "*" } ,
Attestors : [ ] kyverno . AttestorSet { {
Count : intPtr ( 1 ) ,
Entries : [ ] kyverno . Attestor { {
Keyless : & kyverno . KeylessAttestor {
Roots : ` -- -- - BEGIN CERTIFICATE -- -- -
MIIDjTCCAnWgAwIBAgIQb8yUrbw3aYZAubIjOJkFBjANBgkqhkiG9w0BAQsFADBZ
MRMwEQYKCZImiZPyLGQBGRYDY29tMRowGAYKCZImiZPyLGQBGRYKdmVuYWZpZGVt
bzEmMCQGA1UEAxMddmVuYWZpZGVtby1FQzJBTUFaLVFOSVI4OUktQ0EwHhcNMjAx
MjE0MjEzNzAzWhcNMjUxMjE0MjE0NzAzWjBZMRMwEQYKCZImiZPyLGQBGRYDY29t
MRowGAYKCZImiZPyLGQBGRYKdmVuYWZpZGVtbzEmMCQGA1UEAxMddmVuYWZpZGVt
by1FQzJBTUFaLVFOSVI4OUktQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQC5CTVQczGnh77yNxq + BGh5ff0qNcRTkFll + y8lJbMPHevebF7JLWBQTGS7
9 aHIqUQLjy9sPOkdMrDh / vOZNVhVrHon9uwepF81dUMJ9lMbfQSI / tytp78f0z6b
DVRHYZr / taYSkqNPT2FuHOijc7Y + oB3Q1DzPSoBc3a6I5DM6ET6O2GZWo3mqpImG
J8 + dNllYgjVKEuxuPqQjT7VD4fB2GqJbwwL0E8bSyfsgMV9Y + qHdznkm8v + TbYoc
9 uS83f1fjjp98D7VtWpSC4O / 27 JWgEED / BB58sOipUQHiECr6dD5VWGJ9fnVOV2i
vHqj9cKS6BGMkAh99ss0Bu / 3 DEBxAgMBAAGjUTBPMAsGA1UdDwQEAwIBhjAPBgNV
HRMBAf8EBTADAQH / MB0GA1UdDgQWBBTuZecNgrj3Gdv9XpekFZuIkYtu9jAQBgkr
BgEEAYI3FQEEAwIBADANBgkqhkiG9w0BAQsFAAOCAQEADPNrGypaKliXJ + H7gt6b
NJSBdWB9EV63CdvxjLOuqvp3IUu8KIV2mMsulEjxjAb5kya0SURJVFvr9rrLVxvR
e6B2SJUGUKJkX1Cq4nIthwGfJTEnypYhqMKkfUYjqfszU + 1 CerRD2ZTJHeKZsc7M
GdxLXeocztZ220idf6uDYeNLnGLBfkodEgFV0RmrlnHQYQdRqj3hjClLAkNqKVrz
rxNyyQvgaswK + 4 kHAPQhv + ipx4Q0eeROpp3prJ + dD0hhk8niQSKWQWZHyElhzIKv
FlDw3fzPhtberBblY4Y9u525ev999SogMBTXoSkfajRR2ol10xUxY60kVbqoEUln
kA ==
-- -- - END CERTIFICATE -- -- - ` ,
} ,
} } ,
} } ,
} } ,
} } ,
} ,
}
for _ , test := range testCases {
2022-09-06 17:16:44 +02:00
policies , err := yamlutils . GetPolicy ( [ ] byte ( test . policy ) )
2022-04-29 13:48:22 +02:00
assert . NilError ( t , err )
assert . Equal ( t , 1 , len ( policies ) )
rules := computeRules ( policies [ 0 ] )
assert . DeepEqual ( t , test . expectedRules , rules )
}
}
2022-09-12 00:03:35 -07:00
func Test_PodSecurityWithNoExceptions ( t * testing . T ) {
policy := [ ] byte ( ` { "apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata": { "name":"pod-security"},"spec": { "validationFailureAction":"enforce","rules":[ { "name":"restricted","match": { "all":[ { "resources": { "kinds":["Pod"]}}]},"validate": { "podSecurity": { "level":"restricted","version":"v1.24"}}}]}} ` )
policies , err := yamlutils . GetPolicy ( [ ] byte ( policy ) )
assert . NilError ( t , err )
assert . Equal ( t , 1 , len ( policies ) )
rules := computeRules ( policies [ 0 ] )
assert . Equal ( t , 3 , len ( rules ) )
}