1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-15 17:51:20 +00:00

fix: add podSecurity validation checks for exceptions (#9817)

Signed-off-by: Mariam Fahmy <mariam.fahmy@nirmata.com>
This commit is contained in:
Mariam Fahmy 2024-02-28 10:21:10 +02:00 committed by GitHub
parent 511df7a466
commit 07a6bf42f5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 135 additions and 30 deletions

View file

@ -5,6 +5,7 @@ import (
"fmt"
"github.com/kyverno/kyverno/pkg/engine/variables/regex"
"github.com/kyverno/kyverno/pkg/pss/utils"
"github.com/sigstore/k8s-manifest-sigstore/pkg/k8smanifest"
admissionv1 "k8s.io/api/admission/v1"
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
@ -469,6 +470,28 @@ type PodSecurityStandard struct {
Values []string `json:"values,omitempty" yaml:"values,omitempty"`
}
func (pss *PodSecurityStandard) Validate(path *field.Path) (errs field.ErrorList) {
// container level control must specify images
if containsString(utils.PSS_container_level_control, pss.ControlName) {
if len(pss.Images) == 0 {
errs = append(errs, field.Invalid(path.Child("controlName"), pss.ControlName, "exclude.images must be specified for the container level control"))
}
} else if containsString(utils.PSS_pod_level_control, pss.ControlName) {
if len(pss.Images) != 0 {
errs = append(errs, field.Invalid(path.Child("controlName"), pss.ControlName, "exclude.images must not be specified for the pod level control"))
}
}
if pss.RestrictedField != "" && len(pss.Values) == 0 {
errs = append(errs, field.Forbidden(path.Child("values"), "values is required"))
}
if pss.RestrictedField == "" && len(pss.Values) != 0 {
errs = append(errs, field.Forbidden(path.Child("restrictedField"), "restrictedField is required"))
}
return errs
}
// CEL allows validation checks using the Common Expression Language (https://kubernetes.io/docs/reference/using-api/cel/).
type CEL struct {
// Expressions is a list of CELExpression types.

View file

@ -396,16 +396,7 @@ func (r *Rule) ValidatePSaControlNames(path *field.Path) (errs field.ErrorList)
}
for idx, exclude := range podSecurity.Exclude {
// container level control must specify images
if containsString(utils.PSS_container_level_control, exclude.ControlName) {
if len(exclude.Images) == 0 {
errs = append(errs, field.Invalid(path.Child("podSecurity").Child("exclude").Index(idx).Child("controlName"), exclude.ControlName, "exclude.images must be specified for the container level control"))
}
} else if containsString(utils.PSS_pod_level_control, exclude.ControlName) {
if len(exclude.Images) != 0 {
errs = append(errs, field.Invalid(path.Child("podSecurity").Child("exclude").Index(idx).Child("controlName"), exclude.ControlName, "exclude.images must not be specified for the pod level control"))
}
}
errs = append(errs, exclude.Validate(path.Child("podSecurity").Child("exclude").Index(idx))...)
if containsString([]string{"Seccomp", "Capabilities"}, exclude.ControlName) {
continue

View file

@ -100,6 +100,11 @@ func (p *PolicyExceptionSpec) Validate(path *field.Path) (errs field.ErrorList)
for i, e := range p.Exceptions {
errs = append(errs, e.Validate(exceptionsPath.Index(i))...)
}
podSecuityPath := path.Child("podSecurity")
for i, p := range p.PodSecurity {
errs = append(errs, p.Validate(podSecuityPath.Index(i))...)
}
return errs
}

View file

@ -68,20 +68,6 @@ func (v *Validate) Validate(ctx context.Context) (string, error) {
}
}
if v.rule.PodSecurity != nil {
if len(v.rule.PodSecurity.Exclude) != 0 {
for _, exclude := range v.rule.PodSecurity.Exclude {
if exclude.RestrictedField != "" && len(exclude.Values) == 0 {
return "", fmt.Errorf("podSecurity.exclude.values is required")
}
if exclude.RestrictedField == "" && len(exclude.Values) != 0 {
return "", fmt.Errorf("podSecurity.exclude.restrictedField is required")
}
}
}
}
if v.rule.CEL != nil {
for _, expression := range v.rule.CEL.Expressions {
if expression.Expression == "" {

View file

@ -0,0 +1,34 @@
apiVersion: chainsaw.kyverno.io/v1alpha1
kind: Test
metadata:
creationTimestamp: null
name: invalid-pod-security-exceptions
spec:
steps:
- name: Apply the first policy exception
try:
- script:
content: kubectl apply -f exception-1.yaml
check:
($error != null): true
# This check ensures the contents of stderr are exactly as shown.
($stderr): |-
Error from server: error when creating "exception-1.yaml": admission webhook "kyverno-svc.kyverno.svc" denied the request: [spec.podSecurity[0].controlName: Invalid value: "Capabilities": exclude.images must be specified for the container level control, spec.podSecurity[3].controlName: Invalid value: "Privilege Escalation": exclude.images must be specified for the container level control]
- name: Apply the second policy exception
try:
- script:
content: kubectl apply -f exception-2.yaml
check:
($error != null): true
# This check ensures the contents of stderr are exactly as shown.
($stderr): |-
Error from server: error when creating "exception-2.yaml": admission webhook "kyverno-svc.kyverno.svc" denied the request: spec.podSecurity[0].values: Forbidden: values is required
- name: Apply the third policy exception
try:
- script:
content: kubectl apply -f exception-3.yaml
check:
($error != null): true
# This check ensures the contents of stderr are exactly as shown.
($stderr): |-
Error from server: error when creating "exception-3.yaml": admission webhook "kyverno-svc.kyverno.svc" denied the request: spec.podSecurity[0].restrictedField: Forbidden: restrictedField is required

View file

@ -0,0 +1,23 @@
apiVersion: kyverno.io/v2
kind: PolicyException
metadata:
creationTimestamp: null
name: exception-1
spec:
exceptions:
- policyName: psa
ruleNames:
- restricted
match:
all:
- resources:
kinds:
- Pod
podSecurity:
- controlName: Capabilities
- controlName: Host Namespaces
- controlName: HostPath Volumes
- controlName: Privilege Escalation
- controlName: Running as Non-root
- controlName: Seccomp
- controlName: Volume Types

View file

@ -0,0 +1,20 @@
apiVersion: kyverno.io/v2
kind: PolicyException
metadata:
creationTimestamp: null
name: exception-2
spec:
exceptions:
- policyName: psa
ruleNames:
- restricted
match:
all:
- resources:
kinds:
- Pod
podSecurity:
- controlName: "/proc Mount Type"
images:
- nginx
restrictedField: "spec.containers[*].securityContext.procMount"

View file

@ -0,0 +1,21 @@
apiVersion: kyverno.io/v2
kind: PolicyException
metadata:
creationTimestamp: null
name: exception-3
spec:
exceptions:
- policyName: psa
ruleNames:
- restricted
match:
all:
- resources:
kinds:
- Pod
podSecurity:
- controlName: "/proc Mount Type"
images:
- nginx
values:
- "bar"

View file

@ -10,14 +10,16 @@ spec:
- script:
content: kubectl apply -f policy-1.yaml
check:
# This check ensures that the string "undefined field 'automountServiceAccountToken';" is found
# in stderr or else fails
(contains($stderr, 'podSecurity.exclude.values is required')): true
($error != null): true
# This check ensures the contents of stderr are exactly as shown.
($stderr): |-
Error from server: error when creating "policy-1.yaml": admission webhook "validate-policy.kyverno.svc" denied the request: spec.rules[0].podSecurity.exclude[0].values: Forbidden: values is required
- name: Apply the second policy
try:
- script:
content: kubectl apply -f policy-2.yaml
check:
# This check ensures that the string "podSecurity.exclude.restrictedField is required" is found
# in stderr or else fails
(contains($stderr, 'podSecurity.exclude.restrictedField is required')): true
($error != null): true
# This check ensures the contents of stderr are exactly as shown.
($stderr): |-
Error from server: error when creating "policy-2.yaml": admission webhook "validate-policy.kyverno.svc" denied the request: spec.rules[0].podSecurity.exclude[0].restrictedField: Forbidden: restrictedField is required