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
1=== modified file 'nova/api/ec2/cloud.py'
2--- nova/api/ec2/cloud.py 2011-09-15 20:04:31 +0000
3+++ nova/api/ec2/cloud.py 2011-09-16 19:31:24 +0000
4@@ -594,18 +594,31 @@
5 g['ipPermissions'] = []
6 for rule in group.rules:
7 r = {}
8- r['ipProtocol'] = rule.protocol
9- r['fromPort'] = rule.from_port
10- r['toPort'] = rule.to_port
11 r['groups'] = []
12 r['ipRanges'] = []
13 if rule.group_id:
14 source_group = db.security_group_get(context, rule.group_id)
15 r['groups'] += [{'groupName': source_group.name,
16 'userId': source_group.project_id}]
17+ if rule.protocol:
18+ r['ipProtocol'] = rule.protocol
19+ r['fromPort'] = rule.from_port
20+ r['toPort'] = rule.to_port
21+ g['ipPermissions'] += [dict(r)]
22+ else:
23+ for protocol, min_port, max_port in (('icmp', -1, -1),
24+ ('tcp', 1, 65535),
25+ ('udp', 1, 65536)):
26+ r['ipProtocol'] = protocol
27+ r['fromPort'] = min_port
28+ r['toPort'] = max_port
29+ g['ipPermissions'] += [dict(r)]
30 else:
31+ r['ipProtocol'] = rule.protocol
32+ r['fromPort'] = rule.from_port
33+ r['toPort'] = rule.to_port
34 r['ipRanges'] += [{'cidrIp': rule.cidr}]
35- g['ipPermissions'] += [r]
36+ g['ipPermissions'] += [r]
37 return g
38
39 def _rule_args_to_dict(self, context, kwargs):
40
41=== modified file 'nova/db/sqlalchemy/api.py'
42--- nova/db/sqlalchemy/api.py 2011-09-14 20:57:15 +0000
43+++ nova/db/sqlalchemy/api.py 2011-09-16 19:31:24 +0000
44@@ -2827,12 +2827,14 @@
45 result = session.query(models.SecurityGroupIngressRule).\
46 filter_by(deleted=can_read_deleted(context)).\
47 filter_by(parent_group_id=security_group_id).\
48+ options(joinedload_all('grantee_group')).\
49 all()
50 else:
51 # TODO(vish): Join to group and check for project_id
52 result = session.query(models.SecurityGroupIngressRule).\
53 filter_by(deleted=False).\
54 filter_by(parent_group_id=security_group_id).\
55+ options(joinedload_all('grantee_group')).\
56 all()
57 return result
58
59
60=== modified file 'nova/tests/api/ec2/test_cloud.py'
61--- nova/tests/api/ec2/test_cloud.py 2011-09-13 21:04:13 +0000
62+++ nova/tests/api/ec2/test_cloud.py 2011-09-16 19:31:24 +0000
63@@ -305,6 +305,61 @@
64 'ip_protocol': u'tcp'}]}
65 self.assertTrue(authz(self.context, group_name=sec['name'], **kwargs))
66
67+ def test_describe_security_group_ingress_groups(self):
68+ kwargs = {'project_id': self.context.project_id, 'name': 'test'}
69+ sec1 = db.security_group_create(self.context, kwargs)
70+ sec2 = db.security_group_create(self.context,
71+ {'project_id': 'someuser',
72+ 'name': 'somegroup1'})
73+ sec3 = db.security_group_create(self.context,
74+ {'project_id': 'someuser',
75+ 'name': 'othergroup2'})
76+ authz = self.cloud.authorize_security_group_ingress
77+ kwargs = {'ip_permissions': [
78+ {'groups': {'1': {'user_id': u'someuser',
79+ 'group_name': u'somegroup1'}}},
80+ {'ip_protocol': 'tcp',
81+ 'from_port': 80,
82+ 'to_port': 80,
83+ 'groups': {'1': {'user_id': u'someuser',
84+ 'group_name': u'othergroup2'}}}]}
85+ self.assertTrue(authz(self.context, group_name=sec1['name'], **kwargs))
86+ describe = self.cloud.describe_security_groups
87+ groups = describe(self.context, group_name=['test'])
88+ self.assertEquals(len(groups['securityGroupInfo']), 1)
89+ actual_rules = groups['securityGroupInfo'][0]['ipPermissions']
90+ self.assertEquals(len(actual_rules), 4)
91+ expected_rules = [{'fromPort': -1,
92+ 'groups': [{'groupName': 'somegroup1',
93+ 'userId': 'someuser'}],
94+ 'ipProtocol': 'icmp',
95+ 'ipRanges': [],
96+ 'toPort': -1},
97+ {'fromPort': 1,
98+ 'groups': [{'groupName': u'somegroup1',
99+ 'userId': u'someuser'}],
100+ 'ipProtocol': 'tcp',
101+ 'ipRanges': [],
102+ 'toPort': 65535},
103+ {'fromPort': 1,
104+ 'groups': [{'groupName': u'somegroup1',
105+ 'userId': u'someuser'}],
106+ 'ipProtocol': 'udp',
107+ 'ipRanges': [],
108+ 'toPort': 65536},
109+ {'fromPort': 80,
110+ 'groups': [{'groupName': u'othergroup2',
111+ 'userId': u'someuser'}],
112+ 'ipProtocol': u'tcp',
113+ 'ipRanges': [],
114+ 'toPort': 80}]
115+ for rule in expected_rules:
116+ self.assertTrue(rule in actual_rules)
117+
118+ db.security_group_destroy(self.context, sec3['id'])
119+ db.security_group_destroy(self.context, sec2['id'])
120+ db.security_group_destroy(self.context, sec1['id'])
121+
122 def test_revoke_security_group_ingress(self):
123 kwargs = {'project_id': self.context.project_id, 'name': 'test'}
124 sec = db.security_group_create(self.context, kwargs)
125
126=== modified file 'nova/tests/test_api.py'
127--- nova/tests/test_api.py 2011-08-12 04:03:37 +0000
128+++ nova/tests/test_api.py 2011-09-16 19:31:24 +0000
129@@ -515,7 +515,7 @@
130 # be good enough for that.
131 for group in rv:
132 if group.name == security_group_name:
133- self.assertEquals(len(group.rules), 1)
134+ self.assertEquals(len(group.rules), 3)
135 self.assertEquals(len(group.rules[0].grants), 1)
136 self.assertEquals(str(group.rules[0].grants[0]), '%s-%s' %
137 (other_security_group_name, 'fake'))
138
139=== modified file 'nova/virt/libvirt/firewall.py'
140--- nova/virt/libvirt/firewall.py 2011-08-26 17:38:20 +0000
141+++ nova/virt/libvirt/firewall.py 2011-09-16 19:31:24 +0000
142@@ -663,7 +663,9 @@
143 if version == 6 and rule.protocol == 'icmp':
144 protocol = 'icmpv6'
145
146- args = ['-j ACCEPT', '-p', protocol]
147+ args = ['-j ACCEPT']
148+ if protocol:
149+ args += ['-p', protocol]
150
151 if protocol in ['udp', 'tcp']:
152 if rule.from_port == rule.to_port: