admission.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. /*
  2. Copyright 2016 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package podsecuritypolicy
  14. import (
  15. "fmt"
  16. "io"
  17. "sort"
  18. "strings"
  19. "k8s.io/klog"
  20. policyv1beta1 "k8s.io/api/policy/v1beta1"
  21. apiequality "k8s.io/apimachinery/pkg/api/equality"
  22. "k8s.io/apimachinery/pkg/labels"
  23. "k8s.io/apimachinery/pkg/util/validation/field"
  24. "k8s.io/apiserver/pkg/admission"
  25. genericadmissioninit "k8s.io/apiserver/pkg/admission/initializer"
  26. "k8s.io/apiserver/pkg/authentication/user"
  27. "k8s.io/apiserver/pkg/authorization/authorizer"
  28. "k8s.io/client-go/informers"
  29. policylisters "k8s.io/client-go/listers/policy/v1beta1"
  30. api "k8s.io/kubernetes/pkg/apis/core"
  31. "k8s.io/kubernetes/pkg/apis/extensions"
  32. "k8s.io/kubernetes/pkg/apis/policy"
  33. rbacregistry "k8s.io/kubernetes/pkg/registry/rbac"
  34. psp "k8s.io/kubernetes/pkg/security/podsecuritypolicy"
  35. psputil "k8s.io/kubernetes/pkg/security/podsecuritypolicy/util"
  36. "k8s.io/kubernetes/pkg/serviceaccount"
  37. )
  38. // PluginName is a string with the name of the plugin
  39. const PluginName = "PodSecurityPolicy"
  40. // Register registers a plugin
  41. func Register(plugins *admission.Plugins) {
  42. plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
  43. plugin := newPlugin(psp.NewSimpleStrategyFactory(), true)
  44. return plugin, nil
  45. })
  46. }
  47. // Plugin holds state for and implements the admission plugin.
  48. type Plugin struct {
  49. *admission.Handler
  50. strategyFactory psp.StrategyFactory
  51. failOnNoPolicies bool
  52. authz authorizer.Authorizer
  53. lister policylisters.PodSecurityPolicyLister
  54. }
  55. // SetAuthorizer sets the authorizer.
  56. func (p *Plugin) SetAuthorizer(authz authorizer.Authorizer) {
  57. p.authz = authz
  58. }
  59. // ValidateInitialization ensures an authorizer is set.
  60. func (p *Plugin) ValidateInitialization() error {
  61. if p.authz == nil {
  62. return fmt.Errorf("%s requires an authorizer", PluginName)
  63. }
  64. if p.lister == nil {
  65. return fmt.Errorf("%s requires a lister", PluginName)
  66. }
  67. return nil
  68. }
  69. var _ admission.MutationInterface = &Plugin{}
  70. var _ admission.ValidationInterface = &Plugin{}
  71. var _ genericadmissioninit.WantsAuthorizer = &Plugin{}
  72. var _ genericadmissioninit.WantsExternalKubeInformerFactory = &Plugin{}
  73. var auditKeyPrefix = strings.ToLower(PluginName) + "." + policy.GroupName + ".k8s.io"
  74. // newPlugin creates a new PSP admission plugin.
  75. func newPlugin(strategyFactory psp.StrategyFactory, failOnNoPolicies bool) *Plugin {
  76. return &Plugin{
  77. Handler: admission.NewHandler(admission.Create, admission.Update),
  78. strategyFactory: strategyFactory,
  79. failOnNoPolicies: failOnNoPolicies,
  80. }
  81. }
  82. // SetExternalKubeInformerFactory registers an informer
  83. func (p *Plugin) SetExternalKubeInformerFactory(f informers.SharedInformerFactory) {
  84. podSecurityPolicyInformer := f.Policy().V1beta1().PodSecurityPolicies()
  85. p.lister = podSecurityPolicyInformer.Lister()
  86. p.SetReadyFunc(podSecurityPolicyInformer.Informer().HasSynced)
  87. }
  88. // Admit determines if the pod should be admitted based on the requested security context
  89. // and the available PSPs.
  90. //
  91. // 1. Find available PSPs.
  92. // 2. Create the providers, includes setting pre-allocated values if necessary.
  93. // 3. Try to generate and validate a PSP with providers. If we find one then admit the pod
  94. // with the validated PSP. If we don't find any reject the pod and give all errors from the
  95. // failed attempts.
  96. func (p *Plugin) Admit(a admission.Attributes, o admission.ObjectInterfaces) error {
  97. if ignore, err := shouldIgnore(a); err != nil {
  98. return err
  99. } else if ignore {
  100. return nil
  101. }
  102. // only mutate if this is a CREATE request. On updates we only validate.
  103. // TODO(liggitt): allow spec mutation during initializing updates?
  104. if a.GetOperation() != admission.Create {
  105. return nil
  106. }
  107. pod := a.GetObject().(*api.Pod)
  108. // compute the context. Mutation is allowed. ValidatedPSPAnnotation is not taken into account.
  109. allowedPod, pspName, validationErrs, err := p.computeSecurityContext(a, pod, true, "")
  110. if err != nil {
  111. return admission.NewForbidden(a, err)
  112. }
  113. if allowedPod != nil {
  114. *pod = *allowedPod
  115. // annotate and accept the pod
  116. klog.V(4).Infof("pod %s (generate: %s) in namespace %s validated against provider %s", pod.Name, pod.GenerateName, a.GetNamespace(), pspName)
  117. if pod.ObjectMeta.Annotations == nil {
  118. pod.ObjectMeta.Annotations = map[string]string{}
  119. }
  120. pod.ObjectMeta.Annotations[psputil.ValidatedPSPAnnotation] = pspName
  121. key := auditKeyPrefix + "/" + "admit-policy"
  122. if err := a.AddAnnotation(key, pspName); err != nil {
  123. klog.Warningf("failed to set admission audit annotation %s to %s: %v", key, pspName, err)
  124. }
  125. return nil
  126. }
  127. // we didn't validate against any provider, reject the pod and give the errors for each attempt
  128. klog.V(4).Infof("unable to validate pod %s (generate: %s) in namespace %s against any pod security policy: %v", pod.Name, pod.GenerateName, a.GetNamespace(), validationErrs)
  129. return admission.NewForbidden(a, fmt.Errorf("unable to validate against any pod security policy: %v", validationErrs))
  130. }
  131. // Validate verifies attributes against the PodSecurityPolicy
  132. func (p *Plugin) Validate(a admission.Attributes, o admission.ObjectInterfaces) error {
  133. if ignore, err := shouldIgnore(a); err != nil {
  134. return err
  135. } else if ignore {
  136. return nil
  137. }
  138. pod := a.GetObject().(*api.Pod)
  139. // compute the context. Mutation is not allowed. ValidatedPSPAnnotation is used as a hint to gain same speed-up.
  140. allowedPod, pspName, validationErrs, err := p.computeSecurityContext(a, pod, false, pod.ObjectMeta.Annotations[psputil.ValidatedPSPAnnotation])
  141. if err != nil {
  142. return admission.NewForbidden(a, err)
  143. }
  144. if apiequality.Semantic.DeepEqual(pod, allowedPod) {
  145. key := auditKeyPrefix + "/" + "validate-policy"
  146. if err := a.AddAnnotation(key, pspName); err != nil {
  147. klog.Warningf("failed to set admission audit annotation %s to %s: %v", key, pspName, err)
  148. }
  149. return nil
  150. }
  151. // we didn't validate against any provider, reject the pod and give the errors for each attempt
  152. klog.V(4).Infof("unable to validate pod %s (generate: %s) in namespace %s against any pod security policy: %v", pod.Name, pod.GenerateName, a.GetNamespace(), validationErrs)
  153. return admission.NewForbidden(a, fmt.Errorf("unable to validate against any pod security policy: %v", validationErrs))
  154. }
  155. func shouldIgnore(a admission.Attributes) (bool, error) {
  156. if a.GetResource().GroupResource() != api.Resource("pods") {
  157. return true, nil
  158. }
  159. if len(a.GetSubresource()) != 0 {
  160. return true, nil
  161. }
  162. // if we can't convert then fail closed since we've already checked that this is supposed to be a pod object.
  163. // this shouldn't normally happen during admission but could happen if an integrator passes a versioned
  164. // pod object rather than an internal object.
  165. if _, ok := a.GetObject().(*api.Pod); !ok {
  166. return false, admission.NewForbidden(a, fmt.Errorf("unexpected type %T", a.GetObject()))
  167. }
  168. // if this is an update, see if we are only updating the ownerRef/finalizers. Garbage collection does this
  169. // and we should allow it in general, since you had the power to update and the power to delete.
  170. // The worst that happens is that you delete something, but you aren't controlling the privileged object itself
  171. if a.GetOperation() == admission.Update && rbacregistry.IsOnlyMutatingGCFields(a.GetObject(), a.GetOldObject(), apiequality.Semantic) {
  172. return true, nil
  173. }
  174. return false, nil
  175. }
  176. // computeSecurityContext derives a valid security context while trying to avoid any changes to the given pod. I.e.
  177. // if there is a matching policy with the same security context as given, it will be reused. If there is no
  178. // matching policy the returned pod will be nil and the pspName empty. validatedPSPHint is the validated psp name
  179. // saved in kubernetes.io/psp annotation. This psp is usually the one we are looking for.
  180. func (p *Plugin) computeSecurityContext(a admission.Attributes, pod *api.Pod, specMutationAllowed bool, validatedPSPHint string) (*api.Pod, string, field.ErrorList, error) {
  181. // get all constraints that are usable by the user
  182. klog.V(4).Infof("getting pod security policies for pod %s (generate: %s)", pod.Name, pod.GenerateName)
  183. var saInfo user.Info
  184. if len(pod.Spec.ServiceAccountName) > 0 {
  185. saInfo = serviceaccount.UserInfo(a.GetNamespace(), pod.Spec.ServiceAccountName, "")
  186. }
  187. policies, err := p.lister.List(labels.Everything())
  188. if err != nil {
  189. return nil, "", nil, err
  190. }
  191. // if we have no policies and want to succeed then return. Otherwise we'll end up with no
  192. // providers and fail with "unable to validate against any pod security policy" below.
  193. if len(policies) == 0 && !p.failOnNoPolicies {
  194. return pod, "", nil, nil
  195. }
  196. // sort policies by name to make order deterministic
  197. // If mutation is not allowed and validatedPSPHint is provided, check the validated policy first.
  198. // TODO(liggitt): add priority field to allow admins to bucket differently
  199. sort.SliceStable(policies, func(i, j int) bool {
  200. if !specMutationAllowed {
  201. if policies[i].Name == validatedPSPHint {
  202. return true
  203. }
  204. if policies[j].Name == validatedPSPHint {
  205. return false
  206. }
  207. }
  208. return strings.Compare(policies[i].Name, policies[j].Name) < 0
  209. })
  210. providers, errs := p.createProvidersFromPolicies(policies, pod.Namespace)
  211. for _, err := range errs {
  212. klog.V(4).Infof("provider creation error: %v", err)
  213. }
  214. if len(providers) == 0 {
  215. return nil, "", nil, fmt.Errorf("no providers available to validate pod request")
  216. }
  217. var (
  218. allowedMutatedPod *api.Pod
  219. allowingMutatingPSP string
  220. // Map of PSP name to associated validation errors.
  221. validationErrs = map[string]field.ErrorList{}
  222. )
  223. for _, provider := range providers {
  224. podCopy := pod.DeepCopy()
  225. if errs := assignSecurityContext(provider, podCopy); len(errs) > 0 {
  226. validationErrs[provider.GetPSPName()] = errs
  227. continue
  228. }
  229. // the entire pod validated
  230. mutated := !apiequality.Semantic.DeepEqual(pod, podCopy)
  231. if mutated && !specMutationAllowed {
  232. continue
  233. }
  234. if !isAuthorizedForPolicy(a.GetUserInfo(), saInfo, a.GetNamespace(), provider.GetPSPName(), p.authz) {
  235. continue
  236. }
  237. switch {
  238. case !mutated:
  239. // if it validated without mutating anything, use this result
  240. return podCopy, provider.GetPSPName(), nil, nil
  241. case specMutationAllowed && allowedMutatedPod == nil:
  242. // if mutation is allowed and this is the first PSP to allow the pod, remember it,
  243. // but continue to see if another PSP allows without mutating
  244. allowedMutatedPod = podCopy
  245. allowingMutatingPSP = provider.GetPSPName()
  246. }
  247. }
  248. if allowedMutatedPod != nil {
  249. return allowedMutatedPod, allowingMutatingPSP, nil, nil
  250. }
  251. // Pod is rejected. Filter the validation errors to only include errors from authorized PSPs.
  252. aggregate := field.ErrorList{}
  253. for psp, errs := range validationErrs {
  254. if isAuthorizedForPolicy(a.GetUserInfo(), saInfo, a.GetNamespace(), psp, p.authz) {
  255. aggregate = append(aggregate, errs...)
  256. }
  257. }
  258. return nil, "", aggregate, nil
  259. }
  260. // assignSecurityContext creates a security context for each container in the pod
  261. // and validates that the sc falls within the psp constraints. All containers must validate against
  262. // the same psp or is not considered valid.
  263. func assignSecurityContext(provider psp.Provider, pod *api.Pod) field.ErrorList {
  264. errs := field.ErrorList{}
  265. if err := provider.MutatePod(pod); err != nil {
  266. // TODO(tallclair): MutatePod should return a field.ErrorList
  267. errs = append(errs, field.Invalid(field.NewPath(""), pod, err.Error()))
  268. }
  269. errs = append(errs, provider.ValidatePod(pod)...)
  270. return errs
  271. }
  272. // createProvidersFromPolicies creates providers from the constraints supplied.
  273. func (p *Plugin) createProvidersFromPolicies(psps []*policyv1beta1.PodSecurityPolicy, namespace string) ([]psp.Provider, []error) {
  274. var (
  275. // collected providers
  276. providers []psp.Provider
  277. // collected errors to return
  278. errs []error
  279. )
  280. for _, constraint := range psps {
  281. provider, err := psp.NewSimpleProvider(constraint, namespace, p.strategyFactory)
  282. if err != nil {
  283. errs = append(errs, fmt.Errorf("error creating provider for PSP %s: %v", constraint.Name, err))
  284. continue
  285. }
  286. providers = append(providers, provider)
  287. }
  288. return providers, errs
  289. }
  290. func isAuthorizedForPolicy(user, sa user.Info, namespace, policyName string, authz authorizer.Authorizer) bool {
  291. // Check the service account first, as that is the more common use case.
  292. return authorizedForPolicy(sa, namespace, policyName, authz) ||
  293. authorizedForPolicy(user, namespace, policyName, authz)
  294. }
  295. // authorizedForPolicy returns true if info is authorized to perform the "use" verb on the policy resource.
  296. // TODO: check against only the policy group when PSP will be completely moved out of the extensions
  297. func authorizedForPolicy(info user.Info, namespace string, policyName string, authz authorizer.Authorizer) bool {
  298. // Check against extensions API group for backward compatibility
  299. return authorizedForPolicyInAPIGroup(info, namespace, policyName, policy.GroupName, authz) ||
  300. authorizedForPolicyInAPIGroup(info, namespace, policyName, extensions.GroupName, authz)
  301. }
  302. // authorizedForPolicyInAPIGroup returns true if info is authorized to perform the "use" verb on the policy resource in the specified API group.
  303. func authorizedForPolicyInAPIGroup(info user.Info, namespace, policyName, apiGroupName string, authz authorizer.Authorizer) bool {
  304. if info == nil {
  305. return false
  306. }
  307. attr := buildAttributes(info, namespace, policyName, apiGroupName)
  308. decision, reason, err := authz.Authorize(attr)
  309. if err != nil {
  310. klog.V(5).Infof("cannot authorize for policy: %v,%v", reason, err)
  311. }
  312. return (decision == authorizer.DecisionAllow)
  313. }
  314. // buildAttributes builds an attributes record for a SAR based on the user info and policy.
  315. func buildAttributes(info user.Info, namespace, policyName, apiGroupName string) authorizer.Attributes {
  316. // check against the namespace that the pod is being created in to allow per-namespace PSP grants.
  317. attr := authorizer.AttributesRecord{
  318. User: info,
  319. Verb: "use",
  320. Namespace: namespace,
  321. Name: policyName,
  322. APIGroup: apiGroupName,
  323. Resource: "podsecuritypolicies",
  324. ResourceRequest: true,
  325. }
  326. return attr
  327. }