Firewall as a Service (FWaaS) Iptables Driver
blueprint quantum-fwaas-iptables-driver This is IPTables Driver for "Firewall As A Service" feature. This implements - Fwaas rules are mapped to IPTables - The rules are installed in the network namespace of quantum-routers Change-Id: I157182f2c86fbcf8c141b9ad3cfc71168153ebf8
This commit is contained in:
parent
64c865c51f
commit
a31f29d0e7
@ -29,7 +29,7 @@ LOG = logging.getLogger(__name__)
|
||||
FWaaSOpts = [
|
||||
cfg.StrOpt(
|
||||
'driver',
|
||||
default=('neutron.services.firewall.agents.firewall_agent_api.'
|
||||
default=('neutron.services.firewall.drivers.fwaas_base.'
|
||||
'NoopFwaasDriver'),
|
||||
help=_("Name of the FWaaS Driver")),
|
||||
cfg.BoolOpt(
|
||||
@ -84,25 +84,3 @@ class FWaaSAgentRpcCallbackMixin(object):
|
||||
def delete_firewall(self, context, firewall, host):
|
||||
"""Handle RPC cast from plugin to delete a firewall."""
|
||||
pass
|
||||
|
||||
|
||||
class NoopFwaasDriver(object):
|
||||
"""Noop Fwaas Driver.
|
||||
|
||||
Firewall driver which does nothing.
|
||||
This driver is for disabling the firewall functionality.
|
||||
Put in temporarily until Driver changes are integrated when
|
||||
this will come in from there.
|
||||
"""
|
||||
|
||||
def create_firewall(self, apply_list, firewall):
|
||||
pass
|
||||
|
||||
def delete_firewall(self, apply_list, firewall):
|
||||
pass
|
||||
|
||||
def update_firewall(self, apply_list, firewall):
|
||||
pass
|
||||
|
||||
def apply_default_policy(self, apply_list):
|
||||
pass
|
||||
|
16
neutron/services/firewall/drivers/__init__.py
Normal file
16
neutron/services/firewall/drivers/__init__.py
Normal file
@ -0,0 +1,16 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 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.
|
119
neutron/services/firewall/drivers/fwaas_base.py
Normal file
119
neutron/services/firewall/drivers/fwaas_base.py
Normal file
@ -0,0 +1,119 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
#
|
||||
# Copyright 2013 Dell Inc.
|
||||
# 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.
|
||||
#
|
||||
# @author: Rajesh Mohan, Rajesh_Mohan3@Dell.com, DELL Inc.
|
||||
|
||||
import abc
|
||||
|
||||
|
||||
class FwaasDriverBase(object):
|
||||
"""Firewall as a Service Driver base class.
|
||||
|
||||
Using FwaasDriver Class, an instance of L3 perimeter Firewall
|
||||
can be created. The firewall co-exists with the L3 agent.
|
||||
|
||||
One instance is created for each tenant. One firewall policy
|
||||
is associated with each tenant (in the Havana release).
|
||||
|
||||
The Firewall can be visualized as having two zones (in Havana
|
||||
release), trusted and untrusted.
|
||||
|
||||
All the 'internal' interfaces of Neutron Router is treated as trusted. The
|
||||
interface connected to 'external network' is treated as untrusted.
|
||||
|
||||
The policy is applied on traffic ingressing/egressing interfaces on
|
||||
the trusted zone. This implies that policy will be applied for traffic
|
||||
passing from
|
||||
- trusted to untrusted zones
|
||||
- untrusted to trusted zones
|
||||
- trusted to trusted zones
|
||||
|
||||
Policy WILL NOT be applied for traffic from untrusted to untrusted zones.
|
||||
This is not a problem in Havana release as there is only one interface
|
||||
connected to external network.
|
||||
|
||||
Since the policy is applied on the internal interfaces, the traffic
|
||||
will be not be NATed to floating IP. For incoming traffic, the
|
||||
traffic will get NATed to internal IP address before it hits
|
||||
the firewall rules. So, while writing the rules, care should be
|
||||
taken if using rules based on floating IP.
|
||||
|
||||
The firewall rule addition/deletion/insertion/update are done by the
|
||||
management console. When the policy is sent to the driver, the complete
|
||||
policy is sent and the whole policy has to be applied atomically. The
|
||||
firewall rules will not get updated individually. This is to avoid problems
|
||||
related to out-of-order notifications or inconsistent behaviour by partial
|
||||
application of rules.
|
||||
"""
|
||||
|
||||
__metaclass__ = abc.ABCMeta
|
||||
|
||||
@abc.abstractmethod
|
||||
def create_firewall(self, apply_list, firewall):
|
||||
"""Create the Firewall with default (drop all) policy.
|
||||
|
||||
The default policy will be applied on all the interfaces of
|
||||
trusted zone.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_firewall(self, apply_list, firewall):
|
||||
"""Delete firewall.
|
||||
|
||||
Removes all policies created by this instance and frees up
|
||||
all the resources.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def update_firewall(self, apply_list, firewall):
|
||||
"""Apply the policy on all trusted interfaces.
|
||||
|
||||
Remove previous policy and apply the new policy on all trusted
|
||||
interfaces.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def apply_default_policy(self, apply_list, firewall):
|
||||
"""Apply the default policy on all trusted interfaces.
|
||||
|
||||
Remove current policy and apply the default policy on all trusted
|
||||
interfaces.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class NoopFwaasDriver(FwaasDriverBase):
|
||||
"""Noop Fwaas Driver.
|
||||
|
||||
Firewall driver which does nothing.
|
||||
This driver is for disabling Fwaas functionality.
|
||||
"""
|
||||
|
||||
def create_firewall(self, apply_list, firewall):
|
||||
pass
|
||||
|
||||
def delete_firewall(self, apply_list, firewall):
|
||||
pass
|
||||
|
||||
def update_firewall(self, apply_list, firewall):
|
||||
pass
|
||||
|
||||
def apply_default_policy(self, apply_list, firewall):
|
||||
pass
|
16
neutron/services/firewall/drivers/linux/__init__.py
Normal file
16
neutron/services/firewall/drivers/linux/__init__.py
Normal file
@ -0,0 +1,16 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 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.
|
279
neutron/services/firewall/drivers/linux/iptables_fwaas.py
Normal file
279
neutron/services/firewall/drivers/linux/iptables_fwaas.py
Normal file
@ -0,0 +1,279 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
#
|
||||
# Copyright 2013 Dell Inc.
|
||||
# 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.
|
||||
#
|
||||
# @author: Rajesh Mohan, Rajesh_Mohan3@Dell.com, DELL Inc.
|
||||
|
||||
from neutron.agent.linux import iptables_manager
|
||||
from neutron.extensions import firewall as fw_ext
|
||||
from neutron.openstack.common import log as logging
|
||||
from neutron.services.firewall.drivers import fwaas_base
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
FWAAS_DRIVER_NAME = 'Fwaas iptables driver'
|
||||
FWAAS_CHAIN = 'fwaas'
|
||||
FWAAS_DEFAULT_CHAIN = 'fwaas-default-policy'
|
||||
INGRESS_DIRECTION = 'ingress'
|
||||
EGRESS_DIRECTION = 'egress'
|
||||
CHAIN_NAME_PREFIX = {INGRESS_DIRECTION: 'i',
|
||||
EGRESS_DIRECTION: 'o'}
|
||||
|
||||
""" Firewall rules are applied on internal-interfaces of Neutron router.
|
||||
The packets ingressing tenant's network will be on the output
|
||||
direction on internal-interfaces.
|
||||
"""
|
||||
IPTABLES_DIR = {INGRESS_DIRECTION: '-o',
|
||||
EGRESS_DIRECTION: '-i'}
|
||||
IPV4 = 'ipv4'
|
||||
IPV6 = 'ipv6'
|
||||
IP_VER_TAG = {IPV4: 'v4',
|
||||
IPV6: 'v6'}
|
||||
|
||||
|
||||
class IptablesFwaasDriver(fwaas_base.FwaasDriverBase):
|
||||
"""IPTables driver for Firewall As A Service."""
|
||||
|
||||
def __init__(self):
|
||||
LOG.debug(_("Initializing fwaas iptables driver"))
|
||||
|
||||
def create_firewall(self, apply_list, firewall):
|
||||
LOG.debug(_('Creating firewall %(fw_id)s for tenant %(tid)s)'),
|
||||
{'fw_id': firewall['id'], 'tid': firewall['tenant_id']})
|
||||
try:
|
||||
if firewall['admin_state_up']:
|
||||
self._setup_firewall(apply_list, firewall)
|
||||
else:
|
||||
self.apply_default_policy(apply_list, firewall)
|
||||
except (LookupError, RuntimeError):
|
||||
# catch known library exceptions and raise Fwaas generic exception
|
||||
LOG.exception(_("Failed to create firewall: %s"), firewall['id'])
|
||||
raise fw_ext.FirewallInternalDriverError(driver=FWAAS_DRIVER_NAME)
|
||||
|
||||
def delete_firewall(self, apply_list, firewall):
|
||||
LOG.debug(_('Deleting firewall %(fw_id)s for tenant %(tid)s)'),
|
||||
{'fw_id': firewall['id'], 'tid': firewall['tenant_id']})
|
||||
fwid = firewall['id']
|
||||
try:
|
||||
for router_info in apply_list:
|
||||
ipt_mgr = router_info.iptables_manager
|
||||
self._remove_chains(fwid, ipt_mgr)
|
||||
self._remove_default_chains(ipt_mgr)
|
||||
ipt_mgr.apply()
|
||||
except (LookupError, RuntimeError):
|
||||
# catch known library exceptions and raise Fwaas generic exception
|
||||
LOG.exception(_("Failed to delete firewall: %s"), fwid)
|
||||
raise fw_ext.FirewallInternalDriverError(driver=FWAAS_DRIVER_NAME)
|
||||
|
||||
def update_firewall(self, apply_list, firewall):
|
||||
LOG.debug(_('Updating firewall %(fw_id)s for tenant %(tid)s)'),
|
||||
{'fw_id': firewall['id'], 'tid': firewall['tenant_id']})
|
||||
try:
|
||||
if firewall['admin_state_up']:
|
||||
self._setup_firewall(apply_list, firewall)
|
||||
else:
|
||||
self.apply_default_policy(apply_list, firewall)
|
||||
except (LookupError, RuntimeError):
|
||||
# catch known library exceptions and raise Fwaas generic exception
|
||||
LOG.exception(_("Failed to update firewall: %s"), firewall['id'])
|
||||
raise fw_ext.FirewallInternalDriverError(driver=FWAAS_DRIVER_NAME)
|
||||
|
||||
def apply_default_policy(self, apply_list, firewall):
|
||||
LOG.debug(_('Applying firewall %(fw_id)s for tenant %(tid)s)'),
|
||||
{'fw_id': firewall['id'], 'tid': firewall['tenant_id']})
|
||||
fwid = firewall['id']
|
||||
try:
|
||||
for router_info in apply_list:
|
||||
ipt_mgr = router_info.iptables_manager
|
||||
|
||||
# the following only updates local memory; no hole in FW
|
||||
self._remove_chains(fwid, ipt_mgr)
|
||||
self._remove_default_chains(ipt_mgr)
|
||||
|
||||
# create default 'DROP ALL' policy chain
|
||||
self._add_default_policy_chain_v4v6(ipt_mgr)
|
||||
self._enable_policy_chain(fwid, ipt_mgr)
|
||||
|
||||
# apply the changes
|
||||
ipt_mgr.apply()
|
||||
except (LookupError, RuntimeError):
|
||||
# catch known library exceptions and raise Fwaas generic exception
|
||||
LOG.exception(_("Failed to apply default policy on firewall: %s"),
|
||||
fwid)
|
||||
raise fw_ext.FirewallInternalDriverError(driver=FWAAS_DRIVER_NAME)
|
||||
|
||||
def _setup_firewall(self, apply_list, firewall):
|
||||
fwid = firewall['id']
|
||||
for router_info in apply_list:
|
||||
ipt_mgr = router_info.iptables_manager
|
||||
|
||||
# the following only updates local memory; no hole in FW
|
||||
self._remove_chains(fwid, ipt_mgr)
|
||||
self._remove_default_chains(ipt_mgr)
|
||||
|
||||
# create default 'DROP ALL' policy chain
|
||||
self._add_default_policy_chain_v4v6(ipt_mgr)
|
||||
#create chain based on configured policy
|
||||
self._setup_chains(firewall, ipt_mgr)
|
||||
|
||||
# apply the changes
|
||||
ipt_mgr.apply()
|
||||
|
||||
def _get_chain_name(self, fwid, ver, direction):
|
||||
return '%s%s%s' % (CHAIN_NAME_PREFIX[direction],
|
||||
IP_VER_TAG[ver],
|
||||
fwid)
|
||||
|
||||
def _setup_chains(self, firewall, ipt_mgr):
|
||||
"""Create Fwaas chain using the rules in the policy
|
||||
"""
|
||||
fw_rules_list = firewall['firewall_rule_list']
|
||||
fwid = firewall['id']
|
||||
|
||||
#default rules for invalid packets and established sessions
|
||||
invalid_rule = self._drop_invalid_packets_rule()
|
||||
est_rule = self._allow_established_rule()
|
||||
|
||||
for ver in [IPV4, IPV6]:
|
||||
if ver == IPV4:
|
||||
table = ipt_mgr.ipv4['filter']
|
||||
else:
|
||||
table = ipt_mgr.ipv6['filter']
|
||||
ichain_name = self._get_chain_name(fwid, ver, INGRESS_DIRECTION)
|
||||
ochain_name = self._get_chain_name(fwid, ver, EGRESS_DIRECTION)
|
||||
for name in [ichain_name, ochain_name]:
|
||||
table.add_chain(name)
|
||||
table.add_rule(name, invalid_rule)
|
||||
table.add_rule(name, est_rule)
|
||||
|
||||
for rule in fw_rules_list:
|
||||
if not rule['enabled']:
|
||||
continue
|
||||
iptbl_rule = self._convert_fwaas_to_iptables_rule(rule)
|
||||
if rule['ip_version'] == 4:
|
||||
ver = IPV4
|
||||
table = ipt_mgr.ipv4['filter']
|
||||
else:
|
||||
ver = IPV6
|
||||
table = ipt_mgr.ipv6['filter']
|
||||
ichain_name = self._get_chain_name(fwid, ver, INGRESS_DIRECTION)
|
||||
ochain_name = self._get_chain_name(fwid, ver, EGRESS_DIRECTION)
|
||||
table.add_rule(ichain_name, iptbl_rule)
|
||||
table.add_rule(ochain_name, iptbl_rule)
|
||||
self._enable_policy_chain(fwid, ipt_mgr)
|
||||
|
||||
def _remove_default_chains(self, nsid):
|
||||
"""Remove fwaas default policy chain."""
|
||||
self._remove_chain_by_name(IPV4, FWAAS_DEFAULT_CHAIN, nsid)
|
||||
self._remove_chain_by_name(IPV6, FWAAS_DEFAULT_CHAIN, nsid)
|
||||
|
||||
def _remove_chains(self, fwid, ipt_mgr):
|
||||
"""Remove fwaas policy chain."""
|
||||
for ver in [IPV4, IPV6]:
|
||||
for direction in [INGRESS_DIRECTION, EGRESS_DIRECTION]:
|
||||
chain_name = self._get_chain_name(fwid, ver, direction)
|
||||
self._remove_chain_by_name(ver, chain_name, ipt_mgr)
|
||||
|
||||
def _add_default_policy_chain_v4v6(self, ipt_mgr):
|
||||
ipt_mgr.ipv4['filter'].add_chain(FWAAS_DEFAULT_CHAIN)
|
||||
ipt_mgr.ipv4['filter'].add_rule(FWAAS_DEFAULT_CHAIN, '-j DROP')
|
||||
ipt_mgr.ipv6['filter'].add_chain(FWAAS_DEFAULT_CHAIN)
|
||||
ipt_mgr.ipv6['filter'].add_rule(FWAAS_DEFAULT_CHAIN, '-j DROP')
|
||||
|
||||
def _remove_chain_by_name(self, ver, chain_name, ipt_mgr):
|
||||
if ver == IPV4:
|
||||
ipt_mgr.ipv4['filter'].ensure_remove_chain(chain_name)
|
||||
else:
|
||||
ipt_mgr.ipv6['filter'].ensure_remove_chain(chain_name)
|
||||
|
||||
def _add_rules_to_chain(self, ipt_mgr, ver, chain_name, rules):
|
||||
if ver == IPV4:
|
||||
table = ipt_mgr.ipv4['filter']
|
||||
else:
|
||||
table = ipt_mgr.ipv6['filter']
|
||||
for rule in rules:
|
||||
table.add_rule(chain_name, rule)
|
||||
|
||||
def _enable_policy_chain(self, fwid, ipt_mgr):
|
||||
bname = iptables_manager.binary_name
|
||||
|
||||
for (ver, tbl) in [(IPV4, ipt_mgr.ipv4['filter']),
|
||||
(IPV6, ipt_mgr.ipv4['filter'])]:
|
||||
for direction in [INGRESS_DIRECTION, EGRESS_DIRECTION]:
|
||||
chain_name = self._get_chain_name(fwid, ver, direction)
|
||||
chain_name = iptables_manager.get_chain_name(chain_name)
|
||||
if chain_name in tbl.chains:
|
||||
jump_rule = ['%s qr-+ -j %s-%s' % (IPTABLES_DIR[direction],
|
||||
bname, chain_name)]
|
||||
self._add_rules_to_chain(ipt_mgr, ver, 'FORWARD',
|
||||
jump_rule)
|
||||
|
||||
#jump to DROP_ALL policy
|
||||
chain_name = iptables_manager.get_chain_name(FWAAS_DEFAULT_CHAIN)
|
||||
jump_rule = ['-o qr-+ -j %s-%s' % (bname, chain_name)]
|
||||
self._add_rules_to_chain(ipt_mgr, IPV4, 'FORWARD', jump_rule)
|
||||
self._add_rules_to_chain(ipt_mgr, IPV6, 'FORWARD', jump_rule)
|
||||
|
||||
#jump to DROP_ALL policy
|
||||
chain_name = iptables_manager.get_chain_name(FWAAS_DEFAULT_CHAIN)
|
||||
jump_rule = ['-i qr-+ -j %s-%s' % (bname, chain_name)]
|
||||
self._add_rules_to_chain(ipt_mgr, IPV4, 'FORWARD', jump_rule)
|
||||
self._add_rules_to_chain(ipt_mgr, IPV6, 'FORWARD', jump_rule)
|
||||
|
||||
def _convert_fwaas_to_iptables_rule(self, rule):
|
||||
if rule.get('action') == 'allow':
|
||||
rule['action'] = 'ACCEPT'
|
||||
else:
|
||||
rule['action'] = 'DROP'
|
||||
|
||||
args = [self._protocol_arg(rule.get('protocol')),
|
||||
self._port_arg('dport',
|
||||
rule.get('protocol'),
|
||||
rule.get('destination_port')),
|
||||
self._port_arg('sport',
|
||||
rule.get('protocol'),
|
||||
rule.get('source_port')),
|
||||
self._ip_prefix_arg('s', rule.get('source_ip_address')),
|
||||
self._ip_prefix_arg('d', rule.get('destination_ip_address')),
|
||||
self._action_arg(rule.get('action'))]
|
||||
|
||||
iptables_rule = ' '.join(args)
|
||||
return iptables_rule
|
||||
|
||||
def _drop_invalid_packets_rule(self):
|
||||
return '-m state --state INVALID -j DROP'
|
||||
|
||||
def _allow_established_rule(self):
|
||||
return '-m state --state ESTABLISHED,RELATED -j ACCEPT'
|
||||
|
||||
def _action_arg(self, action):
|
||||
if action:
|
||||
return '-j %s' % action
|
||||
return ''
|
||||
|
||||
def _protocol_arg(self, protocol):
|
||||
if protocol:
|
||||
return '-p %s' % protocol
|
||||
return ''
|
||||
|
||||
def _port_arg(self, direction, protocol, port):
|
||||
if not (protocol in ['udp', 'tcp'] and port):
|
||||
return ''
|
||||
return '--%s %s' % (direction, port)
|
||||
|
||||
def _ip_prefix_arg(self, direction, ip_prefix):
|
||||
if ip_prefix:
|
||||
return '-%s %s' % (direction, ip_prefix)
|
||||
return ''
|
15
neutron/tests/unit/services/firewall/drivers/__init__.py
Normal file
15
neutron/tests/unit/services/firewall/drivers/__init__.py
Normal file
@ -0,0 +1,15 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 OpenStack Foundation
|
||||
#
|
||||
# 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.
|
@ -0,0 +1,15 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 OpenStack Foundation
|
||||
#
|
||||
# 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.
|
@ -0,0 +1,199 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
#
|
||||
# Copyright 2013 Dell Inc.
|
||||
# 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.
|
||||
#
|
||||
# @author: Rajesh Mohan, Rajesh_Mohan3@Dell.com, DELL Inc.
|
||||
|
||||
import mock
|
||||
from mock import call
|
||||
from oslo.config import cfg
|
||||
|
||||
from neutron.agent.common import config as a_cfg
|
||||
import neutron.services.firewall.drivers.linux.iptables_fwaas as fwaas
|
||||
from neutron.tests import base
|
||||
from neutron.tests.unit import test_api_v2
|
||||
|
||||
|
||||
_uuid = test_api_v2._uuid
|
||||
FAKE_SRC_PREFIX = '10.0.0.0/24'
|
||||
FAKE_DST_PREFIX = '20.0.0.0/24'
|
||||
FAKE_PROTOCOL = 'tcp'
|
||||
FAKE_SRC_PORT = 5000
|
||||
FAKE_DST_PORT = 22
|
||||
FAKE_FW_ID = 'fake-fw-uuid'
|
||||
|
||||
|
||||
class IptablesFwaasTestCase(base.BaseTestCase):
|
||||
def setUp(self):
|
||||
super(IptablesFwaasTestCase, self).setUp()
|
||||
cfg.CONF.register_opts(a_cfg.ROOT_HELPER_OPTS, 'AGENT')
|
||||
self.utils_exec_p = mock.patch(
|
||||
'neutron.agent.linux.utils.execute')
|
||||
self.utils_exec = self.utils_exec_p.start()
|
||||
self.addCleanup(self.utils_exec_p.stop)
|
||||
self.iptables_cls_p = mock.patch(
|
||||
'neutron.agent.linux.iptables_manager.IptablesManager')
|
||||
iptables_cls = self.iptables_cls_p.start()
|
||||
self.addCleanup(self.iptables_cls_p.stop)
|
||||
self.iptables_inst = mock.Mock()
|
||||
self.v4filter_inst = mock.Mock()
|
||||
self.v6filter_inst = mock.Mock()
|
||||
self.v4filter_inst.chains = []
|
||||
self.v6filter_inst.chains = []
|
||||
self.iptables_inst.ipv4 = {'filter': self.v4filter_inst}
|
||||
self.iptables_inst.ipv6 = {'filter': self.v6filter_inst}
|
||||
iptables_cls.return_value = self.iptables_inst
|
||||
|
||||
self.router_info_inst = mock.Mock()
|
||||
self.router_info_inst.iptables_manager = self.iptables_inst
|
||||
|
||||
self.firewall = fwaas.IptablesFwaasDriver()
|
||||
|
||||
def _fake_rules_v4(self, fwid):
|
||||
rule_list = []
|
||||
rule1 = {'enabled': True,
|
||||
'action': 'allow',
|
||||
'ip_version': 4,
|
||||
'protocol': 'tcp',
|
||||
'destination_port': '80',
|
||||
'source_ip_address': '10.24.4.2'}
|
||||
rule2 = {'enabled': True,
|
||||
'action': 'deny',
|
||||
'ip_version': 4,
|
||||
'protocol': 'tcp',
|
||||
'destination_port': '22'}
|
||||
ingress_chain = ('iv4%s' % fwid)[:11]
|
||||
self.v4filter_inst.chains.append(ingress_chain)
|
||||
egress_chain = ('ov4%s' % fwid)[:11]
|
||||
self.v4filter_inst.chains.append(egress_chain)
|
||||
rule_list.append(rule1)
|
||||
rule_list.append(rule2)
|
||||
return rule_list
|
||||
|
||||
def _fake_firewall_no_rule(self):
|
||||
rule_list = []
|
||||
fw_inst = {'id': FAKE_FW_ID,
|
||||
'admin_state_up': True,
|
||||
'tenant_id': 'tenant-uuid',
|
||||
'firewall_rule_list': rule_list}
|
||||
return fw_inst
|
||||
|
||||
def _fake_firewall(self, rule_list):
|
||||
fw_inst = {'id': FAKE_FW_ID,
|
||||
'admin_state_up': True,
|
||||
'tenant_id': 'tenant-uuid',
|
||||
'firewall_rule_list': rule_list}
|
||||
return fw_inst
|
||||
|
||||
def _fake_firewall_with_admin_down(self, rule_list):
|
||||
fw_inst = {'id': FAKE_FW_ID,
|
||||
'admin_state_up': False,
|
||||
'tenant_id': 'tenant-uuid',
|
||||
'firewall_rule_list': rule_list}
|
||||
return fw_inst
|
||||
|
||||
def _fake_apply_list(self):
|
||||
apply_list = []
|
||||
apply_list.append(self.router_info_inst)
|
||||
return apply_list
|
||||
|
||||
def _setup_firewall_with_rules(self, func):
|
||||
apply_list = self._fake_apply_list()
|
||||
rule_list = self._fake_rules_v4(FAKE_FW_ID)
|
||||
firewall = self._fake_firewall(rule_list)
|
||||
func(apply_list, firewall)
|
||||
invalid_rule = '-m state --state INVALID -j DROP'
|
||||
est_rule = '-m state --state ESTABLISHED,RELATED -j ACCEPT'
|
||||
rule1 = '-p tcp --dport 80 -s 10.24.4.2 -j ACCEPT'
|
||||
rule2 = '-p tcp --dport 22 -j DROP'
|
||||
ingress_chain = 'iv4%s' % firewall['id']
|
||||
egress_chain = 'ov4%s' % firewall['id']
|
||||
bname = fwaas.iptables_manager.binary_name
|
||||
ipt_mgr_ichain = '%s-%s' % (bname, ingress_chain[:11])
|
||||
ipt_mgr_echain = '%s-%s' % (bname, egress_chain[:11])
|
||||
calls = [call.ensure_remove_chain('iv4fake-fw-uuid'),
|
||||
call.ensure_remove_chain('ov4fake-fw-uuid'),
|
||||
call.ensure_remove_chain('fwaas-default-policy'),
|
||||
call.add_chain('fwaas-default-policy'),
|
||||
call.add_rule('fwaas-default-policy', '-j DROP'),
|
||||
call.add_chain(ingress_chain),
|
||||
call.add_rule(ingress_chain, invalid_rule),
|
||||
call.add_rule(ingress_chain, est_rule),
|
||||
call.add_chain(egress_chain),
|
||||
call.add_rule(egress_chain, invalid_rule),
|
||||
call.add_rule(egress_chain, est_rule),
|
||||
call.add_rule(ingress_chain, rule1),
|
||||
call.add_rule(egress_chain, rule1),
|
||||
call.add_rule(ingress_chain, rule2),
|
||||
call.add_rule(egress_chain, rule2),
|
||||
call.add_rule('FORWARD', '-o qr-+ -j %s' % ipt_mgr_ichain),
|
||||
call.add_rule('FORWARD', '-i qr-+ -j %s' % ipt_mgr_echain),
|
||||
call.add_rule('FORWARD', '-o qr-+ -j %s-fwaas-defau' % bname),
|
||||
call.add_rule('FORWARD', '-i qr-+ -j %s-fwaas-defau' % bname)]
|
||||
self.v4filter_inst.assert_has_calls(calls)
|
||||
|
||||
def test_create_firewall_no_rules(self):
|
||||
apply_list = self._fake_apply_list()
|
||||
firewall = self._fake_firewall_no_rule()
|
||||
self.firewall.create_firewall(apply_list, firewall)
|
||||
invalid_rule = '-m state --state INVALID -j DROP'
|
||||
est_rule = '-m state --state ESTABLISHED,RELATED -j ACCEPT'
|
||||
ingress_chain = ('iv4%s' % firewall['id'])
|
||||
egress_chain = ('ov4%s' % firewall['id'])
|
||||
bname = fwaas.iptables_manager.binary_name
|
||||
calls = [call.ensure_remove_chain('iv4fake-fw-uuid'),
|
||||
call.ensure_remove_chain('ov4fake-fw-uuid'),
|
||||
call.ensure_remove_chain('fwaas-default-policy'),
|
||||
call.add_chain('fwaas-default-policy'),
|
||||
call.add_rule('fwaas-default-policy', '-j DROP'),
|
||||
call.add_chain(ingress_chain),
|
||||
call.add_rule(ingress_chain, invalid_rule),
|
||||
call.add_rule(ingress_chain, est_rule),
|
||||
call.add_chain(egress_chain),
|
||||
call.add_rule(egress_chain, invalid_rule),
|
||||
call.add_rule(egress_chain, est_rule),
|
||||
call.add_rule('FORWARD', '-o qr-+ -j %s-fwaas-defau' % bname),
|
||||
call.add_rule('FORWARD', '-i qr-+ -j %s-fwaas-defau' % bname)]
|
||||
self.v4filter_inst.assert_has_calls(calls)
|
||||
|
||||
def test_create_firewall_with_rules(self):
|
||||
self._setup_firewall_with_rules(self.firewall.create_firewall)
|
||||
|
||||
def test_update_firewall_with_rules(self):
|
||||
self._setup_firewall_with_rules(self.firewall.update_firewall)
|
||||
|
||||
def test_delete_firewall(self):
|
||||
apply_list = self._fake_apply_list()
|
||||
firewall = self._fake_firewall_no_rule()
|
||||
self.firewall.delete_firewall(apply_list, firewall)
|
||||
ingress_chain = 'iv4%s' % firewall['id']
|
||||
egress_chain = 'ov4%s' % firewall['id']
|
||||
calls = [call.ensure_remove_chain(ingress_chain),
|
||||
call.ensure_remove_chain(egress_chain),
|
||||
call.ensure_remove_chain('fwaas-default-policy')]
|
||||
self.v4filter_inst.assert_has_calls(calls)
|
||||
|
||||
def test_create_firewall_with_admin_down(self):
|
||||
rule_list = self._fake_rules_v4(FAKE_FW_ID)
|
||||
apply_list = self._fake_apply_list()
|
||||
firewall = self._fake_firewall_with_admin_down(rule_list)
|
||||
self.firewall.create_firewall(apply_list, firewall)
|
||||
calls = [call.ensure_remove_chain('iv4fake-fw-uuid'),
|
||||
call.ensure_remove_chain('ov4fake-fw-uuid'),
|
||||
call.ensure_remove_chain('fwaas-default-policy'),
|
||||
call.add_chain('fwaas-default-policy'),
|
||||
call.add_rule('fwaas-default-policy', '-j DROP')]
|
||||
self.v4filter_inst.assert_has_calls(calls)
|
Loading…
Reference in New Issue
Block a user