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. "context"
  16. "fmt"
  17. "io"
  18. "sort"
  19. "strings"
  20. "k8s.io/klog"
  21. policyv1beta1 "k8s.io/api/policy/v1beta1"
  22. apiequality "k8s.io/apimachinery/pkg/api/equality"
  23. "k8s.io/apimachinery/pkg/labels"
  24. "k8s.io/apimachinery/pkg/util/validation/field"
  25. "k8s.io/apiserver/pkg/admission"
  26. genericadmissioninit "k8s.io/apiserver/pkg/admission/initializer"
  27. "k8s.io/apiserver/pkg/authentication/user"
  28. "k8s.io/apiserver/pkg/authorization/authorizer"
  29. "k8s.io/client-go/informers"
  30. policylisters "k8s.io/client-go/listers/policy/v1beta1"
  31. api "k8s.io/kubernetes/pkg/apis/core"
  32. "k8s.io/kubernetes/pkg/apis/extensions"
  33. "k8s.io/kubernetes/pkg/apis/policy"
  34. rbacregistry "k8s.io/kubernetes/pkg/registry/rbac"
  35. psp "k8s.io/kubernetes/pkg/security/podsecuritypolicy"
  36. psputil "k8s.io/kubernetes/pkg/security/podsecuritypolicy/util"
  37. "k8s.io/kubernetes/pkg/serviceaccount"
  38. )
  39. // PluginName is a string with the name of the plugin
  40. const PluginName = "PodSecurityPolicy"
  41. // Register registers a plugin
  42. func Register(plugins *admission.Plugins) {
  43. plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
  44. plugin := newPlugin(psp.NewSimpleStrategyFactory(), true)
  45. return plugin, nil
  46. })
  47. }
  48. // Plugin holds state for and implements the admission plugin.
  49. type Plugin struct {
  50. *admission.Handler
  51. strategyFactory psp.StrategyFactory
  52. failOnNoPolicies bool
  53. authz authorizer.Authorizer
  54. lister policylisters.PodSecurityPolicyLister
  55. }
  56. // SetAuthorizer sets the authorizer.
  57. func (p *Plugin) SetAuthorizer(authz authorizer.Authorizer) {
  58. p.authz = authz
  59. }
  60. // ValidateInitialization ensures an authorizer is set.
  61. func (p *Plugin) ValidateInitialization() error {
  62. if p.authz == nil {
  63. return fmt.Errorf("%s requires an authorizer", PluginName)
  64. }
  65. if p.lister == nil {
  66. return fmt.Errorf("%s requires a lister", PluginName)
  67. }
  68. return nil
  69. }
  70. var _ admission.MutationInterface = &Plugin{}
  71. var _ admission.ValidationInterface = &Plugin{}
  72. var _ genericadmissioninit.WantsAuthorizer = &Plugin{}
  73. var _ genericadmissioninit.WantsExternalKubeInformerFactory = &Plugin{}
  74. var auditKeyPrefix = strings.ToLower(PluginName) + "." + policy.GroupName + ".k8s.io"
  75. // newPlugin creates a new PSP admission plugin.
  76. func newPlugin(strategyFactory psp.StrategyFactory, failOnNoPolicies bool) *Plugin {
  77. return &Plugin{
  78. Handler: admission.NewHandler(admission.Create, admission.Update),
  79. strategyFactory: strategyFactory,
  80. failOnNoPolicies: failOnNoPolicies,
  81. }
  82. }
  83. // SetExternalKubeInformerFactory registers an informer
  84. func (p *Plugin) SetExternalKubeInformerFactory(f informers.SharedInformerFactory) {
  85. podSecurityPolicyInformer := f.Policy().V1beta1().PodSecurityPolicies()
  86. p.lister = podSecurityPolicyInformer.Lister()
  87. p.SetReadyFunc(podSecurityPolicyInformer.Informer().HasSynced)
  88. }
  89. // Admit determines if the pod should be admitted based on the requested security context
  90. // and the available PSPs.
  91. //
  92. // 1. Find available PSPs.
  93. // 2. Create the providers, includes setting pre-allocated values if necessary.
  94. // 3. Try to generate and validate a PSP with providers. If we find one then admit the pod
  95. // with the validated PSP. If we don't find any reject the pod and give all errors from the
  96. // failed attempts.
  97. func (p *Plugin) Admit(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) error {
  98. if ignore, err := shouldIgnore(a); err != nil {
  99. return err
  100. } else if ignore {
  101. return nil
  102. }
  103. // only mutate if this is a CREATE request. On updates we only validate.
  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(ctx, 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(ctx context.Context, 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(ctx, 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(ctx context.Context, 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. sort.SliceStable(policies, func(i, j int) bool {
  199. if !specMutationAllowed {
  200. if policies[i].Name == validatedPSPHint {
  201. return true
  202. }
  203. if policies[j].Name == validatedPSPHint {
  204. return false
  205. }
  206. }
  207. return strings.Compare(policies[i].Name, policies[j].Name) < 0
  208. })
  209. providers, errs := p.createProvidersFromPolicies(policies, pod.Namespace)
  210. for _, err := range errs {
  211. klog.V(4).Infof("provider creation error: %v", err)
  212. }
  213. if len(providers) == 0 {
  214. return nil, "", nil, fmt.Errorf("no providers available to validate pod request")
  215. }
  216. var (
  217. allowedMutatedPod *api.Pod
  218. allowingMutatingPSP string
  219. // Map of PSP name to associated validation errors.
  220. validationErrs = map[string]field.ErrorList{}
  221. )
  222. for _, provider := range providers {
  223. podCopy := pod.DeepCopy()
  224. if errs := assignSecurityContext(provider, podCopy); len(errs) > 0 {
  225. validationErrs[provider.GetPSPName()] = errs
  226. continue
  227. }
  228. // the entire pod validated
  229. mutated := !apiequality.Semantic.DeepEqual(pod, podCopy)
  230. if mutated && !specMutationAllowed {
  231. continue
  232. }
  233. if !isAuthorizedForPolicy(ctx, a.GetUserInfo(), saInfo, a.GetNamespace(), provider.GetPSPName(), p.authz) {
  234. continue
  235. }
  236. switch {
  237. case !mutated:
  238. // if it validated without mutating anything, use this result
  239. return podCopy, provider.GetPSPName(), nil, nil
  240. case specMutationAllowed && allowedMutatedPod == nil:
  241. // if mutation is allowed and this is the first PSP to allow the pod, remember it,
  242. // but continue to see if another PSP allows without mutating
  243. allowedMutatedPod = podCopy
  244. allowingMutatingPSP = provider.GetPSPName()
  245. }
  246. }
  247. if allowedMutatedPod != nil {
  248. return allowedMutatedPod, allowingMutatingPSP, nil, nil
  249. }
  250. // Pod is rejected. Filter the validation errors to only include errors from authorized PSPs.
  251. aggregate := field.ErrorList{}
  252. for psp, errs := range validationErrs {
  253. if isAuthorizedForPolicy(ctx, a.GetUserInfo(), saInfo, a.GetNamespace(), psp, p.authz) {
  254. aggregate = append(aggregate, errs...)
  255. }
  256. }
  257. return nil, "", aggregate, nil
  258. }
  259. // assignSecurityContext creates a security context for each container in the pod
  260. // and validates that the sc falls within the psp constraints. All containers must validate against
  261. // the same psp or is not considered valid.
  262. func assignSecurityContext(provider psp.Provider, pod *api.Pod) field.ErrorList {
  263. errs := field.ErrorList{}
  264. if err := provider.MutatePod(pod); err != nil {
  265. // TODO(tallclair): MutatePod should return a field.ErrorList
  266. errs = append(errs, field.Invalid(field.NewPath(""), pod, err.Error()))
  267. }
  268. errs = append(errs, provider.ValidatePod(pod)...)
  269. return errs
  270. }
  271. // createProvidersFromPolicies creates providers from the constraints supplied.
  272. func (p *Plugin) createProvidersFromPolicies(psps []*policyv1beta1.PodSecurityPolicy, namespace string) ([]psp.Provider, []error) {
  273. var (
  274. // collected providers
  275. providers []psp.Provider
  276. // collected errors to return
  277. errs []error
  278. )
  279. for _, constraint := range psps {
  280. provider, err := psp.NewSimpleProvider(constraint, namespace, p.strategyFactory)
  281. if err != nil {
  282. errs = append(errs, fmt.Errorf("error creating provider for PSP %s: %v", constraint.Name, err))
  283. continue
  284. }
  285. providers = append(providers, provider)
  286. }
  287. return providers, errs
  288. }
  289. func isAuthorizedForPolicy(ctx context.Context, user, sa user.Info, namespace, policyName string, authz authorizer.Authorizer) bool {
  290. // Check the service account first, as that is the more common use case.
  291. return authorizedForPolicy(ctx, sa, namespace, policyName, authz) ||
  292. authorizedForPolicy(ctx, user, namespace, policyName, authz)
  293. }
  294. // authorizedForPolicy returns true if info is authorized to perform the "use" verb on the policy resource.
  295. // TODO: check against only the policy group when PSP will be completely moved out of the extensions
  296. func authorizedForPolicy(ctx context.Context, info user.Info, namespace string, policyName string, authz authorizer.Authorizer) bool {
  297. // Check against extensions API group for backward compatibility
  298. return authorizedForPolicyInAPIGroup(ctx, info, namespace, policyName, policy.GroupName, authz) ||
  299. authorizedForPolicyInAPIGroup(ctx, info, namespace, policyName, extensions.GroupName, authz)
  300. }
  301. // authorizedForPolicyInAPIGroup returns true if info is authorized to perform the "use" verb on the policy resource in the specified API group.
  302. func authorizedForPolicyInAPIGroup(ctx context.Context, info user.Info, namespace, policyName, apiGroupName string, authz authorizer.Authorizer) bool {
  303. if info == nil {
  304. return false
  305. }
  306. attr := buildAttributes(info, namespace, policyName, apiGroupName)
  307. decision, reason, err := authz.Authorize(ctx, attr)
  308. if err != nil {
  309. klog.V(5).Infof("cannot authorize for policy: %v,%v", reason, err)
  310. }
  311. return (decision == authorizer.DecisionAllow)
  312. }
  313. // buildAttributes builds an attributes record for a SAR based on the user info and policy.
  314. func buildAttributes(info user.Info, namespace, policyName, apiGroupName string) authorizer.Attributes {
  315. // check against the namespace that the pod is being created in to allow per-namespace PSP grants.
  316. attr := authorizer.AttributesRecord{
  317. User: info,
  318. Verb: "use",
  319. Namespace: namespace,
  320. Name: policyName,
  321. APIGroup: apiGroupName,
  322. APIVersion: "*",
  323. Resource: "podsecuritypolicies",
  324. ResourceRequest: true,
  325. }
  326. return attr
  327. }