-
Notifications
You must be signed in to change notification settings - Fork 38
feat: Webhook for CRDs #366
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
ae01d24
Webhook for CRDs
Arvindthiru 4a9844a
working webhook
Arvindthiru 6aa1e01
improve deny logs
Arvindthiru cae2fc9
partial E2E
Arvindthiru 79c6fc6
not working E2E test to not allow user
Arvindthiru 2fa5e81
remove user group e2e
Arvindthiru bd628ef
make reviewable
Arvindthiru a0cc816
add period on comment
Arvindthiru d7e4f03
expand user validation
Arvindthiru 8b9c465
generic handler
Arvindthiru d263567
crd GVK check
Arvindthiru 1670f53
check authenticated group
Arvindthiru 9f4425f
add UTs
Arvindthiru 0f2c184
lint fix
Arvindthiru 4b4794b
fix logic
Arvindthiru fd55d19
fix log
Arvindthiru 2eb3d9a
lint fix
Arvindthiru 36608e6
address comments
Arvindthiru e1768eb
description for allowed
Arvindthiru 0c56a6f
address comments
Arvindthiru 20b4e7c
add comment for group match
Arvindthiru e8ba12e
address comment
Arvindthiru File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| package webhook | ||
|
|
||
| import "go.goms.io/fleet/pkg/webhook/fleetresourcehandler" | ||
|
|
||
| func init() { | ||
| // AddToManagerFuncs is a list of functions to create webhook and add them to a manager. | ||
| AddToManagerFuncs = append(AddToManagerFuncs, fleetresourcehandler.Add) | ||
| } | ||
89 changes: 89 additions & 0 deletions
89
pkg/webhook/fleetresourcehandler/fleetresourcehandler_webhook.go
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| package fleetresourcehandler | ||
|
|
||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "net/http" | ||
| "regexp" | ||
|
|
||
| admissionv1 "k8s.io/api/admission/v1" | ||
| v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" | ||
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
| "k8s.io/klog/v2" | ||
| "sigs.k8s.io/controller-runtime/pkg/client" | ||
| "sigs.k8s.io/controller-runtime/pkg/manager" | ||
| "sigs.k8s.io/controller-runtime/pkg/webhook" | ||
| "sigs.k8s.io/controller-runtime/pkg/webhook/admission" | ||
|
|
||
| "go.goms.io/fleet/pkg/webhook/validation" | ||
| ) | ||
|
|
||
| const ( | ||
| // ValidationPath is the webhook service path which admission requests are routed to for validating custom resource definition resources. | ||
| ValidationPath = "/validate-v1-fleetresourcehandler" | ||
| groupMatch = `^[^.]*\.(.*)` | ||
| crdKind = "CustomResourceDefinition" | ||
| ) | ||
|
|
||
| // Add registers the webhook for K8s bulit-in object types. | ||
| func Add(mgr manager.Manager) error { | ||
| hookServer := mgr.GetWebhookServer() | ||
| hookServer.Register(ValidationPath, &webhook.Admission{Handler: &fleetResourceValidator{Client: mgr.GetClient()}}) | ||
| return nil | ||
| } | ||
|
|
||
| type fleetResourceValidator struct { | ||
| Client client.Client | ||
| decoder *admission.Decoder | ||
| } | ||
|
|
||
| func (v *fleetResourceValidator) Handle(_ context.Context, req admission.Request) admission.Response { | ||
| var response admission.Response | ||
| if req.Operation == admissionv1.Create || req.Operation == admissionv1.Update || req.Operation == admissionv1.Delete { | ||
| switch req.Kind { | ||
| case createCRDGVK(): | ||
| klog.V(2).InfoS("handling CRD resource", "GVK", createCRDGVK()) | ||
| response = v.handleCRD(req) | ||
| default: | ||
| klog.V(2).InfoS("resource is not monitored by fleet resource validator webhook", "GVK", req.Kind.String()) | ||
| response = admission.Allowed(fmt.Sprintf("user: %s in groups: %v is allowed to modify resource with GVK: %s", req.UserInfo.Username, req.UserInfo.Groups, req.Kind.String())) | ||
| } | ||
| } | ||
| return response | ||
| } | ||
|
|
||
| func (v *fleetResourceValidator) handleCRD(req admission.Request) admission.Response { | ||
| var crd v1.CustomResourceDefinition | ||
| if req.Operation == admissionv1.Delete { | ||
| // req.Object is not populated for delete: https://github.com/kubernetes-sigs/controller-runtime/issues/1762. | ||
| if err := v.decoder.DecodeRaw(req.OldObject, &crd); err != nil { | ||
| klog.ErrorS(err, "failed to decode old request object for delete operation", "userName", req.UserInfo.Username, "groups", req.UserInfo.Groups) | ||
| return admission.Errored(http.StatusBadRequest, err) | ||
| } | ||
| } else { | ||
| if err := v.decoder.Decode(req, &crd); err != nil { | ||
| klog.ErrorS(err, "failed to decode request object for create/update operation", "userName", req.UserInfo.Username, "groups", req.UserInfo.Groups) | ||
| return admission.Errored(http.StatusBadRequest, err) | ||
| } | ||
| } | ||
|
|
||
| // This regex works because every CRD name in kubernetes follows this pattern <plural>.<group>. | ||
| group := regexp.MustCompile(groupMatch).FindStringSubmatch(crd.Name)[1] | ||
Arvindthiru marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if validation.CheckCRDGroup(group) && !validation.ValidateUserForCRD(req.UserInfo) { | ||
| return admission.Denied(fmt.Sprintf("failed to validate user: %s in groups: %v to modify fleet CRD: %s", req.UserInfo.Username, req.UserInfo.Groups, crd.Name)) | ||
| } | ||
| return admission.Allowed(fmt.Sprintf("user: %s in groups: %v is allowed to modify CRD: %s", req.UserInfo.Username, req.UserInfo.Groups, crd.Name)) | ||
| } | ||
|
|
||
| func createCRDGVK() metav1.GroupVersionKind { | ||
| return metav1.GroupVersionKind{ | ||
| Group: v1.SchemeGroupVersion.Group, | ||
| Version: v1.SchemeGroupVersion.Version, | ||
| Kind: crdKind, | ||
| } | ||
| } | ||
|
|
||
| func (v *fleetResourceValidator) InjectDecoder(d *admission.Decoder) error { | ||
| v.decoder = d | ||
| return nil | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| package validation | ||
|
|
||
| import "k8s.io/utils/strings/slices" | ||
|
|
||
| var ( | ||
| validObjectGroups = []string{"networking.fleet.azure.com", "fleet.azure.com", "multicluster.x-k8s.io", "placement.karavel.io"} | ||
| ) | ||
|
|
||
| // CheckCRDGroup checks to see if the input CRD group is a fleet CRD group. | ||
| func CheckCRDGroup(group string) bool { | ||
michaelawyu marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| return slices.Contains(validObjectGroups, group) | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| package validation | ||
|
|
||
| import ( | ||
| authenticationv1 "k8s.io/api/authentication/v1" | ||
| "k8s.io/utils/strings/slices" | ||
| ) | ||
|
|
||
| const ( | ||
| mastersGroup = "system:masters" | ||
| ) | ||
|
|
||
| // TODO:(Arvindthiru) Get valid usernames as flag and allow those usernames. | ||
|
|
||
| // ValidateUserForCRD checks to see if user is authenticated to make a request to modify fleet CRDs. | ||
| func ValidateUserForCRD(userInfo authenticationv1.UserInfo) bool { | ||
| return slices.Contains(userInfo.Groups, mastersGroup) | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| package validation | ||
|
|
||
| import ( | ||
| "context" | ||
| "testing" | ||
|
|
||
| "github.com/crossplane/crossplane-runtime/pkg/test" | ||
| "github.com/stretchr/testify/assert" | ||
| v1 "k8s.io/api/authentication/v1" | ||
| "sigs.k8s.io/controller-runtime/pkg/client" | ||
|
|
||
| "go.goms.io/fleet/pkg/utils" | ||
| ) | ||
|
|
||
| func TestValidateUserForCRD(t *testing.T) { | ||
| testCases := map[string]struct { | ||
| client client.Client | ||
| userInfo v1.UserInfo | ||
| wantResult bool | ||
| }{ | ||
| "allow user in system:masters group": { | ||
| client: &test.MockClient{ | ||
| MockList: func(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error { | ||
| return nil | ||
| }, | ||
| }, | ||
| userInfo: v1.UserInfo{ | ||
| Username: "test-user", | ||
| Groups: []string{"system:masters"}, | ||
| }, | ||
| wantResult: true, | ||
| }, | ||
| "fail to validate user with invalid username, groups": { | ||
| client: &test.MockClient{ | ||
| MockList: func(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error { | ||
| return nil | ||
| }, | ||
| }, | ||
| userInfo: v1.UserInfo{ | ||
| Username: "test-user", | ||
| Groups: []string{"test-group"}, | ||
| }, | ||
| wantResult: false, | ||
| }, | ||
| } | ||
|
|
||
| for testName, testCase := range testCases { | ||
| t.Run(testName, func(t *testing.T) { | ||
| gotResult := ValidateUserForCRD(testCase.userInfo) | ||
| assert.Equal(t, testCase.wantResult, gotResult, utils.TestCaseMsg, testName) | ||
| }) | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.