vmware-nsx/vmware_nsx/nsxlib/v3/security.py
Shih-Hao Li d8eeda9baf Move vmware_nsx/neutron/plugins/vmware to vmware_nsx
This is part of new vmware_nsx directory structure proposed in
https://goo.gl/GdWXyH.

Change-Id: I60d6ef62eb724df71dfda90137e00f107e220971
2015-09-14 18:51:57 -07:00

222 lines
8.2 KiB
Python

# Copyright 2015 OpenStack Foundation
# All Rights Reserved
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
NSX-V3 Plugin security integration module
"""
from neutron.db import securitygroups_db
from vmware_nsx.db import nsx_models
from vmware_nsx.nsxlib.v3 import dfw_api as firewall
NSGROUP_CONTAINER = 'NSGroup Container'
DEFAULT_SECTION = 'OS default section for security-groups'
def _get_l4_protocol_name(proto_num):
if proto_num == 6:
return firewall.TCP
elif proto_num == 17:
return firewall.UDP
elif proto_num == 1:
return firewall.ICMPV4
def _decide_service(sg_rule):
ip_proto = securitygroups_db.IP_PROTOCOL_MAP.get(sg_rule['protocol'],
sg_rule['protocol'])
l4_protocol = _get_l4_protocol_name(ip_proto)
if l4_protocol in [firewall.TCP, firewall.UDP]:
# If port_range_min is not specified then we assume all ports are
# matched, relying on neutron to perform validation.
if sg_rule['port_range_min'] is None:
source_ports = []
else:
source_ports = ['%(port_range_min)s-%(port_range_max)s' % sg_rule]
return firewall.get_nsservice(firewall.L4_PORT_SET_NSSERVICE,
l4_protocol=l4_protocol,
source_ports=source_ports)
elif l4_protocol == firewall.ICMPV4:
return firewall.get_nsservice(firewall.ICMP_TYPE_NSSERVICE,
protocol=l4_protocol,
icmp_type=sg_rule['port_range_min'],
icmp_code=sg_rule['port_range_max'])
elif ip_proto is not None:
return firewall.get_nsservice(firewall.IP_PROTOCOL_NSSERVICE,
protocol_number=ip_proto)
def _get_fw_rule_from_sg_rule(sg_rule, nsgroup_id, rmt_nsgroup_id):
# IPV4 or IPV6
ip_protocol = sg_rule['ethertype'].upper()
direction = (
firewall.IN if sg_rule['direction'] == 'ingress' else firewall.OUT)
source = None
local_group = firewall.get_nsgroup_reference(nsgroup_id)
if sg_rule['remote_ip_prefix'] is not None:
source = firewall.get_ip_cidr_reference(sg_rule['remote_ip_prefix'],
ip_protocol)
destination = local_group
else:
if rmt_nsgroup_id:
source = firewall.get_nsgroup_reference(rmt_nsgroup_id)
destination = local_group
if direction == firewall.OUT:
source, destination = destination, source
service = _decide_service(sg_rule)
name = sg_rule['id']
return firewall.get_firewall_rule_dict(name, source,
destination, direction,
ip_protocol, service,
firewall.ALLOW)
def create_firewall_rules(context, section_id, nsgroup_id,
security_group_rules):
# 1. translate rules
# 2. insert in section
# 3. save mappings
firewall_rules = []
for sg_rule in security_group_rules:
remote_nsgroup_id = _get_remote_nsg_mapping(
context, sg_rule, nsgroup_id)
fw_rule = _get_fw_rule_from_sg_rule(
sg_rule, nsgroup_id, remote_nsgroup_id)
firewall_rules.append(
firewall.add_rule_in_section(fw_rule, section_id))
return {'rules': firewall_rules}
def get_nsgroup_name(security_group):
# NOTE(roeyc): We add the security-group id to the NSGroup name,
# for usability purposes.
return '%(name)s - %(id)s' % security_group
def save_sg_rule_mappings(session, firewall_rules):
# REVISIT(roeyc): This method should take care db access only.
rules = [(rule['display_name'], rule['id']) for rule in firewall_rules]
with session.begin(subtransactions=True):
for neutron_id, nsx_id in rules:
mapping = nsx_models.NeutronNsxRuleMapping(
neutron_id=neutron_id, nsx_id=nsx_id)
session.add(mapping)
return mapping
def save_sg_mappings(session, sg_id, nsgroup_id, section_id):
with session.begin(subtransactions=True):
session.add(
nsx_models.NeutronNsxFirewallSectionMapping(neutron_id=sg_id,
nsx_id=section_id))
session.add(
nsx_models.NeutronNsxSecurityGroupMapping(neutron_id=sg_id,
nsx_id=nsgroup_id))
def get_sg_rule_mapping(session, rule_id):
rule_mapping = session.query(nsx_models.NeutronNsxRuleMapping).filter_by(
neutron_id=rule_id).one()
return rule_mapping.nsx_id
def get_sg_mappings(session, sg_id):
nsgroup_mapping = session.query(nsx_models.NeutronNsxSecurityGroupMapping
).filter_by(neutron_id=sg_id).one()
section_mapping = session.query(nsx_models.NeutronNsxFirewallSectionMapping
).filter_by(neutron_id=sg_id).one()
return nsgroup_mapping.nsx_id, section_mapping.nsx_id
def _get_remote_nsg_mapping(context, sg_rule, nsgroup_id):
remote_nsgroup_id = None
remote_group_id = sg_rule.get('remote_group_id')
# skip unnecessary db access when possible
if remote_group_id == sg_rule['security_group_id']:
remote_nsgroup_id = nsgroup_id
elif remote_group_id:
remote_nsgroup_id, _ = get_sg_mappings(context.session,
remote_group_id)
return remote_nsgroup_id
def update_lport_with_security_groups(context, lport_id, original, updated):
added = set(updated) - set(original)
removed = set(original) - set(updated)
for sg_id in added:
nsgroup_id, _ = get_sg_mappings(context.session, sg_id)
firewall.add_nsgroup_member(
nsgroup_id, firewall.LOGICAL_PORT, lport_id)
for sg_id in removed:
nsgroup_id, _ = get_sg_mappings(context.session, sg_id)
firewall.remove_nsgroup_member(
nsgroup_id, lport_id)
def init_nsgroup_container_and_default_section_rules():
# REVISIT(roeyc): Should handle Neutron active-active
# deployment scenario.
nsgroup_description = ('This NSGroup is necessary for OpenStack '
'integration, do not delete.')
section_description = ("This section is handled by OpenStack to contain "
"default rules on security-groups.")
nsgroup_id = _init_nsgroup_container(NSGROUP_CONTAINER,
nsgroup_description)
section_id = _init_default_section(
DEFAULT_SECTION, section_description, nsgroup_id)
return nsgroup_id, section_id
def _init_nsgroup_container(name, description):
nsgroups = firewall.list_nsgroups()
for nsg in nsgroups:
if nsg['display_name'] == name:
# NSGroup container exists and so should the OS default
# security-groups section.
break
else:
# Need to create the nsgroup container and the OS default
# security-groups section.
nsg = firewall.create_nsgroup(name, description, [])
return nsg['id']
def _init_default_section(name, description, nsgroup_id):
fw_sections = firewall.list_sections()
for section in fw_sections:
if section['display_name'] == name:
break
else:
section = firewall.create_empty_section(
name, description, [nsgroup_id], [])
# TODO(roeyc): Add aditional rules to allow IPV6 NDP.
block_rule = firewall.get_firewall_rule_dict(
'Block All', action=firewall.DROP)
firewall.add_rule_in_section(block_rule, section['id'])
return section['id']