def _parse_selectors_on_namespace(crd, direction, pod_selector, ns_selector, rule_block, crd_rules, namespace, matched): ns_name = namespace['metadata'].get('name') ns_labels = namespace['metadata'].get('labels') sg_id = crd['spec']['securityGroupId'] if (ns_selector and ns_labels and driver_utils.match_selector(ns_selector, ns_labels)): if pod_selector: pods = driver_utils.get_pods(pod_selector, ns_name).get('items') if 'ports' in rule_block: for port in rule_block['ports']: if type(port.get('port')) is not int: matched = (_create_sg_rule_on_text_port( sg_id, direction, port, pods, crd_rules, matched, crd)) else: matched = True for pod in pods: pod_ip = driver_utils.get_pod_ip(pod) crd_rules.append( _create_sg_rule(sg_id, direction, pod_ip, port=port, namespace=ns_name)) else: for pod in pods: pod_ip = driver_utils.get_pod_ip(pod) matched = True crd_rules.append( _create_sg_rule(sg_id, direction, pod_ip, namespace=ns_name)) else: ns_pods = driver_utils.get_pods(ns_selector) ns_cidr = driver_utils.get_namespace_subnet_cidr(namespace) if 'ports' in rule_block: for port in rule_block['ports']: if type(port.get('port')) is not int: matched = (_create_sg_rule_on_text_port( sg_id, direction, port, ns_pods, crd_rules, matched, crd)) else: matched = True crd_rules.append( _create_sg_rule(sg_id, direction, ns_cidr, port=port, namespace=ns_name)) else: matched = True crd_rules.append( _create_sg_rule(sg_id, direction, ns_cidr, namespace=ns_name)) return matched, crd_rules
def _get_pods(self, pod_selector, namespace=None, namespace_selector=None): matching_pods = {"items": []} if namespace_selector: matching_namespaces = driver_utils.get_namespaces( namespace_selector) for ns in matching_namespaces.get('items'): matching_pods = driver_utils.get_pods(pod_selector, ns['metadata']['name']) else: matching_pods = driver_utils.get_pods(pod_selector, namespace) return matching_pods.get('items')
def _is_service_affected(self, service, affected_pods): svc_namespace = service['metadata']['namespace'] svc_selector = service['spec'].get('selector') svc_pods = driver_utils.get_pods({ 'selector': svc_selector }, svc_namespace).get('items') return any(pod in svc_pods for pod in affected_pods)
def _parse_rules(direction, crd, namespace): policy = crd['spec']['networkpolicy_spec'] sg_id = crd['spec']['securityGroupId'] ns_labels = namespace['metadata'].get('labels') ns_name = namespace['metadata'].get('name') ns_cidr = utils.get_namespace_subnet_cidr(namespace) rule_direction = 'from' crd_rules = crd['spec'].get('ingressSgRules') if direction == 'egress': rule_direction = 'to' crd_rules = crd['spec'].get('egressSgRules') matched = False rule_list = policy.get(direction, []) for rule_block in rule_list: for rule in rule_block.get(rule_direction, []): pod_selector = rule.get('podSelector') ns_selector = rule.get('namespaceSelector') if (ns_selector and ns_labels and utils.match_selector(ns_selector, ns_labels)): if pod_selector: pods = utils.get_pods(pod_selector, ns_name) for pod in pods.get('items'): pod_ip = utils.get_pod_ip(pod) if 'ports' in rule_block: for port in rule_block['ports']: matched = True crd_rules.append( _create_sg_rule(sg_id, direction, pod_ip, port=port, namespace=ns_name)) else: matched = True crd_rules.append( _create_sg_rule(sg_id, direction, pod_ip, namespace=ns_name)) else: if 'ports' in rule_block: for port in rule_block['ports']: matched = True crd_rules.append( _create_sg_rule(sg_id, direction, ns_cidr, port=port, namespace=ns_name)) else: matched = True crd_rules.append( _create_sg_rule(sg_id, direction, ns_cidr, namespace=ns_name)) return matched, crd_rules
def _create_sg_rule_on_text_port(sg_id, direction, port, rule_selected_pods, crd_rules, matched, crd, allow_all=False, namespace=None): matched_pods = {} spec_pod_selector = crd['spec'].get('podSelector') policy_namespace = crd['metadata']['namespace'] spec_pods = driver_utils.get_pods(spec_pod_selector, policy_namespace).get('items') if direction == 'ingress': for spec_pod in spec_pods: container_ports = driver_utils.get_ports(spec_pod, port) for rule_selected_pod in rule_selected_pods: matched = _create_sg_rules_with_container_ports( matched_pods, container_ports, allow_all, namespace, matched, crd_rules, sg_id, direction, port, rule_selected_pod) elif direction == 'egress': for rule_selected_pod in rule_selected_pods: pod_label = rule_selected_pod['metadata'].get('labels') pod_ns = rule_selected_pod['metadata'].get('namespace') # NOTE(maysams) Do not allow egress traffic to the actual # set of pods the NP is enforced on. if (driver_utils.match_selector(spec_pod_selector, pod_label) and policy_namespace == pod_ns): continue container_ports = driver_utils.get_ports(rule_selected_pod, port) matched = _create_sg_rules_with_container_ports( matched_pods, container_ports, allow_all, namespace, matched, crd_rules, sg_id, direction, port, rule_selected_pod) for container_port, pods in matched_pods.items(): if allow_all: sg_rule = driver_utils.create_security_group_rule_body( sg_id, direction, container_port, protocol=port.get('protocol'), pods=pods) else: namespace_obj = driver_utils.get_namespace(namespace) namespace_cidr = driver_utils.get_namespace_subnet_cidr( namespace_obj) sg_rule = driver_utils.create_security_group_rule_body( sg_id, direction, container_port, protocol=port.get('protocol'), cidr=namespace_cidr, pods=pods) sgr_id = driver_utils.create_security_group_rule(sg_rule) sg_rule['security_group_rule']['id'] = sgr_id crd_rules.append(sg_rule) return matched
def _create_sg_rule_body_on_text_port(self, sg_id, direction, port, resources, crd_rules, pod_selector, policy_namespace, allow_all=False): """Create SG rules when named port is used in the NP rule In case of ingress, the pods selected by NetworkPolicySpec's podSelector have its containers checked for ports with same name as the named port. If true, rules are created for the resource matched in the NP rule selector with that port. In case of egress, all the pods selected by the NetworkPolicyEgressRule's selector have its containers checked for containers ports with same name as the ones defined in NP rule, and if true the rule is created. param sg_id: String with the Security Group ID param direction: String with ingress or egress param port: dict containing port and protocol param resources: list of K8S resources(pod/namespace) or a dict with cird param crd_rules: list of parsed SG rules param pod_selector: dict with NetworkPolicySpec's podSelector param policy_namespace: string with policy namespace param allow_all: True if should parse a allow from/to all rule, False otherwise """ matched_pods = {} if direction == "ingress": selected_pods = driver_utils.get_pods( pod_selector, policy_namespace).get('items') for selected_pod in selected_pods: container_ports = driver_utils.get_ports(selected_pod, port) for resource in resources: self._create_sg_rules_with_container_ports( container_ports, allow_all, resource, matched_pods, crd_rules, sg_id, direction, port) elif direction == "egress": for resource in resources: # NOTE(maysams) Skipping objects that refers to ipblocks # and consequently do not contains a spec field if not resource.get('spec'): LOG.warning("IPBlock for egress with named ports is " "not supported.") continue container_ports = driver_utils.get_ports(resource, port) self._create_sg_rules_with_container_ports( container_ports, allow_all, resource, matched_pods, crd_rules, sg_id, direction, port, pod_selector, policy_namespace) if allow_all: for container_port, pods in matched_pods.items(): sg_rule = driver_utils.create_security_group_rule_body( sg_id, direction, container_port, protocol=port.get('protocol'), pods=pods) crd_rules.append(sg_rule) if direction == 'egress': rules = self._create_svc_egress_sg_rule( sg_id, policy_namespace, port=container_port, protocol=port.get('protocol')) crd_rules.extend(rules)
def _parse_selectors_on_namespace(crd, direction, pod_selector, ns_selector, rule_block, namespace, matched): ns_name = namespace['metadata'].get('name') ns_labels = namespace['metadata'].get('labels') if (ns_selector and ns_labels and driver_utils.match_selector(ns_selector, ns_labels)): if pod_selector: pods = driver_utils.get_pods(pod_selector, ns_name).get('items') if 'ports' in rule_block: for port in rule_block['ports']: if type(port.get('port')) is not int: matched = (_create_sg_rule_on_text_port( direction, port, pods, matched, crd)) else: for pod in pods: pod_ip = driver_utils.get_pod_ip(pod) if not pod_ip: pod_name = pod['metadata']['name'] LOG.debug( "Skipping SG rule creation for pod " "%s due to no IP assigned", pod_name) continue matched = True else: for pod in pods: pod_ip = driver_utils.get_pod_ip(pod) if not pod_ip: pod_name = pod['metadata']['name'] LOG.debug( "Skipping SG rule creation for pod %s due" " to no IP assigned", pod_name) continue matched = True else: ns_pods = driver_utils.get_pods(ns_selector)['items'] if 'ports' in rule_block: for port in rule_block['ports']: if type(port.get('port')) is not int: matched = (_create_sg_rule_on_text_port( direction, port, ns_pods, matched, crd)) else: matched = True else: matched = True return matched
def _create_sg_rule_body_on_text_port(self, direction, port, resources, crd_rules, pod_selector, policy_namespace, allowed_cidrs=None): """Create SG rules when named port is used in the NP rule In case of ingress, the pods selected by NetworkPolicySpec's podSelector have its containers checked for ports with same name as the named port. If true, rules are created for the resource matched in the NP rule selector with that port. In case of egress, all the pods selected by the NetworkPolicyEgressRule's selector have its containers checked for containers ports with same name as the ones defined in NP rule, and if true the rule is created. param sg_id: String with the Security Group ID param direction: String with ingress or egress param port: dict containing port and protocol param resources: list of K8S resources(pod/namespace) or a dict with cird param crd_rules: list of parsed SG rules param pod_selector: dict with NetworkPolicySpec's podSelector param policy_namespace: string with policy namespace param allowed_cidrs: None, or a list of cidrs, where/from the traffic should be allowed. """ matched_pods = {} if direction == "ingress": selected_pods = driver_utils.get_pods( pod_selector, policy_namespace).get('items') for selected_pod in selected_pods: container_ports = driver_utils.get_ports(selected_pod, port) for resource in resources: self._create_sg_rules_with_container_ports( container_ports, allowed_cidrs, resource, matched_pods, crd_rules, direction, port) elif direction == "egress": for resource in resources: # NOTE(maysams) Skipping objects that refers to ipblocks # and consequently do not contains a spec field if not resource.get('spec'): LOG.warning("IPBlock for egress with named ports is " "not supported.") continue container_ports = driver_utils.get_ports(resource, port) self._create_sg_rules_with_container_ports( container_ports, allowed_cidrs, resource, matched_pods, crd_rules, direction, port, pod_selector, policy_namespace) if allowed_cidrs: for container_port, pods in matched_pods.items(): for cidr in allowed_cidrs: sg_rule = driver_utils.create_security_group_rule_body( direction, container_port, # Pod's spec.containers[].port.protocol defaults to TCP protocol=port.get('protocol', 'TCP'), cidr=cidr, pods=pods) crd_rules.append(sg_rule)
def _get_pods_ips(self, pod_selector, namespace=None, namespace_selector=None): ips = [] matching_pods = {"items": []} if namespace_selector: matching_namespaces = driver_utils.get_namespaces( namespace_selector) for ns in matching_namespaces.get('items'): matching_pods = driver_utils.get_pods(pod_selector, ns['metadata']['name']) else: matching_pods = driver_utils.get_pods(pod_selector, namespace) for pod in matching_pods.get('items'): if pod['status'].get('podIP'): pod_ip = pod['status']['podIP'] ns = pod['metadata']['namespace'] ips.append({'cidr': pod_ip, 'namespace': ns}) return ips
def affected_pods(self, policy, selector=None): if selector is not None: pod_selector = selector else: pod_selector = policy['spec'].get('podSelector') if pod_selector: policy_namespace = policy['metadata']['namespace'] pods = driver_utils.get_pods(pod_selector, policy_namespace) return pods.get('items') else: # NOTE(ltomasbo): It affects all the pods on the namespace return self.namespaced_pods(policy)
def _create_svc_egress_sg_rule(self, policy_namespace, sg_rule_body_list, resource=None, port=None, protocol=None): services = driver_utils.get_services() if not resource: svc_subnet = utils.get_subnet_cidr( CONF.neutron_defaults.service_subnet) rule = driver_utils.create_security_group_rule_body( 'egress', port, protocol=protocol, cidr=svc_subnet) if rule not in sg_rule_body_list: sg_rule_body_list.append(rule) return for service in services.get('items'): 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 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 else: ns_name = service['metadata']['namespace'] if ns_name != resource['metadata']['name']: continue cluster_ip = service['spec'].get('clusterIP') if not cluster_ip: continue rule = driver_utils.create_security_group_rule_body( 'egress', port, protocol=protocol, cidr=cluster_ip) if rule not in sg_rule_body_list: sg_rule_body_list.append(rule)
def get_security_groups(self, service, project_id): sg_list = [] 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}, svc_namespace).get('items') # NOTE(ltomasbo): We assume all the pods pointed by a service # have the same labels, and the same policy will be applied to # all of them. Hence only considering the security groups applied # to the first one. if pods: return _get_pod_sgs(pods[0], project_id) return sg_list[:]
def _create_sg_rule_on_text_port(direction, port, rule_selected_pods, matched, crd): spec_pod_selector = crd['spec'].get('podSelector') policy_namespace = crd['metadata']['namespace'] spec_pods = driver_utils.get_pods(spec_pod_selector, policy_namespace).get('items') if direction == 'ingress': for spec_pod in spec_pods: container_ports = driver_utils.get_ports(spec_pod, port) matched = _create_sg_rules_with_container_ports( container_ports, matched) elif direction == 'egress': for rule_selected_pod in rule_selected_pods: pod_label = rule_selected_pod['metadata'].get('labels') pod_ns = rule_selected_pod['metadata'].get('namespace') # NOTE(maysams) Do not allow egress traffic to the actual # set of pods the NP is enforced on. if (driver_utils.match_selector(spec_pod_selector, pod_label) and policy_namespace == pod_ns): continue container_ports = driver_utils.get_ports(rule_selected_pod, port) matched = _create_sg_rules_with_container_ports( container_ports, matched) return matched
def get_security_groups(self, service, project_id): sg_list = [] svc_namespace = service['metadata']['namespace'] svc_selector = service['spec'].get('selector') if svc_selector: # get affected pods by svc selector pods = driver_utils.get_pods({ 'selector': svc_selector }, svc_namespace).get('items') # NOTE(ltomasbo): We assume all the pods pointed by a service # have the same labels, and the same policy will be applied to # all of them. Hence only considering the security groups applied # 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[:]
def _parse_selectors_on_namespace(crd, direction, pod_selector, ns_selector, rule_block, crd_rules, namespace, matched): ns_name = namespace['metadata'].get('name') ns_labels = namespace['metadata'].get('labels') sg_id = crd['spec']['securityGroupId'] if (ns_selector and ns_labels and driver_utils.match_selector(ns_selector, ns_labels)): if pod_selector: pods = driver_utils.get_pods(pod_selector, ns_name).get('items') if 'ports' in rule_block: for port in rule_block['ports']: if type(port.get('port')) is not int: matched = (_create_sg_rule_on_text_port( sg_id, direction, port, pods, crd_rules, matched, crd)) else: matched = True for pod in pods: pod_ip = driver_utils.get_pod_ip(pod) if not pod_ip: pod_name = pod['metadata']['name'] LOG.debug( "Skipping SG rule creation for pod " "%s due to no IP assigned", pod_name) continue sg_rule = _create_sg_rule(sg_id, direction, pod_ip, port=port, namespace=ns_name) if sg_rule not in crd_rules: crd_rules.append(sg_rule) else: for pod in pods: pod_ip = driver_utils.get_pod_ip(pod) if not pod_ip: pod_name = pod['metadata']['name'] LOG.debug( "Skipping SG rule creation for pod %s due" " to no IP assigned", pod_name) continue matched = True sg_rule = _create_sg_rule(sg_id, direction, pod_ip, namespace=ns_name) if sg_rule not in crd_rules: crd_rules.append(sg_rule) else: ns_pods = driver_utils.get_pods(ns_selector)['items'] ns_cidr = driver_utils.get_namespace_subnet_cidr(namespace) if 'ports' in rule_block: for port in rule_block['ports']: if type(port.get('port')) is not int: matched = (_create_sg_rule_on_text_port( sg_id, direction, port, ns_pods, crd_rules, matched, crd)) else: matched = True sg_rule = _create_sg_rule(sg_id, direction, ns_cidr, port=port, namespace=ns_name) if sg_rule not in crd_rules: crd_rules.append(sg_rule) else: matched = True sg_rule = _create_sg_rule(sg_id, direction, ns_cidr, namespace=ns_name) if sg_rule not in crd_rules: crd_rules.append(sg_rule) return matched, crd_rules
def _create_svc_egress_sg_rule(self, policy_namespace, sg_rule_body_list, resource=None, port=None, protocol=None): # FIXME(dulek): We could probably filter by namespace here for pods # and namespace resources? services = driver_utils.get_services() if not resource: svc_subnet = utils.get_subnet_cidr( CONF.neutron_defaults.service_subnet) rule = driver_utils.create_security_group_rule_body( 'egress', port, protocol=protocol, cidr=svc_subnet) if rule not in sg_rule_body_list: sg_rule_body_list.append(rule) return for service in services.get('items'): if service['metadata'].get('deletionTimestamp'): # Ignore services being deleted continue cluster_ip = service['spec'].get('clusterIP') if not cluster_ip or cluster_ip == 'None': # Headless services has 'None' as clusterIP, ignore. continue 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: 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_selector = service['spec'].get('selector') 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']: continue rule = driver_utils.create_security_group_rule_body( 'egress', port, protocol=protocol, cidr=cluster_ip) if rule not in sg_rule_body_list: sg_rule_body_list.append(rule)