blob: b9ee0408d5e8b312299cafef192e8d3a404e8d47 [file] [log] [blame]
Yair Fried4d7efa62013-11-17 17:12:29 +02001# Copyright 2013 Red Hat, Inc.
2# All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
15
Andrea Frittolif9cde7e2014-02-18 09:57:04 +000016from tempest import clients
Yair Fried4d7efa62013-11-17 17:12:29 +020017from tempest.common import debug
18from tempest.common.utils import data_utils
Matthew Treinish6c072292014-01-29 19:15:52 +000019from tempest import config
Yair Fried4d7efa62013-11-17 17:12:29 +020020from tempest.openstack.common import log as logging
21from tempest.scenario import manager
Masayuki Igawa4ded9f02014-02-17 15:05:59 +090022from tempest import test
Yair Fried4d7efa62013-11-17 17:12:29 +020023
Matthew Treinish6c072292014-01-29 19:15:52 +000024CONF = config.CONF
25
Yair Fried4d7efa62013-11-17 17:12:29 +020026LOG = logging.getLogger(__name__)
27
28
Yair Friedbf2e2c42014-01-28 12:06:38 +020029class TestSecurityGroupsBasicOps(manager.NetworkScenarioTest):
Yair Fried4d7efa62013-11-17 17:12:29 +020030
31 """
32 This test suite assumes that Nova has been configured to
33 boot VM's with Neutron-managed networking, and attempts to
34 verify cross tenant connectivity as follows
35
36 ssh:
37 in order to overcome "ip namespace", each tenant has an "access point"
38 VM with floating-ip open to incoming ssh connection allowing network
39 commands (ping/ssh) to be executed from within the
40 tenant-network-namespace
41 Tempest host performs key-based authentication to the ssh server via
42 floating IP address
43
44 connectivity test is done by pinging destination server via source server
45 ssh connection.
46 success - ping returns
47 failure - ping_timeout reached
48
49 setup:
Yair Friedbf2e2c42014-01-28 12:06:38 +020050 for primary tenant:
Yair Fried4d7efa62013-11-17 17:12:29 +020051 1. create a network&subnet
52 2. create a router (if public router isn't configured)
53 3. connect tenant network to public network via router
54 4. create an access point:
55 a. a security group open to incoming ssh connection
56 b. a VM with a floating ip
57 5. create a general empty security group (same as "default", but
58 without rules allowing in-tenant traffic)
Yair Fried4d7efa62013-11-17 17:12:29 +020059
60 tests:
61 1. _verify_network_details
62 2. _verify_mac_addr: for each access point verify that
63 (subnet, fix_ip, mac address) are as defined in the port list
64 3. _test_in_tenant_block: test that in-tenant traffic is disabled
65 without rules allowing it
66 4. _test_in_tenant_allow: test that in-tenant traffic is enabled
67 once an appropriate rule has been created
68 5. _test_cross_tenant_block: test that cross-tenant traffic is disabled
69 without a rule allowing it on destination tenant
70 6. _test_cross_tenant_allow:
71 * test that cross-tenant traffic is enabled once an appropriate
72 rule has been created on destination tenant.
73 * test that reverse traffic is still blocked
74 * test than revesre traffic is enabled once an appropriate rule has
75 been created on source tenant
76
77 assumptions:
Yair Friedbf2e2c42014-01-28 12:06:38 +020078 1. alt_tenant/user existed and is different from primary_tenant/user
Yair Fried4d7efa62013-11-17 17:12:29 +020079 2. Public network is defined and reachable from the Tempest host
80 3. Public router can either be:
81 * defined, in which case all tenants networks can connect directly
82 to it, and cross tenant check will be done on the private IP of the
83 destination tenant
84 or
85 * not defined (empty string), in which case each tanant will have
86 its own router connected to the public network
87 """
88
89 class TenantProperties():
Yair Friedbf2e2c42014-01-28 12:06:38 +020090 """
Yair Fried4d7efa62013-11-17 17:12:29 +020091 helper class to save tenant details
92 id
93 credentials
94 network
95 subnet
96 security groups
97 servers
98 access point
Yair Friedbf2e2c42014-01-28 12:06:38 +020099 """
Yair Fried4d7efa62013-11-17 17:12:29 +0200100
101 def __init__(self, tenant_id, tenant_user, tenant_pass, tenant_name):
Andrea Frittolif9cde7e2014-02-18 09:57:04 +0000102 self.manager = clients.OfficialClientManager(
Yair Fried4d7efa62013-11-17 17:12:29 +0200103 tenant_user,
104 tenant_pass,
105 tenant_name
106 )
Yair Friedbf2e2c42014-01-28 12:06:38 +0200107 self.keypair = None
Yair Fried4d7efa62013-11-17 17:12:29 +0200108 self.tenant_id = tenant_id
109 self.tenant_name = tenant_name
110 self.tenant_user = tenant_user
111 self.tenant_pass = tenant_pass
112 self.network = None
113 self.subnet = None
114 self.router = None
115 self.security_groups = {}
116 self.servers = list()
117
Yair Friedbf2e2c42014-01-28 12:06:38 +0200118 def set_network(self, network, subnet, router):
Yair Fried4d7efa62013-11-17 17:12:29 +0200119 self.network = network
120 self.subnet = subnet
121 self.router = router
122
123 def _get_tenant_credentials(self):
124 return self.tenant_user, self.tenant_pass, self.tenant_name
125
126 @classmethod
127 def check_preconditions(cls):
Yair Friedbf2e2c42014-01-28 12:06:38 +0200128 super(TestSecurityGroupsBasicOps, cls).check_preconditions()
Yair Fried4d7efa62013-11-17 17:12:29 +0200129 if (cls.alt_tenant_id is None) or (cls.tenant_id is cls.alt_tenant_id):
130 msg = 'No alt_tenant defined'
131 cls.enabled = False
132 raise cls.skipException(msg)
Matthew Treinish6c072292014-01-29 19:15:52 +0000133 if not (CONF.network.tenant_networks_reachable or
134 CONF.network.public_network_id):
Yair Fried4d7efa62013-11-17 17:12:29 +0200135 msg = ('Either tenant_networks_reachable must be "true", or '
136 'public_network_id must be defined.')
137 cls.enabled = False
138 raise cls.skipException(msg)
139
140 @classmethod
141 def setUpClass(cls):
Yair Friedbf2e2c42014-01-28 12:06:38 +0200142 super(TestSecurityGroupsBasicOps, cls).setUpClass()
Yair Fried769bbff2013-12-18 16:33:17 +0200143 alt_creds = cls.alt_credentials()
Yair Fried4d7efa62013-11-17 17:12:29 +0200144 cls.alt_tenant_id = cls.manager._get_identity_client(
Yair Fried769bbff2013-12-18 16:33:17 +0200145 *alt_creds
Yair Fried4d7efa62013-11-17 17:12:29 +0200146 ).tenant_id
147 cls.check_preconditions()
148 # TODO(mnewby) Consider looking up entities as needed instead
149 # of storing them as collections on the class.
Yair Fried4d7efa62013-11-17 17:12:29 +0200150 cls.floating_ips = {}
151 cls.tenants = {}
Yair Friedbf2e2c42014-01-28 12:06:38 +0200152 cls.primary_tenant = cls.TenantProperties(cls.tenant_id,
153 *cls.credentials())
154 cls.alt_tenant = cls.TenantProperties(cls.alt_tenant_id,
155 *alt_creds)
156 for tenant in [cls.primary_tenant, cls.alt_tenant]:
Yair Fried4d7efa62013-11-17 17:12:29 +0200157 cls.tenants[tenant.tenant_id] = tenant
Yair Friedbf2e2c42014-01-28 12:06:38 +0200158 cls.floating_ip_access = not CONF.network.public_router_id
Yair Fried4d7efa62013-11-17 17:12:29 +0200159
Yair Friedbf2e2c42014-01-28 12:06:38 +0200160 def cleanup_wrapper(self, resource):
161 self.cleanup_resource(resource, self.__class__.__name__)
162
163 def setUp(self):
164 super(TestSecurityGroupsBasicOps, self).setUp()
165 self._deploy_tenant(self.primary_tenant)
166 self._verify_network_details(self.primary_tenant)
167 self._verify_mac_addr(self.primary_tenant)
Yair Fried4d7efa62013-11-17 17:12:29 +0200168
169 def _create_tenant_keypairs(self, tenant_id):
Yair Friedbf2e2c42014-01-28 12:06:38 +0200170 keypair = self.create_keypair(
Yair Fried4d7efa62013-11-17 17:12:29 +0200171 name=data_utils.rand_name('keypair-smoke-'))
Yair Friedbf2e2c42014-01-28 12:06:38 +0200172 self.addCleanup(self.cleanup_wrapper, keypair)
173 self.tenants[tenant_id].keypair = keypair
Yair Fried4d7efa62013-11-17 17:12:29 +0200174
175 def _create_tenant_security_groups(self, tenant):
Yair Fried4d7efa62013-11-17 17:12:29 +0200176 access_sg = self._create_empty_security_group(
177 namestart='secgroup_access-',
178 tenant_id=tenant.tenant_id
179 )
Yair Friedbf2e2c42014-01-28 12:06:38 +0200180 self.addCleanup(self.cleanup_wrapper, access_sg)
181
Yair Fried4d7efa62013-11-17 17:12:29 +0200182 # don't use default secgroup since it allows in-tenant traffic
183 def_sg = self._create_empty_security_group(
184 namestart='secgroup_general-',
185 tenant_id=tenant.tenant_id
186 )
Yair Friedbf2e2c42014-01-28 12:06:38 +0200187 self.addCleanup(self.cleanup_wrapper, def_sg)
Yair Fried4d7efa62013-11-17 17:12:29 +0200188 tenant.security_groups.update(access=access_sg, default=def_sg)
189 ssh_rule = dict(
190 protocol='tcp',
191 port_range_min=22,
192 port_range_max=22,
193 direction='ingress',
194 )
Yair Friedbf2e2c42014-01-28 12:06:38 +0200195 rule = self._create_security_group_rule(secgroup=access_sg,
196 **ssh_rule)
197 self.addCleanup(self.cleanup_wrapper, rule)
Yair Fried4d7efa62013-11-17 17:12:29 +0200198
199 def _verify_network_details(self, tenant):
200 # Checks that we see the newly created network/subnet/router via
201 # checking the result of list_[networks,routers,subnets]
202 # Check that (router, subnet) couple exist in port_list
203 seen_nets = self._list_networks()
204 seen_names = [n['name'] for n in seen_nets]
205 seen_ids = [n['id'] for n in seen_nets]
206
207 self.assertIn(tenant.network.name, seen_names)
208 self.assertIn(tenant.network.id, seen_ids)
209
210 seen_subnets = [(n['id'], n['cidr'], n['network_id'])
211 for n in self._list_subnets()]
212 mysubnet = (tenant.subnet.id, tenant.subnet.cidr, tenant.network.id)
213 self.assertIn(mysubnet, seen_subnets)
214
215 seen_routers = self._list_routers()
216 seen_router_ids = [n['id'] for n in seen_routers]
217 seen_router_names = [n['name'] for n in seen_routers]
218
219 self.assertIn(tenant.router.name, seen_router_names)
220 self.assertIn(tenant.router.id, seen_router_ids)
221
222 myport = (tenant.router.id, tenant.subnet.id)
223 router_ports = [(i['device_id'], i['fixed_ips'][0]['subnet_id']) for i
224 in self.network_client.list_ports()['ports']
225 if i['device_owner'] == 'network:router_interface']
226
227 self.assertIn(myport, router_ports)
228
229 def _create_server(self, name, tenant, security_groups=None):
230 """
231 creates a server and assigns to security group
232 """
233 self._set_compute_context(tenant)
234 if security_groups is None:
235 security_groups = [tenant.security_groups['default'].name]
236 create_kwargs = {
237 'nics': [
238 {'net-id': tenant.network.id},
239 ],
Yair Friedbf2e2c42014-01-28 12:06:38 +0200240 'key_name': tenant.keypair.name,
Yair Fried4d7efa62013-11-17 17:12:29 +0200241 'security_groups': security_groups,
242 'tenant_id': tenant.tenant_id
243 }
244 server = self.create_server(name=name, create_kwargs=create_kwargs)
Yair Friedbf2e2c42014-01-28 12:06:38 +0200245 self.addCleanup(self.cleanup_wrapper, server)
Yair Fried4d7efa62013-11-17 17:12:29 +0200246 return server
247
248 def _create_tenant_servers(self, tenant, num=1):
249 for i in range(num):
250 name = 'server-{tenant}-gen-{num}-'.format(
251 tenant=tenant.tenant_name,
252 num=i
253 )
254 name = data_utils.rand_name(name)
255 server = self._create_server(name, tenant)
Yair Fried4d7efa62013-11-17 17:12:29 +0200256 tenant.servers.append(server)
257
258 def _set_access_point(self, tenant):
259 """
260 creates a server in a secgroup with rule allowing external ssh
261 in order to access tenant internal network
262 workaround ip namespace
263 """
264 secgroups = [sg.name for sg in tenant.security_groups.values()]
265 name = 'server-{tenant}-access_point-'.format(tenant=tenant.tenant_name
266 )
267 name = data_utils.rand_name(name)
268 server = self._create_server(name, tenant,
269 security_groups=secgroups)
Yair Fried4d7efa62013-11-17 17:12:29 +0200270 tenant.access_point = server
271 self._assign_floating_ips(server)
272
273 def _assign_floating_ips(self, server):
Matthew Treinish6c072292014-01-29 19:15:52 +0000274 public_network_id = CONF.network.public_network_id
Yair Fried4d7efa62013-11-17 17:12:29 +0200275 floating_ip = self._create_floating_ip(server, public_network_id)
Yair Friedbf2e2c42014-01-28 12:06:38 +0200276 self.addCleanup(self.cleanup_wrapper, floating_ip)
Yair Fried4d7efa62013-11-17 17:12:29 +0200277 self.floating_ips.setdefault(server, floating_ip)
278
279 def _create_tenant_network(self, tenant):
Yair Friedbf2e2c42014-01-28 12:06:38 +0200280 network, subnet, router = self._create_networks(tenant.tenant_id)
281 for r in [network, router, subnet]:
282 self.addCleanup(self.cleanup_wrapper, r)
283 tenant.set_network(network, subnet, router)
Yair Fried4d7efa62013-11-17 17:12:29 +0200284
285 def _set_compute_context(self, tenant):
286 self.compute_client = tenant.manager.compute_client
287 return self.compute_client
288
289 def _deploy_tenant(self, tenant_or_id):
290 """
291 creates:
292 network
293 subnet
294 router (if public not defined)
295 access security group
296 access-point server
Yair Fried4d7efa62013-11-17 17:12:29 +0200297 """
298 if not isinstance(tenant_or_id, self.TenantProperties):
299 tenant = self.tenants[tenant_or_id]
300 tenant_id = tenant_or_id
301 else:
302 tenant = tenant_or_id
303 tenant_id = tenant.tenant_id
304 self._set_compute_context(tenant)
305 self._create_tenant_keypairs(tenant_id)
306 self._create_tenant_network(tenant)
307 self._create_tenant_security_groups(tenant)
Yair Fried4d7efa62013-11-17 17:12:29 +0200308 self._set_access_point(tenant)
309
310 def _get_server_ip(self, server, floating=False):
Yair Friedbf2e2c42014-01-28 12:06:38 +0200311 """
Yair Fried4d7efa62013-11-17 17:12:29 +0200312 returns the ip (floating/internal) of a server
Yair Friedbf2e2c42014-01-28 12:06:38 +0200313 """
Yair Fried4d7efa62013-11-17 17:12:29 +0200314 if floating:
armando-migliacciod03f2642014-02-21 19:55:50 -0800315 server_ip = self.floating_ips[server].floating_ip_address
Yair Fried4d7efa62013-11-17 17:12:29 +0200316 else:
armando-migliacciod03f2642014-02-21 19:55:50 -0800317 server_ip = None
Yair Fried4d7efa62013-11-17 17:12:29 +0200318 network_name = self.tenants[server.tenant_id].network.name
armando-migliacciod03f2642014-02-21 19:55:50 -0800319 if network_name in server.networks:
320 server_ip = server.networks[network_name][0]
321 return server_ip
Yair Fried4d7efa62013-11-17 17:12:29 +0200322
323 def _connect_to_access_point(self, tenant):
324 """
325 create ssh connection to tenant access point
326 """
327 access_point_ssh = \
328 self.floating_ips[tenant.access_point].floating_ip_address
Yair Friedbf2e2c42014-01-28 12:06:38 +0200329 private_key = tenant.keypair.private_key
Yair Fried4d7efa62013-11-17 17:12:29 +0200330 access_point_ssh = self._ssh_to_server(access_point_ssh,
331 private_key=private_key)
332 return access_point_ssh
333
Yair Fried4d7efa62013-11-17 17:12:29 +0200334 def _check_connectivity(self, access_point, ip, should_succeed=True):
335 if should_succeed:
336 msg = "Timed out waiting for %s to become reachable" % ip
337 else:
338 # todo(yfried): remove this line when bug 1252620 is fixed
339 return True
340 msg = "%s is reachable" % ip
341 try:
Yair Fried3097dc12014-01-26 08:46:43 +0200342 self.assertTrue(self._check_remote_connectivity(access_point, ip,
343 should_succeed),
Yair Fried4d7efa62013-11-17 17:12:29 +0200344 msg)
345 except Exception:
Attila Fazekas6bfd6492014-02-26 21:25:53 +0100346 debug.log_net_debug()
Yair Fried4d7efa62013-11-17 17:12:29 +0200347 raise
348
349 def _test_in_tenant_block(self, tenant):
350 access_point_ssh = self._connect_to_access_point(tenant)
351 for server in tenant.servers:
352 self._check_connectivity(access_point=access_point_ssh,
353 ip=self._get_server_ip(server),
354 should_succeed=False)
355
356 def _test_in_tenant_allow(self, tenant):
357 ruleset = dict(
358 protocol='icmp',
359 remote_group_id=tenant.security_groups['default'].id,
360 direction='ingress'
361 )
362 rule = self._create_security_group_rule(
363 secgroup=tenant.security_groups['default'],
364 **ruleset
365 )
Yair Friedbf2e2c42014-01-28 12:06:38 +0200366 self.addCleanup(self.cleanup_wrapper, rule)
Yair Fried4d7efa62013-11-17 17:12:29 +0200367 access_point_ssh = self._connect_to_access_point(tenant)
368 for server in tenant.servers:
369 self._check_connectivity(access_point=access_point_ssh,
370 ip=self._get_server_ip(server))
Yair Fried4d7efa62013-11-17 17:12:29 +0200371
372 def _test_cross_tenant_block(self, source_tenant, dest_tenant):
Yair Friedbf2e2c42014-01-28 12:06:38 +0200373 """
Yair Fried4d7efa62013-11-17 17:12:29 +0200374 if public router isn't defined, then dest_tenant access is via
375 floating-ip
Yair Friedbf2e2c42014-01-28 12:06:38 +0200376 """
Yair Fried4d7efa62013-11-17 17:12:29 +0200377 access_point_ssh = self._connect_to_access_point(source_tenant)
378 ip = self._get_server_ip(dest_tenant.access_point,
379 floating=self.floating_ip_access)
380 self._check_connectivity(access_point=access_point_ssh, ip=ip,
381 should_succeed=False)
382
383 def _test_cross_tenant_allow(self, source_tenant, dest_tenant):
Yair Friedbf2e2c42014-01-28 12:06:38 +0200384 """
Yair Fried4d7efa62013-11-17 17:12:29 +0200385 check for each direction:
386 creating rule for tenant incoming traffic enables only 1way traffic
Yair Friedbf2e2c42014-01-28 12:06:38 +0200387 """
Yair Fried4d7efa62013-11-17 17:12:29 +0200388 ruleset = dict(
389 protocol='icmp',
390 direction='ingress'
391 )
392 rule_s2d = self._create_security_group_rule(
393 secgroup=dest_tenant.security_groups['default'],
394 **ruleset
395 )
Yair Friedbf2e2c42014-01-28 12:06:38 +0200396 self.addCleanup(self.cleanup_wrapper, rule_s2d)
397 access_point_ssh = self._connect_to_access_point(source_tenant)
398 ip = self._get_server_ip(dest_tenant.access_point,
399 floating=self.floating_ip_access)
400 self._check_connectivity(access_point_ssh, ip)
Yair Fried4d7efa62013-11-17 17:12:29 +0200401
Yair Friedbf2e2c42014-01-28 12:06:38 +0200402 # test that reverse traffic is still blocked
403 self._test_cross_tenant_block(dest_tenant, source_tenant)
Yair Fried4d7efa62013-11-17 17:12:29 +0200404
Yair Friedbf2e2c42014-01-28 12:06:38 +0200405 # allow reverse traffic and check
406 rule_d2s = self._create_security_group_rule(
407 secgroup=source_tenant.security_groups['default'],
408 **ruleset
409 )
410 self.addCleanup(self.cleanup_wrapper, rule_d2s)
Yair Fried4d7efa62013-11-17 17:12:29 +0200411
Yair Friedbf2e2c42014-01-28 12:06:38 +0200412 access_point_ssh_2 = self._connect_to_access_point(dest_tenant)
413 ip = self._get_server_ip(source_tenant.access_point,
414 floating=self.floating_ip_access)
415 self._check_connectivity(access_point_ssh_2, ip)
Yair Fried4d7efa62013-11-17 17:12:29 +0200416
417 def _verify_mac_addr(self, tenant):
418 """
419 verify that VM (tenant's access point) has the same ip,mac as listed in
420 port list
421 """
422 access_point_ssh = self._connect_to_access_point(tenant)
423 mac_addr = access_point_ssh.get_mac_address()
424 mac_addr = mac_addr.strip().lower()
425 port_list = self.network_client.list_ports()['ports']
426 port_detail_list = [
427 (port['fixed_ips'][0]['subnet_id'],
428 port['fixed_ips'][0]['ip_address'],
429 port['mac_address'].lower()) for port in port_list
430 ]
431 server_ip = self._get_server_ip(tenant.access_point)
432 subnet_id = tenant.subnet.id
433 self.assertIn((subnet_id, server_ip, mac_addr), port_detail_list)
434
Masayuki Igawa4ded9f02014-02-17 15:05:59 +0900435 @test.attr(type='smoke')
436 @test.services('compute', 'network')
Yair Fried4d7efa62013-11-17 17:12:29 +0200437 def test_cross_tenant_traffic(self):
Nachi Ueno26b4c972014-01-17 06:15:13 -0800438 try:
Yair Friedbf2e2c42014-01-28 12:06:38 +0200439 # deploy new tenant
440 self._deploy_tenant(self.alt_tenant)
441 self._verify_network_details(self.alt_tenant)
442 self._verify_mac_addr(self.alt_tenant)
Yair Fried4d7efa62013-11-17 17:12:29 +0200443
Nachi Ueno26b4c972014-01-17 06:15:13 -0800444 # cross tenant check
Yair Friedbf2e2c42014-01-28 12:06:38 +0200445 source_tenant = self.primary_tenant
Nachi Ueno26b4c972014-01-17 06:15:13 -0800446 dest_tenant = self.alt_tenant
447 self._test_cross_tenant_block(source_tenant, dest_tenant)
448 self._test_cross_tenant_allow(source_tenant, dest_tenant)
449 except Exception:
Yair Friedbf2e2c42014-01-28 12:06:38 +0200450 for tenant in self.tenants.values():
451 self._log_console_output(servers=tenant.servers)
452 raise
453
Masayuki Igawa4ded9f02014-02-17 15:05:59 +0900454 @test.attr(type='smoke')
455 @test.services('compute', 'network')
Yair Friedbf2e2c42014-01-28 12:06:38 +0200456 def test_in_tenant_traffic(self):
457 try:
458 self._create_tenant_servers(self.primary_tenant, num=1)
459
460 # in-tenant check
461 self._test_in_tenant_block(self.primary_tenant)
462 self._test_in_tenant_allow(self.primary_tenant)
463
464 except Exception:
465 for tenant in self.tenants.values():
466 self._log_console_output(servers=tenant.servers)
Nachi Ueno26b4c972014-01-17 06:15:13 -0800467 raise