mirror of
https://github.com/kyverno/kyverno.git
synced 2024-12-14 11:57:48 +00:00
refactor validating admission policies (#7835)
Signed-off-by: Mariam Fahmy <mariam.fahmy@nirmata.com>
This commit is contained in:
parent
3ed7303efe
commit
34a6119cc3
19 changed files with 483 additions and 264 deletions
cmd/cli/kubectl-kyverno
apply
test
utils/common
pkg/validatingadmissionpolicy
test/cli
test-validating-admission-policy/validate-deployment
test-vap
check-deployments-replica
disallow-host-path
|
@ -345,6 +345,7 @@ func (c *ApplyCommandConfig) applyCommandHelper() (*common.ResultCounts, []*unst
|
|||
for _, policy := range policies {
|
||||
policyRulesCount += len(autogen.ComputeRules(policy))
|
||||
}
|
||||
policyRulesCount += len(validatingAdmissionPolicies)
|
||||
fmt.Printf("\nApplying %d policy rule(s) to %d resource(s)...\n", policyRulesCount, len(resources))
|
||||
}
|
||||
|
||||
|
|
|
@ -176,8 +176,8 @@ func Test_Apply(t *testing.T) {
|
|||
},
|
||||
{
|
||||
config: ApplyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../test/cli/test-validating-admission-policy/validate-deployment/policy.yaml"},
|
||||
ResourcePaths: []string{"../../../../test/cli/test-validating-admission-policy/validate-deployment/deployment1.yaml"},
|
||||
PolicyPaths: []string{"../../../../test/cli/test-vap/check-deployments-replica/policy.yaml"},
|
||||
ResourcePaths: []string{"../../../../test/cli/test-vap/check-deployments-replica/deployment1.yaml"},
|
||||
PolicyReport: true,
|
||||
},
|
||||
expectedPolicyReports: []preport.PolicyReport{
|
||||
|
@ -194,8 +194,44 @@ func Test_Apply(t *testing.T) {
|
|||
},
|
||||
{
|
||||
config: ApplyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../test/cli/test-validating-admission-policy/validate-deployment/policy.yaml"},
|
||||
ResourcePaths: []string{"../../../../test/cli/test-validating-admission-policy/validate-deployment/deployment2.yaml"},
|
||||
PolicyPaths: []string{"../../../../test/cli/test-vap/check-deployments-replica/policy.yaml"},
|
||||
ResourcePaths: []string{"../../../../test/cli/test-vap/check-deployments-replica/deployment2.yaml"},
|
||||
PolicyReport: true,
|
||||
},
|
||||
expectedPolicyReports: []preport.PolicyReport{
|
||||
{
|
||||
Summary: preport.PolicyReportSummary{
|
||||
Pass: 0,
|
||||
Fail: 1,
|
||||
Skip: 0,
|
||||
Error: 0,
|
||||
Warn: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
config: ApplyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../test/cli/test-vap/disallow-host-path/policy.yaml"},
|
||||
ResourcePaths: []string{"../../../../test/cli/test-vap/disallow-host-path/pod1.yaml"},
|
||||
PolicyReport: true,
|
||||
},
|
||||
expectedPolicyReports: []preport.PolicyReport{
|
||||
{
|
||||
Summary: preport.PolicyReportSummary{
|
||||
Pass: 1,
|
||||
Fail: 0,
|
||||
Skip: 0,
|
||||
Error: 0,
|
||||
Warn: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
config: ApplyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../test/cli/test-vap/disallow-host-path/policy.yaml"},
|
||||
ResourcePaths: []string{"../../../../test/cli/test-vap/disallow-host-path/pod2.yaml"},
|
||||
PolicyReport: true,
|
||||
},
|
||||
expectedPolicyReports: []preport.PolicyReport{
|
||||
|
|
|
@ -64,10 +64,10 @@ func buildPolicyResults(auditWarn bool, engineResponses ...engineapi.EngineRespo
|
|||
isVAP := engineResponse.IsValidatingAdmissionPolicy()
|
||||
|
||||
if isVAP {
|
||||
validatingAdmissionPolicy := engineResponse.ValidatingAdmissionPolicy()
|
||||
ns = validatingAdmissionPolicy.GetNamespace()
|
||||
policyName = validatingAdmissionPolicy.GetName()
|
||||
ann = validatingAdmissionPolicy.GetAnnotations()
|
||||
vap := engineResponse.ValidatingAdmissionPolicy()
|
||||
ns = vap.GetNamespace()
|
||||
policyName = vap.GetName()
|
||||
ann = vap.GetAnnotations()
|
||||
} else {
|
||||
kyvernoPolicy := engineResponse.Policy()
|
||||
ns = kyvernoPolicy.GetNamespace()
|
||||
|
|
|
@ -21,10 +21,10 @@ type TestResults struct {
|
|||
// It's required in case policy is a kyverno policy.
|
||||
// +optional
|
||||
Rule string `json:"rule,omitempty"`
|
||||
// IsVap indicates if the policy is a validating admission policy.
|
||||
// IsValidatingAdmissionPolicy indicates if the policy is a validating admission policy.
|
||||
// It's required in case policy is a validating admission policy.
|
||||
// +optional
|
||||
IsVap bool `json:"isVap"`
|
||||
IsValidatingAdmissionPolicy bool `json:"isValidatingAdmissionPolicy"`
|
||||
// Result mentions the result that the user is expecting.
|
||||
// Possible values are pass, fail and skip.
|
||||
Result policyreportv1alpha2.PolicyResult `json:"result"`
|
||||
|
|
|
@ -136,7 +136,7 @@ func applyPoliciesFromPath(
|
|||
|
||||
for _, rule := range autogen.ComputeRules(p) {
|
||||
for _, res := range values.Results {
|
||||
if res.IsVap {
|
||||
if res.IsValidatingAdmissionPolicy {
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -340,10 +340,10 @@ func buildPolicyResults(
|
|||
isVAP := resp.IsValidatingAdmissionPolicy()
|
||||
|
||||
if isVAP {
|
||||
validatingAdmissionPolicy := resp.ValidatingAdmissionPolicy()
|
||||
ns = validatingAdmissionPolicy.GetNamespace()
|
||||
name = validatingAdmissionPolicy.GetName()
|
||||
ann = validatingAdmissionPolicy.GetAnnotations()
|
||||
vap := resp.ValidatingAdmissionPolicy()
|
||||
ns = vap.GetNamespace()
|
||||
name = vap.GetName()
|
||||
ann = vap.GetAnnotations()
|
||||
} else {
|
||||
kyvernoPolicy := resp.Policy()
|
||||
ns = kyvernoPolicy.GetNamespace()
|
||||
|
@ -398,8 +398,8 @@ func buildPolicyResults(
|
|||
for _, resource := range test.Resources {
|
||||
if resource == resourceName {
|
||||
var resultsKey string
|
||||
resultsKey = GetResultKeyAccordingToTestResults(userDefinedPolicyNamespace, test.Policy, test.Rule, test.Namespace, test.Kind, resource, test.IsVap)
|
||||
if !test.IsVap {
|
||||
resultsKey = GetResultKeyAccordingToTestResults(userDefinedPolicyNamespace, test.Policy, test.Rule, test.Namespace, test.Kind, resource, test.IsValidatingAdmissionPolicy)
|
||||
if !test.IsValidatingAdmissionPolicy {
|
||||
if !slices.Contains(rules, test.Rule) {
|
||||
if !slices.Contains(rules, "autogen-"+test.Rule) {
|
||||
if !slices.Contains(rules, "autogen-cronjob-"+test.Rule) {
|
||||
|
@ -407,12 +407,12 @@ func buildPolicyResults(
|
|||
} else {
|
||||
testResults[i].AutoGeneratedRule = "autogen-cronjob"
|
||||
test.Rule = "autogen-cronjob-" + test.Rule
|
||||
resultsKey = GetResultKeyAccordingToTestResults(userDefinedPolicyNamespace, test.Policy, test.Rule, test.Namespace, test.Kind, resource, test.IsVap)
|
||||
resultsKey = GetResultKeyAccordingToTestResults(userDefinedPolicyNamespace, test.Policy, test.Rule, test.Namespace, test.Kind, resource, test.IsValidatingAdmissionPolicy)
|
||||
}
|
||||
} else {
|
||||
testResults[i].AutoGeneratedRule = "autogen"
|
||||
test.Rule = "autogen-" + test.Rule
|
||||
resultsKey = GetResultKeyAccordingToTestResults(userDefinedPolicyNamespace, test.Policy, test.Rule, test.Namespace, test.Kind, resource, test.IsVap)
|
||||
resultsKey = GetResultKeyAccordingToTestResults(userDefinedPolicyNamespace, test.Policy, test.Rule, test.Namespace, test.Kind, resource, test.IsValidatingAdmissionPolicy)
|
||||
}
|
||||
|
||||
if results[resultsKey].Result == "" {
|
||||
|
@ -434,8 +434,8 @@ func buildPolicyResults(
|
|||
if test.Resource != "" {
|
||||
if test.Policy == policyName && test.Resource == resourceName {
|
||||
var resultsKey string
|
||||
resultsKey = GetResultKeyAccordingToTestResults(userDefinedPolicyNamespace, test.Policy, test.Rule, test.Namespace, test.Kind, test.Resource, test.IsVap)
|
||||
if !test.IsVap {
|
||||
resultsKey = GetResultKeyAccordingToTestResults(userDefinedPolicyNamespace, test.Policy, test.Rule, test.Namespace, test.Kind, test.Resource, test.IsValidatingAdmissionPolicy)
|
||||
if !test.IsValidatingAdmissionPolicy {
|
||||
if !slices.Contains(rules, test.Rule) {
|
||||
if !slices.Contains(rules, "autogen-"+test.Rule) {
|
||||
if !slices.Contains(rules, "autogen-cronjob-"+test.Rule) {
|
||||
|
@ -443,12 +443,12 @@ func buildPolicyResults(
|
|||
} else {
|
||||
testResults[i].AutoGeneratedRule = "autogen-cronjob"
|
||||
test.Rule = "autogen-cronjob-" + test.Rule
|
||||
resultsKey = GetResultKeyAccordingToTestResults(userDefinedPolicyNamespace, test.Policy, test.Rule, test.Namespace, test.Kind, test.Resource, test.IsVap)
|
||||
resultsKey = GetResultKeyAccordingToTestResults(userDefinedPolicyNamespace, test.Policy, test.Rule, test.Namespace, test.Kind, test.Resource, test.IsValidatingAdmissionPolicy)
|
||||
}
|
||||
} else {
|
||||
testResults[i].AutoGeneratedRule = "autogen"
|
||||
test.Rule = "autogen-" + test.Rule
|
||||
resultsKey = GetResultKeyAccordingToTestResults(userDefinedPolicyNamespace, test.Policy, test.Rule, test.Namespace, test.Kind, test.Resource, test.IsVap)
|
||||
resultsKey = GetResultKeyAccordingToTestResults(userDefinedPolicyNamespace, test.Policy, test.Rule, test.Namespace, test.Kind, test.Resource, test.IsValidatingAdmissionPolicy)
|
||||
}
|
||||
|
||||
if results[resultsKey].Result == "" {
|
||||
|
@ -474,7 +474,7 @@ func buildPolicyResults(
|
|||
var resultsKey []string
|
||||
var resultKey string
|
||||
var result policyreportv1alpha2.PolicyReportResult
|
||||
resultsKey = GetAllPossibleResultsKey(policyNamespace, policyName, rule.Name(), resourceNamespace, resourceKind, resourceName, test.IsVap)
|
||||
resultsKey = GetAllPossibleResultsKey(policyNamespace, policyName, rule.Name(), resourceNamespace, resourceKind, resourceName, test.IsValidatingAdmissionPolicy)
|
||||
for _, key := range resultsKey {
|
||||
if val, ok := results[key]; ok {
|
||||
result = val
|
||||
|
@ -507,7 +507,7 @@ func buildPolicyResults(
|
|||
var resultsKey []string
|
||||
var resultKey string
|
||||
var result policyreportv1alpha2.PolicyReportResult
|
||||
resultsKey = GetAllPossibleResultsKey(policyNamespace, policyName, rule.Name(), resourceNamespace, resourceKind, resourceName, test.IsVap)
|
||||
resultsKey = GetAllPossibleResultsKey(policyNamespace, policyName, rule.Name(), resourceNamespace, resourceKind, resourceName, test.IsValidatingAdmissionPolicy)
|
||||
for _, key := range resultsKey {
|
||||
if val, ok := results[key]; ok {
|
||||
result = val
|
||||
|
@ -537,14 +537,14 @@ func buildPolicyResults(
|
|||
}
|
||||
|
||||
for _, rule := range resp.PolicyResponse.Rules {
|
||||
if rule.RuleType() != engineapi.Validation && rule.RuleType() != engineapi.ImageVerify || test.Rule != rule.Name() && !test.IsVap {
|
||||
if rule.RuleType() != engineapi.Validation && rule.RuleType() != engineapi.ImageVerify || test.Rule != rule.Name() && !test.IsValidatingAdmissionPolicy {
|
||||
continue
|
||||
}
|
||||
|
||||
var resultsKey []string
|
||||
var resultKey string
|
||||
var result policyreportv1alpha2.PolicyReportResult
|
||||
resultsKey = GetAllPossibleResultsKey(policyNamespace, policyName, rule.Name(), resourceNamespace, resourceKind, resourceName, test.IsVap)
|
||||
resultsKey = GetAllPossibleResultsKey(policyNamespace, policyName, rule.Name(), resourceNamespace, resourceKind, resourceName, test.IsValidatingAdmissionPolicy)
|
||||
for _, key := range resultsKey {
|
||||
if val, ok := results[key]; ok {
|
||||
result = val
|
||||
|
@ -579,11 +579,11 @@ func buildPolicyResults(
|
|||
return results, testResults
|
||||
}
|
||||
|
||||
func GetAllPossibleResultsKey(policyNamespace, policy, rule, resourceNamespace, kind, resource string, isVap bool) []string {
|
||||
func GetAllPossibleResultsKey(policyNamespace, policy, rule, resourceNamespace, kind, resource string, isVAP bool) []string {
|
||||
var resultsKey []string
|
||||
var resultKey1, resultKey2, resultKey3, resultKey4 string
|
||||
|
||||
if isVap {
|
||||
if isVAP {
|
||||
resultKey1 = fmt.Sprintf("%s-%s-%s", policy, kind, resource)
|
||||
resultKey2 = fmt.Sprintf("%s-%s-%s-%s", policy, resourceNamespace, kind, resource)
|
||||
resultKey3 = fmt.Sprintf("%s-%s-%s-%s", policyNamespace, policy, kind, resource)
|
||||
|
@ -599,9 +599,9 @@ func GetAllPossibleResultsKey(policyNamespace, policy, rule, resourceNamespace,
|
|||
return resultsKey
|
||||
}
|
||||
|
||||
func GetResultKeyAccordingToTestResults(policyNs, policy, rule, resourceNs, kind, resource string, isVap bool) string {
|
||||
func GetResultKeyAccordingToTestResults(policyNs, policy, rule, resourceNs, kind, resource string, isVAP bool) string {
|
||||
var resultKey string
|
||||
if isVap {
|
||||
if isVAP {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s", policy, kind, resource)
|
||||
|
||||
if policyNs != "" && resourceNs != "" {
|
||||
|
|
|
@ -158,7 +158,7 @@ func printTestResult(resps map[string]policyreportv1alpha2.PolicyReportResult, t
|
|||
testCount++
|
||||
row.Resource = color.Resource(v.Kind, v.Namespace, resource)
|
||||
var ruleNameInResultKey string
|
||||
if !v.IsVap {
|
||||
if !v.IsValidatingAdmissionPolicy {
|
||||
if v.AutoGeneratedRule != "" {
|
||||
ruleNameInResultKey = fmt.Sprintf("%s-%s", v.AutoGeneratedRule, v.Rule)
|
||||
} else {
|
||||
|
@ -167,7 +167,7 @@ func printTestResult(resps map[string]policyreportv1alpha2.PolicyReportResult, t
|
|||
}
|
||||
|
||||
var resultKey string
|
||||
if !v.IsVap {
|
||||
if !v.IsValidatingAdmissionPolicy {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s", v.Policy, ruleNameInResultKey, v.Kind, resource)
|
||||
} else {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s", v.Policy, v.Kind, resource)
|
||||
|
@ -177,13 +177,13 @@ func printTestResult(resps map[string]policyreportv1alpha2.PolicyReportResult, t
|
|||
var ns string
|
||||
ns, v.Policy = getUserDefinedPolicyNameAndNamespace(v.Policy)
|
||||
if found && v.Namespace != "" {
|
||||
if !v.IsVap {
|
||||
if !v.IsValidatingAdmissionPolicy {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s-%s", ns, v.Policy, ruleNameInResultKey, v.Namespace, v.Kind, resource)
|
||||
} else {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", ns, v.Policy, v.Namespace, v.Kind, resource)
|
||||
}
|
||||
} else if found {
|
||||
if !v.IsVap {
|
||||
if !v.IsValidatingAdmissionPolicy {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", ns, v.Policy, ruleNameInResultKey, v.Kind, resource)
|
||||
} else {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s", ns, v.Policy, v.Kind, resource)
|
||||
|
@ -193,7 +193,7 @@ func printTestResult(resps map[string]policyreportv1alpha2.PolicyReportResult, t
|
|||
} else if v.Namespace != "" {
|
||||
row.Resource = color.Resource(v.Kind, v.Namespace, resource)
|
||||
|
||||
if !v.IsVap {
|
||||
if !v.IsValidatingAdmissionPolicy {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", v.Policy, ruleNameInResultKey, v.Namespace, v.Kind, resource)
|
||||
} else {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s", v.Policy, v.Namespace, v.Kind, resource)
|
||||
|
@ -242,7 +242,7 @@ func printTestResult(resps map[string]policyreportv1alpha2.PolicyReportResult, t
|
|||
countDeprecatedResource++
|
||||
row.Resource = color.Resource(v.Kind, v.Namespace, v.Resource)
|
||||
var ruleNameInResultKey string
|
||||
if !v.IsVap {
|
||||
if !v.IsValidatingAdmissionPolicy {
|
||||
if v.AutoGeneratedRule != "" {
|
||||
ruleNameInResultKey = fmt.Sprintf("%s-%s", v.AutoGeneratedRule, v.Rule)
|
||||
} else {
|
||||
|
@ -251,7 +251,7 @@ func printTestResult(resps map[string]policyreportv1alpha2.PolicyReportResult, t
|
|||
}
|
||||
|
||||
var resultKey string
|
||||
if !v.IsVap {
|
||||
if !v.IsValidatingAdmissionPolicy {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s", v.Policy, ruleNameInResultKey, v.Kind, v.Resource)
|
||||
} else {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s", v.Policy, v.Kind, v.Resource)
|
||||
|
@ -261,13 +261,13 @@ func printTestResult(resps map[string]policyreportv1alpha2.PolicyReportResult, t
|
|||
var ns string
|
||||
ns, v.Policy = getUserDefinedPolicyNameAndNamespace(v.Policy)
|
||||
if found && v.Namespace != "" {
|
||||
if !v.IsVap {
|
||||
if !v.IsValidatingAdmissionPolicy {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s-%s", ns, v.Policy, ruleNameInResultKey, v.Namespace, v.Kind, v.Resource)
|
||||
} else {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", ns, v.Policy, v.Namespace, v.Kind, v.Resource)
|
||||
}
|
||||
} else if found {
|
||||
if !v.IsVap {
|
||||
if !v.IsValidatingAdmissionPolicy {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", ns, v.Policy, ruleNameInResultKey, v.Kind, v.Resource)
|
||||
} else {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s", ns, v.Policy, v.Kind, v.Resource)
|
||||
|
@ -278,7 +278,7 @@ func printTestResult(resps map[string]policyreportv1alpha2.PolicyReportResult, t
|
|||
} else if v.Namespace != "" {
|
||||
row.Resource = color.Resource(v.Kind, v.Namespace, v.Resource)
|
||||
|
||||
if !v.IsVap {
|
||||
if !v.IsValidatingAdmissionPolicy {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", v.Policy, ruleNameInResultKey, v.Namespace, v.Kind, v.Resource)
|
||||
} else {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s", v.Policy, v.Namespace, v.Kind, v.Resource)
|
||||
|
|
|
@ -17,9 +17,7 @@ import (
|
|||
"github.com/kyverno/kyverno/pkg/clients/dclient"
|
||||
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
|
||||
yamlutils "github.com/kyverno/kyverno/pkg/utils/yaml"
|
||||
"golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
|
||||
"github.com/kyverno/kyverno/pkg/validatingadmissionpolicy"
|
||||
"k8s.io/api/admissionregistration/v1alpha1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
@ -347,62 +345,16 @@ func GetKindsFromRule(rule kyvernov1.Rule, client dclient.Interface) (map[schema
|
|||
return resourceTypesMap, subresourceMap
|
||||
}
|
||||
|
||||
func getKindsFromValidatingAdmissionRule(rule admissionregistrationv1.Rule, client dclient.Interface) (map[schema.GroupVersionKind]bool, map[schema.GroupVersionKind]values.Subresource, error) {
|
||||
func getKindsFromValidatingAdmissionPolicy(policy v1alpha1.ValidatingAdmissionPolicy, client dclient.Interface) (map[schema.GroupVersionKind]bool, map[schema.GroupVersionKind]values.Subresource) {
|
||||
resourceTypesMap := make(map[schema.GroupVersionKind]bool)
|
||||
subresourceMap := make(map[schema.GroupVersionKind]values.Subresource)
|
||||
|
||||
group := rule.APIGroups[0]
|
||||
if group == "" {
|
||||
group = "*"
|
||||
}
|
||||
version := rule.APIVersions[0]
|
||||
|
||||
for _, resource := range rule.Resources {
|
||||
var kind, subresource string
|
||||
|
||||
isSubresource := kubeutils.IsSubresource(resource)
|
||||
if isSubresource {
|
||||
parts := strings.Split(resource, "/")
|
||||
kind = cases.Title(language.English, cases.NoLower).String(parts[0])
|
||||
kind, _ = strings.CutSuffix(kind, "s")
|
||||
|
||||
subresource = parts[1]
|
||||
} else {
|
||||
resource = cases.Title(language.English, cases.NoLower).String(resource)
|
||||
resource, _ = strings.CutSuffix(resource, "s")
|
||||
|
||||
kind = resource
|
||||
subresource = ""
|
||||
}
|
||||
|
||||
gvrss, err := client.Discovery().FindResources(group, version, kind, subresource)
|
||||
if err != nil {
|
||||
log.Info("failed to find resource", "kind", kind, "error", err)
|
||||
return resourceTypesMap, subresourceMap, err
|
||||
}
|
||||
|
||||
for parent, child := range gvrss {
|
||||
// The resource is not a subresource
|
||||
if parent.SubResource == "" {
|
||||
resourceTypesMap[parent.GroupVersionKind()] = true
|
||||
} else {
|
||||
gvk := schema.GroupVersionKind{
|
||||
Group: child.Group, Version: child.Version, Kind: child.Kind,
|
||||
}
|
||||
subresourceMap[gvk] = values.Subresource{
|
||||
APIResource: child,
|
||||
ParentResource: metav1.APIResource{
|
||||
Group: parent.Group,
|
||||
Version: parent.Version,
|
||||
Kind: parent.Kind,
|
||||
Name: parent.Resource,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
kinds := validatingadmissionpolicy.GetKinds(policy)
|
||||
for _, kind := range kinds {
|
||||
addGVKToResourceTypesMap(kind, resourceTypesMap, subresourceMap, client)
|
||||
}
|
||||
|
||||
return resourceTypesMap, subresourceMap, nil
|
||||
return resourceTypesMap, subresourceMap
|
||||
}
|
||||
|
||||
func addGVKToResourceTypesMap(kind string, resourceTypesMap map[schema.GroupVersionKind]bool, subresourceMap map[schema.GroupVersionKind]values.Subresource, client dclient.Interface) {
|
||||
|
|
|
@ -1,146 +1,23 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
||||
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
|
||||
"k8s.io/api/admissionregistration/v1alpha1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
"k8s.io/apiserver/pkg/admission/plugin/cel"
|
||||
"k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy"
|
||||
"k8s.io/apiserver/pkg/admission/plugin/webhook/matchconditions"
|
||||
celconfig "k8s.io/apiserver/pkg/apis/cel"
|
||||
"github.com/kyverno/kyverno/pkg/validatingadmissionpolicy"
|
||||
)
|
||||
|
||||
type ValidatingAdmissionPolicies struct{}
|
||||
|
||||
func (p *ValidatingAdmissionPolicies) ApplyPolicyOnResource(c ApplyPolicyConfig) ([]engineapi.EngineResponse, error) {
|
||||
var engineResponses []engineapi.EngineResponse
|
||||
engineResp := validatingadmissionpolicy.Validate(c.ValidatingAdmissionPolicy, *c.Resource)
|
||||
ruleResp := engineResp.PolicyResponse.Rules[0]
|
||||
|
||||
resPath := fmt.Sprintf("%s/%s/%s", c.Resource.GetNamespace(), c.Resource.GetKind(), c.Resource.GetName())
|
||||
log.V(3).Info("applying policy on resource", "policy", c.ValidatingAdmissionPolicy.GetName(), "resource", resPath)
|
||||
|
||||
startTime := time.Now()
|
||||
|
||||
validations := c.ValidatingAdmissionPolicy.Spec.Validations
|
||||
var expressions, messageExpressions []cel.ExpressionAccessor
|
||||
|
||||
for _, validation := range validations {
|
||||
var reason metav1.StatusReason
|
||||
if validation.Reason == nil {
|
||||
reason = metav1.StatusReasonInvalid
|
||||
} else {
|
||||
reason = *validation.Reason
|
||||
}
|
||||
|
||||
var message string
|
||||
if validation.Message != "" && validation.MessageExpression != "" {
|
||||
message = validation.MessageExpression
|
||||
} else if validation.Message != "" {
|
||||
message = validation.Message
|
||||
} else if validation.MessageExpression != "" {
|
||||
message = validation.MessageExpression
|
||||
} else {
|
||||
message = fmt.Sprintf("error: failed to create %s: %s \"%s\" is forbidden: ValidatingAdmissionPolicy '%s' denied request: failed expression: %s", c.Resource.GetKind(), c.Resource.GetAPIVersion(), c.Resource.GetName(), c.ValidatingAdmissionPolicy.Name, validation.Expression)
|
||||
}
|
||||
|
||||
condition := &validatingadmissionpolicy.ValidationCondition{
|
||||
Expression: validation.Expression,
|
||||
Message: message,
|
||||
Reason: &reason,
|
||||
}
|
||||
|
||||
messageCondition := &validatingadmissionpolicy.MessageExpressionCondition{
|
||||
MessageExpression: validation.MessageExpression,
|
||||
}
|
||||
|
||||
expressions = append(expressions, condition)
|
||||
messageExpressions = append(messageExpressions, messageCondition)
|
||||
}
|
||||
|
||||
hasParams := c.ValidatingAdmissionPolicy.Spec.ParamKind != nil
|
||||
|
||||
filterCompiler := cel.NewFilterCompiler()
|
||||
filter := filterCompiler.Compile(expressions, cel.OptionalVariableDeclarations{HasParams: hasParams, HasAuthorizer: false}, celconfig.PerCallLimit)
|
||||
messageExpressionfilter := filterCompiler.Compile(messageExpressions, cel.OptionalVariableDeclarations{HasParams: hasParams, HasAuthorizer: false}, celconfig.PerCallLimit)
|
||||
|
||||
admissionAttributes := admission.NewAttributesRecord(c.Resource.DeepCopyObject(), nil, c.Resource.GroupVersionKind(), c.Resource.GetNamespace(), c.Resource.GetName(), schema.GroupVersionResource{}, "", admission.Create, nil, false, nil)
|
||||
versionedAttr, _ := admission.NewVersionedAttributes(admissionAttributes, admissionAttributes.GetKind(), nil)
|
||||
|
||||
ctx := context.TODO()
|
||||
failPolicy := admissionregistrationv1.FailurePolicyType(*c.ValidatingAdmissionPolicy.Spec.FailurePolicy)
|
||||
|
||||
matchConditions := c.ValidatingAdmissionPolicy.Spec.MatchConditions
|
||||
var matchExpressions []cel.ExpressionAccessor
|
||||
|
||||
for _, expression := range matchConditions {
|
||||
condition := &matchconditions.MatchCondition{
|
||||
Name: expression.Name,
|
||||
Expression: expression.Expression,
|
||||
}
|
||||
matchExpressions = append(matchExpressions, condition)
|
||||
}
|
||||
|
||||
matchFilter := filterCompiler.Compile(matchExpressions, cel.OptionalVariableDeclarations{HasParams: hasParams, HasAuthorizer: false}, celconfig.PerCallLimit)
|
||||
|
||||
var matchPolicy *v1alpha1.MatchPolicyType
|
||||
if c.ValidatingAdmissionPolicy.Spec.MatchConstraints.MatchPolicy == nil {
|
||||
equivalent := v1alpha1.Equivalent
|
||||
matchPolicy = &equivalent
|
||||
} else {
|
||||
matchPolicy = c.ValidatingAdmissionPolicy.Spec.MatchConstraints.MatchPolicy
|
||||
}
|
||||
|
||||
newMatcher := matchconditions.NewMatcher(matchFilter, nil, &failPolicy, string(*matchPolicy), "")
|
||||
|
||||
auditAnnotations := c.ValidatingAdmissionPolicy.Spec.AuditAnnotations
|
||||
var auditExpressions []cel.ExpressionAccessor
|
||||
|
||||
for _, expression := range auditAnnotations {
|
||||
condition := &validatingadmissionpolicy.AuditAnnotationCondition{
|
||||
Key: expression.Key,
|
||||
ValueExpression: expression.ValueExpression,
|
||||
}
|
||||
auditExpressions = append(auditExpressions, condition)
|
||||
}
|
||||
auditAnnotationFilter := filterCompiler.Compile(auditExpressions, cel.OptionalVariableDeclarations{HasParams: hasParams, HasAuthorizer: false}, celconfig.PerCallLimit)
|
||||
|
||||
validator := validatingadmissionpolicy.NewValidator(filter, newMatcher, auditAnnotationFilter, messageExpressionfilter, &failPolicy, nil)
|
||||
validateResult := validator.Validate(ctx, versionedAttr, nil, celconfig.RuntimeCELCostBudget)
|
||||
|
||||
engineResponse := engineapi.NewEngineResponseWithValidatingAdmissionPolicy(*c.Resource, c.ValidatingAdmissionPolicy, nil)
|
||||
policyResp := engineapi.NewPolicyResponse()
|
||||
var ruleResp *engineapi.RuleResponse
|
||||
isPass := true
|
||||
|
||||
for _, policyDecision := range validateResult.Decisions {
|
||||
if policyDecision.Evaluation == validatingadmissionpolicy.EvalError {
|
||||
isPass = false
|
||||
c.Rc.Error++
|
||||
ruleResp = engineapi.RuleError(c.ValidatingAdmissionPolicy.GetName(), engineapi.Validation, policyDecision.Message, nil)
|
||||
break
|
||||
} else if policyDecision.Action == validatingadmissionpolicy.ActionDeny {
|
||||
isPass = false
|
||||
c.Rc.Fail++
|
||||
ruleResp = engineapi.RuleFail(c.ValidatingAdmissionPolicy.GetName(), engineapi.Validation, policyDecision.Message)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if isPass {
|
||||
if ruleResp.Status() == engineapi.RuleStatusPass {
|
||||
c.Rc.Pass++
|
||||
ruleResp = engineapi.RulePass(c.ValidatingAdmissionPolicy.GetName(), engineapi.Validation, "")
|
||||
} else if ruleResp.Status() == engineapi.RuleStatusFail {
|
||||
c.Rc.Fail++
|
||||
} else if ruleResp.Status() == engineapi.RuleStatusError {
|
||||
c.Rc.Error++
|
||||
}
|
||||
|
||||
policyResp.Add(engineapi.NewExecutionStats(startTime, time.Now()), *ruleResp)
|
||||
|
||||
engineResponse = engineResponse.WithPolicyResponse(policyResp)
|
||||
engineResponses = append(engineResponses, engineResponse)
|
||||
|
||||
return engineResponses, nil
|
||||
return []engineapi.EngineResponse{engineResp}, nil
|
||||
}
|
||||
|
|
|
@ -21,15 +21,13 @@ func (r *ValidatingAdmissionResources) FetchResourcesFromPolicy(resourcePaths []
|
|||
var subresourceMap map[schema.GroupVersionKind]values.Subresource
|
||||
|
||||
for _, policy := range r.policies {
|
||||
for _, rule := range policy.Spec.MatchConstraints.ResourceRules {
|
||||
var resourceTypesInRule map[schema.GroupVersionKind]bool
|
||||
resourceTypesInRule, subresourceMap, err = getKindsFromValidatingAdmissionRule(rule.RuleWithOperations.Rule, dClient)
|
||||
if err != nil {
|
||||
return resources, err
|
||||
}
|
||||
for resourceKind := range resourceTypesInRule {
|
||||
resourceTypesMap[resourceKind] = true
|
||||
}
|
||||
var resourceTypesInRule map[schema.GroupVersionKind]bool
|
||||
resourceTypesInRule, subresourceMap = getKindsFromValidatingAdmissionPolicy(policy, dClient)
|
||||
if err != nil {
|
||||
return resources, err
|
||||
}
|
||||
for resourceKind := range resourceTypesInRule {
|
||||
resourceTypesMap[resourceKind] = true
|
||||
}
|
||||
}
|
||||
|
||||
|
|
5
pkg/validatingadmissionpolicy/log.go
Normal file
5
pkg/validatingadmissionpolicy/log.go
Normal file
|
@ -0,0 +1,5 @@
|
|||
package validatingadmissionpolicy
|
||||
|
||||
import "github.com/kyverno/kyverno/pkg/logging"
|
||||
|
||||
var logger = logging.WithName("validatingadmissionpolicy")
|
182
pkg/validatingadmissionpolicy/validatingadmissionpolicy.go
Normal file
182
pkg/validatingadmissionpolicy/validatingadmissionpolicy.go
Normal file
|
@ -0,0 +1,182 @@
|
|||
package validatingadmissionpolicy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
||||
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
|
||||
"golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
|
||||
"k8s.io/api/admissionregistration/v1alpha1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
"k8s.io/apiserver/pkg/admission/plugin/cel"
|
||||
"k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy"
|
||||
"k8s.io/apiserver/pkg/admission/plugin/webhook/matchconditions"
|
||||
celconfig "k8s.io/apiserver/pkg/apis/cel"
|
||||
)
|
||||
|
||||
func GetKinds(policy v1alpha1.ValidatingAdmissionPolicy) []string {
|
||||
var kindList []string
|
||||
|
||||
matchResources := policy.Spec.MatchConstraints
|
||||
for _, rule := range matchResources.ResourceRules {
|
||||
group := rule.APIGroups[0]
|
||||
version := rule.APIVersions[0]
|
||||
for _, resource := range rule.Resources {
|
||||
isSubresource := kubeutils.IsSubresource(resource)
|
||||
if isSubresource {
|
||||
parts := strings.Split(resource, "/")
|
||||
|
||||
kind := cases.Title(language.English, cases.NoLower).String(parts[0])
|
||||
kind, _ = strings.CutSuffix(kind, "s")
|
||||
subresource := parts[1]
|
||||
|
||||
if group == "" {
|
||||
kindList = append(kindList, strings.Join([]string{version, kind, subresource}, "/"))
|
||||
} else {
|
||||
kindList = append(kindList, strings.Join([]string{group, version, kind, subresource}, "/"))
|
||||
}
|
||||
} else {
|
||||
resource = cases.Title(language.English, cases.NoLower).String(resource)
|
||||
resource, _ = strings.CutSuffix(resource, "s")
|
||||
kind := resource
|
||||
|
||||
if group == "" {
|
||||
kindList = append(kindList, strings.Join([]string{version, kind}, "/"))
|
||||
} else {
|
||||
kindList = append(kindList, strings.Join([]string{group, version, kind}, "/"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return kindList
|
||||
}
|
||||
|
||||
func Validate(policy v1alpha1.ValidatingAdmissionPolicy, resource unstructured.Unstructured) engineapi.EngineResponse {
|
||||
resPath := fmt.Sprintf("%s/%s/%s", resource.GetNamespace(), resource.GetKind(), resource.GetName())
|
||||
logger.V(3).Info("applying policy on resource", "policy", policy.GetName(), "resource", resPath)
|
||||
|
||||
startTime := time.Now()
|
||||
|
||||
var expressions, messageExpressions, matchExpressions, auditExpressions []cel.ExpressionAccessor
|
||||
|
||||
validations := policy.Spec.Validations
|
||||
matchConditions := policy.Spec.MatchConditions
|
||||
auditAnnotations := policy.Spec.AuditAnnotations
|
||||
|
||||
hasParam := policy.Spec.ParamKind != nil
|
||||
|
||||
var failPolicy admissionregistrationv1.FailurePolicyType
|
||||
if policy.Spec.FailurePolicy == nil {
|
||||
failPolicy = admissionregistrationv1.Fail
|
||||
} else {
|
||||
failPolicy = admissionregistrationv1.FailurePolicyType(*policy.Spec.FailurePolicy)
|
||||
}
|
||||
|
||||
var matchPolicy v1alpha1.MatchPolicyType
|
||||
if policy.Spec.MatchConstraints.MatchPolicy == nil {
|
||||
matchPolicy = v1alpha1.Equivalent
|
||||
} else {
|
||||
matchPolicy = *policy.Spec.MatchConstraints.MatchPolicy
|
||||
}
|
||||
|
||||
for _, cel := range validations {
|
||||
condition := &validatingadmissionpolicy.ValidationCondition{
|
||||
Expression: cel.Expression,
|
||||
Message: cel.Message,
|
||||
}
|
||||
messageCondition := &validatingadmissionpolicy.MessageExpressionCondition{
|
||||
MessageExpression: cel.MessageExpression,
|
||||
}
|
||||
expressions = append(expressions, condition)
|
||||
messageExpressions = append(messageExpressions, messageCondition)
|
||||
}
|
||||
|
||||
for _, expression := range matchConditions {
|
||||
condition := &matchconditions.MatchCondition{
|
||||
Name: expression.Name,
|
||||
Expression: expression.Expression,
|
||||
}
|
||||
matchExpressions = append(matchExpressions, condition)
|
||||
}
|
||||
|
||||
for _, auditAnnotation := range auditAnnotations {
|
||||
auditCondition := &validatingadmissionpolicy.AuditAnnotationCondition{
|
||||
Key: auditAnnotation.Key,
|
||||
ValueExpression: auditAnnotation.ValueExpression,
|
||||
}
|
||||
auditExpressions = append(auditExpressions, auditCondition)
|
||||
}
|
||||
|
||||
filterCompiler := cel.NewFilterCompiler()
|
||||
filter := filterCompiler.Compile(
|
||||
expressions,
|
||||
cel.OptionalVariableDeclarations{HasParams: hasParam, HasAuthorizer: false},
|
||||
celconfig.PerCallLimit,
|
||||
)
|
||||
messageExpressionfilter := filterCompiler.Compile(
|
||||
messageExpressions,
|
||||
cel.OptionalVariableDeclarations{HasParams: hasParam, HasAuthorizer: false},
|
||||
celconfig.PerCallLimit,
|
||||
)
|
||||
auditAnnotationFilter := filterCompiler.Compile(
|
||||
auditExpressions,
|
||||
cel.OptionalVariableDeclarations{HasParams: hasParam, HasAuthorizer: false},
|
||||
celconfig.PerCallLimit,
|
||||
)
|
||||
matchConditionFilter := filterCompiler.Compile(
|
||||
matchExpressions,
|
||||
cel.OptionalVariableDeclarations{HasParams: hasParam, HasAuthorizer: false},
|
||||
celconfig.PerCallLimit,
|
||||
)
|
||||
|
||||
newMatcher := matchconditions.NewMatcher(matchConditionFilter, nil, &failPolicy, string(matchPolicy), "")
|
||||
validator := validatingadmissionpolicy.NewValidator(filter, newMatcher, auditAnnotationFilter, messageExpressionfilter, nil, nil)
|
||||
|
||||
admissionAttributes := admission.NewAttributesRecord(
|
||||
resource.DeepCopyObject(),
|
||||
nil, resource.GroupVersionKind(),
|
||||
resource.GetNamespace(),
|
||||
resource.GetName(),
|
||||
schema.GroupVersionResource{},
|
||||
"",
|
||||
admission.Create,
|
||||
nil,
|
||||
false,
|
||||
nil,
|
||||
)
|
||||
versionedAttr, _ := admission.NewVersionedAttributes(admissionAttributes, admissionAttributes.GetKind(), nil)
|
||||
validateResult := validator.Validate(context.TODO(), versionedAttr, nil, celconfig.RuntimeCELCostBudget)
|
||||
|
||||
engineResponse := engineapi.NewEngineResponseWithValidatingAdmissionPolicy(resource, policy, nil)
|
||||
policyResp := engineapi.NewPolicyResponse()
|
||||
var ruleResp *engineapi.RuleResponse
|
||||
isPass := true
|
||||
|
||||
for _, policyDecision := range validateResult.Decisions {
|
||||
if policyDecision.Evaluation == validatingadmissionpolicy.EvalError {
|
||||
isPass = false
|
||||
ruleResp = engineapi.RuleError(policy.GetName(), engineapi.Validation, policyDecision.Message, nil)
|
||||
break
|
||||
} else if policyDecision.Action == validatingadmissionpolicy.ActionDeny {
|
||||
isPass = false
|
||||
ruleResp = engineapi.RuleFail(policy.GetName(), engineapi.Validation, policyDecision.Message)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if isPass {
|
||||
ruleResp = engineapi.RulePass(policy.GetName(), engineapi.Validation, "")
|
||||
}
|
||||
policyResp.Add(engineapi.NewExecutionStats(startTime, time.Now()), *ruleResp)
|
||||
engineResponse = engineResponse.WithPolicyResponse(policyResp)
|
||||
|
||||
return engineResponse
|
||||
}
|
137
pkg/validatingadmissionpolicy/validatingadmissionpolicy_test.go
Normal file
137
pkg/validatingadmissionpolicy/validatingadmissionpolicy_test.go
Normal file
|
@ -0,0 +1,137 @@
|
|||
package validatingadmissionpolicy
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
yamlutils "github.com/kyverno/kyverno/pkg/utils/yaml"
|
||||
)
|
||||
|
||||
func TestGetKinds(t *testing.T) {
|
||||
type test struct {
|
||||
name string
|
||||
policy []byte
|
||||
wantKinds []string
|
||||
}
|
||||
|
||||
tests := []test{
|
||||
{
|
||||
name: "Matching pods",
|
||||
policy: []byte(`
|
||||
apiVersion: admissionregistration.k8s.io/v1alpha1
|
||||
kind: ValidatingAdmissionPolicy
|
||||
metadata:
|
||||
name: "policy-1"
|
||||
spec:
|
||||
failurePolicy: Fail
|
||||
matchConstraints:
|
||||
resourceRules:
|
||||
- apiGroups: [""]
|
||||
apiVersions: ["v1"]
|
||||
operations: ["CREATE", "UPDATE"]
|
||||
resources: ["pods"]
|
||||
validations:
|
||||
- expression: "object.metadata.name.matches('nginx')"
|
||||
`),
|
||||
wantKinds: []string{"v1/Pod"},
|
||||
},
|
||||
{
|
||||
name: "Matching deployments, replicasets, daemonsets and statefulsets",
|
||||
policy: []byte(`
|
||||
apiVersion: admissionregistration.k8s.io/v1alpha1
|
||||
kind: ValidatingAdmissionPolicy
|
||||
metadata:
|
||||
name: "policy-2"
|
||||
spec:
|
||||
failurePolicy: Fail
|
||||
matchConstraints:
|
||||
resourceRules:
|
||||
- apiGroups: ["apps"]
|
||||
apiVersions: ["v1"]
|
||||
operations: ["CREATE", "UPDATE"]
|
||||
resources: ["deployments", "replicasets", "daemonsets", "statefulsets"]
|
||||
validations:
|
||||
- expression: "object.spec.replicas <= 5"
|
||||
`),
|
||||
wantKinds: []string{"apps/v1/Deployment", "apps/v1/Replicaset", "apps/v1/Daemonset", "apps/v1/Statefulset"},
|
||||
},
|
||||
{
|
||||
name: "Matching deployments/scale",
|
||||
policy: []byte(`
|
||||
apiVersion: admissionregistration.k8s.io/v1alpha1
|
||||
kind: ValidatingAdmissionPolicy
|
||||
metadata:
|
||||
name: "policy-3"
|
||||
spec:
|
||||
failurePolicy: Fail
|
||||
matchConstraints:
|
||||
resourceRules:
|
||||
- apiGroups: ["apps"]
|
||||
apiVersions: ["v1"]
|
||||
operations: ["CREATE", "UPDATE"]
|
||||
resources: ["deployments/scale"]
|
||||
validations:
|
||||
- expression: "object.spec.replicas <= 5"
|
||||
`),
|
||||
wantKinds: []string{"apps/v1/Deployment/scale"},
|
||||
},
|
||||
{
|
||||
name: "Matching jobs and cronjobs",
|
||||
policy: []byte(`
|
||||
apiVersion: admissionregistration.k8s.io/v1alpha1
|
||||
kind: ValidatingAdmissionPolicy
|
||||
metadata:
|
||||
name: "policy-4"
|
||||
spec:
|
||||
failurePolicy: Fail
|
||||
matchConstraints:
|
||||
resourceRules:
|
||||
- apiGroups: ["batch"]
|
||||
apiVersions: ["v1"]
|
||||
operations: ["CREATE", "UPDATE"]
|
||||
resources: ["jobs", "cronjobs"]
|
||||
validations:
|
||||
- expression: "object.spec.jobTemplate.spec.template.spec.containers.all(container, has(container.securityContext) && has(container.securityContext.readOnlyRootFilesystem) && container.securityContext.readOnlyRootFilesystem == true)"
|
||||
`),
|
||||
wantKinds: []string{"batch/v1/Job", "batch/v1/Cronjob"},
|
||||
},
|
||||
{
|
||||
name: "Multiple resource rules",
|
||||
policy: []byte(`
|
||||
apiVersion: admissionregistration.k8s.io/v1alpha1
|
||||
kind: ValidatingAdmissionPolicy
|
||||
metadata:
|
||||
name: "policy-5"
|
||||
spec:
|
||||
failurePolicy: Fail
|
||||
matchConstraints:
|
||||
resourceRules:
|
||||
- apiGroups: [""]
|
||||
apiVersions: ["v1"]
|
||||
operations: ["CREATE", "UPDATE"]
|
||||
resources: ["pods"]
|
||||
- apiGroups: ["apps"]
|
||||
apiVersions: ["v1"]
|
||||
operations: ["CREATE", "UPDATE"]
|
||||
resources: ["deployments", "replicasets", "daemonsets", "statefulsets"]
|
||||
- apiGroups: ["batch"]
|
||||
apiVersions: ["v1"]
|
||||
operations: ["CREATE", "UPDATE"]
|
||||
resources: ["jobs", "cronjobs"]
|
||||
validations:
|
||||
- expression: "object.spec.replicas <= 5"
|
||||
`),
|
||||
wantKinds: []string{"v1/Pod", "apps/v1/Deployment", "apps/v1/Replicaset", "apps/v1/Daemonset", "apps/v1/Statefulset", "batch/v1/Job", "batch/v1/Cronjob"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_, policy, _ := yamlutils.GetPolicy(tt.policy)
|
||||
kinds := GetKinds(policy[0])
|
||||
if !reflect.DeepEqual(kinds, tt.wantKinds) {
|
||||
t.Errorf("Expected %v, got %v", tt.wantKinds, kinds)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
apiVersion: admissionregistration.k8s.io/v1alpha1
|
||||
kind: ValidatingAdmissionPolicy
|
||||
metadata:
|
||||
name: "demo-policy.example.com"
|
||||
spec:
|
||||
failurePolicy: Fail
|
||||
matchConstraints:
|
||||
resourceRules:
|
||||
- apiGroups: ["apps"]
|
||||
apiVersions: ["v1"]
|
||||
operations: ["CREATE", "UPDATE"]
|
||||
resources: ["deployments"]
|
||||
validations:
|
||||
- expression: "object.spec.template.spec.containers.all(container, !container.image.contains('latest'))"
|
||||
message: "Using a mutable image tag e.g. 'latest' is not allowed."
|
||||
- expression: "object.spec.replicas <= 5"
|
||||
message: "Replicas must be less than or equal 5"
|
||||
matchConditions:
|
||||
- name: "deployment name"
|
||||
expression: "object.metadata.name == 'nginx-deployment'"
|
|
@ -5,7 +5,7 @@ metadata:
|
|||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
replicas: 3
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
|
@ -16,7 +16,4 @@ spec:
|
|||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.14.2
|
||||
ports:
|
||||
- containerPort: 80
|
||||
|
||||
image: nginx:latest
|
|
@ -17,6 +17,3 @@ spec:
|
|||
containers:
|
||||
- name: nginx
|
||||
image: nginx:latest
|
||||
ports:
|
||||
- containerPort: 80
|
||||
|
14
test/cli/test-vap/check-deployments-replica/policy.yaml
Normal file
14
test/cli/test-vap/check-deployments-replica/policy.yaml
Normal file
|
@ -0,0 +1,14 @@
|
|||
apiVersion: admissionregistration.k8s.io/v1alpha1
|
||||
kind: ValidatingAdmissionPolicy
|
||||
metadata:
|
||||
name: "chech-deployment-replicas"
|
||||
spec:
|
||||
matchConstraints:
|
||||
resourceRules:
|
||||
- apiGroups: ["apps"]
|
||||
apiVersions: ["v1"]
|
||||
operations: ["CREATE", "UPDATE"]
|
||||
resources: ["deployments"]
|
||||
validations:
|
||||
- expression: "object.spec.replicas <= 2"
|
||||
message: "Deployment replicas must be less than or equal 2"
|
14
test/cli/test-vap/disallow-host-path/pod1.yaml
Normal file
14
test/cli/test-vap/disallow-host-path/pod1.yaml
Normal file
|
@ -0,0 +1,14 @@
|
|||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: good-pod
|
||||
spec:
|
||||
containers:
|
||||
- name:
|
||||
image: nginx
|
||||
volumeMounts:
|
||||
- name: udev
|
||||
mountPath: /data
|
||||
volumes:
|
||||
- name: udev
|
||||
emptyDir: {}
|
15
test/cli/test-vap/disallow-host-path/pod2.yaml
Normal file
15
test/cli/test-vap/disallow-host-path/pod2.yaml
Normal file
|
@ -0,0 +1,15 @@
|
|||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: bad-pod
|
||||
spec:
|
||||
containers:
|
||||
- name:
|
||||
image: nginx
|
||||
volumeMounts:
|
||||
- name: udev
|
||||
mountPath: /data
|
||||
volumes:
|
||||
- name: udev
|
||||
hostPath:
|
||||
path: /etc/udev
|
14
test/cli/test-vap/disallow-host-path/policy.yaml
Normal file
14
test/cli/test-vap/disallow-host-path/policy.yaml
Normal file
|
@ -0,0 +1,14 @@
|
|||
apiVersion: admissionregistration.k8s.io/v1alpha1
|
||||
kind: ValidatingAdmissionPolicy
|
||||
metadata:
|
||||
name: "disallow-host-path"
|
||||
spec:
|
||||
matchConstraints:
|
||||
resourceRules:
|
||||
- apiGroups: [""]
|
||||
apiVersions: ["v1"]
|
||||
operations: ["CREATE", "UPDATE"]
|
||||
resources: ["pods"]
|
||||
validations:
|
||||
- expression: "!has(object.spec.volumes) || object.spec.volumes.all(volume, !has(volume.hostPath))"
|
||||
message: "HostPath volumes are forbidden. The field spec.volumes[*].hostPath must be unset."
|
Loading…
Reference in a new issue