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-go: add an exec-based client auth provider #59495

Merged
merged 2 commits into from Mar 1, 2018

Conversation

ericchiang
Copy link
Contributor

@ericchiang ericchiang commented Feb 7, 2018

Updates kubernetes/enhancements#541
Implements kubernetes/community#1503
Closes #57164

client-go: alpha support for exec-based credential providers

/sig auth
/kind feature

@k8s-ci-robot k8s-ci-robot added release-note Denotes a PR that will be considered when it comes time to generate release notes. sig/auth Categorizes an issue or PR as relevant to SIG Auth. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. kind/feature Categorizes issue or PR as related to a new feature. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. labels Feb 7, 2018
@caesarxuchao
Copy link
Member

cc @mikedanese

@ericchiang
Copy link
Contributor Author

ericchiang commented Feb 8, 2018

cc @kubernetes/sig-auth-pr-reviews
cc @nckturner
cc @perotinus
cc @ericavonb

// Response defines metadata about a failed request, including HTTP status code and
// response headers.
type Response struct {
// HTTP header returned by the server.
Copy link
Contributor

Choose a reason for hiding this comment

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

s/header/headers/

@@ -0,0 +1,4 @@
#!/bin/sh -e
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd rather name it test-plugin.sh.

@@ -0,0 +1,4 @@
#!/bin/sh -e

(>&2 echo "$KUBERNETES_EXEC_INFO")
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we need a sub-shell here? And how it differs from echo >&2 "$KUBERNETES_EXEC_INFO" that is easier to understand IMHO.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No reason. Updated.

@ericchiang ericchiang force-pushed the client-auth-exec branch 2 times, most recently from 4aeb04b to ffcd361 Compare February 8, 2018 17:14
@ericchiang
Copy link
Contributor Author

Flaking on #59426

/test pull-kubernetes-unit

@@ -119,6 +119,9 @@ type AuthInfo struct {
// AuthProvider specifies a custom authentication plugin for the kubernetes cluster.
// +optional
AuthProvider *AuthProviderConfig `json:"auth-provider,omitempty"`
// Exec specifies a custom exec-based authentication plugin for the kubernetes cluster.
// +optional
Exec *ExecConfig `json:"exec,omitempty"`
Copy link
Member

Choose a reason for hiding this comment

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

Why did you decide not to implement this as another AuthProvider?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Was called out in the proposal. We know the structure and map[string]string is awkward for expressing things like a list of key/value environment variables.

https://github.com/kubernetes/community/blob/master/contributors/design-proposals/auth/kubectl-exec-plugins.md#kubeconfig-changes

Copy link
Member

Choose a reason for hiding this comment

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

SGTM

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// +genclient
Copy link
Member

Choose a reason for hiding this comment

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

Do we need this?

)

func Convert_clientauthentication_ExecCredentials_To_v1alpha1_ExecCredentials(in *clientauthentication.ExecInfo, out *ExecInfo, s conversion.Scope) error {
return autoConvert_clientauthentication_ExecInfo_To_v1alpha1_ExecInfo(in, out, s)
Copy link
Member

Choose a reason for hiding this comment

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

Why do we need this if it just calls autoConvert?

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

// ExecCredentials are credentials returned by the plugin.
type ExecCredentials struct {
Copy link
Member

Choose a reason for hiding this comment

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

Request/response non-storage APIs have used a single type and spec/status in the past. Any reason to deviate from that convention?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point, I don't have a good reason. Switched to spec/status.

func (c *cache) get(s string) (*Authenticator, bool) {
c.mu.Lock()
defer c.mu.Unlock()
a, ok := c.m[s]
Copy link
Member

Choose a reason for hiding this comment

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

return c.m[s]

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Doesn't seem like that works https://play.golang.org/p/2dzFfrrGBkR

c.mu.Lock()
defer c.mu.Unlock()
if len(c.m) == 0 {
c.m = make(map[string]*Authenticator)
Copy link
Member

Choose a reason for hiding this comment

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

why not expect this to happen on cache initialization?

)

func cacheKey(c *api.ExecConfig) string {
h := fnv.New32a()
Copy link
Member

Choose a reason for hiding this comment

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

The hash map is already hashing. Do we need to double hash?

a.group, schema.GroupVersion{Group: gvk.Group, Version: gvk.Version})
}

if !creds.Expiry.IsZero() {
Copy link
Member

Choose a reason for hiding this comment

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

isn't this if block the same as a.exp = creds.Expiry.Time?

@ericchiang ericchiang force-pushed the client-auth-exec branch 3 times, most recently from da3b7e9 to 08fedf5 Compare February 9, 2018 17:20
@nckturner
Copy link
Contributor

LGTM

@ericchiang
Copy link
Contributor Author

/assign @smarterclayton

For final approval

type ExecCredentialStatus struct {
// ExpirationTimestamp indicates a time when the provided credentials expire.
// +optional
ExpirationTimestamp metav1.Time `json:"expirationTimestamp,omitempty"`
Copy link
Member

Choose a reason for hiding this comment

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

this has to be a pointer if you actually want it omitted from serialization

}

a := &Authenticator{
cmd: config.Command,
Copy link
Member

Choose a reason for hiding this comment

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

it seems like there are three possibilities for this path:

  • relative (e.g. ./foo)
  • absolute (e.g. /path/to/foo)
  • $PATH-based (e.g. curl)

Was there discussion about how to resolve the referenced command here?

There is already code for resolving files referenced from kubeconfig files so that relative paths are resolved relative to the kubeconfig file (see ResolvePaths and GetAuthInfoFileReferences).

My instinct would be to resolve explicitly relative paths ./foo in GetAuthInfoFileReferences, leave absolute paths alone, and resolve bare commands (e.g. curl) using $PATH. Thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I like keeping the relative path logic the same as other files in kubeconfig files. Let me update this.

Copy link
Member

Choose a reason for hiding this comment

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

I guess if you want $PATH behavior you can invoke a local script

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I guess if you want $PATH behavior you can invoke a local script

exec.Command already does that: https://golang.org/pkg/os/exec/#Command

@liggitt
Copy link
Member

liggitt commented Feb 28, 2018

no other comments from me

return []*string{&authInfo.ClientCertificate, &authInfo.ClientKey, &authInfo.TokenFile}
s := []*string{&authInfo.ClientCertificate, &authInfo.ClientKey, &authInfo.TokenFile}
// Only resolve exec command if it isn't PATH based.
if authInfo.Exec != nil && strings.ContainsRune(authInfo.Exec.Command, filepath.Separator) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Relative logic added

Copy link
Member

Choose a reason for hiding this comment

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

SGTM... make sure this nuance makes it into the docs ("./foo is relative to the kubeconfig file, foo looks in the $PATH")

@liggitt
Copy link
Member

liggitt commented Feb 28, 2018

/lgtm

@k8s-github-robot k8s-github-robot removed the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Feb 28, 2018
@ericchiang
Copy link
Contributor Author

(nits addressed)

@ericchiang ericchiang added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Feb 28, 2018
@smarterclayton
Copy link
Contributor

smarterclayton commented Mar 1, 2018 via email

@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: ericchiang, liggitt, mikedanese, smarterclayton

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@k8s-ci-robot k8s-ci-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Mar 1, 2018
@k8s-github-robot
Copy link

/test all [submit-queue is verifying that this PR is safe to merge]

@k8s-github-robot
Copy link

Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions here.

@rajatjindal
Copy link
Contributor

Hi @ericchiang

is there any documentation that I can refer to to use this. I tried with 1.10 RC kubectl, but it does not seem to call my external command at all.

cben added a commit to cben/kubeclient that referenced this pull request Dec 9, 2018
…t out

Matches Go client's behavior:
kubernetes/kubernetes#59495 (comment)
Distinguish 3 cases:
- absolute (e.g. /path/to/foo)
- $PATH-based (e.g. curl)
- relative to config file's dir (e.g. ./foo)

If base dir explicitly set to nil, refuse to execute external commands,
matching existing opt-out from file lookups (ManageIQ#372).

Document security aspects of credential plugins.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. kind/feature Categorizes issue or PR as related to a new feature. lgtm "Looks good to me", indicates that a PR is ready to be merged. release-note Denotes a PR that will be considered when it comes time to generate release notes. sig/auth Categorizes an issue or PR as relevant to SIG Auth. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

10 participants