2025-01-17 17:16:34 +02:00
|
|
|
package admissionpolicy
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
celutils "github.com/kyverno/kyverno/pkg/cel/utils"
|
|
|
|
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
|
|
|
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
|
|
|
|
admissionregistrationv1alpha1 "k8s.io/api/admissionregistration/v1alpha1"
|
|
|
|
corev1 "k8s.io/api/core/v1"
|
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
|
|
"k8s.io/apimachinery/pkg/util/managedfields"
|
|
|
|
"k8s.io/apiserver/pkg/admission"
|
|
|
|
"k8s.io/apiserver/pkg/admission/plugin/cel"
|
|
|
|
"k8s.io/apiserver/pkg/admission/plugin/policy/mutating/patch"
|
|
|
|
"k8s.io/apiserver/pkg/admission/plugin/webhook/matchconditions"
|
|
|
|
celconfig "k8s.io/apiserver/pkg/apis/cel"
|
|
|
|
)
|
|
|
|
|
|
|
|
func mutateResource(
|
|
|
|
policy admissionregistrationv1alpha1.MutatingAdmissionPolicy,
|
|
|
|
resource unstructured.Unstructured,
|
|
|
|
) (engineapi.EngineResponse, error) {
|
|
|
|
startTime := time.Now()
|
|
|
|
|
2025-01-22 17:32:30 +01:00
|
|
|
engineResponse := engineapi.NewEngineResponse(resource, engineapi.NewMutatingAdmissionPolicy(&policy), nil)
|
2025-01-17 17:16:34 +02:00
|
|
|
policyResp := engineapi.NewPolicyResponse()
|
|
|
|
|
|
|
|
gvk := resource.GroupVersionKind()
|
|
|
|
gvr := schema.GroupVersionResource{
|
|
|
|
Group: gvk.Group,
|
|
|
|
Version: gvk.Version,
|
|
|
|
Resource: strings.ToLower(gvk.Kind) + "s",
|
|
|
|
}
|
|
|
|
|
|
|
|
var namespace *corev1.Namespace
|
|
|
|
namespaceName := resource.GetNamespace()
|
|
|
|
// Special case, the namespace object has the namespace of itself.
|
|
|
|
// unset it if the incoming object is a namespace
|
|
|
|
if gvk.Kind == "Namespace" && gvk.Version == "v1" && gvk.Group == "" {
|
|
|
|
namespaceName = ""
|
|
|
|
}
|
|
|
|
|
|
|
|
if namespaceName != "" {
|
|
|
|
namespace = &corev1.Namespace{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: namespaceName,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
a := admission.NewAttributesRecord(resource.DeepCopyObject(), nil, resource.GroupVersionKind(), resource.GetNamespace(), resource.GetName(), gvr, "", admission.Create, nil, false, nil)
|
|
|
|
versionedAttributes, _ := admission.NewVersionedAttributes(a, a.GetKind(), nil)
|
|
|
|
o := admission.NewObjectInterfacesFromScheme(runtime.NewScheme())
|
|
|
|
|
|
|
|
matchConditions := make([]admissionregistrationv1.MatchCondition, 0, len(policy.Spec.MatchConditions))
|
|
|
|
for _, m := range policy.Spec.MatchConditions {
|
|
|
|
matchConditions = append(matchConditions, admissionregistrationv1.MatchCondition(m))
|
|
|
|
}
|
2025-01-31 16:21:43 +02:00
|
|
|
variables := make([]admissionregistrationv1.Variable, 0, len(policy.Spec.Variables))
|
2025-01-17 17:16:34 +02:00
|
|
|
for _, v := range policy.Spec.Variables {
|
2025-01-31 16:21:43 +02:00
|
|
|
variables = append(variables, admissionregistrationv1.Variable(v))
|
2025-01-17 17:16:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// create compiler
|
|
|
|
compiler, err := NewCompiler(matchConditions, variables)
|
|
|
|
if err != nil {
|
|
|
|
return engineResponse, err
|
|
|
|
}
|
|
|
|
compiler.WithMutations(policy.Spec.Mutations)
|
|
|
|
|
|
|
|
optionalVars := cel.OptionalVariableDeclarations{
|
|
|
|
HasParams: false,
|
|
|
|
HasAuthorizer: false,
|
|
|
|
HasPatchTypes: true,
|
|
|
|
StrictCost: true,
|
|
|
|
}
|
|
|
|
// compile variables
|
|
|
|
compiler.CompileVariables(optionalVars)
|
|
|
|
|
|
|
|
var failPolicy admissionregistrationv1.FailurePolicyType
|
|
|
|
if policy.Spec.FailurePolicy == nil {
|
|
|
|
failPolicy = admissionregistrationv1.Fail
|
|
|
|
} else {
|
|
|
|
failPolicy = admissionregistrationv1.FailurePolicyType(*policy.Spec.FailurePolicy)
|
|
|
|
}
|
|
|
|
|
|
|
|
// compile matchers
|
|
|
|
matcher := matchconditions.NewMatcher(compiler.CompileMatchConditions(optionalVars), &failPolicy, "policy", "mutate", policy.Name)
|
|
|
|
if matcher != nil {
|
|
|
|
matchResults := matcher.Match(context.TODO(), versionedAttributes, nil, nil)
|
|
|
|
|
|
|
|
// if preconditions are not met, then skip mutations
|
|
|
|
if !matchResults.Matches {
|
|
|
|
return engineResponse, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// compile mutations
|
|
|
|
patchers := compiler.CompileMutations(optionalVars)
|
|
|
|
// apply mutations
|
|
|
|
for _, patcher := range patchers {
|
|
|
|
patchRequest := patch.Request{
|
|
|
|
MatchedResource: gvr,
|
|
|
|
VersionedAttributes: versionedAttributes,
|
|
|
|
ObjectInterfaces: o,
|
|
|
|
OptionalVariables: cel.OptionalVariableBindings{VersionedParams: nil, Authorizer: nil},
|
|
|
|
Namespace: namespace,
|
|
|
|
TypeConverter: managedfields.NewDeducedTypeConverter(),
|
|
|
|
}
|
|
|
|
newVersionedObject, err := patcher.Patch(context.TODO(), patchRequest, celconfig.RuntimeCELCostBudget)
|
|
|
|
if err != nil {
|
|
|
|
return engineResponse, nil
|
|
|
|
}
|
|
|
|
versionedAttributes.Dirty = true
|
|
|
|
versionedAttributes.VersionedObject = newVersionedObject
|
|
|
|
}
|
|
|
|
patchedResource, err := celutils.ConvertObjectToUnstructured(versionedAttributes.VersionedObject)
|
|
|
|
if err != nil {
|
|
|
|
return engineResponse, err
|
|
|
|
}
|
|
|
|
|
|
|
|
ruleResp := engineapi.RulePass(policy.GetName(), engineapi.Mutation, "", nil)
|
|
|
|
policyResp.Add(engineapi.NewExecutionStats(startTime, time.Now()), *ruleResp)
|
|
|
|
engineResponse = engineResponse.
|
|
|
|
WithPatchedResource(*patchedResource).
|
|
|
|
WithPolicyResponse(policyResp)
|
|
|
|
|
|
|
|
return engineResponse, nil
|
|
|
|
}
|