validation.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. /*
  2. Copyright 2017 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. "net"
  16. "regexp"
  17. "strings"
  18. apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
  19. unversionedvalidation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
  20. "k8s.io/apimachinery/pkg/util/intstr"
  21. "k8s.io/apimachinery/pkg/util/sets"
  22. "k8s.io/apimachinery/pkg/util/validation"
  23. "k8s.io/apimachinery/pkg/util/validation/field"
  24. api "k8s.io/kubernetes/pkg/apis/core"
  25. apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
  26. "k8s.io/kubernetes/pkg/apis/networking"
  27. )
  28. // ValidateNetworkPolicyName can be used to check whether the given networkpolicy
  29. // name is valid.
  30. func ValidateNetworkPolicyName(name string, prefix bool) []string {
  31. return apimachineryvalidation.NameIsDNSSubdomain(name, prefix)
  32. }
  33. // ValidateNetworkPolicyPort validates a NetworkPolicyPort
  34. func ValidateNetworkPolicyPort(port *networking.NetworkPolicyPort, portPath *field.Path) field.ErrorList {
  35. allErrs := field.ErrorList{}
  36. if port.Protocol != nil && *port.Protocol != api.ProtocolTCP && *port.Protocol != api.ProtocolUDP && *port.Protocol != api.ProtocolSCTP {
  37. allErrs = append(allErrs, field.NotSupported(portPath.Child("protocol"), *port.Protocol, []string{string(api.ProtocolTCP), string(api.ProtocolUDP), string(api.ProtocolSCTP)}))
  38. }
  39. if port.Port != nil {
  40. if port.Port.Type == intstr.Int {
  41. for _, msg := range validation.IsValidPortNum(int(port.Port.IntVal)) {
  42. allErrs = append(allErrs, field.Invalid(portPath.Child("port"), port.Port.IntVal, msg))
  43. }
  44. } else {
  45. for _, msg := range validation.IsValidPortName(port.Port.StrVal) {
  46. allErrs = append(allErrs, field.Invalid(portPath.Child("port"), port.Port.StrVal, msg))
  47. }
  48. }
  49. }
  50. return allErrs
  51. }
  52. // ValidateNetworkPolicyPeer validates a NetworkPolicyPeer
  53. func ValidateNetworkPolicyPeer(peer *networking.NetworkPolicyPeer, peerPath *field.Path) field.ErrorList {
  54. allErrs := field.ErrorList{}
  55. numPeers := 0
  56. if peer.PodSelector != nil {
  57. numPeers++
  58. allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(peer.PodSelector, peerPath.Child("podSelector"))...)
  59. }
  60. if peer.NamespaceSelector != nil {
  61. numPeers++
  62. allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(peer.NamespaceSelector, peerPath.Child("namespaceSelector"))...)
  63. }
  64. if peer.IPBlock != nil {
  65. numPeers++
  66. allErrs = append(allErrs, ValidateIPBlock(peer.IPBlock, peerPath.Child("ipBlock"))...)
  67. }
  68. if numPeers == 0 {
  69. allErrs = append(allErrs, field.Required(peerPath, "must specify a peer"))
  70. } else if numPeers > 1 && peer.IPBlock != nil {
  71. allErrs = append(allErrs, field.Forbidden(peerPath, "may not specify both ipBlock and another peer"))
  72. }
  73. return allErrs
  74. }
  75. // ValidateNetworkPolicySpec tests if required fields in the networkpolicy spec are set.
  76. func ValidateNetworkPolicySpec(spec *networking.NetworkPolicySpec, fldPath *field.Path) field.ErrorList {
  77. allErrs := field.ErrorList{}
  78. allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(&spec.PodSelector, fldPath.Child("podSelector"))...)
  79. // Validate ingress rules.
  80. for i, ingress := range spec.Ingress {
  81. ingressPath := fldPath.Child("ingress").Index(i)
  82. for i, port := range ingress.Ports {
  83. portPath := ingressPath.Child("ports").Index(i)
  84. allErrs = append(allErrs, ValidateNetworkPolicyPort(&port, portPath)...)
  85. }
  86. for i, from := range ingress.From {
  87. fromPath := ingressPath.Child("from").Index(i)
  88. allErrs = append(allErrs, ValidateNetworkPolicyPeer(&from, fromPath)...)
  89. }
  90. }
  91. // Validate egress rules
  92. for i, egress := range spec.Egress {
  93. egressPath := fldPath.Child("egress").Index(i)
  94. for i, port := range egress.Ports {
  95. portPath := egressPath.Child("ports").Index(i)
  96. allErrs = append(allErrs, ValidateNetworkPolicyPort(&port, portPath)...)
  97. }
  98. for i, to := range egress.To {
  99. toPath := egressPath.Child("to").Index(i)
  100. allErrs = append(allErrs, ValidateNetworkPolicyPeer(&to, toPath)...)
  101. }
  102. }
  103. // Validate PolicyTypes
  104. allowed := sets.NewString(string(networking.PolicyTypeIngress), string(networking.PolicyTypeEgress))
  105. if len(spec.PolicyTypes) > len(allowed) {
  106. allErrs = append(allErrs, field.Invalid(fldPath.Child("policyTypes"), &spec.PolicyTypes, "may not specify more than two policyTypes"))
  107. return allErrs
  108. }
  109. for i, pType := range spec.PolicyTypes {
  110. policyPath := fldPath.Child("policyTypes").Index(i)
  111. if !allowed.Has(string(pType)) {
  112. allErrs = append(allErrs, field.NotSupported(policyPath, pType, []string{string(networking.PolicyTypeIngress), string(networking.PolicyTypeEgress)}))
  113. }
  114. }
  115. return allErrs
  116. }
  117. // ValidateNetworkPolicy validates a networkpolicy.
  118. func ValidateNetworkPolicy(np *networking.NetworkPolicy) field.ErrorList {
  119. allErrs := apivalidation.ValidateObjectMeta(&np.ObjectMeta, true, ValidateNetworkPolicyName, field.NewPath("metadata"))
  120. allErrs = append(allErrs, ValidateNetworkPolicySpec(&np.Spec, field.NewPath("spec"))...)
  121. return allErrs
  122. }
  123. // ValidateNetworkPolicyUpdate tests if an update to a NetworkPolicy is valid.
  124. func ValidateNetworkPolicyUpdate(update, old *networking.NetworkPolicy) field.ErrorList {
  125. allErrs := field.ErrorList{}
  126. allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&update.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata"))...)
  127. allErrs = append(allErrs, ValidateNetworkPolicySpec(&update.Spec, field.NewPath("spec"))...)
  128. return allErrs
  129. }
  130. // ValidateIPBlock validates a cidr and the except fields of an IpBlock NetworkPolicyPeer
  131. func ValidateIPBlock(ipb *networking.IPBlock, fldPath *field.Path) field.ErrorList {
  132. allErrs := field.ErrorList{}
  133. if len(ipb.CIDR) == 0 || ipb.CIDR == "" {
  134. allErrs = append(allErrs, field.Required(fldPath.Child("cidr"), ""))
  135. return allErrs
  136. }
  137. cidrIPNet, err := apivalidation.ValidateCIDR(ipb.CIDR)
  138. if err != nil {
  139. allErrs = append(allErrs, field.Invalid(fldPath.Child("cidr"), ipb.CIDR, "not a valid CIDR"))
  140. return allErrs
  141. }
  142. exceptCIDR := ipb.Except
  143. for i, exceptIP := range exceptCIDR {
  144. exceptPath := fldPath.Child("except").Index(i)
  145. exceptCIDR, err := apivalidation.ValidateCIDR(exceptIP)
  146. if err != nil {
  147. allErrs = append(allErrs, field.Invalid(exceptPath, exceptIP, "not a valid CIDR"))
  148. return allErrs
  149. }
  150. cidrMaskLen, _ := cidrIPNet.Mask.Size()
  151. exceptMaskLen, _ := exceptCIDR.Mask.Size()
  152. if !cidrIPNet.Contains(exceptCIDR.IP) || cidrMaskLen >= exceptMaskLen {
  153. allErrs = append(allErrs, field.Invalid(exceptPath, exceptIP, "must be a strict subset of `cidr`"))
  154. }
  155. }
  156. return allErrs
  157. }
  158. // ValidateIngress tests if required fields in the Ingress are set.
  159. func ValidateIngress(ingress *networking.Ingress) field.ErrorList {
  160. allErrs := apivalidation.ValidateObjectMeta(&ingress.ObjectMeta, true, ValidateIngressName, field.NewPath("metadata"))
  161. allErrs = append(allErrs, ValidateIngressSpec(&ingress.Spec, field.NewPath("spec"))...)
  162. return allErrs
  163. }
  164. // ValidateIngressName validates that the given name can be used as an Ingress name.
  165. var ValidateIngressName = apimachineryvalidation.NameIsDNSSubdomain
  166. func validateIngressTLS(spec *networking.IngressSpec, fldPath *field.Path) field.ErrorList {
  167. allErrs := field.ErrorList{}
  168. // TODO: Perform a more thorough validation of spec.TLS.Hosts that takes
  169. // the wildcard spec from RFC 6125 into account.
  170. for _, itls := range spec.TLS {
  171. for i, host := range itls.Hosts {
  172. if strings.Contains(host, "*") {
  173. for _, msg := range validation.IsWildcardDNS1123Subdomain(host) {
  174. allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("hosts"), host, msg))
  175. }
  176. continue
  177. }
  178. for _, msg := range validation.IsDNS1123Subdomain(host) {
  179. allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("hosts"), host, msg))
  180. }
  181. }
  182. }
  183. return allErrs
  184. }
  185. // ValidateIngressSpec tests if required fields in the IngressSpec are set.
  186. func ValidateIngressSpec(spec *networking.IngressSpec, fldPath *field.Path) field.ErrorList {
  187. allErrs := field.ErrorList{}
  188. // TODO: Is a default backend mandatory?
  189. if spec.Backend != nil {
  190. allErrs = append(allErrs, validateIngressBackend(spec.Backend, fldPath.Child("backend"))...)
  191. } else if len(spec.Rules) == 0 {
  192. allErrs = append(allErrs, field.Invalid(fldPath, spec.Rules, "either `backend` or `rules` must be specified"))
  193. }
  194. if len(spec.Rules) > 0 {
  195. allErrs = append(allErrs, validateIngressRules(spec.Rules, fldPath.Child("rules"))...)
  196. }
  197. if len(spec.TLS) > 0 {
  198. allErrs = append(allErrs, validateIngressTLS(spec, fldPath.Child("tls"))...)
  199. }
  200. return allErrs
  201. }
  202. // ValidateIngressUpdate tests if required fields in the Ingress are set.
  203. func ValidateIngressUpdate(ingress, oldIngress *networking.Ingress) field.ErrorList {
  204. allErrs := apivalidation.ValidateObjectMetaUpdate(&ingress.ObjectMeta, &oldIngress.ObjectMeta, field.NewPath("metadata"))
  205. allErrs = append(allErrs, ValidateIngressSpec(&ingress.Spec, field.NewPath("spec"))...)
  206. return allErrs
  207. }
  208. // ValidateIngressStatusUpdate tests if required fields in the Ingress are set when updating status.
  209. func ValidateIngressStatusUpdate(ingress, oldIngress *networking.Ingress) field.ErrorList {
  210. allErrs := apivalidation.ValidateObjectMetaUpdate(&ingress.ObjectMeta, &oldIngress.ObjectMeta, field.NewPath("metadata"))
  211. allErrs = append(allErrs, apivalidation.ValidateLoadBalancerStatus(&ingress.Status.LoadBalancer, field.NewPath("status", "loadBalancer"))...)
  212. return allErrs
  213. }
  214. func validateIngressRules(ingressRules []networking.IngressRule, fldPath *field.Path) field.ErrorList {
  215. allErrs := field.ErrorList{}
  216. if len(ingressRules) == 0 {
  217. return append(allErrs, field.Required(fldPath, ""))
  218. }
  219. for i, ih := range ingressRules {
  220. if len(ih.Host) > 0 {
  221. if isIP := (net.ParseIP(ih.Host) != nil); isIP {
  222. allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("host"), ih.Host, "must be a DNS name, not an IP address"))
  223. }
  224. // TODO: Ports and ips are allowed in the host part of a url
  225. // according to RFC 3986, consider allowing them.
  226. if strings.Contains(ih.Host, "*") {
  227. for _, msg := range validation.IsWildcardDNS1123Subdomain(ih.Host) {
  228. allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("host"), ih.Host, msg))
  229. }
  230. continue
  231. }
  232. for _, msg := range validation.IsDNS1123Subdomain(ih.Host) {
  233. allErrs = append(allErrs, field.Invalid(fldPath.Index(i).Child("host"), ih.Host, msg))
  234. }
  235. }
  236. allErrs = append(allErrs, validateIngressRuleValue(&ih.IngressRuleValue, fldPath.Index(0))...)
  237. }
  238. return allErrs
  239. }
  240. func validateIngressRuleValue(ingressRule *networking.IngressRuleValue, fldPath *field.Path) field.ErrorList {
  241. allErrs := field.ErrorList{}
  242. if ingressRule.HTTP != nil {
  243. allErrs = append(allErrs, validateHTTPIngressRuleValue(ingressRule.HTTP, fldPath.Child("http"))...)
  244. }
  245. return allErrs
  246. }
  247. func validateHTTPIngressRuleValue(httpIngressRuleValue *networking.HTTPIngressRuleValue, fldPath *field.Path) field.ErrorList {
  248. allErrs := field.ErrorList{}
  249. if len(httpIngressRuleValue.Paths) == 0 {
  250. allErrs = append(allErrs, field.Required(fldPath.Child("paths"), ""))
  251. }
  252. for i, rule := range httpIngressRuleValue.Paths {
  253. if len(rule.Path) > 0 {
  254. if !strings.HasPrefix(rule.Path, "/") {
  255. allErrs = append(allErrs, field.Invalid(fldPath.Child("paths").Index(i).Child("path"), rule.Path, "must be an absolute path"))
  256. }
  257. // TODO: More draconian path regex validation.
  258. // Path must be a valid regex. This is the basic requirement.
  259. // In addition to this any characters not allowed in a path per
  260. // RFC 3986 section-3.3 cannot appear as a literal in the regex.
  261. // Consider the example: http://host/valid?#bar, everything after
  262. // the last '/' is a valid regex that matches valid#bar, which
  263. // isn't a valid path, because the path terminates at the first ?
  264. // or #. A more sophisticated form of validation would detect that
  265. // the user is confusing url regexes with path regexes.
  266. _, err := regexp.CompilePOSIX(rule.Path)
  267. if err != nil {
  268. allErrs = append(allErrs, field.Invalid(fldPath.Child("paths").Index(i).Child("path"), rule.Path, "must be a valid regex"))
  269. }
  270. }
  271. allErrs = append(allErrs, validateIngressBackend(&rule.Backend, fldPath.Child("backend"))...)
  272. }
  273. return allErrs
  274. }
  275. // validateIngressBackend tests if a given backend is valid.
  276. func validateIngressBackend(backend *networking.IngressBackend, fldPath *field.Path) field.ErrorList {
  277. allErrs := field.ErrorList{}
  278. // All backends must reference a single local service by name, and a single service port by name or number.
  279. if len(backend.ServiceName) == 0 {
  280. return append(allErrs, field.Required(fldPath.Child("serviceName"), ""))
  281. }
  282. for _, msg := range apivalidation.ValidateServiceName(backend.ServiceName, false) {
  283. allErrs = append(allErrs, field.Invalid(fldPath.Child("serviceName"), backend.ServiceName, msg))
  284. }
  285. allErrs = append(allErrs, apivalidation.ValidatePortNumOrName(backend.ServicePort, fldPath.Child("servicePort"))...)
  286. return allErrs
  287. }