strategy.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  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 seccomp
  14. import (
  15. "fmt"
  16. "strings"
  17. "k8s.io/apimachinery/pkg/util/validation/field"
  18. api "k8s.io/kubernetes/pkg/apis/core"
  19. )
  20. const (
  21. // AllowAny is the wildcard used to allow any profile.
  22. AllowAny = "*"
  23. // The annotation key specifying the default seccomp profile.
  24. DefaultProfileAnnotationKey = "seccomp.security.alpha.kubernetes.io/defaultProfileName"
  25. // The annotation key specifying the allowed seccomp profiles.
  26. AllowedProfilesAnnotationKey = "seccomp.security.alpha.kubernetes.io/allowedProfileNames"
  27. )
  28. // Strategy defines the interface for all seccomp constraint strategies.
  29. type Strategy interface {
  30. // Generate returns a profile based on constraint rules.
  31. Generate(annotations map[string]string, pod *api.Pod) (string, error)
  32. // Validate ensures that the specified values fall within the range of the strategy.
  33. ValidatePod(pod *api.Pod) field.ErrorList
  34. // Validate ensures that the specified values fall within the range of the strategy.
  35. ValidateContainer(pod *api.Pod, container *api.Container) field.ErrorList
  36. }
  37. type strategy struct {
  38. defaultProfile string
  39. allowedProfiles map[string]bool
  40. // For printing error messages (preserves order).
  41. allowedProfilesString string
  42. // does the strategy allow any profile (wildcard)
  43. allowAnyProfile bool
  44. }
  45. var _ Strategy = &strategy{}
  46. // NewStrategy creates a new strategy that enforces seccomp profile constraints.
  47. func NewStrategy(pspAnnotations map[string]string) Strategy {
  48. var allowedProfiles map[string]bool
  49. allowAnyProfile := false
  50. if allowed, ok := pspAnnotations[AllowedProfilesAnnotationKey]; ok {
  51. profiles := strings.Split(allowed, ",")
  52. allowedProfiles = make(map[string]bool, len(profiles))
  53. for _, p := range profiles {
  54. if p == AllowAny {
  55. allowAnyProfile = true
  56. continue
  57. }
  58. allowedProfiles[p] = true
  59. }
  60. }
  61. return &strategy{
  62. defaultProfile: pspAnnotations[DefaultProfileAnnotationKey],
  63. allowedProfiles: allowedProfiles,
  64. allowedProfilesString: pspAnnotations[AllowedProfilesAnnotationKey],
  65. allowAnyProfile: allowAnyProfile,
  66. }
  67. }
  68. // Generate returns a profile based on constraint rules.
  69. func (s *strategy) Generate(annotations map[string]string, pod *api.Pod) (string, error) {
  70. if annotations[api.SeccompPodAnnotationKey] != "" {
  71. // Profile already set, nothing to do.
  72. return annotations[api.SeccompPodAnnotationKey], nil
  73. }
  74. return s.defaultProfile, nil
  75. }
  76. // ValidatePod ensures that the specified values on the pod fall within the range
  77. // of the strategy.
  78. func (s *strategy) ValidatePod(pod *api.Pod) field.ErrorList {
  79. allErrs := field.ErrorList{}
  80. podSpecFieldPath := field.NewPath("pod", "metadata", "annotations").Key(api.SeccompPodAnnotationKey)
  81. podProfile := pod.Annotations[api.SeccompPodAnnotationKey]
  82. if !s.allowAnyProfile && len(s.allowedProfiles) == 0 && podProfile != "" {
  83. allErrs = append(allErrs, field.Forbidden(podSpecFieldPath, "seccomp may not be set"))
  84. return allErrs
  85. }
  86. if !s.profileAllowed(podProfile) {
  87. msg := fmt.Sprintf("%s is not an allowed seccomp profile. Valid values are %v", podProfile, s.allowedProfilesString)
  88. allErrs = append(allErrs, field.Forbidden(podSpecFieldPath, msg))
  89. }
  90. return allErrs
  91. }
  92. // ValidateContainer ensures that the specified values on the container fall within
  93. // the range of the strategy.
  94. func (s *strategy) ValidateContainer(pod *api.Pod, container *api.Container) field.ErrorList {
  95. allErrs := field.ErrorList{}
  96. fieldPath := field.NewPath("pod", "metadata", "annotations").Key(api.SeccompContainerAnnotationKeyPrefix + container.Name)
  97. containerProfile := profileForContainer(pod, container)
  98. if !s.allowAnyProfile && len(s.allowedProfiles) == 0 && containerProfile != "" {
  99. allErrs = append(allErrs, field.Forbidden(fieldPath, "seccomp may not be set"))
  100. return allErrs
  101. }
  102. if !s.profileAllowed(containerProfile) {
  103. msg := fmt.Sprintf("%s is not an allowed seccomp profile. Valid values are %v", containerProfile, s.allowedProfilesString)
  104. allErrs = append(allErrs, field.Forbidden(fieldPath, msg))
  105. }
  106. return allErrs
  107. }
  108. // profileAllowed checks if profile is in allowedProfiles or if allowedProfiles
  109. // contains the wildcard.
  110. func (s *strategy) profileAllowed(profile string) bool {
  111. // for backwards compatibility and PSPs without a defined list of allowed profiles.
  112. // If a PSP does not have allowedProfiles set then we should allow an empty profile.
  113. // This will mean that the runtime default is used.
  114. if len(s.allowedProfiles) == 0 && profile == "" {
  115. return true
  116. }
  117. return s.allowAnyProfile || s.allowedProfiles[profile]
  118. }
  119. // profileForContainer returns the container profile if set, otherwise the pod profile.
  120. func profileForContainer(pod *api.Pod, container *api.Container) string {
  121. containerProfile, ok := pod.Annotations[api.SeccompContainerAnnotationKeyPrefix+container.Name]
  122. if ok {
  123. return containerProfile
  124. }
  125. return pod.Annotations[api.SeccompPodAnnotationKey]
  126. }