1
2
3
4
19
20 package staticpod
21
22 import (
23 "fmt"
24 "path/filepath"
25
26 "github.com/pkg/errors"
27
28 v1 "k8s.io/api/core/v1"
29 "k8s.io/utils/ptr"
30
31 kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
32 kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
33 certphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs"
34 "k8s.io/kubernetes/cmd/kubeadm/app/util/users"
35 )
36
37 type pathOwnerAndPermissionsUpdaterFunc func(path string, uid, gid int64, perms uint32) error
38 type pathOwnerUpdaterFunc func(path string, uid, gid int64) error
39
40
41 func RunComponentAsNonRoot(componentName string, pod *v1.Pod, usersAndGroups *users.UsersAndGroups, cfg *kubeadmapi.ClusterConfiguration) error {
42 switch componentName {
43 case kubeadmconstants.KubeAPIServer:
44 return runKubeAPIServerAsNonRoot(
45 pod,
46 usersAndGroups.Users.ID(kubeadmconstants.KubeAPIServerUserName),
47 usersAndGroups.Groups.ID(kubeadmconstants.KubeAPIServerUserName),
48 usersAndGroups.Groups.ID(kubeadmconstants.ServiceAccountKeyReadersGroupName),
49 users.UpdatePathOwnerAndPermissions,
50 cfg,
51 )
52 case kubeadmconstants.KubeControllerManager:
53 return runKubeControllerManagerAsNonRoot(
54 pod,
55 usersAndGroups.Users.ID(kubeadmconstants.KubeControllerManagerUserName),
56 usersAndGroups.Groups.ID(kubeadmconstants.KubeControllerManagerUserName),
57 usersAndGroups.Groups.ID(kubeadmconstants.ServiceAccountKeyReadersGroupName),
58 users.UpdatePathOwnerAndPermissions,
59 cfg,
60 )
61 case kubeadmconstants.KubeScheduler:
62 return runKubeSchedulerAsNonRoot(
63 pod,
64 usersAndGroups.Users.ID(kubeadmconstants.KubeSchedulerUserName),
65 usersAndGroups.Groups.ID(kubeadmconstants.KubeSchedulerUserName),
66 users.UpdatePathOwnerAndPermissions,
67 )
68 case kubeadmconstants.Etcd:
69 return runEtcdAsNonRoot(
70 pod,
71 usersAndGroups.Users.ID(kubeadmconstants.EtcdUserName),
72 usersAndGroups.Groups.ID(kubeadmconstants.EtcdUserName),
73 users.UpdatePathOwnerAndPermissions,
74 users.UpdatePathOwner,
75 cfg,
76 )
77 }
78 return errors.New(fmt.Sprintf("component name %q is not valid", componentName))
79 }
80
81
82 func runKubeAPIServerAsNonRoot(pod *v1.Pod, runAsUser, runAsGroup, supplementalGroup *int64, updatePathOwnerAndPermissions pathOwnerAndPermissionsUpdaterFunc, cfg *kubeadmapi.ClusterConfiguration) error {
83 saPublicKeyFile := filepath.Join(cfg.CertificatesDir, kubeadmconstants.ServiceAccountPublicKeyName)
84 if err := updatePathOwnerAndPermissions(saPublicKeyFile, *runAsUser, *runAsGroup, 0600); err != nil {
85 return err
86 }
87 saPrivateKeyFile := filepath.Join(cfg.CertificatesDir, kubeadmconstants.ServiceAccountPrivateKeyName)
88 if err := updatePathOwnerAndPermissions(saPrivateKeyFile, 0, *supplementalGroup, 0640); err != nil {
89 return err
90 }
91 apiServerKeyFile := filepath.Join(cfg.CertificatesDir, kubeadmconstants.APIServerKeyName)
92 if err := updatePathOwnerAndPermissions(apiServerKeyFile, *runAsUser, *runAsGroup, 0600); err != nil {
93 return err
94 }
95 apiServerKubeletClientKeyFile := filepath.Join(cfg.CertificatesDir, kubeadmconstants.APIServerKubeletClientKeyName)
96 if err := updatePathOwnerAndPermissions(apiServerKubeletClientKeyFile, *runAsUser, *runAsGroup, 0600); err != nil {
97 return err
98 }
99 frontProxyClientKeyName := filepath.Join(cfg.CertificatesDir, kubeadmconstants.FrontProxyClientKeyName)
100 if err := updatePathOwnerAndPermissions(frontProxyClientKeyName, *runAsUser, *runAsGroup, 0600); err != nil {
101 return err
102 }
103 if cfg.Etcd.External == nil {
104 apiServerEtcdClientKeyFile := filepath.Join(cfg.CertificatesDir, kubeadmconstants.APIServerEtcdClientKeyName)
105 if err := updatePathOwnerAndPermissions(apiServerEtcdClientKeyFile, *runAsUser, *runAsGroup, 0600); err != nil {
106 return err
107 }
108 }
109 pod.Spec.Containers[0].SecurityContext = &v1.SecurityContext{
110 Capabilities: &v1.Capabilities{
111
112 Drop: []v1.Capability{"ALL"},
113
114
115
116 Add: []v1.Capability{"NET_BIND_SERVICE"},
117 },
118 }
119 pod.Spec.SecurityContext.RunAsGroup = runAsGroup
120 pod.Spec.SecurityContext.RunAsUser = runAsUser
121 pod.Spec.SecurityContext.SupplementalGroups = []int64{*supplementalGroup}
122 return nil
123 }
124
125
126 func runKubeControllerManagerAsNonRoot(pod *v1.Pod, runAsUser, runAsGroup, supplementalGroup *int64, updatePathOwnerAndPermissions pathOwnerAndPermissionsUpdaterFunc, cfg *kubeadmapi.ClusterConfiguration) error {
127 kubeconfigFile := filepath.Join(kubeadmconstants.KubernetesDir, kubeadmconstants.ControllerManagerKubeConfigFileName)
128 if err := updatePathOwnerAndPermissions(kubeconfigFile, *runAsUser, *runAsGroup, 0600); err != nil {
129 return err
130 }
131 saPrivateKeyFile := filepath.Join(cfg.CertificatesDir, kubeadmconstants.ServiceAccountPrivateKeyName)
132 if err := updatePathOwnerAndPermissions(saPrivateKeyFile, 0, *supplementalGroup, 0640); err != nil {
133 return err
134 }
135 if res, _ := certphase.UsingExternalCA(cfg); !res {
136 caKeyFile := filepath.Join(cfg.CertificatesDir, kubeadmconstants.CAKeyName)
137 err := updatePathOwnerAndPermissions(caKeyFile, *runAsUser, *runAsGroup, 0600)
138 if err != nil {
139 return err
140 }
141 }
142 pod.Spec.Containers[0].SecurityContext = &v1.SecurityContext{
143 AllowPrivilegeEscalation: ptr.To(false),
144 Capabilities: &v1.Capabilities{
145
146 Drop: []v1.Capability{"ALL"},
147 },
148 }
149 pod.Spec.SecurityContext.RunAsUser = runAsUser
150 pod.Spec.SecurityContext.RunAsGroup = runAsGroup
151 pod.Spec.SecurityContext.SupplementalGroups = []int64{*supplementalGroup}
152 return nil
153 }
154
155
156 func runKubeSchedulerAsNonRoot(pod *v1.Pod, runAsUser, runAsGroup *int64, updatePathOwnerAndPermissions pathOwnerAndPermissionsUpdaterFunc) error {
157 kubeconfigFile := filepath.Join(kubeadmconstants.KubernetesDir, kubeadmconstants.SchedulerKubeConfigFileName)
158 if err := updatePathOwnerAndPermissions(kubeconfigFile, *runAsUser, *runAsGroup, 0600); err != nil {
159 return err
160 }
161 pod.Spec.Containers[0].SecurityContext = &v1.SecurityContext{
162 AllowPrivilegeEscalation: ptr.To(false),
163
164 Capabilities: &v1.Capabilities{
165 Drop: []v1.Capability{"ALL"},
166 },
167 }
168 pod.Spec.SecurityContext.RunAsUser = runAsUser
169 pod.Spec.SecurityContext.RunAsGroup = runAsGroup
170 return nil
171 }
172
173
174 func runEtcdAsNonRoot(pod *v1.Pod, runAsUser, runAsGroup *int64, updatePathOwnerAndPermissions pathOwnerAndPermissionsUpdaterFunc, updatePathOwner pathOwnerUpdaterFunc, cfg *kubeadmapi.ClusterConfiguration) error {
175 if err := updatePathOwner(cfg.Etcd.Local.DataDir, *runAsUser, *runAsGroup); err != nil {
176 return err
177 }
178 etcdServerKeyFile := filepath.Join(cfg.CertificatesDir, kubeadmconstants.EtcdServerKeyName)
179 if err := updatePathOwnerAndPermissions(etcdServerKeyFile, *runAsUser, *runAsGroup, 0600); err != nil {
180 return err
181 }
182 etcdPeerKeyFile := filepath.Join(cfg.CertificatesDir, kubeadmconstants.EtcdPeerKeyName)
183 if err := updatePathOwnerAndPermissions(etcdPeerKeyFile, *runAsUser, *runAsGroup, 0600); err != nil {
184 return err
185 }
186 pod.Spec.Containers[0].SecurityContext = &v1.SecurityContext{
187 AllowPrivilegeEscalation: ptr.To(false),
188
189 Capabilities: &v1.Capabilities{
190 Drop: []v1.Capability{"ALL"},
191 },
192 }
193 pod.Spec.SecurityContext.RunAsUser = runAsUser
194 pod.Spec.SecurityContext.RunAsGroup = runAsGroup
195 return nil
196 }
197
View as plain text