Kubernetes integration with OpenStack networking
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

network_policy_security_groups.py 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. # Copyright 2018 Red Hat, Inc.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. from kuryr_kubernetes import clients
  15. from kuryr_kubernetes import config
  16. from kuryr_kubernetes import constants
  17. from kuryr_kubernetes.controller.drivers import base
  18. from kuryr_kubernetes.controller.drivers import utils as driver_utils
  19. from kuryr_kubernetes import exceptions
  20. from oslo_config import cfg
  21. from oslo_log import log as logging
  22. LOG = logging.getLogger(__name__)
  23. def _get_namespace_labels(namespace):
  24. kubernetes = clients.get_kubernetes_client()
  25. try:
  26. path = '{}/{}'.format(
  27. constants.K8S_API_NAMESPACES, namespace)
  28. LOG.debug("K8s API Query %s", path)
  29. namespaces = kubernetes.get(path)
  30. LOG.debug("Return Namespace: %s", namespaces)
  31. except exceptions.K8sResourceNotFound:
  32. LOG.exception("Namespace not found")
  33. raise
  34. except exceptions.K8sClientException:
  35. LOG.exception("Kubernetes Client Exception")
  36. raise
  37. return namespaces['metadata'].get('labels')
  38. def _create_sg_rules(crd, pod, pod_selector, rule_block, crd_rules,
  39. direction, matched, namespace=None):
  40. pod_labels = pod['metadata'].get('labels')
  41. # NOTE (maysams) No need to differentiate between podSelector
  42. # with empty value or with '{}', as they have same result in here.
  43. if (pod_selector and
  44. driver_utils.match_selector(pod_selector, pod_labels)):
  45. matched = True
  46. pod_ip = driver_utils.get_pod_ip(pod)
  47. sg_id = crd['spec']['securityGroupId']
  48. if 'ports' in rule_block:
  49. for port in rule_block['ports']:
  50. sg_rule = driver_utils.create_security_group_rule_body(
  51. sg_id, direction, port.get('port'),
  52. protocol=port.get('protocol'), cidr=pod_ip,
  53. namespace=namespace)
  54. sgr_id = driver_utils.create_security_group_rule(sg_rule)
  55. sg_rule['security_group_rule']['id'] = sgr_id
  56. crd_rules.append(sg_rule)
  57. else:
  58. sg_rule = driver_utils.create_security_group_rule_body(
  59. sg_id, direction,
  60. port_range_min=1,
  61. port_range_max=65535,
  62. cidr=pod_ip,
  63. namespace=namespace)
  64. sgr_id = driver_utils.create_security_group_rule(sg_rule)
  65. sg_rule['security_group_rule']['id'] = sgr_id
  66. crd_rules.append(sg_rule)
  67. return matched
  68. def _parse_rules(direction, crd, pod):
  69. policy = crd['spec']['networkpolicy_spec']
  70. pod_namespace = pod['metadata']['namespace']
  71. pod_namespace_labels = _get_namespace_labels(pod_namespace)
  72. policy_namespace = crd['metadata']['namespace']
  73. rule_direction = 'from'
  74. crd_rules = crd['spec'].get('ingressSgRules')
  75. if direction == 'egress':
  76. rule_direction = 'to'
  77. crd_rules = crd['spec'].get('egressSgRules')
  78. matched = False
  79. rule_list = policy.get(direction, None)
  80. for rule_block in rule_list:
  81. for rule in rule_block.get(rule_direction, []):
  82. namespace_selector = rule.get('namespaceSelector')
  83. pod_selector = rule.get('podSelector')
  84. if namespace_selector == {}:
  85. if _create_sg_rules(crd, pod, pod_selector, rule_block,
  86. crd_rules, direction, matched):
  87. matched = True
  88. elif namespace_selector:
  89. if (pod_namespace_labels and
  90. driver_utils.match_selector(namespace_selector,
  91. pod_namespace_labels)):
  92. if _create_sg_rules(crd, pod, pod_selector, rule_block,
  93. crd_rules, direction, matched,
  94. pod_namespace):
  95. matched = True
  96. else:
  97. if pod_namespace == policy_namespace:
  98. if _create_sg_rules(crd, pod, pod_selector, rule_block,
  99. crd_rules, direction, matched,
  100. pod_namespace):
  101. matched = True
  102. return matched, crd_rules
  103. def _get_pod_sgs(pod, project_id):
  104. sg_list = []
  105. pod_labels = pod['metadata'].get('labels')
  106. pod_namespace = pod['metadata']['namespace']
  107. knp_crds = driver_utils.get_kuryrnetpolicy_crds(
  108. namespace=pod_namespace)
  109. for crd in knp_crds.get('items'):
  110. pod_selector = crd['spec'].get('podSelector')
  111. if pod_selector:
  112. if driver_utils.match_selector(pod_selector, pod_labels):
  113. LOG.debug("Appending %s",
  114. str(crd['spec']['securityGroupId']))
  115. sg_list.append(str(crd['spec']['securityGroupId']))
  116. else:
  117. LOG.debug("Appending %s", str(crd['spec']['securityGroupId']))
  118. sg_list.append(str(crd['spec']['securityGroupId']))
  119. # NOTE(maysams) Pods that are not selected by any Networkpolicy
  120. # are fully accessible. Thus, the default security group is associated.
  121. if not sg_list:
  122. sg_list = config.CONF.neutron_defaults.pod_security_groups
  123. if not sg_list:
  124. raise cfg.RequiredOptError('pod_security_groups',
  125. cfg.OptGroup('neutron_defaults'))
  126. return sg_list[:]
  127. class NetworkPolicySecurityGroupsDriver(base.PodSecurityGroupsDriver):
  128. """Provides security groups for pods based on network policies"""
  129. def get_security_groups(self, pod, project_id):
  130. return _get_pod_sgs(pod, project_id)
  131. def create_sg_rules(self, pod):
  132. LOG.debug("Creating sg rule for pod: %s", pod['metadata']['name'])
  133. knp_crds = driver_utils.get_kuryrnetpolicy_crds()
  134. for crd in knp_crds.get('items'):
  135. crd_selector = crd['spec'].get('podSelector')
  136. i_matched, i_rules = _parse_rules('ingress', crd, pod)
  137. e_matched, e_rules = _parse_rules('egress', crd, pod)
  138. if i_matched or e_matched:
  139. driver_utils.patch_kuryr_crd(crd, i_rules,
  140. e_rules, crd_selector)
  141. def delete_sg_rules(self, pod):
  142. LOG.debug("Deleting sg rule for pod: %s", pod['metadata']['name'])
  143. pod_ip = driver_utils.get_pod_ip(pod)
  144. knp_crds = driver_utils.get_kuryrnetpolicy_crds()
  145. for crd in knp_crds.get('items'):
  146. crd_selector = crd['spec'].get('podSelector')
  147. ingress_rule_list = crd['spec'].get('ingressSgRules')
  148. egress_rule_list = crd['spec'].get('egressSgRules')
  149. i_rules = []
  150. e_rules = []
  151. matched = False
  152. for i_rule in ingress_rule_list:
  153. LOG.debug("Parsing ingress rule: %r", i_rule)
  154. remote_ip_prefix = i_rule['security_group_rule'].get(
  155. 'remote_ip_prefix')
  156. if remote_ip_prefix and remote_ip_prefix == pod_ip:
  157. matched = True
  158. driver_utils.delete_security_group_rule(
  159. i_rule['security_group_rule']['id'])
  160. else:
  161. i_rules.append(i_rule)
  162. for e_rule in egress_rule_list:
  163. LOG.debug("Parsing egress rule: %r", e_rule)
  164. remote_ip_prefix = e_rule['security_group_rule'].get(
  165. 'remote_ip_prefix')
  166. if remote_ip_prefix and remote_ip_prefix == pod_ip:
  167. matched = True
  168. driver_utils.delete_security_group_rule(
  169. e_rule['security_group_rule']['id'])
  170. else:
  171. e_rules.append(e_rule)
  172. if matched:
  173. driver_utils.patch_kuryr_crd(crd, i_rules, e_rules,
  174. crd_selector)
  175. def update_sg_rules(self, pod):
  176. LOG.debug("Updating sg rule for pod: %s", pod['metadata']['name'])
  177. self.delete_sg_rules(pod)
  178. self.create_sg_rules(pod)
  179. def create_namespace_sg(self, namespace, project_id, crd_spec):
  180. LOG.debug("Security group driver does not create SGs for the "
  181. "namespaces.")
  182. return {}
  183. def delete_sg(self, sg_id):
  184. LOG.debug("Security group driver does not implement deleting "
  185. "SGs.")
  186. def delete_namespace_sg_rules(self, namespace):
  187. LOG.debug("Security group driver does not delete SG rules for "
  188. "namespace.")
  189. def create_namespace_sg_rules(self, namespace):
  190. LOG.debug("Security group driver does not create SG rules for "
  191. "namespace.")
  192. def update_namespace_sg_rules(self, namespace):
  193. LOG.debug("Security group driver does not update SG rules for "
  194. "namespace.")
  195. class NetworkPolicyServiceSecurityGroupsDriver(
  196. base.ServiceSecurityGroupsDriver):
  197. """Provides security groups for services based on network policies"""
  198. def get_security_groups(self, service, project_id):
  199. sg_list = []
  200. svc_namespace = service['metadata']['namespace']
  201. svc_selector = service['spec'].get('selector')
  202. # skip is no selector
  203. if svc_selector:
  204. # get affected pods by svc selector
  205. pods = driver_utils.get_pods({'selector': svc_selector},
  206. svc_namespace).get('items')
  207. # NOTE(ltomasbo): We assume all the pods pointed by a service
  208. # have the same labels, and the same policy will be applied to
  209. # all of them. Hence only considering the security groups applied
  210. # to the first one.
  211. if pods:
  212. return _get_pod_sgs(pods[0], project_id)
  213. return sg_list[:]