k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/cmd/kubeadm/app/util/staticpod/utils_linux_test.go (about) 1 //go:build linux 2 // +build linux 3 4 /* 5 Copyright 2021 The Kubernetes Authors. 6 7 Licensed under the Apache License, Version 2.0 (the "License"); 8 you may not use this file except in compliance with the License. 9 You may obtain a copy of the License at 10 11 http://www.apache.org/licenses/LICENSE-2.0 12 13 Unless required by applicable law or agreed to in writing, software 14 distributed under the License is distributed on an "AS IS" BASIS, 15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 See the License for the specific language governing permissions and 17 limitations under the License. 18 */ 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 }