neutron-fwaas/neutron_fwaas/services/firewall/drivers/mcafee/ngfw_fwaas.py

344 lines
11 KiB
Python

# Copyright 2015 Intel Corporation.
# Copyright 2015 Isaku Yamahata <isaku.yamahata at intel com>
# <isaku.yamahata at gmail com>
# Copyright 2015 Yalei Wang <yalei.wang at intel com>
#
# 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.
#
import netaddr
from neutron.common import constants
from oslo_config import cfg
from oslo_log import helpers as log_helpers
from oslo_log import log as logging
from oslo_serialization import jsonutils
from neutron_fwaas._i18n import _
from neutron_fwaas.services.firewall.drivers import fwaas_base
from neutron_fwaas.services.firewall.drivers.mcafee import smc_api
NGFWOpts = [
cfg.StrOpt(
'smc_url',
default='',
help=_("URL to contact SMC server")
),
cfg.StrOpt(
'smc_api_auth_key',
default='',
help=_("Authentication key to SMC API")
),
cfg.StrOpt(
'smc_api_version',
default='',
help=_("verion of SMC API")
),
]
cfg.CONF.register_opts(NGFWOpts, 'ngfw')
LOG = logging.getLogger(__name__)
class NgfwFwaasDriver(fwaas_base.FwaasDriverBase):
"""Firewall driver for NGFW Fwaas of Mcafee """
def __init__(self):
LOG.debug("Initializing FWaas Mcafee NGFW driver")
super(NgfwFwaasDriver, self).__init__()
self._host_list = []
self._network_list = []
self._smc_url = cfg.CONF.ngfw.smc_url
self.fw_ips_template_ref = None
self.fw_template_ref = None
self.connection = smc_api.SMCAPIConnection(
self._smc_url,
cfg.CONF.ngfw.smc_api_version,
cfg.CONF.ngfw.smc_api_auth_key)
@log_helpers.log_method_call
def create_firewall(self, agent_mode, apply_list, firewall):
# call update_firewall, because one tenant only support
# one firewall
return self.update_firewall(agent_mode, apply_list, firewall)
@log_helpers.log_method_call
def delete_firewall(self, agent_mode, apply_list, firewall):
# tell SMC server to remove the ngfw policy
return self._delete_policy(apply_list, firewall)
@log_helpers.log_method_call
def update_firewall(self, agent_mode, apply_list, firewall):
for router_info in apply_list:
rt = router_info.router
# only update the policy when the router is active
if (rt['tenant_id'] == firewall['tenant_id'] and
rt['status'] == 'ACTIVE'):
self._update_policy(rt, firewall)
def _delete_policy(self, apply_list, firewall):
for router_info in apply_list:
rt = router_info.router
self._clear_policy(rt, firewall)
@log_helpers.log_method_call
def apply_default_policy(self, apply_list, firewall):
return self._delete_policy(apply_list, firewall)
def _update_policy(self, router, firewall):
# clear all the policy first
self._clear_policy(router, firewall)
if firewall['admin_state_up']:
self._setup_policy(router, firewall)
def _is_ips_policy(self, policy_name):
return policy_name[len(policy_name) - 4:].lower() == '-ips'
def _get_policy_ref(self, policy_name):
# get the template ref at the first time
if not self.fw_ips_template_ref or not self.fw_template_ref:
r = self.connection.get('elements/fw_template_policy')
fw_template_list = r[0]['result']
for tplt in fw_template_list:
if tplt['name'] == "Firewall Inspection Template":
self.fw_ips_template_ref = tplt['href'].replace(
self._smc_url +
"/%s/" % cfg.CONF.ngfw.smc_api_version,
'')
elif tplt['name'] == "Firewall Template":
self.fw_template_ref = tplt['href'].replace(
self._smc_url +
"/%s/" % cfg.CONF.ngfw.smc_api_version,
'')
# use different template base on the policy name
if self._is_ips_policy(policy_name):
template = self.fw_ips_template_ref
else:
template = self.fw_template_ref
# create the policy in SMC server
fw_policy = {
"name": policy_name,
"template": template
}
ref = self._get_ref_from_service_data('fw_policy', fw_policy)
return ref
def _parse_port(self, source_port):
min_port = ''
max_port = ''
if source_port is None:
min_port = 0
max_port = 65535
elif ':' in source_port:
ports = source_port.split(':')
min_port = int(ports[0])
max_port = int(ports[1])
else:
min_port = int(source_port)
max_port = ''
return min_port, max_port
def _get_ref_from_addr(self, addr):
if addr == 'None':
return addr
ip = netaddr.IPNetwork(addr)
if str(ip.netmask) != "255.255.255.255":
# create network objects
ref = self._create_network(addr)
else:
# create host objects
ref = self._create_host(str(ip.ip))
return ref
def _get_ref_from_service_data(self, service_path, service_data):
json_data = jsonutils.dumps(service_data)
r = self.connection.post_element(service_path, json_data)
srv_ref = r.headers['location']
return srv_ref
def _convert_ipv4_to_ngfw_rule(self, rule):
# convert the ipv4 rule into ngfw rules
# create src/dst of hosts or networks
src_ref = self._get_ref_from_addr(str(rule['source_ip_address']))
dst_ref = self._get_ref_from_addr(str(rule['destination_ip_address']))
# create service
srv_ref = ''
service_dict = {}
service = "%s_service" % rule['protocol']
if rule['protocol'] in (constants.PROTO_NAME_TCP,
constants.PROTO_NAME_UDP):
source_port = rule['source_port']
dest_port = rule['destination_port']
min_src_port, max_src_port = self._parse_port(source_port)
min_dst_port, max_dst_port = self._parse_port(dest_port)
service_data = {
"name": "service-%s" % rule['name'],
"min_src_port": min_src_port,
"max_src_port": (min_src_port if max_src_port == ''
else max_src_port),
"min_dst_port": min_dst_port,
"max_dst_port": (min_dst_port if max_dst_port == ''
else max_dst_port)
}
srv_ref = self._get_ref_from_service_data(service,
service_data)
service_dict = {"service": [srv_ref]}
elif rule['protocol'] == constants.PROTO_NAME_ICMP:
# only ping is supported
service_data = {
"name": "service%s" % "22",
"icmp_type": 0,
"icmp_code": 0
}
srv_ref = self._get_ref_from_service_data(service,
service_data)
service_dict = {"service": [srv_ref]}
elif rule['protocol'] is None:
# protocol "ANY" is translated to accept all, no service create
# here
# TODO(yalie): add rules for different protocol, not ignore the
# other value like ports.
service_dict = {"any": True}
else:
raise NotImplementedError(
_("not support %s protocol now") % rule['protocol'])
# create fw rule
action = "discard" if rule["action"] == "deny" else "allow"
payload = {
"name": rule['name'],
"action": {
"action": action,
"connection_tracking_options": {}
},
"destinations": {"dst": [dst_ref]},
"services": service_dict,
"sources": {"src": [src_ref]}
}
json_data = jsonutils.dumps(payload)
return json_data
def _get_policy_name(self, router, fw):
# SMC server would bind the different NGFW policy with different
# routers(sg-engine) in a tenant
return "%s_%s_%s" % (
fw['id'][0:7], fw['firewall_policy_id'][0:7], router['id'][0:7])
def _setup_policy(self, router, fw):
# one tenant should use only one policy
with self.connection.login_server():
# create policy ref
policy_name = self._get_policy_name(router, fw)
policy_ref = self._get_policy_ref(policy_name)
# post service
for rule in fw['firewall_rule_list']:
if not rule['enabled']:
continue
if rule['ip_version'] == 4:
json_data = self._convert_ipv4_to_ngfw_rule(rule)
self.connection.post(policy_ref +
"/fw_ipv4_access_rule",
json_data, raw=True)
else:
msg = (_('Unsupported IP version rule. %(version)') %
{'version': rule['ip_version']})
raise ValueError(msg)
# upload the policy
self.connection.post(policy_ref + "/upload", '', raw=True)
def _clear_policy(self, router, fw):
# find the policy used by the tenant and firewall
policy_name = self._get_policy_name(router, fw)
path_policy_filter = 'elements/fw_policy?filter=%s' % policy_name
with self.connection.login_server():
r = self.connection.get(path=path_policy_filter)
fw_list = r[0]['result']
for f in fw_list:
if f['name'] == policy_name:
self.connection.delete(f['href'], raw=True)
# Warning, find unused elements and delete them.
r = self.connection.get(path='elements/search_unused')
element_list = r[0]['result']
for element in element_list:
self.connection.delete(element['href'], raw=True)
def _create_host(self, ip):
ref = None
host_json_def = {
"name": "host-%s" % str(ip),
"address": ip
}
with self.connection.login_server():
ref = self._get_ref_from_service_data('host', host_json_def)
return ref
def _create_network(self, cidr):
ref = None
net_json_def = {
"name": "network-%s" % str(cidr),
"ipv4_network": cidr
}
with self.connection.login_server():
# some network maybe pre-created by router-plugin
r = self.connection.get("elements/network")
networks = r[0]['result']
for net in networks:
if net['name'] == "network-%s" % cidr:
ref = net['href']
return ref
ref = self._get_ref_from_service_data('network', net_json_def)
return ref