-
Notifications
You must be signed in to change notification settings - Fork 37
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
KEP: Multi-cluster API gateway #25
Open
yue9944882
wants to merge
1
commit into
open-cluster-management-io:main
Choose a base branch
from
yue9944882:kep-api-gateway
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
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
189 changes: 189 additions & 0 deletions
189
enhancements/sig-architecture/21-cluster-gateway/README.md
This file contains 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,189 @@ | ||
# Multi-Cluster API Gateway | ||
|
||
## Release Signoff Checklist | ||
|
||
- [ ] Enhancement is `implementable` | ||
- [ ] Design details are appropriately documented from clear requirements | ||
- [ ] Test plan is defined | ||
- [ ] Graduation criteria for dev preview, tech preview, GA | ||
- [ ] User-facing documentation is created in [website](https://github.com/open-cluster-management-io/open-cluster-management-io.github.io/) | ||
|
||
## Summary | ||
|
||
After solving the issues of (1) hub -> spoke network connectivity [#19](https://github.com/open-cluster-management-io/enhancements/pull/19) | ||
and (2) hub -> spoke authentication via token projection [#24](https://github.com/open-cluster-management-io/enhancements/pull/24) | ||
the last missing puzzle for dynamic multi-cluster API invocation will be an | ||
API gateway for helping switching contexts between clusters. This API gateway | ||
will be proxying the requests to any managed cluster similar to the mechanism | ||
of existing `serivce/proxy` or `pod/proxy`, and the gateway is supposed to be | ||
plugging into the kubernetes cluster as an aggregated apiserver so that the | ||
hub cluster can discover a new proxy subresource named `clustergateways/proxy`. | ||
|
||
## Motivation | ||
|
||
### Dynamically Switching between Managed Clusters | ||
|
||
An old-fashion approach of reading resources from multiple managed clusters | ||
will be looping over the clusters and instantiate new client instances then | ||
send the actual requests. While w/ the help of gateway, switching client context | ||
between the managed clusters will be as simple as replacing the target resource | ||
name of the `clustergateway` resource, e.g. for accessing a cluster named `foo`, | ||
all the client required to do is simply requesting the `proxy` subresource for | ||
the new resource `clustergateway` named `foo`. Additionally, accessing arbitrary | ||
non-resource paths will also be supported, e.g. for probing the overall health | ||
of the managed cluster, we wil be call an API path of `/apis/open-cluster-management.io/clustergateways/foo/proxy/healthz` | ||
against the hub kube-apiserver. Note that the API gateway is supposed to use the | ||
projected service-account token [#24](https://github.com/open-cluster-management-io/enhancements/pull/24) | ||
to identify itself when proxying requests to the managed cluster's kube-apiserver. | ||
|
||
### Keep KubeConfig/Tokens Invisible From Clients | ||
|
||
In most cases, we hope to grant the accessibility of managed clusters' | ||
kube-apiserver to hub components w/o exposing the plain tokens. For the gateway, | ||
it can be easily done by restricting the proxy clients RBAC rights to the | ||
`clustergateways/proxy` subresource, also the verbs can be tightened so that we | ||
can for example ban some proxy clients from deleting or listing resources. | ||
To secure the tokens from the both the gateway users and the other tenants | ||
sharing the hub cluster, all the projected tokens are supposed to be maintained | ||
under a single high-secured namespace named `managed-cluster-credentials`. | ||
|
||
### Network Connectivity | ||
|
||
If the managed cluster is running in an isolated network, we will have to rely on | ||
the work proposed by KEP [#19](https://github.com/open-cluster-management-io/enhancements/pull/19) | ||
which establishes reverse proxy tunnels between the hub and spoke clusters so the | ||
those requests proxied by the gateway can finally reach the managed cluster's | ||
apiserver. | ||
|
||
## Goals | ||
|
||
- Having a new resource `clustergateway` and its subresource `proxy` based on | ||
apiserver aggregation. | ||
- Adding a new optional component named `cluster-gateway` for delegating requests | ||
to the spoke clusters. | ||
|
||
## Proposal | ||
|
||
An initial POC implementation now goes publicly available under the repo [cluster-gateway](https://github.com/oam-dev/cluster-gateway). | ||
|
||
|
||
### API | ||
|
||
By installing the gateway to the hub cluster via applying an `APIService`, the new | ||
resource should be visible by the `kubectl api-resources` command. A sample will | ||
be: | ||
|
||
```yaml | ||
apiVersion: "proxy.open-cluster-management.io/v1alpha1" | ||
kind: "ClusterGateway" | ||
metadata: | ||
name: <..> | ||
spec: | ||
provider: "" | ||
access: | ||
endpoint: "https://127.0.0.1:9443" | ||
caBundle: "..." | ||
credential: | ||
type: [X509Certificate|ServiceAccountToken|ManagedServiceAccount] | ||
x509Certificate: | ||
certificate: "..." | ||
privateKey: "..." | ||
serviceAccountToken: | ||
token: "..." | ||
managedServiceAccount: | ||
namespace: ".." | ||
name: "..." | ||
status: { } | ||
``` | ||
|
||
Note that the credential being used by the gateway can be from 3 diffrent | ||
sources: | ||
|
||
- X509Certificate: PEM encoded certificates and keys. | ||
- ServiceAccountToken: The JWT token signed and copied from the managed cluster. | ||
- ManagedServiceAccount: Referencing an existing managed service-account, and read | ||
the corresponding projected tokens to authenticate the proxying requests. | ||
|
||
### Client Usage | ||
|
||
|
||
There can be two major kinds of clients using the api gateway: | ||
|
||
- Kubectl Users: In some cases, the hub administrators need a one-time read/write | ||
to a managed cluster e.g. when trouble-shooting or operating certain cluster. The | ||
only thing we will need to do to make the kubectl works with the gatewa is just | ||
simply adding a suffix `/apis/open-cluster-management.io/clustergateways/<cluster name>/proxy/` | ||
to the original hub kubeconfig, this will magically turn the hub kubeconfig to a | ||
kubeconfig dedicated for any managed cluster. | ||
|
||
- Hub System: Components in hub cluster can also do the similar things as appending | ||
a suffix to the request manually. Also we will be providing a set of client library | ||
by "client expansion" to make the integration easier: | ||
|
||
A sample of the client library's interface will be: | ||
|
||
```go | ||
type ClusterGatewayExpansion interface { | ||
RESTClient(clusterName string) rest.Interface | ||
|
||
GetKubernetesClient(clusterName string) kubernetes.Interface | ||
GetControllerRuntimeClient(clusterName string, options client.Options) (client.Client, error) | ||
|
||
RoundTripperForCluster(clusterName string) http.RoundTripper | ||
RoundTripperForClusterFromContext() http.RoundTripper | ||
RoundTripperForClusterFromContextWrapper(http.RoundTripper) http.RoundTripper | ||
} | ||
// See also: https://github.com/oam-dev/cluster-gateway/blob/master/pkg/generated/clientset/versioned/typed/cluster/v1alpha1/clustergateway_expansion.go#L34-L35 | ||
``` | ||
|
||
For example to read a namespace from cluster "foo" via the library will be easy as: | ||
|
||
```go | ||
kubernetes.New(gatewayClient. | ||
ClusterV1alpha1(). | ||
ClusterGateways(). | ||
RESTClient("foo")). | ||
CoreV1(). | ||
Namespaces(). | ||
Get(....) | ||
``` | ||
|
||
|
||
### Authorizing the Proxying Requests | ||
|
||
|
||
Due to the fact that all the outbound requests from the gateway will be sharing a | ||
common identity when reaching a managed cluster, we are not able to control the | ||
granularity of authorization to discriminate different end users working behind | ||
the proxy. However, practically it can be difficult to the control the RBAC scope | ||
of the outbound requests from the gateway dynamically b/c for now in OCM there's | ||
no such framework as "multi-cluster authorization". But one feasible solution is to | ||
have an additional authorization check before actually proxying the requests via the | ||
`rbacv1.SubjectAccessReview` (SAR) api. The subject of the SAR should keep the | ||
original client identity, but the verb can be virtual such as `<cluster-name>/<verb>`. | ||
For example, when a client "user1" is attempting to read a namespace "default" from | ||
cluster "foo", then the following SAR will be asserted before proxying: | ||
|
||
```yaml | ||
apiVersion: authorization.k8s.io/v1 | ||
kind: SubjectAccessReview | ||
spec: | ||
resourceAttributes: | ||
group: "" | ||
resource: "namespaces" | ||
verb: "foo/get" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah, is the verb can also be virtual? so in this case, the user needs to have this rule on hub, right? |
||
name: "default" | ||
``` | ||
|
||
Alternatively in the future if there's a native central authorization engine in OCM, | ||
we can simply switch from manual SAR calls to the engine. | ||
|
||
|
||
### Trimming Off Persisting Layer | ||
|
||
Usually an aggregated apiserver requires ETCD backends, but this can be trimmed off if | ||
we can restrict the `ManagedServiceAccount` to be the only source of the authentication | ||
credentials. By customizing the REST storage implementation of the aggregated | ||
`ClusterGateway` resource, we can transform the secrets projected from managed cluster | ||
to a virtual `ClusterGateway` instance. The virtual instance should be completely read-only | ||
to avoid conflicts with the underlying secret resource. |
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.
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 do not think this will support mTLS, should we add this in the non-goal?