From eaf6ed0e5156fad0ccf6c84a83b0a203a60d5e18 Mon Sep 17 00:00:00 2001 From: Mangirdas Judeikis Date: Tue, 10 Dec 2024 19:08:29 +0200 Subject: [PATCH] Fix impersonation for non-system users Signed-off-by: Mangirdas Judeikis On-behalf-of: SAP mangirdas.judeikis@sap.com --- go.mod | 64 +++++++++--------- go.sum | 88 ++++++++++++------------- pkg/authorization/bootstrap/policy.go | 7 ++ pkg/cache/server/config.go | 6 +- pkg/proxy/config.go | 2 +- pkg/server/config.go | 6 +- pkg/server/handler.go | 75 +++++++++++++++++++++ pkg/server/handler_test.go | 78 ++++++++++++++++++++++ test/e2e/authorizer/impersonate_test.go | 83 +++++++++++++++++++++++ 9 files changed, 328 insertions(+), 81 deletions(-) create mode 100644 test/e2e/authorizer/impersonate_test.go diff --git a/go.mod b/go.mod index ef3026d377a..e2598a9887a 100644 --- a/go.mod +++ b/go.mod @@ -168,36 +168,36 @@ require ( replace ( github.com/kcp-dev/kcp/sdk => ./sdk - k8s.io/api => github.com/kcp-dev/kubernetes/staging/src/k8s.io/api v0.0.0-20240918143026-ab5c3a6448cb - k8s.io/apiextensions-apiserver => github.com/kcp-dev/kubernetes/staging/src/k8s.io/apiextensions-apiserver v0.0.0-20240918143026-ab5c3a6448cb - k8s.io/apimachinery => github.com/kcp-dev/kubernetes/staging/src/k8s.io/apimachinery v0.0.0-20240918143026-ab5c3a6448cb - k8s.io/apiserver => github.com/kcp-dev/kubernetes/staging/src/k8s.io/apiserver v0.0.0-20240918143026-ab5c3a6448cb - k8s.io/cli-runtime => github.com/kcp-dev/kubernetes/staging/src/k8s.io/cli-runtime v0.0.0-20240918143026-ab5c3a6448cb - k8s.io/client-go => github.com/kcp-dev/kubernetes/staging/src/k8s.io/client-go v0.0.0-20240918143026-ab5c3a6448cb - k8s.io/cloud-provider => github.com/kcp-dev/kubernetes/staging/src/k8s.io/cloud-provider v0.0.0-20240918143026-ab5c3a6448cb - k8s.io/cluster-bootstrap => github.com/kcp-dev/kubernetes/staging/src/k8s.io/cluster-bootstrap v0.0.0-20240918143026-ab5c3a6448cb - k8s.io/code-generator => github.com/kcp-dev/kubernetes/staging/src/k8s.io/code-generator v0.0.0-20240918143026-ab5c3a6448cb - k8s.io/component-base => github.com/kcp-dev/kubernetes/staging/src/k8s.io/component-base v0.0.0-20240918143026-ab5c3a6448cb - k8s.io/component-helpers => github.com/kcp-dev/kubernetes/staging/src/k8s.io/component-helpers v0.0.0-20240918143026-ab5c3a6448cb - k8s.io/controller-manager => github.com/kcp-dev/kubernetes/staging/src/k8s.io/controller-manager v0.0.0-20240918143026-ab5c3a6448cb - k8s.io/cri-api => github.com/kcp-dev/kubernetes/staging/src/k8s.io/cri-api v0.0.0-20240918143026-ab5c3a6448cb - k8s.io/cri-client => github.com/kcp-dev/kubernetes/staging/src/k8s.io/cri-client v0.0.0-20240918143026-ab5c3a6448cb - k8s.io/csi-translation-lib => github.com/kcp-dev/kubernetes/staging/src/k8s.io/csi-translation-lib v0.0.0-20240918143026-ab5c3a6448cb - k8s.io/dynamic-resource-allocation => github.com/kcp-dev/kubernetes/staging/src/k8s.io/dynamic-resource-allocation v0.0.0-20240918143026-ab5c3a6448cb - k8s.io/endpointslice => github.com/kcp-dev/kubernetes/staging/src/k8s.io/endpointslice v0.0.0-20240918143026-ab5c3a6448cb - k8s.io/kms => github.com/kcp-dev/kubernetes/staging/src/k8s.io/kms v0.0.0-20240918143026-ab5c3a6448cb - k8s.io/kube-aggregator => github.com/kcp-dev/kubernetes/staging/src/k8s.io/kube-aggregator v0.0.0-20240918143026-ab5c3a6448cb - k8s.io/kube-controller-manager => github.com/kcp-dev/kubernetes/staging/src/k8s.io/kube-controller-manager v0.0.0-20240918143026-ab5c3a6448cb - k8s.io/kube-proxy => github.com/kcp-dev/kubernetes/staging/src/k8s.io/kube-proxy v0.0.0-20240918143026-ab5c3a6448cb - k8s.io/kube-scheduler => github.com/kcp-dev/kubernetes/staging/src/k8s.io/kube-scheduler v0.0.0-20240918143026-ab5c3a6448cb - k8s.io/kubectl => github.com/kcp-dev/kubernetes/staging/src/k8s.io/kubectl v0.0.0-20240918143026-ab5c3a6448cb - k8s.io/kubelet => github.com/kcp-dev/kubernetes/staging/src/k8s.io/kubelet v0.0.0-20240918143026-ab5c3a6448cb - k8s.io/kubernetes => github.com/kcp-dev/kubernetes v0.0.0-20240918143026-ab5c3a6448cb - k8s.io/legacy-cloud-providers => github.com/kcp-dev/kubernetes/staging/src/k8s.io/legacy-cloud-providers v0.0.0-20240918143026-ab5c3a6448cb - k8s.io/metrics => github.com/kcp-dev/kubernetes/staging/src/k8s.io/metrics v0.0.0-20240918143026-ab5c3a6448cb - k8s.io/mount-utils => github.com/kcp-dev/kubernetes/staging/src/k8s.io/mount-utils v0.0.0-20240918143026-ab5c3a6448cb - k8s.io/pod-security-admission => github.com/kcp-dev/kubernetes/staging/src/k8s.io/pod-security-admission v0.0.0-20240918143026-ab5c3a6448cb - k8s.io/sample-apiserver => github.com/kcp-dev/kubernetes/staging/src/k8s.io/sample-apiserver v0.0.0-20240918143026-ab5c3a6448cb - k8s.io/sample-cli-plugin => github.com/kcp-dev/kubernetes/staging/src/k8s.io/sample-cli-plugin v0.0.0-20240918143026-ab5c3a6448cb - k8s.io/sample-controller => github.com/kcp-dev/kubernetes/staging/src/k8s.io/sample-controller v0.0.0-20240918143026-ab5c3a6448cb + k8s.io/api => github.com/kcp-dev/kubernetes/staging/src/k8s.io/api v0.0.0-20241210173536-70835f6af5da + k8s.io/apiextensions-apiserver => github.com/kcp-dev/kubernetes/staging/src/k8s.io/apiextensions-apiserver v0.0.0-20241210173536-70835f6af5da + k8s.io/apimachinery => github.com/kcp-dev/kubernetes/staging/src/k8s.io/apimachinery v0.0.0-20241210173536-70835f6af5da + k8s.io/apiserver => github.com/kcp-dev/kubernetes/staging/src/k8s.io/apiserver v0.0.0-20241210173536-70835f6af5da + k8s.io/cli-runtime => github.com/kcp-dev/kubernetes/staging/src/k8s.io/cli-runtime v0.0.0-20241210173536-70835f6af5da + k8s.io/client-go => github.com/kcp-dev/kubernetes/staging/src/k8s.io/client-go v0.0.0-20241210173536-70835f6af5da + k8s.io/cloud-provider => github.com/kcp-dev/kubernetes/staging/src/k8s.io/cloud-provider v0.0.0-20241210173536-70835f6af5da + k8s.io/cluster-bootstrap => github.com/kcp-dev/kubernetes/staging/src/k8s.io/cluster-bootstrap v0.0.0-20241210173536-70835f6af5da + k8s.io/code-generator => github.com/kcp-dev/kubernetes/staging/src/k8s.io/code-generator v0.0.0-20241210173536-70835f6af5da + k8s.io/component-base => github.com/kcp-dev/kubernetes/staging/src/k8s.io/component-base v0.0.0-20241210173536-70835f6af5da + k8s.io/component-helpers => github.com/kcp-dev/kubernetes/staging/src/k8s.io/component-helpers v0.0.0-20241210173536-70835f6af5da + k8s.io/controller-manager => github.com/kcp-dev/kubernetes/staging/src/k8s.io/controller-manager v0.0.0-20241210173536-70835f6af5da + k8s.io/cri-api => github.com/kcp-dev/kubernetes/staging/src/k8s.io/cri-api v0.0.0-20241210173536-70835f6af5da + k8s.io/cri-client => github.com/kcp-dev/kubernetes/staging/src/k8s.io/cri-client v0.0.0-20241210173536-70835f6af5da + k8s.io/csi-translation-lib => github.com/kcp-dev/kubernetes/staging/src/k8s.io/csi-translation-lib v0.0.0-20241210173536-70835f6af5da + k8s.io/dynamic-resource-allocation => github.com/kcp-dev/kubernetes/staging/src/k8s.io/dynamic-resource-allocation v0.0.0-20241210173536-70835f6af5da + k8s.io/endpointslice => github.com/kcp-dev/kubernetes/staging/src/k8s.io/endpointslice v0.0.0-20241210173536-70835f6af5da + k8s.io/kms => github.com/kcp-dev/kubernetes/staging/src/k8s.io/kms v0.0.0-20241210173536-70835f6af5da + k8s.io/kube-aggregator => github.com/kcp-dev/kubernetes/staging/src/k8s.io/kube-aggregator v0.0.0-20241210173536-70835f6af5da + k8s.io/kube-controller-manager => github.com/kcp-dev/kubernetes/staging/src/k8s.io/kube-controller-manager v0.0.0-20241210173536-70835f6af5da + k8s.io/kube-proxy => github.com/kcp-dev/kubernetes/staging/src/k8s.io/kube-proxy v0.0.0-20241210173536-70835f6af5da + k8s.io/kube-scheduler => github.com/kcp-dev/kubernetes/staging/src/k8s.io/kube-scheduler v0.0.0-20241210173536-70835f6af5da + k8s.io/kubectl => github.com/kcp-dev/kubernetes/staging/src/k8s.io/kubectl v0.0.0-20241210173536-70835f6af5da + k8s.io/kubelet => github.com/kcp-dev/kubernetes/staging/src/k8s.io/kubelet v0.0.0-20241210173536-70835f6af5da + k8s.io/kubernetes => github.com/kcp-dev/kubernetes v0.0.0-20241210173536-70835f6af5da + k8s.io/legacy-cloud-providers => github.com/kcp-dev/kubernetes/staging/src/k8s.io/legacy-cloud-providers v0.0.0-20241210173536-70835f6af5da + k8s.io/metrics => github.com/kcp-dev/kubernetes/staging/src/k8s.io/metrics v0.0.0-20241210173536-70835f6af5da + k8s.io/mount-utils => github.com/kcp-dev/kubernetes/staging/src/k8s.io/mount-utils v0.0.0-20241210173536-70835f6af5da + k8s.io/pod-security-admission => github.com/kcp-dev/kubernetes/staging/src/k8s.io/pod-security-admission v0.0.0-20241210173536-70835f6af5da + k8s.io/sample-apiserver => github.com/kcp-dev/kubernetes/staging/src/k8s.io/sample-apiserver v0.0.0-20241210173536-70835f6af5da + k8s.io/sample-cli-plugin => github.com/kcp-dev/kubernetes/staging/src/k8s.io/sample-cli-plugin v0.0.0-20241210173536-70835f6af5da + k8s.io/sample-controller => github.com/kcp-dev/kubernetes/staging/src/k8s.io/sample-controller v0.0.0-20241210173536-70835f6af5da ) diff --git a/go.sum b/go.sum index 9d1927dde4e..6f88281c037 100644 --- a/go.sum +++ b/go.sum @@ -148,50 +148,50 @@ github.com/kcp-dev/apimachinery/v2 v2.0.1-0.20240817110845-a9eb9752bfeb h1:W11F/ github.com/kcp-dev/apimachinery/v2 v2.0.1-0.20240817110845-a9eb9752bfeb/go.mod h1:mEDD1K5BVUXJ4CP6wcJ0vZUf+7tbFMjkCFzBKsUNj18= github.com/kcp-dev/client-go v0.0.0-20240912145314-f5949d81732a h1:O9SNM3MqMlwoEAPSWxk/yw4JU211KpVsAFjTXWQcMEk= github.com/kcp-dev/client-go v0.0.0-20240912145314-f5949d81732a/go.mod h1:h5jC8rEbkyGUgV86+sgtMMcl950ooGzk+iLrQnbCR6o= -github.com/kcp-dev/kubernetes v0.0.0-20240918143026-ab5c3a6448cb h1:Y0q7dUBB2AShwvoHuAb2q/GeCfk5JRnziTy0s/oV+fs= -github.com/kcp-dev/kubernetes v0.0.0-20240918143026-ab5c3a6448cb/go.mod h1:oRHZZzd7fEOSk0mY8fxM++8QPpafz47cbtQUxWENogQ= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/api v0.0.0-20240918143026-ab5c3a6448cb h1:6vSaQJE2W9etXQFdHL9xWDUuzv8f8oTZ3tsXBL9UxMU= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/api v0.0.0-20240918143026-ab5c3a6448cb/go.mod h1:6X07YVZkpyT/6XVz4cwyYM2oYH3A3k2QR54H7JXMD90= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/apiextensions-apiserver v0.0.0-20240918143026-ab5c3a6448cb h1:ZoC/9f3PdmoNg5nezL/QrSP8S/lE+nvYsRHf6/bP450= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/apiextensions-apiserver v0.0.0-20240918143026-ab5c3a6448cb/go.mod h1:8EZw4zqlExmz7lUTE/P7V0vdAyfiYL84i4ZUHY6qyrk= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/apimachinery v0.0.0-20240918143026-ab5c3a6448cb h1:0obIpoEinm9CtUjjPKAZ1vL/c6OrlrlNzUJzjsw3zNE= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/apimachinery v0.0.0-20240918143026-ab5c3a6448cb/go.mod h1:5F0wbie5xX1jDEg5sk5dr+KF8rwFkYtZFHDhSF/UsG4= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/apiserver v0.0.0-20240918143026-ab5c3a6448cb h1:1J6FC8pvCrMeWdnM2bbMZuhHGeeLG+JvQ1uUPnwwlC8= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/apiserver v0.0.0-20240918143026-ab5c3a6448cb/go.mod h1:EC5je+P5ix2QCV4zbwxlY8Zk5MJe4e1eKXxjCMd/7Eo= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/client-go v0.0.0-20240918143026-ab5c3a6448cb h1:yuzQJrRaTpdylN62AQH7IsQth4gf4pSHX/MBcEEcuYw= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/client-go v0.0.0-20240918143026-ab5c3a6448cb/go.mod h1:l7HaB8VBHdNA72/wtAohDsemuLiVNdW6hx9lNB5J088= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/cloud-provider v0.0.0-20240918143026-ab5c3a6448cb h1:iU1IOtSALTD1HRO6w/v1rz2Ia5e8AObchFVVa0KibDY= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/cloud-provider v0.0.0-20240918143026-ab5c3a6448cb/go.mod h1:Z1By/ZJf4qFPOirsblzPEI/p61GMTqgsLITfY2hffQo= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/cluster-bootstrap v0.0.0-20240918143026-ab5c3a6448cb h1:RTNzkTSbn8QTIsPu+dN6AOhEpkdJyCxBg4G8oReXqSk= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/cluster-bootstrap v0.0.0-20240918143026-ab5c3a6448cb/go.mod h1:A1M/anf3QTVOO3C4oma0zIeBekzHJtgO3Ght7QLBOV8= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/code-generator v0.0.0-20240918143026-ab5c3a6448cb h1:tXzwNHi7U49G65ldKyikILcq1Ymni9F8Dho325mrnWw= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/code-generator v0.0.0-20240918143026-ab5c3a6448cb/go.mod h1:FeckrMB5SHLGBJWSRr79xheTG7il5LcGhzdx/v88Jus= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/component-base v0.0.0-20240918143026-ab5c3a6448cb h1:5N/enNWDb3CJ5693LevtUb2sdn6l3FpwYl4H9QtehWQ= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/component-base v0.0.0-20240918143026-ab5c3a6448cb/go.mod h1:pgdjhgz6QeKjIVxzIYq4JFZ7VBJRutg/n5W9OKX33qA= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/component-helpers v0.0.0-20240918143026-ab5c3a6448cb h1:edIODpJNACZOypwxCCsdIq6RjI3b4CnajhHLsSBrWZc= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/component-helpers v0.0.0-20240918143026-ab5c3a6448cb/go.mod h1:Mx6U6lQkq013rHloS+AAq00nd0b3kkI8cuIu+o2SA7g= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/controller-manager v0.0.0-20240918143026-ab5c3a6448cb h1:aImImYvcBW5bikHNI81ZOak9hGa1hrVVNLdVJVwX32U= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/controller-manager v0.0.0-20240918143026-ab5c3a6448cb/go.mod h1:Dc3aSKcPoKmbdnOXAHq/V4Bavxxht9+1bTU8R5ap9yM= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/cri-api v0.0.0-20240918143026-ab5c3a6448cb h1:bok0obA6QA9v5V258so92kdwDrB880VMMuDFlzHdxnk= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/cri-api v0.0.0-20240918143026-ab5c3a6448cb/go.mod h1:e2pTb6psrP2AtdW24SxJaesf2402rQ0YjNa7qYssoi0= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/cri-client v0.0.0-20240918143026-ab5c3a6448cb h1:Q4XwHSQrBqPbJn4kEd/WnzZq2wD1fap/SWr8FymrDCE= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/cri-client v0.0.0-20240918143026-ab5c3a6448cb/go.mod h1:iz2L6DSY4Ur/Bc/gwXkKZlTnkd/97jgITpRAnuZE2jU= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/csi-translation-lib v0.0.0-20240918143026-ab5c3a6448cb h1:Y3NQx3SSq2MnLUTzHl2tvQAqfEGRvAUVGTL82XEO3Ng= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/csi-translation-lib v0.0.0-20240918143026-ab5c3a6448cb/go.mod h1:9M1LHui5W3M9sY3jRBxHpPt30VQ+GdJLgWyGH27/DaI= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/dynamic-resource-allocation v0.0.0-20240918143026-ab5c3a6448cb h1:DuKG7XICOWdL8A4K82RIuRML5gNwQS8XcbssEI0C9rA= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/dynamic-resource-allocation v0.0.0-20240918143026-ab5c3a6448cb/go.mod h1:85Fs9bUmpkY/tUQh6zXjvI5BbN7EfiQYQGj07Zf9Dg0= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/kms v0.0.0-20240918143026-ab5c3a6448cb h1:R9utUkok9aychTtScqCgx3++UgkrFROQLwSviwYlVxU= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/kms v0.0.0-20240918143026-ab5c3a6448cb/go.mod h1:gClzb5q8LLAagWlaL9S/rt8IcU3iY6gRARKN09DY4o8= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/kube-aggregator v0.0.0-20240918143026-ab5c3a6448cb h1:pkKGAfRuMVinPSEMoj9rKGXCS6+tQ+gPuIlLRCpVn04= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/kube-aggregator v0.0.0-20240918143026-ab5c3a6448cb/go.mod h1:npcdPN5G2BMexN5GsPN/xjRN57Ars6h7O7wpjjLJuck= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/kube-controller-manager v0.0.0-20240918143026-ab5c3a6448cb h1:w8MEXqfERavUaJSdARdzK7B/85IKbZGRNnrkG+axy5Q= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/kube-controller-manager v0.0.0-20240918143026-ab5c3a6448cb/go.mod h1:vMjQhLaEOvaFZvq0RLwFPFF2i660WpvyRAYzBLtXByI= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/kubelet v0.0.0-20240918143026-ab5c3a6448cb h1:8obYE0YRPZ15LdG1fryD0fdoXFWmdvkYf7T3aXB+J5s= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/kubelet v0.0.0-20240918143026-ab5c3a6448cb/go.mod h1:W3k9YOX2gYkx8IbOyQ9mTgWjJHqIZ6/2fBVKLQOiW/E= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/mount-utils v0.0.0-20240918143026-ab5c3a6448cb h1:f8WXdnBTGh1nm3zUbqqocKYj+LjNuF3fE1BbyQLtWcQ= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/mount-utils v0.0.0-20240918143026-ab5c3a6448cb/go.mod h1:p5r0u2M9KzooTgHDz4zRsUt02y4Yx7/5uPwgr0nSGqg= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/pod-security-admission v0.0.0-20240918143026-ab5c3a6448cb h1:aRyoWEKViBwukoJgNi3pbolQO/N6Ljb+jLPihjigusk= -github.com/kcp-dev/kubernetes/staging/src/k8s.io/pod-security-admission v0.0.0-20240918143026-ab5c3a6448cb/go.mod h1:kgTU85Q97g45QWn61Zi55b8iDTap0ZcsXxRSlUMt8o4= +github.com/kcp-dev/kubernetes v0.0.0-20241210173536-70835f6af5da h1:ge+uTPSd/rVCFVtmJtbyDgUVIDNKP87c8UAqIJIXLy8= +github.com/kcp-dev/kubernetes v0.0.0-20241210173536-70835f6af5da/go.mod h1:oRHZZzd7fEOSk0mY8fxM++8QPpafz47cbtQUxWENogQ= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/api v0.0.0-20241210173536-70835f6af5da h1:zJm4V/I2DX4vKCEObGwFdHDFYO57h8eLPOa6YP5jHyg= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/api v0.0.0-20241210173536-70835f6af5da/go.mod h1:6X07YVZkpyT/6XVz4cwyYM2oYH3A3k2QR54H7JXMD90= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/apiextensions-apiserver v0.0.0-20241210173536-70835f6af5da h1:MLFm4biDeVf1d8LCOiaNqt2HKk2FTQwMoyY8Op515w4= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/apiextensions-apiserver v0.0.0-20241210173536-70835f6af5da/go.mod h1:8EZw4zqlExmz7lUTE/P7V0vdAyfiYL84i4ZUHY6qyrk= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/apimachinery v0.0.0-20241210173536-70835f6af5da h1:Zn3DzCGIxiUfeZ5YnvNch3CKao5GEy9wr3hyYjj8adg= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/apimachinery v0.0.0-20241210173536-70835f6af5da/go.mod h1:5F0wbie5xX1jDEg5sk5dr+KF8rwFkYtZFHDhSF/UsG4= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/apiserver v0.0.0-20241210173536-70835f6af5da h1:GVkydE8v6TQsI4WtSqY2lS7CdbbkO8XBdqmrttiBYGA= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/apiserver v0.0.0-20241210173536-70835f6af5da/go.mod h1:EC5je+P5ix2QCV4zbwxlY8Zk5MJe4e1eKXxjCMd/7Eo= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/client-go v0.0.0-20241210173536-70835f6af5da h1:s6AZJ+0JMbh6zpBai1l1NJegR3cTOnAT3FiEIkybaD8= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/client-go v0.0.0-20241210173536-70835f6af5da/go.mod h1:l7HaB8VBHdNA72/wtAohDsemuLiVNdW6hx9lNB5J088= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/cloud-provider v0.0.0-20241210173536-70835f6af5da h1:9VzHywk0Kmlj/IckG2oUvB3X9lme6EtPEkQp/CtwUug= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/cloud-provider v0.0.0-20241210173536-70835f6af5da/go.mod h1:Z1By/ZJf4qFPOirsblzPEI/p61GMTqgsLITfY2hffQo= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/cluster-bootstrap v0.0.0-20241210173536-70835f6af5da h1:qDJwqfNei55IqSmNQx6jOXJL+eAT4Bq/YW4k9aGeslo= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/cluster-bootstrap v0.0.0-20241210173536-70835f6af5da/go.mod h1:A1M/anf3QTVOO3C4oma0zIeBekzHJtgO3Ght7QLBOV8= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/code-generator v0.0.0-20241210173536-70835f6af5da h1:xV4OKQexYjHBMMibDOtMuaMSYXIMy2Yt8Jd+bTFCh7U= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/code-generator v0.0.0-20241210173536-70835f6af5da/go.mod h1:FeckrMB5SHLGBJWSRr79xheTG7il5LcGhzdx/v88Jus= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/component-base v0.0.0-20241210173536-70835f6af5da h1:nh29URdqy1lL9QAD/RasPYwkHmN8IXJQa/xcxGDNObc= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/component-base v0.0.0-20241210173536-70835f6af5da/go.mod h1:pgdjhgz6QeKjIVxzIYq4JFZ7VBJRutg/n5W9OKX33qA= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/component-helpers v0.0.0-20241210173536-70835f6af5da h1:b0oi28NgFBhjLf2+zKLf6+tHgJlWQmLVhi2PtpBfrCM= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/component-helpers v0.0.0-20241210173536-70835f6af5da/go.mod h1:Mx6U6lQkq013rHloS+AAq00nd0b3kkI8cuIu+o2SA7g= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/controller-manager v0.0.0-20241210173536-70835f6af5da h1:pHXhfKbxw12rCjjFZMKB9SV00j37/8ct5WmDn59Od7Q= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/controller-manager v0.0.0-20241210173536-70835f6af5da/go.mod h1:Dc3aSKcPoKmbdnOXAHq/V4Bavxxht9+1bTU8R5ap9yM= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/cri-api v0.0.0-20241210173536-70835f6af5da h1:mfehGST8wkWgSVl7E67khNa3LWcPXhL1YirXkB9SVSM= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/cri-api v0.0.0-20241210173536-70835f6af5da/go.mod h1:e2pTb6psrP2AtdW24SxJaesf2402rQ0YjNa7qYssoi0= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/cri-client v0.0.0-20241210173536-70835f6af5da h1:ThtxW1UXbAjNnXIWaWpnJ5Fo5Nww3vM1FY9NLVU/54Y= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/cri-client v0.0.0-20241210173536-70835f6af5da/go.mod h1:iz2L6DSY4Ur/Bc/gwXkKZlTnkd/97jgITpRAnuZE2jU= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/csi-translation-lib v0.0.0-20241210173536-70835f6af5da h1:16nxRzjQ4orUwuUwgp/STGaviJoWHidEWHf8EK/O0YA= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/csi-translation-lib v0.0.0-20241210173536-70835f6af5da/go.mod h1:9M1LHui5W3M9sY3jRBxHpPt30VQ+GdJLgWyGH27/DaI= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/dynamic-resource-allocation v0.0.0-20241210173536-70835f6af5da h1:3Co5IREoa1BarVxkSmC0GF0aTJ7c6ZtMI4PETqm08rM= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/dynamic-resource-allocation v0.0.0-20241210173536-70835f6af5da/go.mod h1:85Fs9bUmpkY/tUQh6zXjvI5BbN7EfiQYQGj07Zf9Dg0= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/kms v0.0.0-20241210173536-70835f6af5da h1:wfqTQkm+JlEiIyDqVQLYDPC5nyt85/+Hu8fpWbEFaiw= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/kms v0.0.0-20241210173536-70835f6af5da/go.mod h1:gClzb5q8LLAagWlaL9S/rt8IcU3iY6gRARKN09DY4o8= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/kube-aggregator v0.0.0-20241210173536-70835f6af5da h1:DlXu3maYuKJrIwddBffoKBAvxE/WBsHANFHDRPIwEZQ= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/kube-aggregator v0.0.0-20241210173536-70835f6af5da/go.mod h1:npcdPN5G2BMexN5GsPN/xjRN57Ars6h7O7wpjjLJuck= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/kube-controller-manager v0.0.0-20241210173536-70835f6af5da h1:DZFOpl3uHtf1+hRJ8EcL5iM8hIdFyaQSSdQo0nycZA8= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/kube-controller-manager v0.0.0-20241210173536-70835f6af5da/go.mod h1:vMjQhLaEOvaFZvq0RLwFPFF2i660WpvyRAYzBLtXByI= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/kubelet v0.0.0-20241210173536-70835f6af5da h1:dQyv4ys8uk1Ag6o6xjXKcdPfRTIjJvxBb5DHMtMpqjA= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/kubelet v0.0.0-20241210173536-70835f6af5da/go.mod h1:W3k9YOX2gYkx8IbOyQ9mTgWjJHqIZ6/2fBVKLQOiW/E= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/mount-utils v0.0.0-20241210173536-70835f6af5da h1:xU3X9wLbmUl3uawxPweoDKnXeVsHvXbqt2Thiy8+PR4= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/mount-utils v0.0.0-20241210173536-70835f6af5da/go.mod h1:p5r0u2M9KzooTgHDz4zRsUt02y4Yx7/5uPwgr0nSGqg= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/pod-security-admission v0.0.0-20241210173536-70835f6af5da h1:zIHhuwIW8m/VzvjZ57KAc8qja3BSgbO9n509skOsvPo= +github.com/kcp-dev/kubernetes/staging/src/k8s.io/pod-security-admission v0.0.0-20241210173536-70835f6af5da/go.mod h1:kgTU85Q97g45QWn61Zi55b8iDTap0ZcsXxRSlUMt8o4= github.com/kcp-dev/logicalcluster/v3 v3.0.5 h1:JbYakokb+5Uinz09oTXomSUJVQsqfxEvU4RyHUYxHOU= github.com/kcp-dev/logicalcluster/v3 v3.0.5/go.mod h1:EWBUBxdr49fUB1cLMO4nOdBWmYifLbP1LfoL20KkXYY= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= diff --git a/pkg/authorization/bootstrap/policy.go b/pkg/authorization/bootstrap/policy.go index 15346b33e17..dadc0dbe013 100644 --- a/pkg/authorization/bootstrap/policy.go +++ b/pkg/authorization/bootstrap/policy.go @@ -19,6 +19,7 @@ package bootstrap import ( rbacv1 "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apiserver/pkg/authentication/user" rbacv1helpers "k8s.io/kubernetes/pkg/apis/rbac/v1" rbacrest "k8s.io/kubernetes/pkg/registry/rbac/rest" "k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy" @@ -46,6 +47,12 @@ const ( SystemKcpWorkspaceAccessGroup = "system:kcp:workspace:access" ) +const ( + // SystemMastersGroup is the group inherited from k8s codebase - all powerful, all knowing! + // Users should not be added to this group. + SystemMastersGroup = user.SystemPrivilegedGroup +) + // ClusterRoleBindings return default rolebindings to the default roles. func clusterRoleBindings() []rbacv1.ClusterRoleBinding { return []rbacv1.ClusterRoleBinding{ diff --git a/pkg/cache/server/config.go b/pkg/cache/server/config.go index cf5445c3351..8dc039c2afb 100644 --- a/pkg/cache/server/config.go +++ b/pkg/cache/server/config.go @@ -136,8 +136,10 @@ func NewConfig(opts *cacheserveroptions.CompletedOptions, optionalLocalShardRest } serverConfig.Config.BuildHandlerChainFunc = func(apiHandler http.Handler, genericConfig *genericapiserver.Config) (secure http.Handler) { - apiHandler = genericapiserver.DefaultBuildHandlerChainFromAuthz(apiHandler, genericConfig) - apiHandler = genericapiserver.DefaultBuildHandlerChainBeforeAuthz(apiHandler, genericConfig) + apiHandler = genericapiserver.DefaultBuildHandlerChainFromAuthzToCompletion(apiHandler, genericConfig) + apiHandler = genericapiserver.DefaultBuildHandlerChainFromImpersonationToAuthz(apiHandler, genericConfig) + apiHandler = genericapiserver.DefaultBuildHandlerChainFromStartToBeforeImpersonation(apiHandler, genericConfig) + apiHandler = filters.WithAuditEventClusterAnnotation(apiHandler) apiHandler = filters.WithClusterScope(apiHandler) apiHandler = WithShardScope(apiHandler) diff --git a/pkg/proxy/config.go b/pkg/proxy/config.go index efb057dfb6d..58a7d7dbc69 100644 --- a/pkg/proxy/config.go +++ b/pkg/proxy/config.go @@ -97,7 +97,7 @@ func NewConfig(ctx context.Context, opts *proxyoptions.Options) (*Config, error) &clientcmd.ClientConfigLoadingRules{ExplicitPath: c.Options.ShardsKubeconfig}, // We override the Server here so that the user doesn't have to specify unused server value // The Server must have HTTPS scheme otherwise CA won't be loaded (see IsConfigTransportTLS method) - &clientcmd.ConfigOverrides{ClusterInfo: clientcmdapi.Cluster{Server: "https://fakeserver.io"}}).ClientConfig() + &clientcmd.ConfigOverrides{ClusterInfo: clientcmdapi.Cluster{Server: "https://kcp.io/fake"}}).ClientConfig() if err != nil { return nil, fmt.Errorf("failed to load shard kubeconfig: %w", err) } diff --git a/pkg/server/config.go b/pkg/server/config.go index 91aa66ec8b9..d03673fe73c 100644 --- a/pkg/server/config.go +++ b/pkg/server/config.go @@ -409,7 +409,7 @@ func NewConfig(opts kcpserveroptions.CompletedOptions) (*Config, error) { authorizerWithoutAudit := genericConfig.Authorization.Authorizer // configure audit logging enabled authorizer chain and build the apiHandler using this configuration. genericConfig.Authorization.Authorizer = authorization.WithAuditLogging("request.auth.kcp.io", genericConfig.Authorization.Authorizer) - apiHandler = genericapiserver.DefaultBuildHandlerChainFromAuthz(apiHandler, genericConfig) + apiHandler = genericapiserver.DefaultBuildHandlerChainFromAuthzToCompletion(apiHandler, genericConfig) // reset authorizer chain with audit logging disabled. genericConfig.Authorization.Authorizer = authorizerWithoutAudit @@ -442,7 +442,9 @@ func NewConfig(opts kcpserveroptions.CompletedOptions) (*Config, error) { apiHandler = WithVirtualWorkspacesProxy(apiHandler, shardVirtualWorkspaceURL, virtualWorkspaceServerProxyTransport, proxy) } - apiHandler = genericapiserver.DefaultBuildHandlerChainBeforeAuthz(apiHandler, genericConfig) + apiHandler = genericapiserver.DefaultBuildHandlerChainFromImpersonationToAuthz(apiHandler, genericConfig) + apiHandler = WithImpersonationGatekeeper(apiHandler) + apiHandler = genericapiserver.DefaultBuildHandlerChainFromStartToBeforeImpersonation(apiHandler, genericConfig) // this will be replaced in DefaultBuildHandlerChain. So at worst we get twice as many warning. // But this is not harmful as the kcp warnings are not many. diff --git a/pkg/server/handler.go b/pkg/server/handler.go index 6c31f15f48f..293b0dc7f58 100644 --- a/pkg/server/handler.go +++ b/pkg/server/handler.go @@ -30,6 +30,7 @@ import ( "github.com/emicklei/go-restful/v3" jwt2 "gopkg.in/square/go-jose.v2/jwt" + authenticationv1 "k8s.io/api/authentication/v1" apiextensionsapiserver "k8s.io/apiextensions-apiserver/pkg/apiserver" "k8s.io/apiextensions-apiserver/pkg/kcp" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -47,6 +48,8 @@ import ( clientgotransport "k8s.io/client-go/transport" "k8s.io/klog/v2" "k8s.io/kubernetes/pkg/controlplane/apiserver/miniaggregator" + + authorizationbootstrap "github.com/kcp-dev/kcp/pkg/authorization/bootstrap" ) var ( @@ -253,6 +256,78 @@ func WithRequestIdentity(handler http.Handler) http.Handler { }) } +type privilege int + +const ( + unprivileged privilege = iota + priviledged + superPrivileged +) + +var ( + // specialGroups specify groups with special meaning kcp. Lower privilege (= lower number) + // cannot impersonate higher privilege levels. + specialGroups = map[string]privilege{ + authorizationbootstrap.SystemMastersGroup: superPrivileged, + authorizationbootstrap.SystemKcpAdminGroup: priviledged, + } +) + +// WithImpersonationGatekeeper checks the request for impersonations and validates them, +// if they are valid. If they are not, will return a 403. +// We check for impersonation in the request headers, early to avoid it being propagated to +// the backend services. +func WithImpersonationGatekeeper(handler http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + // Impersonation check is only done when impersonation is requested. + // And impersonations is only allowed for the users, who have metadata in the ctx. + // Else just pass the request. + impersonationUser := req.Header.Get(authenticationv1.ImpersonateUserHeader) + if len(impersonationUser) == 0 { + handler.ServeHTTP(w, req) + return + } + requester, exists := request.UserFrom(req.Context()) + if !exists { + responsewriters.ErrorNegotiated( + apierrors.NewForbidden(schema.GroupResource{}, "", fmt.Errorf("impersonation is invalid for the requestor")), + errorCodecs, schema.GroupVersion{}, w, req) + return + } + if validImpersonation(requester.GetGroups(), req.Header[authenticationv1.ImpersonateGroupHeader]) { + handler.ServeHTTP(w, req) + return + } + + responsewriters.ErrorNegotiated( + apierrors.NewForbidden(schema.GroupResource{}, "", fmt.Errorf("impersonation is not allowed for the requestor")), + errorCodecs, schema.GroupVersion{}, w, req) + }) +} + +// maxUserPrivilege returns the highest privilege level found among the user's groups. +func maxUserPrivilege(userGroups []string) privilege { + max := unprivileged + for _, g := range userGroups { + if p, found := specialGroups[g]; found && p > max { + max = p + } + } + return max +} + +// validImpersonation checks if a user can impersonate all requested groups. +func validImpersonation(userGroups, requestedGroups []string) bool { + userMax := maxUserPrivilege(userGroups) + + for _, g := range requestedGroups { + if userMax < specialGroups[g] { + return false + } + } + return true +} + func processResourceIdentity(req *http.Request, requestInfo *request.RequestInfo) (*http.Request, error) { if !requestInfo.IsResourceRequest { return req, nil diff --git a/pkg/server/handler_test.go b/pkg/server/handler_test.go index 0924e151a66..c0d662796d2 100644 --- a/pkg/server/handler_test.go +++ b/pkg/server/handler_test.go @@ -25,6 +25,8 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apiserver/pkg/endpoints/request" + + authorizationbootstrap "github.com/kcp-dev/kcp/pkg/authorization/bootstrap" ) func TestProcessResourceIdentity(t *testing.T) { @@ -153,3 +155,79 @@ func TestProcessResourceIdentity(t *testing.T) { }) } } + +func TestCheckImpersonation(t *testing.T) { + var systemUserGroup = "system:user:group" + nonExistingGroup := "non-existing-group" + specialGroups = map[string]privilege{ + authorizationbootstrap.SystemMastersGroup: superPrivileged, + authorizationbootstrap.SystemKcpAdminGroup: priviledged, + systemUserGroup: unprivileged, + } + + tests := []struct { + name string + userGroups []string + requestedGroups []string + expectedResult bool + }{ + { + name: "Single group - allowed", + userGroups: []string{authorizationbootstrap.SystemMastersGroup}, + requestedGroups: []string{authorizationbootstrap.SystemKcpAdminGroup}, + expectedResult: true, + }, + { + name: "Multiple groups - allowed", + userGroups: []string{authorizationbootstrap.SystemMastersGroup}, + requestedGroups: []string{authorizationbootstrap.SystemKcpAdminGroup, systemUserGroup}, + expectedResult: true, + }, + { + name: "Single group - not allowed", + userGroups: []string{authorizationbootstrap.SystemKcpAdminGroup}, + requestedGroups: []string{authorizationbootstrap.SystemMastersGroup}, + expectedResult: false, + }, + { + name: "Multiple groups - mixed permissions", + userGroups: []string{authorizationbootstrap.SystemKcpAdminGroup}, + requestedGroups: []string{authorizationbootstrap.SystemMastersGroup, systemUserGroup}, + expectedResult: false, + }, + { + name: "Multiple groups - lower permissions only", + userGroups: []string{systemUserGroup}, + requestedGroups: []string{authorizationbootstrap.SystemKcpAdminGroup, authorizationbootstrap.SystemMastersGroup}, + expectedResult: false, + }, + { + name: "Empty user groups", + userGroups: []string{}, + requestedGroups: []string{authorizationbootstrap.SystemKcpAdminGroup}, + expectedResult: false, + }, + { + name: "Empty requested groups", + userGroups: []string{authorizationbootstrap.SystemMastersGroup}, + requestedGroups: []string{}, + expectedResult: true, + }, + { + name: "Unknown requested group", + userGroups: []string{authorizationbootstrap.SystemMastersGroup}, + requestedGroups: []string{nonExistingGroup}, + expectedResult: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := validImpersonation(tt.userGroups, tt.requestedGroups) + if result != tt.expectedResult { + t.Errorf("checkImpersonation(%v, %v) = %v; want %v", + tt.userGroups, tt.requestedGroups, result, tt.expectedResult) + } + }) + } +} diff --git a/test/e2e/authorizer/impersonate_test.go b/test/e2e/authorizer/impersonate_test.go new file mode 100644 index 00000000000..b5105d0176b --- /dev/null +++ b/test/e2e/authorizer/impersonate_test.go @@ -0,0 +1,83 @@ +/* +Copyright 2024 The KCP Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package authorizer + +import ( + "context" + "testing" + "time" + + kcpkubernetesclientset "github.com/kcp-dev/client-go/kubernetes" + "github.com/stretchr/testify/require" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/rest" + + tenancyv1alpha1 "github.com/kcp-dev/kcp/sdk/apis/tenancy/v1alpha1" + kcpclientset "github.com/kcp-dev/kcp/sdk/client/clientset/versioned/cluster" + "github.com/kcp-dev/kcp/test/e2e/framework" +) + +func TestImpersonation(t *testing.T) { + t.Parallel() + framework.Suite(t, "control-plane") + + ctx, cancelFn := context.WithCancel(context.Background()) + t.Cleanup(cancelFn) + + server := framework.SharedKcpServer(t) + cfg := server.BaseConfig(t) + + org, _ := framework.NewOrganizationFixture(t, server) + _, wsObj := framework.NewWorkspaceFixture(t, server, org) + + kubeClusterClient, err := kcpkubernetesclientset.NewForConfig(cfg) + require.NoError(t, err) + + t.Log("Make user-1 an admin of the org") + framework.AdmitWorkspaceAccess(ctx, t, kubeClusterClient, org, []string{"user-1"}, []string{"cluster-admin"}, true) + user1Cfg := framework.StaticTokenUserConfig("user-1", cfg) + user1Client, err := kcpclientset.NewForConfig(user1Cfg) + require.NoError(t, err) + + t.Logf("User-1 should not be able to edit workspace status") + var ws *tenancyv1alpha1.Workspace + require.Eventually(t, func() bool { + var err error + ws, err = user1Client.TenancyV1alpha1().Workspaces().Cluster(org).Get(ctx, wsObj.Name, metav1.GetOptions{}) + require.NoError(t, err) + ws.Status.Phase = "Scheduling" + _, err = user1Client.TenancyV1alpha1().Workspaces().Cluster(org).UpdateStatus(ctx, ws, metav1.UpdateOptions{}) + return apierrors.IsForbidden(err) + }, wait.ForeverTestTimeout, time.Millisecond*100, "user-1 should not be able to edit its own workspace status") + + user1Cfg.Impersonate = rest.ImpersonationConfig{ + UserName: "user-1", + Groups: []string{"system:masters"}, + } + user1Client, err = kcpclientset.NewForConfig(user1Cfg) + require.NoError(t, err) + + t.Logf("User-1 should NOT be able to edit workspace status with system:masters impersonation") + require.Eventually(t, func() bool { + ws.Status.Phase = "Scheduling" + _, err = user1Client.TenancyV1alpha1().Workspaces().Cluster(org).UpdateStatus(ctx, ws, metav1.UpdateOptions{}) + return apierrors.IsForbidden(err) + }, wait.ForeverTestTimeout, time.Millisecond*100, "user-1 should NOT be able to edit its own workspace status with impersonation") +}