2019-08-13 11:32:12 -07:00
|
|
|
package policy
|
|
|
|
|
|
|
|
import (
|
2022-12-09 14:45:11 +01:00
|
|
|
"context"
|
2019-08-26 13:34:42 -07:00
|
|
|
"fmt"
|
|
|
|
"reflect"
|
2019-12-26 11:50:41 -08:00
|
|
|
"strings"
|
2019-08-13 11:32:12 -07:00
|
|
|
"time"
|
|
|
|
|
2021-02-25 15:21:55 -08:00
|
|
|
jsonpatch "github.com/evanphx/json-patch/v5"
|
2020-03-17 11:05:20 -07:00
|
|
|
"github.com/go-logr/logr"
|
2022-05-17 13:12:43 +02:00
|
|
|
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
2022-08-31 14:03:47 +08:00
|
|
|
"github.com/kyverno/kyverno/pkg/clients/dclient"
|
2023-01-02 18:14:40 +01:00
|
|
|
"github.com/kyverno/kyverno/pkg/config"
|
2020-10-07 11:12:31 -07:00
|
|
|
"github.com/kyverno/kyverno/pkg/engine"
|
2022-12-09 14:45:11 +01:00
|
|
|
enginecontext "github.com/kyverno/kyverno/pkg/engine/context"
|
2022-12-16 18:26:48 +08:00
|
|
|
"github.com/kyverno/kyverno/pkg/engine/context/resolvers"
|
2020-10-07 11:12:31 -07:00
|
|
|
"github.com/kyverno/kyverno/pkg/engine/response"
|
2022-12-21 17:44:02 +01:00
|
|
|
"github.com/kyverno/kyverno/pkg/logging"
|
2022-12-07 16:08:37 +01:00
|
|
|
"github.com/kyverno/kyverno/pkg/registryclient"
|
2022-03-31 18:34:52 +02:00
|
|
|
jsonutils "github.com/kyverno/kyverno/pkg/utils/json"
|
2019-08-13 11:32:12 -07:00
|
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
|
|
)
|
|
|
|
|
|
|
|
// applyPolicy applies policy on a resource
|
2022-12-07 16:08:37 +01:00
|
|
|
func applyPolicy(
|
|
|
|
policy kyvernov1.PolicyInterface,
|
|
|
|
resource unstructured.Unstructured,
|
|
|
|
logger logr.Logger,
|
|
|
|
excludeGroupRole []string,
|
|
|
|
client dclient.Interface,
|
|
|
|
rclient registryclient.Client,
|
2022-12-16 18:26:48 +08:00
|
|
|
informerCacheResolvers resolvers.ConfigmapResolver,
|
2022-12-07 16:08:37 +01:00
|
|
|
namespaceLabels map[string]string,
|
2023-01-02 18:14:40 +01:00
|
|
|
cfg config.Configuration,
|
2022-05-10 19:01:29 +02:00
|
|
|
) (responses []*response.EngineResponse) {
|
2019-08-13 11:32:12 -07:00
|
|
|
startTime := time.Now()
|
|
|
|
defer func() {
|
2020-05-17 09:54:32 -07:00
|
|
|
name := resource.GetKind() + "/" + resource.GetName()
|
|
|
|
ns := resource.GetNamespace()
|
|
|
|
if ns != "" {
|
|
|
|
name = ns + "/" + name
|
|
|
|
}
|
|
|
|
|
2020-07-09 11:48:34 -07:00
|
|
|
logger.V(3).Info("applyPolicy", "resource", name, "processingTime", time.Since(startTime).String())
|
2019-08-13 11:32:12 -07:00
|
|
|
}()
|
2019-08-26 13:34:42 -07:00
|
|
|
|
2020-12-23 15:10:07 -08:00
|
|
|
var engineResponses []*response.EngineResponse
|
|
|
|
var engineResponseMutation, engineResponseValidation *response.EngineResponse
|
2019-08-26 13:34:42 -07:00
|
|
|
var err error
|
2020-12-21 11:04:19 -08:00
|
|
|
|
2022-12-09 14:45:11 +01:00
|
|
|
ctx := enginecontext.NewContext()
|
2022-12-21 17:44:02 +01:00
|
|
|
data, err := resource.MarshalJSON()
|
|
|
|
if err != nil {
|
|
|
|
logging.Error(err, "failed to marshal resource")
|
|
|
|
}
|
|
|
|
err = enginecontext.AddResource(ctx, data)
|
2020-03-24 00:35:05 +05:30
|
|
|
if err != nil {
|
2021-02-22 17:22:23 -08:00
|
|
|
logger.Error(err, "failed to add transform resource to ctx")
|
|
|
|
}
|
|
|
|
err = ctx.AddNamespace(resource.GetNamespace())
|
|
|
|
if err != nil {
|
|
|
|
logger.Error(err, "failed to add namespace to ctx")
|
2020-03-24 00:35:05 +05:30
|
|
|
}
|
2020-12-21 11:04:19 -08:00
|
|
|
|
2023-01-02 18:14:40 +01:00
|
|
|
if err := ctx.AddImageInfos(&resource, cfg); err != nil {
|
2021-04-28 14:21:11 -07:00
|
|
|
logger.Error(err, "unable to add image info to variables context")
|
|
|
|
}
|
|
|
|
|
2022-10-13 16:03:49 +02:00
|
|
|
if err := ctx.AddOperation("CREATE"); err != nil {
|
|
|
|
logger.Error(err, "unable to set operation in context")
|
|
|
|
}
|
|
|
|
|
2022-12-16 18:26:48 +08:00
|
|
|
engineResponseMutation, err = mutation(policy, resource, logger, ctx, rclient, informerCacheResolvers, namespaceLabels)
|
2019-08-13 11:32:12 -07:00
|
|
|
if err != nil {
|
2020-03-17 11:05:20 -07:00
|
|
|
logger.Error(err, "failed to process mutation rule")
|
2019-08-13 11:32:12 -07:00
|
|
|
}
|
|
|
|
|
2022-12-02 09:14:23 +01:00
|
|
|
policyCtx := engine.NewPolicyContextWithJsonContext(ctx).
|
|
|
|
WithPolicy(policy).
|
|
|
|
WithNewResource(resource).
|
|
|
|
WithNamespaceLabels(namespaceLabels).
|
|
|
|
WithClient(client).
|
2022-12-16 18:26:48 +08:00
|
|
|
WithExcludeGroupRole(excludeGroupRole...).
|
|
|
|
WithInformerCacheResolver(informerCacheResolvers)
|
2021-02-01 12:59:13 -08:00
|
|
|
|
2023-01-02 18:14:40 +01:00
|
|
|
engineResponseValidation = engine.Validate(context.TODO(), rclient, policyCtx, cfg)
|
2020-05-26 13:56:07 -07:00
|
|
|
engineResponses = append(engineResponses, mergeRuleRespose(engineResponseMutation, engineResponseValidation))
|
2019-08-13 11:32:12 -07:00
|
|
|
|
2019-08-26 13:34:42 -07:00
|
|
|
return engineResponses
|
2019-08-13 11:32:12 -07:00
|
|
|
}
|
2020-11-30 12:54:48 -08:00
|
|
|
|
2022-12-07 16:08:37 +01:00
|
|
|
func mutation(
|
|
|
|
policy kyvernov1.PolicyInterface,
|
|
|
|
resource unstructured.Unstructured,
|
|
|
|
log logr.Logger,
|
2022-12-09 14:45:11 +01:00
|
|
|
jsonContext enginecontext.Interface,
|
2022-12-07 16:08:37 +01:00
|
|
|
rclient registryclient.Client,
|
2022-12-16 18:26:48 +08:00
|
|
|
informerCacheResolvers resolvers.ConfigmapResolver,
|
2022-12-07 16:08:37 +01:00
|
|
|
namespaceLabels map[string]string,
|
|
|
|
) (*response.EngineResponse, error) {
|
2022-12-02 09:14:23 +01:00
|
|
|
policyContext := engine.NewPolicyContextWithJsonContext(jsonContext).
|
|
|
|
WithPolicy(policy).
|
|
|
|
WithNamespaceLabels(namespaceLabels).
|
2022-12-16 18:26:48 +08:00
|
|
|
WithNewResource(resource).
|
|
|
|
WithInformerCacheResolver(informerCacheResolvers)
|
2020-12-23 15:10:07 -08:00
|
|
|
|
2022-12-09 14:45:11 +01:00
|
|
|
engineResponse := engine.Mutate(context.TODO(), rclient, policyContext)
|
2020-06-30 11:53:27 -07:00
|
|
|
if !engineResponse.IsSuccessful() {
|
2020-03-17 11:05:20 -07:00
|
|
|
log.V(4).Info("failed to apply mutation rules; reporting them")
|
2019-08-26 13:34:42 -07:00
|
|
|
return engineResponse, nil
|
2019-08-20 16:57:19 -07:00
|
|
|
}
|
2020-12-21 11:04:19 -08:00
|
|
|
// Verify if the JSON patches returned by the Mutate are already applied to the resource
|
2019-08-26 13:34:42 -07:00
|
|
|
if reflect.DeepEqual(resource, engineResponse.PatchedResource) {
|
|
|
|
// resources matches
|
2020-12-21 11:04:19 -08:00
|
|
|
log.V(4).Info("resource already satisfies the policy")
|
2019-08-26 13:34:42 -07:00
|
|
|
return engineResponse, nil
|
2019-08-20 16:57:19 -07:00
|
|
|
}
|
2020-03-17 11:05:20 -07:00
|
|
|
return getFailedOverallRuleInfo(resource, engineResponse, log)
|
2019-08-26 13:34:42 -07:00
|
|
|
}
|
2019-08-20 16:57:19 -07:00
|
|
|
|
2019-08-26 13:34:42 -07:00
|
|
|
// getFailedOverallRuleInfo gets detailed info for over-all mutation failure
|
2020-12-23 15:10:07 -08:00
|
|
|
func getFailedOverallRuleInfo(resource unstructured.Unstructured, engineResponse *response.EngineResponse, log logr.Logger) (*response.EngineResponse, error) {
|
2019-08-26 13:34:42 -07:00
|
|
|
rawResource, err := resource.MarshalJSON()
|
|
|
|
if err != nil {
|
2020-10-27 00:41:16 +05:30
|
|
|
log.Error(err, "failed to marshall resource")
|
2020-12-23 15:10:07 -08:00
|
|
|
return &response.EngineResponse{}, err
|
2019-08-13 11:32:12 -07:00
|
|
|
}
|
|
|
|
|
2019-08-26 13:34:42 -07:00
|
|
|
// resource does not match so there was a mutation rule violated
|
|
|
|
for index, rule := range engineResponse.PolicyResponse.Rules {
|
2020-05-26 23:07:48 -07:00
|
|
|
log.V(4).Info("verifying if policy rule was applied before", "rule", rule.Name)
|
2020-05-26 13:56:07 -07:00
|
|
|
|
2020-06-03 17:47:06 -07:00
|
|
|
patches := rule.Patches
|
|
|
|
|
2022-04-01 07:26:47 +02:00
|
|
|
patch, err := jsonpatch.DecodePatch(jsonutils.JoinPatches(patches...))
|
2019-08-26 13:34:42 -07:00
|
|
|
if err != nil {
|
2020-05-26 13:56:07 -07:00
|
|
|
log.Error(err, "failed to decode JSON patch", "patches", patches)
|
2020-12-23 15:10:07 -08:00
|
|
|
return &response.EngineResponse{}, err
|
2019-08-26 13:34:42 -07:00
|
|
|
}
|
2019-08-13 11:32:12 -07:00
|
|
|
|
2019-08-26 13:34:42 -07:00
|
|
|
// apply the patches returned by mutate to the original resource
|
|
|
|
patchedResource, err := patch.Apply(rawResource)
|
|
|
|
if err != nil {
|
2020-05-26 13:56:07 -07:00
|
|
|
log.Error(err, "failed to apply JSON patch", "patches", patches)
|
2020-12-23 15:10:07 -08:00
|
|
|
return &response.EngineResponse{}, err
|
2019-08-26 13:34:42 -07:00
|
|
|
}
|
2020-06-03 17:47:06 -07:00
|
|
|
|
2019-08-26 13:34:42 -07:00
|
|
|
if !jsonpatch.Equal(patchedResource, rawResource) {
|
2020-03-17 11:05:20 -07:00
|
|
|
log.V(4).Info("policy rule conditions not satisfied by resource", "rule", rule.Name)
|
2021-09-26 02:12:31 -07:00
|
|
|
engineResponse.PolicyResponse.Rules[index].Status = response.RuleStatusFail
|
2020-05-26 13:56:07 -07:00
|
|
|
engineResponse.PolicyResponse.Rules[index].Message = fmt.Sprintf("mutation json patches not found at resource path %s", extractPatchPath(patches, log))
|
2019-08-26 13:34:42 -07:00
|
|
|
}
|
2019-08-13 11:32:12 -07:00
|
|
|
}
|
2020-05-26 13:56:07 -07:00
|
|
|
|
2019-08-26 13:34:42 -07:00
|
|
|
return engineResponse, nil
|
2019-08-13 11:32:12 -07:00
|
|
|
}
|
2019-12-26 11:50:41 -08:00
|
|
|
|
2020-03-17 11:05:20 -07:00
|
|
|
func extractPatchPath(patches [][]byte, log logr.Logger) string {
|
2019-12-26 11:50:41 -08:00
|
|
|
var resultPath []string
|
|
|
|
// extract the patch path and value
|
|
|
|
for _, patch := range patches {
|
2022-07-10 20:56:31 -07:00
|
|
|
if data, err := jsonutils.UnmarshalPatchOperation(patch); err != nil {
|
2020-03-17 11:05:20 -07:00
|
|
|
log.Error(err, "failed to decode the generate patch", "patch", string(patch))
|
2019-12-26 11:50:41 -08:00
|
|
|
continue
|
2022-04-01 07:26:47 +02:00
|
|
|
} else {
|
|
|
|
resultPath = append(resultPath, data.Path)
|
2019-12-26 11:50:41 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return strings.Join(resultPath, ";")
|
|
|
|
}
|
2020-05-26 13:56:07 -07:00
|
|
|
|
2020-12-23 15:10:07 -08:00
|
|
|
func mergeRuleRespose(mutation, validation *response.EngineResponse) *response.EngineResponse {
|
2020-05-26 13:56:07 -07:00
|
|
|
mutation.PolicyResponse.Rules = append(mutation.PolicyResponse.Rules, validation.PolicyResponse.Rules...)
|
|
|
|
return mutation
|
|
|
|
}
|