-
Notifications
You must be signed in to change notification settings - Fork 39.8k
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
Dynamic webhook admission control plugin #46388
Dynamic webhook admission control plugin #46388
Conversation
wg.Wait() | ||
|
||
if failCount > 0 { | ||
return ErrWebhookRejected |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this descriptive enough? An API caller has no idea why it was rejected and the only possible way to figure that out is by accessing the API server logs, which is likely not an option. Is there any good way to display the error(s) back to the user? (To me, it would be nice if all webhook responses with Status.Result.Reason
were returned to the user that way they could possibly remedy the issue.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess I can fix that, sure.
// TODO: set KeyData/CertData | ||
}, | ||
UserAgent: "kube-apiserver-admission", | ||
Timeout: 30 * time.Second, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason we cannot make this configurable? Might not be necessary for 1.0 but I can see this being useful in the future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the future. not needed for mvp I think.
@@ -115,4 +132,12 @@ func (i pluginInitializer) Initialize(plugin admission.Interface) { | |||
if wants, ok := plugin.(WantsQuotaRegistry); ok { | |||
wants.SetQuotaRegistry(i.quotaRegistry) | |||
} | |||
|
|||
if wants, ok := plugin.(WantsServiceResolver); ok { | |||
wants.SetQuotaRegistry(i.serviceResolver) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pushed without running :)
61cbce5
to
374b5b7
Compare
"k8s.io/kubernetes/pkg/api" | ||
admissionv1alpha1 "k8s.io/kubernetes/pkg/apis/admission/v1alpha1" | ||
|
||
// install the clientgo admission API for use with api registry |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/clientgo//
defer testServer.Close() | ||
serverURL, err := url.ParseRequestURI(testServer.URL) | ||
if err != nil { | ||
t.Fatalf("???? %v", err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove the "????"
rootCAs.AppendCertsFromPEM(caCert) | ||
testServer := httptest.NewUnstartedServer(http.HandlerFunc(webhookHandler)) | ||
testServer.TLS = &tls.Config{ | ||
Certificates: []tls.Certificate{cert}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it correct to let the server use a cert generated from cleintCert and clientKey?
Z0gPepfIFptDMl5wKWyw4o2XVSZ69Ur8tQQynoNyX/FTIx6tQ//hzg== | ||
-----END CERTIFICATE-----`) | ||
|
||
var badCAKey = []byte(`-----BEGIN RSA PRIVATE KEY----- |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Forget to add a test that uses badCAKey?
"wildcard": { | ||
rule: apiserver.RuleWithOperations{ | ||
Rule: apiserver.Rule{ | ||
APIGroups: []string{"*"}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The validation rule requires the APIGroups/APIVersions/Resources/Operations to be non-empty. Can you update these rules to be valid?
fs.StringVar(&s.ProxyClientKeyFile, "proxy-client-key-file", s.ProxyClientKeyFile, | ||
"client certificate key used to prove the identity of the aggragator or kube-apiserver when it proxies requests to a user api-server") | ||
fs.StringVar(&s.ProxyClientCertFile, "proxy-client-cert-file", s.ProxyClientCertFile, ""+ | ||
"Client certificate used to prove the identity of the aggragator or kube-apiserver "+ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo
cloudConfig []byte | ||
restMapper meta.RESTMapper | ||
quotaRegistry quota.Registry | ||
type WantsServiceResolver interface { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Quick godoc on each
if e.Reason != "" { | ||
return fmt.Sprintf("rejected by webhook %q: %v", e.WebhookName, e.Reason) | ||
} | ||
return fmt.Sprintf("rejected by webhook %q without explanation", e.WebhookName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rejected by admission webhook ...
for both
"k8s.io/kubernetes/pkg/api" | ||
admissionv1alpha1 "k8s.io/kubernetes/pkg/apis/admission/v1alpha1" | ||
"k8s.io/kubernetes/pkg/apis/admissionregistration" | ||
adinit "k8s.io/kubernetes/pkg/kubeapiserver/admission" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
weird prefix, I don't care that much but should be admissioninit
defer wg.Done() | ||
allowed, reason, err := a.callHook(hook, attr) | ||
if err != nil { | ||
glog.Warningf("Failed calling webhook %v: %v", hook.Name, err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
utilruntime.HandleError()
wg.Wait() | ||
close(errCh) | ||
|
||
errs := []error{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
var errs []error
avoids allocating if errors is empty.
return fmt.Errorf("failed listing hooks: %v", err) | ||
} | ||
|
||
errCh := make(chan error, len(hooks)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wire a context through here so we can add cancelation in the future. context.TODO is fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
file a follow up issue on admission that we need to be passing a context down into the admission chain.
} | ||
|
||
var statusCode int | ||
if response.StatusCode(&statusCode); statusCode < 200 || statusCode >= 300 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is unnecessary and already handled by response.Error().
return false, "", fmt.Errorf("error contacting webhook %q: %d", h.Name, statusCode) | ||
} | ||
|
||
if err := response.Into(&request); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fold this into 172.
|
||
if !request.Status.Allowed { | ||
if request.Status.Result != nil { | ||
reason = string(request.Status.Result.Reason) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should be returning the actual status back, and then converting it to an error. We shouldn't be transforming status -> string -> error.
I.e. the status field should be passed (if specified) otherwise we need to create a stub one which is generic admission failure. That status should then be wrapped in StatusErr{}. I think you could just get away with returning the error here - reason doesn't need to be separately split.
} | ||
|
||
// TODO: cache these instead of constructing one each time | ||
cfg := &rest.Config{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Going to be really expensive to do this on every single admission call. Certainly can't afford to do it at scale. How high scale are you planning on using this at? I would say open a bug for this and we can fix it post dcut.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is alpha. So I think high scale isn't super important. If this is too limiting then we can fix it as a bug.
For e2e test we really need a way to start an image that has embedded code to act as a server. Do we have a small image we use in e2e that has go installed in it (so we can |
23e3a52
to
0ab9415
Compare
To properly register the types in the admission API group we need to create an "install" package and wire it up. This is required by the webhook admission controller being developed as part of kubernetes/community#132
As part of kubernetes/community#132, thsi commit adds a generic webhook admission controller. This plugin allows for a completely declarative approach for filtering/matching admission requests and for matching admission requests, calls out to an external webhook for handling admission requests.
5d12dc1
to
04a7269
Compare
@k8s-bot pull-kubernetes-e2e-gce-etcd3 test this |
1 similar comment
@k8s-bot pull-kubernetes-e2e-gce-etcd3 test this |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm. One nit.
limitations under the License. | ||
*/ | ||
|
||
// Package webhook checks a webhook for configured operation admission |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: s/checks a webhook/checks webhooks
if the etcd3 test fails again I will fix the nit, otherwise I'll fix in a followup. |
Also fix a bug in rest client
04a7269
to
c46e231
Compare
/lgtm May god have mercy on our souls |
@lavalamp: The following test(s) failed:
Full PR test history. Your PR dashboard. Please help us cut down on flakes by linking to an open issue when you hit one in your PR. Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. I understand the commands that are listed here. |
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: lavalamp, smarterclayton The full list of commands accepted by this bot can be found here.
Needs approval from an approver in each of these OWNERS Files:
You can indicate your approval by writing |
@k8s-bot pull-kubernetes-e2e-gce-etcd3 test this |
|
||
// SetWebhookSource sets the webhook source-- admittedly this is probably | ||
// specific to the external admission hook plugin. | ||
func (i *PluginInitializer) SetWebhookSource(w WebhookSource) *PluginInitializer { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm going to remove SetWebhookSource. This should be a SetExternalClient, and we should initialize a webhookconfiguration manager in the method, like what Clayton did in b75238f#diff-3551f205184f7273b78e802da8fdc72fR93.
Automatic merge from submit-queue (batch tested with PRs 46239, 46627, 46346, 46388, 46524) |
Automatic merge from submit-queue (batch tested with PRs 47204, 46808, 47432, 47400, 47099) Make the generic webhook admission controller use the dynamic webhook config manager Based on #46672 and #46388. Only the last commit is unique. * removed `SetWebhookSource` from the PluginInitializer * implemented `SetExternalClientset` for the generic webhook admisson controller, initializing an ExternalWebhookConfigurationManager in the method.
Automatic merge from submit-queue (batch tested with PRs 46076, 43879, 44897, 46556, 46654) Use meta.v1 GroupVersionKind with json tags to generate OpenAPI spec We are using two different GVK struct in generation of OpenAPI extensions. This PR unify that and also add json tags to meta.v1 GVK to comply with json naming system in other serializations. Also the value of Action extension is now lowercase. ref: kubernetes/kubernetes#46388
Automatic merge from submit-queue (batch tested with PRs 46076, 43879, 44897, 46556, 46654) Use meta.v1 GroupVersionKind with json tags to generate OpenAPI spec We are using two different GVK struct in generation of OpenAPI extensions. This PR unify that and also add json tags to meta.v1 GVK to comply with json naming system in other serializations. Also the value of Action extension is now lowercase. ref: kubernetes/kubernetes#46388 Kubernetes-commit: e97b72296f36f0282fc9453fe288d249d906667b
Unit tests pass.
Needs plumbing:
Also at least one thing will need to be renamed after Chao's PR merges.