mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-09 17:37:12 +00:00
* 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>
144 lines
5.1 KiB
Go
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
|
|
}
|