Skip to content

Commit

Permalink
allow fleet networking object
Browse files Browse the repository at this point in the history
  • Loading branch information
Ryan Zhang committed Oct 20, 2023
1 parent cb7ac59 commit f165ec2
Show file tree
Hide file tree
Showing 8 changed files with 333 additions and 28 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ require (
github.com/spf13/cobra v1.7.0
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.8.4
go.goms.io/fleet-networking v0.2.7
go.uber.org/atomic v1.11.0
go.uber.org/zap v1.24.0
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,8 @@ github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQ
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.goms.io/fleet-networking v0.2.7 h1:lVs2/GiCjo18BRgACib+VPnENUMh+2YbYXoeNtcAvw0=
go.goms.io/fleet-networking v0.2.7/go.mod h1:JoWG82La5nV29mooOnPpIhy6/Pi4oGXQk21CPF1UStg=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
Expand Down
33 changes: 19 additions & 14 deletions pkg/webhook/fleetresourcehandler/fleetresourcehandler_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"regexp"
"strings"

fleetnetworkingv1alpha1 "go.goms.io/fleet-networking/api/v1alpha1"
admissionv1 "k8s.io/api/admission/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
Expand All @@ -29,7 +30,6 @@ const (
fleetMemberNamespacePrefix = "fleet-member"
fleetNamespacePrefix = "fleet"
kubeNamespacePrefix = "kube"
handleResourceFmt = "handling %s resource"
)

// Add registers the webhook for K8s built-in object types.
Expand All @@ -47,37 +47,36 @@ type fleetResourceValidator struct {

// Handle receives the request then allows/denies the request to modify fleet resources.
func (v *fleetResourceValidator) Handle(ctx context.Context, req admission.Request) admission.Response {
// special case for Kind:Namespace resources req.Name and req.Namespace has the same value the ObjectMeta.Name of Namespace.
if req.Kind.Kind == "Namespace" {
req.Namespace = ""
}
namespacedName := types.NamespacedName{Name: req.Name, Namespace: req.Namespace}
var response admission.Response
if req.Operation == admissionv1.Create || req.Operation == admissionv1.Update || req.Operation == admissionv1.Delete {
switch {
case req.Kind == validation.CRDGVK:
klog.V(2).InfoS("handling CRD resource", "GVK", validation.CRDGVK, "namespacedName", namespacedName, "operation", req.Operation, "subResource", req.SubResource)
klog.V(2).InfoS("handling CRD resource", "name", req.Name, "operation", req.Operation, "subResource", req.SubResource)
response = v.handleCRD(req)
case req.Kind == validation.V1Alpha1MCGVK:
klog.V(2).InfoS("handling v1alpha1 member cluster resource", "GVK", validation.V1Alpha1MCGVK, "namespacedName", namespacedName, "operation", req.Operation, "subResource", req.SubResource)
klog.V(2).InfoS("handling v1alpha1 member cluster resource", "name", req.Name, "operation", req.Operation, "subResource", req.SubResource)
response = v.handleV1Alpha1MemberCluster(req)
case req.Kind == validation.MCGVK:
klog.V(2).InfoS("handling member cluster resource", "GVK", validation.MCGVK, "namespacedName", namespacedName, "operation", req.Operation, "subResource", req.SubResource)
klog.V(2).InfoS("handling member cluster resource", "name", req.Name, "operation", req.Operation, "subResource", req.SubResource)
response = v.handleMemberCluster(req)
case req.Kind == validation.NamespaceGVK:
klog.V(2).InfoS("handling namespace resource", "GVK", validation.NamespaceGVK, "namespacedName", namespacedName, "operation", req.Operation, "subResource", req.SubResource)
klog.V(2).InfoS("handling namespace resource", "name", req.Name, "operation", req.Operation, "subResource", req.SubResource)
response = v.handleNamespace(req)
case req.Kind == validation.V1Alpha1IMCGVK || req.Kind == validation.V1Alpha1WorkGVK || req.Kind == validation.IMCGVK || req.Kind == validation.WorkGVK:
klog.V(2).InfoS(fmt.Sprintf(handleResourceFmt, req.RequestKind.Kind), "GVK", req.RequestKind, "namespacedName", namespacedName, "operation", req.Operation, "subResource", req.SubResource)
klog.V(2).InfoS("handling fleet owned namespaced resource in system namespace", "GVK", req.RequestKind, "namespacedName", namespacedName, "operation", req.Operation, "subResource", req.SubResource)
response = v.handleFleetMemberNamespacedResource(ctx, req)
case req.Kind == validation.EventGVK:
klog.V(2).InfoS("handling event resource", "GVK", validation.EventGVK, "namespacedName", namespacedName, "operation", req.Operation, "subResource", req.SubResource)
klog.V(3).InfoS("handling event resource", "namespacedName", namespacedName, "operation", req.Operation, "subResource", req.SubResource)
response = v.handleEvent(ctx, req)
case req.Kind.Group == fleetnetworkingv1alpha1.GroupVersion.Group && req.Kind.Version == fleetnetworkingv1alpha1.GroupVersion.Version:
klog.V(2).InfoS("handling fleet networking resource", "GVK", req.RequestKind, "namespacedName", namespacedName, "operation", req.Operation, "subResource", req.SubResource)
response = v.handleFleetNetworkingResources(req)
case req.Namespace != "":
klog.V(2).InfoS(fmt.Sprintf(handleResourceFmt, req.RequestKind.Kind), "GVK", req.RequestKind, "namespacedName", namespacedName, "operation", req.Operation, "subResource", req.SubResource)
klog.V(2).InfoS("handling namespaced resource created in system namespace", "GVK", req.RequestKind, "namespacedName", namespacedName, "operation", req.Operation, "subResource", req.SubResource)
response = validation.ValidateUserForResource(req, v.whiteListedUsers)
default:
klog.V(2).InfoS("resource is not monitored by fleet resource validator webhook", "GVK", req.RequestKind, "namespacedName", namespacedName, "operation", req.Operation, "subResource", req.SubResource)
klog.V(3).InfoS("resource is not monitored by fleet resource validator webhook", "GVK", req.RequestKind, "namespacedName", namespacedName, "operation", req.Operation, "subResource", req.SubResource)
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()))
}
}
Expand Down Expand Up @@ -141,7 +140,7 @@ func (v *fleetResourceValidator) handleFleetMemberNamespacedResource(ctx context
}
return response
}
klog.InfoS("namespace name doesn't begin with fleet-member prefix so we allow all operations on these namespaces",
klog.V(3).InfoS("namespace name doesn't begin with fleet-member prefix so we allow all operations on these namespaces",
"user", req.UserInfo.Username, "groups", req.UserInfo.Groups, "operation", req.Operation, "kind", req.RequestKind.Kind, "subResource", req.SubResource, "namespacedName", types.NamespacedName{Name: req.Name, Namespace: req.Namespace})
return admission.Allowed("namespace name doesn't begin with fleet-member prefix so we allow all operations on these namespaces for the request object")
}
Expand All @@ -152,6 +151,12 @@ func (v *fleetResourceValidator) handleEvent(_ context.Context, _ admission.Requ
return admission.Allowed("all events are allowed")
}

// handleFleetNetworkingResources allows requests to modify fleet networking resources.
func (v *fleetResourceValidator) handleFleetNetworkingResources(_ admission.Request) admission.Response {
// TODO: add more check on the fleet networking resources.
return admission.Allowed("requests for fleet networking resources are all allowed")
}

// handlerNamespace allows/denies request to modify namespace after validation.
func (v *fleetResourceValidator) handleNamespace(req admission.Request) admission.Response {
fleetMatchResult := strings.HasPrefix(req.Name, fleetNamespacePrefix)
Expand Down
12 changes: 6 additions & 6 deletions pkg/webhook/validation/uservalidation.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func ValidateUserForFleetCRD(req admission.Request, whiteListedUsers []string, g
klog.V(2).InfoS(deniedModifyResource, "user", userInfo.Username, "groups", userInfo.Groups, "operation", req.Operation, "GVK", req.RequestKind, "subResource", req.SubResource, "namespacedName", namespacedName)
return admission.Denied(fmt.Sprintf(resourceDeniedFormat, userInfo.Username, userInfo.Groups, req.Operation, req.RequestKind, req.SubResource, namespacedName))
}
klog.V(2).InfoS(allowedModifyResource, "user", userInfo.Username, "groups", userInfo.Groups, "operation", req.Operation, "GVK", req.RequestKind, "subResource", req.SubResource, "namespacedName", namespacedName)
klog.V(3).InfoS(allowedModifyResource, "user", userInfo.Username, "groups", userInfo.Groups, "operation", req.Operation, "GVK", req.RequestKind, "subResource", req.SubResource, "namespacedName", namespacedName)
return admission.Allowed(fmt.Sprintf(resourceAllowedFormat, userInfo.Username, userInfo.Groups, req.Operation, req.RequestKind, req.SubResource, namespacedName))
}

Expand All @@ -69,7 +69,7 @@ func ValidateUserForResource(req admission.Request, whiteListedUsers []string) a
namespacedName := types.NamespacedName{Name: req.Name, Namespace: req.Namespace}
userInfo := req.UserInfo
if isMasterGroupUserOrWhiteListedUser(whiteListedUsers, userInfo) || isUserAuthenticatedServiceAccount(userInfo) || isUserKubeScheduler(userInfo) || isUserKubeControllerManager(userInfo) || isNodeGroupUser(userInfo) {
klog.V(2).InfoS(allowedModifyResource, "user", userInfo.Username, "groups", userInfo.Groups, "operation", req.Operation, "GVK", req.RequestKind, "subResource", req.SubResource, "namespacedName", namespacedName)
klog.V(3).InfoS(allowedModifyResource, "user", userInfo.Username, "groups", userInfo.Groups, "operation", req.Operation, "GVK", req.RequestKind, "subResource", req.SubResource, "namespacedName", namespacedName)
return admission.Allowed(fmt.Sprintf(resourceAllowedFormat, userInfo.Username, userInfo.Groups, req.Operation, req.RequestKind, req.SubResource, namespacedName))
}
klog.V(2).InfoS(deniedModifyResource, "user", userInfo.Username, "groups", userInfo.Groups, "operation", req.Operation, "GVK", req.RequestKind, "subResource", req.SubResource, "namespacedName", namespacedName)
Expand All @@ -89,7 +89,7 @@ func ValidateMemberClusterUpdate(currentObj, oldObj client.Object, req admission
}
if (isLabelUpdated || isAnnotationUpdated) && !isObjUpdated {
// we allow any user to modify MemberCluster/Namespace labels/annotations.
klog.V(2).InfoS("user in groups is allowed to modify member cluster labels/annotations", "user", userInfo.Username, "groups", userInfo.Groups, "operation", req.Operation, "GVK", req.RequestKind, "subResource", req.SubResource, "namespacedName", namespacedName)
klog.V(3).InfoS("user in groups is allowed to modify member cluster labels/annotations", "user", userInfo.Username, "groups", userInfo.Groups, "operation", req.Operation, "GVK", req.RequestKind, "subResource", req.SubResource, "namespacedName", namespacedName)
response = admission.Allowed(fmt.Sprintf(resourceAllowedFormat, userInfo.Username, userInfo.Groups, req.Operation, req.RequestKind, req.SubResource, namespacedName))
}
if isObjUpdated {
Expand Down Expand Up @@ -184,7 +184,7 @@ func ValidateMCIdentity(ctx context.Context, client client.Client, req admission
var mc fleetv1alpha1.MemberCluster
if err := client.Get(ctx, types.NamespacedName{Name: mcName}, &mc); err != nil {
// fail open, if the webhook cannot get member cluster resources we don't block the request.
klog.V(2).ErrorS(err, fmt.Sprintf("failed to get v1alpha1 member cluster resource for request to modify %+v/%s, allowing request to be handled by api server", req.RequestKind, req.SubResource),
klog.ErrorS(err, fmt.Sprintf("failed to get v1alpha1 member cluster resource for request to modify %+v/%s, allowing request to be handled by api server", req.RequestKind, req.SubResource),
"user", userInfo.Username, "groups", userInfo.Groups, "namespacedName", namespacedName)
return admission.Allowed(fmt.Sprintf(resourceAllowedGetMCFailed, userInfo.Username, userInfo.Groups, req.Operation, req.RequestKind, req.SubResource, namespacedName))
}
Expand All @@ -193,7 +193,7 @@ func ValidateMCIdentity(ctx context.Context, client client.Client, req admission
var mc clusterv1beta1.MemberCluster
if err := client.Get(ctx, types.NamespacedName{Name: mcName}, &mc); err != nil {
// fail open, if the webhook cannot get member cluster resources we don't block the request.
klog.V(2).ErrorS(err, fmt.Sprintf("failed to get member cluster resource for request to modify %+v/%s, allowing request to be handled by api server", req.RequestKind, req.SubResource),
klog.ErrorS(err, fmt.Sprintf("failed to get member cluster resource for request to modify %+v/%s, allowing request to be handled by api server", req.RequestKind, req.SubResource),
"user", userInfo.Username, "groups", userInfo.Groups, "namespacedName", namespacedName)
return admission.Allowed(fmt.Sprintf(resourceAllowedGetMCFailed, userInfo.Username, userInfo.Groups, req.Operation, req.RequestKind, req.SubResource, namespacedName))
}
Expand All @@ -202,7 +202,7 @@ func ValidateMCIdentity(ctx context.Context, client client.Client, req admission

// For the upstream E2E we use hub agent service account's token which allows member agent to modify Work status, hence we use serviceAccountFmt to make the check.
if identity == userInfo.Username || fmt.Sprintf(serviceAccountFmt, identity) == userInfo.Username {
klog.V(2).InfoS(allowedModifyResource, "user", userInfo.Username, "groups", userInfo.Groups, "operation", req.Operation, "GVK", req.RequestKind, "subResource", req.SubResource, "namespacedName", namespacedName)
klog.V(3).InfoS(allowedModifyResource, "user", userInfo.Username, "groups", userInfo.Groups, "operation", req.Operation, "GVK", req.RequestKind, "subResource", req.SubResource, "namespacedName", namespacedName)
return admission.Allowed(fmt.Sprintf(resourceAllowedFormat, userInfo.Username, userInfo.Groups, req.Operation, req.RequestKind, req.SubResource, namespacedName))
}
klog.V(2).InfoS(deniedModifyResource, "user", userInfo.Username, "groups", userInfo.Groups, "operation", req.Operation, "GVK", req.RequestKind, "subResource", req.SubResource, "namespacedName", namespacedName)
Expand Down
2 changes: 1 addition & 1 deletion test/e2e/v1alpha1/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ test suites, follow the steps below:
5. Run the tests:

```shell
make run-e2e
make run-e2e-v1alpha1
```

## Access the `Kind` clusters
Expand Down
2 changes: 2 additions & 0 deletions test/e2e/v1alpha1/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
workv1alpha1 "sigs.k8s.io/work-api/pkg/apis/v1alpha1"

fleetnetworkingv1alpha1 "go.goms.io/fleet-networking/api/v1alpha1"
"go.goms.io/fleet/apis/v1alpha1"
"go.goms.io/fleet/pkg/utils"
"go.goms.io/fleet/test/e2e/framework"
Expand Down Expand Up @@ -161,6 +162,7 @@ func init() {
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
utilruntime.Must(v1alpha1.AddToScheme(scheme))
utilruntime.Must(workv1alpha1.AddToScheme(scheme))
utilruntime.Must(fleetnetworkingv1alpha1.AddToScheme(scheme))
utilruntime.Must(apiextensionsv1.AddToScheme(scheme))
}

Expand Down
Loading

0 comments on commit f165ec2

Please sign in to comment.