| /* |
| Copyright 2017 The Kubernetes Authors. |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| package validation |
| |
| import ( |
| apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation" |
| unversionedvalidation "k8s.io/apimachinery/pkg/apis/meta/v1/validation" |
| "k8s.io/apimachinery/pkg/util/intstr" |
| "k8s.io/apimachinery/pkg/util/sets" |
| "k8s.io/apimachinery/pkg/util/validation" |
| "k8s.io/apimachinery/pkg/util/validation/field" |
| utilfeature "k8s.io/apiserver/pkg/util/feature" |
| api "k8s.io/kubernetes/pkg/apis/core" |
| apivalidation "k8s.io/kubernetes/pkg/apis/core/validation" |
| "k8s.io/kubernetes/pkg/apis/networking" |
| "k8s.io/kubernetes/pkg/features" |
| ) |
| |
| // ValidateNetworkPolicyName can be used to check whether the given networkpolicy |
| // name is valid. |
| func ValidateNetworkPolicyName(name string, prefix bool) []string { |
| return apimachineryvalidation.NameIsDNSSubdomain(name, prefix) |
| } |
| |
| // ValidateNetworkPolicyPort validates a NetworkPolicyPort |
| func ValidateNetworkPolicyPort(port *networking.NetworkPolicyPort, portPath *field.Path) field.ErrorList { |
| allErrs := field.ErrorList{} |
| if utilfeature.DefaultFeatureGate.Enabled(features.SCTPSupport) { |
| if port.Protocol != nil && *port.Protocol != api.ProtocolTCP && *port.Protocol != api.ProtocolUDP && *port.Protocol != api.ProtocolSCTP { |
| allErrs = append(allErrs, field.NotSupported(portPath.Child("protocol"), *port.Protocol, []string{string(api.ProtocolTCP), string(api.ProtocolUDP), string(api.ProtocolSCTP)})) |
| } |
| } else if port.Protocol != nil && *port.Protocol != api.ProtocolTCP && *port.Protocol != api.ProtocolUDP { |
| allErrs = append(allErrs, field.NotSupported(portPath.Child("protocol"), *port.Protocol, []string{string(api.ProtocolTCP), string(api.ProtocolUDP)})) |
| } |
| if port.Port != nil { |
| if port.Port.Type == intstr.Int { |
| for _, msg := range validation.IsValidPortNum(int(port.Port.IntVal)) { |
| allErrs = append(allErrs, field.Invalid(portPath.Child("port"), port.Port.IntVal, msg)) |
| } |
| } else { |
| for _, msg := range validation.IsValidPortName(port.Port.StrVal) { |
| allErrs = append(allErrs, field.Invalid(portPath.Child("port"), port.Port.StrVal, msg)) |
| } |
| } |
| } |
| |
| return allErrs |
| } |
| |
| // ValidateNetworkPolicyPeer validates a NetworkPolicyPeer |
| func ValidateNetworkPolicyPeer(peer *networking.NetworkPolicyPeer, peerPath *field.Path) field.ErrorList { |
| allErrs := field.ErrorList{} |
| numPeers := 0 |
| |
| if peer.PodSelector != nil { |
| numPeers++ |
| allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(peer.PodSelector, peerPath.Child("podSelector"))...) |
| } |
| if peer.NamespaceSelector != nil { |
| numPeers++ |
| allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(peer.NamespaceSelector, peerPath.Child("namespaceSelector"))...) |
| } |
| if peer.IPBlock != nil { |
| numPeers++ |
| allErrs = append(allErrs, ValidateIPBlock(peer.IPBlock, peerPath.Child("ipBlock"))...) |
| } |
| |
| if numPeers == 0 { |
| allErrs = append(allErrs, field.Required(peerPath, "must specify a peer")) |
| } else if numPeers > 1 && peer.IPBlock != nil { |
| allErrs = append(allErrs, field.Forbidden(peerPath, "may not specify both ipBlock and another peer")) |
| } |
| |
| return allErrs |
| } |
| |
| // ValidateNetworkPolicySpec tests if required fields in the networkpolicy spec are set. |
| func ValidateNetworkPolicySpec(spec *networking.NetworkPolicySpec, fldPath *field.Path) field.ErrorList { |
| allErrs := field.ErrorList{} |
| allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(&spec.PodSelector, fldPath.Child("podSelector"))...) |
| |
| // Validate ingress rules. |
| for i, ingress := range spec.Ingress { |
| ingressPath := fldPath.Child("ingress").Index(i) |
| for i, port := range ingress.Ports { |
| portPath := ingressPath.Child("ports").Index(i) |
| allErrs = append(allErrs, ValidateNetworkPolicyPort(&port, portPath)...) |
| } |
| for i, from := range ingress.From { |
| fromPath := ingressPath.Child("from").Index(i) |
| allErrs = append(allErrs, ValidateNetworkPolicyPeer(&from, fromPath)...) |
| } |
| } |
| // Validate egress rules |
| for i, egress := range spec.Egress { |
| egressPath := fldPath.Child("egress").Index(i) |
| for i, port := range egress.Ports { |
| portPath := egressPath.Child("ports").Index(i) |
| allErrs = append(allErrs, ValidateNetworkPolicyPort(&port, portPath)...) |
| } |
| for i, to := range egress.To { |
| toPath := egressPath.Child("to").Index(i) |
| allErrs = append(allErrs, ValidateNetworkPolicyPeer(&to, toPath)...) |
| } |
| } |
| // Validate PolicyTypes |
| allowed := sets.NewString(string(networking.PolicyTypeIngress), string(networking.PolicyTypeEgress)) |
| if len(spec.PolicyTypes) > len(allowed) { |
| allErrs = append(allErrs, field.Invalid(fldPath.Child("policyTypes"), &spec.PolicyTypes, "may not specify more than two policyTypes")) |
| return allErrs |
| } |
| for i, pType := range spec.PolicyTypes { |
| policyPath := fldPath.Child("policyTypes").Index(i) |
| for _, p := range spec.PolicyTypes { |
| if !allowed.Has(string(p)) { |
| allErrs = append(allErrs, field.NotSupported(policyPath, pType, []string{string(networking.PolicyTypeIngress), string(networking.PolicyTypeEgress)})) |
| } |
| } |
| } |
| return allErrs |
| } |
| |
| // ValidateNetworkPolicy validates a networkpolicy. |
| func ValidateNetworkPolicy(np *networking.NetworkPolicy) field.ErrorList { |
| allErrs := apivalidation.ValidateObjectMeta(&np.ObjectMeta, true, ValidateNetworkPolicyName, field.NewPath("metadata")) |
| allErrs = append(allErrs, ValidateNetworkPolicySpec(&np.Spec, field.NewPath("spec"))...) |
| return allErrs |
| } |
| |
| // ValidateNetworkPolicyUpdate tests if an update to a NetworkPolicy is valid. |
| func ValidateNetworkPolicyUpdate(update, old *networking.NetworkPolicy) field.ErrorList { |
| allErrs := field.ErrorList{} |
| allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&update.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata"))...) |
| allErrs = append(allErrs, ValidateNetworkPolicySpec(&update.Spec, field.NewPath("spec"))...) |
| return allErrs |
| } |
| |
| // ValidateIPBlock validates a cidr and the except fields of an IpBlock NetworkPolicyPeer |
| func ValidateIPBlock(ipb *networking.IPBlock, fldPath *field.Path) field.ErrorList { |
| allErrs := field.ErrorList{} |
| if len(ipb.CIDR) == 0 || ipb.CIDR == "" { |
| allErrs = append(allErrs, field.Required(fldPath.Child("cidr"), "")) |
| return allErrs |
| } |
| cidrIPNet, err := apivalidation.ValidateCIDR(ipb.CIDR) |
| if err != nil { |
| allErrs = append(allErrs, field.Invalid(fldPath.Child("cidr"), ipb.CIDR, "not a valid CIDR")) |
| return allErrs |
| } |
| exceptCIDR := ipb.Except |
| for i, exceptIP := range exceptCIDR { |
| exceptPath := fldPath.Child("except").Index(i) |
| exceptCIDR, err := apivalidation.ValidateCIDR(exceptIP) |
| if err != nil { |
| allErrs = append(allErrs, field.Invalid(exceptPath, exceptIP, "not a valid CIDR")) |
| return allErrs |
| } |
| if !cidrIPNet.Contains(exceptCIDR.IP) { |
| allErrs = append(allErrs, field.Invalid(exceptPath, exceptCIDR.IP, "not within CIDR range")) |
| } |
| } |
| return allErrs |
| } |