validation.go 22 KB

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