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

🌱 adding TokenReview.auth.k8s.io/v1 webhook support #1440

Conversation

christopherhein
Copy link
Member

This adds a new pkg/webhook/authentication/ package for setting up the ServeHTTP func for the webook server to be able to handle TokenReview.authentication.k8s.io/(v1,v1beta1) requests.

Closes #1436

Signed-off-by: Chris Hein me@chrishein.com

@k8s-ci-robot k8s-ci-robot added do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. labels Mar 19, 2021
@k8s-ci-robot k8s-ci-robot added the size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. label Mar 19, 2021
@christopherhein
Copy link
Member Author

@rfranzke would you be interested in giving this a review and seeing if it aligns with what you implemented for Gardener?

@christopherhein christopherhein changed the title [WIP] 🌱 adding TokenReview.auth.k8s.io/v1 webhook support 🌱 adding TokenReview.auth.k8s.io/v1 webhook support Mar 19, 2021
@k8s-ci-robot k8s-ci-robot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Mar 19, 2021
@christopherhein
Copy link
Member Author

/assign @alvaroaleman
/cc @coderanger

@christopherhein
Copy link
Member Author

/test pull-controller-runtime-test-master

pkg/webhook/alias.go Outdated Show resolved Hide resolved
@rfranzke
Copy link

/assign

Copy link

@rfranzke rfranzke left a comment

Choose a reason for hiding this comment

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

Thanks @christopherhein, I don't have too many comments because I'm not very familiar with the design decisions made for the admission webhooks, but overall I like it and it looks like it's going into the right direction! :)

Once this PR is merged, I will use it as reference to also contribute the authorization handling.

Generally, I see that many things are now "duplicated" and this might then happen again for the authz. Maybe we should try more to encapsulate shared functionality to make it reusable (not sure if this is always possible in all cases). Probably a maintainer can give better recommendations here.

examples/tokenreview/tokenreview.go Outdated Show resolved Hide resolved
pkg/webhook/authentication/http.go Show resolved Hide resolved
Comment on lines 46 to 78
var body []byte
var err error
ctx := r.Context()
if wh.WithContextFunc != nil {
ctx = wh.WithContextFunc(ctx, r)
}

var reviewResponse Response
if r.Body != nil {
if body, err = ioutil.ReadAll(r.Body); err != nil {
wh.log.Error(err, "unable to read the body from the incoming request")
reviewResponse = Errored(err)
wh.writeResponse(w, reviewResponse)
return
}
} else {
err = errors.New("request body is empty")
reviewResponse = Errored(err)
wh.writeResponse(w, reviewResponse)
return
}

// verify the content type is accurate
contentType := r.Header.Get("Content-Type")
if contentType != "application/json" {
err = fmt.Errorf("contentType=%s, expected application/json", contentType)
wh.log.Error(err, "unable to process a request with an unknown content type", "content type", contentType)
reviewResponse = Errored(err)
wh.writeResponse(w, reviewResponse)
return
}

Choose a reason for hiding this comment

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

This is exactly the same like in

var body []byte
var err error
ctx := r.Context()
if wh.WithContextFunc != nil {
ctx = wh.WithContextFunc(ctx, r)
}
var reviewResponse Response
if r.Body != nil {
if body, err = ioutil.ReadAll(r.Body); err != nil {
wh.log.Error(err, "unable to read the body from the incoming request")
reviewResponse = Errored(http.StatusBadRequest, err)
wh.writeResponse(w, reviewResponse)
return
}
} else {
err = errors.New("request body is empty")
wh.log.Error(err, "bad request")
reviewResponse = Errored(http.StatusBadRequest, err)
wh.writeResponse(w, reviewResponse)
return
}
// verify the content type is accurate
contentType := r.Header.Get("Content-Type")
if contentType != "application/json" {
err = fmt.Errorf("contentType=%s, expected application/json", contentType)
wh.log.Error(err, "unable to process a request with an unknown content type", "content type", contentType)
reviewResponse = Errored(http.StatusBadRequest, err)
wh.writeResponse(w, reviewResponse)
return
}
and I think it will be exactly the ~same for authorization (https://github.com/gardener/gardener/blob/master/pkg/admissioncontroller/webhooks/auth/seed/handler.go#L68-L95).

Probably it should be made re-useable and moved into a function?

@christopherhein
Copy link
Member Author

christopherhein commented Mar 22, 2021

Maybe I should take a step back and first move some of this logic out of the admission package in separate PRs then come back to this and use those packages.

@vincepri curious what you think, you'll see we called out a bunch of duplication. I can move some of those packages around/make a little more generic upfront in separate PRs then adjust this PR to have the moved packages/helpers, or we could move forward with some duplication and clean up after…

Rethinking this implementation a bit I was able to cut down on a bunch of the duplication, @rfranzke about the checks, got ideas on how we could pass all the different implementations of Response around? Since that and write response maps to specific writers for those k8s types.

@christopherhein christopherhein force-pushed the tokenreview-webhook-support branch 5 times, most recently from 057ee0d to fe774d6 Compare March 23, 2021 04:11
@christopherhein
Copy link
Member Author

@alvaroaleman any chance you'd have some time to review this? 🙏

pkg/webhook/alias.go Outdated Show resolved Hide resolved
// unregistered type to the v1 GVK, the decoder will coerce a v1beta1 TokenReview to v1.
// The actual TokenReview GVK will be used to write a typed response in case the
// webhook config permits multiple versions, otherwise this response will fail.
req := Request{}
Copy link
Contributor

Choose a reason for hiding this comment

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

this is maybe more dangerous that it might seem? What if a new field is added to v1beta1 that's actually different than v1 (they're not guarnateed to be the same, just round-trippable). We could end up making a decision on bad data, right?

I suppose for new named fields this is the same as operating on old types against a new apiserver.

If v1 and v1beta1 both introduced a field called foo with different types though, this would just break entirely.

Is v1beta1 deprecated? May be worth commenting, cause that makes the chances of that happening much less.

Copy link
Member Author

Choose a reason for hiding this comment

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

v1beta1 is deprecated in 1.19 as per - https://github.com/kubernetes/api/blob/master/authentication/v1beta1/types.go#L25-L31 currently it looks like v1 and v1beta1 are in parity. Given it's deprecated I assume that means we can support this with the expectation that v1beta1 shouldn't chnage, right?

If so I can add a note to the comment above. A lot of this was carried over from the Admissions webhook, even this comment tbh.

Copy link
Member Author

Choose a reason for hiding this comment

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

For now I've added a comment, mentioning that v1beta1 is deprecated as of 1.19 and will be removed in 1.22 per the v1.22 deprecation guide - https://kubernetes.io/docs/reference/using-api/deprecation-guide/#tokenreview-v122

@christopherhein christopherhein force-pushed the tokenreview-webhook-support branch 3 times, most recently from a0dcd8d to a0e1d83 Compare March 24, 2021 23:10
Copy link
Member

@alvaroaleman alvaroaleman left a comment

Choose a reason for hiding this comment

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

Sorry for the delay in reviewing, I have a lot on my plate right now and this is a pretty big PR :) I am also not very familiar with the existing webhook code, so apologies if I am criticizing something you got from there.


var reviewResponse Response
if r.Body != nil {
if body, err = ioutil.ReadAll(r.Body); err != nil {
Copy link
Member

Choose a reason for hiding this comment

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

We need to close the body if its non-nil

Copy link
Member Author

Choose a reason for hiding this comment

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

return
}
} else {
err = errors.New("request body is empty")
Copy link
Member

Choose a reason for hiding this comment

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

From the godocs of net/http.Request:

 For server requests, the Request Body is always non-nil
	// but will return EOF immediately when no body is present.

So this doesn't need to be an else but simply at the top level after the r.Body != nil and check for the length of var body 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.

This is another carryover from the admission package. https://github.com/kubernetes-sigs/controller-runtime/blob/master/pkg/webhook/admission/http.go#L53-L67

That being said since we're not actually checking the HTTP Method, I believe this is what this block is meant to confirm that this isn't a GET, but I could have also understood the goals of it.

Copy link
Member Author

Choose a reason for hiding this comment

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

Do you think I should remove this?

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've switched around the way this is handled, the first check r.Body == nil now checks if it's a request that doesn't support a body, like a Get and fails out then we go on to reading the body.

}

// verify the content type is accurate
contentType := r.Header.Get("Content-Type")
Copy link
Member

Choose a reason for hiding this comment

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

Is this check needed? Deserialization will fail if its not json or not?

Copy link
Member Author

Choose a reason for hiding this comment

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

This was carried over from the admission package. https://github.com/kubernetes-sigs/controller-runtime/blob/master/pkg/webhook/admission/http.go#L69-L77 I'd guess this is to give a bit more visibility into what actually went wrong in the request. Should I remove it?

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 I guess that isn't what happens, the decoder doesn't seem to check what the actual headers are. If you don't have this check and the body is valid the request could be successful or could fail other places.

Copy link
Contributor

Choose a reason for hiding this comment

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

you get better errors this way too -- if you try to deserialize proto as json, for instance, you just end up with a really confusing error, whereas if you explicitly check content-type, you can provide a better error (plus, it's more correct, and like maybe theoretically avoids weird behavior where some blob of non-json looks enough like json to be deserialized, but is subtly wrong).

pkg/webhook/authentication/http.go Outdated Show resolved Hide resolved
pkg/webhook/authentication/http.go Outdated Show resolved Hide resolved
pkg/webhook/authentication/http.go Outdated Show resolved Hide resolved
@christopherhein
Copy link
Member Author

/test pull-controller-runtime-test-master

Signed-off-by: Chris Hein <me@chrishein.com>
@christopherhein
Copy link
Member Author

@alvaroaleman this should be all updated.

))

By("checking that a message is populated for 'Unauthenticated' responses")
Expect(ReviewResponse(false, authenticationv1.UserInfo{}, "UNACCEPTABLE!")).To(Equal(
Copy link
Contributor

Choose a reason for hiding this comment

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

lemongrab unacceptable with wiggly arms

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 unacceptable 😝

Copy link
Contributor

@DirectXMan12 DirectXMan12 left a comment

Choose a reason for hiding this comment

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

/lgtm
/approve

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label May 14, 2021
@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: christopherhein, DirectXMan12

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 May 14, 2021
@christopherhein
Copy link
Member Author

/test pull-controller-runtime-test-master

@k8s-ci-robot k8s-ci-robot merged commit ae50d4c into kubernetes-sigs:master May 14, 2021
@k8s-ci-robot k8s-ci-robot added this to the v0.9.x milestone May 14, 2021
@christopherhein christopherhein deleted the tokenreview-webhook-support branch May 14, 2021 20:46
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. lgtm "Looks good to me", indicates that a PR is ready to be merged. 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.

TokenReview.authentication.k8s.io resource helpers for Wehbook Server
5 participants