-
Notifications
You must be signed in to change notification settings - Fork 177
feat(config): deny resources by using RESTMapper as an interceptor #149
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
Conversation
bcf0d30 to
d6289c0
Compare
ardaguclu
left a comment
There was a problem hiding this 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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
|
/lgtm |
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.
1711402 to
9a6ebe8
Compare
| } | ||
|
|
||
| func isNotAllowedError(gvk *schema.GroupVersionKind) error { | ||
| return fmt.Errorf("resource not allowed: %s", gvk.String()) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It just simply sets http 200 statically https://github.com/mark3labs/mcp-go/blob/1eddde7bd69b760f745a1b4064969cffcf97e935/server/streamable_http.go#L347 :(
There was a problem hiding this comment.
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
| return a.delegate.CoreV1().Pods(namespace), nil | ||
| } | ||
|
|
||
| func (a *AccessControlClientset) PodsExec(namespace, name string) (*rest.Request, error) { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
ardaguclu
left a comment
There was a problem hiding this 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 |
There was a problem hiding this comment.
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(). |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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"}). |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
I think it's a good start, but I'm still not fully convinced about the solution. For now, I think we need to keep adding Denied Tests for each new feature we implement. |
|
/lgtm |

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