2025-02-04 13:52:48 +01:00
|
|
|
package vpol
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/go-logr/logr"
|
2025-02-04 21:32:18 +01:00
|
|
|
"github.com/kyverno/kyverno/pkg/breaker"
|
2025-02-04 13:52:48 +01:00
|
|
|
celengine "github.com/kyverno/kyverno/pkg/cel/engine"
|
|
|
|
celpolicy "github.com/kyverno/kyverno/pkg/cel/policy"
|
2025-02-04 21:32:18 +01:00
|
|
|
"github.com/kyverno/kyverno/pkg/client/clientset/versioned"
|
2025-02-04 13:52:48 +01:00
|
|
|
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
|
|
|
admissionutils "github.com/kyverno/kyverno/pkg/utils/admission"
|
2025-02-04 21:32:18 +01:00
|
|
|
reportutils "github.com/kyverno/kyverno/pkg/utils/report"
|
2025-02-04 13:52:48 +01:00
|
|
|
"github.com/kyverno/kyverno/pkg/webhooks/handlers"
|
|
|
|
"go.uber.org/multierr"
|
|
|
|
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
|
2025-02-04 21:32:18 +01:00
|
|
|
"k8s.io/apimachinery/pkg/util/wait"
|
2025-02-04 13:52:48 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
type handler struct {
|
2025-02-04 21:32:18 +01:00
|
|
|
context celpolicy.Context
|
|
|
|
engine celengine.Engine
|
|
|
|
kyvernoClient versioned.Interface
|
|
|
|
reportsBreaker breaker.Breaker
|
2025-02-04 13:52:48 +01:00
|
|
|
}
|
|
|
|
|
2025-02-04 21:32:18 +01:00
|
|
|
func New(
|
|
|
|
engine celengine.Engine,
|
|
|
|
context celpolicy.Context,
|
|
|
|
kyvernoClient versioned.Interface,
|
|
|
|
reportsBreaker breaker.Breaker,
|
|
|
|
) *handler {
|
2025-02-04 13:52:48 +01:00
|
|
|
return &handler{
|
2025-02-04 21:32:18 +01:00
|
|
|
context: context,
|
|
|
|
engine: engine,
|
|
|
|
kyvernoClient: kyvernoClient,
|
|
|
|
reportsBreaker: reportsBreaker,
|
2025-02-04 13:52:48 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-06 04:49:41 +01:00
|
|
|
func (h *handler) Validate(ctx context.Context, logger logr.Logger, admissionRequest handlers.AdmissionRequest, failurePolicy string, startTime time.Time) handlers.AdmissionResponse {
|
|
|
|
request := celengine.RequestFromAdmission(h.context, admissionRequest.AdmissionRequest)
|
|
|
|
response, err := h.engine.Handle(ctx, request)
|
2025-02-04 13:52:48 +01:00
|
|
|
if err != nil {
|
2025-02-06 04:49:41 +01:00
|
|
|
return admissionutils.Response(admissionRequest.UID, err)
|
2025-02-04 13:52:48 +01:00
|
|
|
}
|
2025-02-04 21:32:18 +01:00
|
|
|
var group wait.Group
|
|
|
|
defer group.Wait()
|
|
|
|
group.Start(func() {
|
2025-02-06 04:49:41 +01:00
|
|
|
err := h.admissionReport(ctx, request, response)
|
2025-02-04 21:32:18 +01:00
|
|
|
if err != nil {
|
|
|
|
logger.Error(err, "failed to create report")
|
|
|
|
}
|
|
|
|
})
|
2025-02-06 04:49:41 +01:00
|
|
|
return h.admissionResponse(request, response)
|
2025-02-04 15:35:52 +01:00
|
|
|
}
|
|
|
|
|
2025-02-06 04:49:41 +01:00
|
|
|
func (h *handler) admissionResponse(request celengine.EngineRequest, response celengine.EngineResponse) handlers.AdmissionResponse {
|
2025-02-04 13:52:48 +01:00
|
|
|
var errs []error
|
2025-02-04 15:35:52 +01:00
|
|
|
var warnings []string
|
2025-02-04 13:52:48 +01:00
|
|
|
for _, policy := range response.Policies {
|
|
|
|
if policy.Actions.Has(admissionregistrationv1.Deny) {
|
|
|
|
for _, rule := range policy.Rules {
|
|
|
|
switch rule.Status() {
|
|
|
|
case engineapi.RuleStatusFail:
|
|
|
|
errs = append(errs, fmt.Errorf("Policy %s rule %s failed: %s", policy.Policy.GetName(), rule.Name(), rule.Message()))
|
|
|
|
case engineapi.RuleStatusError:
|
|
|
|
errs = append(errs, fmt.Errorf("Policy %s rule %s error: %s", policy.Policy.GetName(), rule.Name(), rule.Message()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2025-02-04 15:35:52 +01:00
|
|
|
if policy.Actions.Has(admissionregistrationv1.Warn) {
|
|
|
|
for _, rule := range policy.Rules {
|
|
|
|
switch rule.Status() {
|
|
|
|
case engineapi.RuleStatusFail:
|
|
|
|
warnings = append(warnings, fmt.Sprintf("Policy %s rule %s failed: %s", policy.Policy.GetName(), rule.Name(), rule.Message()))
|
|
|
|
case engineapi.RuleStatusError:
|
|
|
|
warnings = append(warnings, fmt.Sprintf("Policy %s rule %s error: %s", policy.Policy.GetName(), rule.Name(), rule.Message()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2025-02-04 13:52:48 +01:00
|
|
|
}
|
2025-02-06 04:49:41 +01:00
|
|
|
return admissionutils.Response(request.AdmissionRequest().UID, multierr.Combine(errs...), warnings...)
|
2025-02-04 13:52:48 +01:00
|
|
|
}
|
2025-02-04 21:32:18 +01:00
|
|
|
|
2025-02-06 04:49:41 +01:00
|
|
|
func (h *handler) admissionReport(ctx context.Context, request celengine.EngineRequest, response celengine.EngineResponse) error {
|
|
|
|
admissionRequest := request.AdmissionRequest()
|
|
|
|
object, oldObject, err := admissionutils.ExtractResources(nil, admissionRequest)
|
2025-02-04 21:32:18 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if object.Object == nil {
|
|
|
|
object = oldObject
|
|
|
|
}
|
|
|
|
responses := make([]engineapi.EngineResponse, 0, len(response.Policies))
|
|
|
|
for _, r := range response.Policies {
|
|
|
|
engineResponse := engineapi.EngineResponse{
|
|
|
|
Resource: object,
|
|
|
|
PolicyResponse: engineapi.PolicyResponse{
|
|
|
|
Rules: r.Rules,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
engineResponse = engineResponse.WithPolicy(engineapi.NewValidatingPolicy(&r.Policy))
|
|
|
|
responses = append(responses, engineResponse)
|
|
|
|
}
|
2025-02-06 04:49:41 +01:00
|
|
|
report := reportutils.BuildAdmissionReport(object, admissionRequest, responses...)
|
2025-02-04 21:32:18 +01:00
|
|
|
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
|
|
|
|
}
|