mirror of https://github.com/k3s-io/k3s
Added e2e test to check admission webhook works for pods/attach.
parent
64bd0e4589
commit
201111b644
|
@ -52,21 +52,23 @@ const (
|
|||
roleBindingName = "webhook-auth-reader"
|
||||
|
||||
// The webhook configuration names should not be reused between test instances.
|
||||
crWebhookConfigName = "e2e-test-webhook-config-cr"
|
||||
webhookConfigName = "e2e-test-webhook-config"
|
||||
mutatingWebhookConfigName = "e2e-test-mutating-webhook-config"
|
||||
podMutatingWebhookConfigName = "e2e-test-mutating-webhook-pod"
|
||||
crMutatingWebhookConfigName = "e2e-test-mutating-webhook-config-cr"
|
||||
webhookFailClosedConfigName = "e2e-test-webhook-fail-closed"
|
||||
webhookForWebhooksConfigName = "e2e-test-webhook-for-webhooks-config"
|
||||
removableValidatingHookName = "e2e-test-should-be-removable-validating-webhook-config"
|
||||
removableMutatingHookName = "e2e-test-should-be-removable-mutating-webhook-config"
|
||||
crdWebhookConfigName = "e2e-test-webhook-config-crd"
|
||||
crWebhookConfigName = "e2e-test-webhook-config-cr"
|
||||
webhookConfigName = "e2e-test-webhook-config"
|
||||
attachingPodWebhookConfigName = "e2e-test-webhook-config-attaching-pod"
|
||||
mutatingWebhookConfigName = "e2e-test-mutating-webhook-config"
|
||||
podMutatingWebhookConfigName = "e2e-test-mutating-webhook-pod"
|
||||
crMutatingWebhookConfigName = "e2e-test-mutating-webhook-config-cr"
|
||||
webhookFailClosedConfigName = "e2e-test-webhook-fail-closed"
|
||||
webhookForWebhooksConfigName = "e2e-test-webhook-for-webhooks-config"
|
||||
removableValidatingHookName = "e2e-test-should-be-removable-validating-webhook-config"
|
||||
removableMutatingHookName = "e2e-test-should-be-removable-mutating-webhook-config"
|
||||
crdWebhookConfigName = "e2e-test-webhook-config-crd"
|
||||
|
||||
skipNamespaceLabelKey = "skip-webhook-admission"
|
||||
skipNamespaceLabelValue = "yes"
|
||||
skippedNamespaceName = "exempted-namesapce"
|
||||
disallowedPodName = "disallowed-pod"
|
||||
toBeAttachedPodName = "to-be-attached-pod"
|
||||
hangingPodName = "hanging-pod"
|
||||
disallowedConfigMapName = "disallowed-configmap"
|
||||
allowedConfigMapName = "allowed-configmap"
|
||||
|
@ -117,6 +119,12 @@ var _ = SIGDescribe("AdmissionWebhook", func() {
|
|||
testWebhook(f)
|
||||
})
|
||||
|
||||
It("Should be able to deny attaching pod", func() {
|
||||
webhookCleanup := registerWebhookForAttachingPod(f, context)
|
||||
defer webhookCleanup()
|
||||
testAttachingPodWebhook(f)
|
||||
})
|
||||
|
||||
It("Should be able to deny custom resource creation", func() {
|
||||
testcrd, err := framework.CreateTestCRD(f)
|
||||
if err != nil {
|
||||
|
@ -405,6 +413,53 @@ func registerWebhook(f *framework.Framework, context *certContext) func() {
|
|||
}
|
||||
}
|
||||
|
||||
func registerWebhookForAttachingPod(f *framework.Framework, context *certContext) func() {
|
||||
client := f.ClientSet
|
||||
By("Registering the webhook via the AdmissionRegistration API")
|
||||
|
||||
namespace := f.Namespace.Name
|
||||
configName := attachingPodWebhookConfigName
|
||||
// A webhook that cannot talk to server, with fail-open policy
|
||||
failOpenHook := failingWebhook(namespace, "fail-open.k8s.io")
|
||||
policyIgnore := v1beta1.Ignore
|
||||
failOpenHook.FailurePolicy = &policyIgnore
|
||||
|
||||
_, err := client.AdmissionregistrationV1beta1().ValidatingWebhookConfigurations().Create(&v1beta1.ValidatingWebhookConfiguration{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: configName,
|
||||
},
|
||||
Webhooks: []v1beta1.Webhook{
|
||||
{
|
||||
Name: "deny-attaching-pod.k8s.io",
|
||||
Rules: []v1beta1.RuleWithOperations{{
|
||||
Operations: []v1beta1.OperationType{v1beta1.Connect},
|
||||
Rule: v1beta1.Rule{
|
||||
APIGroups: []string{""},
|
||||
APIVersions: []string{"v1"},
|
||||
Resources: []string{"pods/attach"},
|
||||
},
|
||||
}},
|
||||
ClientConfig: v1beta1.WebhookClientConfig{
|
||||
Service: &v1beta1.ServiceReference{
|
||||
Namespace: namespace,
|
||||
Name: serviceName,
|
||||
Path: strPtr("/pods/attach"),
|
||||
},
|
||||
CABundle: context.signingCert,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
framework.ExpectNoError(err, "registering webhook config %s with namespace %s", configName, namespace)
|
||||
|
||||
// The webhook configuration is honored in 10s.
|
||||
time.Sleep(10 * time.Second)
|
||||
|
||||
return func() {
|
||||
client.AdmissionregistrationV1beta1().ValidatingWebhookConfigurations().Delete(configName, nil)
|
||||
}
|
||||
}
|
||||
|
||||
func registerMutatingWebhookForConfigMap(f *framework.Framework, context *certContext) func() {
|
||||
client := f.ClientSet
|
||||
By("Registering the mutating configmap webhook via the AdmissionRegistration API")
|
||||
|
@ -642,6 +697,21 @@ func testWebhook(f *framework.Framework) {
|
|||
Expect(err).To(BeNil())
|
||||
}
|
||||
|
||||
func testAttachingPodWebhook(f *framework.Framework) {
|
||||
By("create a pod")
|
||||
client := f.ClientSet
|
||||
pod := toBeAttachedPod(f)
|
||||
_, err := client.CoreV1().Pods(f.Namespace.Name).Create(pod)
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
By("'kubectl attach' the pod, should be denied by the webhook")
|
||||
_, err = framework.NewKubectlCommand("attach", fmt.Sprintf("--namespace=%v", f.Namespace.Name), pod.Name, "-i", "-c=container1").Exec()
|
||||
Expect(err).NotTo(BeNil())
|
||||
if e, a := "attaching to pod 'to-be-attached-pod' is not allowed", err.Error(); !strings.Contains(a, e) {
|
||||
framework.Failf("unexpected 'kubectl attach' error message. expected to contain %q, got %q", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
// failingWebhook returns a webhook with rule of create configmaps,
|
||||
// but with an invalid client config so that server cannot communicate with it
|
||||
func failingWebhook(namespace, name string) v1beta1.Webhook {
|
||||
|
@ -930,6 +1000,22 @@ func hangingPod(f *framework.Framework) *v1.Pod {
|
|||
}
|
||||
}
|
||||
|
||||
func toBeAttachedPod(f *framework.Framework) *v1.Pod {
|
||||
return &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: toBeAttachedPodName,
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Name: "container1",
|
||||
Image: imageutils.GetPauseImageName(),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func nonCompliantConfigMap(f *framework.Framework) *v1.ConfigMap {
|
||||
return &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
|
|
|
@ -1 +1 @@
|
|||
1.12v1
|
||||
1.12v2
|
||||
|
|
|
@ -60,7 +60,7 @@ func serve(w http.ResponseWriter, r *http.Request, admit admitFunc) {
|
|||
return
|
||||
}
|
||||
|
||||
glog.V(2).Info(fmt.Sprintf("handling request: %v", body))
|
||||
glog.V(2).Info(fmt.Sprintf("handling request: %s", body))
|
||||
|
||||
// The AdmissionReview that was sent to the webhook
|
||||
requestedAdmissionReview := v1beta1.AdmissionReview{}
|
||||
|
@ -99,6 +99,10 @@ func servePods(w http.ResponseWriter, r *http.Request) {
|
|||
serve(w, r, admitPods)
|
||||
}
|
||||
|
||||
func serveAttachingPods(w http.ResponseWriter, r *http.Request) {
|
||||
serve(w, r, denySpecificAttachment)
|
||||
}
|
||||
|
||||
func serveMutatePods(w http.ResponseWriter, r *http.Request) {
|
||||
serve(w, r, mutatePods)
|
||||
}
|
||||
|
@ -130,6 +134,7 @@ func main() {
|
|||
|
||||
http.HandleFunc("/always-deny", serveAlwaysDeny)
|
||||
http.HandleFunc("/pods", servePods)
|
||||
http.HandleFunc("/pods/attach", serveAttachingPods)
|
||||
http.HandleFunc("/mutating-pods", serveMutatePods)
|
||||
http.HandleFunc("/configmaps", serveConfigmaps)
|
||||
http.HandleFunc("/mutating-configmaps", serveMutateConfigmaps)
|
||||
|
|
|
@ -101,3 +101,41 @@ func mutatePods(ar v1beta1.AdmissionReview) *v1beta1.AdmissionResponse {
|
|||
}
|
||||
return &reviewResponse
|
||||
}
|
||||
|
||||
// denySpecificAttachment denies `kubectl attach to-be-attached-pod -i -c=container1"
|
||||
// or equivalent client requests.
|
||||
func denySpecificAttachment(ar v1beta1.AdmissionReview) *v1beta1.AdmissionResponse {
|
||||
glog.V(2).Info("handling attaching pods")
|
||||
if ar.Request.Name != "to-be-attached-pod" {
|
||||
return &v1beta1.AdmissionResponse{Allowed: true}
|
||||
}
|
||||
podResource := metav1.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}
|
||||
if e, a := podResource, ar.Request.Resource; e != a {
|
||||
err := fmt.Errorf("expect resource to be %s, got %s", e, a)
|
||||
glog.Error(err)
|
||||
return toAdmissionResponse(err)
|
||||
}
|
||||
if e, a := "attach", ar.Request.SubResource; e != a {
|
||||
err := fmt.Errorf("expect subresource to be %s, got %s", e, a)
|
||||
glog.Error(err)
|
||||
return toAdmissionResponse(err)
|
||||
}
|
||||
|
||||
raw := ar.Request.Object.Raw
|
||||
podAttachOptions := corev1.PodAttachOptions{}
|
||||
deserializer := codecs.UniversalDeserializer()
|
||||
if _, _, err := deserializer.Decode(raw, nil, &podAttachOptions); err != nil {
|
||||
glog.Error(err)
|
||||
return toAdmissionResponse(err)
|
||||
}
|
||||
glog.V(2).Info(fmt.Sprintf("podAttachOptions=%#v\n", podAttachOptions))
|
||||
if !podAttachOptions.Stdin || podAttachOptions.Container != "container1" {
|
||||
return &v1beta1.AdmissionResponse{Allowed: true}
|
||||
}
|
||||
return &v1beta1.AdmissionResponse{
|
||||
Allowed: false,
|
||||
Result: &metav1.Status{
|
||||
Message: "attaching to pod 'to-be-attached-pod' is not allowed",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ func (i *ImageConfig) SetVersion(version string) {
|
|||
}
|
||||
|
||||
var (
|
||||
AdmissionWebhook = ImageConfig{e2eRegistry, "webhook", "1.12v1", false}
|
||||
AdmissionWebhook = ImageConfig{e2eRegistry, "webhook", "1.12v2", false}
|
||||
APIServer = ImageConfig{e2eRegistry, "sample-apiserver", "1.0", false}
|
||||
AppArmorLoader = ImageConfig{gcRegistry, "apparmor-loader", "0.1", false}
|
||||
BusyBox = ImageConfig{gcRegistry, "busybox", "1.24", false}
|
||||
|
|
Loading…
Reference in New Issue