diff --git a/doc/source/devref/network_policy.rst b/doc/source/devref/network_policy.rst index 71714b9b8..70ef8b530 100644 --- a/doc/source/devref/network_policy.rst +++ b/doc/source/devref/network_policy.rst @@ -296,7 +296,7 @@ restriction was enforced. Allow traffic from namespace ++++++++++++++++++++++++++++ -The following network policy only allows allowing ingress traffic +The following network policy only allows ingress traffic from namespace with the label ``purpose=test``: .. code-block:: yaml @@ -363,9 +363,94 @@ egress rule allowing traffic to everywhere. .. note:: - The Service security groups need to be rechecked when a network policy - that affects ingress traffic is created, and also everytime - a pod or namespace is created. + Only when using Amphora Octavia provider and Services with selector, + the Load Balancer security groups need to be rechecked when a + network policy that affects ingress traffic is created, and + also everytime a pod or namespace is created. Network Policy + is not enforced on Services without Selectors. + +Allow traffic to a Pod in a Namespace ++++++++++++++++++++++++++++++++++++++ + +The following network policy only allows egress traffic from Pods +with the label ``app=demo`` at Namespace with label ``app=demo``: + +.. code-block:: yaml + + apiVersion: networking.k8s.io/v1 + kind: NetworkPolicy + metadata: + name: block-egress + namespace: client + spec: + podSelector: + matchLabels: + app: client + policyTypes: + - Egress + egress: + - from: + - namespaceSelector: + matchLabels: + app: demo + podSelector: + matchLabels: + app: demo + +The resulting CRD has an ingress rules allowing traffic +from everywhere and egress rules to the selected Pod +and the Service that points to it. + +.. code-block:: yaml + + apiVersion: openstack.org/v1 + kind: KuryrNetworkPolicy + metadata: ... + spec: + egressSgRules: + - namespace: demo + sgRule: + description: Kuryr-Kubernetes NetPolicy SG rule + direction: egress + ethertype: IPv4 + port_range_max: 65535 + port_range_min: 1 + protocol: tcp + remote_ip_prefix: 10.0.2.120 + - sgRule: + description: Kuryr-Kubernetes NetPolicy SG rule + direction: egress + ethertype: IPv4 + port_range_max: 65535 + port_range_min: 1 + protocol: tcp + remote_ip_prefix: 10.0.0.144 + ingressSgRules: + - sgRule: + description: Kuryr-Kubernetes NetPolicy SG rule + direction: ingress + ethertype: IPv4 + - sgRule: + description: Kuryr-Kubernetes NetPolicy SG rule + direction: ingress + ethertype: IPv6 + podSelector: + matchLabels: + app: client + policyTypes: + - Egress + status: + podSelector: + matchLabels: + app: client + securityGroupId: 322a347b-0684-4aea-945a-5f204361a64e + securityGroupRules: ... + +.. note:: + + A Network Policy egress rule creates a Security Group rule + corresponding to a Service(with or without selectors) that + points to the selected Pod. Create network policy flow diff --git a/kuryr_kubernetes/controller/drivers/network_policy.py b/kuryr_kubernetes/controller/drivers/network_policy.py index 3a2ef3265..54a8f2d98 100644 --- a/kuryr_kubernetes/controller/drivers/network_policy.py +++ b/kuryr_kubernetes/controller/drivers/network_policy.py @@ -542,26 +542,39 @@ class NetworkPolicyDriver(base.NetworkPolicyDriver): return for service in services.get('items'): + svc_name = service['metadata']['name'] + svc_namespace = service['metadata']['namespace'] if self._is_pod(resource): pod_labels = resource['metadata'].get('labels') svc_selector = service['spec'].get('selector') - if not svc_selector or not pod_labels: - continue - else: + if not svc_selector: + targets = driver_utils.get_endpoints_targets( + svc_name, svc_namespace) + pod_ip = resource['status'].get('podIP') + if pod_ip and pod_ip not in targets: + continue + elif pod_labels: if not driver_utils.match_labels( svc_selector, pod_labels): continue elif resource.get('cidr'): # NOTE(maysams) Accounts for traffic to pods under # a service matching an IPBlock rule. - svc_namespace = service['metadata']['namespace'] - if svc_namespace != policy_namespace: - continue svc_selector = service['spec'].get('selector') - pods = driver_utils.get_pods({'selector': svc_selector}, - svc_namespace).get('items') - if not self._pods_in_ip_block(pods, resource): - continue + if not svc_selector: + # Retrieving targets of services on any Namespace + targets = driver_utils.get_endpoints_targets( + svc_name, svc_namespace) + if (not targets or + not self._targets_in_ip_block(targets, resource)): + continue + else: + if svc_namespace != policy_namespace: + continue + pods = driver_utils.get_pods({'selector': svc_selector}, + svc_namespace).get('items') + if not self._pods_in_ip_block(pods, resource): + continue else: ns_name = service['metadata']['namespace'] if ns_name != resource['metadata']['name']: @@ -583,6 +596,13 @@ class NetworkPolicyDriver(base.NetworkPolicyDriver): return True return False + def _targets_in_ip_block(self, targets, resource): + for target in targets: + if (ipaddress.ip_address(target) + not in ipaddress.ip_network(resource.get('cidr'))): + return False + return True + def parse_network_policy_rules(self, policy): """Create security group rule bodies out of network policies. diff --git a/kuryr_kubernetes/controller/drivers/network_policy_security_groups.py b/kuryr_kubernetes/controller/drivers/network_policy_security_groups.py index 26aceffee..55673bd76 100644 --- a/kuryr_kubernetes/controller/drivers/network_policy_security_groups.py +++ b/kuryr_kubernetes/controller/drivers/network_policy_security_groups.py @@ -433,7 +433,6 @@ class NetworkPolicyServiceSecurityGroupsDriver( svc_namespace = service['metadata']['namespace'] svc_selector = service['spec'].get('selector') - # skip is no selector if svc_selector: # get affected pods by svc selector pods = driver_utils.get_pods({'selector': svc_selector}, @@ -444,4 +443,11 @@ class NetworkPolicyServiceSecurityGroupsDriver( # to the first one. if pods: return _get_pod_sgs(pods[0]) + else: + # NOTE(maysams): Network Policy is not enforced on Services + # without selectors. + sg_list = config.CONF.neutron_defaults.pod_security_groups + if not sg_list: + raise cfg.RequiredOptError('pod_security_groups', + cfg.OptGroup('neutron_defaults')) return sg_list[:] diff --git a/kuryr_kubernetes/controller/drivers/utils.py b/kuryr_kubernetes/controller/drivers/utils.py index dc66ecebc..9d7a3657f 100644 --- a/kuryr_kubernetes/controller/drivers/utils.py +++ b/kuryr_kubernetes/controller/drivers/utils.py @@ -586,3 +586,24 @@ def get_port_annot_pci_info(nodename, neutron_port): LOG.exception('Exception when reading annotations ' '%s and converting from json', annot_name) return pci_info + + +def get_endpoints_targets(name, namespace): + kubernetes = clients.get_kubernetes_client() + target_ips = [] + try: + klb_crd = kubernetes.get( + f'{constants.K8S_API_CRD_NAMESPACES}/{namespace}/' + f'kuryrloadbalancers/{name}') + except k_exc.K8sResourceNotFound: + LOG.debug("KuryrLoadBalancer %s not found on Namespace %s.", + name, namespace) + return target_ips + except k_exc.K8sClientException: + LOG.exception('Exception when getting K8s Endpoints.') + raise + + for ep_slice in klb_crd['spec'].get('endpointSlices', []): + for endpoint in ep_slice.get('endpoints', []): + target_ips.extend(endpoint.get('addresses', [])) + return target_ips diff --git a/kuryr_kubernetes/controller/handlers/kuryrnetworkpolicy.py b/kuryr_kubernetes/controller/handlers/kuryrnetworkpolicy.py index 3ea3a8149..98781b87f 100644 --- a/kuryr_kubernetes/controller/handlers/kuryrnetworkpolicy.py +++ b/kuryr_kubernetes/controller/handlers/kuryrnetworkpolicy.py @@ -217,8 +217,8 @@ class KuryrNetworkPolicyHandler(k8s_base.ResourceEventHandler): for service in services.get('items', []): # TODO(ltomasbo): Skip other services that are not affected # by the policy - # FIXME(dulek): Make sure to include svcs without selector when - # we start supporting them. + # NOTE(maysams): Network Policy is not enforced on Services + # without selectors for Amphora Octavia provider. if (not service['spec'].get('selector') or not self._is_service_affected(service, pods_to_update)): continue diff --git a/releasenotes/notes/network-policy-support-on-services-without-selectors-fea06ab71a8a6f2a.yaml b/releasenotes/notes/network-policy-support-on-services-without-selectors-fea06ab71a8a6f2a.yaml new file mode 100644 index 000000000..706b5f6be --- /dev/null +++ b/releasenotes/notes/network-policy-support-on-services-without-selectors-fea06ab71a8a6f2a.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + Kuryr can now handle egress Network Policy that allows + traffic to Pods being Pointed by a Service without Selector. + Also, ingress Network Policy is not enforced on Services + without Selectors when the Octavia provider is Amphora.