Allow any port or protocol in security group rules
Neutron allows setting port or protocol wildcard by not specifying any value for them. Example, these are allowed by neutron: neutron security-group-rule-create --direction egress <sgid> neutron security-group-rule-create --direction egress --protocol tcp <sgid> Specifying '-1' for IP protocol means a wildcard IP protocol. validate_ip_protocol is updated accordingly. 'All ports' choice is added to 'Open Port' field. Change-Id: I4a7262eda89e3206c743fee14c78aa6b49308ce6 Closes-Bug: 1669467
This commit is contained in:
parent
343cbd19b5
commit
87337ff255
@ -255,8 +255,8 @@ class ValidatorsTests(test.TestCase):
|
|||||||
icmp_code)
|
icmp_code)
|
||||||
|
|
||||||
def test_ip_proto_validator(self):
|
def test_ip_proto_validator(self):
|
||||||
VALID_PROTO = (0, 255)
|
VALID_PROTO = (0, 255, -1)
|
||||||
INVALID_PROTO = (-1, 256)
|
INVALID_PROTO = (-2, 256)
|
||||||
|
|
||||||
for proto in VALID_PROTO:
|
for proto in VALID_PROTO:
|
||||||
self.assertIsNone(validators.validate_ip_protocol(proto))
|
self.assertIsNone(validators.validate_ip_protocol(proto))
|
||||||
|
@ -43,7 +43,7 @@ def validate_icmp_code_range(icmp_code):
|
|||||||
|
|
||||||
|
|
||||||
def validate_ip_protocol(ip_proto):
|
def validate_ip_protocol(ip_proto):
|
||||||
if ip_proto not in range(0, 256):
|
if ip_proto < -1 or ip_proto > 255:
|
||||||
raise ValidationError(_("Not a valid IP protocol number"))
|
raise ValidationError(_("Not a valid IP protocol number"))
|
||||||
|
|
||||||
|
|
||||||
|
@ -261,9 +261,11 @@ class AddRule(forms.SelfHandlingForm):
|
|||||||
backend = api.network.security_group_backend(self.request)
|
backend = api.network.security_group_backend(self.request)
|
||||||
|
|
||||||
rules_dict = getattr(settings, 'SECURITY_GROUP_RULES', [])
|
rules_dict = getattr(settings, 'SECURITY_GROUP_RULES', [])
|
||||||
common_rules = [(k, rules_dict[k]['name'])
|
common_rules = [
|
||||||
for k in rules_dict
|
(k, rules_dict[k]['name'])
|
||||||
if rules_dict[k].get('backend', backend) == backend]
|
for k in rules_dict
|
||||||
|
if rules_dict[k].get('backend', backend) == backend
|
||||||
|
]
|
||||||
common_rules.sort()
|
common_rules.sort()
|
||||||
custom_rules = [('tcp', _('Custom TCP Rule')),
|
custom_rules = [('tcp', _('Custom TCP Rule')),
|
||||||
('udp', _('Custom UDP Rule')),
|
('udp', _('Custom UDP Rule')),
|
||||||
@ -276,6 +278,10 @@ class AddRule(forms.SelfHandlingForm):
|
|||||||
if backend == 'neutron':
|
if backend == 'neutron':
|
||||||
self.fields['direction'].choices = [('ingress', _('Ingress')),
|
self.fields['direction'].choices = [('ingress', _('Ingress')),
|
||||||
('egress', _('Egress'))]
|
('egress', _('Egress'))]
|
||||||
|
self.fields['ip_protocol'].help_text = _(
|
||||||
|
"Enter an integer value between -1 and 255 "
|
||||||
|
"(-1 means wild card)."
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
# direction and ethertype are not supported in Nova secgroup.
|
# direction and ethertype are not supported in Nova secgroup.
|
||||||
self.fields['direction'].widget = forms.HiddenInput()
|
self.fields['direction'].widget = forms.HiddenInput()
|
||||||
@ -284,6 +290,13 @@ class AddRule(forms.SelfHandlingForm):
|
|||||||
# and it is available only for neutron security group.
|
# and it is available only for neutron security group.
|
||||||
self.fields['ip_protocol'].widget = forms.HiddenInput()
|
self.fields['ip_protocol'].widget = forms.HiddenInput()
|
||||||
|
|
||||||
|
if backend == 'neutron':
|
||||||
|
self.fields['port_or_range'].choices = [
|
||||||
|
('port', _('Port')),
|
||||||
|
('range', _('Port Range')),
|
||||||
|
('all', _('All ports')),
|
||||||
|
]
|
||||||
|
|
||||||
if not getattr(settings, 'OPENSTACK_NEUTRON_NETWORK',
|
if not getattr(settings, 'OPENSTACK_NEUTRON_NETWORK',
|
||||||
{}).get('enable_ipv6', True):
|
{}).get('enable_ipv6', True):
|
||||||
self.fields['cidr'].version = forms.IPv4
|
self.fields['cidr'].version = forms.IPv4
|
||||||
@ -323,7 +336,11 @@ class AddRule(forms.SelfHandlingForm):
|
|||||||
self._update_and_pop_error(cleaned_data, 'ip_protocol', rule_menu)
|
self._update_and_pop_error(cleaned_data, 'ip_protocol', rule_menu)
|
||||||
self._update_and_pop_error(cleaned_data, 'icmp_code', None)
|
self._update_and_pop_error(cleaned_data, 'icmp_code', None)
|
||||||
self._update_and_pop_error(cleaned_data, 'icmp_type', None)
|
self._update_and_pop_error(cleaned_data, 'icmp_type', None)
|
||||||
if port_or_range == "port":
|
if port_or_range == 'all':
|
||||||
|
self._update_and_pop_error(cleaned_data, 'port', None)
|
||||||
|
self._update_and_pop_error(cleaned_data, 'from_port', None)
|
||||||
|
self._update_and_pop_error(cleaned_data, 'to_port', None)
|
||||||
|
elif port_or_range == "port":
|
||||||
self._update_and_pop_error(cleaned_data, 'from_port', port)
|
self._update_and_pop_error(cleaned_data, 'from_port', port)
|
||||||
self._update_and_pop_error(cleaned_data, 'to_port', port)
|
self._update_and_pop_error(cleaned_data, 'to_port', port)
|
||||||
if port is None:
|
if port is None:
|
||||||
|
@ -1029,3 +1029,94 @@ class SecurityGroupsNeutronTests(SecurityGroupsViewTests):
|
|||||||
res = self.client.post(self.edit_url, formData)
|
res = self.client.post(self.edit_url, formData)
|
||||||
self.assertFormError(res, 'form', 'cidr',
|
self.assertFormError(res, 'form', 'cidr',
|
||||||
'Invalid version for IP address')
|
'Invalid version for IP address')
|
||||||
|
|
||||||
|
@test.create_stubs({api.network: ('security_group_list',
|
||||||
|
'security_group_backend')})
|
||||||
|
def test_detail_add_rule_invalid_port(self):
|
||||||
|
sec_group = self.security_groups.first()
|
||||||
|
sec_group_list = self.security_groups.list()
|
||||||
|
rule = self.security_group_rules.first()
|
||||||
|
|
||||||
|
api.network.security_group_backend(
|
||||||
|
IsA(http.HttpRequest)).AndReturn(self.secgroup_backend)
|
||||||
|
api.network.security_group_list(
|
||||||
|
IsA(http.HttpRequest)).AndReturn(sec_group_list)
|
||||||
|
if django.VERSION >= (1, 9):
|
||||||
|
api.network.security_group_backend(
|
||||||
|
IsA(http.HttpRequest)).AndReturn(self.secgroup_backend)
|
||||||
|
api.network.security_group_list(
|
||||||
|
IsA(http.HttpRequest)).AndReturn(sec_group_list)
|
||||||
|
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
formData = {'method': 'AddRule',
|
||||||
|
'id': sec_group.id,
|
||||||
|
'port_or_range': 'port',
|
||||||
|
'port': -1,
|
||||||
|
'rule_menu': rule.ip_protocol,
|
||||||
|
'cidr': rule.ip_range['cidr'],
|
||||||
|
'remote': 'cidr'}
|
||||||
|
res = self.client.post(self.edit_url, formData)
|
||||||
|
self.assertNoMessages()
|
||||||
|
self.assertContains(res, "Not a valid port number")
|
||||||
|
|
||||||
|
@test.create_stubs({api.network: ('security_group_rule_create',
|
||||||
|
'security_group_list',
|
||||||
|
'security_group_backend',)})
|
||||||
|
def test_detail_add_rule_ingress_tcp_without_port(self):
|
||||||
|
sec_group = self.security_groups.first()
|
||||||
|
sec_group_list = self.security_groups.list()
|
||||||
|
rule = self.security_group_rules.list()[3]
|
||||||
|
|
||||||
|
api.network.security_group_backend(
|
||||||
|
IsA(http.HttpRequest)).AndReturn(self.secgroup_backend)
|
||||||
|
api.network.security_group_rule_create(IsA(http.HttpRequest),
|
||||||
|
sec_group.id, 'ingress', 'IPv4',
|
||||||
|
'tcp',
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
rule.ip_range['cidr'],
|
||||||
|
None).AndReturn(rule)
|
||||||
|
api.network.security_group_list(
|
||||||
|
IsA(http.HttpRequest)).AndReturn(sec_group_list)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
formData = {'id': sec_group.id,
|
||||||
|
'direction': 'ingress',
|
||||||
|
'port_or_range': 'all',
|
||||||
|
'rule_menu': 'tcp',
|
||||||
|
'cidr': rule.ip_range['cidr'],
|
||||||
|
'remote': 'cidr'}
|
||||||
|
res = self.client.post(self.edit_url, formData)
|
||||||
|
self.assertRedirectsNoFollow(res, self.detail_url)
|
||||||
|
|
||||||
|
@test.create_stubs({api.network: ('security_group_rule_create',
|
||||||
|
'security_group_list',
|
||||||
|
'security_group_backend',)})
|
||||||
|
def test_detail_add_rule_custom_without_protocol(self):
|
||||||
|
sec_group = self.security_groups.first()
|
||||||
|
sec_group_list = self.security_groups.list()
|
||||||
|
rule = self.security_group_rules.list()[3]
|
||||||
|
|
||||||
|
api.network.security_group_backend(
|
||||||
|
IsA(http.HttpRequest)).AndReturn(self.secgroup_backend)
|
||||||
|
api.network.security_group_rule_create(IsA(http.HttpRequest),
|
||||||
|
sec_group.id, 'ingress', 'IPv4',
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
rule.ip_range['cidr'],
|
||||||
|
None).AndReturn(rule)
|
||||||
|
api.network.security_group_list(
|
||||||
|
IsA(http.HttpRequest)).AndReturn(sec_group_list)
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
formData = {'id': sec_group.id,
|
||||||
|
'direction': 'ingress',
|
||||||
|
'port_or_range': 'port',
|
||||||
|
'rule_menu': 'custom',
|
||||||
|
'ip_protocol': -1,
|
||||||
|
'cidr': rule.ip_range['cidr'],
|
||||||
|
'remote': 'cidr'}
|
||||||
|
res = self.client.post(self.edit_url, formData)
|
||||||
|
self.assertRedirectsNoFollow(res, self.detail_url)
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Securtiy group "Add rule" form now allows to specify 'any' IP protocol
|
||||||
|
and 'any' port number (for TCP and UDP protocols). This feature is
|
||||||
|
available when neutron is used as a networking back-end.
|
||||||
|
You can specify 'any' IP protocol for 'Other Protocol' and ``-1`` means
|
||||||
|
'any' IP protocol. You can also see ``All ports`` choice in 'Open Port'
|
||||||
|
field in case of TCP or UDP protocol is selected.
|
Loading…
Reference in New Issue
Block a user