1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-09 17:37:12 +00:00
kyverno/pkg/pss/utils.go
ToLToL 1b9a2fca21
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 09:16:31 +00:00

144 lines
5.1 KiB
Go

package pss
import (
"strings"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/utils"
corev1 "k8s.io/api/core/v1"
"k8s.io/pod-security-admission/policy"
)
func containsContainer(containers interface{}, containerName string) bool {
switch v := containers.(type) {
case []interface{}:
for _, container := range v {
switch v := container.(type) {
case corev1.Container:
if v.Name == containerName {
return true
}
case corev1.EphemeralContainer:
if v.Name == containerName {
return true
}
}
}
case []corev1.Container:
for _, container := range v {
if container.Name == containerName {
return true
}
}
case []corev1.EphemeralContainer:
for _, container := range v {
if container.Name == containerName {
return true
}
}
}
return false
}
// Get copy of pod with containers (containers, initContainers, ephemeralContainers) matching the exclude.image
func getPodWithMatchingContainers(exclude []kyvernov1.PodSecurityStandard, pod *corev1.Pod) (podCopy corev1.Pod) {
podCopy = *pod
podCopy.Spec.Containers = []corev1.Container{}
podCopy.Spec.InitContainers = []corev1.Container{}
podCopy.Spec.EphemeralContainers = []corev1.EphemeralContainer{}
for _, container := range pod.Spec.Containers {
for _, excludeRule := range exclude {
// Ignore all restrictedFields when we only specify the `controlName` with no `restrictedField`
controlNameOnly := excludeRule.RestrictedField == ""
if !utils.ContainsString(excludeRule.Images, container.Image) {
continue
}
if strings.Contains(excludeRule.RestrictedField, "spec.containers[*]") || controlNameOnly {
// Add to matchingContainers if either it's empty or is unique
if len(podCopy.Spec.Containers) == 0 || !containsContainer(podCopy.Spec.Containers, container.Name) {
podCopy.Spec.Containers = append(podCopy.Spec.Containers, container)
}
}
}
}
for _, container := range pod.Spec.InitContainers {
for _, excludeRule := range exclude {
// Ignore all restrictedFields when we only specify the `controlName` with no `restrictedField`
controlNameOnly := excludeRule.RestrictedField == ""
if !utils.ContainsString(excludeRule.Images, container.Image) {
continue
}
if strings.Contains(excludeRule.RestrictedField, "spec.initContainers[*]") || controlNameOnly {
// Add to matchingContainers if either it's empty or is unique
if len(podCopy.Spec.InitContainers) == 0 || !containsContainer(podCopy.Spec.InitContainers, container.Name) {
podCopy.Spec.InitContainers = append(podCopy.Spec.InitContainers, container)
}
}
}
}
for _, container := range pod.Spec.EphemeralContainers {
for _, excludeRule := range exclude {
// Ignore all restrictedFields when we only specify the `controlName` with no `restrictedField`
controlNameOnly := excludeRule.RestrictedField == ""
if !utils.ContainsString(excludeRule.Images, container.Image) {
continue
}
if strings.Contains(excludeRule.RestrictedField, "spec.ephemeralContainers[*]") || controlNameOnly {
// Add to matchingContainers if either it's empty or is unique
if len(podCopy.Spec.EphemeralContainers) == 0 || !containsContainer(podCopy.Spec.EphemeralContainers, container.Name) {
podCopy.Spec.EphemeralContainers = append(podCopy.Spec.EphemeralContainers, container)
}
}
}
}
return podCopy
}
// Get containers NOT matching images specified in Exclude values
func getPodWithNotMatchingContainers(exclude []kyvernov1.PodSecurityStandard, pod *corev1.Pod, podWithMatchingContainers *corev1.Pod) (podCopy corev1.Pod) {
// Only copy containers because we have already evaluated the pod-level controls
// e.g.: spec.securityContext.hostProcess
podCopy.Spec.Containers = []corev1.Container{}
podCopy.Spec.InitContainers = []corev1.Container{}
podCopy.Spec.EphemeralContainers = []corev1.EphemeralContainer{}
// Append containers that are not in podWithMatchingContainers already evaluated in EvaluatePod()
for _, container := range pod.Spec.Containers {
if !containsContainer(podWithMatchingContainers.Spec.Containers, container.Name) {
podCopy.Spec.Containers = append(podCopy.Spec.Containers, container)
}
}
for _, container := range pod.Spec.InitContainers {
if !containsContainer(podWithMatchingContainers.Spec.InitContainers, container.Name) {
podCopy.Spec.InitContainers = append(podCopy.Spec.InitContainers, container)
}
}
for _, container := range pod.Spec.EphemeralContainers {
if !containsContainer(podWithMatchingContainers.Spec.EphemeralContainers, container.Name) {
podCopy.Spec.EphemeralContainers = append(podCopy.Spec.EphemeralContainers, container)
}
}
return podCopy
}
// Get restrictedFields from Check.ID
func getRestrictedFields(check policy.Check) []restrictedField {
for _, control := range PSS_controls_to_check_id {
for _, checkID := range control {
if check.ID == checkID {
return PSS_controls[checkID]
}
}
}
return nil
}
func containsContainerLevelControl(restrictedFields []restrictedField) bool {
for _, restrictedField := range restrictedFields {
if strings.Contains(restrictedField.path, "ontainers[*]") {
return true
}
}
return false
}