diff --git a/Makefile b/Makefile index 60c2c9c36e8..c7b1d77784c 100644 --- a/Makefile +++ b/Makefile @@ -84,8 +84,8 @@ KWOK_IMAGE = registry.k8s.io/kwok/kwok:$(KWOK_VERSION) VPC_NAT_GW_IMG = $(REGISTRY)/vpc-nat-gateway:$(VERSION) ANP_TEST_IMAGE = registry.k8s.io/e2e-test-images/agnhost:2.45 -ANP_CR_YAML = https://raw.githubusercontent.com/kubernetes-sigs/network-policy-api/main/config/crd/standard/policy.networking.k8s.io_adminnetworkpolicies.yaml -BANP_CR_YAML = https://raw.githubusercontent.com/kubernetes-sigs/network-policy-api/main/config/crd/standard/policy.networking.k8s.io_baselineadminnetworkpolicies.yaml +ANP_CR_YAML = https://raw.githubusercontent.com/kubernetes-sigs/network-policy-api/refs/heads/main/config/crd/experimental/policy.networking.k8s.io_adminnetworkpolicies.yaml +BANP_CR_YAML = https://raw.githubusercontent.com/kubernetes-sigs/network-policy-api/refs/heads/main/config/crd/experimental/policy.networking.k8s.io_baselineadminnetworkpolicies.yaml E2E_NETWORK = kube-ovn-vlan diff --git a/pkg/controller/admin_network_policy.go b/pkg/controller/admin_network_policy.go index b5bcd3282d1..aaa9814f068 100644 --- a/pkg/controller/admin_network_policy.go +++ b/pkg/controller/admin_network_policy.go @@ -3,6 +3,7 @@ package controller import ( "errors" "fmt" + "net" "reflect" "strings" "unicode" @@ -487,12 +488,6 @@ func (c *Controller) validateAnpConfig(anp *v1alpha1.AdminNetworkPolicy) error { return err } - if len(anp.Spec.Ingress) == 0 && len(anp.Spec.Egress) == 0 { - err := fmt.Errorf("one of ingress/egress rules must be set, both ingress/egress are empty for anp %s", anp.Name) - klog.Error(err) - return err - } - return nil } @@ -510,7 +505,7 @@ func (c *Controller) fetchSelectedPods(anpSubject *v1alpha1.AdminNetworkPolicySu if err != nil { return nil, fmt.Errorf("failed to fetch pods, %w", err) } - } else { + } else if anpSubject.Pods != nil { nsSelector, err := metav1.LabelSelectorAsSelector(&anpSubject.Pods.NamespaceSelector) if err != nil { return nil, fmt.Errorf("error creating ns label selector, %w", err) @@ -621,7 +616,6 @@ func (c *Controller) fetchEgressSelectedAddresses(egressPeer *v1alpha1.AdminNetw var v4Addresses, v6Addresses []string // Exactly one of the selector pointers must be set for a given peer. - // Do not support Nodes and Networks filter now switch { case egressPeer.Namespaces != nil: nsSelector, err := metav1.LabelSelectorAsSelector(egressPeer.Namespaces) @@ -647,8 +641,19 @@ func (c *Controller) fetchEgressSelectedAddresses(egressPeer *v1alpha1.AdminNetw if err != nil { return nil, nil, fmt.Errorf("failed to fetch egress peer addresses, %w", err) } + case egressPeer.Nodes != nil: + nodesSelector, err := metav1.LabelSelectorAsSelector(egressPeer.Nodes) + if err != nil { + return nil, nil, fmt.Errorf("error creating nodes label selector, %w", err) + } + v4Addresses, v6Addresses, err = c.fetchNodesAddrs(nodesSelector) + if err != nil { + return nil, nil, fmt.Errorf("failed to fetch egress peer addresses, %w", err) + } + case len(egressPeer.Networks) != 0: + v4Addresses, v6Addresses = fetchCIDRAddrs(egressPeer.Networks) default: - return nil, nil, errors.New("either Namespaces or Pods must be specified in egressPeer") + return nil, nil, errors.New("at least one egressPeer must be specified") } return v4Addresses, v6Addresses, nil @@ -851,7 +856,7 @@ func isLabelsMatch(namespaces *metav1.LabelSelector, pods *v1alpha1.NamespacedPo if nsSelector.Matches(labels.Set(nsLabels)) { return true } - } else { + } else if pods != nil { nsSelector, _ := metav1.LabelSelectorAsSelector(&pods.NamespaceSelector) podSelector, _ := metav1.LabelSelectorAsSelector(&pods.PodSelector) klog.V(3).Infof("pods is not nil, nsSelector %s, podSelector %s", nsSelector.String(), podSelector.String()) @@ -972,3 +977,46 @@ func isRulesArrayEmpty(ruleNames [util.AnpMaxRules]ChangedName) bool { } return isEmpty } + +func (c *Controller) fetchNodesAddrs(nodeSelector labels.Selector) ([]string, []string, error) { + nodes, err := c.nodesLister.List(nodeSelector) + if err != nil { + klog.Errorf("failed to list nodes: %v", err) + return nil, nil, err + } + v4Addresses := make([]string, 0, len(nodes)) + v6Addresses := make([]string, 0, len(nodes)) + + klog.V(3).Infof("fetch nodes addresses, selector is %s", nodeSelector.String()) + for _, node := range nodes { + nodeIPv4, nodeIPv6 := util.GetNodeInternalIP(*node) + if nodeIPv4 != "" { + v4Addresses = append(v4Addresses, nodeIPv4) + } + if nodeIPv6 != "" { + v6Addresses = append(v6Addresses, nodeIPv6) + } + } + + return v4Addresses, v6Addresses, nil +} + +func fetchCIDRAddrs(networks []v1alpha1.CIDR) ([]string, []string) { + v4Addresses := make([]string, 0, len(networks)) + v6Addresses := make([]string, 0, len(networks)) + + for _, network := range networks { + if _, _, err := net.ParseCIDR(string(network)); err != nil { + klog.Errorf("invalid cidr %s", string(network)) + continue + } + switch util.CheckProtocol(string(network)) { + case kubeovnv1.ProtocolIPv4: + v4Addresses = append(v4Addresses, string(network)) + case kubeovnv1.ProtocolIPv6: + v6Addresses = append(v6Addresses, string(network)) + } + } + + return v4Addresses, v6Addresses +} diff --git a/yamls/adminnetworkpolicies-v0.1.5.yaml b/yamls/adminnetworkpolicies-experimental.yaml similarity index 83% rename from yamls/adminnetworkpolicies-v0.1.5.yaml rename to yamls/adminnetworkpolicies-experimental.yaml index 41c95048b83..09e98b5c2cc 100644 --- a/yamls/adminnetworkpolicies-v0.1.5.yaml +++ b/yamls/adminnetworkpolicies-experimental.yaml @@ -4,7 +4,7 @@ metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/network-policy-api/pull/30 policy.networking.k8s.io/bundle-version: v0.1.1 - policy.networking.k8s.io/channel: standard + policy.networking.k8s.io/channel: experimental creationTimestamp: null name: adminnetworkpolicies.policy.networking.k8s.io spec: @@ -115,6 +115,16 @@ spec: maxProperties: 1 minProperties: 1 properties: + namedPort: + description: |- + NamedPort selects a port on a pod(s) based on name. + + + Support: Extended + + + + type: string portNumber: description: |- Port selects a port on a pod(s) based on number. @@ -190,6 +200,7 @@ spec: type: object type: object maxItems: 100 + minItems: 1 type: array to: description: |- @@ -209,6 +220,53 @@ spec: maxProperties: 1 minProperties: 1 properties: + domainNames: + description: |- + DomainNames provides a way to specify domain names as peers. + + + DomainNames is only supported for ALLOW rules. In order to control + access, DomainNames ALLOW rules should be used with a lower priority + egress deny -- this allows the admin to maintain an explicit "allowlist" + of reachable domains. + + + DomainNames can have up to 25 domain names specified in one rule. + + + Support: Extended + + + + items: + description: |- + DomainName describes one or more domain names to be used as a peer. + + + DomainName can be an exact match, or use the wildcard specifier '*' to match + one or more labels. + + + '*', the wildcard specifier, matches one or more entire labels. It does not + support partial matches. '*' may only be specified as a prefix. + + + Examples: + - `kubernetes.io` matches only `kubernetes.io`. + It does not match "www.kubernetes.io", "blog.kubernetes.io", + "my-kubernetes.io", or "wikipedia.org". + - `blog.kubernetes.io` matches only "blog.kubernetes.io". + It does not match "www.kubernetes.io" or "kubernetes.io". + - `*.kubernetes.io` matches subdomains of kubernetes.io. + "www.kubernetes.io", "blog.kubernetes.io", and + "latest.blog.kubernetes.io" match, however "kubernetes.io", and + "wikipedia.org" do not. + pattern: ^(\*\.)?([a-zA-z0-9]([-a-zA-Z0-9_]*[a-zA-Z0-9])?\.)+[a-zA-z0-9]([-a-zA-Z0-9_]*[a-zA-Z0-9])?\.?$ + type: string + maxItems: 25 + minItems: 1 + type: array + x-kubernetes-list-type: set namespaces: description: |- Namespaces defines a way to select all pods within a set of Namespaces. @@ -243,11 +301,106 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + networks: + description: |- + Networks defines a way to select peers via CIDR blocks. + 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. + + + 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". + + + Networks can have upto 25 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"). + This string must be validated by implementations using net.ParseCIDR + TODO: Introduce CEL CIDR validation regex isCIDR() in Kube 1.31 when it is available. + maxLength: 43 + 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: 25 + minItems: 1 + type: array + x-kubernetes-list-type: set + nodes: + description: |- + Nodes defines a way to select a set of nodes in + the cluster. This field follows standard label selector + semantics; if present but empty, it selects all Nodes. + + + Support: Extended + + + + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -299,11 +452,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -346,11 +501,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -373,6 +530,11 @@ spec: - action - to type: object + x-kubernetes-validations: + - message: networks/nodes peer cannot be set with namedPorts since + there are no namedPorts for networks/nodes + rule: '!(self.to.exists(peer, has(peer.networks) || has(peer.nodes)) + && has(self.ports) && self.ports.exists(port, has(port.namedPort)))' maxItems: 100 type: array ingress: @@ -463,11 +625,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -519,11 +683,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -566,11 +732,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -617,6 +785,16 @@ spec: maxProperties: 1 minProperties: 1 properties: + namedPort: + description: |- + NamedPort selects a port on a pod(s) based on name. + + + Support: Extended + + + + type: string portNumber: description: |- Port selects a port on a pod(s) based on number. @@ -692,6 +870,7 @@ spec: type: object type: object maxItems: 100 + minItems: 1 type: array required: - action @@ -755,11 +934,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -805,11 +986,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -851,11 +1034,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string diff --git a/yamls/baselineadminnetworkpolicies-v0.1.5.yaml b/yamls/baselineadminnetworkpolicies-experimental.yaml similarity index 86% rename from yamls/baselineadminnetworkpolicies-v0.1.5.yaml rename to yamls/baselineadminnetworkpolicies-experimental.yaml index 6d62098da74..ccff69467fc 100644 --- a/yamls/baselineadminnetworkpolicies-v0.1.5.yaml +++ b/yamls/baselineadminnetworkpolicies-experimental.yaml @@ -4,7 +4,7 @@ metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/network-policy-api/pull/30 policy.networking.k8s.io/bundle-version: v0.1.1 - policy.networking.k8s.io/channel: standard + policy.networking.k8s.io/channel: experimental creationTimestamp: null name: baselineadminnetworkpolicies.policy.networking.k8s.io spec: @@ -105,6 +105,16 @@ spec: maxProperties: 1 minProperties: 1 properties: + namedPort: + description: |- + NamedPort selects a port on a pod(s) based on name. + + + Support: Extended + + + + type: string portNumber: description: |- Port selects a port on a pod(s) based on number. @@ -180,11 +190,12 @@ spec: type: object type: object maxItems: 100 + minItems: 1 type: array to: description: |- To is the list of destinations whose traffic this rule applies to. - If any AdminNetworkPolicyEgressPeer matches the destination of outgoing + If any BaselineAdminNetworkPolicyEgressPeer matches the destination of outgoing traffic then the specified action is applied. This field must be defined and contain at least one item. @@ -192,7 +203,7 @@ spec: Support: Core items: description: |- - AdminNetworkPolicyEgressPeer defines a peer to allow traffic to. + BaselineAdminNetworkPolicyEgressPeer defines a peer to allow traffic to. 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. @@ -233,11 +244,106 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + networks: + description: |- + Networks defines a way to select peers via CIDR blocks. + 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. + + + 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". + + + Networks can have upto 25 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"). + This string must be validated by implementations using net.ParseCIDR + TODO: Introduce CEL CIDR validation regex isCIDR() in Kube 1.31 when it is available. + maxLength: 43 + 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: 25 + minItems: 1 + type: array + x-kubernetes-list-type: set + nodes: + description: |- + Nodes defines a way to select a set of nodes in + the cluster. This field follows standard label selector + semantics; if present but empty, it selects all Nodes. + + + Support: Extended + + + + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -289,11 +395,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -336,11 +444,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -363,6 +473,11 @@ spec: - action - to type: object + x-kubernetes-validations: + - message: networks/nodes peer cannot be set with namedPorts since + there are no namedPorts for networks/nodes + rule: '!(self.to.exists(peer, has(peer.networks) || has(peer.nodes)) + && has(self.ports) && self.ports.exists(port, has(port.namedPort)))' maxItems: 100 type: array ingress: @@ -449,11 +564,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -505,11 +622,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -552,11 +671,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -603,6 +724,16 @@ spec: maxProperties: 1 minProperties: 1 properties: + namedPort: + description: |- + NamedPort selects a port on a pod(s) based on name. + + + Support: Extended + + + + type: string portNumber: description: |- Port selects a port on a pod(s) based on number. @@ -678,6 +809,7 @@ spec: type: object type: object maxItems: 100 + minItems: 1 type: array required: - action @@ -724,11 +856,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -774,11 +908,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string @@ -820,11 +956,13 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic required: - key - operator type: object type: array + x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string