380 lines
16 KiB
Python
380 lines
16 KiB
Python
# Copyright (c) 2015 Midokura SARL
|
|
# 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 testscenarios
|
|
|
|
from tempest.common import utils
|
|
from tempest import config
|
|
from tempest.lib import decorators
|
|
|
|
from neutron_fwaas.tests.tempest_plugin.tests.scenario import base
|
|
|
|
CONF = config.CONF
|
|
|
|
load_tests = testscenarios.load_tests_apply_scenarios
|
|
|
|
|
|
class TestFWaaS(base.FWaaSScenarioTest):
|
|
scenarios = [
|
|
('without router insersion', {
|
|
'router_insertion': False,
|
|
}),
|
|
('with router insersion', {
|
|
'router_insertion': True,
|
|
}),
|
|
]
|
|
|
|
def setUp(self):
|
|
super(TestFWaaS, self).setUp()
|
|
required_exts = ['fwaas', 'security-group', 'router']
|
|
if self.router_insertion:
|
|
required_exts.append('fwaasrouterinsertion')
|
|
for ext in required_exts:
|
|
if not utils.is_extension_enabled(ext, 'network'):
|
|
msg = "%s Extension not enabled." % ext
|
|
raise self.skipException(msg)
|
|
self._router_ids = None
|
|
|
|
def _create_server(self, network, security_group=None):
|
|
keys = self.create_keypair()
|
|
kwargs = {}
|
|
if security_group is not None:
|
|
kwargs['security_groups'] = [{'name': security_group['name']}]
|
|
server = self.create_server(
|
|
key_name=keys['name'],
|
|
networks=[{'uuid': network['id']}],
|
|
wait_until='ACTIVE',
|
|
**kwargs)
|
|
return server, keys
|
|
|
|
def _create_firewall(self, **kwargs):
|
|
if self._router_ids is not None:
|
|
kwargs['router_ids'] = self._router_ids
|
|
return self.create_firewall(**kwargs)
|
|
|
|
def _empty_policy(self, **kwargs):
|
|
# NOTE(yamamoto): an empty policy would deny all
|
|
fw_policy = self.create_firewall_policy(firewall_rules=[])
|
|
fw = self._create_firewall(firewall_policy_id=fw_policy['id'])
|
|
self._wait_firewall_ready(fw['id'])
|
|
return {
|
|
'fw': fw,
|
|
'fw_policy': fw_policy,
|
|
}
|
|
|
|
def _all_disabled_rules(self, **kwargs):
|
|
# NOTE(yamamoto): a policy whose rules are all disabled would deny all
|
|
fw_rule = self.create_firewall_rule(action="allow", enabled=False)
|
|
fw_policy = self.create_firewall_policy(firewall_rules=[fw_rule['id']])
|
|
fw = self._create_firewall(firewall_policy_id=fw_policy['id'])
|
|
self._wait_firewall_ready(fw['id'])
|
|
return {
|
|
'fw': fw,
|
|
'fw_policy': fw_policy,
|
|
'fw_rule': fw_rule,
|
|
}
|
|
|
|
def _block_ip(self, server1_fixed_ip, server2_fixed_ip, **kwargs):
|
|
rules = [
|
|
# NOTE(yamamoto): The filtering is taken place after
|
|
# destination ip is rewritten to fixed-ip.
|
|
self.create_firewall_rule(destination_ip_address=server1_fixed_ip,
|
|
action="deny"),
|
|
self.create_firewall_rule(destination_ip_address=server2_fixed_ip,
|
|
action="deny"),
|
|
self.create_firewall_rule(action="allow"),
|
|
]
|
|
rule_ids = [r['id'] for r in rules]
|
|
fw_policy = self.create_firewall_policy(firewall_rules=rule_ids)
|
|
fw = self._create_firewall(firewall_policy_id=fw_policy['id'])
|
|
self._wait_firewall_ready(fw['id'])
|
|
return {
|
|
'fw': fw,
|
|
'fw_policy': fw_policy,
|
|
'server1_fixed_ip': server1_fixed_ip,
|
|
'server2_fixed_ip': server2_fixed_ip,
|
|
}
|
|
|
|
def _block_icmp(self, **kwargs):
|
|
fw_rule = self.create_firewall_rule(
|
|
protocol="icmp",
|
|
action="deny")
|
|
fw_rule_allow = self.create_firewall_rule(
|
|
action="allow")
|
|
fw_policy = self.create_firewall_policy(
|
|
firewall_rules=[fw_rule['id'], fw_rule_allow['id']])
|
|
fw = self._create_firewall(firewall_policy_id=fw_policy['id'])
|
|
self._wait_firewall_ready(fw['id'])
|
|
return {
|
|
'fw': fw,
|
|
'fw_policy': fw_policy,
|
|
'fw_rule': fw_rule,
|
|
}
|
|
|
|
def _block_all_with_default_allow(self, **kwargs):
|
|
fw_rule = self.create_firewall_rule(
|
|
action="deny")
|
|
fw_rule_allow = self.create_firewall_rule(
|
|
action="allow")
|
|
fw_policy = self.create_firewall_policy(
|
|
firewall_rules=[fw_rule['id'], fw_rule_allow['id']])
|
|
fw = self._create_firewall(firewall_policy_id=fw_policy['id'])
|
|
self._wait_firewall_ready(fw['id'])
|
|
return {
|
|
'fw': fw,
|
|
'fw_policy': fw_policy,
|
|
'fw_rule': fw_rule,
|
|
}
|
|
|
|
def _admin_disable(self, **kwargs):
|
|
# NOTE(yamamoto): A firewall with admin_state_up=False would block all
|
|
fw_rule = self.create_firewall_rule(action="allow")
|
|
fw_policy = self.create_firewall_policy(firewall_rules=[fw_rule['id']])
|
|
fw = self._create_firewall(firewall_policy_id=fw_policy['id'],
|
|
admin_state_up=False)
|
|
self._wait_firewall_ready(fw['id'])
|
|
return {
|
|
'fw': fw,
|
|
'fw_policy': fw_policy,
|
|
'fw_rule': fw_rule,
|
|
}
|
|
|
|
def _allow_ssh_and_icmp(self, ctx):
|
|
fw_ssh_rule = self.create_firewall_rule(
|
|
protocol="tcp",
|
|
destination_port=22,
|
|
action="allow")
|
|
fw_icmp_rule = self.create_firewall_rule(
|
|
protocol="icmp",
|
|
action="allow")
|
|
for rule in [fw_ssh_rule, fw_icmp_rule]:
|
|
self.firewall_policies_client.insert_firewall_rule_in_policy(
|
|
firewall_policy_id=ctx['fw_policy']['id'],
|
|
firewall_rule_id=rule['id'],
|
|
insert_before=ctx['fw_rule']['id'])
|
|
self.addCleanup(
|
|
self._remove_rule_and_wait,
|
|
firewall_id=ctx['fw']['id'],
|
|
firewall_policy_id=ctx['fw_policy']['id'],
|
|
firewall_rule_id=rule['id'])
|
|
self._wait_firewall_ready(ctx['fw']['id'])
|
|
|
|
def _remove_rule_and_wait(self, firewall_id, firewall_policy_id,
|
|
firewall_rule_id):
|
|
self.firewall_policies_client.remove_firewall_rule_from_policy(
|
|
firewall_policy_id=firewall_policy_id,
|
|
firewall_rule_id=firewall_rule_id)
|
|
self._wait_firewall_ready(firewall_id)
|
|
|
|
def _delete_fw(self, ctx):
|
|
self.delete_firewall_and_wait(ctx['fw']['id'])
|
|
|
|
def _set_admin_up(self, firewall_id, up):
|
|
self.firewalls_client.update_firewall(firewall_id=firewall_id,
|
|
admin_state_up=up)
|
|
self._wait_firewall_ready(firewall_id=firewall_id)
|
|
|
|
def _admin_enable(self, ctx):
|
|
self._set_admin_up(ctx['fw']['id'], up=True)
|
|
|
|
def _remove_rule(self, ctx):
|
|
self._remove_rule_and_wait(
|
|
firewall_id=ctx['fw']['id'],
|
|
firewall_policy_id=ctx['fw_policy']['id'],
|
|
firewall_rule_id=ctx['fw_rule']['id'])
|
|
|
|
def _disable_rule(self, ctx):
|
|
self.firewall_rules_client.update_firewall_rule(
|
|
firewall_rule_id=ctx['fw_rule']['id'],
|
|
enabled=False)
|
|
self._wait_firewall_ready(ctx['fw']['id'])
|
|
|
|
def _allow_ip(self, ctx):
|
|
self._delete_fw(ctx)
|
|
server1_fixed_ip = ctx['server1_fixed_ip']
|
|
server2_fixed_ip = ctx['server2_fixed_ip']
|
|
rules = [
|
|
# NOTE(yamamoto): The filtering is taken place after
|
|
# destination ip is rewritten to fixed-ip.
|
|
# The return traffic should be allowed regardless
|
|
# of firewall rules.
|
|
self.create_firewall_rule(
|
|
destination_ip_address=server1_fixed_ip,
|
|
action="allow"),
|
|
self.create_firewall_rule(
|
|
destination_ip_address=server2_fixed_ip,
|
|
action="allow"),
|
|
]
|
|
rule_ids = [r['id'] for r in rules]
|
|
fw_policy = self.create_firewall_policy(firewall_rules=rule_ids)
|
|
fw = self._create_firewall(firewall_policy_id=fw_policy['id'])
|
|
self._wait_firewall_ready(fw['id'])
|
|
|
|
def _confirm_allowed(self, **kwargs):
|
|
self.check_connectivity(check_reverse_icmp_ip=self._public_gateway_ip,
|
|
**kwargs)
|
|
|
|
def _confirm_allowed_oneway(self, **kwargs):
|
|
# Can ping and ssh, but can't ping back to the public gateway.
|
|
# Same as _confirm_allowed if _public_gateway_ip is None.
|
|
self.check_connectivity(check_reverse_icmp_ip=self._public_gateway_ip,
|
|
should_reverse_connect=False, **kwargs)
|
|
|
|
def _confirm_blocked(self, **kwargs):
|
|
self.check_connectivity(should_connect=False, **kwargs)
|
|
|
|
def _confirm_icmp_blocked_but_tcp(self, **kwargs):
|
|
self.check_connectivity(should_connect=False, check_ssh=False,
|
|
**kwargs)
|
|
self.check_connectivity(check_icmp=False, **kwargs)
|
|
|
|
def _create_topology(self):
|
|
"""Create a topology for testing
|
|
|
|
+--------+ +-----------+
|
|
|"server"| | "subnet" |
|
|
| VM +-------------+ "network" |
|
|
+--------+ +----+------+
|
|
|
|
|
| router interface port
|
|
+----+-----+
|
|
| "router" |
|
|
+----+-----+
|
|
| router gateway port
|
|
|
|
|
|
|
|
+----+------------------+
|
|
| existing network |
|
|
| ("public_network_id") |
|
|
+-----------------------+
|
|
"""
|
|
public_network_id = CONF.network.public_network_id
|
|
network, subnet, router = self.create_networks()
|
|
security_group = self._create_security_group()
|
|
server, keys = self._create_server(network,
|
|
security_group=security_group)
|
|
private_key = keys['private_key']
|
|
server_floating_ip = self.create_floating_ip(server, public_network_id)
|
|
fixed_ip = list(server['addresses'].values())[0][0]['addr']
|
|
floating_ip = server_floating_ip['floating_ip_address']
|
|
return fixed_ip, floating_ip, private_key, router
|
|
|
|
def _get_public_gateway_ip(self):
|
|
self._public_gateway_ip = None
|
|
router = self._get_router()
|
|
ext_gw_info = router['external_gateway_info']
|
|
ext_fixed_ips = ext_gw_info['external_fixed_ips']
|
|
for ip in ext_fixed_ips:
|
|
subnet_id = ip['subnet_id']
|
|
res = self.os_admin.subnets_client.show_subnet(subnet_id)
|
|
subnet = res['subnet']
|
|
# REVISIT(yamamoto): IPv4 assumption
|
|
if subnet['ip_version'] == 4:
|
|
self._public_gateway_ip = subnet['gateway_ip']
|
|
return
|
|
|
|
def _test_firewall_basic(self, block, allow=None,
|
|
confirm_allowed=None, confirm_blocked=None):
|
|
if allow is None:
|
|
allow = self._delete_fw
|
|
if confirm_allowed is None:
|
|
confirm_allowed = self._confirm_allowed
|
|
if confirm_blocked is None:
|
|
confirm_blocked = self._confirm_blocked
|
|
ssh_login = CONF.validation.image_ssh_user
|
|
|
|
if self.router_insertion and CONF.network.public_router_id:
|
|
# NOTE(yamamoto): If public_router_id is configured
|
|
# router1 and router2 will be the same router.
|
|
msg = "This test assumes no public_router_id configured"
|
|
raise self.skipException(msg)
|
|
|
|
server1_fixed_ip, server1_floating_ip, private_key1, router1 = \
|
|
self._create_topology()
|
|
server2_fixed_ip, server2_floating_ip, private_key2, router2 = \
|
|
self._create_topology()
|
|
self._get_public_gateway_ip()
|
|
if self.router_insertion:
|
|
# Specify the router when creating a firewall and ensures that
|
|
# the other router (router2) is not affected by the firewall
|
|
self._router_ids = [router1['id']]
|
|
confirm_allowed2 = self.check_connectivity
|
|
confirm_blocked2 = self.check_connectivity
|
|
else:
|
|
# Without router insertion, all routers should be affected
|
|
# equally
|
|
confirm_allowed2 = confirm_allowed
|
|
confirm_blocked2 = confirm_blocked
|
|
self.check_connectivity(ip_address=server1_floating_ip,
|
|
username=ssh_login,
|
|
private_key=private_key1)
|
|
self.check_connectivity(ip_address=server2_floating_ip,
|
|
username=ssh_login,
|
|
private_key=private_key2)
|
|
ctx = block(server1_fixed_ip=server1_fixed_ip,
|
|
server1_floating_ip=server1_floating_ip,
|
|
server2_fixed_ip=server2_fixed_ip,
|
|
server2_floating_ip=server2_floating_ip)
|
|
confirm_blocked(ip_address=server1_floating_ip, username=ssh_login,
|
|
private_key=private_key1)
|
|
confirm_blocked2(ip_address=server2_floating_ip, username=ssh_login,
|
|
private_key=private_key2)
|
|
allow(ctx)
|
|
confirm_allowed(ip_address=server1_floating_ip, username=ssh_login,
|
|
private_key=private_key1)
|
|
confirm_allowed2(ip_address=server2_floating_ip, username=ssh_login,
|
|
private_key=private_key2)
|
|
|
|
@decorators.idempotent_id('f970f6b3-6541-47ac-a9ea-f769be1e21a8')
|
|
def test_firewall_block_ip(self):
|
|
self._test_firewall_basic(block=self._block_ip, allow=self._allow_ip,
|
|
confirm_allowed=self._confirm_allowed_oneway)
|
|
|
|
@decorators.idempotent_id('b985d010-994a-4055-bd5c-9e961464ccde')
|
|
def test_firewall_block_icmp(self):
|
|
self._test_firewall_basic(
|
|
block=self._block_icmp,
|
|
confirm_blocked=self._confirm_icmp_blocked_but_tcp)
|
|
|
|
@decorators.idempotent_id('ca473af0-26f9-4fad-9550-1c34371c900e')
|
|
def test_firewall_insert_rule(self):
|
|
self._test_firewall_basic(
|
|
block=self._block_icmp,
|
|
allow=self._allow_ssh_and_icmp,
|
|
confirm_blocked=self._confirm_icmp_blocked_but_tcp)
|
|
|
|
@decorators.idempotent_id('54a937a6-cecf-444c-b3f9-b67a1c1b7411')
|
|
def test_firewall_remove_rule(self):
|
|
self._test_firewall_basic(block=self._block_all_with_default_allow,
|
|
allow=self._remove_rule)
|
|
|
|
@decorators.idempotent_id('12a18776-9b60-4479-9988-f45971c96a92')
|
|
def test_firewall_disable_rule(self):
|
|
self._test_firewall_basic(block=self._block_all_with_default_allow,
|
|
allow=self._disable_rule)
|
|
|
|
@decorators.idempotent_id('a2a58c1f-49ad-4b5f-9463-e746b9efe08a')
|
|
def test_firewall_empty_policy(self):
|
|
self._test_firewall_basic(block=self._empty_policy)
|
|
|
|
@decorators.idempotent_id('477a47e0-5156-4784-9417-f77970d85c36')
|
|
def test_firewall_all_disabled_rules(self):
|
|
self._test_firewall_basic(block=self._all_disabled_rules)
|
|
|
|
@decorators.idempotent_id('a83f51c5-1a18-4d2a-a778-c368e4d95c29')
|
|
def test_firewall_admin_disable(self):
|
|
self._test_firewall_basic(block=self._admin_disable,
|
|
allow=self._admin_enable)
|