admission.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  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. "context"
  24. "io"
  25. apierrors "k8s.io/apimachinery/pkg/api/errors"
  26. utilerrors "k8s.io/apimachinery/pkg/util/errors"
  27. "k8s.io/apimachinery/pkg/util/validation/field"
  28. "k8s.io/apiserver/pkg/admission"
  29. api "k8s.io/kubernetes/pkg/apis/core"
  30. "k8s.io/kubernetes/pkg/apis/core/pods"
  31. )
  32. // PluginName indicates name of admission plugin.
  33. const PluginName = "AlwaysPullImages"
  34. // Register registers a plugin
  35. func Register(plugins *admission.Plugins) {
  36. plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
  37. return NewAlwaysPullImages(), nil
  38. })
  39. }
  40. // AlwaysPullImages is an implementation of admission.Interface.
  41. // It looks at all new pods and overrides each container's image pull policy to Always.
  42. type AlwaysPullImages struct {
  43. *admission.Handler
  44. }
  45. var _ admission.MutationInterface = &AlwaysPullImages{}
  46. var _ admission.ValidationInterface = &AlwaysPullImages{}
  47. // Admit makes an admission decision based on the request attributes
  48. func (a *AlwaysPullImages) Admit(ctx context.Context, attributes admission.Attributes, o admission.ObjectInterfaces) (err error) {
  49. // Ignore all calls to subresources or resources other than pods.
  50. if shouldIgnore(attributes) {
  51. return nil
  52. }
  53. pod, ok := attributes.GetObject().(*api.Pod)
  54. if !ok {
  55. return apierrors.NewBadRequest("Resource was marked with kind Pod but was unable to be converted")
  56. }
  57. pods.VisitContainersWithPath(&pod.Spec, func(c *api.Container, _ *field.Path) bool {
  58. c.ImagePullPolicy = api.PullAlways
  59. return true
  60. })
  61. return nil
  62. }
  63. // Validate makes sure that all containers are set to always pull images
  64. func (*AlwaysPullImages) Validate(ctx context.Context, attributes admission.Attributes, o admission.ObjectInterfaces) (err error) {
  65. if shouldIgnore(attributes) {
  66. return nil
  67. }
  68. pod, ok := attributes.GetObject().(*api.Pod)
  69. if !ok {
  70. return apierrors.NewBadRequest("Resource was marked with kind Pod but was unable to be converted")
  71. }
  72. var allErrs []error
  73. pods.VisitContainersWithPath(&pod.Spec, func(c *api.Container, p *field.Path) bool {
  74. if c.ImagePullPolicy != api.PullAlways {
  75. allErrs = append(allErrs, admission.NewForbidden(attributes,
  76. field.NotSupported(p.Child("imagePullPolicy"), c.ImagePullPolicy, []string{string(api.PullAlways)}),
  77. ))
  78. }
  79. return true
  80. })
  81. if len(allErrs) > 0 {
  82. return utilerrors.NewAggregate(allErrs)
  83. }
  84. return nil
  85. }
  86. func shouldIgnore(attributes admission.Attributes) bool {
  87. // Ignore all calls to subresources or resources other than pods.
  88. if len(attributes.GetSubresource()) != 0 || attributes.GetResource().GroupResource() != api.Resource("pods") {
  89. return true
  90. }
  91. return false
  92. }
  93. // NewAlwaysPullImages creates a new always pull images admission control handler
  94. func NewAlwaysPullImages() *AlwaysPullImages {
  95. return &AlwaysPullImages{
  96. Handler: admission.NewHandler(admission.Create, admission.Update),
  97. }
  98. }