2020-03-11 18:14:23 -07:00
|
|
|
package validate
|
|
|
|
|
|
|
|
import (
|
2023-03-22 21:14:57 +08:00
|
|
|
"context"
|
2020-03-11 18:14:23 -07:00
|
|
|
"fmt"
|
|
|
|
|
2022-05-17 13:12:43 +02:00
|
|
|
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
2023-01-30 17:47:19 +05:30
|
|
|
"github.com/kyverno/kyverno/pkg/engine/anchor"
|
2020-10-07 11:12:31 -07:00
|
|
|
"github.com/kyverno/kyverno/pkg/policy/common"
|
2020-03-11 18:14:23 -07:00
|
|
|
)
|
|
|
|
|
2021-10-02 16:53:02 -07:00
|
|
|
// Validate validates a 'validate' rule
|
2020-03-11 18:14:23 -07:00
|
|
|
type Validate struct {
|
|
|
|
// rule to hold 'validate' rule specifications
|
2022-05-17 13:12:43 +02:00
|
|
|
rule *kyvernov1.Validation
|
2020-03-11 18:14:23 -07:00
|
|
|
}
|
|
|
|
|
2022-05-17 08:19:03 +02:00
|
|
|
// NewValidateFactory returns a new instance of Mutate validation checker
|
2022-05-17 13:12:43 +02:00
|
|
|
func NewValidateFactory(rule *kyvernov1.Validation) *Validate {
|
2020-03-11 18:14:23 -07:00
|
|
|
m := Validate{
|
|
|
|
rule: rule,
|
|
|
|
}
|
2021-10-02 16:53:02 -07:00
|
|
|
|
2020-03-11 18:14:23 -07:00
|
|
|
return &m
|
|
|
|
}
|
|
|
|
|
2022-05-17 08:19:03 +02:00
|
|
|
// Validate validates the 'validate' rule
|
2023-03-22 21:14:57 +08:00
|
|
|
func (v *Validate) Validate(ctx context.Context) (string, error) {
|
2021-10-02 16:53:02 -07:00
|
|
|
if err := v.validateElements(); err != nil {
|
2020-03-11 18:14:23 -07:00
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
2022-03-06 20:07:51 +01:00
|
|
|
if target := v.rule.GetPattern(); target != nil {
|
2023-01-30 17:47:19 +05:30
|
|
|
if path, err := common.ValidatePattern(target, "/", func(a anchor.Anchor) bool {
|
|
|
|
return anchor.IsCondition(a) ||
|
|
|
|
anchor.IsExistence(a) ||
|
|
|
|
anchor.IsEquality(a) ||
|
|
|
|
anchor.IsNegation(a) ||
|
|
|
|
anchor.IsGlobal(a)
|
|
|
|
}); err != nil {
|
2020-03-11 18:14:23 -07:00
|
|
|
return fmt.Sprintf("pattern.%s", path), err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-06 20:07:51 +01:00
|
|
|
if target := v.rule.GetAnyPattern(); target != nil {
|
2021-10-02 16:53:02 -07:00
|
|
|
anyPattern, err := v.rule.DeserializeAnyPattern()
|
2020-11-13 16:25:51 -08:00
|
|
|
if err != nil {
|
2021-02-19 09:09:41 -08:00
|
|
|
return "anyPattern", fmt.Errorf("failed to deserialize anyPattern, expect array: %v", err)
|
2020-11-13 16:25:51 -08:00
|
|
|
}
|
|
|
|
for i, pattern := range anyPattern {
|
2023-01-30 17:47:19 +05:30
|
|
|
if path, err := common.ValidatePattern(pattern, "/", func(a anchor.Anchor) bool {
|
|
|
|
return anchor.IsCondition(a) ||
|
|
|
|
anchor.IsExistence(a) ||
|
|
|
|
anchor.IsEquality(a) ||
|
|
|
|
anchor.IsNegation(a) ||
|
|
|
|
anchor.IsGlobal(a)
|
|
|
|
}); err != nil {
|
2020-03-11 18:14:23 -07:00
|
|
|
return fmt.Sprintf("anyPattern[%d].%s", i, path), err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-10-02 16:53:02 -07:00
|
|
|
|
|
|
|
if v.rule.ForEachValidation != nil {
|
2021-10-14 12:50:52 +05:30
|
|
|
for _, foreach := range v.rule.ForEachValidation {
|
|
|
|
if err := v.validateForEach(foreach); err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
2021-10-02 16:53:02 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-01 00:30:55 +03:00
|
|
|
if v.rule.CEL != nil {
|
|
|
|
for _, expression := range v.rule.CEL.Expressions {
|
|
|
|
if expression.Expression == "" {
|
|
|
|
return "", fmt.Errorf("cel.expressions.expression is required")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if v.rule.CEL.ParamKind != nil {
|
|
|
|
if v.rule.CEL.ParamKind.APIVersion == "" {
|
|
|
|
return "", fmt.Errorf("cel.paramKind.apiVersion is required")
|
|
|
|
}
|
|
|
|
|
|
|
|
if v.rule.CEL.ParamKind.Kind == "" {
|
|
|
|
return "", fmt.Errorf("cel.paramKind.kind is required")
|
|
|
|
}
|
|
|
|
|
|
|
|
if v.rule.CEL.ParamRef == nil {
|
|
|
|
return "", fmt.Errorf("cel.paramRef is required")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if v.rule.CEL.ParamRef != nil {
|
|
|
|
if v.rule.CEL.ParamRef.Name == "" {
|
|
|
|
return "", fmt.Errorf("cel.paramRef.name is required")
|
|
|
|
}
|
|
|
|
|
|
|
|
if v.rule.CEL.ParamKind == nil {
|
|
|
|
return "", fmt.Errorf("cel.paramKind is required")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if v.rule.CEL.AuditAnnotations != nil {
|
|
|
|
for _, auditAnnotation := range v.rule.CEL.AuditAnnotations {
|
|
|
|
if auditAnnotation.Key == "" {
|
|
|
|
return "", fmt.Errorf("cel.auditAnnotation.key is required")
|
|
|
|
}
|
|
|
|
|
|
|
|
if auditAnnotation.ValueExpression == "" {
|
|
|
|
return "", fmt.Errorf("cel.auditAnnotation.valueExpression is required")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-11 18:14:23 -07:00
|
|
|
return "", nil
|
|
|
|
}
|
|
|
|
|
2021-10-02 16:53:02 -07:00
|
|
|
func (v *Validate) validateElements() error {
|
|
|
|
count := validationElemCount(v.rule)
|
2021-10-02 16:57:40 -07:00
|
|
|
if count == 0 {
|
2023-06-01 00:30:55 +03:00
|
|
|
return fmt.Errorf("one of pattern, anyPattern, deny, foreach, cel must be specified")
|
2021-10-02 16:53:02 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if count > 1 {
|
2023-06-01 00:30:55 +03:00
|
|
|
return fmt.Errorf("only one of pattern, anyPattern, deny, foreach, cel can be specified")
|
2021-10-02 16:53:02 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-05-17 13:12:43 +02:00
|
|
|
func validationElemCount(v *kyvernov1.Validation) int {
|
2021-10-02 16:53:02 -07:00
|
|
|
if v == nil {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
count := 0
|
2022-03-06 20:07:51 +01:00
|
|
|
if v.GetPattern() != nil {
|
2021-10-02 16:53:02 -07:00
|
|
|
count++
|
|
|
|
}
|
|
|
|
|
2022-03-06 20:07:51 +01:00
|
|
|
if v.GetAnyPattern() != nil {
|
2021-10-02 16:53:02 -07:00
|
|
|
count++
|
|
|
|
}
|
|
|
|
|
|
|
|
if v.Deny != nil {
|
|
|
|
count++
|
|
|
|
}
|
|
|
|
|
|
|
|
if v.ForEachValidation != nil {
|
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
|
|
|
count++
|
|
|
|
}
|
|
|
|
|
|
|
|
if v.PodSecurity != nil {
|
2021-10-02 16:53:02 -07:00
|
|
|
count++
|
|
|
|
}
|
|
|
|
|
2023-06-01 00:30:55 +03:00
|
|
|
if v.CEL != nil {
|
|
|
|
count++
|
|
|
|
}
|
|
|
|
|
2022-08-31 02:14:54 +09:00
|
|
|
if v.Manifests != nil && len(v.Manifests.Attestors) != 0 {
|
|
|
|
count++
|
|
|
|
}
|
|
|
|
|
2021-10-02 16:53:02 -07:00
|
|
|
return count
|
|
|
|
}
|
|
|
|
|
2022-05-17 13:12:43 +02:00
|
|
|
func (v *Validate) validateForEach(foreach kyvernov1.ForEachValidation) error {
|
2021-10-02 16:53:02 -07:00
|
|
|
if foreach.List == "" {
|
|
|
|
return fmt.Errorf("foreach.list is required")
|
2020-03-11 18:14:23 -07:00
|
|
|
}
|
|
|
|
|
2021-10-02 16:53:02 -07:00
|
|
|
count := foreachElemCount(foreach)
|
2021-10-02 16:57:40 -07:00
|
|
|
if count == 0 {
|
2023-01-04 21:22:43 -08:00
|
|
|
return fmt.Errorf("one of pattern, anyPattern, deny, or a nested foreach must be specified")
|
2021-10-02 16:53:02 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if count > 1 {
|
2023-01-04 21:22:43 -08:00
|
|
|
return fmt.Errorf("only one of pattern, anyPattern, deny, or a nested foreach can be specified")
|
2020-03-11 18:14:23 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2021-10-02 16:53:02 -07:00
|
|
|
|
2022-05-17 13:12:43 +02:00
|
|
|
func foreachElemCount(foreach kyvernov1.ForEachValidation) int {
|
2021-10-02 16:53:02 -07:00
|
|
|
count := 0
|
2022-03-06 20:07:51 +01:00
|
|
|
if foreach.GetPattern() != nil {
|
2021-10-02 16:53:02 -07:00
|
|
|
count++
|
|
|
|
}
|
|
|
|
|
2022-03-06 20:07:51 +01:00
|
|
|
if foreach.GetAnyPattern() != nil {
|
2021-10-02 16:53:02 -07:00
|
|
|
count++
|
|
|
|
}
|
|
|
|
|
|
|
|
if foreach.Deny != nil {
|
|
|
|
count++
|
|
|
|
}
|
|
|
|
|
2023-01-04 21:22:43 -08:00
|
|
|
if foreach.ForEachValidation != nil {
|
|
|
|
count++
|
|
|
|
}
|
|
|
|
|
2021-10-02 16:53:02 -07:00
|
|
|
return count
|
|
|
|
}
|