mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-31 03:45:17 +00:00
fix: return all the exceptions that match the incoming resource (#10722)
* fix: return all the exceptions that match the incoming resource Signed-off-by: Mariam Fahmy <mariam.fahmy@nirmata.com> * fix: modify log messages Signed-off-by: Mariam Fahmy <mariam.fahmy@nirmata.com> --------- Signed-off-by: Mariam Fahmy <mariam.fahmy@nirmata.com>
This commit is contained in:
parent
2855d27ce4
commit
716611b7ea
38 changed files with 836 additions and 167 deletions
cmd/cli/kubectl-kyverno/commands/test
pkg
test/conformance/chainsaw
exceptions
multiple-exceptions-with-pod-security
multiple-exceptions
reports
admission/exception
background
exception-with-podsecurity
exception
multiple-exceptions-with-pod-security
|
@ -90,7 +90,7 @@ func printCheckResult(
|
|||
// patchedTargetSubresourceName string
|
||||
// podSecurityChecks contains pod security checks (only if this is a pod security rule)
|
||||
"podSecurityChecks": rule.PodSecurityChecks(),
|
||||
"exception ": rule.Exception(),
|
||||
"exceptions": rule.Exceptions(),
|
||||
}
|
||||
if check.Assert.Value != nil {
|
||||
errs, err := assert.Assert(ctx, nil, assert.Parse(ctx, check.Assert.Value), data, nil)
|
||||
|
|
|
@ -2,6 +2,7 @@ package background
|
|||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
|
@ -361,8 +362,8 @@ func (c *controller) reconcileReport(
|
|||
}
|
||||
policyNameToLabel[key] = reportutils.PolicyLabel(policy)
|
||||
}
|
||||
for _, exception := range exceptions {
|
||||
key, err := cache.MetaNamespaceKeyFunc(exception)
|
||||
for i, exception := range exceptions {
|
||||
key, err := cache.MetaNamespaceKeyFunc(&exceptions[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -376,13 +377,24 @@ func (c *controller) reconcileReport(
|
|||
policyNameToLabel[key] = reportutils.ValidatingAdmissionPolicyBindingLabel(binding)
|
||||
}
|
||||
for _, result := range observed.GetResults() {
|
||||
// if the policy did not change, keep the result
|
||||
// The result is kept as it is if:
|
||||
// 1. The Kyverno policy and its matched exceptions are unchanged
|
||||
// 2. The ValidatingAdmissionPolicy and its matched binding are unchanged
|
||||
keepResult := true
|
||||
exception := result.Properties["exceptions"]
|
||||
exceptions := strings.Split(exception, ",")
|
||||
for _, exception := range exceptions {
|
||||
exceptionLabel := policyNameToLabel[exception]
|
||||
if exceptionLabel != "" && expected[exceptionLabel] != actual[exceptionLabel] {
|
||||
keepResult = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
label := policyNameToLabel[result.Policy]
|
||||
exceptionLabel := policyNameToLabel[result.Properties["exception"]]
|
||||
vapBindingLabel := policyNameToLabel[result.Properties["binding"]]
|
||||
if (label != "" && expected[label] == actual[label]) ||
|
||||
(exceptionLabel != "" && expected[exceptionLabel] == actual[exceptionLabel]) ||
|
||||
(vapBindingLabel != "" && expected[vapBindingLabel] == actual[vapBindingLabel]) {
|
||||
(vapBindingLabel != "" && expected[vapBindingLabel] == actual[vapBindingLabel]) || keepResult {
|
||||
ruleResults = append(ruleResults, result)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,8 +43,8 @@ type RuleResponse struct {
|
|||
patchedTargetSubresourceName string
|
||||
// podSecurityChecks contains pod security checks (only if this is a pod security rule)
|
||||
podSecurityChecks *PodSecurityChecks
|
||||
// exception is the exception applied (if any)
|
||||
exception *kyvernov2.PolicyException
|
||||
// exceptions are the exceptions applied (if any)
|
||||
exceptions []kyvernov2.PolicyException
|
||||
// binding is the validatingadmissionpolicybinding (if any)
|
||||
binding *v1alpha1.ValidatingAdmissionPolicyBinding
|
||||
// emitWarning enable passing rule message as warning to api server warning header
|
||||
|
@ -88,8 +88,8 @@ func RuleFail(name string, ruleType RuleType, msg string) *RuleResponse {
|
|||
return NewRuleResponse(name, ruleType, msg, RuleStatusFail)
|
||||
}
|
||||
|
||||
func (r RuleResponse) WithException(exception *kyvernov2.PolicyException) *RuleResponse {
|
||||
r.exception = exception
|
||||
func (r RuleResponse) WithExceptions(exceptions []kyvernov2.PolicyException) *RuleResponse {
|
||||
r.exceptions = exceptions
|
||||
return &r
|
||||
}
|
||||
|
||||
|
@ -129,8 +129,8 @@ func (r *RuleResponse) Stats() ExecutionStats {
|
|||
return r.stats
|
||||
}
|
||||
|
||||
func (r *RuleResponse) Exception() *kyvernov2.PolicyException {
|
||||
return r.exception
|
||||
func (r *RuleResponse) Exceptions() []kyvernov2.PolicyException {
|
||||
return r.exceptions
|
||||
}
|
||||
|
||||
func (r *RuleResponse) ValidatingAdmissionPolicyBinding() *v1alpha1.ValidatingAdmissionPolicyBinding {
|
||||
|
@ -138,7 +138,7 @@ func (r *RuleResponse) ValidatingAdmissionPolicyBinding() *v1alpha1.ValidatingAd
|
|||
}
|
||||
|
||||
func (r *RuleResponse) IsException() bool {
|
||||
return r.exception != nil
|
||||
return len(r.exceptions) > 0
|
||||
}
|
||||
|
||||
func (r *RuleResponse) PodSecurityChecks() *PodSecurityChecks {
|
||||
|
|
|
@ -2,6 +2,7 @@ package engine
|
|||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
|
@ -64,17 +65,21 @@ func (e *engine) filterRule(
|
|||
logger.Error(err, "failed to get exceptions")
|
||||
return nil
|
||||
}
|
||||
// check if there is a policy exception matches the incoming resource
|
||||
exception := engineutils.MatchesException(exceptions, policyContext, logger)
|
||||
if exception != nil {
|
||||
key, err := cache.MetaNamespaceKeyFunc(exception)
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to compute policy exception key", "namespace", exception.GetNamespace(), "name", exception.GetName())
|
||||
return engineapi.RuleError(rule.Name, ruleType, "failed to compute exception key", err)
|
||||
} else {
|
||||
logger.V(3).Info("policy rule skipped due to policy exception", "exception", key)
|
||||
return engineapi.RuleSkip(rule.Name, ruleType, "rule skipped due to policy exception "+key).WithException(exception)
|
||||
// check if there are policy exceptions that match the incoming resource
|
||||
matchedExceptions := engineutils.MatchesException(exceptions, policyContext, logger)
|
||||
if len(matchedExceptions) > 0 {
|
||||
var keys []string
|
||||
for i, exception := range matchedExceptions {
|
||||
key, err := cache.MetaNamespaceKeyFunc(&matchedExceptions[i])
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to compute policy exception key", "namespace", exception.GetNamespace(), "name", exception.GetName())
|
||||
return engineapi.RuleError(rule.Name, ruleType, "failed to compute exception key", err)
|
||||
}
|
||||
keys = append(keys, key)
|
||||
}
|
||||
|
||||
logger.V(3).Info("policy rule is skipped due to policy exceptions", "exceptions", keys)
|
||||
return engineapi.RuleSkip(rule.Name, ruleType, "rule is skipped due to policy exception "+strings.Join(keys, ", ")).WithExceptions(matchedExceptions)
|
||||
}
|
||||
|
||||
newResource := policyContext.NewResource()
|
||||
|
|
|
@ -2,6 +2,7 @@ package mutation
|
|||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
|
@ -37,19 +38,23 @@ func (h mutateExistingHandler) Process(
|
|||
contextLoader engineapi.EngineContextLoader,
|
||||
exceptions []*kyvernov2.PolicyException,
|
||||
) (unstructured.Unstructured, []engineapi.RuleResponse) {
|
||||
// check if there is a policy exception matches the incoming resource
|
||||
exception := engineutils.MatchesException(exceptions, policyContext, logger)
|
||||
if exception != nil {
|
||||
key, err := cache.MetaNamespaceKeyFunc(exception)
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to compute policy exception key", "namespace", exception.GetNamespace(), "name", exception.GetName())
|
||||
return resource, handlers.WithError(rule, engineapi.Mutation, "failed to compute exception key", err)
|
||||
} else {
|
||||
logger.V(3).Info("policy rule skipped due to policy exception", "exception", key)
|
||||
return resource, handlers.WithResponses(
|
||||
engineapi.RuleSkip(rule.Name, engineapi.Mutation, "rule skipped due to policy exception "+key).WithException(exception),
|
||||
)
|
||||
// check if there are policy exceptions that match the incoming resource
|
||||
matchedExceptions := engineutils.MatchesException(exceptions, policyContext, logger)
|
||||
if len(matchedExceptions) > 0 {
|
||||
var keys []string
|
||||
for i, exception := range matchedExceptions {
|
||||
key, err := cache.MetaNamespaceKeyFunc(&matchedExceptions[i])
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to compute policy exception key", "namespace", exception.GetNamespace(), "name", exception.GetName())
|
||||
return resource, handlers.WithError(rule, engineapi.Mutation, "failed to compute exception key", err)
|
||||
}
|
||||
keys = append(keys, key)
|
||||
}
|
||||
|
||||
logger.V(3).Info("policy rule is skipped due to policy exceptions", "exceptions", keys)
|
||||
return resource, handlers.WithResponses(
|
||||
engineapi.RuleSkip(rule.Name, engineapi.Mutation, "rule is skipped due to policy exceptions"+strings.Join(keys, ", ")).WithExceptions(matchedExceptions),
|
||||
)
|
||||
}
|
||||
|
||||
var responses []engineapi.RuleResponse
|
||||
|
|
|
@ -2,6 +2,7 @@ package mutation
|
|||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
json_patch "github.com/evanphx/json-patch/v5"
|
||||
"github.com/go-logr/logr"
|
||||
|
@ -68,19 +69,23 @@ func (h mutateImageHandler) Process(
|
|||
contextLoader engineapi.EngineContextLoader,
|
||||
exceptions []*kyvernov2.PolicyException,
|
||||
) (unstructured.Unstructured, []engineapi.RuleResponse) {
|
||||
// check if there is a policy exception matches the incoming resource
|
||||
exception := engineutils.MatchesException(exceptions, policyContext, logger)
|
||||
if exception != nil {
|
||||
key, err := cache.MetaNamespaceKeyFunc(exception)
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to compute policy exception key", "namespace", exception.GetNamespace(), "name", exception.GetName())
|
||||
return resource, handlers.WithError(rule, engineapi.Mutation, "failed to compute exception key", err)
|
||||
} else {
|
||||
logger.V(3).Info("policy rule skipped due to policy exception", "exception", key)
|
||||
return resource, handlers.WithResponses(
|
||||
engineapi.RuleSkip(rule.Name, engineapi.Mutation, "rule skipped due to policy exception "+key).WithException(exception),
|
||||
)
|
||||
// check if there are policy exceptions that match the incoming resource
|
||||
matchedExceptions := engineutils.MatchesException(exceptions, policyContext, logger)
|
||||
if len(matchedExceptions) > 0 {
|
||||
var keys []string
|
||||
for i, exception := range matchedExceptions {
|
||||
key, err := cache.MetaNamespaceKeyFunc(&matchedExceptions[i])
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to compute policy exception key", "namespace", exception.GetNamespace(), "name", exception.GetName())
|
||||
return resource, handlers.WithError(rule, engineapi.Mutation, "failed to compute exception key", err)
|
||||
}
|
||||
keys = append(keys, key)
|
||||
}
|
||||
|
||||
logger.V(3).Info("policy rule is skipped due to policy exceptions", "exceptions", keys)
|
||||
return resource, handlers.WithResponses(
|
||||
engineapi.RuleSkip(rule.Name, engineapi.Mutation, "rule is skipped due to policy exceptions"+strings.Join(keys, ", ")).WithExceptions(matchedExceptions),
|
||||
)
|
||||
}
|
||||
|
||||
jsonContext := policyContext.JSONContext()
|
||||
|
|
|
@ -2,6 +2,7 @@ package mutation
|
|||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
|
@ -30,19 +31,23 @@ func (h mutateResourceHandler) Process(
|
|||
contextLoader engineapi.EngineContextLoader,
|
||||
exceptions []*kyvernov2.PolicyException,
|
||||
) (unstructured.Unstructured, []engineapi.RuleResponse) {
|
||||
// check if there is a policy exception matches the incoming resource
|
||||
exception := engineutils.MatchesException(exceptions, policyContext, logger)
|
||||
if exception != nil {
|
||||
key, err := cache.MetaNamespaceKeyFunc(exception)
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to compute policy exception key", "namespace", exception.GetNamespace(), "name", exception.GetName())
|
||||
return resource, handlers.WithError(rule, engineapi.Mutation, "failed to compute exception key", err)
|
||||
} else {
|
||||
logger.V(3).Info("policy rule skipped due to policy exception", "exception", key)
|
||||
return resource, handlers.WithResponses(
|
||||
engineapi.RuleSkip(rule.Name, engineapi.Mutation, "rule skipped due to policy exception "+key).WithException(exception),
|
||||
)
|
||||
// check if there are policy exceptions that match the incoming resource
|
||||
matchedExceptions := engineutils.MatchesException(exceptions, policyContext, logger)
|
||||
if len(matchedExceptions) > 0 {
|
||||
var keys []string
|
||||
for i, exception := range matchedExceptions {
|
||||
key, err := cache.MetaNamespaceKeyFunc(&matchedExceptions[i])
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to compute policy exception key", "namespace", exception.GetNamespace(), "name", exception.GetName())
|
||||
return resource, handlers.WithError(rule, engineapi.Mutation, "failed to compute exception key", err)
|
||||
}
|
||||
keys = append(keys, key)
|
||||
}
|
||||
|
||||
logger.V(3).Info("policy rule is skipped due to policy exceptions", "exceptions", keys)
|
||||
return resource, handlers.WithResponses(
|
||||
engineapi.RuleSkip(rule.Name, engineapi.Mutation, "rule is skipped due to policy exceptions"+strings.Join(keys, ", ")).WithExceptions(matchedExceptions),
|
||||
)
|
||||
}
|
||||
|
||||
_, subresource := policyContext.ResourceKind()
|
||||
|
|
|
@ -3,6 +3,7 @@ package validation
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
|
@ -47,19 +48,23 @@ func (h validateCELHandler) Process(
|
|||
_ engineapi.EngineContextLoader,
|
||||
exceptions []*kyvernov2.PolicyException,
|
||||
) (unstructured.Unstructured, []engineapi.RuleResponse) {
|
||||
// check if there is a policy exception matches the incoming resource
|
||||
exception := engineutils.MatchesException(exceptions, policyContext, logger)
|
||||
if exception != nil {
|
||||
key, err := cache.MetaNamespaceKeyFunc(exception)
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to compute policy exception key", "namespace", exception.GetNamespace(), "name", exception.GetName())
|
||||
return resource, handlers.WithError(rule, engineapi.Validation, "failed to compute exception key", err)
|
||||
} else {
|
||||
logger.V(3).Info("policy rule skipped due to policy exception", "exception", key)
|
||||
return resource, handlers.WithResponses(
|
||||
engineapi.RuleSkip(rule.Name, engineapi.Validation, "rule skipped due to policy exception "+key).WithException(exception),
|
||||
)
|
||||
// check if there are policy exceptions that match the incoming resource
|
||||
matchedExceptions := engineutils.MatchesException(exceptions, policyContext, logger)
|
||||
if len(matchedExceptions) > 0 {
|
||||
var keys []string
|
||||
for i, exception := range matchedExceptions {
|
||||
key, err := cache.MetaNamespaceKeyFunc(&matchedExceptions[i])
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to compute policy exception key", "namespace", exception.GetNamespace(), "name", exception.GetName())
|
||||
return resource, handlers.WithError(rule, engineapi.Validation, "failed to compute exception key", err)
|
||||
}
|
||||
keys = append(keys, key)
|
||||
}
|
||||
|
||||
logger.V(3).Info("policy rule is skipped due to policy exceptions", "exceptions", keys)
|
||||
return resource, handlers.WithResponses(
|
||||
engineapi.RuleSkip(rule.Name, engineapi.Validation, "rule is skipped due to policy exceptions"+strings.Join(keys, ", ")).WithExceptions(matchedExceptions),
|
||||
)
|
||||
}
|
||||
|
||||
// check if a corresponding validating admission policy is generated
|
||||
|
|
|
@ -47,19 +47,23 @@ func (h validateImageHandler) Process(
|
|||
_ engineapi.EngineContextLoader,
|
||||
exceptions []*kyvernov2.PolicyException,
|
||||
) (unstructured.Unstructured, []engineapi.RuleResponse) {
|
||||
// check if there is a policy exception matches the incoming resource
|
||||
exception := engineutils.MatchesException(exceptions, policyContext, logger)
|
||||
if exception != nil {
|
||||
key, err := cache.MetaNamespaceKeyFunc(exception)
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to compute policy exception key", "namespace", exception.GetNamespace(), "name", exception.GetName())
|
||||
return resource, handlers.WithError(rule, engineapi.ImageVerify, "failed to compute exception key", err)
|
||||
} else {
|
||||
logger.V(3).Info("policy rule skipped due to policy exception", "exception", key)
|
||||
return resource, handlers.WithResponses(
|
||||
engineapi.RuleSkip(rule.Name, engineapi.ImageVerify, "rule skipped due to policy exception "+key).WithException(exception),
|
||||
)
|
||||
// check if there are policy exceptions that match the incoming resource
|
||||
matchedExceptions := engineutils.MatchesException(exceptions, policyContext, logger)
|
||||
if len(matchedExceptions) > 0 {
|
||||
var keys []string
|
||||
for i, exception := range matchedExceptions {
|
||||
key, err := cache.MetaNamespaceKeyFunc(&matchedExceptions[i])
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to compute policy exception key", "namespace", exception.GetNamespace(), "name", exception.GetName())
|
||||
return resource, handlers.WithError(rule, engineapi.Validation, "failed to compute exception key", err)
|
||||
}
|
||||
keys = append(keys, key)
|
||||
}
|
||||
|
||||
logger.V(3).Info("policy rule is skipped due to policy exceptions", "exceptions", keys)
|
||||
return resource, handlers.WithResponses(
|
||||
engineapi.RuleSkip(rule.Name, engineapi.Validation, "rule is skipped due to policy exceptions"+strings.Join(keys, ", ")).WithExceptions(matchedExceptions),
|
||||
)
|
||||
}
|
||||
|
||||
skippedImages := make([]string, 0)
|
||||
|
|
|
@ -59,19 +59,23 @@ func (h validateManifestHandler) Process(
|
|||
_ engineapi.EngineContextLoader,
|
||||
exceptions []*kyvernov2.PolicyException,
|
||||
) (unstructured.Unstructured, []engineapi.RuleResponse) {
|
||||
// check if there is a policy exception matches the incoming resource
|
||||
exception := engineutils.MatchesException(exceptions, policyContext, logger)
|
||||
if exception != nil {
|
||||
key, err := cache.MetaNamespaceKeyFunc(exception)
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to compute policy exception key", "namespace", exception.GetNamespace(), "name", exception.GetName())
|
||||
return resource, handlers.WithError(rule, engineapi.Validation, "failed to compute exception key", err)
|
||||
} else {
|
||||
logger.V(3).Info("policy rule skipped due to policy exception", "exception", key)
|
||||
return resource, handlers.WithResponses(
|
||||
engineapi.RuleSkip(rule.Name, engineapi.Validation, "rule skipped due to policy exception "+key).WithException(exception),
|
||||
)
|
||||
// check if there are policy exceptions that match the incoming resource
|
||||
matchedExceptions := engineutils.MatchesException(exceptions, policyContext, logger)
|
||||
if len(matchedExceptions) > 0 {
|
||||
var keys []string
|
||||
for i, exception := range matchedExceptions {
|
||||
key, err := cache.MetaNamespaceKeyFunc(&matchedExceptions[i])
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to compute policy exception key", "namespace", exception.GetNamespace(), "name", exception.GetName())
|
||||
return resource, handlers.WithError(rule, engineapi.Validation, "failed to compute exception key", err)
|
||||
}
|
||||
keys = append(keys, key)
|
||||
}
|
||||
|
||||
logger.V(3).Info("policy rule is skipped due to policy exceptions", "exceptions", keys)
|
||||
return resource, handlers.WithResponses(
|
||||
engineapi.RuleSkip(rule.Name, engineapi.Validation, "rule is skipped due to policy exceptions"+strings.Join(keys, ", ")).WithExceptions(matchedExceptions),
|
||||
)
|
||||
}
|
||||
|
||||
// verify manifest
|
||||
|
|
|
@ -44,17 +44,29 @@ func (h validatePssHandler) Process(
|
|||
return resource, nil
|
||||
}
|
||||
|
||||
// check if there is a policy exception matches the incoming resource
|
||||
exception := engineutils.MatchesException(exceptions, policyContext, logger)
|
||||
if exception != nil && !exception.HasPodSecurity() {
|
||||
key, err := cache.MetaNamespaceKeyFunc(exception)
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to compute policy exception key", "namespace", exception.GetNamespace(), "name", exception.GetName())
|
||||
return resource, handlers.WithError(rule, engineapi.Validation, "failed to compute exception key", err)
|
||||
} else {
|
||||
logger.V(3).Info("policy rule skipped due to policy exception", "exception", key)
|
||||
// check if there are policy exceptions that match the incoming resource
|
||||
matchedExceptions := engineutils.MatchesException(exceptions, policyContext, logger)
|
||||
if len(matchedExceptions) > 0 {
|
||||
var polex kyvernov2.PolicyException
|
||||
hasPodSecurity := true
|
||||
|
||||
for i, exception := range matchedExceptions {
|
||||
if !exception.HasPodSecurity() {
|
||||
hasPodSecurity = false
|
||||
polex = matchedExceptions[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !hasPodSecurity {
|
||||
key, err := cache.MetaNamespaceKeyFunc(&polex)
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to compute policy exception key", "namespace", polex.GetNamespace(), "name", polex.GetName())
|
||||
return resource, handlers.WithError(rule, engineapi.Validation, "failed to compute exception key", err)
|
||||
}
|
||||
logger.V(3).Info("policy rule is skipped due to policy exception", "exception", key)
|
||||
return resource, handlers.WithResponses(
|
||||
engineapi.RuleSkip(rule.Name, engineapi.Validation, "rule skipped due to policy exception "+key).WithException(exception),
|
||||
engineapi.RuleSkip(rule.Name, engineapi.Validation, "rule is skipped due to policy exception "+key).WithExceptions([]kyvernov2.PolicyException{polex}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -91,21 +103,25 @@ func (h validatePssHandler) Process(
|
|||
)
|
||||
} else {
|
||||
// apply pod security exceptions if exist
|
||||
if exception != nil && exception.HasPodSecurity() {
|
||||
pssChecks, err = pss.ApplyPodSecurityExclusion(levelVersion, exception.Spec.PodSecurity, pssChecks, pod)
|
||||
if len(pssChecks) == 0 && err == nil {
|
||||
key, err := cache.MetaNamespaceKeyFunc(exception)
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to compute policy exception key", "namespace", exception.GetNamespace(), "name", exception.GetName())
|
||||
return resource, handlers.WithError(rule, engineapi.Validation, "failed to compute exception key", err)
|
||||
} else {
|
||||
podSecurityChecks.Checks = pssChecks
|
||||
logger.V(3).Info("policy rule skipped due to policy exception", "exception", key)
|
||||
return resource, handlers.WithResponses(
|
||||
engineapi.RuleSkip(rule.Name, engineapi.Validation, "rule skipped due to policy exception "+key).WithException(exception).WithPodSecurityChecks(podSecurityChecks),
|
||||
)
|
||||
}
|
||||
var excludes []kyvernov1.PodSecurityStandard
|
||||
var keys []string
|
||||
for i, exception := range matchedExceptions {
|
||||
key, err := cache.MetaNamespaceKeyFunc(&matchedExceptions[i])
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to compute policy exception key", "namespace", exception.GetNamespace(), "name", exception.GetName())
|
||||
return resource, handlers.WithError(rule, engineapi.Validation, "failed to compute exception key", err)
|
||||
}
|
||||
keys = append(keys, key)
|
||||
excludes = append(excludes, exception.Spec.PodSecurity...)
|
||||
}
|
||||
|
||||
pssChecks, err = pss.ApplyPodSecurityExclusion(levelVersion, excludes, pssChecks, pod)
|
||||
if len(pssChecks) == 0 && err == nil {
|
||||
podSecurityChecks.Checks = pssChecks
|
||||
logger.V(3).Info("policy rule is skipped due to policy exceptions", "exceptions", keys)
|
||||
return resource, handlers.WithResponses(
|
||||
engineapi.RuleSkip(rule.Name, engineapi.Validation, "rule is skipped due to policy exceptions "+strings.Join(keys, ", ")).WithExceptions(matchedExceptions).WithPodSecurityChecks(podSecurityChecks),
|
||||
)
|
||||
}
|
||||
msg := fmt.Sprintf(`Validation rule '%s' failed. It violates PodSecurity "%s:%s": %s`, rule.Name, podSecurity.Level, podSecurity.Version, pss.FormatChecksPrint(pssChecks))
|
||||
return resource, handlers.WithResponses(
|
||||
|
|
|
@ -40,19 +40,23 @@ func (h validateResourceHandler) Process(
|
|||
contextLoader engineapi.EngineContextLoader,
|
||||
exceptions []*kyvernov2.PolicyException,
|
||||
) (unstructured.Unstructured, []engineapi.RuleResponse) {
|
||||
// check if there is a policy exception matches the incoming resource
|
||||
exception := engineutils.MatchesException(exceptions, policyContext, logger)
|
||||
if exception != nil {
|
||||
key, err := cache.MetaNamespaceKeyFunc(exception)
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to compute policy exception key", "namespace", exception.GetNamespace(), "name", exception.GetName())
|
||||
return resource, handlers.WithError(rule, engineapi.Validation, "failed to compute exception key", err)
|
||||
} else {
|
||||
logger.V(3).Info("policy rule skipped due to policy exception", "exception", key)
|
||||
return resource, handlers.WithResponses(
|
||||
engineapi.RuleSkip(rule.Name, engineapi.Validation, "rule skipped due to policy exception "+key).WithException(exception),
|
||||
)
|
||||
// check if there are policy exceptions that match the incoming resource
|
||||
matchedExceptions := engineutils.MatchesException(exceptions, policyContext, logger)
|
||||
if len(matchedExceptions) > 0 {
|
||||
var keys []string
|
||||
for i, exception := range matchedExceptions {
|
||||
key, err := cache.MetaNamespaceKeyFunc(&matchedExceptions[i])
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to compute policy exception key", "namespace", exception.GetNamespace(), "name", exception.GetName())
|
||||
return resource, handlers.WithError(rule, engineapi.Validation, "failed to compute exception key", err)
|
||||
}
|
||||
keys = append(keys, key)
|
||||
}
|
||||
|
||||
logger.V(3).Info("policy rule is skipped due to policy exceptions", "exceptions", keys)
|
||||
return resource, handlers.WithResponses(
|
||||
engineapi.RuleSkip(rule.Name, engineapi.Validation, "rule is skipped due to policy exceptions"+strings.Join(keys, ", ")).WithExceptions(matchedExceptions),
|
||||
)
|
||||
}
|
||||
v := newValidator(logger, contextLoader, policyContext, rule)
|
||||
return resource, handlers.WithResponses(v.validate(ctx))
|
||||
|
|
|
@ -15,7 +15,8 @@ import (
|
|||
|
||||
// MatchesException takes a list of exceptions and checks if there is an exception applies to the incoming resource.
|
||||
// It returns the matched policy exception.
|
||||
func MatchesException(polexs []*kyvernov2.PolicyException, policyContext engineapi.PolicyContext, logger logr.Logger) *kyvernov2.PolicyException {
|
||||
func MatchesException(polexs []*kyvernov2.PolicyException, policyContext engineapi.PolicyContext, logger logr.Logger) []kyvernov2.PolicyException {
|
||||
var matchedExceptions []kyvernov2.PolicyException
|
||||
gvk, subresource := policyContext.ResourceKind()
|
||||
resource := policyContext.NewResource()
|
||||
if resource.Object == nil {
|
||||
|
@ -40,10 +41,10 @@ func MatchesException(polexs []*kyvernov2.PolicyException, policyContext enginea
|
|||
continue
|
||||
}
|
||||
}
|
||||
return polex
|
||||
matchedExceptions = append(matchedExceptions, *polex)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return matchedExceptions
|
||||
}
|
||||
|
||||
func checkMatchesResources(
|
||||
|
|
|
@ -222,16 +222,51 @@ func NewBackgroundSuccessEvent(source Source, policy kyvernov1.PolicyInterface,
|
|||
}
|
||||
|
||||
func NewPolicyExceptionEvents(engineResponse engineapi.EngineResponse, ruleResp engineapi.RuleResponse, source Source) []Info {
|
||||
exception := ruleResp.Exception()
|
||||
exceptionName, exceptionNamespace := exception.GetName(), exception.GetNamespace()
|
||||
policyMessage := fmt.Sprintf("resource %s was skipped from rule %s due to policy exception %s/%s", resourceKey(engineResponse.PatchedResource), ruleResp.Name(), exceptionNamespace, exceptionName)
|
||||
pol := engineResponse.Policy().AsKyvernoPolicy()
|
||||
var exceptionMessage string
|
||||
exceptions := ruleResp.Exceptions()
|
||||
exceptionNames := make([]string, 0, len(exceptions))
|
||||
events := make([]Info, 0, len(exceptions))
|
||||
|
||||
// build the events of the policy exceptions
|
||||
pol := engineResponse.Policy().AsKyvernoPolicy()
|
||||
if pol.GetNamespace() == "" {
|
||||
exceptionMessage = fmt.Sprintf("resource %s was skipped from policy rule %s/%s", resourceKey(engineResponse.PatchedResource), pol.GetName(), ruleResp.Name())
|
||||
} else {
|
||||
exceptionMessage = fmt.Sprintf("resource %s was skipped from policy rule %s/%s/%s", resourceKey(engineResponse.PatchedResource), pol.GetNamespace(), pol.GetName(), ruleResp.Name())
|
||||
}
|
||||
|
||||
related := engineResponse.GetResourceSpec()
|
||||
for _, exception := range exceptions {
|
||||
ns := exception.GetNamespace()
|
||||
name := exception.GetName()
|
||||
exceptionNames = append(exceptionNames, ns+"/"+name)
|
||||
|
||||
exceptionEvent := Info{
|
||||
Regarding: corev1.ObjectReference{
|
||||
// TODO: iirc it's not safe to assume api version is set
|
||||
APIVersion: "kyverno.io/v2",
|
||||
Kind: "PolicyException",
|
||||
Name: name,
|
||||
Namespace: ns,
|
||||
UID: exception.GetUID(),
|
||||
},
|
||||
Related: &corev1.ObjectReference{
|
||||
APIVersion: related.APIVersion,
|
||||
Kind: related.Kind,
|
||||
Name: related.Name,
|
||||
Namespace: related.Namespace,
|
||||
UID: types.UID(related.UID),
|
||||
},
|
||||
Reason: PolicySkipped,
|
||||
Message: exceptionMessage,
|
||||
Source: source,
|
||||
Action: ResourcePassed,
|
||||
}
|
||||
events = append(events, exceptionEvent)
|
||||
}
|
||||
|
||||
// build the policy events
|
||||
policyMessage := fmt.Sprintf("resource %s was skipped from rule %s due to policy exceptions %s", resourceKey(engineResponse.PatchedResource), ruleResp.Name(), strings.Join(exceptionNames, ", "))
|
||||
regarding := corev1.ObjectReference{
|
||||
// TODO: iirc it's not safe to assume api version is set
|
||||
APIVersion: "kyverno.io/v1",
|
||||
|
@ -240,7 +275,6 @@ func NewPolicyExceptionEvents(engineResponse engineapi.EngineResponse, ruleResp
|
|||
Namespace: pol.GetNamespace(),
|
||||
UID: pol.GetUID(),
|
||||
}
|
||||
related := engineResponse.GetResourceSpec()
|
||||
policyEvent := Info{
|
||||
Regarding: regarding,
|
||||
Related: &corev1.ObjectReference{
|
||||
|
@ -255,28 +289,8 @@ func NewPolicyExceptionEvents(engineResponse engineapi.EngineResponse, ruleResp
|
|||
Source: source,
|
||||
Action: ResourcePassed,
|
||||
}
|
||||
exceptionEvent := Info{
|
||||
Regarding: corev1.ObjectReference{
|
||||
// TODO: iirc it's not safe to assume api version is set
|
||||
APIVersion: "kyverno.io/v2",
|
||||
Kind: "PolicyException",
|
||||
Name: exceptionName,
|
||||
Namespace: exceptionNamespace,
|
||||
UID: exception.GetUID(),
|
||||
},
|
||||
Related: &corev1.ObjectReference{
|
||||
APIVersion: related.APIVersion,
|
||||
Kind: related.Kind,
|
||||
Name: related.Name,
|
||||
Namespace: related.Namespace,
|
||||
UID: types.UID(related.UID),
|
||||
},
|
||||
Reason: PolicySkipped,
|
||||
Message: exceptionMessage,
|
||||
Source: source,
|
||||
Action: ResourcePassed,
|
||||
}
|
||||
return []Info{policyEvent, exceptionEvent}
|
||||
events = append(events, policyEvent)
|
||||
return events
|
||||
}
|
||||
|
||||
func NewCleanupPolicyEvent(policy kyvernov2.CleanupPolicyInterface, resource unstructured.Unstructured, err error) Info {
|
||||
|
|
|
@ -110,8 +110,13 @@ func ToPolicyReportResult(policyType engineapi.PolicyType, policyName string, ru
|
|||
*resource,
|
||||
}
|
||||
}
|
||||
if ruleResult.Exception() != nil {
|
||||
addProperty("exception", ruleResult.Exception().Name, &result)
|
||||
exceptions := ruleResult.Exceptions()
|
||||
if len(exceptions) > 0 {
|
||||
var names []string
|
||||
for _, exception := range exceptions {
|
||||
names = append(names, exception.Name)
|
||||
}
|
||||
addProperty("exceptions", strings.Join(names, ","), &result)
|
||||
}
|
||||
pss := ruleResult.PodSecurityChecks()
|
||||
if pss != nil && len(pss.Checks) > 0 {
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
## Description
|
||||
|
||||
This test creates two policy exceptions that match the same policy. It is expected that the pod that satisfies both exceptions will be created successfully.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
1. Create a policy that applies the baseline profile.
|
||||
|
||||
2. Create two exceptions for the init containters as follows:
|
||||
- The first exception `init1-exception-baseline` allows the values of `NET_ADMIN` and `NET_RAW` capabilities in the init containers.
|
||||
- The second exception `init2-exception-baseline` allows the values of `SYS_TIME` capabilities in the init containers.
|
||||
|
||||
3. Create a pod with two init containers. The first init container should have the `NET_ADMIN` and `NET_RAW` capabilities, and the second init container should have the `SYS_TIME` capability. It is expected that the pod will be created successfully as it matches both exceptions.
|
||||
|
||||
|
||||
## Reference Issue(s)
|
||||
|
||||
#10580
|
|
@ -0,0 +1,21 @@
|
|||
apiVersion: chainsaw.kyverno.io/v1alpha1
|
||||
kind: Test
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: multiple-exceptions-with-pod-security
|
||||
spec:
|
||||
steps:
|
||||
- name: step-01
|
||||
try:
|
||||
- apply:
|
||||
file: policy.yaml
|
||||
- assert:
|
||||
file: policy-assert.yaml
|
||||
- name: step-02
|
||||
try:
|
||||
- apply:
|
||||
file: exceptions.yaml
|
||||
- name: step-03
|
||||
try:
|
||||
- apply:
|
||||
file: pod.yaml
|
|
@ -0,0 +1,44 @@
|
|||
apiVersion: kyverno.io/v2
|
||||
kind: PolicyException
|
||||
metadata:
|
||||
name: init1-exception-baseline
|
||||
spec:
|
||||
exceptions:
|
||||
- policyName: psp-baseline
|
||||
ruleNames:
|
||||
- baseline
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Pod
|
||||
podSecurity:
|
||||
- controlName: Capabilities
|
||||
images:
|
||||
- 'alpine:latest'
|
||||
restrictedField: spec.initContainers[*].securityContext.capabilities.add
|
||||
values:
|
||||
- NET_ADMIN
|
||||
- NET_RAW
|
||||
---
|
||||
apiVersion: kyverno.io/v2
|
||||
kind: PolicyException
|
||||
metadata:
|
||||
name: init2-exception-baseline
|
||||
spec:
|
||||
exceptions:
|
||||
- policyName: psp-baseline
|
||||
ruleNames:
|
||||
- baseline
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Pod
|
||||
podSecurity:
|
||||
- controlName: Capabilities
|
||||
images:
|
||||
- 'busybox:latest'
|
||||
restrictedField: spec.initContainers[*].securityContext.capabilities.add
|
||||
values:
|
||||
- SYS_TIME
|
|
@ -0,0 +1,56 @@
|
|||
---
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: test-pod
|
||||
spec:
|
||||
containers:
|
||||
- image: alpine:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: primary
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
runAsGroup: 1000
|
||||
runAsNonRoot: true
|
||||
runAsUser: 1000
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
initContainers:
|
||||
- image: alpine:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: init1
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
add:
|
||||
- NET_ADMIN
|
||||
- NET_RAW
|
||||
drop:
|
||||
- ALL
|
||||
privileged: false
|
||||
readOnlyRootFilesystem: false
|
||||
runAsGroup: 10001
|
||||
runAsNonRoot: true
|
||||
runAsUser: 10001
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
- image: busybox:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: init2
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
add:
|
||||
- SYS_TIME
|
||||
drop:
|
||||
- ALL
|
||||
privileged: false
|
||||
readOnlyRootFilesystem: true
|
||||
runAsGroup: 10002
|
||||
runAsNonRoot: true
|
||||
runAsUser: 10002
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
|
@ -0,0 +1,9 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: psp-baseline
|
||||
status:
|
||||
conditions:
|
||||
- reason: Succeeded
|
||||
status: "True"
|
||||
type: Ready
|
|
@ -0,0 +1,19 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: psp-baseline
|
||||
spec:
|
||||
failurePolicy: Ignore
|
||||
background: true
|
||||
validationFailureAction: Enforce
|
||||
rules:
|
||||
- name: baseline
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Pod
|
||||
validate:
|
||||
podSecurity:
|
||||
level: baseline
|
||||
version: v1.29
|
|
@ -0,0 +1,18 @@
|
|||
## Description
|
||||
|
||||
This test creates two policy exceptions that match the same policy. It is expected that the pod that satisfies both exceptions will be created successfully.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
1. Create a policy that applies the baseline profile.
|
||||
|
||||
2. Create two exceptions as follows:
|
||||
- The first exception `exception-baseline` that exempts the whole pod from the baseline profile.
|
||||
- The second exception `init-exception-baseline` allows the values of `SYS_TIME` capabilities in the init containers.
|
||||
|
||||
3. Create a pod with two init containers. The first init container should have the `NET_ADMIN` and `NET_RAW` capabilities, and the second init container should have the `SYS_TIME` capability. It is expected that the pod will be created successfully as it matches both exceptions.
|
||||
|
||||
|
||||
## Reference Issue(s)
|
||||
|
||||
#10580
|
21
test/conformance/chainsaw/exceptions/multiple-exceptions/chainsaw-test.yaml
Executable file
21
test/conformance/chainsaw/exceptions/multiple-exceptions/chainsaw-test.yaml
Executable file
|
@ -0,0 +1,21 @@
|
|||
apiVersion: chainsaw.kyverno.io/v1alpha1
|
||||
kind: Test
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: multiple-exceptions
|
||||
spec:
|
||||
steps:
|
||||
- name: step-01
|
||||
try:
|
||||
- apply:
|
||||
file: policy.yaml
|
||||
- assert:
|
||||
file: policy-assert.yaml
|
||||
- name: step-02
|
||||
try:
|
||||
- apply:
|
||||
file: exceptions.yaml
|
||||
- name: step-03
|
||||
try:
|
||||
- apply:
|
||||
file: pod.yaml
|
|
@ -0,0 +1,36 @@
|
|||
apiVersion: kyverno.io/v2
|
||||
kind: PolicyException
|
||||
metadata:
|
||||
name: exception-baseline
|
||||
spec:
|
||||
exceptions:
|
||||
- policyName: psp-baseline
|
||||
ruleNames:
|
||||
- baseline
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Pod
|
||||
---
|
||||
apiVersion: kyverno.io/v2
|
||||
kind: PolicyException
|
||||
metadata:
|
||||
name: init-exception-baseline
|
||||
spec:
|
||||
exceptions:
|
||||
- policyName: psp-baseline
|
||||
ruleNames:
|
||||
- baseline
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Pod
|
||||
podSecurity:
|
||||
- controlName: Capabilities
|
||||
images:
|
||||
- 'busybox:latest'
|
||||
restrictedField: spec.initContainers[*].securityContext.capabilities.add
|
||||
values:
|
||||
- SYS_TIME
|
|
@ -0,0 +1,56 @@
|
|||
---
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: test-pod
|
||||
spec:
|
||||
containers:
|
||||
- image: alpine:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: primary
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
runAsGroup: 1000
|
||||
runAsNonRoot: true
|
||||
runAsUser: 1000
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
initContainers:
|
||||
- image: alpine:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: init1
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
add:
|
||||
- NET_ADMIN
|
||||
- NET_RAW
|
||||
drop:
|
||||
- ALL
|
||||
privileged: false
|
||||
readOnlyRootFilesystem: false
|
||||
runAsGroup: 10001
|
||||
runAsNonRoot: true
|
||||
runAsUser: 10001
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
- image: busybox:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: init2
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
add:
|
||||
- SYS_TIME
|
||||
drop:
|
||||
- ALL
|
||||
privileged: false
|
||||
readOnlyRootFilesystem: true
|
||||
runAsGroup: 10002
|
||||
runAsNonRoot: true
|
||||
runAsUser: 10002
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
|
@ -0,0 +1,9 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: psp-baseline
|
||||
status:
|
||||
conditions:
|
||||
- reason: Succeeded
|
||||
status: "True"
|
||||
type: Ready
|
|
@ -0,0 +1,19 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: psp-baseline
|
||||
spec:
|
||||
failurePolicy: Ignore
|
||||
background: true
|
||||
validationFailureAction: Enforce
|
||||
rules:
|
||||
- name: baseline
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Pod
|
||||
validate:
|
||||
podSecurity:
|
||||
level: baseline
|
||||
version: v1.29
|
|
@ -16,7 +16,7 @@ results:
|
|||
scored: true
|
||||
source: kyverno
|
||||
properties:
|
||||
exception: mynewpolex
|
||||
exceptions: mynewpolex
|
||||
summary:
|
||||
error: 0
|
||||
fail: 0
|
||||
|
|
|
@ -9,7 +9,7 @@ metadata:
|
|||
results:
|
||||
- policy: psa-1
|
||||
properties:
|
||||
exception: pod-security-exception
|
||||
exceptions: pod-security-exception
|
||||
result: skip
|
||||
rule: restricted
|
||||
scored: true
|
||||
|
|
|
@ -16,7 +16,7 @@ results:
|
|||
scored: true
|
||||
source: kyverno
|
||||
properties:
|
||||
exception: mynewpolex
|
||||
exceptions: mynewpolex
|
||||
summary:
|
||||
error: 0
|
||||
fail: 0
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
## Description
|
||||
|
||||
This test makes sure that the report is generated correctly when multiple exceptions are created for the same policy.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
1. Create a pod with two init containers. The first init container should have the `NET_ADMIN` and `NET_RAW` capabilities, and the second init container should have the `SYS_TIME` capability.
|
||||
|
||||
2. Create a policy that applies the baseline profile.
|
||||
|
||||
3. Create two exceptions for the init containters as follows:
|
||||
- The first exception `init1-exception-baseline` allows the values of `NET_ADMIN` and `NET_RAW` capabilities in the init containers.
|
||||
- The second exception `init2-exception-baseline` allows the values of `SYS_TIME` capabilities in the init containers.
|
||||
|
||||
4. It is expected that a policy report is generated with a `skip` result.
|
||||
|
||||
5. Delete the first exception.
|
||||
|
||||
6. It is expected that a policy report is updated with a `fail` result since the first init container violates the policy and it isn't excluded by the second exception.
|
||||
|
||||
|
||||
|
||||
## Reference Issue(s)
|
||||
|
||||
#10580
|
|
@ -0,0 +1,45 @@
|
|||
apiVersion: chainsaw.kyverno.io/v1alpha1
|
||||
kind: Test
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: multiple-exceptions-with-pod-security
|
||||
spec:
|
||||
steps:
|
||||
- name: step-01
|
||||
try:
|
||||
- apply:
|
||||
file: pod.yaml
|
||||
- name: step-02
|
||||
try:
|
||||
- apply:
|
||||
file: policy.yaml
|
||||
- assert:
|
||||
file: policy-assert.yaml
|
||||
- name: step-03
|
||||
try:
|
||||
- apply:
|
||||
file: exceptions.yaml
|
||||
- name: step-04
|
||||
try:
|
||||
- sleep:
|
||||
duration: 5s
|
||||
- name: step-05
|
||||
try:
|
||||
- assert:
|
||||
file: report-skip-assert.yaml
|
||||
- name: step-06
|
||||
try:
|
||||
- script:
|
||||
env:
|
||||
- name: NAMESPACE
|
||||
value: ($namespace)
|
||||
content: |
|
||||
kubectl delete polex init1-exception-baseline -n $NAMESPACE
|
||||
- name: step-07
|
||||
try:
|
||||
- sleep:
|
||||
duration: 5s
|
||||
- name: step-08
|
||||
try:
|
||||
- assert:
|
||||
file: report-fail-assert.yaml
|
|
@ -0,0 +1,44 @@
|
|||
apiVersion: kyverno.io/v2
|
||||
kind: PolicyException
|
||||
metadata:
|
||||
name: init1-exception-baseline
|
||||
spec:
|
||||
exceptions:
|
||||
- policyName: psp-baseline
|
||||
ruleNames:
|
||||
- baseline
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Pod
|
||||
podSecurity:
|
||||
- controlName: Capabilities
|
||||
images:
|
||||
- 'alpine:latest'
|
||||
restrictedField: spec.initContainers[*].securityContext.capabilities.add
|
||||
values:
|
||||
- NET_ADMIN
|
||||
- NET_RAW
|
||||
---
|
||||
apiVersion: kyverno.io/v2
|
||||
kind: PolicyException
|
||||
metadata:
|
||||
name: init2-exception-baseline
|
||||
spec:
|
||||
exceptions:
|
||||
- policyName: psp-baseline
|
||||
ruleNames:
|
||||
- baseline
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Pod
|
||||
podSecurity:
|
||||
- controlName: Capabilities
|
||||
images:
|
||||
- 'busybox:latest'
|
||||
restrictedField: spec.initContainers[*].securityContext.capabilities.add
|
||||
values:
|
||||
- SYS_TIME
|
|
@ -0,0 +1,56 @@
|
|||
---
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: test-pod
|
||||
spec:
|
||||
containers:
|
||||
- image: alpine:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: primary
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
runAsGroup: 1000
|
||||
runAsNonRoot: true
|
||||
runAsUser: 1000
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
initContainers:
|
||||
- image: alpine:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: init1
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
add:
|
||||
- NET_ADMIN
|
||||
- NET_RAW
|
||||
drop:
|
||||
- ALL
|
||||
privileged: false
|
||||
readOnlyRootFilesystem: false
|
||||
runAsGroup: 10001
|
||||
runAsNonRoot: true
|
||||
runAsUser: 10001
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
- image: busybox:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
name: init2
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
add:
|
||||
- SYS_TIME
|
||||
drop:
|
||||
- ALL
|
||||
privileged: false
|
||||
readOnlyRootFilesystem: true
|
||||
runAsGroup: 10002
|
||||
runAsNonRoot: true
|
||||
runAsUser: 10002
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
|
@ -0,0 +1,9 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: psp-baseline
|
||||
status:
|
||||
conditions:
|
||||
- reason: Succeeded
|
||||
status: "True"
|
||||
type: Ready
|
|
@ -0,0 +1,19 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: psp-baseline
|
||||
spec:
|
||||
failurePolicy: Ignore
|
||||
background: true
|
||||
validationFailureAction: Enforce
|
||||
rules:
|
||||
- name: baseline
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Pod
|
||||
validate:
|
||||
podSecurity:
|
||||
level: baseline
|
||||
version: v1.29
|
|
@ -0,0 +1,33 @@
|
|||
apiVersion: wgpolicyk8s.io/v1alpha2
|
||||
kind: PolicyReport
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/managed-by: kyverno
|
||||
ownerReferences:
|
||||
- apiVersion: v1
|
||||
kind: Pod
|
||||
name: test-pod
|
||||
results:
|
||||
- message: 'Validation rule ''baseline'' failed. It violates PodSecurity "baseline:v1.29":
|
||||
(Forbidden reason: non-default capabilities, field error list: [spec.initContainers[0].securityContext.capabilities.add
|
||||
is forbidden, forbidden values found: [NET_ADMIN NET_RAW]])'
|
||||
policy: psp-baseline
|
||||
properties:
|
||||
controls: capabilities_baseline
|
||||
controlsJSON: '[{"ID":"capabilities_baseline","Name":"Capabilities","Images":["docker.io/alpine:latest","docker.io/busybox:latest"]}]'
|
||||
standard: baseline
|
||||
version: v1.29
|
||||
result: fail
|
||||
rule: baseline
|
||||
scored: true
|
||||
source: kyverno
|
||||
scope:
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
name: test-pod
|
||||
summary:
|
||||
error: 0
|
||||
fail: 1
|
||||
pass: 0
|
||||
skip: 0
|
||||
warn: 0
|
|
@ -0,0 +1,27 @@
|
|||
apiVersion: wgpolicyk8s.io/v1alpha2
|
||||
kind: PolicyReport
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/managed-by: kyverno
|
||||
ownerReferences:
|
||||
- apiVersion: v1
|
||||
kind: Pod
|
||||
name: test-pod
|
||||
results:
|
||||
- policy: psp-baseline
|
||||
properties:
|
||||
exceptions: init1-exception-baseline,init2-exception-baseline
|
||||
result: skip
|
||||
rule: baseline
|
||||
scored: true
|
||||
source: kyverno
|
||||
scope:
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
name: test-pod
|
||||
summary:
|
||||
error: 0
|
||||
fail: 0
|
||||
pass: 0
|
||||
skip: 1
|
||||
warn: 0
|
Loading…
Add table
Reference in a new issue