Skip to content
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

Client auth provider plugin framework #23066

Merged
merged 1 commit into from
Apr 22, 2016

Conversation

cjcullen
Copy link
Member

Allows client plugins to modify the underlying transport to, for example, add custom authorization headers.

@k8s-github-robot k8s-github-robot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Mar 16, 2016
@cjcullen
Copy link
Member Author

@jdef @jlowdermilk @deads2k @smarterclayton @erictune I'd love to hear your thoughts.

@k8s-bot
Copy link

k8s-bot commented Mar 16, 2016

GCE e2e build/test passed for commit 759f65de42125a402f759a8080b0100e8cd7cc9e.

pluginsLock.Lock()
defer pluginsLock.Unlock()
if _, found := plugins[name]; found {
glog.Fatalf("Round tripper plugin %q was registered twice", name)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dislike fatalf, complicates unit testing. can this be an error instead?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. I wasn't sure what the right way to do this would be in a client binary. Would Errorf and then overwrite be reasonable?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could registration be made more explicit? as in the kubectl process invokes some "RegisterAuthPlugins()" func vs. using init? RegisterAuthPlugins() would basically do what init does now, except that it could return an error if registration for any plugin fails.

so .. if we do that, then you could return fmt.Errorf(...) here and NOT overwrite.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SGTM.

@jdef
Copy link
Contributor

jdef commented Mar 16, 2016

nits, otherwise lgtm. thanks for putting this together!

@jdef
Copy link
Contributor

jdef commented Mar 16, 2016

any chance this would get "backported" to a future 1.2.x release?

@@ -62,6 +62,9 @@ type Config struct {
// TODO: demonstrate an OAuth2 compatible client.
BearerToken string

// Server requires plugin-specified authentication.
AuthPlugin string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this ends up being enough structure for what we're likely to need. It seems like the next likely step is needing to have a piece of information I need to pass through to my plugin. Internally we wouldn't make it ahead of time, but you're plumbing this through a serialized file so I'm more concerned.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My initial (self-serving) thought was that other plugins could handle config themselves. E.g. @jdef's DCOS plugin could read an environment variable, another plugin could have its own conf file.

That said, I am very open to changes in structure/naming/etc.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My initial (self-serving) thought was that other plugins could handle config themselves. E.g. @jdef's DCOS plugin could read an environment variable, another plugin could have its own conf file.

I think that we should try to keep configuration in one location, so that the behavior of a kubeconfig file relatively predictable without needing to set particular env vars. During the debugging process, I find "show me the content of file X" easier than, "what was your env when you ran that command".

@deads2k
Copy link
Contributor

deads2k commented Mar 17, 2016

any chance this would get "backported" to a future 1.2.x release?

It's a neat feature, but would we then end up considering backports for all the plugins we'd want for it too? I don't know that I'd make that tradeoff.

@@ -92,6 +92,8 @@ type AuthInfo struct {
Username string `json:"username,omitempty"`
// Password is the password for basic authentication to the kubernetes cluster.
Password string `json:"password,omitempty"`
// AuthPlugin is a custom plugin used to authenticate to the kubernetes cluster.
AuthPlugin string `json:"auth-plugin,omitempty"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be called "AuthProvider"? Or CustomAuthProvider? If this is specified, does it take precedence over the other fields in this map?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would kind of expect to be able to have a custom auth plugin that uses username/password that overrides the default basic auth flow.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So AuthProvider would take precedence over any other field, but its implementation would be provided those fields? I think that is reasonable.

It does change the meaning of the fields slightly. Instead of just being "* for authentication to the kubernetes cluster", they would now be "* for authentication to the k8s cluster or to a custom intermediary".

Other than figuring out naming, is this something we can punt on until we have a concrete use case? Or does the fact that this gets serialized mean we need to figure it out up front?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other than figuring out naming, is this something we can punt on until we have a concrete use case? Or does the fact that this gets serialized mean we need to figure it out up front?

I think we should decide which fields get plumbed through to the plugins before deciding on a serialization. Passing the information from the AuthInfo doesn't seem unreasonable. It also doesn't seem unreasonable to allow plumb through a map[string]string or an object registered in the scheme down through either.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay. How about something like:

type AuthProviderConfig struct {
    Name   string
    Config json.RawMessage
}

Plugins would then be provided with the specified Config when instantiated. Simple plugins (like mine) can specify a Name, and leave Config blank. More complex plugins could define and parse their own config, and it would all look nice and pretty in the kubeconfig file.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sounds pretty flexible, and at first glace I really like this. Upon
further reflection I wonder if a configmap might be more appropriate? Or
would that be too restrictive? Configmap is an API standard after all..

On Mon, Mar 21, 2016 at 7:27 PM, CJ Cullen notifications@github.com wrote:

In pkg/client/unversioned/clientcmd/api/types.go
#23066 (comment)
:

@@ -92,6 +92,8 @@ type AuthInfo struct {
Username string json:"username,omitempty"
// Password is the password for basic authentication to the kubernetes cluster.
Password string json:"password,omitempty"

  • // AuthPlugin is a custom plugin used to authenticate to the kubernetes cluster.
  • AuthPlugin string json:"auth-plugin,omitempty"

Okay. How about something like:

type AuthProviderConfig struct {
Name string
Config json.RawMessage
}

Plugins would then be provided with the specified Config when
instantiated. Simple plugins (like mine) can specify a Name, and leave
Config blank. More complex plugins could define and parse their own config,
and it would all look nice and pretty in the kubeconfig file.


You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub
https://github.com/kubernetes/kubernetes/pull/23066/files/759f65de42125a402f759a8080b0100e8cd7cc9e#r56915022

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ConfigMap would be less flexible for plugin builders (string->string map instead of generic JSON blob) without providing much of a benefit.

I need to look into whether it'd be better to use something like runtime.RawExtension & runtime.Object. I'm hoping @smarterclayton or @krousey can kick in some feedback on that.

@cjcullen
Copy link
Member Author

Reworked this WIP after the first round of comments:

  1. Changed AuthPlugin string to AuthProvider *AuthProviderConfig (which has a *json.RawMessage in it).
  2. Made RegisterPlugin return an error. RegisterPlugin is still called from the init() func of the plugin (let's the structs stay non-exported), but at least the function will be testable.
  3. Reused the WrapTransport field of the transport config, instead of plumbing knowledge of auth plugins into the generic client transport.
  4. Moved where the plugins are imported from. I didn't previously consider that clients are created from other places than just cmd/kubectl.

PTAL

@k8s-bot
Copy link

k8s-bot commented Mar 22, 2016

GCE e2e build/test failed for commit 6513f0d96e0e699f117afee7b1f59838044980e4.

Please reference the list of currently known flakes when examining this failure. If you request a re-test, you must reference the issue describing the flake.

// AuthProviderConfig
type AuthProviderConfig struct {
Name string `json:"name"`
Config *json.RawMessage `json:"config"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm more used to seeing runtime.RawExtension, and runtime.Object. It's what we we use downstream and it works reasonably well.

I do like this a lot better than just a name.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed that there is already a runtime.RawExtension, in the v1 clientcmd types, and a corresponding runtime.Object in the unversioned types. Can I just reuse that? Or am I better off defining a new field?

I'm still looking for where (if) the extensions are actually registered/used...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still looking for where (if) the extensions are actually registered/used...

They were there so that deep integrators like openshift would be able to supply custom configuration values and the kube decoding/encoding path wouldn't strip them out. They aren't natively used in kube.

Since the core kube code will actually be interpreting these values, the structure you've described here is more suitable. If you were going to say, "we aren't going to provide this, build your own "gcp" on top", then you'd point them to the existing Extensions field and let them do their thing.

@cjcullen
Copy link
Member Author

Talked w/ @erictune about this offline. He recommended that we punt on the config format until we have a concrete use case. So, for now, AuthProviderConfig would just have a name:

type AuthProviderConfig struct {
    Name string `json:"name"`
}

So, the serialized version would look like:

users:
- name: cjcullen
  user:
    auth-provider:
      name: gcp
...

This would still be valid if we later added a config: field or something else to the AuthProviderConfig.

Thoughts?

@jdef
Copy link
Contributor

jdef commented Mar 26, 2016

fine w/ me

On Fri, Mar 25, 2016 at 4:51 PM, CJ Cullen notifications@github.com wrote:

Talked w/ @erictune https://github.com/erictune about this offline. He
recommended that we punt on the config format until we have a concrete use
case. So, for now, AuthProviderConfig would just have a name:

type AuthProviderConfig struct {
Name string json:"name"
}

So, the serialized version would look like:

users:

  • name: cjcullen
    user:
    auth-provider:
    name: gcp
    ...

This would still be valid if we later added a config: field or something
else to the AuthProviderConfig.

Thoughts?


You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub
#23066 (comment)

@deads2k
Copy link
Contributor

deads2k commented Mar 28, 2016

Thoughts?

That's fine. Are you still ok with making it a slice to allow multiple participants? That would change serialization.

@cjcullen
Copy link
Member Author

Yeah, I'll make it a slice. A little awkward, but probably outweighed by the serialization BWC benefits.

@cjcullen
Copy link
Member Author

Made AuthProviders a slice, and added a test for chaining several AuthProviders together.

PTAL

@k8s-bot
Copy link

k8s-bot commented Mar 29, 2016

GCE e2e build/test passed for commit 4b5ba3e3d31159bfd17d0a5ec4b10c3bfa65b89d.

@jdef
Copy link
Contributor

jdef commented Mar 29, 2016

👍

@cjcullen cjcullen changed the title WIP: Client auth plugin framework Client auth provider plugin framework Mar 30, 2016
@cjcullen
Copy link
Member Author

Removing the "WIP" as I think we've settled on the general direction for this.

@k8s-github-robot k8s-github-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Apr 14, 2016
@k8s-github-robot k8s-github-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Apr 15, 2016
@k8s-bot
Copy link

k8s-bot commented Apr 15, 2016

GCE e2e build/test failed for commit 03f9b5a.

Please reference the list of currently known flakes when examining this failure. If you request a re-test, you must reference the issue describing the flake.

@cjcullen
Copy link
Member Author

@k8s-bot e2e test this issue: #23545

@k8s-bot
Copy link

k8s-bot commented Apr 15, 2016

GCE e2e build/test passed for commit 03f9b5a.

@erictune
Copy link
Member

LGTM

@erictune erictune added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Apr 20, 2016
@k8s-bot
Copy link

k8s-bot commented Apr 21, 2016

GCE e2e build/test passed for commit 03f9b5a.

@k8s-bot
Copy link

k8s-bot commented Apr 21, 2016

GCE e2e build/test passed for commit 03f9b5a.

@k8s-github-robot
Copy link

@cjcullen
You must link to the test flake issue which caused you to request this manual re-test.
Re-test requests should be in the form of: k8s-bot test this issue: #<number>
Here is the list of open test flakes.

@k8s-github-robot k8s-github-robot removed the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Apr 21, 2016
@cjcullen
Copy link
Member Author

@k8s-bot unit test this issue: #24603

@cjcullen
Copy link
Member Author

Re-adding LGTM.

@cjcullen cjcullen added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Apr 21, 2016
@k8s-bot
Copy link

k8s-bot commented Apr 21, 2016

GCE e2e build/test passed for commit 03f9b5a.

@k8s-bot
Copy link

k8s-bot commented Apr 22, 2016

GCE e2e build/test passed for commit 03f9b5a.

@k8s-github-robot
Copy link

Automatic merge from submit-queue

@k8s-github-robot k8s-github-robot merged commit f4beccf into kubernetes:master Apr 22, 2016
@bobbyrullo bobbyrullo mentioned this pull request May 5, 2016
k8s-github-robot pushed a commit that referenced this pull request May 8, 2016
Automatic merge from submit-queue

Support persisting config from kubecfg AuthProvider plugins

Plumbs through an interface to the plugin that can persist a `map[string]string` config for just that plugin. Also adds `config` to the AuthProvider serialization type, and `Login()` to the AuthProvider plugin interface.
Modified the gcp AuthProvider to cache short-term access tokens in the kubecfg file.

Builds on #23066 
@bobbyrullo @deads2k @jlowdermilk @erictune
k8s-github-robot pushed a commit that referenced this pull request Aug 6, 2016
…als-auth-providers

Automatic merge from submit-queue

kubectl config set-crentials: add arguments for auth providers

This PR adds `--auth-provider` and `--auth-provider-arg` flags to the
`kubectl config set-credentials` sub-command.

There's currently no way of interacting with the new auth provider framework added in #23066 through kubectl. You have to render a custom kubeconfig to use them. Additionally `kubectl config set` just sort of craps out when attempting to interact with authentication info objects (#29312).

This is a minimal implementation of allowing `kubect config set-credentials` to set fields for client auth providers.

cc @cjcullen @kubernetes/kubectl
@jheiss jheiss mentioned this pull request Oct 12, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
lgtm "Looks good to me", indicates that a PR is ready to be merged. release-note-none Denotes a PR that doesn't merit a release note. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet