2020-03-11 18:14:23 -07:00
package validate
import (
"fmt"
2021-10-02 16:53:02 -07:00
"strings"
2020-03-11 18:14:23 -07:00
2022-05-17 13:12:43 +02:00
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
2022-05-17 07:56:48 +02:00
commonAnchors "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
2020-03-11 18:14:23 -07:00
func ( v * Validate ) Validate ( ) ( 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 {
if path , err := common . ValidatePattern ( target , "/" , [ ] commonAnchors . IsAnchor { commonAnchors . IsConditionAnchor , commonAnchors . IsExistenceAnchor , commonAnchors . IsEqualityAnchor , commonAnchors . IsNegationAnchor , commonAnchors . IsGlobalAnchor } ) ; 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 {
2021-09-13 18:59:28 +03:00
if path , err := common . ValidatePattern ( pattern , "/" , [ ] commonAnchors . IsAnchor { commonAnchors . IsConditionAnchor , commonAnchors . IsExistenceAnchor , commonAnchors . IsEqualityAnchor , commonAnchors . IsNegationAnchor , commonAnchors . IsGlobalAnchor } ) ; 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
}
}
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 {
2021-10-02 16:53:02 -07:00
return fmt . Errorf ( "one of pattern, anyPattern, deny, foreach must be specified" )
}
if count > 1 {
return fmt . Errorf ( "only one of pattern, anyPattern, deny, foreach can be specified" )
}
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 ++
}
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
}
2022-04-08 16:54:35 +02:00
if ! strings . HasPrefix ( foreach . List , "request.object" ) && ! strings . HasPrefix ( foreach . List , "request.userInfo" ) {
return fmt . Errorf ( "foreach.list must start with either 'request.object' or 'request.userInfo', e.g. 'request.object.spec.containers', 'request.userInfo.groups'" )
2021-10-02 16:53:02 -07:00
}
count := foreachElemCount ( foreach )
2021-10-02 16:57:40 -07:00
if count == 0 {
2021-10-02 16:53:02 -07:00
return fmt . Errorf ( "one of pattern, anyPattern, deny must be specified" )
}
if count > 1 {
return fmt . Errorf ( "only one of pattern, anyPattern, deny 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 ++
}
return count
}