admission.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. /*
  2. Copyright 2015 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 alwayspullimages contains an admission controller that modifies every new Pod to force
  14. // the image pull policy to Always. This is useful in a multitenant cluster so that users can be
  15. // assured that their private images can only be used by those who have the credentials to pull
  16. // them. Without this admission controller, once an image has been pulled to a node, any pod from
  17. // any user can use it simply by knowing the image's name (assuming the Pod is scheduled onto the
  18. // right node), without any authorization check against the image. With this admission controller
  19. // enabled, images are always pulled prior to starting containers, which means valid credentials are
  20. // required.
  21. package alwayspullimages
  22. import (
  23. "io"
  24. apierrors "k8s.io/apimachinery/pkg/api/errors"
  25. "k8s.io/apimachinery/pkg/util/validation/field"
  26. "k8s.io/apiserver/pkg/admission"
  27. api "k8s.io/kubernetes/pkg/apis/core"
  28. )
  29. // PluginName indicates name of admission plugin.
  30. const PluginName = "AlwaysPullImages"
  31. // Register registers a plugin
  32. func Register(plugins *admission.Plugins) {
  33. plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
  34. return NewAlwaysPullImages(), nil
  35. })
  36. }
  37. // AlwaysPullImages is an implementation of admission.Interface.
  38. // It looks at all new pods and overrides each container's image pull policy to Always.
  39. type AlwaysPullImages struct {
  40. *admission.Handler
  41. }
  42. var _ admission.MutationInterface = &AlwaysPullImages{}
  43. var _ admission.ValidationInterface = &AlwaysPullImages{}
  44. // Admit makes an admission decision based on the request attributes
  45. func (a *AlwaysPullImages) Admit(attributes admission.Attributes, o admission.ObjectInterfaces) (err error) {
  46. // Ignore all calls to subresources or resources other than pods.
  47. if shouldIgnore(attributes) {
  48. return nil
  49. }
  50. pod, ok := attributes.GetObject().(*api.Pod)
  51. if !ok {
  52. return apierrors.NewBadRequest("Resource was marked with kind Pod but was unable to be converted")
  53. }
  54. for i := range pod.Spec.InitContainers {
  55. pod.Spec.InitContainers[i].ImagePullPolicy = api.PullAlways
  56. }
  57. for i := range pod.Spec.Containers {
  58. pod.Spec.Containers[i].ImagePullPolicy = api.PullAlways
  59. }
  60. return nil
  61. }
  62. // Validate makes sure that all containers are set to always pull images
  63. func (*AlwaysPullImages) Validate(attributes admission.Attributes, o admission.ObjectInterfaces) (err error) {
  64. if shouldIgnore(attributes) {
  65. return nil
  66. }
  67. pod, ok := attributes.GetObject().(*api.Pod)
  68. if !ok {
  69. return apierrors.NewBadRequest("Resource was marked with kind Pod but was unable to be converted")
  70. }
  71. for i := range pod.Spec.InitContainers {
  72. if pod.Spec.InitContainers[i].ImagePullPolicy != api.PullAlways {
  73. return admission.NewForbidden(attributes,
  74. field.NotSupported(field.NewPath("spec", "initContainers").Index(i).Child("imagePullPolicy"),
  75. pod.Spec.InitContainers[i].ImagePullPolicy, []string{string(api.PullAlways)},
  76. ),
  77. )
  78. }
  79. }
  80. for i := range pod.Spec.Containers {
  81. if pod.Spec.Containers[i].ImagePullPolicy != api.PullAlways {
  82. return admission.NewForbidden(attributes,
  83. field.NotSupported(field.NewPath("spec", "containers").Index(i).Child("imagePullPolicy"),
  84. pod.Spec.Containers[i].ImagePullPolicy, []string{string(api.PullAlways)},
  85. ),
  86. )
  87. }
  88. }
  89. return nil
  90. }
  91. func shouldIgnore(attributes admission.Attributes) bool {
  92. // Ignore all calls to subresources or resources other than pods.
  93. if len(attributes.GetSubresource()) != 0 || attributes.GetResource().GroupResource() != api.Resource("pods") {
  94. return true
  95. }
  96. return false
  97. }
  98. // NewAlwaysPullImages creates a new always pull images admission control handler
  99. func NewAlwaysPullImages() *AlwaysPullImages {
  100. return &AlwaysPullImages{
  101. Handler: admission.NewHandler(admission.Create, admission.Update),
  102. }
  103. }