k8s.io/kubernetes@v1.29.3/test/e2e/auth/node_authz.go (about) 1 /* 2 Copyright 2017 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package auth 18 19 import ( 20 "context" 21 "fmt" 22 "time" 23 24 apierrors "k8s.io/apimachinery/pkg/api/errors" 25 26 v1 "k8s.io/api/core/v1" 27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 "k8s.io/apimachinery/pkg/util/wait" 29 clientset "k8s.io/client-go/kubernetes" 30 restclient "k8s.io/client-go/rest" 31 "k8s.io/kubernetes/test/e2e/feature" 32 "k8s.io/kubernetes/test/e2e/framework" 33 imageutils "k8s.io/kubernetes/test/utils/image" 34 admissionapi "k8s.io/pod-security-admission/api" 35 36 "github.com/onsi/ginkgo/v2" 37 "github.com/onsi/gomega" 38 ) 39 40 const ( 41 nodesGroup = "system:nodes" 42 nodeNamePrefix = "system:node:" 43 ) 44 45 var _ = SIGDescribe(feature.NodeAuthorizer, func() { 46 47 f := framework.NewDefaultFramework("node-authz") 48 f.NamespacePodSecurityLevel = admissionapi.LevelBaseline 49 // client that will impersonate a node 50 var c clientset.Interface 51 var ns string 52 var asUser string 53 var nodeName string 54 ginkgo.BeforeEach(func(ctx context.Context) { 55 ns = f.Namespace.Name 56 57 nodeList, err := f.ClientSet.CoreV1().Nodes().List(ctx, metav1.ListOptions{}) 58 framework.ExpectNoError(err, "failed to list nodes in namespace: %s", ns) 59 gomega.Expect(nodeList.Items).NotTo(gomega.BeEmpty()) 60 nodeName = nodeList.Items[0].Name 61 asUser = nodeNamePrefix + nodeName 62 ginkgo.By("Creating a kubernetes client that impersonates a node") 63 config, err := framework.LoadConfig() 64 framework.ExpectNoError(err, "failed to load kubernetes client config") 65 config.Impersonate = restclient.ImpersonationConfig{ 66 UserName: asUser, 67 Groups: []string{nodesGroup}, 68 } 69 c, err = clientset.NewForConfig(config) 70 framework.ExpectNoError(err, "failed to create Clientset for the given config: %+v", *config) 71 72 }) 73 ginkgo.It("Getting a non-existent secret should exit with the Forbidden error, not a NotFound error", func(ctx context.Context) { 74 _, err := c.CoreV1().Secrets(ns).Get(ctx, "foo", metav1.GetOptions{}) 75 if !apierrors.IsForbidden(err) { 76 framework.Failf("should be a forbidden error, got %#v", err) 77 } 78 }) 79 80 ginkgo.It("Getting an existing secret should exit with the Forbidden error", func(ctx context.Context) { 81 ginkgo.By("Create a secret for testing") 82 secret := &v1.Secret{ 83 ObjectMeta: metav1.ObjectMeta{ 84 Namespace: ns, 85 Name: "node-auth-secret", 86 }, 87 StringData: map[string]string{}, 88 } 89 _, err := f.ClientSet.CoreV1().Secrets(ns).Create(ctx, secret, metav1.CreateOptions{}) 90 framework.ExpectNoError(err, "failed to create secret (%s:%s) %+v", ns, secret.Name, *secret) 91 _, err = c.CoreV1().Secrets(ns).Get(ctx, secret.Name, metav1.GetOptions{}) 92 if !apierrors.IsForbidden(err) { 93 framework.Failf("should be a forbidden error, got %#v", err) 94 } 95 }) 96 97 ginkgo.It("Getting a non-existent configmap should exit with the Forbidden error, not a NotFound error", func(ctx context.Context) { 98 _, err := c.CoreV1().ConfigMaps(ns).Get(ctx, "foo", metav1.GetOptions{}) 99 if !apierrors.IsForbidden(err) { 100 framework.Failf("should be a forbidden error, got %#v", err) 101 } 102 }) 103 104 ginkgo.It("Getting an existing configmap should exit with the Forbidden error", func(ctx context.Context) { 105 ginkgo.By("Create a configmap for testing") 106 configmap := &v1.ConfigMap{ 107 ObjectMeta: metav1.ObjectMeta{ 108 Namespace: ns, 109 Name: "node-auth-configmap", 110 }, 111 Data: map[string]string{ 112 "data": "content", 113 }, 114 } 115 _, err := f.ClientSet.CoreV1().ConfigMaps(ns).Create(ctx, configmap, metav1.CreateOptions{}) 116 framework.ExpectNoError(err, "failed to create configmap (%s:%s) %+v", ns, configmap.Name, *configmap) 117 _, err = c.CoreV1().ConfigMaps(ns).Get(ctx, configmap.Name, metav1.GetOptions{}) 118 if !apierrors.IsForbidden(err) { 119 framework.Failf("should be a forbidden error, got %#v", err) 120 } 121 }) 122 123 ginkgo.It("Getting a secret for a workload the node has access to should succeed", func(ctx context.Context) { 124 ginkgo.By("Create a secret for testing") 125 secret := &v1.Secret{ 126 ObjectMeta: metav1.ObjectMeta{ 127 Namespace: ns, 128 Name: "node-auth-secret", 129 }, 130 Data: map[string][]byte{ 131 "data": []byte("keep it secret"), 132 }, 133 } 134 _, err := f.ClientSet.CoreV1().Secrets(ns).Create(ctx, secret, metav1.CreateOptions{}) 135 framework.ExpectNoError(err, "failed to create secret (%s:%s)", ns, secret.Name) 136 137 ginkgo.By("Node should not get the secret") 138 _, err = c.CoreV1().Secrets(ns).Get(ctx, secret.Name, metav1.GetOptions{}) 139 if !apierrors.IsForbidden(err) { 140 framework.Failf("should be a forbidden error, got %#v", err) 141 } 142 143 ginkgo.By("Create a pod that use the secret") 144 pod := &v1.Pod{ 145 ObjectMeta: metav1.ObjectMeta{ 146 Name: "pause", 147 }, 148 Spec: v1.PodSpec{ 149 Containers: []v1.Container{ 150 { 151 Name: "pause", 152 Image: imageutils.GetPauseImageName(), 153 }, 154 }, 155 NodeName: nodeName, 156 Volumes: []v1.Volume{ 157 { 158 Name: "node-auth-secret", 159 VolumeSource: v1.VolumeSource{ 160 Secret: &v1.SecretVolumeSource{ 161 SecretName: secret.Name, 162 }, 163 }, 164 }, 165 }, 166 }, 167 } 168 169 _, err = f.ClientSet.CoreV1().Pods(ns).Create(ctx, pod, metav1.CreateOptions{}) 170 framework.ExpectNoError(err, "failed to create pod (%s:%s)", ns, pod.Name) 171 172 ginkgo.By("The node should able to access the secret") 173 itv := framework.Poll 174 dur := 1 * time.Minute 175 err = wait.Poll(itv, dur, func() (bool, error) { 176 _, err = c.CoreV1().Secrets(ns).Get(ctx, secret.Name, metav1.GetOptions{}) 177 if err != nil { 178 framework.Logf("Failed to get secret %v, err: %v", secret.Name, err) 179 return false, nil 180 } 181 return true, nil 182 }) 183 framework.ExpectNoError(err, "failed to get secret after trying every %v for %v (%s:%s)", itv, dur, ns, secret.Name) 184 }) 185 186 ginkgo.It("A node shouldn't be able to create another node", func(ctx context.Context) { 187 node := &v1.Node{ 188 ObjectMeta: metav1.ObjectMeta{Name: "foo"}, 189 TypeMeta: metav1.TypeMeta{ 190 Kind: "Node", 191 APIVersion: "v1", 192 }, 193 } 194 ginkgo.By(fmt.Sprintf("Create node foo by user: %v", asUser)) 195 _, err := c.CoreV1().Nodes().Create(ctx, node, metav1.CreateOptions{}) 196 197 // NOTE: If the test fails and a new node IS created, we need to delete it. If we don't, we'd have 198 // a zombie node in a NotReady state which will delay further tests since we're waiting for all 199 // tests to be in the Ready state. 200 ginkgo.DeferCleanup(framework.IgnoreNotFound(f.ClientSet.CoreV1().Nodes().Delete), node.Name, metav1.DeleteOptions{}) 201 202 if !apierrors.IsForbidden(err) { 203 framework.Failf("should be a forbidden error, got %#v", err) 204 } 205 }) 206 207 ginkgo.It("A node shouldn't be able to delete another node", func(ctx context.Context) { 208 ginkgo.By(fmt.Sprintf("Create node foo by user: %v", asUser)) 209 err := c.CoreV1().Nodes().Delete(ctx, "foo", metav1.DeleteOptions{}) 210 if !apierrors.IsForbidden(err) { 211 framework.Failf("should be a forbidden error, got %#v", err) 212 } 213 }) 214 })