Ensure egress NP works with Service without selectors

This commit ensures egress Network policy can also work with
services without selectors.

Change-Id: I26e1dce0b6e363f027ee6d4dfea99053ffe80bbe
This commit is contained in:
Maysa Macedo 2020-10-06 20:57:41 +00:00
parent 4ce01a7908
commit 525dc1521d
6 changed files with 156 additions and 17 deletions

View File

@ -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

View File

@ -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.

View File

@ -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[:]

View File

@ -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

View File

@ -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

View File

@ -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.