Skip to content

Conversation

@manusa
Copy link
Member

@manusa manusa commented Jun 23, 2025

Fixes #132

This approach ensures that resources in the deny list are always processed regardless of the implementation.

The RESTMapper takes care of verifying that the requested Group Version Kind complies with the deny list while checking for the REST endpoint.

/cc @ardaguclu

@manusa manusa force-pushed the feat/access-control-rest-mapper branch from bcf0d30 to d6289c0 Compare June 23, 2025 13:53
@manusa manusa added this to the 0.1.0 milestone Jun 25, 2025 — with automated-tasks
Copy link
Member

@ardaguclu ardaguclu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change looks nice. Dropped a few comments.


type AccessControlRESTMapper struct {
delegate *restmapper.DeferredDiscoveryRESTMapper
staticConfig *config.StaticConfig // TODO: maybe just store the denied resource slice
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my opinion, we can pass staticConfig in here instead of denied resource slice. Maybe in the future, we want to extend this based on allowed resources similar to denied resources.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think, I'm leaning towards your TODO comment. We can pass only denyList instead of an entire staticConfig (as it includes different type of resources)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's why I left a TODO comment, I think that we can leave it and reconsider it once we have more pieces of the puzzle.

// isAllowed checks the resource is in denied list or not.
// If it is in denied list, this function returns false.
func (a AccessControlRESTMapper) isAllowed(gvk *schema.GroupVersionKind) bool {
if a.staticConfig == nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic is true. But if staticConfig is accidentally passed as nil, this would mean we allow all resources. Just brainstorming, would it be too disruptive if we return false, if staticConfig is nil? or can we return error in NewAccessControlRESTMapper if staticConfig is nil?. WDYT?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: the method is the same as the original method, I just moved it here.

It's very likely that this method will be deeply refactored in the future.

Currently, staticConfig can be nil (in fact will be nil unless a config is explicitly loaded.

If we go with #151 (comment) or similar, then probably staticConfig will always have a value and we can probably change this.

@ardaguclu
Copy link
Member

/lgtm

manusa added 4 commits July 1, 2025 05:53
This approach ensures that resources in the deny list are **always**
processed regardless of the implementation.

The RESTMapper takes care of verifying that the requested Group Version Kind
complies with the deny list while checking for the REST endpoint.
@manusa manusa force-pushed the feat/access-control-rest-mapper branch from 1711402 to 9a6ebe8 Compare July 1, 2025 05:27
@manusa manusa marked this pull request as ready for review July 1, 2025 05:27
@manusa manusa requested a review from ardaguclu July 1, 2025 05:27
}

func isNotAllowedError(gvk *schema.GroupVersionKind) error {
return fmt.Errorf("resource not allowed: %s", gvk.String())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(not a blocker for this PR):
We have to return 403: Forbidden HTTP Status. But I'm not really sure that mcp library we use support this. If it doesn't support returning http code we have, we'll have major issue in oauth side as well.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I don't think the library (or even the protocol) is prepared for fine-grained operation status codes yet.
As I understand it, the protocol negotiates authentication and authorization first, and then enables

image

return a.delegate.CoreV1().Pods(namespace), nil
}

func (a *AccessControlClientset) PodsExec(namespace, name string) (*rest.Request, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about moving createExecutor also in here and this function returns remotecommand.Executor?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be done now, Schema and ParameterCodec are now reused from client-go globals

@manusa manusa requested a review from ardaguclu July 1, 2025 12:14
Copy link
Member

@ardaguclu ardaguclu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really like the way of how this PR handles the problem. Dropped a few non-blocker comments. Other than that this looks good to me.

response, err := k.manager.clientSet.AuthorizationV1().SelfSubjectAccessReviews().Create(ctx, &authv1.SelfSubjectAccessReview{
accessReviews, err := k.manager.accessControlClientSet.SelfSubjectAccessReviews()
if err != nil {
return false
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

very nice!

url = append(url, gvr.Resource)
var table metav1.Table
err := k.manager.clientSet.CoreV1().RESTClient().
err := k.manager.discoveryClient.RESTClient().
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think, now we support all the resources

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what you mean with this.
AFAIU, before all resources were supported too, this was/is just a lazy way to pass share the RESTClient.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NVM

"application/json",
}, ",")).
AbsPath(url...).
SpecificallyVersionedParams(&options.ListOptions, k.manager.parameterCodec, schema.GroupVersion{Version: "v1"}).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't want to block this PR for this. So we can handle it in a subsequent PR. Can we now safely remove parameterCodec from manager? or we still require it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

parameterCodec should no longer be in Manager.

CloseWatchKubeConfig CloseWatchKubeConfig
}

var Scheme = scheme.Scheme
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(not a blocker for this PR)
It is unfortunate that we are tied to scheme.Scheme (as cluster is a dynamic environment, we'll miss some new resources that is not defined in our static scheme). But I understand the reason of this. I'd recommend moving this to its own scheme.go file.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's merge this in first and reiterate on that.
In this case, I just needed a quick way to access the Scheme and ParameterCodec. I recalled you mentioned about using a global Scheme at some other PR so went on this direction.

@manusa
Copy link
Member Author

manusa commented Jul 1, 2025

I really like the way of how this PR handles the problem

I think it's a good start, but I'm still not fully convinced about the solution.
I like the RESTMapper because it ensures the access control is verified for absolutely each request.
However, this is not possible for the ClientSets. The only way I envisioned to do this was by using a RoundTripper, but it added a lot of complexity and didn't cover the problem completely.

For now, I think we need to keep adding Denied Tests for each new feature we implement.

@ardaguclu
Copy link
Member

/lgtm

@manusa manusa merged commit af2a8cd into main Jul 1, 2025
5 checks passed
@manusa manusa deleted the feat/access-control-rest-mapper branch July 1, 2025 12:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow/Deny resources list

3 participants