Skip to content

Commit 5a368fc

Browse files
committed
docs/proposal: submit poll based binding
1 parent 0644b16 commit 5a368fc

3 files changed

Lines changed: 233 additions & 0 deletions

File tree

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
---
2+
type: proposal
3+
title: Global / Federated Rules API
4+
status: in-progress
5+
owner: s-urbaniak
6+
---
7+
8+
* **Owners:**
9+
* @s-urbaniak
10+
11+
* **Related Tickets:**
12+
* N/A
13+
14+
## Summary
15+
16+
kube-bind offers a user-friendly approach of being able to bind API resources provided by an external party using a simple URL. This URL can be invoked directly via the terminal and the rest of the binding process is finalized in a user web browser session.
17+
18+
At the end a local callback ensures that the necessary metadata of the binding process is passed back to the kubectl `bind` process.
19+
20+
While being user-friendly the current approach has a couple of drawbacks. An alternative method is described which adds a new "polling" based approach vs. the current "callback" based approach.
21+
22+
## Why
23+
24+
kube-bind allows to bind APIs offered by providers using a URL passed to the `bind` subcommand.
25+
As part of the binding process the following steps are executed:
26+
27+
1. The kubectl `bind` plugin retrieves provider metadata
28+
which includes information about supported authentication methods at the provider.
29+
Currently, OAuth 2 code grant flow is supported.
30+
2. The kubectl `bind` renders an authentication URL on the terminal,
31+
augmented with additional session data.
32+
3. The user executes the rendered URL link
33+
and finalizes authentication and API selection in his local browser.
34+
35+
kubectl `bind` plugin blocks until authentication and API selection is finalized.
36+
Currently, this is accomplished using a localhost web server listening on an ephemeral local port on the user machine.
37+
38+
After the end of the above process the kube-bind backend invokes an HTTP 302 localhost redirect in the user browser session. The callback URL includes a serialized `kubebindv1alpha1.BindingResponse` JSON object. This object is passed as a base64 encoded `response` parameter which is added to the callback localhost URL as a query parameter.
39+
40+
The following figure illustrates the current process:
41+
42+
![](localhost-callback.png)
43+
Link: https://whimsical.com/kube-bind-auth-flow-FYCNPvj5m27pKUDVYxVRGy
44+
45+
The current solution has a couple of drawbacks:
46+
47+
1. The kubectl `bind` plugin starts localhost a web server running on the user machine. This makes it currently impossible to use kubectl `bind` on a remote machine i.e. via an SSH session.
48+
49+
2. The serialized encoded length of the `kubebindv1alpha1.BindingResponse` is currently passed as a URL parameter which should not exceed ~2000 characters in most web browser implementations. Worse, this upper bound is not standardized. Finally, passing large URL parameters is not recommended.
50+
51+
The current method will be called as "callback based" approach in the remainder of this proposal.
52+
53+
## Goals
54+
55+
1. **MUST** enable the possibility to use kubectl `bind` on a remote machine i.e. via an SSH session.
56+
2. **MUST** remove the necessity to start a localhost web server on the user machine.
57+
3. **MUST** remove the limitation of encoding the binding response object via a URL query parameter.
58+
4. **MUST** retain user-friendliness of invoking a single URL to bind external APIs.
59+
60+
## How
61+
62+
### kubectl `bind`
63+
64+
Next to the above describe "callback based" approach kubectl `bind` additionally supports a "polling" based approach.
65+
66+
Instead of having a localhost web server blocking until a callback http request is executed by the user web browser session, the kubectl `bind` process regularly polls the kube-bind backend whether the authentication and API resource selection process is finalized.
67+
68+
As a result, the kubectl `bind` process downloads the `kubebindv1alpha1.BindingResponse` JSON object directly from the polled URL.
69+
70+
Here, the following steps are executed:
71+
72+
1. The kubectl `bind` plugin retrieves provider metadata
73+
which includes information about supported authentication methods at the provider.
74+
75+
Here, a new authentication polling based variant of the OAuth2 code grant called `OAuth2CodeGrantPoll` is introduced:
76+
77+
```go
78+
type BindingProvider struct {
79+
metav1.TypeMeta `json:",inline"`
80+
81+
AuthenticationMethods []AuthenticationMethod `json:"authenticationMethods,omitempty"`
82+
}
83+
84+
type AuthenticationMethod struct {
85+
// method is the name of the authentication method. The follow methods are supported:
86+
//
87+
// - "OAuth2CodeGrant"
88+
// - "OAuth2CodeGrantPoll"
89+
//
90+
Method string `json:"method,omitempty"`
91+
92+
// OAuth2CodeGrant is the configuration for the OAuth2 code grant flow.
93+
OAuth2CodeGrant *OAuth2CodeGrant `json:"oauth2CodeGrant,omitempty"`
94+
95+
// OAuth2CodeGrant is the configuration for the OAuth2 code grant flow
96+
// using a poll based approach.
97+
OAuth2CodeGrantPoll *OAuth2CodeGrantPoll `json:"oauth2CodeGrantPoll,omitempty"`
98+
}
99+
100+
type OAuth2CodeGrantPoll struct {
101+
// sessionURL is the service provider url that the service consumer will use to create a new session.
102+
// The session is valid during the authentication and resource selection process.
103+
// Once resource selection and binding is finalized, the session is invalidated.
104+
//
105+
// +required
106+
// +kubebuilder:validation:Required
107+
// +kubebuilder:validation:MinLength=1
108+
SessionURL string `json:"sessionURL"`
109+
110+
// authenticatedURL is the service provider url that the service consumer will use to authenticate against
111+
// the service provider in case of using OIDC mode made, e.g: www.mangodb.com/kubernetes/authorize.
112+
//
113+
// +required
114+
// +kubebuilder:validation:Required
115+
// +kubebuilder:validation:MinLength=1
116+
AuthenticatedURL string `json:"authenticatedURL"`
117+
118+
// pollURL is the service provider url that is used to be poll regularly, i.e. "www.mangodb.com/bound".
119+
// The backend returns a HTTP 403 while the process is ongoing
120+
// and a HTTP 200 status code once the authentication and API resource selection is finalized.
121+
// The http response body includes the `kubebindv1alpha1.BindingResponse` object.
122+
//
123+
// +required
124+
// +kubebuilder:validation:Required
125+
// +kubebuilder:validation:MinLength=1
126+
PollURL string `json:"pollURL"`
127+
128+
// pollInterval is the expected polling interval in Go's `time.Duration` format.
129+
// If exceeded, the kube-bind backend may return a 429 Too Many Requests http status code.
130+
//
131+
// +required
132+
// +kubebuilder:validation:Required
133+
// +kubebuilder:validation:MinLength=1
134+
PollInterval string `json:"pollInterval"`
135+
}
136+
```
137+
138+
2. The kubectl `bind` renders an authentication URL on the terminal.
139+
140+
3. The user executes the rendered URL link
141+
and finalizes authentication and API selection in his local browser.
142+
143+
4. kubectl `bind` regularly polls at every `pollInterval`.
144+
While the user process is ongoing in the user browser session
145+
the kube-bind backend returns an HTTP 403 status code.
146+
Once the user process is finalized the kube-bind backend returns an HTTP 200 status code.
147+
The response body includes the `kubebindv1alpha1.BindingResponse` object.
148+
149+
kubectl `bind` plugin blocks until authentication and API selection is finalized.
150+
151+
The following figure illustrates the poll based approach:
152+
153+
![](poll-based.png)
154+
Link: https://whimsical.com/kube-bind-auth-flow-FYCNPvj5m27pKUDVYxVRGy
155+
156+
### Backend conventions
157+
158+
#### Existing `oauth2CodeGrant` protocol
159+
160+
The existing `oauth2CodeGrant` authentication method implies the following protocol conventions:
161+
162+
- **MUST** client invokes an authentication URL given by the `authenticatedURL`
163+
- **MUST** The authentication URL includes mutually exclusively either 1. a localhost callback port parameter `p`
164+
or 2. an explicit callback URL parameter `u`.
165+
In the case of a given localhost port parameter former case a `http://localhost:<p>/callback` callback URL is assumed.
166+
- **MUST** The authentication URL includes a session ID `s` that is generated on client side.
167+
- **MUST** The authentication URL includes a cluster ID `c` that is generated on client side.
168+
169+
#### Proposed `oauth2CodeGrantPoll` protocol
170+
171+
The proposed `oauth2CodeGrantPoll` introduces the following changes:
172+
173+
1. The callback URL is removed, it is unneeded.
174+
2. Both, the session ID and cluster ID generation are now in the responsibility of the backend, not the client.
175+
3. An additional ephemeral session secret is generated on the backend side.
176+
177+
- **MUST** client invokes a HTTP POST request against the `sessionURL`.
178+
The backend returns a `kubebindv1alpha1.Oauth2CodeGrantPollSession` response with the following attributes:
179+
```go
180+
// Oauth2CodeGrantPollSession is a non-CRUD resource that is returned by the server before
181+
// authentication.
182+
// It specifies the necessary session attributes and secrets
183+
// for a polling based OAuth2 Code Grant backed binding process.
184+
type Oauth2CodeGrantPollSession struct {
185+
metav1.TypeMeta `json:",inline"`
186+
187+
// sessionID is a unique identifier for this binding session. It is valid until authentication,
188+
// resource selection, and binding is finalized.
189+
//
190+
// +required
191+
// +kubebuilder:validation:Required
192+
// +kubebuilder:validation:MinLength=1
193+
SessionID string `json:"sessionID"`
194+
195+
// clusterID is a unique identifier for the cluster where resources are bound to.
196+
// The cluster ID is valid beyond the authentication, resource selection, and binding process.
197+
//
198+
// +required
199+
// +kubebuilder:validation:Required
200+
// +kubebuilder:validation:MinLength=1
201+
ClusterID string `json:"clusterID"`
202+
203+
// sessionSecret is an ephemeral secret that is valid for this session only.
204+
// It is used to sign all subsequent requests during the authentication, resource selection, and binding process.
205+
//
206+
// +required
207+
// +kubebuilder:validation:Required
208+
// +kubebuilder:validation:MinLength=1
209+
SessionSecret string `json:"sessionSecret"`
210+
}
211+
```
212+
- **MUST** The authentication URL includes a session ID query parameter `s` that was returned from the backend.
213+
- **MUST** The authentication URL includes a base64 encoded SHA256 HMAC signature query parameter `h` that is generated on client side.
214+
The signature signs a payload consisting of all request parameters, the host, scheme, and request body.
215+
- **MUST** The authentication URL includes a random nonce `n` that is generated on client side.
216+
217+
The described scheme is heavily inspired by the concept of a credentialed client as part of OAuth 2.1 [1].
218+
Using this scheme every client has credentials (the ephemeral session secret provided by the backend)
219+
but its identity is not established by the backend.
220+
221+
[1] https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-00#section-2.1
222+
223+
### Reference implementation
224+
225+
The existing reference implementation needs to be extended to be able to handle the above described polling based approach.
226+
Most notably the existing implementation needs to maintain session state.
227+
To accomplish this, the reference implementation will include a hashring based cache which stores the necessary session data.
228+
For simplicity this proposal suggests to use groupcache [2] which has support for consistent hashing.
229+
It lacks native support for TTL but this can be achieved using application logic.
230+
231+
[2] https://github.com/golang/groupcache
232+
233+
# Alternatives
392 KB
Loading

docs/proposals/poll-based.png

520 KB
Loading

0 commit comments

Comments
 (0)