diff --git a/pkg/apis/policy/v1alpha1/types_validation.go b/pkg/apis/policy/v1alpha1/types_validation.go index 1330d3293f..32872f90bf 100644 --- a/pkg/apis/policy/v1alpha1/types_validation.go +++ b/pkg/apis/policy/v1alpha1/types_validation.go @@ -67,8 +67,7 @@ func (pr *PolicyResource) Validate() error { } if pr.Name != nil { - // // make non-regexp a regexp to match exactly to the given name - // *pr.Name = "^" + *pr.Name + "$" + // make non-regexp a regexp to match exactly to the given name if _, err := regexp.Compile(*pr.Name); err != nil { return fmt.Errorf("invalied regex, err: %v", err) } diff --git a/webhooks/admission.go b/webhooks/admission.go index 6fcdcb4929..cf146e7ce1 100644 --- a/webhooks/admission.go +++ b/webhooks/admission.go @@ -1,7 +1,6 @@ package webhooks import ( - "fmt" "regexp" types "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1" @@ -64,7 +63,6 @@ func IsRuleApplicableToResource(kind string, resourceRaw []byte, policyResource if policyResource.Name != nil { policyResourceName, isRegex := parseRegexPolicyResourceName(*policyResource.Name) - fmt.Println("policyResourceName, name, isRegex", policyResourceName, name, isRegex) // if no regex used, check if names are matched, return directly if !isRegex && policyResourceName != name { diff --git a/webhooks/mutation.go b/webhooks/mutation.go index 722bfc4799..b787874f3f 100644 --- a/webhooks/mutation.go +++ b/webhooks/mutation.go @@ -6,6 +6,8 @@ import ( "log" "os" + "github.com/nirmata/kube-policy/pkg/violation" + controller "github.com/nirmata/kube-policy/controller" kubeclient "github.com/nirmata/kube-policy/kubeclient" types "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1" @@ -104,59 +106,88 @@ func getPolicyPatchingSets(policy types.Policy) PatchingSets { // Applies all policy rules to the created object and returns list of processed JSON patches. // May return nil patches if it is not necessary to create patches for requested object. // Returns error ONLY in case when creation of resource should be denied. -func (mw *MutationWebhook) applyPolicyRules(request *v1beta1.AdmissionRequest, policy types.Policy) ([]PatchBytes, int, error) { +func (mw *MutationWebhook) applyPolicyRules(request *v1beta1.AdmissionRequest, policy types.Policy) ([]PatchBytes, []violation.Info, error) { return mw.applyPolicyRulesOnResource(request.Kind.Kind, request.Object.Raw, policy) } // TODO: add another violation field in return elements // kind is the type of object being manipulated -func (mw *MutationWebhook) applyPolicyRulesOnResource(kind string, rawResource []byte, policy types.Policy) ([]PatchBytes, int, error) { +func (mw *MutationWebhook) applyPolicyRulesOnResource(kind string, rawResource []byte, policy types.Policy) ([]PatchBytes, []violation.Info, error) { patchingSets := getPolicyPatchingSets(policy) var policyPatches []PatchBytes - violationCount := 0 + var violations []violation.Info + + meta := parseMetadataFromObject(rawResource) + resourceKind := parseKindFromObject(rawResource) + resourceName := parseNameFromMetadata(meta) + ns := parseNamespaceFromMetadata(meta) for ruleIdx, rule := range policy.Spec.Rules { err := rule.Validate() if err != nil { mw.logger.Printf("Invalid rule detected: #%d in policy %s, err: %v\n", ruleIdx, policy.ObjectMeta.Name, err) - violationCount++ continue } if ok, err := IsRuleApplicableToResource(kind, rawResource, rule.Resource); !ok { - mw.logger.Printf("Rule %d of policy %s does not match the request", ruleIdx, policy.Name) - violationCount++ - return nil, violationCount, err + mw.logger.Printf("Rule %d of policy %s is not applicable to the request", ruleIdx, policy.Name) + return nil, nil, err } // configMapGenerator and secretGenerator can be applied only to namespaces if kind == "Namespace" { err = mw.applyRuleGenerators(rawResource, rule) - if err != nil && patchingSets == PatchingSetsStopOnError { - violationCount++ - return nil, violationCount, fmt.Errorf("Failed to apply generators from rule #%d: %s", ruleIdx, err) + if err != nil { + violations = append(violations, violation.Info{ + Kind: resourceKind, + Resource: ns + "/" + resourceName, + Policy: policy.Name, + RuleName: string(ruleIdx), + Reason: err.Error(), + }) + + if patchingSets == PatchingSetsStopOnError { + return nil, violations, fmt.Errorf("Failed to apply generators from rule #%d: %s", ruleIdx, err) + } } } + rulePatchesProcessed, err := ProcessPatches(rule.Patches, rawResource, patchingSets) if err != nil { - violationCount++ - return nil, violationCount, fmt.Errorf("Failed to process patches from rule #%d: %s", ruleIdx, err) + violations = append(violations, violation.Info{ + Kind: resourceKind, + Resource: ns + "/" + resourceName, + Policy: policy.Name, + RuleName: string(ruleIdx), + Reason: err.Error(), + }) + return nil, violations, fmt.Errorf("Failed to process patches from rule #%d: %s", ruleIdx, err) } if rulePatchesProcessed != nil { policyPatches = append(policyPatches, rulePatchesProcessed...) mw.logger.Printf("Rule %d: prepared %d patches", ruleIdx, len(rulePatchesProcessed)) + + if len(rulePatchesProcessed) < len(rule.Patches) { + violations = append(violations, violation.Info{ + Kind: resourceKind, + Resource: ns + "/" + resourceName, + Policy: policy.Name, + RuleName: string(ruleIdx), + Reason: fmt.Sprintf("%v out of %v patches prepared", len(rulePatchesProcessed), len(rule.Patches)), + }) + } } else { mw.logger.Printf("Rule %d: no patches prepared", ruleIdx) } } - // if no rules are validate, return error to deny resource creation + // empty patch, return error to deny resource creation if policyPatches == nil { - return nil, violationCount, fmt.Errorf("no patches prepared, violations: %v", violationCount) + return nil, nil, fmt.Errorf("no patches prepared") } - return policyPatches, violationCount, nil + return policyPatches, violations, nil } // Applies "configMapGenerator" and "secretGenerator" described in PolicyRule diff --git a/webhooks/utils.go b/webhooks/utils.go index 42a463681f..fcca09f150 100644 --- a/webhooks/utils.go +++ b/webhooks/utils.go @@ -14,6 +14,13 @@ func parseMetadataFromObject(bytes []byte) map[string]interface{} { return objectJSON["metadata"].(map[string]interface{}) } +func parseKindFromObject(bytes []byte) string { + var objectJSON map[string]interface{} + json.Unmarshal(bytes, &objectJSON) + + return objectJSON["kind"].(string) +} + func parseLabelsFromMetadata(meta map[string]interface{}) labels.Set { if interfaceMap, ok := meta["labels"].(map[string]interface{}); ok { labelMap := make(labels.Set, len(interfaceMap))