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:
commit
f41a83fb56
8 changed files with 153 additions and 53 deletions
|
@ -145,13 +145,13 @@ spec:
|
|||
AnyValue: {}
|
||||
validate:
|
||||
type: object
|
||||
required:
|
||||
- pattern
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
pattern:
|
||||
AnyValue: {}
|
||||
anyPattern:
|
||||
AnyValue: {}
|
||||
generate:
|
||||
type: object
|
||||
required:
|
||||
|
|
|
@ -145,13 +145,13 @@ spec:
|
|||
AnyValue: {}
|
||||
validate:
|
||||
type: object
|
||||
required:
|
||||
- pattern
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
pattern:
|
||||
AnyValue: {}
|
||||
anyPattern:
|
||||
AnyValue: {}
|
||||
generate:
|
||||
type: object
|
||||
required:
|
||||
|
|
|
@ -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/)
|
||||
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue