1
2
3
4
19
20 package staticpod
21
22 import (
23 "path/filepath"
24 "reflect"
25 "testing"
26
27 v1 "k8s.io/api/core/v1"
28 "k8s.io/utils/ptr"
29
30 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
31 kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
32 )
33
34 type ownerAndPermissions struct {
35 uid int64
36 gid int64
37 permissions uint32
38 }
39
40 func verifyPodSecurityContext(t *testing.T, pod *v1.Pod, wantRunAsUser, wantRunAsGroup int64, wantSupGroup []int64) {
41 t.Helper()
42 wantPodSecurityContext := &v1.PodSecurityContext{
43 RunAsUser: ptr.To(wantRunAsUser),
44 RunAsGroup: ptr.To(wantRunAsGroup),
45 SupplementalGroups: wantSupGroup,
46 SeccompProfile: &v1.SeccompProfile{
47 Type: v1.SeccompProfileTypeRuntimeDefault,
48 },
49 }
50 if !reflect.DeepEqual(wantPodSecurityContext, pod.Spec.SecurityContext) {
51 t.Errorf("unexpected diff in PodSecurityContext, want: %+v, got: %+v", wantPodSecurityContext, pod.Spec.SecurityContext)
52 }
53 }
54
55 func verifyContainerSecurityContext(t *testing.T, container v1.Container, addCaps, dropCaps []v1.Capability, allowPrivielege *bool) {
56 t.Helper()
57 wantContainerSecurityContext := &v1.SecurityContext{
58 AllowPrivilegeEscalation: allowPrivielege,
59 Capabilities: &v1.Capabilities{
60 Add: addCaps,
61 Drop: dropCaps,
62 },
63 }
64 if !reflect.DeepEqual(wantContainerSecurityContext, container.SecurityContext) {
65 t.Errorf("unexpected diff in container SecurityContext, want: %+v, got: %+v", wantContainerSecurityContext, container.SecurityContext)
66 }
67 }
68
69 func verifyFilePermissions(t *testing.T, updatedFiles, wantFiles map[string]ownerAndPermissions) {
70 t.Helper()
71 if !reflect.DeepEqual(updatedFiles, wantFiles) {
72 t.Errorf("unexpected diff in file owners and permissions want: %+v, got: %+v", wantFiles, updatedFiles)
73 }
74 }
75
76 func TestRunKubeAPIServerAsNonRoot(t *testing.T) {
77 cfg := &kubeadm.ClusterConfiguration{}
78 pod := ComponentPod(v1.Container{Name: "kube-apiserver"}, nil, nil)
79 var runAsUser, runAsGroup, supGroup int64 = 1000, 1001, 1002
80 updatedFiles := map[string]ownerAndPermissions{}
81 if err := runKubeAPIServerAsNonRoot(&pod, &runAsUser, &runAsGroup, &supGroup, func(path string, uid, gid int64, perms uint32) error {
82 updatedFiles[path] = ownerAndPermissions{uid: uid, gid: gid, permissions: perms}
83 return nil
84 }, cfg); err != nil {
85 t.Fatal(err)
86 }
87 verifyPodSecurityContext(t, &pod, runAsUser, runAsGroup, []int64{supGroup})
88 verifyContainerSecurityContext(t, pod.Spec.Containers[0], []v1.Capability{"NET_BIND_SERVICE"}, []v1.Capability{"ALL"}, nil)
89 wantUpdateFiles := map[string]ownerAndPermissions{
90 filepath.Join(cfg.CertificatesDir, kubeadmconstants.ServiceAccountPublicKeyName): {uid: runAsUser, gid: runAsGroup, permissions: 0600},
91 filepath.Join(cfg.CertificatesDir, kubeadmconstants.ServiceAccountPrivateKeyName): {uid: 0, gid: supGroup, permissions: 0640},
92 filepath.Join(cfg.CertificatesDir, kubeadmconstants.APIServerKeyName): {uid: runAsUser, gid: runAsGroup, permissions: 0600},
93 filepath.Join(cfg.CertificatesDir, kubeadmconstants.APIServerKubeletClientKeyName): {uid: runAsUser, gid: runAsGroup, permissions: 0600},
94 filepath.Join(cfg.CertificatesDir, kubeadmconstants.FrontProxyClientKeyName): {uid: runAsUser, gid: runAsGroup, permissions: 0600},
95 filepath.Join(cfg.CertificatesDir, kubeadmconstants.APIServerEtcdClientKeyName): {uid: runAsUser, gid: runAsGroup, permissions: 0600},
96 }
97 verifyFilePermissions(t, updatedFiles, wantUpdateFiles)
98 }
99
100 func TestRunKubeControllerManagerAsNonRoot(t *testing.T) {
101 cfg := &kubeadm.ClusterConfiguration{}
102 pod := ComponentPod(v1.Container{Name: "kube-controller-manager"}, nil, nil)
103 var runAsUser, runAsGroup, supGroup int64 = 1000, 1001, 1002
104 updatedFiles := map[string]ownerAndPermissions{}
105 if err := runKubeControllerManagerAsNonRoot(&pod, &runAsUser, &runAsGroup, &supGroup, func(path string, uid, gid int64, perms uint32) error {
106 updatedFiles[path] = ownerAndPermissions{uid: uid, gid: gid, permissions: perms}
107 return nil
108 }, cfg); err != nil {
109 t.Fatal(err)
110 }
111 verifyPodSecurityContext(t, &pod, runAsUser, runAsGroup, []int64{supGroup})
112 verifyContainerSecurityContext(t, pod.Spec.Containers[0], nil, []v1.Capability{"ALL"}, ptr.To(false))
113 wantUpdateFiles := map[string]ownerAndPermissions{
114 filepath.Join(kubeadmconstants.KubernetesDir, kubeadmconstants.ControllerManagerKubeConfigFileName): {uid: runAsUser, gid: runAsGroup, permissions: 0600},
115 filepath.Join(cfg.CertificatesDir, kubeadmconstants.ServiceAccountPrivateKeyName): {uid: 0, gid: supGroup, permissions: 0640},
116 filepath.Join(cfg.CertificatesDir, kubeadmconstants.CAKeyName): {uid: runAsUser, gid: runAsGroup, permissions: 0600},
117 }
118 verifyFilePermissions(t, updatedFiles, wantUpdateFiles)
119 }
120
121 func TestRunKubeSchedulerAsNonRoot(t *testing.T) {
122 pod := ComponentPod(v1.Container{Name: "kube-scheduler"}, nil, nil)
123 var runAsUser, runAsGroup int64 = 1000, 1001
124 updatedFiles := map[string]ownerAndPermissions{}
125 if err := runKubeSchedulerAsNonRoot(&pod, &runAsUser, &runAsGroup, func(path string, uid, gid int64, perms uint32) error {
126 updatedFiles[path] = ownerAndPermissions{uid: uid, gid: gid, permissions: perms}
127 return nil
128 }); err != nil {
129 t.Fatal(err)
130 }
131 verifyPodSecurityContext(t, &pod, runAsUser, runAsGroup, nil)
132 verifyContainerSecurityContext(t, pod.Spec.Containers[0], nil, []v1.Capability{"ALL"}, ptr.To(false))
133 wantUpdateFiles := map[string]ownerAndPermissions{
134 filepath.Join(kubeadmconstants.KubernetesDir, kubeadmconstants.SchedulerKubeConfigFileName): {uid: runAsUser, gid: runAsGroup, permissions: 0600},
135 }
136 verifyFilePermissions(t, updatedFiles, wantUpdateFiles)
137 }
138
139 func TestRunEtcdAsNonRoot(t *testing.T) {
140 cfg := &kubeadm.ClusterConfiguration{
141 Etcd: kubeadm.Etcd{
142 Local: &kubeadm.LocalEtcd{
143 DataDir: "/var/lib/etcd/data",
144 },
145 },
146 }
147 pod := ComponentPod(v1.Container{Name: "etcd"}, nil, nil)
148 var runAsUser, runAsGroup int64 = 1000, 1001
149 updatedFiles := map[string]ownerAndPermissions{}
150 if err := runEtcdAsNonRoot(&pod, &runAsUser, &runAsGroup, func(path string, uid, gid int64, perms uint32) error {
151 updatedFiles[path] = ownerAndPermissions{uid: uid, gid: gid, permissions: perms}
152 return nil
153 },
154 func(path string, uid, gid int64) error {
155 updatedFiles[path] = ownerAndPermissions{uid: uid, gid: gid, permissions: 0700}
156 return nil
157 }, cfg); err != nil {
158 t.Fatal(err)
159 }
160 verifyPodSecurityContext(t, &pod, runAsUser, runAsGroup, nil)
161 verifyContainerSecurityContext(t, pod.Spec.Containers[0], nil, []v1.Capability{"ALL"}, ptr.To(false))
162 wantUpdateFiles := map[string]ownerAndPermissions{
163 cfg.Etcd.Local.DataDir: {uid: runAsUser, gid: runAsGroup, permissions: 0700},
164 filepath.Join(cfg.CertificatesDir, kubeadmconstants.EtcdServerKeyName): {uid: runAsUser, gid: runAsGroup, permissions: 0600},
165 filepath.Join(cfg.CertificatesDir, kubeadmconstants.EtcdPeerKeyName): {uid: runAsUser, gid: runAsGroup, permissions: 0600},
166 }
167 verifyFilePermissions(t, updatedFiles, wantUpdateFiles)
168 }
169
View as plain text