Merge lp:~soren/nova/secgroup-fixes into lp:~hudson-openstack/nova/trunk

Proposed by Soren Hansen
Status: Merged
Approved by: Vish Ishaya
Approved revision: 1532
Merged at revision: 1586
Proposed branch: lp:~soren/nova/secgroup-fixes
Merge into: lp:~hudson-openstack/nova/trunk
Diff against target: 152 lines (+78/-6)
5 files modified
nova/api/ec2/cloud.py (+17/-4)
nova/db/sqlalchemy/api.py (+2/-0)
nova/tests/api/ec2/test_cloud.py (+55/-0)
nova/tests/test_api.py (+1/-1)
nova/virt/libvirt/firewall.py (+3/-1)
To merge this branch: bzr merge lp:~soren/nova/secgroup-fixes
Reviewer Review Type Date Requested Status
Dan Prince (community) Abstain
Vish Ishaya (community) Approve
Christopher MacGown (community) Approve
Rick Harris (community) Approve
Review via email: mp+73794@code.launchpad.net

Description of the change

Fix a bug that would make spawning new instances fail if no port/protocol is given (for rules granting access for other security groups).

To post a comment you must log in.
Revision history for this message
Soren Hansen (soren) wrote :
Revision history for this message
Rick Harris (rconradharris) wrote :

Good bug report, good patch. Nice job guys.

LGTM.

review: Approve
Revision history for this message
Vish Ishaya (vishvananda) wrote :

This definitely fixes the linked bug, but it doesn't really address the way that security groups with no source ports are "supposed to" work.

According to this bug:

https://bugs.launchpad.net/nova/+bug/829609

When you specify no source group, amazon actually creates 3 separate rules to allow all tcp udp and icmp traffic from the group. So it seems like empty ones actually should be converted.

I think there are two options for this:

a) create the 3 rules when we first receive the request and migrate old empty rules into three rules

b) continue to allow them to be created as is, but have a conversion that displays them as three rules to the client, and implements allow all when modifying ip tables.

If i read your patch correctly, you've done the part of b which makes them allow all traffic, but we still haven't made them display properly to the client. I think I'm ok with this if we add a bug for fixing the client display as well, although I am concerned about certain edge cases. Like what happens if someone tries to revoke one of the faux rules?

review: Needs Information
Revision history for this message
Vish Ishaya (vishvananda) wrote :

Interesting. As far as I can tell, amazon does not support allowing ports from source groups. It is all or nothing:

euca2ools 1.3:

vishvananda@firefly:~$ euca-authorize -P tcp -p 22 -o test test
test test None tcp 22 22 None
InvalidParameterCombination: The parameter 'SourceSecurityGroupName' may not be used in combination with 'FromPort'

euca2ools 1.2:

vagrant@nova:/tmp/bzr/lp819477$ euca-authorize -o test test
GROUP test
PERMISSION test ALLOWS GRPNAME test
vagrant@nova:/tmp/bzr/lp819477$ euca-describe-groups
GROUP 911567224702 test test
PERMISSION 911567224702 test ALLOWS tcp 1 65535 GRPNAME test
PERMISSION 911567224702 test ALLOWS udp 1 65535 GRPNAME test
PERMISSION 911567224702 test ALLOWS icmp -1 -1 GRPNAME test

There doesn't seem to be any way to revoke just one of those rules. So I guess you're fix is fine as long as we change the display of the group to show all 3 on describe.

Revision history for this message
Christopher MacGown (0x44) wrote :

This fix looks good, Approve with the change Vish suggested above.

review: Approve
Revision history for this message
Soren Hansen (soren) wrote :

Vish, happy with it like this?

Revision history for this message
Vish Ishaya (vishvananda) wrote :

This is good, although it might be nice to show both source groups with a specific protocol and without. Seems like it could be accomplished with another if inside the if rule.group_id:

if rule.protocol:
  # show one rule
else:
  # show three rules

Thoughts?

Vish

Revision history for this message
Soren Hansen (soren) wrote :

> This is good, although it might be nice to show both source groups with a
> specific protocol and without. Seems like it could be accomplished with
> another if inside the if rule.group_id:
>
> if rule.protocol:
> # show one rule
> else:
> # show three rules
>
> Thoughts?

Depends. Do we want to allow source group filters with protocols and ports and stuff if EC2 doesn't?

Revision history for this message
Vish Ishaya (vishvananda) wrote :

Extra feature!

Well it doesn't appear that you changed the actual rule creation code, so it will be supported as is regardless. It just won't display them properly.

Revision history for this message
Vish Ishaya (vishvananda) wrote :

ping. I thought you mentioned in irc that you were going to make the change to show 3 or one rule?

Revision history for this message
Vish Ishaya (vishvananda) wrote :

thx soren.

review: Approve
Revision history for this message
OpenStack Infra (hudson-openstack) wrote :
Download full text (135.0 KiB)

The attempt to merge lp:~soren/nova/secgroup-fixes into lp:nova failed. Below is the output from the failed tests.

CloudTestCase
    test_ajax_console OK 1.89
    test_allocate_address OK 0.27
    test_associate_disassociate_address OK 1.44
    test_authorize_revoke_security_group_ingress_by_id OK 0.36
    test_authorize_security_group_fail_missing_source_group OK 0.27
    test_authorize_security_group_ingress OK 0.29
    test_authorize_security_group_ingress_already_exists OK 0.48
    test_authorize_security_group_ingress_ip_permissions_groups OK 0.33
    test_authorize_security_group_ingress_ip_permissions_ip_rangesOK 0.28
    test_authorize_security_group_ingress_missing_group_name_or_idOK 0.19
    test_authorize_security_group_ingress_missing_protocol_paramsOK 0.25
    test_console_output OK 1.39
    test_create_delete_security_group OK 0.45
    test_create_image OK 4.16
    test_create_snapshot OK 0.46
    test_create_volume_from_snapshot OK 0.63
    test_delete_key_pair OK 0.42
    test_delete_security_group_by_id OK 0.25
    test_delete_security_group_no_params OK 0.20
    test_delete_security_group_with_bad_group_id OK 0.22
    test_delete_security_group_with_bad_name OK 0.23
    test_delete_snapshot OK 0.45
    test_deregister_image OK 0.22
    test_deregister_image_wrong_container_type OK 0.20
    test_describe_addresses OK 0.51
    test_describe_availability_zones OK 0.24
    test_describe_image_attribute OK 0.19
    test_describe_image_attribute_block_device_mapping OK 0.20
    test_describe_image_attribute_root_device_name OK 0.20
    test_describe_image_mapping OK 0.21
    test_describe_images OK 0.20
    test_describe_instance_attribute OK 0.21
    test_describe_instances OK 0.41
    test_describe_instances_bdm OK 1.48
    test_describe_instances_deleted OK 0.27
    test_describe_key_pairs OK 0.69
    test_describe_regions OK 0.22
    test_describe_security_group_ingress_groups OK 0.47
    test_describe_security_groups OK 0.29
    test_describe_security_groups_by_id OK 0.33
    test_describe_snapshots OK 0.23
    test_describe_volumes OK 0.28...

Revision history for this message
OpenStack Infra (hudson-openstack) wrote :
Download full text (135.0 KiB)

The attempt to merge lp:~soren/nova/secgroup-fixes into lp:nova failed. Below is the output from the failed tests.

CloudTestCase
    test_ajax_console OK 1.88
    test_allocate_address OK 0.26
    test_associate_disassociate_address OK 1.42
    test_authorize_revoke_security_group_ingress_by_id OK 0.35
    test_authorize_security_group_fail_missing_source_group OK 0.27
    test_authorize_security_group_ingress OK 0.28
    test_authorize_security_group_ingress_already_exists OK 0.48
    test_authorize_security_group_ingress_ip_permissions_groups OK 0.33
    test_authorize_security_group_ingress_ip_permissions_ip_rangesOK 0.28
    test_authorize_security_group_ingress_missing_group_name_or_idOK 0.20
    test_authorize_security_group_ingress_missing_protocol_paramsOK 0.26
    test_console_output OK 1.35
    test_create_delete_security_group OK 0.43
    test_create_image OK 5.29
    test_create_snapshot OK 0.46
    test_create_volume_from_snapshot OK 0.63
    test_delete_key_pair OK 0.43
    test_delete_security_group_by_id OK 0.24
    test_delete_security_group_no_params OK 0.19
    test_delete_security_group_with_bad_group_id OK 0.21
    test_delete_security_group_with_bad_name OK 0.22
    test_delete_snapshot OK 0.44
    test_deregister_image OK 0.19
    test_deregister_image_wrong_container_type OK 0.19
    test_describe_addresses OK 0.50
    test_describe_availability_zones OK 0.25
    test_describe_image_attribute OK 0.20
    test_describe_image_attribute_block_device_mapping OK 0.21
    test_describe_image_attribute_root_device_name OK 0.19
    test_describe_image_mapping OK 0.19
    test_describe_images OK 0.20
    test_describe_instance_attribute OK 0.20
    test_describe_instances OK 0.40
    test_describe_instances_bdm OK 1.49
    test_describe_instances_deleted OK 0.28
    test_describe_key_pairs OK 0.58
    test_describe_regions OK 0.22
    test_describe_security_group_ingress_groups OK 0.47
    test_describe_security_groups OK 0.30
    test_describe_security_groups_by_id OK 0.32
    test_describe_snapshots OK 0.24
    test_describe_volumes OK 0.27...

Revision history for this message
Dan Prince (dan-prince) wrote :

Soren:

I'm also getting a failure w/ this branch on Smokestack:

(nova.rpc): TRACE: File "/usr/lib/pymodules/python2.6/nova/network/linux_net.py", line 614, in update_dhcp
(nova.rpc): TRACE: _execute(*cmd, run_as_root=True)
(nova.rpc): TRACE: File "/usr/lib/pymodules/python2.6/nova/network/linux_net.py", line 710, in _execute
(nova.rpc): TRACE: return utils.execute(*cmd, **kwargs)
(nova.rpc): TRACE: File "/usr/lib/pymodules/python2.6/nova/utils.py", line 188, in execute
(nova.rpc): TRACE: cmd=' '.join(cmd))
(nova.rpc): TRACE: ProcessExecutionError: Unexpected error while running command.
(nova.rpc): TRACE: Command: sudo FLAGFILE=/etc/nova/nova.conf NETWORK_ID=1 dnsmasq --strict-order --bind-interfaces --interface=xenbr0 --conf-file= --domain=novalocal --pid-file=/var/lib/nova/networks/nova-xenbr0.pid --listen-address=192.168.0.1 --except-interface=lo --dhcp-range=192.168.0.2,static,120s --dhcp-lease-max=256 --dhcp-hostsfile=/var/lib/nova/networks/nova-xenbr0.conf --dhcp-script=/usr/bin/nova-dhcpbridge --leasefile-ro
(nova.rpc): TRACE: Exit code: 2
(nova.rpc): TRACE: Stdout: ''
(nova.rpc): TRACE: Stderr: '\ndnsmasq: failed to create listening socket for fe80::f4d9:9eff:fe86:44cf: No such device or address\n'
(nova.rpc): TRACE:
2011-09-16 17:29:27,469 ERROR nova.rpc [-] Returning exception Unexpected error while running command.

---

Let me know if you need more info.

review: Needs Fixing
Revision history for this message
Soren Hansen (soren) wrote :

> Let me know if you need more info.

That would be great. I have no idea what might be causing this and I can't seem to reproduce it.

Revision history for this message
Dan Prince (dan-prince) wrote :

Hey Soren,

My bad. The dnsmasq issue isn't from your branch at all. I get this on trunk as well. Sorry man, please disregard the above stack trace.

review: Abstain

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'nova/api/ec2/cloud.py'
--- nova/api/ec2/cloud.py 2011-09-15 20:04:31 +0000
+++ nova/api/ec2/cloud.py 2011-09-16 19:31:24 +0000
@@ -594,18 +594,31 @@
594 g['ipPermissions'] = []594 g['ipPermissions'] = []
595 for rule in group.rules:595 for rule in group.rules:
596 r = {}596 r = {}
597 r['ipProtocol'] = rule.protocol
598 r['fromPort'] = rule.from_port
599 r['toPort'] = rule.to_port
600 r['groups'] = []597 r['groups'] = []
601 r['ipRanges'] = []598 r['ipRanges'] = []
602 if rule.group_id:599 if rule.group_id:
603 source_group = db.security_group_get(context, rule.group_id)600 source_group = db.security_group_get(context, rule.group_id)
604 r['groups'] += [{'groupName': source_group.name,601 r['groups'] += [{'groupName': source_group.name,
605 'userId': source_group.project_id}]602 'userId': source_group.project_id}]
603 if rule.protocol:
604 r['ipProtocol'] = rule.protocol
605 r['fromPort'] = rule.from_port
606 r['toPort'] = rule.to_port
607 g['ipPermissions'] += [dict(r)]
608 else:
609 for protocol, min_port, max_port in (('icmp', -1, -1),
610 ('tcp', 1, 65535),
611 ('udp', 1, 65536)):
612 r['ipProtocol'] = protocol
613 r['fromPort'] = min_port
614 r['toPort'] = max_port
615 g['ipPermissions'] += [dict(r)]
606 else:616 else:
617 r['ipProtocol'] = rule.protocol
618 r['fromPort'] = rule.from_port
619 r['toPort'] = rule.to_port
607 r['ipRanges'] += [{'cidrIp': rule.cidr}]620 r['ipRanges'] += [{'cidrIp': rule.cidr}]
608 g['ipPermissions'] += [r]621 g['ipPermissions'] += [r]
609 return g622 return g
610623
611 def _rule_args_to_dict(self, context, kwargs):624 def _rule_args_to_dict(self, context, kwargs):
612625
=== modified file 'nova/db/sqlalchemy/api.py'
--- nova/db/sqlalchemy/api.py 2011-09-14 20:57:15 +0000
+++ nova/db/sqlalchemy/api.py 2011-09-16 19:31:24 +0000
@@ -2827,12 +2827,14 @@
2827 result = session.query(models.SecurityGroupIngressRule).\2827 result = session.query(models.SecurityGroupIngressRule).\
2828 filter_by(deleted=can_read_deleted(context)).\2828 filter_by(deleted=can_read_deleted(context)).\
2829 filter_by(parent_group_id=security_group_id).\2829 filter_by(parent_group_id=security_group_id).\
2830 options(joinedload_all('grantee_group')).\
2830 all()2831 all()
2831 else:2832 else:
2832 # TODO(vish): Join to group and check for project_id2833 # TODO(vish): Join to group and check for project_id
2833 result = session.query(models.SecurityGroupIngressRule).\2834 result = session.query(models.SecurityGroupIngressRule).\
2834 filter_by(deleted=False).\2835 filter_by(deleted=False).\
2835 filter_by(parent_group_id=security_group_id).\2836 filter_by(parent_group_id=security_group_id).\
2837 options(joinedload_all('grantee_group')).\
2836 all()2838 all()
2837 return result2839 return result
28382840
28392841
=== modified file 'nova/tests/api/ec2/test_cloud.py'
--- nova/tests/api/ec2/test_cloud.py 2011-09-13 21:04:13 +0000
+++ nova/tests/api/ec2/test_cloud.py 2011-09-16 19:31:24 +0000
@@ -305,6 +305,61 @@
305 'ip_protocol': u'tcp'}]}305 'ip_protocol': u'tcp'}]}
306 self.assertTrue(authz(self.context, group_name=sec['name'], **kwargs))306 self.assertTrue(authz(self.context, group_name=sec['name'], **kwargs))
307307
308 def test_describe_security_group_ingress_groups(self):
309 kwargs = {'project_id': self.context.project_id, 'name': 'test'}
310 sec1 = db.security_group_create(self.context, kwargs)
311 sec2 = db.security_group_create(self.context,
312 {'project_id': 'someuser',
313 'name': 'somegroup1'})
314 sec3 = db.security_group_create(self.context,
315 {'project_id': 'someuser',
316 'name': 'othergroup2'})
317 authz = self.cloud.authorize_security_group_ingress
318 kwargs = {'ip_permissions': [
319 {'groups': {'1': {'user_id': u'someuser',
320 'group_name': u'somegroup1'}}},
321 {'ip_protocol': 'tcp',
322 'from_port': 80,
323 'to_port': 80,
324 'groups': {'1': {'user_id': u'someuser',
325 'group_name': u'othergroup2'}}}]}
326 self.assertTrue(authz(self.context, group_name=sec1['name'], **kwargs))
327 describe = self.cloud.describe_security_groups
328 groups = describe(self.context, group_name=['test'])
329 self.assertEquals(len(groups['securityGroupInfo']), 1)
330 actual_rules = groups['securityGroupInfo'][0]['ipPermissions']
331 self.assertEquals(len(actual_rules), 4)
332 expected_rules = [{'fromPort': -1,
333 'groups': [{'groupName': 'somegroup1',
334 'userId': 'someuser'}],
335 'ipProtocol': 'icmp',
336 'ipRanges': [],
337 'toPort': -1},
338 {'fromPort': 1,
339 'groups': [{'groupName': u'somegroup1',
340 'userId': u'someuser'}],
341 'ipProtocol': 'tcp',
342 'ipRanges': [],
343 'toPort': 65535},
344 {'fromPort': 1,
345 'groups': [{'groupName': u'somegroup1',
346 'userId': u'someuser'}],
347 'ipProtocol': 'udp',
348 'ipRanges': [],
349 'toPort': 65536},
350 {'fromPort': 80,
351 'groups': [{'groupName': u'othergroup2',
352 'userId': u'someuser'}],
353 'ipProtocol': u'tcp',
354 'ipRanges': [],
355 'toPort': 80}]
356 for rule in expected_rules:
357 self.assertTrue(rule in actual_rules)
358
359 db.security_group_destroy(self.context, sec3['id'])
360 db.security_group_destroy(self.context, sec2['id'])
361 db.security_group_destroy(self.context, sec1['id'])
362
308 def test_revoke_security_group_ingress(self):363 def test_revoke_security_group_ingress(self):
309 kwargs = {'project_id': self.context.project_id, 'name': 'test'}364 kwargs = {'project_id': self.context.project_id, 'name': 'test'}
310 sec = db.security_group_create(self.context, kwargs)365 sec = db.security_group_create(self.context, kwargs)
311366
=== modified file 'nova/tests/test_api.py'
--- nova/tests/test_api.py 2011-08-12 04:03:37 +0000
+++ nova/tests/test_api.py 2011-09-16 19:31:24 +0000
@@ -515,7 +515,7 @@
515 # be good enough for that.515 # be good enough for that.
516 for group in rv:516 for group in rv:
517 if group.name == security_group_name:517 if group.name == security_group_name:
518 self.assertEquals(len(group.rules), 1)518 self.assertEquals(len(group.rules), 3)
519 self.assertEquals(len(group.rules[0].grants), 1)519 self.assertEquals(len(group.rules[0].grants), 1)
520 self.assertEquals(str(group.rules[0].grants[0]), '%s-%s' %520 self.assertEquals(str(group.rules[0].grants[0]), '%s-%s' %
521 (other_security_group_name, 'fake'))521 (other_security_group_name, 'fake'))
522522
=== modified file 'nova/virt/libvirt/firewall.py'
--- nova/virt/libvirt/firewall.py 2011-08-26 17:38:20 +0000
+++ nova/virt/libvirt/firewall.py 2011-09-16 19:31:24 +0000
@@ -663,7 +663,9 @@
663 if version == 6 and rule.protocol == 'icmp':663 if version == 6 and rule.protocol == 'icmp':
664 protocol = 'icmpv6'664 protocol = 'icmpv6'
665665
666 args = ['-j ACCEPT', '-p', protocol]666 args = ['-j ACCEPT']
667 if protocol:
668 args += ['-p', protocol]
667669
668 if protocol in ['udp', 'tcp']:670 if protocol in ['udp', 'tcp']:
669 if rule.from_port == rule.to_port:671 if rule.from_port == rule.to_port: