2024-10-16 10:51:38 +02:00
|
|
|
package v1
|
2022-03-02 00:19:31 +01:00
|
|
|
|
|
|
|
import (
|
2024-07-29 16:46:11 +03:00
|
|
|
"bytes"
|
2024-04-30 20:26:12 +08:00
|
|
|
"sort"
|
2022-03-02 00:19:31 +01:00
|
|
|
"strings"
|
|
|
|
|
2022-05-17 13:12:43 +02:00
|
|
|
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
2022-03-02 00:19:31 +01:00
|
|
|
"github.com/kyverno/kyverno/pkg/engine/variables"
|
2023-03-24 11:01:49 +01:00
|
|
|
datautils "github.com/kyverno/kyverno/pkg/utils/data"
|
2022-04-01 10:34:25 +02:00
|
|
|
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
|
2024-10-31 17:12:22 +03:00
|
|
|
admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1"
|
2022-03-02 00:19:31 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
// the kyvernoRule holds the temporary kyverno rule struct
|
|
|
|
// each field is a pointer to the actual object
|
|
|
|
// when serializing data, we would expect to drop the omitempty key
|
|
|
|
// otherwise (without the pointer), it will be set to empty value
|
|
|
|
// - an empty struct in this case, some may fail the schema validation
|
|
|
|
// may related to:
|
|
|
|
// https://github.com/kyverno/kyverno/pull/549#discussion_r360088556
|
|
|
|
// https://github.com/kyverno/kyverno/issues/568
|
|
|
|
|
|
|
|
type kyvernoRule struct {
|
2024-10-31 17:12:22 +03:00
|
|
|
Name string `json:"name"`
|
|
|
|
MatchResources *kyvernov1.MatchResources `json:"match"`
|
|
|
|
ExcludeResources *kyvernov1.MatchResources `json:"exclude,omitempty"`
|
|
|
|
CELPreconditions *[]admissionregistrationv1beta1.MatchCondition `json:"celPreconditions,omitempty"`
|
|
|
|
Context *[]kyvernov1.ContextEntry `json:"context,omitempty"`
|
|
|
|
AnyAllConditions *kyvernov1.ConditionsWrapper `json:"preconditions,omitempty"`
|
|
|
|
Mutation *kyvernov1.Mutation `json:"mutate,omitempty"`
|
|
|
|
Validation *kyvernov1.Validation `json:"validate,omitempty"`
|
|
|
|
VerifyImages []kyvernov1.ImageVerification `json:"verifyImages,omitempty"`
|
|
|
|
SkipBackgroundRequests *bool `json:"skipBackgroundRequests,omitempty"`
|
2022-03-02 00:19:31 +01:00
|
|
|
}
|
|
|
|
|
2022-05-17 13:12:43 +02:00
|
|
|
func createRule(rule *kyvernov1.Rule) *kyvernoRule {
|
2022-04-05 17:12:22 +02:00
|
|
|
if rule == nil {
|
2022-03-02 18:20:27 +01:00
|
|
|
return nil
|
2022-03-02 00:19:31 +01:00
|
|
|
}
|
2022-04-05 17:12:22 +02:00
|
|
|
jsonFriendlyStruct := kyvernoRule{
|
2024-09-24 18:17:38 +03:00
|
|
|
Name: rule.Name,
|
|
|
|
VerifyImages: rule.VerifyImages,
|
|
|
|
SkipBackgroundRequests: rule.SkipBackgroundRequests,
|
2022-03-02 00:19:31 +01:00
|
|
|
}
|
2023-03-24 11:01:49 +01:00
|
|
|
if !datautils.DeepEqual(rule.MatchResources, kyvernov1.MatchResources{}) {
|
2022-04-05 17:12:22 +02:00
|
|
|
jsonFriendlyStruct.MatchResources = rule.MatchResources.DeepCopy()
|
2022-03-02 00:19:31 +01:00
|
|
|
}
|
2024-09-10 13:14:49 +02:00
|
|
|
if rule.ExcludeResources != nil && !datautils.DeepEqual(*rule.ExcludeResources, kyvernov1.MatchResources{}) {
|
2022-04-05 17:12:22 +02:00
|
|
|
jsonFriendlyStruct.ExcludeResources = rule.ExcludeResources.DeepCopy()
|
2022-03-02 00:19:31 +01:00
|
|
|
}
|
2024-09-11 03:32:10 +02:00
|
|
|
if rule.Mutation != nil && !datautils.DeepEqual(*rule.Mutation, kyvernov1.Mutation{}) {
|
2022-04-05 17:12:22 +02:00
|
|
|
jsonFriendlyStruct.Mutation = rule.Mutation.DeepCopy()
|
2022-03-02 00:19:31 +01:00
|
|
|
}
|
2024-09-11 11:12:53 +02:00
|
|
|
if rule.Validation != nil && !datautils.DeepEqual(*rule.Validation, kyvernov1.Validation{}) {
|
2022-04-05 17:12:22 +02:00
|
|
|
jsonFriendlyStruct.Validation = rule.Validation.DeepCopy()
|
2022-03-02 00:19:31 +01:00
|
|
|
}
|
2024-08-05 15:46:50 +03:00
|
|
|
kyvernoAnyAllConditions := rule.GetAnyAllConditions()
|
2022-03-02 00:19:31 +01:00
|
|
|
switch typedAnyAllConditions := kyvernoAnyAllConditions.(type) {
|
2022-05-17 13:12:43 +02:00
|
|
|
case kyvernov1.AnyAllConditions:
|
2023-03-24 11:01:49 +01:00
|
|
|
if !datautils.DeepEqual(typedAnyAllConditions, kyvernov1.AnyAllConditions{}) {
|
2022-04-05 17:12:22 +02:00
|
|
|
jsonFriendlyStruct.AnyAllConditions = rule.DeepCopy().RawAnyAllConditions
|
2022-03-02 00:19:31 +01:00
|
|
|
}
|
2022-05-17 13:12:43 +02:00
|
|
|
case []kyvernov1.Condition:
|
2022-03-02 00:19:31 +01:00
|
|
|
if len(typedAnyAllConditions) > 0 {
|
2022-04-05 17:12:22 +02:00
|
|
|
jsonFriendlyStruct.AnyAllConditions = rule.DeepCopy().RawAnyAllConditions
|
2022-03-02 00:19:31 +01:00
|
|
|
}
|
|
|
|
}
|
2022-04-05 17:12:22 +02:00
|
|
|
if len(rule.Context) > 0 {
|
|
|
|
jsonFriendlyStruct.Context = &rule.DeepCopy().Context
|
2022-03-02 00:19:31 +01:00
|
|
|
}
|
2024-10-31 17:12:22 +03:00
|
|
|
if len(rule.CELPreconditions) > 0 {
|
|
|
|
jsonFriendlyStruct.CELPreconditions = &rule.DeepCopy().CELPreconditions
|
|
|
|
}
|
2022-04-05 17:12:22 +02:00
|
|
|
return &jsonFriendlyStruct
|
|
|
|
}
|
|
|
|
|
2022-05-17 13:12:43 +02:00
|
|
|
type generateResourceFilters func(kyvernov1.ResourceFilters, []string) kyvernov1.ResourceFilters
|
2022-03-02 00:19:31 +01:00
|
|
|
|
2022-05-17 13:12:43 +02:00
|
|
|
func generateRule(name string, rule *kyvernov1.Rule, tplKey, shift string, kinds []string, grf generateResourceFilters) *kyvernov1.Rule {
|
2022-04-05 17:12:22 +02:00
|
|
|
if rule == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
rule = rule.DeepCopy()
|
|
|
|
rule.Name = name
|
2022-03-02 00:19:31 +01:00
|
|
|
// overwrite Kinds by pod controllers defined in the annotation
|
|
|
|
if len(rule.MatchResources.Any) > 0 {
|
2022-04-05 17:12:22 +02:00
|
|
|
rule.MatchResources.Any = grf(rule.MatchResources.Any, kinds)
|
2022-03-02 00:19:31 +01:00
|
|
|
} else if len(rule.MatchResources.All) > 0 {
|
2022-04-05 17:12:22 +02:00
|
|
|
rule.MatchResources.All = grf(rule.MatchResources.All, kinds)
|
2022-03-02 00:19:31 +01:00
|
|
|
} else {
|
2022-04-05 17:12:22 +02:00
|
|
|
rule.MatchResources.Kinds = kinds
|
2022-03-02 00:19:31 +01:00
|
|
|
}
|
2024-09-10 13:14:49 +02:00
|
|
|
if rule.ExcludeResources != nil {
|
|
|
|
if len(rule.ExcludeResources.Any) > 0 {
|
|
|
|
rule.ExcludeResources.Any = grf(rule.ExcludeResources.Any, kinds)
|
|
|
|
} else if len(rule.ExcludeResources.All) > 0 {
|
|
|
|
rule.ExcludeResources.All = grf(rule.ExcludeResources.All, kinds)
|
|
|
|
} else {
|
|
|
|
if len(rule.ExcludeResources.Kinds) != 0 {
|
|
|
|
rule.ExcludeResources.Kinds = kinds
|
|
|
|
}
|
2022-03-02 00:19:31 +01:00
|
|
|
}
|
|
|
|
}
|
2024-09-11 03:32:10 +02:00
|
|
|
if rule.Mutation != nil {
|
|
|
|
if target := rule.Mutation.GetPatchStrategicMerge(); target != nil {
|
|
|
|
newMutation := &kyvernov1.Mutation{}
|
|
|
|
newMutation.SetPatchStrategicMerge(
|
2022-03-06 20:07:51 +01:00
|
|
|
map[string]interface{}{
|
2022-03-02 00:19:31 +01:00
|
|
|
"spec": map[string]interface{}{
|
2024-09-11 03:32:10 +02:00
|
|
|
tplKey: target,
|
2022-03-02 00:19:31 +01:00
|
|
|
},
|
|
|
|
},
|
2022-03-06 20:07:51 +01:00
|
|
|
)
|
2024-09-11 03:32:10 +02:00
|
|
|
rule.Mutation = newMutation
|
|
|
|
return rule
|
2022-03-02 00:19:31 +01:00
|
|
|
}
|
2024-09-11 03:32:10 +02:00
|
|
|
if len(rule.Mutation.ForEachMutation) > 0 && rule.Mutation.ForEachMutation != nil {
|
|
|
|
var newForEachMutation []kyvernov1.ForEachMutation
|
|
|
|
for _, foreach := range rule.Mutation.ForEachMutation {
|
|
|
|
temp := kyvernov1.ForEachMutation{
|
|
|
|
List: foreach.List,
|
|
|
|
Context: foreach.Context,
|
|
|
|
AnyAllConditions: foreach.AnyAllConditions,
|
|
|
|
}
|
|
|
|
temp.SetPatchStrategicMerge(
|
|
|
|
map[string]interface{}{
|
|
|
|
"spec": map[string]interface{}{
|
|
|
|
tplKey: foreach.GetPatchStrategicMerge(),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
)
|
|
|
|
newForEachMutation = append(newForEachMutation, temp)
|
|
|
|
}
|
|
|
|
rule.Mutation = &kyvernov1.Mutation{
|
|
|
|
ForEachMutation: newForEachMutation,
|
|
|
|
}
|
|
|
|
return rule
|
2022-03-02 00:19:31 +01:00
|
|
|
}
|
|
|
|
}
|
2024-09-11 11:12:53 +02:00
|
|
|
if rule.Validation != nil {
|
|
|
|
if target := rule.Validation.GetPattern(); target != nil {
|
|
|
|
newValidate := &kyvernov1.Validation{
|
2024-09-24 18:17:38 +03:00
|
|
|
Message: variables.FindAndShiftReferences(logger, rule.Validation.Message, shift, "pattern"),
|
|
|
|
FailureAction: rule.Validation.FailureAction,
|
|
|
|
FailureActionOverrides: rule.Validation.FailureActionOverrides,
|
|
|
|
AllowExistingViolations: rule.Validation.AllowExistingViolations,
|
2024-09-11 11:12:53 +02:00
|
|
|
}
|
|
|
|
newValidate.SetPattern(
|
|
|
|
map[string]interface{}{
|
|
|
|
"spec": map[string]interface{}{
|
|
|
|
tplKey: target,
|
|
|
|
},
|
2022-03-02 00:19:31 +01:00
|
|
|
},
|
2024-09-11 11:12:53 +02:00
|
|
|
)
|
|
|
|
rule.Validation = newValidate
|
|
|
|
return rule
|
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
|
|
|
}
|
2024-09-11 11:12:53 +02:00
|
|
|
if rule.Validation.Deny != nil {
|
|
|
|
deny := &kyvernov1.Validation{
|
2024-09-25 15:44:39 +02:00
|
|
|
Message: variables.FindAndShiftReferences(logger, rule.Validation.Message, shift, "deny"),
|
|
|
|
Deny: rule.Validation.Deny,
|
|
|
|
FailureAction: rule.Validation.FailureAction,
|
|
|
|
FailureActionOverrides: rule.Validation.FailureActionOverrides,
|
|
|
|
AllowExistingViolations: rule.Validation.AllowExistingViolations,
|
2024-09-11 11:12:53 +02:00
|
|
|
}
|
|
|
|
rule.Validation = deny
|
|
|
|
return rule
|
2022-03-02 00:19:31 +01:00
|
|
|
}
|
2024-09-11 11:12:53 +02:00
|
|
|
if rule.Validation.PodSecurity != nil {
|
|
|
|
newExclude := make([]kyvernov1.PodSecurityStandard, len(rule.Validation.PodSecurity.Exclude))
|
|
|
|
copy(newExclude, rule.Validation.PodSecurity.Exclude)
|
|
|
|
podSecurity := &kyvernov1.Validation{
|
|
|
|
Message: variables.FindAndShiftReferences(logger, rule.Validation.Message, shift, "podSecurity"),
|
|
|
|
PodSecurity: &kyvernov1.PodSecurity{
|
|
|
|
Level: rule.Validation.PodSecurity.Level,
|
|
|
|
Version: rule.Validation.PodSecurity.Version,
|
|
|
|
Exclude: newExclude,
|
2022-04-05 17:12:22 +02:00
|
|
|
},
|
2024-09-25 15:44:39 +02:00
|
|
|
FailureAction: rule.Validation.FailureAction,
|
|
|
|
FailureActionOverrides: rule.Validation.FailureActionOverrides,
|
|
|
|
AllowExistingViolations: rule.Validation.AllowExistingViolations,
|
2022-04-05 17:12:22 +02:00
|
|
|
}
|
2024-09-11 11:12:53 +02:00
|
|
|
rule.Validation = podSecurity
|
|
|
|
return rule
|
2022-04-05 17:12:22 +02:00
|
|
|
}
|
2024-09-11 11:12:53 +02:00
|
|
|
if rule.Validation.GetAnyPattern() != nil {
|
|
|
|
anyPatterns, err := rule.Validation.DeserializeAnyPattern()
|
|
|
|
if err != nil {
|
|
|
|
logger.Error(err, "failed to deserialize anyPattern, expect type array")
|
|
|
|
}
|
|
|
|
var patterns []interface{}
|
|
|
|
for _, pattern := range anyPatterns {
|
|
|
|
newPattern := map[string]interface{}{
|
|
|
|
"spec": map[string]interface{}{
|
|
|
|
tplKey: pattern,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
patterns = append(patterns, newPattern)
|
|
|
|
}
|
|
|
|
failureAction := rule.Validation.FailureAction
|
|
|
|
failureActionOverrides := rule.Validation.FailureActionOverrides
|
|
|
|
rule.Validation = &kyvernov1.Validation{
|
2024-09-25 15:44:39 +02:00
|
|
|
Message: variables.FindAndShiftReferences(logger, rule.Validation.Message, shift, "anyPattern"),
|
|
|
|
FailureAction: failureAction,
|
|
|
|
FailureActionOverrides: failureActionOverrides,
|
|
|
|
AllowExistingViolations: rule.Validation.AllowExistingViolations,
|
2024-09-11 11:12:53 +02:00
|
|
|
}
|
|
|
|
rule.Validation.SetAnyPattern(patterns)
|
|
|
|
return rule
|
2022-03-02 00:19:31 +01:00
|
|
|
}
|
2024-09-11 11:12:53 +02:00
|
|
|
if len(rule.Validation.ForEachValidation) > 0 && rule.Validation.ForEachValidation != nil {
|
|
|
|
newForeachValidate := make([]kyvernov1.ForEachValidation, len(rule.Validation.ForEachValidation))
|
|
|
|
copy(newForeachValidate, rule.Validation.ForEachValidation)
|
|
|
|
failureAction := rule.Validation.FailureAction
|
|
|
|
failureActionOverrides := rule.Validation.FailureActionOverrides
|
|
|
|
rule.Validation = &kyvernov1.Validation{
|
2024-09-25 15:44:39 +02:00
|
|
|
Message: variables.FindAndShiftReferences(logger, rule.Validation.Message, shift, "pattern"),
|
|
|
|
ForEachValidation: newForeachValidate,
|
|
|
|
FailureAction: failureAction,
|
|
|
|
FailureActionOverrides: failureActionOverrides,
|
|
|
|
AllowExistingViolations: rule.Validation.AllowExistingViolations,
|
2024-09-11 11:12:53 +02:00
|
|
|
}
|
|
|
|
return rule
|
|
|
|
}
|
|
|
|
if rule.HasValidateCEL() {
|
|
|
|
cel := rule.Validation.CEL.DeepCopy()
|
|
|
|
rule.Validation.CEL = cel
|
|
|
|
return rule
|
|
|
|
}
|
|
|
|
if rule.HasValidateAssert() {
|
|
|
|
rule.Validation.Assert = createAutogenAssertion(*rule.Validation.Assert.DeepCopy(), tplKey)
|
|
|
|
return rule
|
2022-03-02 00:19:31 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if rule.VerifyImages != nil {
|
2022-05-17 13:12:43 +02:00
|
|
|
newVerifyImages := make([]kyvernov1.ImageVerification, len(rule.VerifyImages))
|
2022-03-02 00:19:31 +01:00
|
|
|
for i, vi := range rule.VerifyImages {
|
2022-04-29 09:12:01 +02:00
|
|
|
newVerifyImages[i] = *vi.DeepCopy()
|
2022-03-02 00:19:31 +01:00
|
|
|
}
|
2022-04-05 17:12:22 +02:00
|
|
|
rule.VerifyImages = newVerifyImages
|
|
|
|
return rule
|
2022-03-02 00:19:31 +01:00
|
|
|
}
|
2022-03-02 18:20:27 +01:00
|
|
|
return nil
|
2022-03-02 00:19:31 +01:00
|
|
|
}
|
|
|
|
|
2022-04-05 17:12:22 +02:00
|
|
|
func getAutogenRuleName(prefix string, name string) string {
|
|
|
|
name = prefix + "-" + name
|
2022-03-02 00:19:31 +01:00
|
|
|
if len(name) > 63 {
|
|
|
|
name = name[:63]
|
|
|
|
}
|
2022-04-05 17:12:22 +02:00
|
|
|
return name
|
|
|
|
}
|
2022-03-02 00:19:31 +01:00
|
|
|
|
2022-04-05 17:12:22 +02:00
|
|
|
func isAutogenRuleName(name string) bool {
|
|
|
|
return strings.HasPrefix(name, "autogen-")
|
|
|
|
}
|
2022-03-02 00:19:31 +01:00
|
|
|
|
2022-05-17 13:12:43 +02:00
|
|
|
func getAnyAllAutogenRule(v kyvernov1.ResourceFilters, match string, kinds []string) kyvernov1.ResourceFilters {
|
2022-04-05 17:12:22 +02:00
|
|
|
anyKind := v.DeepCopy()
|
|
|
|
for i, value := range v {
|
|
|
|
if kubeutils.ContainsKind(value.Kinds, match) {
|
|
|
|
anyKind[i].Kinds = kinds
|
2022-03-02 00:19:31 +01:00
|
|
|
}
|
|
|
|
}
|
2022-04-05 17:12:22 +02:00
|
|
|
return anyKind
|
|
|
|
}
|
2022-03-02 00:19:31 +01:00
|
|
|
|
2022-05-17 13:12:43 +02:00
|
|
|
func generateRuleForControllers(rule *kyvernov1.Rule, controllers string) *kyvernov1.Rule {
|
2022-04-05 17:12:22 +02:00
|
|
|
if isAutogenRuleName(rule.Name) || controllers == "" {
|
2022-12-14 14:06:24 +01:00
|
|
|
debug.Info("skip generateRuleForControllers")
|
2022-04-05 17:12:22 +02:00
|
|
|
return nil
|
2022-03-02 00:19:31 +01:00
|
|
|
}
|
2022-12-14 14:06:24 +01:00
|
|
|
debug.Info("processing rule", "rulename", rule.Name)
|
2024-09-10 13:14:49 +02:00
|
|
|
match := rule.MatchResources
|
|
|
|
matchKinds := match.GetKinds()
|
|
|
|
var excludeKinds []string
|
|
|
|
if exclude := rule.ExcludeResources; exclude != nil {
|
|
|
|
excludeKinds = exclude.GetKinds()
|
|
|
|
}
|
2022-04-05 17:12:22 +02:00
|
|
|
if !kubeutils.ContainsKind(matchKinds, "Pod") || (len(excludeKinds) != 0 && !kubeutils.ContainsKind(excludeKinds, "Pod")) {
|
|
|
|
return nil
|
2022-03-02 00:19:31 +01:00
|
|
|
}
|
2022-04-05 17:12:22 +02:00
|
|
|
// Support backwards compatibility
|
|
|
|
skipAutoGeneration := false
|
|
|
|
var controllersValidated []string
|
|
|
|
if controllers == "all" {
|
|
|
|
skipAutoGeneration = true
|
|
|
|
} else if controllers != "none" && controllers != "all" {
|
2022-11-14 11:18:45 +01:00
|
|
|
controllersList := map[string]int{
|
|
|
|
"DaemonSet": 1,
|
|
|
|
"Deployment": 1,
|
|
|
|
"Job": 1,
|
|
|
|
"StatefulSet": 1,
|
|
|
|
"ReplicaSet": 1,
|
|
|
|
"ReplicationController": 1,
|
|
|
|
}
|
2024-04-30 20:26:12 +08:00
|
|
|
for _, value := range splitKinds(controllers, ",") {
|
2022-04-05 17:12:22 +02:00
|
|
|
if _, ok := controllersList[value]; ok {
|
|
|
|
controllersValidated = append(controllersValidated, value)
|
2022-03-06 20:07:51 +01:00
|
|
|
}
|
2022-03-02 00:19:31 +01:00
|
|
|
}
|
2022-04-05 17:12:22 +02:00
|
|
|
if len(controllersValidated) > 0 {
|
|
|
|
skipAutoGeneration = true
|
2022-03-02 00:19:31 +01:00
|
|
|
}
|
|
|
|
}
|
2022-04-05 17:12:22 +02:00
|
|
|
if skipAutoGeneration {
|
|
|
|
if controllers == "all" {
|
2022-11-14 11:18:45 +01:00
|
|
|
controllers = "DaemonSet,Deployment,Job,StatefulSet,ReplicaSet,ReplicationController"
|
2022-04-05 17:12:22 +02:00
|
|
|
} else {
|
|
|
|
controllers = strings.Join(controllersValidated, ",")
|
2022-03-02 00:19:31 +01:00
|
|
|
}
|
|
|
|
}
|
2022-04-05 17:12:22 +02:00
|
|
|
return generateRule(
|
|
|
|
getAutogenRuleName("autogen", rule.Name),
|
|
|
|
rule,
|
|
|
|
"template",
|
|
|
|
"spec/template",
|
2024-04-30 20:26:12 +08:00
|
|
|
splitKinds(controllers, ","),
|
2022-05-17 13:12:43 +02:00
|
|
|
func(r kyvernov1.ResourceFilters, kinds []string) kyvernov1.ResourceFilters {
|
2022-04-05 17:12:22 +02:00
|
|
|
return getAnyAllAutogenRule(r, "Pod", kinds)
|
|
|
|
},
|
|
|
|
)
|
2022-03-02 00:19:31 +01:00
|
|
|
}
|
|
|
|
|
2024-04-30 20:26:12 +08:00
|
|
|
func splitKinds(controllers, separator string) []string {
|
|
|
|
kinds := strings.Split(controllers, separator)
|
|
|
|
sort.Strings(kinds)
|
|
|
|
return kinds
|
|
|
|
}
|
|
|
|
|
2022-05-17 13:12:43 +02:00
|
|
|
func generateCronJobRule(rule *kyvernov1.Rule, controllers string) *kyvernov1.Rule {
|
2022-04-05 17:12:22 +02:00
|
|
|
hasCronJob := strings.Contains(controllers, PodControllerCronJob) || strings.Contains(controllers, "all")
|
|
|
|
if !hasCronJob {
|
|
|
|
return nil
|
2022-03-02 00:19:31 +01:00
|
|
|
}
|
2022-12-14 14:06:24 +01:00
|
|
|
debug.Info("generating rule for cronJob")
|
2022-04-05 17:12:22 +02:00
|
|
|
return generateRule(
|
|
|
|
getAutogenRuleName("autogen-cronjob", rule.Name),
|
2022-04-29 11:12:21 +02:00
|
|
|
generateRuleForControllers(rule, controllers),
|
2022-04-05 17:12:22 +02:00
|
|
|
"jobTemplate",
|
|
|
|
"spec/jobTemplate/spec/template",
|
|
|
|
[]string{PodControllerCronJob},
|
2022-05-17 13:12:43 +02:00
|
|
|
func(r kyvernov1.ResourceFilters, kinds []string) kyvernov1.ResourceFilters {
|
2023-06-07 20:32:35 +02:00
|
|
|
anyKind := r.DeepCopy()
|
|
|
|
for i := range anyKind {
|
|
|
|
anyKind[i].Kinds = kinds
|
|
|
|
}
|
|
|
|
return anyKind
|
2022-04-05 17:12:22 +02:00
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2024-07-29 16:46:11 +03:00
|
|
|
var (
|
|
|
|
podReplacementRules [][2][]byte = [][2][]byte{
|
|
|
|
{[]byte("request.object.spec"), []byte("request.object.spec.template.spec")},
|
|
|
|
{[]byte("request.oldObject.spec"), []byte("request.oldObject.spec.template.spec")},
|
|
|
|
{[]byte("request.object.metadata"), []byte("request.object.spec.template.metadata")},
|
|
|
|
{[]byte("request.oldObject.metadata"), []byte("request.oldObject.spec.template.metadata")},
|
|
|
|
}
|
|
|
|
podCELReplacementRules [][2][]byte = [][2][]byte{
|
|
|
|
{[]byte("object.spec"), []byte("object.spec.template.spec")},
|
|
|
|
{[]byte("oldObject.spec"), []byte("oldObject.spec.template.spec")},
|
|
|
|
{[]byte("object.metadata"), []byte("object.spec.template.metadata")},
|
|
|
|
{[]byte("oldObject.metadata"), []byte("oldObject.spec.template.metadata")},
|
|
|
|
}
|
|
|
|
cronJobReplacementRules [][2][]byte = [][2][]byte{
|
|
|
|
{[]byte("request.object.spec"), []byte("request.object.spec.jobTemplate.spec.template.spec")},
|
|
|
|
{[]byte("request.oldObject.spec"), []byte("request.oldObject.spec.jobTemplate.spec.template.spec")},
|
|
|
|
{[]byte("request.object.metadata"), []byte("request.object.spec.jobTemplate.spec.template.metadata")},
|
|
|
|
{[]byte("request.oldObject.metadata"), []byte("request.oldObject.spec.jobTemplate.spec.template.metadata")},
|
|
|
|
}
|
|
|
|
cronJobCELReplacementRules [][2][]byte = [][2][]byte{
|
|
|
|
{[]byte("object.spec"), []byte("object.spec.jobTemplate.spec.template.spec")},
|
|
|
|
{[]byte("oldObject.spec"), []byte("oldObject.spec.jobTemplate.spec.template.spec")},
|
|
|
|
{[]byte("object.metadata"), []byte("object.spec.jobTemplate.spec.template.metadata")},
|
|
|
|
{[]byte("oldObject.metadata"), []byte("oldObject.spec.jobTemplate.spec.template.metadata")},
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
func updateFields(data []byte, kind string, cel bool) []byte {
|
|
|
|
switch kind {
|
|
|
|
case "Pod":
|
|
|
|
if cel {
|
|
|
|
for _, replacement := range podCELReplacementRules {
|
|
|
|
data = bytes.ReplaceAll(data, replacement[0], replacement[1])
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for _, replacement := range podReplacementRules {
|
|
|
|
data = bytes.ReplaceAll(data, replacement[0], replacement[1])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case "Cronjob":
|
|
|
|
if cel {
|
|
|
|
for _, replacement := range cronJobCELReplacementRules {
|
|
|
|
data = bytes.ReplaceAll(data, replacement[0], replacement[1])
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for _, replacement := range cronJobReplacementRules {
|
|
|
|
data = bytes.ReplaceAll(data, replacement[0], replacement[1])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
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
|
|
|
|
2024-07-29 16:46:11 +03:00
|
|
|
return data
|
2023-07-21 12:19:47 +03:00
|
|
|
}
|