-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
proposal: external kubectl auth providers
- Loading branch information
1 parent
fdb9fbd
commit 76938f5
Showing
1 changed file
with
193 additions
and
0 deletions.
There are no files selected for viewing
193 changes: 193 additions & 0 deletions
193
contributors/design-proposals/auth/kubectl-exec-plugins.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,193 @@ | ||
# Out-of-tree client authentication providers | ||
|
||
Author: @ericchiang | ||
|
||
# Objective | ||
|
||
This document describes a credential rotation strategy for kubectl and client-go | ||
using an exec-based plugin mechanism. | ||
|
||
# Background | ||
|
||
Kubernetes clients can provide three kind of credentials: bearer tokens, TLS | ||
client certs, and basic auth username and password. Kubeconfigs can either | ||
in-line the credential, load credentials from a file, or can use an `AuthProvider` | ||
to actively fetch and rotate credentials. `AuthProviders` are compiled into kubectl | ||
and target specific providers (GCP, Keystone, Azure AD) or implement a | ||
specification supported but a subset of vendors (OpenID Connect). | ||
|
||
External tools that do rotation generally have to be called on a regular bases in | ||
case the current credentials have expired, which leads to wrapping `kubectl` with | ||
another tool or aliasing. For example, this is a [real example]( | ||
https://github.com/heptio/authenticator#4-set-up-kubectl-to-use-heptio-authenticator-for-aws-tokens) | ||
from Heptio's AWS authenticator: | ||
|
||
``` | ||
kubectl --kubeconfig /path/to/kubeconfig --token "$(heptio-authenticator-aws token -i CLUSTER_ID -r ROLE_ARN)" [...] | ||
``` | ||
|
||
Beside resulting in a long command, this potentially encourages distributions to | ||
wrap or fork kubectl, changing the way that users interact with different | ||
Kubernetes clusters. | ||
|
||
# Proposal | ||
|
||
This proposal builds off of earlier requests to [support exec-based plugins]( | ||
https://github.com/kubernetes/kubernetes/issues/35530#issuecomment-256170024), and | ||
proposes that we should add this as a first-class feature of kubectl. Specifically, | ||
kubectl (and client-go) should be able to receive credentials by executing a | ||
command and reading that command's stdout. | ||
|
||
In fact, kubectl already does this today. The GCP plugin can already be configured | ||
to [call a command]( | ||
https://github.com/kubernetes/client-go/blob/kubernetes-1.8.5/plugin/pkg/client/auth/gcp/gcp.go#L228-L240) | ||
other than `gcloud`. | ||
|
||
## Kubeconfig changes | ||
|
||
The current `AuthProviderConfig` uses `map[string]string` for configuration, which | ||
makes it hard to express things like a list of arguments or list key/value environment | ||
variables. As such, `AuthProviderConfig` should add another field which expresses the | ||
`exec` config. This has the benefit of a more natural structure, but the trade-off of | ||
not being compatible with the existing `kubectl config set-credentials` implementation. | ||
|
||
```go | ||
const ( | ||
AuthProviderExecKindBearerToken = "bearer-token" | ||
AuthProviderExecKindClientCert = "tls-certificate" | ||
) | ||
|
||
type AuthProviderConfig struct { | ||
Name string `json:"name"` | ||
Config map[string]string `json:"config"` | ||
|
||
// Exec should only be provided if Name and Config aren't. | ||
Exec ExecAuthProviderConfig `json:"exec"` | ||
} | ||
|
||
type ExecAuthProviderConfig struct { | ||
Command string `json:"command"` | ||
Args []string `json:"args"` | ||
// Env defines additional environment variables to expose to the process. These | ||
// are unioned with the host's environment, with variable values lists here having | ||
// precedence. | ||
Env []ExecEnvVar `json:"env"` | ||
// Kind determines the kind of credentials the command will output to stdout. | ||
// This can be "bearer-token" or "tls-certificate". | ||
Kind string `json:"kind"` | ||
} | ||
|
||
type ExecEnvVar struct { | ||
Name string `json:"name"` | ||
Value string `json:"value"` | ||
|
||
// TODO: Load env vars from files or from other envs? | ||
} | ||
``` | ||
|
||
This would allow a user block of a kubeconfig to declare the following: | ||
|
||
```yaml | ||
users: | ||
- name: mmosley | ||
user: | ||
auth-provider: | ||
exec: | ||
kind: bearer-token | ||
command: /bin/kubectl-login | ||
args: ["hello", "world"] | ||
env: | ||
- name: "VERSION" | ||
value: "v1.0.1" | ||
``` | ||
The AWS authenticator above example becomes: | ||
``` | ||
users: | ||
- name: kubernetes-admin | ||
user: | ||
auth-provider: | ||
exec: | ||
kind: bearer-token | ||
command: heptio-authenticator-aws | ||
# CLUSTER_ID and ROLE_ARN should be replaced with actual desired values. | ||
args: ["token", "-i", "(CLUSTER_ID)", "-r", "(ROLE_ARN)"] | ||
``` | ||
## TLS client certificate support | ||
The current The auth provider interface doesn't let the user modify the dialer, | ||
only wrap the transport. | ||
``` | ||
type AuthProvider interface { | ||
// WrapTransport allows the plugin to create a modified RoundTripper that | ||
// attaches authorization headers (or other info) to requests. | ||
WrapTransport(http.RoundTripper) http.RoundTripper | ||
// Login allows the plugin to initialize its configuration. It must not | ||
// require direct user interaction. | ||
Login() error | ||
} | ||
``` | ||
|
||
Since this doesn't let a `AuthProvider` supply things like client certificates, | ||
the signature of the `AuthProvider` should change too ([with corresponding changes | ||
to `k8s.io/client-go-transport`]( | ||
https://gist.github.com/ericchiang/7f5804403b359ebdf79dcf76c4071bff)): | ||
|
||
``` | ||
import ( | ||
"k8s.io/client-go/transport" | ||
// ... | ||
) | ||
type AuthProvider interface { | ||
// UpdateTransportConfig updates a config by adding a transport wrapper, | ||
// setting a bearer token (should ignore if one is already set), or adding | ||
// TLS client certificate credentials. | ||
UpdateTransportConfig(c *transport.Config) | ||
// Login() dropped, it was never used. | ||
} | ||
``` | ||
|
||
This would let auth transports supply TLS credentials, as well as instrument | ||
transports with in-memory rotation code like the utilities implemented by | ||
[`k8s.io/client-go/util/certificate`](https://godoc.org/k8s.io/client-go/util/certificate). | ||
|
||
If the exec command specifies `kind: tls-certificate`, the command is expected to | ||
output a unencrypted PEM encoded private key, followed by the PEM encoded client | ||
certificate chain. | ||
|
||
``` | ||
-----BEGIN RSA PRIVATE KEY----- | ||
MIIEowIBAAKCAQEA6IVXGPX5yP2Q6TAlQXIQsavzSqZ973iZvpQBGTI6M98gTSVm | ||
# ... | ||
TUwpZmylTKEJ9zLt2PADglyDrQ2D+1WNzh966Oo9c+kZt4WJM0aF | ||
-----END RSA PRIVATE KEY----- | ||
-----BEGIN CERTIFICATE----- | ||
MIIDCzCCAfOgAwIBAgIJAKK9m2Cfg5uhMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNV | ||
# ... | ||
xar2YeJ6mCCzSAPM69DP | ||
-----END CERTIFICATE----- | ||
-----BEGIN CERTIFICATE----- | ||
MIIDCzCCAfOgAwIBAgIJAPqJyUfmRxGLMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNV | ||
# ... | ||
zgmWLORg+ls1H1oaJiNW | ||
-----END CERTIFICATE----- | ||
``` | ||
|
||
The `AuthProvider` then adds those credentials to the `transport.Config`. | ||
|
||
## Login | ||
|
||
Historically, `AuthProviders` have had a `Login()` method with the hope that it | ||
could trigger bootstrapping into the cluster. While no providers implement this | ||
method, the Azure `AuthProvider` can already prompt an [interactive auth flow]( | ||
https://github.com/kubernetes/client-go/blob/kubernetes-1.8.5/plugin/pkg/client/auth/azure/azure.go#L343). | ||
This suggests that an exec'd tool should be able to trigger its own custom logins, | ||
either by opening a browser, or performing a text based prompt. | ||
|
||
We should take care that interactive stderr and stdin are correctly | ||
inherited by the sub-process to enable this kind of interaction. |