2022-09-09 12:48:29 +02:00
package mutation
import (
2022-11-28 11:30:14 +01:00
"context"
2022-09-09 12:48:29 +02:00
"fmt"
"time"
"github.com/go-logr/logr"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
2024-10-02 17:35:05 +05:30
"github.com/kyverno/kyverno/pkg/breaker"
"github.com/kyverno/kyverno/pkg/client/clientset/versioned"
2024-09-04 15:55:34 +05:30
"github.com/kyverno/kyverno/pkg/config"
2022-09-09 12:48:29 +02:00
"github.com/kyverno/kyverno/pkg/engine"
2023-01-30 12:41:09 +01:00
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
2023-05-13 10:56:54 +02:00
"github.com/kyverno/kyverno/pkg/engine/mutate/patch"
2022-09-09 12:48:29 +02:00
"github.com/kyverno/kyverno/pkg/event"
"github.com/kyverno/kyverno/pkg/metrics"
2024-09-25 16:11:43 +03:00
"github.com/kyverno/kyverno/pkg/toggle"
2022-12-12 21:32:11 +01:00
"github.com/kyverno/kyverno/pkg/tracing"
2022-09-09 12:48:29 +02:00
engineutils "github.com/kyverno/kyverno/pkg/utils/engine"
jsonutils "github.com/kyverno/kyverno/pkg/utils/json"
2024-10-02 17:35:05 +05:30
reportutils "github.com/kyverno/kyverno/pkg/utils/report"
"github.com/kyverno/kyverno/pkg/webhooks/handlers"
2022-09-09 12:48:29 +02:00
webhookutils "github.com/kyverno/kyverno/pkg/webhooks/utils"
2022-12-12 21:32:11 +01:00
"go.opentelemetry.io/otel/trace"
2023-06-08 12:23:20 +02:00
"gomodules.xyz/jsonpatch/v2"
2022-09-09 12:48:29 +02:00
admissionv1 "k8s.io/api/admission/v1"
2024-10-02 17:35:05 +05:30
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2022-09-09 12:48:29 +02:00
corev1listers "k8s.io/client-go/listers/core/v1"
)
type MutationHandler interface {
// HandleMutation handles validating webhook admission request
// If there are no errors in validating rule we apply generation rules
// patchedResource is the (resource + patches) after applying mutation rules
2024-10-02 17:35:05 +05:30
HandleMutation ( context . Context , handlers . AdmissionRequest , [ ] kyvernov1 . PolicyInterface , * engine . PolicyContext , time . Time , config . Configuration ) ( [ ] byte , [ ] string , error )
2022-09-09 12:48:29 +02:00
}
func NewMutationHandler (
log logr . Logger ,
2024-10-02 17:35:05 +05:30
kyvernoClient versioned . Interface ,
2023-02-02 11:58:34 +01:00
engine engineapi . Engine ,
2022-09-09 12:48:29 +02:00
eventGen event . Interface ,
nsLister corev1listers . NamespaceLister ,
2022-12-09 14:45:11 +01:00
metrics metrics . MetricsConfigManager ,
2024-10-02 17:35:05 +05:30
admissionReports bool ,
2024-10-11 15:12:29 +05:30
reportsConfig reportutils . ReportingConfiguration ,
2024-10-02 17:35:05 +05:30
reportsBreaker breaker . Breaker ,
2022-09-09 12:48:29 +02:00
) MutationHandler {
return & mutationHandler {
2024-10-02 17:35:05 +05:30
log : log ,
kyvernoClient : kyvernoClient ,
engine : engine ,
eventGen : eventGen ,
nsLister : nsLister ,
metrics : metrics ,
admissionReports : admissionReports ,
2024-10-11 15:12:29 +05:30
reportsConfig : reportsConfig ,
2024-10-02 17:35:05 +05:30
reportsBreaker : reportsBreaker ,
2022-09-09 12:48:29 +02:00
}
}
type mutationHandler struct {
2024-10-02 17:35:05 +05:30
log logr . Logger
kyvernoClient versioned . Interface
engine engineapi . Engine
eventGen event . Interface
nsLister corev1listers . NamespaceLister
metrics metrics . MetricsConfigManager
admissionReports bool
2024-10-11 15:12:29 +05:30
reportsConfig reportutils . ReportingConfiguration
2024-10-02 17:35:05 +05:30
reportsBreaker breaker . Breaker
2022-09-09 12:48:29 +02:00
}
func ( h * mutationHandler ) HandleMutation (
2022-12-09 14:45:11 +01:00
ctx context . Context ,
2024-10-02 17:35:05 +05:30
request handlers . AdmissionRequest ,
2022-09-09 12:48:29 +02:00
policies [ ] kyvernov1 . PolicyInterface ,
policyContext * engine . PolicyContext ,
admissionRequestTimestamp time . Time ,
2024-09-04 15:55:34 +05:30
cfg config . Configuration ,
2022-09-09 12:48:29 +02:00
) ( [ ] byte , [ ] string , error ) {
2024-09-04 15:55:34 +05:30
mutatePatches , mutateEngineResponses , err := h . applyMutations ( ctx , request , policies , policyContext , cfg )
2022-09-09 12:48:29 +02:00
if err != nil {
return nil , nil , err
}
2024-09-25 16:11:43 +03:00
if toggle . FromContext ( ctx ) . DumpMutatePatches ( ) {
h . log . V ( 2 ) . Info ( "" , "generated patches" , string ( mutatePatches ) )
}
2022-09-09 12:48:29 +02:00
return mutatePatches , webhookutils . GetWarningMessages ( mutateEngineResponses ) , nil
}
// applyMutations handles mutating webhook admission request
// return value: generated patches, triggered policies, engine responses correspdonding to the triggered policies
func ( v * mutationHandler ) applyMutations (
2022-12-09 14:45:11 +01:00
ctx context . Context ,
2024-10-02 17:35:05 +05:30
request handlers . AdmissionRequest ,
2022-09-09 12:48:29 +02:00
policies [ ] kyvernov1 . PolicyInterface ,
policyContext * engine . PolicyContext ,
2024-09-04 15:55:34 +05:30
cfg config . Configuration ,
2023-03-23 13:58:52 +01:00
) ( [ ] byte , [ ] engineapi . EngineResponse , error ) {
2022-09-09 12:48:29 +02:00
if len ( policies ) == 0 {
return nil , nil , nil
}
2023-05-13 10:56:54 +02:00
var patches [ ] jsonpatch . JsonPatchOperation
2023-03-23 13:58:52 +01:00
var engineResponses [ ] engineapi . EngineResponse
2023-11-22 22:31:46 +05:30
failurePolicy := kyvernov1 . Ignore
2022-09-09 12:48:29 +02:00
for _ , policy := range policies {
spec := policy . GetSpec ( )
2023-12-22 21:07:17 +08:00
if ! spec . HasMutateStandard ( ) {
2022-09-09 12:48:29 +02:00
continue
}
2022-12-12 21:32:11 +01:00
err := tracing . ChildSpan1 (
ctx ,
"" ,
fmt . Sprintf ( "POLICY %s/%s" , policy . GetNamespace ( ) , policy . GetName ( ) ) ,
func ( ctx context . Context , span trace . Span ) error {
v . log . V ( 3 ) . Info ( "applying policy mutate rules" , "policy" , policy . GetName ( ) )
currentContext := policyContext . WithPolicy ( policy )
2023-11-22 22:31:46 +05:30
if policy . GetSpec ( ) . GetFailurePolicy ( ctx ) == kyvernov1 . Fail {
failurePolicy = kyvernov1 . Fail
}
2024-10-02 17:35:05 +05:30
engineResponse , policyPatches , err := v . applyMutation ( ctx , request . AdmissionRequest , currentContext , failurePolicy )
2022-12-12 21:32:11 +01:00
if err != nil {
return fmt . Errorf ( "mutation policy %s error: %v" , policy . GetName ( ) , err )
}
if len ( policyPatches ) > 0 {
patches = append ( patches , policyPatches ... )
rules := engineResponse . GetSuccessRules ( )
if len ( rules ) != 0 {
v . log . Info ( "mutation rules from policy applied successfully" , "policy" , policy . GetName ( ) , "rules" , rules )
}
}
2023-03-23 13:58:52 +01:00
if engineResponse != nil {
policyContext = currentContext . WithNewResource ( engineResponse . PatchedResource )
2024-09-05 15:32:00 +05:30
emitWarning := policy . GetSpec ( ) . EmitWarning
if emitWarning != nil && * emitWarning {
resp := engineResponse . WithWarning ( )
engineResponse = & resp
}
2023-03-23 13:58:52 +01:00
engineResponses = append ( engineResponses , * engineResponse )
}
2022-12-12 21:32:11 +01:00
return nil
} ,
)
if err != nil {
return nil , nil , err
2022-09-09 12:48:29 +02:00
}
}
2024-09-04 15:55:34 +05:30
events := webhookutils . GenerateEvents ( engineResponses , false , cfg )
2023-04-13 15:00:50 +08:00
v . eventGen . Add ( events ... )
2022-09-09 12:48:29 +02:00
2024-10-07 18:47:35 +05:30
go func ( ) {
2024-10-11 15:12:29 +05:30
if v . needsReports ( request , v . admissionReports ) {
2024-10-07 18:47:35 +05:30
if err := v . createReports ( context . TODO ( ) , policyContext . NewResource ( ) , request , engineResponses ... ) ; err != nil {
v . log . Error ( err , "failed to create report" )
}
2024-10-02 17:35:05 +05:30
}
2024-10-07 18:47:35 +05:30
} ( )
2024-10-02 17:35:05 +05:30
2022-09-09 12:48:29 +02:00
logMutationResponse ( patches , engineResponses , v . log )
// patches holds all the successful patches, if no patch is created, it returns nil
2023-05-13 10:56:54 +02:00
return jsonutils . JoinPatches ( patch . ConvertPatches ( patches ... ) ... ) , engineResponses , nil
2022-09-09 12:48:29 +02:00
}
2023-11-22 22:31:46 +05:30
func ( h * mutationHandler ) applyMutation ( ctx context . Context , request admissionv1 . AdmissionRequest , policyContext * engine . PolicyContext , failurePolicy kyvernov1 . FailurePolicyType ) ( * engineapi . EngineResponse , [ ] jsonpatch . JsonPatchOperation , error ) {
2022-09-09 12:48:29 +02:00
if request . Kind . Kind != "Namespace" && request . Namespace != "" {
2022-12-21 21:30:45 +01:00
policyContext = policyContext . WithNamespaceLabels ( engineutils . GetNamespaceSelectorsFromNamespaceLister ( request . Kind . Kind , request . Namespace , h . nsLister , h . log ) )
2022-09-09 12:48:29 +02:00
}
2023-02-03 06:01:11 +01:00
engineResponse := h . engine . Mutate ( ctx , policyContext )
2022-09-09 12:48:29 +02:00
policyPatches := engineResponse . GetPatches ( )
2022-11-08 12:36:13 +01:00
if ! engineResponse . IsSuccessful ( ) {
2023-11-22 22:31:46 +05:30
if webhookutils . BlockRequest ( [ ] engineapi . EngineResponse { engineResponse } , failurePolicy , h . log ) {
h . log . Info ( "failed to apply policy, blocking request" , "policy" , policyContext . Policy ( ) . GetName ( ) , "rules" , engineResponse . GetFailedRulesWithErrors ( ) )
return nil , nil , fmt . Errorf ( "failed to apply policy %s rules %v" , policyContext . Policy ( ) . GetName ( ) , engineResponse . GetFailedRulesWithErrors ( ) )
} else {
h . log . Info ( "ignoring unsuccessful engine responses" , "policy" , policyContext . Policy ( ) . GetName ( ) , "rules" , engineResponse . GetFailedRulesWithErrors ( ) )
return & engineResponse , nil , nil
}
2022-09-09 12:48:29 +02:00
}
2023-03-22 15:55:00 +01:00
return & engineResponse , policyPatches , nil
2022-09-09 12:48:29 +02:00
}
2024-10-02 17:35:05 +05:30
func ( h * mutationHandler ) createReports (
ctx context . Context ,
resource unstructured . Unstructured ,
request handlers . AdmissionRequest ,
engineResponses ... engineapi . EngineResponse ,
) error {
report := reportutils . BuildMutationReport ( resource , request . AdmissionRequest , engineResponses ... )
if len ( report . GetResults ( ) ) > 0 {
err := h . reportsBreaker . Do ( ctx , func ( ctx context . Context ) error {
_ , err := reportutils . CreateReport ( ctx , report , h . kyvernoClient )
return err
} )
if err != nil {
return err
}
}
return nil
}
2023-05-13 10:56:54 +02:00
func logMutationResponse ( patches [ ] jsonpatch . JsonPatchOperation , engineResponses [ ] engineapi . EngineResponse , logger logr . Logger ) {
2022-09-09 12:48:29 +02:00
if len ( patches ) != 0 {
logger . V ( 4 ) . Info ( "created patches" , "count" , len ( patches ) )
}
// if any of the policies fails, print out the error
if ! engineutils . IsResponseSuccessful ( engineResponses ) {
2024-08-28 19:09:58 +02:00
logger . Error ( fmt . Errorf ( webhookutils . GetErrorMsg ( engineResponses ) ) , "failed to apply mutation rules on the resource, reporting policy violation" ) //nolint:govet,staticcheck
2022-09-09 12:48:29 +02:00
}
}