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

generate violation in mutation when substitute path not present

This commit is contained in:
Shuting Zhao 2020-01-09 12:24:37 -08:00
parent 731fdb3e07
commit f78ca61859
7 changed files with 169 additions and 7 deletions

View file

@ -22,7 +22,7 @@ import (
"github.com/nirmata/kyverno/pkg/engine/variables"
)
// processOverlay processes validation patterns on the resource
// processOverlay processes mutation overlay on the resource
func ProcessOverlay(ctx context.EvalInterface, rule kyverno.Rule, resource unstructured.Unstructured) (resp response.RuleResponse, patchedResource unstructured.Unstructured) {
startTime := time.Now()
glog.V(4).Infof("started applying overlay rule %q (%v)", rule.Name, startTime)
@ -32,6 +32,16 @@ func ProcessOverlay(ctx context.EvalInterface, rule kyverno.Rule, resource unstr
resp.RuleStats.ProcessingTime = time.Since(startTime)
glog.V(4).Infof("finished applying overlay rule %q (%v)", resp.Name, resp.RuleStats.ProcessingTime)
}()
// if referenced is not present, we skip processing the rule and report violation
if err := variables.ValidateVariables(ctx, rule.Mutation.Overlay); err != nil {
glog.V(3).Infof("Skip applying rule '%s' on resource '%s/%s/%s': %s", rule.Name, resource.GetKind(), resource.GetNamespace(), resource.GetName(), err.Error())
resp.Success = true
resp.PathNotPresent = true
resp.Message = err.Error()
return resp, resource
}
// substitute variables
// first pass we substitute all the JMESPATH substitution for the variable
// variable: {{<JMESPATH>}}

View file

@ -82,11 +82,20 @@ func Mutate(policyContext PolicyContext) (resp response.EngineResponse) {
if rule.Mutation.Overlay != nil {
var ruleResponse response.RuleResponse
ruleResponse, patchedResource = mutate.ProcessOverlay(ctx, rule, patchedResource)
if ruleResponse.Success == true && ruleResponse.Patches == nil {
// overlay pattern does not match the resource conditions
glog.V(4).Infof(ruleResponse.Message)
continue
} else if ruleResponse.Success == true {
if ruleResponse.Success == true {
// - variable substitution path is not present
if ruleResponse.PathNotPresent {
glog.V(4).Infof(ruleResponse.Message)
resp.PolicyResponse.Rules = append(resp.PolicyResponse.Rules, ruleResponse)
continue
}
// - overlay pattern does not match the resource conditions
if ruleResponse.Patches == nil {
glog.V(4).Infof(ruleResponse.Message)
continue
}
glog.Infof("Mutate overlay in rule '%s' successfully applied on %s/%s/%s", rule.Name, resource.GetKind(), resource.GetNamespace(), resource.GetName())
}

View file

@ -88,3 +88,68 @@ func Test_VariableSubstitutionOverlay(t *testing.T) {
t.Error("patches dont match")
}
}
func Test_variableSubstitutionPathNotExist(t *testing.T) {
resourceRaw := []byte(`{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"name": "check-root-user"
},
"spec": {
"containers": [
{
"name": "check-root-user",
"image": "nginxinc/nginx-unprivileged",
"securityContext": {
"runAsNonRoot": true
}
}
]
}
}`)
policyraw := []byte(`{
"apiVersion": "kyverno.io/v1",
"kind": "ClusterPolicy",
"metadata": {
"name": "substitue-variable"
},
"spec": {
"rules": [
{
"name": "test-path-not-exist",
"match": {
"resources": {
"kinds": [
"Pod"
]
}
},
"mutate": {
"overlay": {
"spec": {
"name": "{{request.object.metadata.name1}}"
}
}
}
}
]
}
}`)
var policy kyverno.ClusterPolicy
json.Unmarshal(policyraw, &policy)
resourceUnstructured, err := utils.ConvertToUnstructured(resourceRaw)
assert.NilError(t, err)
ctx := context.NewContext()
ctx.AddResource(resourceRaw)
policyContext := PolicyContext{
Policy: policy,
Context: ctx,
NewResource: *resourceUnstructured}
er := Mutate(policyContext)
assert.Assert(t, er.PolicyResponse.Rules[0].PathNotPresent, true)
}

View file

@ -65,6 +65,8 @@ type RuleResponse struct {
Success bool `json:"success"`
// statistics
RuleStats `json:",inline"`
// PathNotPresent indicates whether referenced path in variable substitution exist
PathNotPresent bool `json:"pathNotPresent"`
}
//ToString ...
@ -119,3 +121,13 @@ func (er EngineResponse) getRules(success bool) []string {
}
return rules
}
// IsPathNotPresent checks if the referenced path(in variable substitution) exist
func (er EngineResponse) IsPathNotPresent() bool {
for _, r := range er.PolicyResponse.Rules {
if r.PathNotPresent {
return true
}
}
return false
}

View file

@ -15,7 +15,8 @@ func GeneratePVsFromEngineResponse(ers []response.EngineResponse) (pvInfos []Inf
glog.V(4).Infof("resource %v, has not been assigned a name, not creating a policy violation for it", er.PolicyResponse.Resource)
continue
}
if er.IsSuccesful() {
// skip when response succeed AND referenced paths exist
if er.IsSuccesful() && !er.IsPathNotPresent() {
continue
}
glog.V(4).Infof("Building policy violation for engine response %v", er)

View file

@ -0,0 +1,60 @@
package policyviolation
import (
"testing"
"github.com/nirmata/kyverno/pkg/engine/response"
"gotest.tools/assert"
)
func Test_GeneratePVsFromEngineResponse_PathNotExist(t *testing.T) {
ers := []response.EngineResponse{
response.EngineResponse{
PolicyResponse: response.PolicyResponse{
Policy: "test-substitue-variable",
Resource: response.ResourceSpec{
Kind: "Pod",
Name: "test",
Namespace: "test",
},
Rules: []response.RuleResponse{
response.RuleResponse{
Name: "test-path-not-exist",
Type: "Mutation",
Message: "referenced paths are not present: request.object.metadata.name1",
Success: true,
PathNotPresent: true,
},
response.RuleResponse{
Name: "test-path-exist",
Type: "Mutation",
Success: true,
PathNotPresent: false,
},
},
},
},
response.EngineResponse{
PolicyResponse: response.PolicyResponse{
Policy: "test-substitue-variable2",
Resource: response.ResourceSpec{
Kind: "Pod",
Name: "test",
Namespace: "test",
},
Rules: []response.RuleResponse{
response.RuleResponse{
Name: "test-path-not-exist-accross-policy",
Type: "Mutation",
Message: "referenced paths are not present: request.object.metadata.name1",
Success: true,
PathNotPresent: true,
},
},
},
},
}
pvInfos := GeneratePVsFromEngineResponse(ers)
assert.Assert(t, len(pvInfos) == 2)
}

View file

@ -8,6 +8,7 @@ import (
"github.com/nirmata/kyverno/pkg/engine/response"
engineutils "github.com/nirmata/kyverno/pkg/engine/utils"
policyctr "github.com/nirmata/kyverno/pkg/policy"
"github.com/nirmata/kyverno/pkg/policyviolation"
"github.com/nirmata/kyverno/pkg/utils"
v1beta1 "k8s.io/api/admission/v1beta1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@ -96,6 +97,10 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest, resou
patches = append(patches, annPatches)
}
// generate violation when referenced path does not exist
pvInfos := policyviolation.GeneratePVsFromEngineResponse(engineResponses)
ws.pvGenerator.Add(pvInfos...)
// ADD EVENTS
events := generateEvents(engineResponses, (request.Operation == v1beta1.Update))
ws.eventGen.Add(events...)