Skip to content

Commit 433394d

Browse files
committed
exec credential provider: updates after convo with sig-auth
Some specifics about the contents of this PR: - refer to named extension correctly (see 3df8de5) - update Config field with impl doc - note that Cluster is kubeconfig shadow - add Cluster reader helper method - say when Cluster is set Signed-off-by: Andrew Keesler <[email protected]>
1 parent d1f9226 commit 433394d

File tree

1 file changed

+115
-11
lines changed
  • keps/sig-auth/541-external-credential-providers

1 file changed

+115
-11
lines changed

keps/sig-auth/541-external-credential-providers/README.md

Lines changed: 115 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,11 @@ users:
150150
On Fedora: dnf install example-client-go-exec-plugin
151151
152152
...
153+
154+
# Whether or not to provide cluster information, which could potentially contain
155+
# very large CA data, to this exec plugin as a part of the KUBERNETES_EXEC_INFO
156+
# environment variable.
157+
provideClusterInfo: true
153158
clusters:
154159
- name: my-cluster
155160
cluster:
@@ -195,6 +200,12 @@ type ExecConfig struct {
195200
// present. For example, `brew install foo-cli` might be a good InstallHint for
196201
// foo-cli on Mac OS systems.
197202
InstallHint string `json:"installHint,omitempty"`
203+
204+
// ProvideClusterInfo determines whether or not to provide cluster information,
205+
// which could potentially contain very large CA data, to this exec plugin as a
206+
// part of the KUBERNETES_EXEC_INFO environment variable. By default, it is set
207+
// to false.
208+
ProvideClusterInfo bool `json:"provideClusterInfo"`
198209
}
199210
```
200211

@@ -212,6 +223,10 @@ variables set in the client process are also passed to the provider.
212223
`installHint` specifies help text to print to the user when the required binary
213224
is missing.
214225

226+
`provideClusterInfo` specifies whether to provide cluster information, which could
227+
potentially contain very large CA data, to this exec plugin as a part
228+
of the `KUBERNETES_EXEC_INFO` environment variable.
229+
215230
### Provider input format
216231

217232
In JSON:
@@ -252,39 +267,69 @@ type ExecCredential struct {
252267
// the transport.
253268
type ExecCredentialSpec struct {
254269
// Cluster contains information to allow an exec plugin to communicate with the
255-
// kubernetes cluster being authenticated to.
256-
Cluster Cluster `json:"cluster"`
270+
// kubernetes cluster being authenticated to. Note that Cluster is non-nil only
271+
// when provideClusterInfo is set to true in the exec provider config (i.e.,
272+
// ExecConfig.ProvideClusterInfo).
273+
// +optional
274+
Cluster *Cluster `json:"cluster,omitempty"`
257275
}
258276

259277
// Cluster contains information to allow an exec plugin to communicate with the
260278
// kubernetes cluster being authenticated to.
279+
//
280+
// To ensure that this struct contains everything someone would need to communicate
281+
// with a kubernetes cluster (just like they would via a kubeconfig), the fields
282+
// should shadow "k8s.io/client-go/tools/clientcmd/api/v1".Cluster, with the exception
283+
// of CertificateAuthority, since CA data will always be passed to the plugin as bytes.
261284
type Cluster struct {
262285
// Server is the address of the kubernetes cluster (https://hostname:port).
263286
Server string `json:"server"`
264-
// ServerName is passed to the server for SNI and is used in the client to
287+
// TLSServerName is passed to the server for SNI and is used in the client to
265288
// check server certificates against. If ServerName is empty, the hostname
266289
// used to contact the server is used.
267290
// +optional
268-
ServerName string `json:"serverName,omitempty"`
291+
TLSServerName string `json:"tls-server-name,omitempty"`
292+
// InsecureSkipTLSVerify skips the validity check for the server's certificate.
293+
// This will make your HTTPS connections insecure.
294+
// +optional
295+
InsecureSkipTLSVerify bool `json:"insecure-skip-tls-verify,omitempty"`
269296
// CAData contains PEM-encoded certificate authority certificates.
270297
// If empty, system roots should be used.
271298
// +listType=atomic
272299
// +optional
273-
CAData []byte `json:"caData,omitempty"`
300+
CertificateAuthorityData []byte `json:"certificate-authority-data,omitempty"`
274301
// ProxyURL is the URL to the proxy to be used for all requests to this
275302
// cluster.
276303
// +optional
277304
ProxyURL string `json:"proxy-url,omitempty"`
278-
// Config holds additional config data that is specific to the exec plugin
279-
// with regards to the cluster being authenticated to.
305+
// Config holds additional config data that is specific to the exec
306+
// plugin with regards to the cluster being authenticated to.
307+
//
308+
// This data is sourced from the clientcmd Cluster object's extensions[exec] field:
309+
//
310+
// clusters:
311+
// - name: my-cluster
312+
// cluster:
313+
// ...
314+
// extensions:
315+
// - name: client.authentication.k8s.io/exec # reserved extension name for per cluster exec config
316+
// extension:
317+
// audience: 06e3fbd18de8 # arbitrary config
318+
//
319+
// In some environments, the user config may be exactly the same across many clusters
320+
// (i.e. call this exec plugin) minus some details that are specific to each cluster
321+
// such as the audience. This field allows the per cluster config to be directly
322+
// specified with the cluster info. Using this field to store secret data is not
323+
// recommended as one of the prime benefits of exec plugins is that no secrets need
324+
// to be stored directly in the kubeconfig.
280325
// +optional
281326
Config runtime.RawExtension `json:"config,omitempty"`
282327
}
283328
```
284329

285330
The Go struct for the `clusters.[...].cluster.extensions[...].extension` field:
286331

287-
```golang
332+
```go
288333
// Cluster contains information about how to communicate with a kubernetes cluster
289334
type Cluster struct {
290335
// Server is the address of the kubernetes cluster (https://hostname:port).
@@ -313,20 +358,72 @@ has completed parsing its `kubeconfig`, flags and environment variables).
313358
This allows the executable to perform different actions based on the current
314359
cluster (i.e. get a token for a particular cluster). The `Cluster` struct is
315360
flexible in that it not only provides all details required to communicate with
316-
the cluster (hostname and TLS config), but that is also allows arbitrary
361+
the cluster as one would via a `kubeconfig` (i.e. everything from
362+
`"k8s.io/client-go/tools/clientcmd/api/v1".Cluster`), but also allows arbitrary
317363
per-cluster configuration to be passed to the executable via the `config` field.
318364
This field can contain arbitrary data that is passed to the executable without
319365
modification. This allows extra user-defined data (i.e. an OAuth client ID for
320366
audience scoping) to be passed through the `spec.cluster` field. The user
321-
configures this via the `kubeconfig`'s `clusters.[...].cluster.extensions[exec].extension`
322-
field. The `exec` named extension is reserved for this purpose.
367+
configures this via the `kubeconfig`'s `clusters.[...].cluster.extensions[client.authentication.k8s.io/exec].extension`
368+
field. The `client.authentication.k8s.io/exec` named extension is reserved for this
369+
purpose.
370+
371+
The `spec.cluster` field is a pointer so that the plugin can easily determine whether
372+
the cluster information is valid. The cluster information will only be provided when 1)
373+
the `ExecCredential` version supports the `spec.cluster` field (note: `v1alpha1`
374+
does not support this field) and 2) the `provideClusterInfo` field is set to `true`
375+
for this `kubeconfig` `AuthInfo` entry. The `provideClusterInfo` option is opt-in for
376+
the following reasons.
377+
1. To prevent potentially large CA bundles from being set in the environment via the
378+
`Cluster.CertificateAuthorityData` field and causing system issues.
379+
1. To design for the majority of plugins that will not use this cluster information
380+
(this is an assumption).
381+
1. To give the `kubeconfig` creator, which most likely understands the runtime and
382+
security properties of the exec plugin, the power to enable or disable this
383+
cluster information being set in the environment for a plugin to consume.
384+
385+
To ensure that the `Cluster` struct maintains the same cluster connection
386+
capabilities as one gets from a `kubeconfig`, the `Cluster` fields (Go and JSON) must
387+
be named the same as `"k8s.io/client-go/tools/clientcmd/api/v1".Cluster`, with the
388+
exception of `CertificateAuthority`, which has been left out since CA data will always
389+
be passed to the plugin as bytes.
323390

324391
This data is passed to the executable via the `KUBERNETES_EXEC_INFO` environment
325392
variable in a JSON serialized object. Note that an environment variable is used
326393
over passing this information via standard input because standard input is
327394
reserved for interactive flows between the user and executable (i.e. to prompt
328395
for username and password).
329396

397+
To make it easier for a plugin to use this `KUBERNETES_EXEC_INFO` environment
398+
variable to connect to the referent cluster, a set of helper functions will be added
399+
to create a `"k8s.io/client-go/rest".Config` from the `KUBERNETES_EXEC_INFO`
400+
environment variable. These helper functions will live in
401+
`k8s.io/client-go/tools/auth/exec/exec.go` so that non-plugin developers won't pull
402+
in this new package and the new package can safely depend on
403+
`k8s.io/client-go/rest`. The helper functions are as follows.
404+
405+
```golang
406+
// LoadExecCredentialFromEnv is a helper-wrapper around LoadExecCredential that loads from the
407+
// well-known KUBERNETES_EXEC_INFO environment variable.
408+
//
409+
// When the KUBERNETES_EXEC_INFO environment variable is not set or is empty, then this function
410+
// will immediately return an error.
411+
func LoadExecCredentialFromEnv() (runtime.Object, *rest.Config, error)
412+
413+
// LoadExecCredential loads the configuration needed for an exec plugin to communicate with a
414+
// cluster.
415+
//
416+
// LoadExecCredential expects the provided data to be a serialized client.authentication.k8s.io
417+
// ExecCredential object (of any version). If the provided data is invalid (i.e., it cannot be
418+
// unmarshalled into any known client.authentication.k8s.io ExecCredential version), an error will
419+
// be returned. A successfully unmarshalled ExecCredential will be returned as the first return
420+
// value.
421+
//
422+
// If the provided data is successfully unmarshalled, but it does not contain cluster information
423+
// (i.e., ExecCredential.Spec.Cluster == nil), then the returned rest.Config and error will be nil.
424+
func LoadExecCredential(data []byte) (runtime.Object, *rest.Config, error)
425+
```
426+
330427
### Provider output format
331428

332429
In JSON:
@@ -416,6 +513,13 @@ Unit tests to confirm:
416513
+ Credentials are used across many requests (as long as they are still valid)
417514
- Single flight all calls to a given executable (when the config is the same)
418515
- Reasonable timeout to executable calls so clients do not hang indefinitely
516+
- `"k8s.io/client-go/pkg/apis/clientauthentication".Cluster` (and external types)
517+
fields (Go and JSON) properly shadow
518+
`"k8s.io/client-go/tools/clientcmd/api/v1".Cluster` fields (with the exception of
519+
`CertificateAuthority` for reasons stated in design) so
520+
that structs are kept up to date
521+
- Helper methods properly create `"k8s.io/client-go/rest".Config` from
522+
`"k8s.io/client-go/pkg/apis/clientauthentication".Cluster`
419523

420524
Integration (or e2e CLI) tests to confirm:
421525

0 commit comments

Comments
 (0)