1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-16 20:48:42 +00:00

refactor: resolve roles/cluster roles/top level GVK earlier in the admission chain (#6775)

* refactor: remove more admission request pointers

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* more

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* refactor: resolve roles/cluster roles earlier in the admission chain

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* fix

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* enrich

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

* enrich

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>

---------

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
This commit is contained in:
Charles-Edouard Brétéché 2023-04-04 12:23:20 +02:00 committed by GitHub
parent 9bca7b36b1
commit 4634760e9e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 120 additions and 89 deletions

View file

@ -70,7 +70,7 @@ func NewServer(
"POST", "POST",
config.CleanupValidatingWebhookServicePath, config.CleanupValidatingWebhookServicePath,
handlers.FromAdmissionFunc("VALIDATE", validationHandler). handlers.FromAdmissionFunc("VALIDATE", validationHandler).
WithDump(debugModeOpts.DumpPayload, nil, nil). WithDump(debugModeOpts.DumpPayload).
WithSubResourceFilter(). WithSubResourceFilter().
WithMetrics(policyLogger, metricsConfig.Config(), metrics.WebhookValidating). WithMetrics(policyLogger, metricsConfig.Config(), metrics.WebhookValidating).
WithAdmission(policyLogger.WithName("validate")). WithAdmission(policyLogger.WithName("validate")).

View file

@ -544,6 +544,7 @@ func main() {
runtime, runtime,
kubeInformer.Rbac().V1().RoleBindings().Lister(), kubeInformer.Rbac().V1().RoleBindings().Lister(),
kubeInformer.Rbac().V1().ClusterRoleBindings().Lister(), kubeInformer.Rbac().V1().ClusterRoleBindings().Lister(),
dClient.Discovery(),
) )
// start informers and wait for cache sync // start informers and wait for cache sync
// we need to call start again because we potentially registered new informers // we need to call start again because we potentially registered new informers

View file

@ -5,7 +5,6 @@ import (
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1" kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1"
"github.com/kyverno/kyverno/pkg/clients/dclient"
"github.com/kyverno/kyverno/pkg/config" "github.com/kyverno/kyverno/pkg/config"
engineapi "github.com/kyverno/kyverno/pkg/engine/api" engineapi "github.com/kyverno/kyverno/pkg/engine/api"
enginectx "github.com/kyverno/kyverno/pkg/engine/context" enginectx "github.com/kyverno/kyverno/pkg/engine/context"
@ -190,9 +189,9 @@ func NewPolicyContext(operation kyvernov1.AdmissionOperation) *PolicyContext {
} }
func NewPolicyContextFromAdmissionRequest( func NewPolicyContextFromAdmissionRequest(
client dclient.IDiscovery,
request admissionv1.AdmissionRequest, request admissionv1.AdmissionRequest,
admissionInfo kyvernov1beta1.RequestInfo, admissionInfo kyvernov1beta1.RequestInfo,
gvk schema.GroupVersionKind,
configuration config.Configuration, configuration config.Configuration,
) (*PolicyContext, error) { ) (*PolicyContext, error) {
ctx, err := newVariablesContext(request, &admissionInfo) ctx, err := newVariablesContext(request, &admissionInfo)
@ -206,10 +205,6 @@ func NewPolicyContextFromAdmissionRequest(
if err := ctx.AddImageInfos(&newResource, configuration); err != nil { if err := ctx.AddImageInfos(&newResource, configuration); err != nil {
return nil, fmt.Errorf("failed to add image information to the policy rule context: %w", err) return nil, fmt.Errorf("failed to add image information to the policy rule context: %w", err)
} }
gvk, err := client.GetGVKFromGVR(schema.GroupVersionResource(request.Resource))
if err != nil {
return nil, err
}
policyContext := NewPolicyContextWithJsonContext(kyvernov1.AdmissionOperation(request.Operation), ctx). policyContext := NewPolicyContextWithJsonContext(kyvernov1.AdmissionOperation(request.Operation), ctx).
WithNewResource(newResource). WithNewResource(newResource).
WithOldResource(oldResource). WithOldResource(oldResource).

View file

@ -39,21 +39,16 @@ func (inner AdmissionHandler) withAdmission(logger logr.Logger) HttpHandler {
return return
} }
logger := logger.WithValues( logger := logger.WithValues(
"kind", admissionReview.Request.Kind.Kind,
"gvk", admissionReview.Request.Kind, "gvk", admissionReview.Request.Kind,
"gvr", admissionReview.Request.Resource,
"namespace", admissionReview.Request.Namespace, "namespace", admissionReview.Request.Namespace,
"name", admissionReview.Request.Name, "name", admissionReview.Request.Name,
"operation", admissionReview.Request.Operation, "operation", admissionReview.Request.Operation,
"uid", admissionReview.Request.UID, "uid", admissionReview.Request.UID,
"user", admissionReview.Request.UserInfo, "user", admissionReview.Request.UserInfo,
) )
admissionReview.Response = &admissionv1.AdmissionResponse{
Allowed: true,
UID: admissionReview.Request.UID,
}
admissionRequest := AdmissionRequest{ admissionRequest := AdmissionRequest{
AdmissionRequest: *admissionReview.Request, AdmissionRequest: *admissionReview.Request,
// TODO: roles/clusterroles
} }
admissionResponse := inner(request.Context(), logger, admissionRequest, startTime) admissionResponse := inner(request.Context(), logger, admissionRequest, startTime)
admissionReview.Response = &admissionResponse admissionReview.Response = &admissionResponse

View file

@ -6,47 +6,37 @@ import (
"time" "time"
"github.com/go-logr/logr" "github.com/go-logr/logr"
"github.com/kyverno/kyverno/pkg/userinfo"
admissionutils "github.com/kyverno/kyverno/pkg/utils/admission" admissionutils "github.com/kyverno/kyverno/pkg/utils/admission"
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube" kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
admissionv1 "k8s.io/api/admission/v1"
authenticationv1 "k8s.io/api/authentication/v1" authenticationv1 "k8s.io/api/authentication/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
rbacv1listers "k8s.io/client-go/listers/rbac/v1"
) )
func (inner AdmissionHandler) WithDump( func (inner AdmissionHandler) WithDump(
enabled bool, enabled bool,
rbLister rbacv1listers.RoleBindingLister,
crbLister rbacv1listers.ClusterRoleBindingLister,
) AdmissionHandler { ) AdmissionHandler {
if !enabled { if !enabled {
return inner return inner
} }
return inner.withDump(rbLister, crbLister).WithTrace("DUMP") return inner.withDump().WithTrace("DUMP")
} }
func (inner AdmissionHandler) withDump( func (inner AdmissionHandler) withDump() AdmissionHandler {
rbLister rbacv1listers.RoleBindingLister,
crbLister rbacv1listers.ClusterRoleBindingLister,
) AdmissionHandler {
return func(ctx context.Context, logger logr.Logger, request AdmissionRequest, startTime time.Time) AdmissionResponse { return func(ctx context.Context, logger logr.Logger, request AdmissionRequest, startTime time.Time) AdmissionResponse {
response := inner(ctx, logger, request, startTime) response := inner(ctx, logger, request, startTime)
dumpPayload(logger, rbLister, crbLister, request.AdmissionRequest, response) dumpPayload(logger, request, response)
return response return response
} }
} }
func dumpPayload( func dumpPayload(
logger logr.Logger, logger logr.Logger,
rbLister rbacv1listers.RoleBindingLister, request AdmissionRequest,
crbLister rbacv1listers.ClusterRoleBindingLister,
request admissionv1.AdmissionRequest,
response AdmissionResponse, response AdmissionResponse,
) { ) {
reqPayload, err := newAdmissionRequestPayload(request, rbLister, crbLister) reqPayload, err := newAdmissionRequestPayload(request)
if err != nil { if err != nil {
logger.Error(err, "Failed to extract resources") logger.Error(err, "Failed to extract resources")
} else { } else {
@ -77,11 +67,9 @@ type admissionRequestPayload struct {
} }
func newAdmissionRequestPayload( func newAdmissionRequestPayload(
request admissionv1.AdmissionRequest, request AdmissionRequest,
rbLister rbacv1listers.RoleBindingLister,
crbLister rbacv1listers.ClusterRoleBindingLister,
) (*admissionRequestPayload, error) { ) (*admissionRequestPayload, error) {
newResource, oldResource, err := admissionutils.ExtractResources(nil, request) newResource, oldResource, err := admissionutils.ExtractResources(nil, request.AdmissionRequest)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -92,15 +80,6 @@ func newAdmissionRequestPayload(
return nil, err return nil, err
} }
} }
var roles, clusterRoles []string
if rbLister != nil && crbLister != nil {
if r, cr, err := userinfo.GetRoleRef(rbLister, crbLister, request.UserInfo); err != nil {
return nil, err
} else {
roles = r
clusterRoles = cr
}
}
return redactPayload(&admissionRequestPayload{ return redactPayload(&admissionRequestPayload{
UID: request.UID, UID: request.UID,
Kind: request.Kind, Kind: request.Kind,
@ -113,8 +92,8 @@ func newAdmissionRequestPayload(
Namespace: request.Namespace, Namespace: request.Namespace,
Operation: string(request.Operation), Operation: string(request.Operation),
UserInfo: request.UserInfo, UserInfo: request.UserInfo,
Roles: roles, Roles: request.Roles,
ClusterRoles: clusterRoles, ClusterRoles: request.ClusterRoles,
Object: newResource, Object: newResource,
OldObject: oldResource, OldObject: oldResource,
DryRun: request.DryRun, DryRun: request.DryRun,

View file

@ -141,7 +141,7 @@ func Test_RedactPayload(t *testing.T) {
var req admissionv1.AdmissionRequest var req admissionv1.AdmissionRequest
err := json.Unmarshal(c.requestPayload, &req) err := json.Unmarshal(c.requestPayload, &req)
assert.NilError(t, err) assert.NilError(t, err)
payload, err := newAdmissionRequestPayload(req, nil, nil) payload, err := newAdmissionRequestPayload(AdmissionRequest{AdmissionRequest: req})
assert.NilError(t, err) assert.NilError(t, err)
if payload.Object.Object != nil { if payload.Object.Object != nil {
data, err := datautils.ToMap(payload.Object.Object["data"]) data, err := datautils.ToMap(payload.Object.Object["data"])

View file

@ -0,0 +1,61 @@
package handlers
import (
"context"
"time"
"github.com/go-logr/logr"
"github.com/kyverno/kyverno/pkg/clients/dclient"
"github.com/kyverno/kyverno/pkg/userinfo"
admissionutils "github.com/kyverno/kyverno/pkg/utils/admission"
"k8s.io/apimachinery/pkg/runtime/schema"
rbacv1listers "k8s.io/client-go/listers/rbac/v1"
)
func (inner AdmissionHandler) WithRoles(
rbLister rbacv1listers.RoleBindingLister,
crbLister rbacv1listers.ClusterRoleBindingLister,
) AdmissionHandler {
return inner.withRoles(rbLister, crbLister).WithTrace("ROLES")
}
func (inner AdmissionHandler) WithTopLevelGVK(
client dclient.IDiscovery,
) AdmissionHandler {
return inner.withTopLevelGVK(client).WithTrace("GVK")
}
func (inner AdmissionHandler) withRoles(
rbLister rbacv1listers.RoleBindingLister,
crbLister rbacv1listers.ClusterRoleBindingLister,
) AdmissionHandler {
return func(ctx context.Context, logger logr.Logger, request AdmissionRequest, startTime time.Time) AdmissionResponse {
roles, clusterRoles, err := userinfo.GetRoleRef(rbLister, crbLister, request.UserInfo)
if err != nil {
return admissionutils.Response(request.UID, err)
}
request.Roles = roles
request.ClusterRoles = clusterRoles
logger = logger.WithValues(
"roles", roles,
"clusterroles", clusterRoles,
)
return inner(ctx, logger, request, startTime)
}
}
func (inner AdmissionHandler) withTopLevelGVK(
client dclient.IDiscovery,
) AdmissionHandler {
return func(ctx context.Context, logger logr.Logger, request AdmissionRequest, startTime time.Time) AdmissionResponse {
gvk, err := client.GetGVKFromGVR(schema.GroupVersionResource(request.Resource))
if err != nil {
return admissionutils.Response(request.UID, err)
}
request.GroupVersionKind = gvk
logger = logger.WithValues(
"resource.gvk", gvk,
)
return inner(ctx, logger, request, startTime)
}
}

View file

@ -16,6 +16,8 @@ import (
const namespaceControllerUsername = "system:serviceaccount:kube-system:namespace-controller" const namespaceControllerUsername = "system:serviceaccount:kube-system:namespace-controller"
var kyvernoUsername = fmt.Sprintf("system:serviceaccount:%s:%s", config.KyvernoNamespace(), config.KyvernoServiceAccountName())
func (inner AdmissionHandler) WithProtection(enabled bool) AdmissionHandler { func (inner AdmissionHandler) WithProtection(enabled bool) AdmissionHandler {
if !enabled { if !enabled {
return inner return inner
@ -37,7 +39,7 @@ func (inner AdmissionHandler) withProtection() AdmissionHandler {
for _, resource := range []unstructured.Unstructured{newResource, oldResource} { for _, resource := range []unstructured.Unstructured{newResource, oldResource} {
resLabels := resource.GetLabels() resLabels := resource.GetLabels()
if resLabels[kyvernov1.LabelAppManagedBy] == kyvernov1.ValueKyvernoApp { if resLabels[kyvernov1.LabelAppManagedBy] == kyvernov1.ValueKyvernoApp {
if request.UserInfo.Username != fmt.Sprintf("system:serviceaccount:%s:%s", config.KyvernoNamespace(), config.KyvernoServiceAccountName()) { if request.UserInfo.Username != kyvernoUsername {
logger.Info("Access to the resource not authorized, this is a kyverno managed resource and should be altered only by kyverno") logger.Info("Access to the resource not authorized, this is a kyverno managed resource and should be altered only by kyverno")
return admissionutils.Response(request.UID, errors.New("A kyverno managed resource can only be modified by kyverno")) return admissionutils.Response(request.UID, errors.New("A kyverno managed resource can only be modified by kyverno"))
} }

View file

@ -7,11 +7,21 @@ import (
"github.com/go-logr/logr" "github.com/go-logr/logr"
admissionv1 "k8s.io/api/admission/v1" admissionv1 "k8s.io/api/admission/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
) )
type AdmissionRequest struct { type AdmissionRequest struct {
// AdmissionRequest is the original admission request. // AdmissionRequest is the original admission request.
admissionv1.AdmissionRequest admissionv1.AdmissionRequest
// Roles is a list of possible role send the request.
Roles []string
// ClusterRoles is a list of possible clusterRoles send the request.
ClusterRoles []string
// GroupVersionKind is the top level GVK.
GroupVersionKind schema.GroupVersionKind
} }
type AdmissionResponse = admissionv1.AdmissionResponse type AdmissionResponse = admissionv1.AdmissionResponse

View file

@ -36,8 +36,6 @@ func NewFakeHandlers(ctx context.Context, policyCache policycache.Cache) webhook
dclient := dclient.NewEmptyFakeClient() dclient := dclient.NewEmptyFakeClient()
configuration := config.NewDefaultConfiguration() configuration := config.NewDefaultConfiguration()
rbLister := informers.Rbac().V1().RoleBindings().Lister()
crbLister := informers.Rbac().V1().ClusterRoleBindings().Lister()
urLister := kyvernoInformers.Kyverno().V1beta1().UpdateRequests().Lister().UpdateRequests(config.KyvernoNamespace()) urLister := kyvernoInformers.Kyverno().V1beta1().UpdateRequests().Lister().UpdateRequests(config.KyvernoNamespace())
peLister := kyvernoInformers.Kyverno().V2alpha1().PolicyExceptions().Lister() peLister := kyvernoInformers.Kyverno().V2alpha1().PolicyExceptions().Lister()
rclient := registryclient.NewOrDie() rclient := registryclient.NewOrDie()
@ -53,7 +51,7 @@ func NewFakeHandlers(ctx context.Context, policyCache policycache.Cache) webhook
urGenerator: updaterequest.NewFake(), urGenerator: updaterequest.NewFake(),
eventGen: event.NewFake(), eventGen: event.NewFake(),
openApiManager: openapi.NewFake(), openApiManager: openapi.NewFake(),
pcBuilder: webhookutils.NewPolicyContextBuilder(configuration, dclient, rbLister, crbLister), pcBuilder: webhookutils.NewPolicyContextBuilder(configuration),
engine: engine.NewEngine( engine: engine.NewEngine(
configuration, configuration,
dclient, dclient,

View file

@ -96,7 +96,7 @@ func NewHandlers(
urGenerator: urGenerator, urGenerator: urGenerator,
eventGen: eventGen, eventGen: eventGen,
openApiManager: openApiManager, openApiManager: openApiManager,
pcBuilder: webhookutils.NewPolicyContextBuilder(configuration, client, rbLister, crbLister), pcBuilder: webhookutils.NewPolicyContextBuilder(configuration),
admissionReports: admissionReports, admissionReports: admissionReports,
} }
} }
@ -120,7 +120,7 @@ func (h *resourceHandlers) Validate(ctx context.Context, logger logr.Logger, req
logger.V(4).Info("processing policies for validate admission request", "validate", len(policies), "mutate", len(mutatePolicies), "generate", len(generatePolicies)) logger.V(4).Info("processing policies for validate admission request", "validate", len(policies), "mutate", len(mutatePolicies), "generate", len(generatePolicies))
policyContext, err := h.pcBuilder.Build(request.AdmissionRequest) policyContext, err := h.pcBuilder.Build(request.AdmissionRequest, request.Roles, request.ClusterRoles, request.GroupVersionKind)
if err != nil { if err != nil {
return errorResponse(logger, request.UID, err, "failed create policy context") return errorResponse(logger, request.UID, err, "failed create policy context")
} }
@ -132,7 +132,7 @@ func (h *resourceHandlers) Validate(ctx context.Context, logger logr.Logger, req
policyContext = policyContext.WithNamespaceLabels(namespaceLabels) policyContext = policyContext.WithNamespaceLabels(namespaceLabels)
vh := validation.NewValidationHandler(logger, h.kyvernoClient, h.engine, h.pCache, h.pcBuilder, h.eventGen, h.admissionReports, h.metricsConfig, h.configuration) vh := validation.NewValidationHandler(logger, h.kyvernoClient, h.engine, h.pCache, h.pcBuilder, h.eventGen, h.admissionReports, h.metricsConfig, h.configuration)
ok, msg, warnings := vh.HandleValidation(ctx, request.AdmissionRequest, policies, policyContext, startTime) ok, msg, warnings := vh.HandleValidation(ctx, request, policies, policyContext, startTime)
if !ok { if !ok {
logger.Info("admission request denied") logger.Info("admission request denied")
return admissionutils.Response(request.UID, errors.New(msg), warnings...) return admissionutils.Response(request.UID, errors.New(msg), warnings...)
@ -155,7 +155,7 @@ func (h *resourceHandlers) Mutate(ctx context.Context, logger logr.Logger, reque
return admissionutils.ResponseSuccess(request.UID) return admissionutils.ResponseSuccess(request.UID)
} }
logger.V(4).Info("processing policies for mutate admission request", "mutatePolicies", len(mutatePolicies), "verifyImagesPolicies", len(verifyImagesPolicies)) logger.V(4).Info("processing policies for mutate admission request", "mutatePolicies", len(mutatePolicies), "verifyImagesPolicies", len(verifyImagesPolicies))
policyContext, err := h.pcBuilder.Build(request.AdmissionRequest) policyContext, err := h.pcBuilder.Build(request.AdmissionRequest, request.Roles, request.ClusterRoles, request.GroupVersionKind)
if err != nil { if err != nil {
logger.Error(err, "failed to build policy context") logger.Error(err, "failed to build policy context")
return admissionutils.Response(request.UID, err) return admissionutils.Response(request.UID, err)
@ -168,7 +168,7 @@ func (h *resourceHandlers) Mutate(ctx context.Context, logger logr.Logger, reque
} }
newRequest := patchRequest(mutatePatches, request.AdmissionRequest, logger) newRequest := patchRequest(mutatePatches, request.AdmissionRequest, logger)
// rebuild context to process images updated via mutate policies // rebuild context to process images updated via mutate policies
policyContext, err = h.pcBuilder.Build(newRequest) policyContext, err = h.pcBuilder.Build(newRequest, request.Roles, request.ClusterRoles, request.GroupVersionKind)
if err != nil { if err != nil {
logger.Error(err, "failed to build policy context") logger.Error(err, "failed to build policy context")
return admissionutils.Response(request.UID, err) return admissionutils.Response(request.UID, err)

View file

@ -17,6 +17,7 @@ import (
"github.com/kyverno/kyverno/pkg/tracing" "github.com/kyverno/kyverno/pkg/tracing"
admissionutils "github.com/kyverno/kyverno/pkg/utils/admission" admissionutils "github.com/kyverno/kyverno/pkg/utils/admission"
reportutils "github.com/kyverno/kyverno/pkg/utils/report" reportutils "github.com/kyverno/kyverno/pkg/utils/report"
"github.com/kyverno/kyverno/pkg/webhooks/handlers"
webhookutils "github.com/kyverno/kyverno/pkg/webhooks/utils" webhookutils "github.com/kyverno/kyverno/pkg/webhooks/utils"
"go.opentelemetry.io/otel/trace" "go.opentelemetry.io/otel/trace"
admissionv1 "k8s.io/api/admission/v1" admissionv1 "k8s.io/api/admission/v1"
@ -29,7 +30,7 @@ type ValidationHandler interface {
// HandleValidation handles validating webhook admission request // HandleValidation handles validating webhook admission request
// If there are no errors in validating rule we apply generation rules // If there are no errors in validating rule we apply generation rules
// patchedResource is the (resource + patches) after applying mutation rules // patchedResource is the (resource + patches) after applying mutation rules
HandleValidation(context.Context, admissionv1.AdmissionRequest, []kyvernov1.PolicyInterface, *engine.PolicyContext, time.Time) (bool, string, []string) HandleValidation(context.Context, handlers.AdmissionRequest, []kyvernov1.PolicyInterface, *engine.PolicyContext, time.Time) (bool, string, []string)
} }
func NewValidationHandler( func NewValidationHandler(
@ -70,12 +71,12 @@ type validationHandler struct {
func (v *validationHandler) HandleValidation( func (v *validationHandler) HandleValidation(
ctx context.Context, ctx context.Context,
request admissionv1.AdmissionRequest, request handlers.AdmissionRequest,
policies []kyvernov1.PolicyInterface, policies []kyvernov1.PolicyInterface,
policyContext *engine.PolicyContext, policyContext *engine.PolicyContext,
admissionRequestTimestamp time.Time, admissionRequestTimestamp time.Time,
) (bool, string, []string) { ) (bool, string, []string) {
resourceName := admissionutils.GetResourceName(request) resourceName := admissionutils.GetResourceName(request.AdmissionRequest)
logger := v.log.WithValues("action", "validate", "resource", resourceName, "operation", request.Operation, "gvk", request.Kind) logger := v.log.WithValues("action", "validate", "resource", resourceName, "operation", request.Operation, "gvk", request.Kind)
var deletionTimeStamp *metav1.Time var deletionTimeStamp *metav1.Time
@ -145,12 +146,12 @@ func (v *validationHandler) HandleValidation(
func (v *validationHandler) buildAuditResponses( func (v *validationHandler) buildAuditResponses(
ctx context.Context, ctx context.Context,
resource unstructured.Unstructured, resource unstructured.Unstructured,
request admissionv1.AdmissionRequest, request handlers.AdmissionRequest,
namespaceLabels map[string]string, namespaceLabels map[string]string,
) ([]engineapi.EngineResponse, error) { ) ([]engineapi.EngineResponse, error) {
gvr := schema.GroupVersionResource(request.Resource) gvr := schema.GroupVersionResource(request.Resource)
policies := v.pCache.GetPolicies(policycache.ValidateAudit, gvr, request.SubResource, request.Namespace) policies := v.pCache.GetPolicies(policycache.ValidateAudit, gvr, request.SubResource, request.Namespace)
policyContext, err := v.pcBuilder.Build(request) policyContext, err := v.pcBuilder.Build(request.AdmissionRequest, request.Roles, request.ClusterRoles, request.GroupVersionKind)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -175,12 +176,12 @@ func (v *validationHandler) buildAuditResponses(
func (v *validationHandler) handleAudit( func (v *validationHandler) handleAudit(
ctx context.Context, ctx context.Context,
resource unstructured.Unstructured, resource unstructured.Unstructured,
request admissionv1.AdmissionRequest, request handlers.AdmissionRequest,
namespaceLabels map[string]string, namespaceLabels map[string]string,
engineResponses ...engineapi.EngineResponse, engineResponses ...engineapi.EngineResponse,
) { ) {
createReport := v.admissionReports createReport := v.admissionReports
if admissionutils.IsDryRun(request) { if admissionutils.IsDryRun(request.AdmissionRequest) {
createReport = false createReport = false
} }
// we don't need reports for deletions // we don't need reports for deletions
@ -208,7 +209,7 @@ func (v *validationHandler) handleAudit(
v.eventGen.Add(events...) v.eventGen.Add(events...)
if createReport { if createReport {
responses = append(responses, engineResponses...) responses = append(responses, engineResponses...)
report := reportutils.BuildAdmissionReport(resource, request, responses...) report := reportutils.BuildAdmissionReport(resource, request.AdmissionRequest, responses...)
if len(report.GetResults()) > 0 { if len(report.GetResults()) > 0 {
_, err = reportutils.CreateReport(ctx, report, v.kyvernoClient) _, err = reportutils.CreateReport(ctx, report, v.kyvernoClient)
if err != nil { if err != nil {

View file

@ -8,6 +8,7 @@ import (
"github.com/go-logr/logr" "github.com/go-logr/logr"
"github.com/julienschmidt/httprouter" "github.com/julienschmidt/httprouter"
"github.com/kyverno/kyverno/pkg/clients/dclient"
"github.com/kyverno/kyverno/pkg/config" "github.com/kyverno/kyverno/pkg/config"
"github.com/kyverno/kyverno/pkg/logging" "github.com/kyverno/kyverno/pkg/logging"
"github.com/kyverno/kyverno/pkg/metrics" "github.com/kyverno/kyverno/pkg/metrics"
@ -81,6 +82,7 @@ func NewServer(
runtime runtimeutils.Runtime, runtime runtimeutils.Runtime,
rbLister rbacv1listers.RoleBindingLister, rbLister rbacv1listers.RoleBindingLister,
crbLister rbacv1listers.ClusterRoleBindingLister, crbLister rbacv1listers.ClusterRoleBindingLister,
discovery dclient.IDiscovery,
) Server { ) Server {
mux := httprouter.New() mux := httprouter.New()
resourceLogger := logger.WithName("resource") resourceLogger := logger.WithName("resource")
@ -96,7 +98,9 @@ func NewServer(
return handler. return handler.
WithFilter(configuration). WithFilter(configuration).
WithProtection(toggle.ProtectManagedResources.Enabled()). WithProtection(toggle.ProtectManagedResources.Enabled()).
WithDump(debugModeOpts.DumpPayload, rbLister, crbLister). WithDump(debugModeOpts.DumpPayload).
WithTopLevelGVK(discovery).
WithRoles(rbLister, crbLister).
WithOperationFilter(admissionv1.Create, admissionv1.Update, admissionv1.Connect). WithOperationFilter(admissionv1.Create, admissionv1.Update, admissionv1.Connect).
WithMetrics(resourceLogger, metricsConfig.Config(), metrics.WebhookMutating). WithMetrics(resourceLogger, metricsConfig.Config(), metrics.WebhookMutating).
WithAdmission(resourceLogger.WithName("mutate")) WithAdmission(resourceLogger.WithName("mutate"))
@ -111,7 +115,9 @@ func NewServer(
return handler. return handler.
WithFilter(configuration). WithFilter(configuration).
WithProtection(toggle.ProtectManagedResources.Enabled()). WithProtection(toggle.ProtectManagedResources.Enabled()).
WithDump(debugModeOpts.DumpPayload, rbLister, crbLister). WithDump(debugModeOpts.DumpPayload).
WithTopLevelGVK(discovery).
WithRoles(rbLister, crbLister).
WithMetrics(resourceLogger, metricsConfig.Config(), metrics.WebhookValidating). WithMetrics(resourceLogger, metricsConfig.Config(), metrics.WebhookValidating).
WithAdmission(resourceLogger.WithName("validate")) WithAdmission(resourceLogger.WithName("validate"))
}, },
@ -120,7 +126,7 @@ func NewServer(
"POST", "POST",
config.PolicyMutatingWebhookServicePath, config.PolicyMutatingWebhookServicePath,
handlers.FromAdmissionFunc("MUTATE", policyHandlers.Mutate). handlers.FromAdmissionFunc("MUTATE", policyHandlers.Mutate).
WithDump(debugModeOpts.DumpPayload, rbLister, crbLister). WithDump(debugModeOpts.DumpPayload).
WithMetrics(policyLogger, metricsConfig.Config(), metrics.WebhookMutating). WithMetrics(policyLogger, metricsConfig.Config(), metrics.WebhookMutating).
WithAdmission(policyLogger.WithName("mutate")). WithAdmission(policyLogger.WithName("mutate")).
ToHandlerFunc(), ToHandlerFunc(),
@ -129,7 +135,7 @@ func NewServer(
"POST", "POST",
config.PolicyValidatingWebhookServicePath, config.PolicyValidatingWebhookServicePath,
handlers.FromAdmissionFunc("VALIDATE", policyHandlers.Validate). handlers.FromAdmissionFunc("VALIDATE", policyHandlers.Validate).
WithDump(debugModeOpts.DumpPayload, rbLister, crbLister). WithDump(debugModeOpts.DumpPayload).
WithSubResourceFilter(). WithSubResourceFilter().
WithMetrics(policyLogger, metricsConfig.Config(), metrics.WebhookValidating). WithMetrics(policyLogger, metricsConfig.Config(), metrics.WebhookValidating).
WithAdmission(policyLogger.WithName("validate")). WithAdmission(policyLogger.WithName("validate")).
@ -139,7 +145,7 @@ func NewServer(
"POST", "POST",
config.ExceptionValidatingWebhookServicePath, config.ExceptionValidatingWebhookServicePath,
handlers.FromAdmissionFunc("VALIDATE", exceptionHandlers.Validate). handlers.FromAdmissionFunc("VALIDATE", exceptionHandlers.Validate).
WithDump(debugModeOpts.DumpPayload, rbLister, crbLister). WithDump(debugModeOpts.DumpPayload).
WithSubResourceFilter(). WithSubResourceFilter().
WithMetrics(exceptionLogger, metricsConfig.Config(), metrics.WebhookValidating). WithMetrics(exceptionLogger, metricsConfig.Config(), metrics.WebhookValidating).
WithAdmission(exceptionLogger.WithName("validate")). WithAdmission(exceptionLogger.WithName("validate")).

View file

@ -1,51 +1,34 @@
package utils package utils
import ( import (
"fmt"
kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1" kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1"
"github.com/kyverno/kyverno/pkg/clients/dclient"
"github.com/kyverno/kyverno/pkg/config" "github.com/kyverno/kyverno/pkg/config"
"github.com/kyverno/kyverno/pkg/engine" "github.com/kyverno/kyverno/pkg/engine"
"github.com/kyverno/kyverno/pkg/userinfo"
admissionv1 "k8s.io/api/admission/v1" admissionv1 "k8s.io/api/admission/v1"
rbacv1listers "k8s.io/client-go/listers/rbac/v1" "k8s.io/apimachinery/pkg/runtime/schema"
) )
type PolicyContextBuilder interface { type PolicyContextBuilder interface {
Build(admissionv1.AdmissionRequest) (*engine.PolicyContext, error) Build(admissionv1.AdmissionRequest, []string, []string, schema.GroupVersionKind) (*engine.PolicyContext, error)
} }
type policyContextBuilder struct { type policyContextBuilder struct {
configuration config.Configuration configuration config.Configuration
client dclient.Interface
rbLister rbacv1listers.RoleBindingLister
crbLister rbacv1listers.ClusterRoleBindingLister
} }
func NewPolicyContextBuilder( func NewPolicyContextBuilder(
configuration config.Configuration, configuration config.Configuration,
client dclient.Interface,
rbLister rbacv1listers.RoleBindingLister,
crbLister rbacv1listers.ClusterRoleBindingLister,
) PolicyContextBuilder { ) PolicyContextBuilder {
return &policyContextBuilder{ return &policyContextBuilder{
configuration: configuration, configuration: configuration,
client: client,
rbLister: rbLister,
crbLister: crbLister,
} }
} }
func (b *policyContextBuilder) Build(request admissionv1.AdmissionRequest) (*engine.PolicyContext, error) { func (b *policyContextBuilder) Build(request admissionv1.AdmissionRequest, roles, clusterRoles []string, gvk schema.GroupVersionKind) (*engine.PolicyContext, error) {
userRequestInfo := kyvernov1beta1.RequestInfo{ userRequestInfo := kyvernov1beta1.RequestInfo{
AdmissionUserInfo: *request.UserInfo.DeepCopy(), AdmissionUserInfo: *request.UserInfo.DeepCopy(),
Roles: roles,
ClusterRoles: clusterRoles,
} }
if roles, clusterRoles, err := userinfo.GetRoleRef(b.rbLister, b.crbLister, request.UserInfo); err != nil { return engine.NewPolicyContextFromAdmissionRequest(request, userRequestInfo, gvk, b.configuration)
return nil, fmt.Errorf("failed to fetch RBAC information for request: %w", err)
} else {
userRequestInfo.Roles = roles
userRequestInfo.ClusterRoles = clusterRoles
}
return engine.NewPolicyContextFromAdmissionRequest(b.client.Discovery(), request, userRequestInfo, b.configuration)
} }