1
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
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
198
199
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 })
215
View as plain text