1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-06 07:57:07 +00:00

Merge pull request #306 from nirmata/285_allow_OR_across_overlay_patterns

285 allow or across overlay patterns
This commit is contained in:
Shivkumar Dudhani 2019-08-21 19:26:22 -07:00 committed by GitHub
commit f41a83fb56
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 153 additions and 53 deletions

View file

@ -145,13 +145,13 @@ spec:
AnyValue: {}
validate:
type: object
required:
- pattern
properties:
message:
type: string
pattern:
AnyValue: {}
anyPattern:
AnyValue: {}
generate:
type: object
required:

View file

@ -145,13 +145,13 @@ spec:
AnyValue: {}
validate:
type: object
required:
- pattern
properties:
message:
type: string
pattern:
AnyValue: {}
anyPattern:
AnyValue: {}
generate:
type: object
required:

View file

@ -101,6 +101,42 @@ spec :
mem)ory: "2048Mi"
````
### Allow OR across overlay pattern
In some cases one content can be defined at a different level. For example, a security context can be defined at the Pod or Container level. The validation rule should pass if one of the conditions is met.
`anyPattern` can be used to check on at least one of condition, it is the array of pattern, and the rule will be passed if at least one pattern is true.
<small>*Note: either `pattern` or `anyPattern` is allowed in each rule, they can't be decalred in the same rule.*</small>
````yaml
apiVersion: kyverno.io/v1alpha1
kind: Policy
metadata:
name: check-container-security-context
spec:
rules:
- name: check-root-user
exclude:
resources:
namespaces:
- kube-system
match:
resources:
kinds:
- Pod
validate:
message: "Root user is not allowed. Set runAsNonRoot to true."
anyPattern:
- spec:
securityContext:
runAsNonRoot: true
- spec:
containers:
- name: "*"
securityContext:
runAsNonRoot: true
````
Additional examples are available in [examples](/examples/)

View file

@ -1,22 +1,27 @@
apiVersion : kyverno.io/v1alpha1
apiVersion: kyverno.io/v1alpha1
kind: Policy
metadata:
name: policy-security-context
name: check-container-security-context
spec:
# validationFailureAction: "audit"
rules:
- name: validate-runAsNonRoot
- name: check-root-user
exclude:
resources:
namespaces:
- kube-system
match:
resources:
kinds:
- Deployment
selector :
matchLabels:
test: psp
- Pod
validate:
message: "security context 'runAsNonRoot' shoud be set to true"
pattern:
spec:
template:
spec:
message: "Root user is not allowed. Set runAsNonRoot to true."
anyPattern:
- spec:
securityContext:
runAsNonRoot: true
- spec:
containers:
- name: "*"
securityContext:
runAsNonRoot: true

View file

@ -1,21 +1,10 @@
apiVersion: apps/v1
kind: Deployment
apiVersion: v1
kind: Pod
metadata:
name: psp-demo-unprivileged
labels:
test: psp
name: sec-ctx-unprivileged
spec:
replicas: 1
selector:
matchLabels:
test: psp
template:
metadata:
labels:
test: psp
spec:
securityContext:
runAsNonRoot: true
containers:
- name: sec-ctx-unprivileged
image: nginxinc/nginx-unprivileged
# securityContext:
# runAsNonRoot: true
containers:
- name: imagen-with-hostpath
image: nginxinc/nginx-unprivileged

View file

@ -68,8 +68,9 @@ type Patch struct {
// Validation describes the way how Validating Webhook will check the resource on creation
type Validation struct {
Message string `json:"message"`
Pattern interface{} `json:"pattern"`
Message string `json:"message"`
Pattern interface{} `json:"pattern"`
AnyPattern []interface{} `json:"anyPattern"`
}
// Generation describes which resources will be created when other resource is created

View file

@ -61,16 +61,7 @@ func Validate(policy kyverno.Policy, resource unstructured.Unstructured) (respon
continue
}
ruleInfo := info.NewRuleInfo(rule.Name, info.Validation)
err := validateResourceWithPattern(resourceInt, rule.Validation.Pattern)
if err != nil {
ruleInfo.Fail()
ruleInfo.Addf("Failed to apply pattern: %v.", err)
} else {
ruleInfo.Add("Pattern succesfully validated")
glog.V(4).Infof("pattern validated succesfully on resource %s/%s", resource.GetNamespace(), resource.GetName())
}
ruleInfo := validatePatterns(resource, rule.Validation, rule.Name)
incrementAppliedRuleCount()
ruleInfos = append(ruleInfos, ruleInfo)
}
@ -78,6 +69,47 @@ func Validate(policy kyverno.Policy, resource unstructured.Unstructured) (respon
return response
}
// validatePatterns validate pattern and anyPattern
func validatePatterns(resource unstructured.Unstructured, validation kyverno.Validation, ruleName string) info.RuleInfo {
var errs []error
ruleInfo := info.NewRuleInfo(ruleName, info.Validation)
if validation.Pattern != nil {
err := validateResourceWithPattern(resource.Object, validation.Pattern)
if err != nil {
ruleInfo.Fail()
ruleInfo.Addf("Failed to apply pattern: %v", err)
} else {
ruleInfo.Add("Pattern succesfully validated")
glog.V(4).Infof("pattern validated succesfully on resource %s/%s", resource.GetNamespace(), resource.GetName())
}
return ruleInfo
}
if validation.AnyPattern != nil {
for _, pattern := range validation.AnyPattern {
if err := validateResourceWithPattern(resource.Object, pattern); err != nil {
errs = append(errs, err)
}
}
failedPattern := len(errs)
patterns := len(validation.AnyPattern)
// all pattern fail
if failedPattern == patterns {
ruleInfo.Fail()
ruleInfo.Addf("None of anyPattern succeed: %v", errs)
} else {
ruleInfo.Addf("%d/%d patterns succesfully validated", patterns-failedPattern, patterns)
glog.V(4).Infof("%d/%d patterns validated succesfully on resource %s/%s", patterns-failedPattern, patterns, resource.GetNamespace(), resource.GetName())
}
return ruleInfo
}
return info.RuleInfo{}
}
// validateResourceWithPattern is a start of element-by-element validation process
// It assumes that validation is started from root, so "/" is passed
func validateResourceWithPattern(resource, pattern interface{}) error {

View file

@ -3,6 +3,7 @@ package webhooks
import (
"encoding/json"
"fmt"
"reflect"
"github.com/golang/glog"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
@ -25,7 +26,10 @@ func (ws *WebhookServer) HandlePolicyValidation(request *v1beta1.AdmissionReques
raw := request.Object.Raw
if err := json.Unmarshal(raw, &policy); err != nil {
glog.Errorf("Failed to unmarshal policy admission request, err %v\n", err)
return &v1beta1.AdmissionResponse{Allowed: false}
return &v1beta1.AdmissionResponse{Allowed: false,
Result: &metav1.Status{
Message: fmt.Sprintf("Failed to unmarshal policy admission request err %v", err),
}}
}
// check for uniqueness of rule names while CREATE/DELET
admissionResp = ws.validateUniqueRuleName(policy)
@ -37,12 +41,45 @@ func (ws *WebhookServer) HandlePolicyValidation(request *v1beta1.AdmissionReques
return admissionResp
}
func (ws *WebhookServer) validatePolicy(policy *kyverno.Policy) *v1beta1.AdmissionResponse {
admissionResp := ws.validateUniqueRuleName(policy)
if !admissionResp.Allowed {
return admissionResp
}
return ws.validateOverlayPattern(policy)
}
func (ws *WebhookServer) validateOverlayPattern(policy *kyverno.Policy) *v1beta1.AdmissionResponse {
for _, rule := range policy.Spec.Rules {
if reflect.DeepEqual(rule.Validation, kyverno.Validation{}) {
continue
}
if rule.Validation.Pattern == nil && len(rule.Validation.AnyPattern) == 0 {
return &v1beta1.AdmissionResponse{
Allowed: false,
Result: &metav1.Status{
Message: fmt.Sprintf("Invalid policy, neither pattern nor anyPattern found in validate rule %s", rule.Name),
},
}
}
if rule.Validation.Pattern != nil && len(rule.Validation.AnyPattern) != 0 {
return &v1beta1.AdmissionResponse{
Allowed: false,
Result: &metav1.Status{
Message: fmt.Sprintf("Invalid policy, either pattern or anyPattern is allowed in validate rule %s", rule.Name),
},
}
}
}
return &v1beta1.AdmissionResponse{Allowed: true}
}
// Verify if the Rule names are unique within a policy
func (ws *WebhookServer) validateUniqueRuleName(policy *kyverno.Policy) *v1beta1.AdmissionResponse {
// =======
// func (ws *WebhookServer) validateUniqueRuleName(rawPolicy []byte) *v1beta1.AdmissionResponse {
// var policy *kyverno.Policy
// >>>>>>> policyViolation
var ruleNames []string
for _, rule := range policy.Spec.Rules {
@ -60,7 +97,7 @@ func (ws *WebhookServer) validateUniqueRuleName(policy *kyverno.Policy) *v1beta1
ruleNames = append(ruleNames, rule.Name)
}
glog.V(3).Infof("Policy validation passed")
glog.V(4).Infof("Policy validation passed")
return &v1beta1.AdmissionResponse{
Allowed: true,
}