diff --git a/apis/v1alpha1/shared_types.go b/apis/v1alpha1/shared_types.go index ba37b92c..ef42965c 100644 --- a/apis/v1alpha1/shared_types.go +++ b/apis/v1alpha1/shared_types.go @@ -121,6 +121,13 @@ type PortRange struct { } // AdminNetworkPolicyIngressPeer defines an in-cluster peer to allow traffic from. +// +// Note that presence of a Service object with this policy subject as its backend +// has no impact on the behavior of the policy applied to the peer +// trying to talk to the Service. It will work in the same way as if the +// Service didn't exist since policy is applied after ServiceVIP (clusterIP, +// externalIP, loadBalancerIngressVIP) is rewritten to the backendIPs. +// // Exactly one of the selector pointers must be set for a given peer. If a // consumer observes none of its fields are set, they must assume an unknown // option has been specified and fail closed. @@ -145,6 +152,13 @@ type AdminNetworkPolicyIngressPeer struct { } // AdminNetworkPolicyEgressPeer defines a peer to allow traffic to. +// +// Note that presence of a Service object with this peer as its backend +// has no impact on the behavior of the policy applied to the subject +// trying to talk to the Service. It will work in the same way as if the +// Service didn't exist since policy is applied after ServiceVIP (clusterIP, +// externalIP, loadBalancerIngressVIP) is rewritten to the backendIPs. +// // Exactly one of the selector pointers must be set for a given peer. If a // consumer observes none of its fields are set, they must assume an unknown // option has been specified and fail closed. @@ -175,6 +189,33 @@ type AdminNetworkPolicyEgressPeer struct { // // +optional Nodes *metav1.LabelSelector `json:"nodes,omitempty"` + // Networks defines a way to select peers via CIDR blocks (both v4 & v6). + // This is intended for representing entities that live outside the cluster, + // which can't be selected by pods, namespaces and nodes peers, but note + // that cluster-internal traffic will be checked against the rule as + // well. So if you Allow or Deny traffic to `"0.0.0.0/0"`, that will allow + // or deny all IPv4 pod-to-pod traffic as well. If you don't want that, + // add a rule that Passes all pod traffic before the Networks rule. + // + // Do not provide serviceCIDR range in `networks` peer. This is NOT supported. + // While 0.0.0.0/0 will match on the serviceVIP as well, it does not make much sense + // anyways in that case because policies are expected to be applied after the serviceVIP + // (clusterIP, externalIP, loadbalancer.Ingress.VIP) is rewritten to the actual backend IPs. + // So policy match finally gets applied on the endpointIPs not serviceVIPs. + // + // Each item in Networks should be provided in the CIDR format and should be + // IPv4 or IPv6, for example "10.0.0.0/8" or "fd00::/8". IPv4 address embedded + // in IPv6 addresses are not supported. + // + // Networks can have upto 30 CIDRs specified. + // + // Support: Extended + // + // + // +optional + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=30 + Networks []CIDR `json:"networks,omitempty"` } // NamespacedPeer defines a flexible way to select Namespaces in a cluster. @@ -237,3 +278,13 @@ type NamespacedPodPeer struct { // PodSelector metav1.LabelSelector `json:"podSelector"` } + +// CIDR is an IP address range in CIDR notation (for example, "10.0.0.0/8" or "fd00::/8"). +// The regex for the IPv4 and IPv6 CIDR range was taken from +// https://blog.markhatton.co.uk/2011/03/15/regular-expressions-for-ip-addresses-cidr-ranges-and-hostnames/ +// The resulting regex is an OR of both regexes. IPv4 address embedded in IPv6 addresses are not supported. +// TODO: Change the CIDR's validation regex to use CEL isCIDR() in Kube 1.31 when it is available. +// +kubebuilder:validation:Pattern=`(^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(3[0-2]|[1-2][0-9]|[0-9]))$)|(^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(\/(12[0-8]|1[0-1][0-9]|[1-9][0-9]|[0-9]))$)` +// +kubebuilder:validation:XValidation:rule="self.contains(':') != self.contains('.')",message="CIDR must be either an IPv4 or IPv6 address. IPv4 address embedded in IPv6 addresses are not supported" +// +kubebuilder:validation:MaxLength=43 +type CIDR string diff --git a/apis/v1alpha1/zz_generated.deepcopy.go b/apis/v1alpha1/zz_generated.deepcopy.go index 5518baca..82b1b61d 100644 --- a/apis/v1alpha1/zz_generated.deepcopy.go +++ b/apis/v1alpha1/zz_generated.deepcopy.go @@ -70,6 +70,11 @@ func (in *AdminNetworkPolicyEgressPeer) DeepCopyInto(out *AdminNetworkPolicyEgre *out = new(v1.LabelSelector) (*in).DeepCopyInto(*out) } + if in.Networks != nil { + in, out := &in.Networks, &out.Networks + *out = make([]CIDR, len(*in)) + copy(*out, *in) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AdminNetworkPolicyEgressPeer. diff --git a/config/crd/experimental/policy.networking.k8s.io_adminnetworkpolicies.yaml b/config/crd/experimental/policy.networking.k8s.io_adminnetworkpolicies.yaml index c1900148..7aec2ec0 100644 --- a/config/crd/experimental/policy.networking.k8s.io_adminnetworkpolicies.yaml +++ b/config/crd/experimental/policy.networking.k8s.io_adminnetworkpolicies.yaml @@ -208,6 +208,15 @@ spec: items: description: |- AdminNetworkPolicyEgressPeer defines a peer to allow traffic to. + + + Note that presence of a Service object with this peer as its backend + has no impact on the behavior of the policy applied to the subject + trying to talk to the Service. It will work in the same way as if the + Service didn't exist since policy is applied after ServiceVIP (clusterIP, + externalIP, loadBalancerIngressVIP) is rewritten to the backendIPs. + + Exactly one of the selector pointers must be set for a given peer. If a consumer observes none of its fields are set, they must assume an unknown option has been specified and fail closed. @@ -310,6 +319,54 @@ spec: maxItems: 100 type: array type: object + networks: + description: |- + Networks defines a way to select peers via CIDR blocks (both v4 & v6). + This is intended for representing entities that live outside the cluster, + which can't be selected by pods, namespaces and nodes peers, but note + that cluster-internal traffic will be checked against the rule as + well. So if you Allow or Deny traffic to `"0.0.0.0/0"`, that will allow + or deny all IPv4 pod-to-pod traffic as well. If you don't want that, + add a rule that Passes all pod traffic before the Networks rule. + + + Do not provide serviceCIDR range in `networks` peer. This is NOT supported. + While 0.0.0.0/0 will match on the serviceVIP as well, it does not make much sense + anyways in that case because policies are expected to be applied after the serviceVIP + (clusterIP, externalIP, loadbalancer.Ingress.VIP) is rewritten to the actual backend IPs. + So policy match finally gets applied on the endpointIPs not serviceVIPs. + + + Each item in Networks should be provided in the CIDR format and should be + IPv4 or IPv6, for example "10.0.0.0/8" or "fd00::/8". IPv4 address embedded + in IPv6 addresses are not supported. + + + Networks can have upto 30 CIDRs specified. + + + Support: Extended + + + + items: + description: |- + CIDR is an IP address range in CIDR notation (for example, "10.0.0.0/8" or "fd00::/8"). + The regex for the IPv4 and IPv6 CIDR range was taken from + https://blog.markhatton.co.uk/2011/03/15/regular-expressions-for-ip-addresses-cidr-ranges-and-hostnames/ + The resulting regex is an OR of both regexes. IPv4 address embedded in IPv6 addresses are not supported. + TODO: Change the CIDR's validation regex to use CEL isCIDR() in Kube 1.31 when it is available. + maxLength: 43 + pattern: (^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(3[0-2]|[1-2][0-9]|[0-9]))$)|(^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(\/(12[0-8]|1[0-1][0-9]|[1-9][0-9]|[0-9]))$) + type: string + x-kubernetes-validations: + - message: CIDR must be either an IPv4 or IPv6 address. + IPv4 address embedded in IPv6 addresses are not + supported + rule: self.contains(':') != self.contains('.') + maxItems: 30 + minItems: 1 + type: array nodes: description: |- Nodes defines a way to select a set of nodes in @@ -576,6 +633,15 @@ spec: items: description: |- AdminNetworkPolicyIngressPeer defines an in-cluster peer to allow traffic from. + + + Note that presence of a Service object with this policy subject as its backend + has no impact on the behavior of the policy applied to the peer + trying to talk to the Service. It will work in the same way as if the + Service didn't exist since policy is applied after ServiceVIP (clusterIP, + externalIP, loadBalancerIngressVIP) is rewritten to the backendIPs. + + Exactly one of the selector pointers must be set for a given peer. If a consumer observes none of its fields are set, they must assume an unknown option has been specified and fail closed. diff --git a/config/crd/experimental/policy.networking.k8s.io_baselineadminnetworkpolicies.yaml b/config/crd/experimental/policy.networking.k8s.io_baselineadminnetworkpolicies.yaml index 6ebb436e..2b591f5d 100644 --- a/config/crd/experimental/policy.networking.k8s.io_baselineadminnetworkpolicies.yaml +++ b/config/crd/experimental/policy.networking.k8s.io_baselineadminnetworkpolicies.yaml @@ -199,6 +199,15 @@ spec: items: description: |- AdminNetworkPolicyEgressPeer defines a peer to allow traffic to. + + + Note that presence of a Service object with this peer as its backend + has no impact on the behavior of the policy applied to the subject + trying to talk to the Service. It will work in the same way as if the + Service didn't exist since policy is applied after ServiceVIP (clusterIP, + externalIP, loadBalancerIngressVIP) is rewritten to the backendIPs. + + Exactly one of the selector pointers must be set for a given peer. If a consumer observes none of its fields are set, they must assume an unknown option has been specified and fail closed. @@ -301,6 +310,54 @@ spec: maxItems: 100 type: array type: object + networks: + description: |- + Networks defines a way to select peers via CIDR blocks (both v4 & v6). + This is intended for representing entities that live outside the cluster, + which can't be selected by pods, namespaces and nodes peers, but note + that cluster-internal traffic will be checked against the rule as + well. So if you Allow or Deny traffic to `"0.0.0.0/0"`, that will allow + or deny all IPv4 pod-to-pod traffic as well. If you don't want that, + add a rule that Passes all pod traffic before the Networks rule. + + + Do not provide serviceCIDR range in `networks` peer. This is NOT supported. + While 0.0.0.0/0 will match on the serviceVIP as well, it does not make much sense + anyways in that case because policies are expected to be applied after the serviceVIP + (clusterIP, externalIP, loadbalancer.Ingress.VIP) is rewritten to the actual backend IPs. + So policy match finally gets applied on the endpointIPs not serviceVIPs. + + + Each item in Networks should be provided in the CIDR format and should be + IPv4 or IPv6, for example "10.0.0.0/8" or "fd00::/8". IPv4 address embedded + in IPv6 addresses are not supported. + + + Networks can have upto 30 CIDRs specified. + + + Support: Extended + + + + items: + description: |- + CIDR is an IP address range in CIDR notation (for example, "10.0.0.0/8" or "fd00::/8"). + The regex for the IPv4 and IPv6 CIDR range was taken from + https://blog.markhatton.co.uk/2011/03/15/regular-expressions-for-ip-addresses-cidr-ranges-and-hostnames/ + The resulting regex is an OR of both regexes. IPv4 address embedded in IPv6 addresses are not supported. + TODO: Change the CIDR's validation regex to use CEL isCIDR() in Kube 1.31 when it is available. + maxLength: 43 + pattern: (^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(3[0-2]|[1-2][0-9]|[0-9]))$)|(^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(\/(12[0-8]|1[0-1][0-9]|[1-9][0-9]|[0-9]))$) + type: string + x-kubernetes-validations: + - message: CIDR must be either an IPv4 or IPv6 address. + IPv4 address embedded in IPv6 addresses are not + supported + rule: self.contains(':') != self.contains('.') + maxItems: 30 + minItems: 1 + type: array nodes: description: |- Nodes defines a way to select a set of nodes in @@ -564,6 +621,15 @@ spec: items: description: |- AdminNetworkPolicyIngressPeer defines an in-cluster peer to allow traffic from. + + + Note that presence of a Service object with this policy subject as its backend + has no impact on the behavior of the policy applied to the peer + trying to talk to the Service. It will work in the same way as if the + Service didn't exist since policy is applied after ServiceVIP (clusterIP, + externalIP, loadBalancerIngressVIP) is rewritten to the backendIPs. + + Exactly one of the selector pointers must be set for a given peer. If a consumer observes none of its fields are set, they must assume an unknown option has been specified and fail closed. diff --git a/config/crd/standard/policy.networking.k8s.io_adminnetworkpolicies.yaml b/config/crd/standard/policy.networking.k8s.io_adminnetworkpolicies.yaml index 4b5f0c5a..f00cadb8 100644 --- a/config/crd/standard/policy.networking.k8s.io_adminnetworkpolicies.yaml +++ b/config/crd/standard/policy.networking.k8s.io_adminnetworkpolicies.yaml @@ -198,6 +198,15 @@ spec: items: description: |- AdminNetworkPolicyEgressPeer defines a peer to allow traffic to. + + + Note that presence of a Service object with this peer as its backend + has no impact on the behavior of the policy applied to the subject + trying to talk to the Service. It will work in the same way as if the + Service didn't exist since policy is applied after ServiceVIP (clusterIP, + externalIP, loadBalancerIngressVIP) is rewritten to the backendIPs. + + Exactly one of the selector pointers must be set for a given peer. If a consumer observes none of its fields are set, they must assume an unknown option has been specified and fail closed. @@ -445,6 +454,15 @@ spec: items: description: |- AdminNetworkPolicyIngressPeer defines an in-cluster peer to allow traffic from. + + + Note that presence of a Service object with this policy subject as its backend + has no impact on the behavior of the policy applied to the peer + trying to talk to the Service. It will work in the same way as if the + Service didn't exist since policy is applied after ServiceVIP (clusterIP, + externalIP, loadBalancerIngressVIP) is rewritten to the backendIPs. + + Exactly one of the selector pointers must be set for a given peer. If a consumer observes none of its fields are set, they must assume an unknown option has been specified and fail closed. diff --git a/config/crd/standard/policy.networking.k8s.io_baselineadminnetworkpolicies.yaml b/config/crd/standard/policy.networking.k8s.io_baselineadminnetworkpolicies.yaml index 9b23189a..358a6821 100644 --- a/config/crd/standard/policy.networking.k8s.io_baselineadminnetworkpolicies.yaml +++ b/config/crd/standard/policy.networking.k8s.io_baselineadminnetworkpolicies.yaml @@ -189,6 +189,15 @@ spec: items: description: |- AdminNetworkPolicyEgressPeer defines a peer to allow traffic to. + + + Note that presence of a Service object with this peer as its backend + has no impact on the behavior of the policy applied to the subject + trying to talk to the Service. It will work in the same way as if the + Service didn't exist since policy is applied after ServiceVIP (clusterIP, + externalIP, loadBalancerIngressVIP) is rewritten to the backendIPs. + + Exactly one of the selector pointers must be set for a given peer. If a consumer observes none of its fields are set, they must assume an unknown option has been specified and fail closed. @@ -433,6 +442,15 @@ spec: items: description: |- AdminNetworkPolicyIngressPeer defines an in-cluster peer to allow traffic from. + + + Note that presence of a Service object with this policy subject as its backend + has no impact on the behavior of the policy applied to the peer + trying to talk to the Service. It will work in the same way as if the + Service didn't exist since policy is applied after ServiceVIP (clusterIP, + externalIP, loadBalancerIngressVIP) is rewritten to the backendIPs. + + Exactly one of the selector pointers must be set for a given peer. If a consumer observes none of its fields are set, they must assume an unknown option has been specified and fail closed. diff --git a/site-src/npeps/npep-126-egress-traffic-control.md b/site-src/npeps/npep-126-egress-traffic-control.md index 199f5358..77fd521f 100644 --- a/site-src/npeps/npep-126-egress-traffic-control.md +++ b/site-src/npeps/npep-126-egress-traffic-control.md @@ -188,6 +188,16 @@ correctly define the boundary between "internal" and "external" destinations wit to a Kubernetes cluster which is why this field is generic enough to select any CIDR destination. +Note2: Do not provide serviceCIDR range in `networks` peer. This is NOT supported. +While 0.0.0.0/0 will match on the serviceVIP as well, it does not make much sense +anyways in that case because policies are expected to be applied after the serviceVIP +(clusterIP, externalIP, loadbalancer.Ingress.VIP) is rewritten to the actual backend IPs. +So policy match finally gets applied on the endpointIPs not serviceVIPs. +The same is true for intra-cluster traffic. Today we only support expressing pods as policy +subjects and pods, nodes, cidrs as peers. This in the `Service` world translates to `endpoints` +OR `endpointslices`. So any services created between subject-to-peer traffic with an applied policy +will behave like the service didn't really exist and that its a normal pod-to-pod communication. + Example: Let's define ANP and BANP that refer to some CIDR networks: ``` apiVersion: policy.networking.k8s.io/v1alpha1