validation.go 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  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 validation
  14. import (
  15. "fmt"
  16. "path/filepath"
  17. "regexp"
  18. "strings"
  19. apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
  20. unversionedvalidation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
  21. "k8s.io/apimachinery/pkg/util/sets"
  22. "k8s.io/apimachinery/pkg/util/validation/field"
  23. appsvalidation "k8s.io/kubernetes/pkg/apis/apps/validation"
  24. core "k8s.io/kubernetes/pkg/apis/core"
  25. apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
  26. "k8s.io/kubernetes/pkg/apis/policy"
  27. "k8s.io/kubernetes/pkg/security/apparmor"
  28. "k8s.io/kubernetes/pkg/security/podsecuritypolicy/seccomp"
  29. psputil "k8s.io/kubernetes/pkg/security/podsecuritypolicy/util"
  30. )
  31. // ValidatePodDisruptionBudget validates a PodDisruptionBudget and returns an ErrorList
  32. // with any errors.
  33. func ValidatePodDisruptionBudget(pdb *policy.PodDisruptionBudget) field.ErrorList {
  34. allErrs := ValidatePodDisruptionBudgetSpec(pdb.Spec, field.NewPath("spec"))
  35. allErrs = append(allErrs, ValidatePodDisruptionBudgetStatus(pdb.Status, field.NewPath("status"))...)
  36. return allErrs
  37. }
  38. // ValidatePodDisruptionBudgetSpec validates a PodDisruptionBudgetSpec and returns an ErrorList
  39. // with any errors.
  40. func ValidatePodDisruptionBudgetSpec(spec policy.PodDisruptionBudgetSpec, fldPath *field.Path) field.ErrorList {
  41. allErrs := field.ErrorList{}
  42. if spec.MinAvailable != nil && spec.MaxUnavailable != nil {
  43. allErrs = append(allErrs, field.Invalid(fldPath, spec, "minAvailable and maxUnavailable cannot be both set"))
  44. }
  45. if spec.MinAvailable != nil {
  46. allErrs = append(allErrs, appsvalidation.ValidatePositiveIntOrPercent(*spec.MinAvailable, fldPath.Child("minAvailable"))...)
  47. allErrs = append(allErrs, appsvalidation.IsNotMoreThan100Percent(*spec.MinAvailable, fldPath.Child("minAvailable"))...)
  48. }
  49. if spec.MaxUnavailable != nil {
  50. allErrs = append(allErrs, appsvalidation.ValidatePositiveIntOrPercent(*spec.MaxUnavailable, fldPath.Child("maxUnavailable"))...)
  51. allErrs = append(allErrs, appsvalidation.IsNotMoreThan100Percent(*spec.MaxUnavailable, fldPath.Child("maxUnavailable"))...)
  52. }
  53. allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(spec.Selector, fldPath.Child("selector"))...)
  54. return allErrs
  55. }
  56. // ValidatePodDisruptionBudgetStatus validates a PodDisruptionBudgetStatus and returns an ErrorList
  57. // with any errors.
  58. func ValidatePodDisruptionBudgetStatus(status policy.PodDisruptionBudgetStatus, fldPath *field.Path) field.ErrorList {
  59. allErrs := field.ErrorList{}
  60. allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.DisruptionsAllowed), fldPath.Child("disruptionsAllowed"))...)
  61. allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.CurrentHealthy), fldPath.Child("currentHealthy"))...)
  62. allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.DesiredHealthy), fldPath.Child("desiredHealthy"))...)
  63. allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.ExpectedPods), fldPath.Child("expectedPods"))...)
  64. return allErrs
  65. }
  66. // ValidatePodSecurityPolicyName can be used to check whether the given
  67. // pod security policy name is valid.
  68. // Prefix indicates this name will be used as part of generation, in which case
  69. // trailing dashes are allowed.
  70. var ValidatePodSecurityPolicyName = apimachineryvalidation.NameIsDNSSubdomain
  71. // ValidatePodSecurityPolicy validates a PodSecurityPolicy and returns an ErrorList
  72. // with any errors.
  73. func ValidatePodSecurityPolicy(psp *policy.PodSecurityPolicy) field.ErrorList {
  74. allErrs := field.ErrorList{}
  75. allErrs = append(allErrs, apivalidation.ValidateObjectMeta(&psp.ObjectMeta, false, ValidatePodSecurityPolicyName, field.NewPath("metadata"))...)
  76. allErrs = append(allErrs, ValidatePodSecurityPolicySpecificAnnotations(psp.Annotations, field.NewPath("metadata").Child("annotations"))...)
  77. allErrs = append(allErrs, ValidatePodSecurityPolicySpec(&psp.Spec, field.NewPath("spec"))...)
  78. return allErrs
  79. }
  80. // ValidatePodSecurityPolicySpec validates a PodSecurityPolicySpec and returns an ErrorList
  81. // with any errors.
  82. func ValidatePodSecurityPolicySpec(spec *policy.PodSecurityPolicySpec, fldPath *field.Path) field.ErrorList {
  83. allErrs := field.ErrorList{}
  84. allErrs = append(allErrs, validatePSPRunAsUser(fldPath.Child("runAsUser"), &spec.RunAsUser)...)
  85. allErrs = append(allErrs, validatePSPRunAsGroup(fldPath.Child("runAsGroup"), spec.RunAsGroup)...)
  86. allErrs = append(allErrs, validatePSPSELinux(fldPath.Child("seLinux"), &spec.SELinux)...)
  87. allErrs = append(allErrs, validatePSPSupplementalGroup(fldPath.Child("supplementalGroups"), &spec.SupplementalGroups)...)
  88. allErrs = append(allErrs, validatePSPFSGroup(fldPath.Child("fsGroup"), &spec.FSGroup)...)
  89. allErrs = append(allErrs, validatePodSecurityPolicyVolumes(fldPath, spec.Volumes)...)
  90. if len(spec.RequiredDropCapabilities) > 0 && hasCap(policy.AllowAllCapabilities, spec.AllowedCapabilities) {
  91. allErrs = append(allErrs, field.Invalid(field.NewPath("requiredDropCapabilities"), spec.RequiredDropCapabilities,
  92. "must be empty when all capabilities are allowed by a wildcard"))
  93. }
  94. allErrs = append(allErrs, validatePSPCapsAgainstDrops(spec.RequiredDropCapabilities, spec.DefaultAddCapabilities, field.NewPath("defaultAddCapabilities"))...)
  95. allErrs = append(allErrs, validatePSPCapsAgainstDrops(spec.RequiredDropCapabilities, spec.AllowedCapabilities, field.NewPath("allowedCapabilities"))...)
  96. allErrs = append(allErrs, validatePSPDefaultAllowPrivilegeEscalation(fldPath.Child("defaultAllowPrivilegeEscalation"), spec.DefaultAllowPrivilegeEscalation, spec.AllowPrivilegeEscalation)...)
  97. allErrs = append(allErrs, validatePSPAllowedProcMountTypes(fldPath.Child("allowedProcMountTypes"), spec.AllowedProcMountTypes)...)
  98. allErrs = append(allErrs, validatePSPAllowedHostPaths(fldPath.Child("allowedHostPaths"), spec.AllowedHostPaths)...)
  99. allErrs = append(allErrs, validatePSPAllowedFlexVolumes(fldPath.Child("allowedFlexVolumes"), spec.AllowedFlexVolumes)...)
  100. allErrs = append(allErrs, validatePSPAllowedCSIDrivers(fldPath.Child("allowedCSIDrivers"), spec.AllowedCSIDrivers)...)
  101. allErrs = append(allErrs, validatePodSecurityPolicySysctls(fldPath.Child("allowedUnsafeSysctls"), spec.AllowedUnsafeSysctls)...)
  102. allErrs = append(allErrs, validatePodSecurityPolicySysctls(fldPath.Child("forbiddenSysctls"), spec.ForbiddenSysctls)...)
  103. allErrs = append(allErrs, validatePodSecurityPolicySysctlListsDoNotOverlap(fldPath.Child("allowedUnsafeSysctls"), fldPath.Child("forbiddenSysctls"), spec.AllowedUnsafeSysctls, spec.ForbiddenSysctls)...)
  104. allErrs = append(allErrs, validateRuntimeClassStrategy(fldPath.Child("runtimeClass"), spec.RuntimeClass)...)
  105. return allErrs
  106. }
  107. // ValidatePodSecurityPolicySpecificAnnotations validates annotations and returns an ErrorList
  108. // with any errors.
  109. func ValidatePodSecurityPolicySpecificAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList {
  110. allErrs := field.ErrorList{}
  111. if p := annotations[apparmor.DefaultProfileAnnotationKey]; p != "" {
  112. if err := apparmor.ValidateProfileFormat(p); err != nil {
  113. allErrs = append(allErrs, field.Invalid(fldPath.Key(apparmor.DefaultProfileAnnotationKey), p, err.Error()))
  114. }
  115. }
  116. if allowed := annotations[apparmor.AllowedProfilesAnnotationKey]; allowed != "" {
  117. for _, p := range strings.Split(allowed, ",") {
  118. if err := apparmor.ValidateProfileFormat(p); err != nil {
  119. allErrs = append(allErrs, field.Invalid(fldPath.Key(apparmor.AllowedProfilesAnnotationKey), allowed, err.Error()))
  120. }
  121. }
  122. }
  123. if p := annotations[seccomp.DefaultProfileAnnotationKey]; p != "" {
  124. allErrs = append(allErrs, apivalidation.ValidateSeccompProfile(p, fldPath.Key(seccomp.DefaultProfileAnnotationKey))...)
  125. }
  126. if allowed := annotations[seccomp.AllowedProfilesAnnotationKey]; allowed != "" {
  127. for _, p := range strings.Split(allowed, ",") {
  128. if p == seccomp.AllowAny {
  129. continue
  130. }
  131. allErrs = append(allErrs, apivalidation.ValidateSeccompProfile(p, fldPath.Key(seccomp.AllowedProfilesAnnotationKey))...)
  132. }
  133. }
  134. return allErrs
  135. }
  136. // validatePSPAllowedHostPaths makes sure all allowed host paths follow:
  137. // 1. path prefix is required
  138. // 2. path prefix does not have any element which is ".."
  139. func validatePSPAllowedHostPaths(fldPath *field.Path, allowedHostPaths []policy.AllowedHostPath) field.ErrorList {
  140. allErrs := field.ErrorList{}
  141. for i, target := range allowedHostPaths {
  142. if target.PathPrefix == "" {
  143. allErrs = append(allErrs, field.Required(fldPath.Index(i), "is required"))
  144. break
  145. }
  146. parts := strings.Split(filepath.ToSlash(target.PathPrefix), "/")
  147. for _, item := range parts {
  148. if item == ".." {
  149. allErrs = append(allErrs, field.Invalid(fldPath.Index(i), target.PathPrefix, "must not contain '..'"))
  150. break // even for `../../..`, one error is sufficient to make the point
  151. }
  152. }
  153. }
  154. return allErrs
  155. }
  156. func validatePSPAllowedFlexVolumes(fldPath *field.Path, flexVolumes []policy.AllowedFlexVolume) field.ErrorList {
  157. allErrs := field.ErrorList{}
  158. if len(flexVolumes) > 0 {
  159. for idx, fv := range flexVolumes {
  160. if len(fv.Driver) == 0 {
  161. allErrs = append(allErrs, field.Required(fldPath.Child("allowedFlexVolumes").Index(idx).Child("driver"),
  162. "must specify a driver"))
  163. }
  164. }
  165. }
  166. return allErrs
  167. }
  168. func validatePSPAllowedCSIDrivers(fldPath *field.Path, csiDrivers []policy.AllowedCSIDriver) field.ErrorList {
  169. allErrs := field.ErrorList{}
  170. if len(csiDrivers) > 0 {
  171. for idx, csiDriver := range csiDrivers {
  172. fieldPath := fldPath.Child("allowedCSIDriver").Index(idx).Child("name")
  173. allErrs = append(allErrs, apivalidation.ValidateCSIDriverName(csiDriver.Name, fieldPath)...)
  174. }
  175. }
  176. return allErrs
  177. }
  178. // validatePSPSELinux validates the SELinux fields of PodSecurityPolicy.
  179. func validatePSPSELinux(fldPath *field.Path, seLinux *policy.SELinuxStrategyOptions) field.ErrorList {
  180. allErrs := field.ErrorList{}
  181. // ensure the selinux strategy has a valid rule
  182. supportedSELinuxRules := sets.NewString(
  183. string(policy.SELinuxStrategyMustRunAs),
  184. string(policy.SELinuxStrategyRunAsAny),
  185. )
  186. if !supportedSELinuxRules.Has(string(seLinux.Rule)) {
  187. allErrs = append(allErrs, field.NotSupported(fldPath.Child("rule"), seLinux.Rule, supportedSELinuxRules.List()))
  188. }
  189. return allErrs
  190. }
  191. // validatePSPRunAsUser validates the RunAsUser fields of PodSecurityPolicy.
  192. func validatePSPRunAsUser(fldPath *field.Path, runAsUser *policy.RunAsUserStrategyOptions) field.ErrorList {
  193. allErrs := field.ErrorList{}
  194. // ensure the user strategy has a valid rule
  195. supportedRunAsUserRules := sets.NewString(
  196. string(policy.RunAsUserStrategyMustRunAs),
  197. string(policy.RunAsUserStrategyMustRunAsNonRoot),
  198. string(policy.RunAsUserStrategyRunAsAny),
  199. )
  200. if !supportedRunAsUserRules.Has(string(runAsUser.Rule)) {
  201. allErrs = append(allErrs, field.NotSupported(fldPath.Child("rule"), runAsUser.Rule, supportedRunAsUserRules.List()))
  202. }
  203. // validate range settings
  204. for idx, rng := range runAsUser.Ranges {
  205. allErrs = append(allErrs, validateUserIDRange(fldPath.Child("ranges").Index(idx), rng)...)
  206. }
  207. return allErrs
  208. }
  209. // validatePSPRunAsGroup validates the RunAsGroup fields of PodSecurityPolicy.
  210. func validatePSPRunAsGroup(fldPath *field.Path, runAsGroup *policy.RunAsGroupStrategyOptions) field.ErrorList {
  211. var allErrs field.ErrorList
  212. if runAsGroup == nil {
  213. return allErrs
  214. }
  215. switch runAsGroup.Rule {
  216. case policy.RunAsGroupStrategyRunAsAny:
  217. if len(runAsGroup.Ranges) != 0 {
  218. allErrs = append(allErrs, field.Invalid(fldPath.Child("ranges"), runAsGroup.Ranges, "Ranges must be empty"))
  219. }
  220. case policy.RunAsGroupStrategyMustRunAs, policy.RunAsGroupStrategyMayRunAs:
  221. if len(runAsGroup.Ranges) == 0 {
  222. allErrs = append(allErrs, field.Invalid(fldPath.Child("ranges"), runAsGroup.Ranges, "must provide at least one range"))
  223. }
  224. // validate range settings
  225. for idx, rng := range runAsGroup.Ranges {
  226. allErrs = append(allErrs, validateGroupIDRange(fldPath.Child("ranges").Index(idx), rng)...)
  227. }
  228. default:
  229. supportedRunAsGroupRules := []string{
  230. string(policy.RunAsGroupStrategyMustRunAs),
  231. string(policy.RunAsGroupStrategyRunAsAny),
  232. string(policy.RunAsGroupStrategyMayRunAs),
  233. }
  234. allErrs = append(allErrs, field.NotSupported(fldPath.Child("rule"), runAsGroup.Rule, supportedRunAsGroupRules))
  235. }
  236. return allErrs
  237. }
  238. // validatePSPFSGroup validates the FSGroupStrategyOptions fields of the PodSecurityPolicy.
  239. func validatePSPFSGroup(fldPath *field.Path, groupOptions *policy.FSGroupStrategyOptions) field.ErrorList {
  240. allErrs := field.ErrorList{}
  241. supportedRules := sets.NewString(
  242. string(policy.FSGroupStrategyMustRunAs),
  243. string(policy.FSGroupStrategyMayRunAs),
  244. string(policy.FSGroupStrategyRunAsAny),
  245. )
  246. if !supportedRules.Has(string(groupOptions.Rule)) {
  247. allErrs = append(allErrs, field.NotSupported(fldPath.Child("rule"), groupOptions.Rule, supportedRules.List()))
  248. }
  249. for idx, rng := range groupOptions.Ranges {
  250. allErrs = append(allErrs, validateGroupIDRange(fldPath.Child("ranges").Index(idx), rng)...)
  251. }
  252. return allErrs
  253. }
  254. // validatePSPSupplementalGroup validates the SupplementalGroupsStrategyOptions fields of the PodSecurityPolicy.
  255. func validatePSPSupplementalGroup(fldPath *field.Path, groupOptions *policy.SupplementalGroupsStrategyOptions) field.ErrorList {
  256. allErrs := field.ErrorList{}
  257. supportedRules := sets.NewString(
  258. string(policy.SupplementalGroupsStrategyRunAsAny),
  259. string(policy.SupplementalGroupsStrategyMayRunAs),
  260. string(policy.SupplementalGroupsStrategyMustRunAs),
  261. )
  262. if !supportedRules.Has(string(groupOptions.Rule)) {
  263. allErrs = append(allErrs, field.NotSupported(fldPath.Child("rule"), groupOptions.Rule, supportedRules.List()))
  264. }
  265. for idx, rng := range groupOptions.Ranges {
  266. allErrs = append(allErrs, validateGroupIDRange(fldPath.Child("ranges").Index(idx), rng)...)
  267. }
  268. return allErrs
  269. }
  270. // validatePodSecurityPolicyVolumes validates the volume fields of PodSecurityPolicy.
  271. func validatePodSecurityPolicyVolumes(fldPath *field.Path, volumes []policy.FSType) field.ErrorList {
  272. allErrs := field.ErrorList{}
  273. allowed := psputil.GetAllFSTypesAsSet()
  274. // add in the * value since that is a pseudo type that is not included by default
  275. allowed.Insert(string(policy.All))
  276. for _, v := range volumes {
  277. if !allowed.Has(string(v)) {
  278. allErrs = append(allErrs, field.NotSupported(fldPath.Child("volumes"), v, allowed.List()))
  279. }
  280. }
  281. return allErrs
  282. }
  283. // validatePSPDefaultAllowPrivilegeEscalation validates the DefaultAllowPrivilegeEscalation field against the AllowPrivilegeEscalation field of a PodSecurityPolicy.
  284. func validatePSPDefaultAllowPrivilegeEscalation(fldPath *field.Path, defaultAllowPrivilegeEscalation *bool, allowPrivilegeEscalation bool) field.ErrorList {
  285. allErrs := field.ErrorList{}
  286. if defaultAllowPrivilegeEscalation != nil && *defaultAllowPrivilegeEscalation && !allowPrivilegeEscalation {
  287. allErrs = append(allErrs, field.Invalid(fldPath, defaultAllowPrivilegeEscalation, "Cannot set DefaultAllowPrivilegeEscalation to true without also setting AllowPrivilegeEscalation to true"))
  288. }
  289. return allErrs
  290. }
  291. // validatePSPAllowedProcMountTypes validates the DefaultAllowPrivilegeEscalation field against the AllowPrivilegeEscalation field of a PodSecurityPolicy.
  292. func validatePSPAllowedProcMountTypes(fldPath *field.Path, allowedProcMountTypes []core.ProcMountType) field.ErrorList {
  293. allErrs := field.ErrorList{}
  294. for i, procMountType := range allowedProcMountTypes {
  295. if err := apivalidation.ValidateProcMountType(fldPath.Index(i), procMountType); err != nil {
  296. allErrs = append(allErrs, err)
  297. }
  298. }
  299. return allErrs
  300. }
  301. const sysctlPatternSegmentFmt string = "([a-z0-9][-_a-z0-9]*)?[a-z0-9*]"
  302. // SysctlPatternFmt is a regex used for matching valid sysctl patterns.
  303. const SysctlPatternFmt string = "(" + apivalidation.SysctlSegmentFmt + "\\.)*" + sysctlPatternSegmentFmt
  304. var sysctlPatternRegexp = regexp.MustCompile("^" + SysctlPatternFmt + "$")
  305. // IsValidSysctlPattern checks if name is a valid sysctl pattern.
  306. func IsValidSysctlPattern(name string) bool {
  307. if len(name) > apivalidation.SysctlMaxLength {
  308. return false
  309. }
  310. return sysctlPatternRegexp.MatchString(name)
  311. }
  312. func validatePodSecurityPolicySysctlListsDoNotOverlap(allowedSysctlsFldPath, forbiddenSysctlsFldPath *field.Path, allowedUnsafeSysctls, forbiddenSysctls []string) field.ErrorList {
  313. allErrs := field.ErrorList{}
  314. for i, allowedSysctl := range allowedUnsafeSysctls {
  315. isAllowedSysctlPattern := false
  316. allowedSysctlPrefix := ""
  317. if strings.HasSuffix(allowedSysctl, "*") {
  318. isAllowedSysctlPattern = true
  319. allowedSysctlPrefix = strings.TrimSuffix(allowedSysctl, "*")
  320. }
  321. for j, forbiddenSysctl := range forbiddenSysctls {
  322. isForbiddenSysctlPattern := false
  323. forbiddenSysctlPrefix := ""
  324. if strings.HasSuffix(forbiddenSysctl, "*") {
  325. isForbiddenSysctlPattern = true
  326. forbiddenSysctlPrefix = strings.TrimSuffix(forbiddenSysctl, "*")
  327. }
  328. switch {
  329. case isAllowedSysctlPattern && isForbiddenSysctlPattern:
  330. if strings.HasPrefix(allowedSysctlPrefix, forbiddenSysctlPrefix) {
  331. allErrs = append(allErrs, field.Invalid(allowedSysctlsFldPath.Index(i), allowedUnsafeSysctls[i], fmt.Sprintf("sysctl overlaps with %v", forbiddenSysctl)))
  332. } else if strings.HasPrefix(forbiddenSysctlPrefix, allowedSysctlPrefix) {
  333. allErrs = append(allErrs, field.Invalid(forbiddenSysctlsFldPath.Index(j), forbiddenSysctls[j], fmt.Sprintf("sysctl overlaps with %v", allowedSysctl)))
  334. }
  335. case isAllowedSysctlPattern:
  336. if strings.HasPrefix(forbiddenSysctl, allowedSysctlPrefix) {
  337. allErrs = append(allErrs, field.Invalid(forbiddenSysctlsFldPath.Index(j), forbiddenSysctls[j], fmt.Sprintf("sysctl overlaps with %v", allowedSysctl)))
  338. }
  339. case isForbiddenSysctlPattern:
  340. if strings.HasPrefix(allowedSysctl, forbiddenSysctlPrefix) {
  341. allErrs = append(allErrs, field.Invalid(allowedSysctlsFldPath.Index(i), allowedUnsafeSysctls[i], fmt.Sprintf("sysctl overlaps with %v", forbiddenSysctl)))
  342. }
  343. default:
  344. if allowedSysctl == forbiddenSysctl {
  345. allErrs = append(allErrs, field.Invalid(allowedSysctlsFldPath.Index(i), allowedUnsafeSysctls[i], fmt.Sprintf("sysctl overlaps with %v", forbiddenSysctl)))
  346. }
  347. }
  348. }
  349. }
  350. return allErrs
  351. }
  352. // validatePodSecurityPolicySysctls validates the sysctls fields of PodSecurityPolicy.
  353. func validatePodSecurityPolicySysctls(fldPath *field.Path, sysctls []string) field.ErrorList {
  354. allErrs := field.ErrorList{}
  355. if len(sysctls) == 0 {
  356. return allErrs
  357. }
  358. coversAll := false
  359. for i, s := range sysctls {
  360. if len(s) == 0 {
  361. allErrs = append(allErrs, field.Invalid(fldPath.Index(i), sysctls[i], fmt.Sprintf("empty sysctl not allowed")))
  362. } else if !IsValidSysctlPattern(string(s)) {
  363. allErrs = append(
  364. allErrs,
  365. field.Invalid(fldPath.Index(i), sysctls[i], fmt.Sprintf("must have at most %d characters and match regex %s",
  366. apivalidation.SysctlMaxLength,
  367. SysctlPatternFmt,
  368. )),
  369. )
  370. } else if s[0] == '*' {
  371. coversAll = true
  372. }
  373. }
  374. if coversAll && len(sysctls) > 1 {
  375. allErrs = append(allErrs, field.Forbidden(fldPath.Child("items"), fmt.Sprintf("if '*' is present, must not specify other sysctls")))
  376. }
  377. return allErrs
  378. }
  379. func validateUserIDRange(fldPath *field.Path, rng policy.IDRange) field.ErrorList {
  380. return validateIDRanges(fldPath, rng.Min, rng.Max)
  381. }
  382. func validateGroupIDRange(fldPath *field.Path, rng policy.IDRange) field.ErrorList {
  383. return validateIDRanges(fldPath, rng.Min, rng.Max)
  384. }
  385. // validateIDRanges ensures the range is valid.
  386. func validateIDRanges(fldPath *field.Path, min, max int64) field.ErrorList {
  387. allErrs := field.ErrorList{}
  388. // if 0 <= Min <= Max then we do not need to validate max. It is always greater than or
  389. // equal to 0 and Min.
  390. if min < 0 {
  391. allErrs = append(allErrs, field.Invalid(fldPath.Child("min"), min, "min cannot be negative"))
  392. }
  393. if max < 0 {
  394. allErrs = append(allErrs, field.Invalid(fldPath.Child("max"), max, "max cannot be negative"))
  395. }
  396. if min > max {
  397. allErrs = append(allErrs, field.Invalid(fldPath.Child("min"), min, "min cannot be greater than max"))
  398. }
  399. return allErrs
  400. }
  401. // validatePSPCapsAgainstDrops ensures an allowed cap is not listed in the required drops.
  402. func validatePSPCapsAgainstDrops(requiredDrops []core.Capability, capsToCheck []core.Capability, fldPath *field.Path) field.ErrorList {
  403. allErrs := field.ErrorList{}
  404. if requiredDrops == nil {
  405. return allErrs
  406. }
  407. for _, cap := range capsToCheck {
  408. if hasCap(cap, requiredDrops) {
  409. allErrs = append(allErrs, field.Invalid(fldPath, cap,
  410. fmt.Sprintf("capability is listed in %s and requiredDropCapabilities", fldPath.String())))
  411. }
  412. }
  413. return allErrs
  414. }
  415. // validateRuntimeClassStrategy ensures all the RuntimeClass restrictions are valid.
  416. func validateRuntimeClassStrategy(fldPath *field.Path, rc *policy.RuntimeClassStrategyOptions) field.ErrorList {
  417. if rc == nil {
  418. return nil
  419. }
  420. var allErrs field.ErrorList
  421. allowed := map[string]bool{}
  422. for i, name := range rc.AllowedRuntimeClassNames {
  423. if name != policy.AllowAllRuntimeClassNames {
  424. allErrs = append(allErrs, apivalidation.ValidateRuntimeClassName(name, fldPath.Child("allowedRuntimeClassNames").Index(i))...)
  425. }
  426. if allowed[name] {
  427. allErrs = append(allErrs, field.Duplicate(fldPath.Child("allowedRuntimeClassNames").Index(i), name))
  428. }
  429. allowed[name] = true
  430. }
  431. if rc.DefaultRuntimeClassName != nil {
  432. allErrs = append(allErrs, apivalidation.ValidateRuntimeClassName(*rc.DefaultRuntimeClassName, fldPath.Child("defaultRuntimeClassName"))...)
  433. if !allowed[*rc.DefaultRuntimeClassName] && !allowed[policy.AllowAllRuntimeClassNames] {
  434. allErrs = append(allErrs, field.Required(fldPath.Child("allowedRuntimeClassNames"),
  435. fmt.Sprintf("default %q must be allowed", *rc.DefaultRuntimeClassName)))
  436. }
  437. }
  438. if allowed[policy.AllowAllRuntimeClassNames] && len(rc.AllowedRuntimeClassNames) > 1 {
  439. allErrs = append(allErrs, field.Invalid(fldPath.Child("allowedRuntimeClassNames"), rc.AllowedRuntimeClassNames, "if '*' is present, must not specify other RuntimeClass names"))
  440. }
  441. return allErrs
  442. }
  443. // ValidatePodSecurityPolicyUpdate validates a PSP for updates.
  444. func ValidatePodSecurityPolicyUpdate(old *policy.PodSecurityPolicy, new *policy.PodSecurityPolicy) field.ErrorList {
  445. allErrs := field.ErrorList{}
  446. allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&new.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata"))...)
  447. allErrs = append(allErrs, ValidatePodSecurityPolicySpecificAnnotations(new.Annotations, field.NewPath("metadata").Child("annotations"))...)
  448. allErrs = append(allErrs, ValidatePodSecurityPolicySpec(&new.Spec, field.NewPath("spec"))...)
  449. return allErrs
  450. }
  451. // hasCap checks for needle in haystack.
  452. func hasCap(needle core.Capability, haystack []core.Capability) bool {
  453. for _, c := range haystack {
  454. if needle == c {
  455. return true
  456. }
  457. }
  458. return false
  459. }