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:
parent
9bca7b36b1
commit
4634760e9e
14 changed files with 120 additions and 89 deletions
|
@ -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")).
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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).
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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"])
|
||||||
|
|
61
pkg/webhooks/handlers/enrich.go
Normal file
61
pkg/webhooks/handlers/enrich.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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")).
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue