Change:
<ins> /* Copyright 2017 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package auth import ( "fmt" "time" "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/wait" clientset "k8s.io/client-go/kubernetes" restclient "k8s.io/client-go/rest" "k8s.io/kubernetes/test/e2e/framework" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) const ( NodesGroup     = "system:nodes" NodeNamePrefix = "system:node:" ) var _ = SIGDescribe("[Feature:NodeAuthorizer]", func() { f := framework.NewDefaultFramework("node-authz") // client that will impersonate a node var c clientset.Interface var ns string var asUser string var defaultSaSecret string var nodeName string BeforeEach(func() { ns = f.Namespace.Name nodeList, err := f.ClientSet.CoreV1().Nodes().List(metav1.ListOptions{}) Expect(err).NotTo(HaveOccurred()) Expect(len(nodeList.Items)).NotTo(Equal(0)) nodeName = nodeList.Items[0].Name asUser = NodeNamePrefix + nodeName sa, err := f.ClientSet.CoreV1().ServiceAccounts(ns).Get("default", metav1.GetOptions{}) Expect(len(sa.Secrets)).NotTo(Equal(0)) Expect(err).NotTo(HaveOccurred()) defaultSaSecret = sa.Secrets[0].Name By("Creating a kubernetes client that impersonates a node") config, err := framework.LoadConfig() Expect(err).NotTo(HaveOccurred()) config.Impersonate = restclient.ImpersonationConfig{ UserName: asUser, Groups:   []string{NodesGroup}, } c, err = clientset.NewForConfig(config) Expect(err).NotTo(HaveOccurred()) }) It("Getting a non-existent secret should exit with the Forbidden error, not a NotFound error", func() { _, err := c.CoreV1().Secrets(ns).Get("foo", metav1.GetOptions{}) Expect(apierrors.IsForbidden(err)).Should(Equal(true)) }) It("Getting an existent secret should exit with the Forbidden error", func() { _, err := c.CoreV1().Secrets(ns).Get(defaultSaSecret, metav1.GetOptions{}) Expect(apierrors.IsForbidden(err)).Should(Equal(true)) }) It("Getting a secret for a workload the node has access to should succeed", func() { By("Create a secret for testing") secret := &v1.Secret{ ObjectMeta: metav1.ObjectMeta{ Namespace: ns, Name:      "node-auth-secret", }, Data: map[string][]byte{ "data": []byte("keep it secret"), }, } _, err := f.ClientSet.CoreV1().Secrets(ns).Create(secret) Expect(err).NotTo(HaveOccurred()) By("Node should not get the secret") _, err = c.CoreV1().Secrets(ns).Get(secret.Name, metav1.GetOptions{}) Expect(apierrors.IsForbidden(err)).Should(Equal(true)) By("Create a pod that use the secret") pod := &v1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "pause", }, Spec: v1.PodSpec{ Containers: []v1.Container{ { Name:  "pause", Image: framework.GetPauseImageName(f.ClientSet), }, }, NodeName: nodeName, Volumes: []v1.Volume{ { Name: "node-auth-secret", VolumeSource: v1.VolumeSource{ Secret: &v1.SecretVolumeSource{ SecretName: secret.Name, }, }, }, }, }, } _, err = f.ClientSet.CoreV1().Pods(ns).Create(pod) Expect(err).NotTo(HaveOccurred()) By("The node should able to access the secret") err = wait.Poll(framework.Poll, 1*time.Minute, func() (bool, error) { _, err = c.CoreV1().Secrets(ns).Get(secret.Name, metav1.GetOptions{}) if err != nil { framework.Logf("Failed to get secret %v, err: %v", secret.Name, err) return false, nil } return true, nil }) Expect(err).NotTo(HaveOccurred()) }) It("A node shouldn't be able to create an other node", func() { node := &v1.Node{ ObjectMeta: metav1.ObjectMeta{Name: "foo"}, TypeMeta: metav1.TypeMeta{ Kind:       "Node", APIVersion: "v1", }, } By(fmt.Sprintf("Create node foo by user: %v", asUser)) _, err := c.CoreV1().Nodes().Create(node) Expect(apierrors.IsForbidden(err)).Should(Equal(true)) }) It("A node shouldn't be able to delete an other node", func() { By(fmt.Sprintf("Create node foo by user: %v", asUser)) err := c.CoreV1().Nodes().Delete("foo", &metav1.DeleteOptions{}) Expect(apierrors.IsForbidden(err)).Should(Equal(true)) }) }) </ins>

Discussion:
Is this a BUG REPORT or FEATURE REQUEST? (choose one): TESTING REQUEST Spawned from: It would be nice with some e2e tests for the Node Authorizer so consumers of it (kubeadm, GCE, GKE, etc.) could easily make sure it's enabled and working, just by editing when running e2e's. I wrote a quick bash script of what it could do: The bash script above is in no way comprehensive enough, but on the other hand it took ~5mins to write :) For now (1.7), we might use something like this bash script in our kubeadm e2e CI job to make sure the Node Authorizer/Admission plugin is enabled correctly. cc
was assigned for triaging this... Does such an e2e test already exist somewhere? said that he could take this on if not...
no negative e2e test exists (make sure things the Node authorizer disallows fail) negative integration tests exist at negative e2e tests for this will be tricky, since you'll need node credentials and a specific authz and admission config.
See the above bash script, I was able to do it purely from kubectl by impersonating the group and node user... If we feature-gate this e2e test, we can run it for the deployments that actually have unique node credentials
cc this maybe could be a good task for you... :)
Cool, thanks for point me this.
You basically only have to implement these commands as an e2e test initially (by impersonating a node in the cluster). Then when that's done, we can check if there are more than these four cases that needs to be verified. This should go under a separate and be enabled in deployments (like kubeadm) where nodes have unique credentials.
I didn't find a way to simulate a node credential, do you have any good idea ? after reading the integration testing code, I fell it's enough the guarantee the quality, WDYT?