diff --git a/pkg/engine/mutation.go b/pkg/engine/mutation.go index 98a63eb268..625b0b2371 100644 --- a/pkg/engine/mutation.go +++ b/pkg/engine/mutation.go @@ -1,8 +1,10 @@ package engine import ( + "encoding/json" "fmt" "reflect" + "regexp" "strings" "time" @@ -170,8 +172,12 @@ func ForceMutate(ctx context.EvalInterface, policy kyverno.ClusterPolicy, resour if mutation.Overlay != nil { overlay := mutation.Overlay - if overlay, err = variables.SubstituteVars(ctx, overlay); err != nil { - return unstructured.Unstructured{}, err + if ctx != nil { + if overlay, err = variables.SubstituteVars(ctx, overlay); err != nil { + return unstructured.Unstructured{}, err + } + } else { + overlay = replaceSubstituteVariables(overlay) } resource, err = mutateResourceWithOverlay(resource, overlay) @@ -192,6 +198,30 @@ func ForceMutate(ctx context.EvalInterface, policy kyverno.ClusterPolicy, resour return resource, nil } +func replaceSubstituteVariables(overlay interface{}) interface{} { + overlayRaw, err := json.Marshal(overlay) + if err != nil { + return overlay + } + + regex := regexp.MustCompile(`\{\{([^{}]*)\}\}`) + for { + if len(regex.FindAllStringSubmatch(string(overlayRaw), -1)) > 0 { + overlayRaw = regex.ReplaceAll(overlayRaw, []byte(`placeholderValue`)) + } else { + break + } + } + + var output interface{} + err = json.Unmarshal(overlayRaw, &output) + if err != nil { + return overlay + } + + return output +} + func incrementAppliedRuleCount(resp *response.EngineResponse) { resp.PolicyResponse.RulesAppliedCount++ } diff --git a/pkg/openapi/validation.go b/pkg/openapi/validation.go index c8fc9084b5..5f89d8499a 100644 --- a/pkg/openapi/validation.go +++ b/pkg/openapi/validation.go @@ -11,7 +11,6 @@ import ( "github.com/golang/glog" "github.com/nirmata/kyverno/pkg/engine" - "github.com/nirmata/kyverno/pkg/engine/context" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1" @@ -76,13 +75,7 @@ func ValidatePolicyMutation(policy v1.ClusterPolicy) error { newResource := unstructured.Unstructured{Object: resource} newResource.SetKind(kind) - ctx := context.NewContext() - err := ctx.AddSA("kyvernoDummyUsername") - if err != nil { - glog.V(4).Infof("Failed to load service account in context:%v", err) - } - - patchedResource, err := engine.ForceMutate(ctx, *newPolicy.DeepCopy(), newResource) + patchedResource, err := engine.ForceMutate(nil, *newPolicy.DeepCopy(), newResource) if err != nil { return err } diff --git a/pkg/openapi/validation_test.go b/pkg/openapi/validation_test.go index e534478908..813c2ed15b 100644 --- a/pkg/openapi/validation_test.go +++ b/pkg/openapi/validation_test.go @@ -42,8 +42,8 @@ func Test_ValidateMutationPolicy(t *testing.T) { errMessage: `ValidationError(io.k8s.api.core.v1.Pod.spec.automountServiceAccountToken): invalid type for io.k8s.api.core.v1.PodSpec.automountServiceAccountToken: got "integer", expected "boolean"`, }, { - description: "Testing Policies with substitute variables", - policy: []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"add-ns-access-controls","annotations":{"policies.kyverno.io/category":"Workload Isolation","policies.kyverno.io/description":"Create roles and role bindings for a new namespace"}},"spec":{"background":false,"rules":[{"name":"add-sa-annotation","match":{"resources":{"kinds":["Namespace"]}},"mutate":{"overlay":{"metadata":{"annotations":{"nirmata.io/ns-creator":"{{serviceAccountName}}"}}}}},{"name":"generate-owner-role","match":{"resources":{"kinds":["Namespace"]}},"preconditions":[{"key":"{{request.userInfo.username}}","operator":"NotEqual","value":""},{"key":"{{serviceAccountName}}","operator":"NotEqual","value":""},{"key":"{{serviceAccountNamespace}}","operator":"NotEqual","value":""}],"generate":{"kind":"ClusterRole","name":"ns-owner-{{request.object.metadata.name}}-{{request.userInfo.username}}","data":{"metadata":{"annotations":{"nirmata.io/ns-creator":"{{serviceAccountName}}"}},"rules":[{"apiGroups":[""],"resources":["namespaces"],"verbs":["delete"],"resourceNames":["{{request.object.metadata.name}}"]}]}}},{"name":"generate-owner-role-binding","match":{"resources":{"kinds":["Namespace"]}},"preconditions":[{"key":"{{request.userInfo.username}}","operator":"NotEqual","value":""},{"key":"{{serviceAccountName}}","operator":"NotEqual","value":""},{"key":"{{serviceAccountNamespace}}","operator":"NotEqual","value":""}],"generate":{"kind":"ClusterRoleBinding","name":"ns-owner-{{request.object.metadata.name}}-{{request.userInfo.username}}-binding","data":{"metadata":{"annotations":{"nirmata.io/ns-creator":"{{serviceAccountName}}"}},"roleRef":{"apiGroup":"rbac.authorization.k8s.io","kind":"ClusterRole","name":"ns-owner-{{request.object.metadata.name}}-{{request.userInfo.username}}"},"subjects":[{"kind":"ServiceAccount","name":"{{serviceAccountName}}","namespace":"{{serviceAccountNamespace}}"}]}}},{"name":"generate-admin-role-binding","match":{"resources":{"kinds":["Namespace"]}},"preconditions":[{"key":"{{request.userInfo.username}}","operator":"NotEqual","value":""},{"key":"{{serviceAccountName}}","operator":"NotEqual","value":""},{"key":"{{serviceAccountNamespace}}","operator":"NotEqual","value":""}],"generate":{"kind":"RoleBinding","name":"ns-admin-{{request.object.metadata.name}}-{{request.userInfo.username}}-binding","namespace":"{{request.object.metadata.name}}","data":{"metadata":{"annotations":{"nirmata.io/ns-creator":"{{serviceAccountName}}"}},"roleRef":{"apiGroup":"rbac.authorization.k8s.io","kind":"ClusterRole","name":"admin"},"subjects":[{"kind":"ServiceAccount","name":"{{serviceAccountName}}","namespace":"{{serviceAccountNamespace}}"}]}}}]}}`), + description: "Dealing with nested variables", + policy: []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"add-ns-access-controls","annotations":{"policies.kyverno.io/category":"Workload Isolation","policies.kyverno.io/description":"Create roles and role bindings for a new namespace"}},"spec":{"background":false,"rules":[{"name":"add-sa-annotation","match":{"resources":{"kinds":["Namespace"]}},"mutate":{"overlay":{"metadata":{"annotations":{"nirmata.io/ns-creator":"{{serviceAccountName-{{something}}}}"}}}}},{"name":"generate-owner-role","match":{"resources":{"kinds":["Namespace"]}},"preconditions":[{"key":"{{request.userInfo.username}}","operator":"NotEqual","value":""},{"key":"{{serviceAccountName}}","operator":"NotEqual","value":""},{"key":"{{serviceAccountNamespace}}","operator":"NotEqual","value":""}],"generate":{"kind":"ClusterRole","name":"ns-owner-{{request.object.metadata.name{{something}}}}-{{request.userInfo.username}}","data":{"metadata":{"annotations":{"nirmata.io/ns-creator":"{{serviceAccountName}}"}},"rules":[{"apiGroups":[""],"resources":["namespaces"],"verbs":["delete"],"resourceNames":["{{request.object.metadata.name}}"]}]}}},{"name":"generate-owner-role-binding","match":{"resources":{"kinds":["Namespace"]}},"preconditions":[{"key":"{{request.userInfo.username}}","operator":"NotEqual","value":""},{"key":"{{serviceAccountName}}","operator":"NotEqual","value":""},{"key":"{{serviceAccountNamespace}}","operator":"NotEqual","value":""}],"generate":{"kind":"ClusterRoleBinding","name":"ns-owner-{{request.object.metadata.name}}-{{request.userInfo.username}}-binding","data":{"metadata":{"annotations":{"nirmata.io/ns-creator":"{{serviceAccountName}}"}},"roleRef":{"apiGroup":"rbac.authorization.k8s.io","kind":"ClusterRole","name":"ns-owner-{{request.object.metadata.name}}-{{request.userInfo.username}}"},"subjects":[{"kind":"ServiceAccount","name":"{{serviceAccountName}}","namespace":"{{serviceAccountNamespace}}"}]}}},{"name":"generate-admin-role-binding","match":{"resources":{"kinds":["Namespace"]}},"preconditions":[{"key":"{{request.userInfo.username}}","operator":"NotEqual","value":""},{"key":"{{serviceAccountName}}","operator":"NotEqual","value":""},{"key":"{{serviceAccountNamespace}}","operator":"NotEqual","value":""}],"generate":{"kind":"RoleBinding","name":"ns-admin-{{request.object.metadata.name}}-{{request.userInfo.username}}-binding","namespace":"{{request.object.metadata.name}}","data":{"metadata":{"annotations":{"nirmata.io/ns-creator":"{{serviceAccountName}}"}},"roleRef":{"apiGroup":"rbac.authorization.k8s.io","kind":"ClusterRole","name":"admin"},"subjects":[{"kind":"ServiceAccount","name":"{{serviceAccountName}}","namespace":"{{serviceAccountNamespace}}"}]}}}]}}`), }, }