Merge "Restrict user private network cidr input"

This commit is contained in:
Jenkins 2016-08-21 12:15:32 +00:00 committed by Gerrit Code Review
commit 0e0b01fd27
7 changed files with 264 additions and 1 deletions

View File

@ -1615,6 +1615,19 @@ Ignore all listed Nova extensions, and behave as if they were unsupported.
Can be used to selectively disable certain costly extensions for performance
reasons.
``ALLOWED_PRIVATE_SUBNET_CIDR``
-------------------------------
.. versionadded:: 10.0.0(Newton)
Default: ``{'ipv4': [], 'ipv6': []}``
Dict used to restrict user private subnet cidr range.
An empty list means that user input will not be restricted
for a corresponding IP version. By default, there is
no restriction for both IPv4 and IPv6.
Example: ``{'ipv4': ['192.168.0.0/16', '10.0.0.0/8'], 'ipv6': ['fc00::/7',]}``
``ADMIN_FILTER_DATA_FIRST``
---------------------------

View File

@ -22,12 +22,25 @@ from horizon import exceptions
from openstack_dashboard import api
from openstack_dashboard.dashboards.project.networks.subnets \
import workflows as project_workflows
from openstack_dashboard.dashboards.project.networks import workflows \
as net_workflows
LOG = logging.getLogger(__name__)
class CreateSubnetInfoAction(project_workflows.CreateSubnetInfoAction):
check_subnet_range = False
class CreateSubnetInfo(project_workflows.CreateSubnetInfo):
action_class = CreateSubnetInfoAction
class CreateSubnet(project_workflows.CreateSubnet):
default_steps = (CreateSubnetInfo,
net_workflows.CreateSubnetDetail)
def get_success_url(self):
return reverse("horizon:admin:networks:detail",
args=(self.context.get('network_id'),))
@ -53,6 +66,17 @@ class CreateSubnet(project_workflows.CreateSubnet):
return True if subnet else False
class UpdateSubnetInfoAction(project_workflows.UpdateSubnetInfoAction):
check_subnet_range = False
class UpdateSubnetInfo(project_workflows.UpdateSubnetInfo):
action_class = UpdateSubnetInfoAction
class UpdateSubnet(project_workflows.UpdateSubnet):
success_url = "horizon:admin:networks:detail"
failure_url = "horizon:admin:networks:detail"
default_steps = (UpdateSubnetInfo,
project_workflows.UpdateSubnetDetail)

View File

@ -765,6 +765,189 @@ class NetworkTests(test.TestCase, NetworkStubMixin):
self.test_network_create_post_with_subnet_cidr_without_mask(
test_with_subnetpool=True)
@test.update_settings(
ALLOWED_PRIVATE_SUBNET_CIDR={'ipv4': ['192.168.0.0/16']})
@test.create_stubs({api.neutron: ('is_extension_supported',
'profile_list',
'subnetpool_list')})
def test_network_create_post_with_subnet_cidr_invalid_v4_range(
self,
test_with_profile=False,
test_with_subnetpool=False
):
network = self.networks.first()
subnet = self.subnets.first()
if test_with_profile:
net_profiles = self.net_profiles.list()
net_profile_id = self.net_profiles.first().id
api.neutron.profile_list(IsA(http.HttpRequest),
'network').AndReturn(net_profiles)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'subnet_allocation').\
AndReturn(True)
api.neutron.subnetpool_list(IsA(http.HttpRequest)).\
AndReturn(self.subnetpools.list())
self.mox.ReplayAll()
form_data = {'net_name': network.name,
'shared': False,
'admin_state': network.admin_state_up,
'with_subnet': True}
if test_with_profile:
form_data['net_profile_id'] = net_profile_id
if test_with_subnetpool:
subnetpool = self.subnetpools.first()
form_data['subnetpool'] = subnetpool.id
form_data['prefixlen'] = subnetpool.default_prefixlen
form_data.update(form_data_subnet(subnet, cidr='30.30.30.0/24',
allocation_pools=[]))
url = reverse('horizon:project:networks:create')
res = self.client.post(url, form_data)
expected_msg = ("CIDRs allowed for user private ipv4 networks "
"are 192.168.0.0/16.")
self.assertContains(res, expected_msg)
@test.update_settings(
ALLOWED_PRIVATE_SUBNET_CIDR={'ipv4': ['192.168.0.0/16']})
@test.update_settings(
OPENSTACK_NEUTRON_NETWORK={'profile_support': 'cisco'})
def test_network_create_post_with_subnet_cidr_invalid_v4_range_w_profile(
self):
self.test_network_create_post_with_subnet_cidr_invalid_v4_range(
test_with_profile=True)
@test.update_settings(
ALLOWED_PRIVATE_SUBNET_CIDR={'ipv4': ['192.168.0.0/16']})
def test_network_create_post_with_subnet_cidr_invalid_v4_range_w_snpool(
self):
self.test_network_create_post_with_subnet_cidr_invalid_v4_range(
test_with_subnetpool=True)
@test.update_settings(ALLOWED_PRIVATE_SUBNET_CIDR={'ipv6': ['fc00::/9']})
@test.create_stubs({api.neutron: ('is_extension_supported',
'profile_list',
'subnetpool_list')})
def test_network_create_post_with_subnet_cidr_invalid_v6_range(
self,
test_with_profile=False,
test_with_subnetpool=False
):
network = self.networks.first()
subnet_v6 = self.subnets.list()[3]
if test_with_profile:
net_profiles = self.net_profiles.list()
net_profile_id = self.net_profiles.first().id
api.neutron.profile_list(IsA(http.HttpRequest),
'network').AndReturn(net_profiles)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'subnet_allocation').\
AndReturn(True)
api.neutron.subnetpool_list(IsA(http.HttpRequest)).\
AndReturn(self.subnetpools.list())
self.mox.ReplayAll()
form_data = {'net_name': network.name,
'shared': False,
'admin_state': network.admin_state_up,
'with_subnet': True}
if test_with_profile:
form_data['net_profile_id'] = net_profile_id
if test_with_subnetpool:
subnetpool = self.subnetpools.first()
form_data['subnetpool'] = subnetpool.id
form_data['prefixlen'] = subnetpool.default_prefixlen
form_data.update(form_data_subnet(subnet_v6, cidr='fc00::/7',
allocation_pools=[]))
url = reverse('horizon:project:networks:create')
res = self.client.post(url, form_data)
expected_msg = ("CIDRs allowed for user private ipv6 networks "
"are fc00::/9.")
self.assertContains(res, expected_msg)
@test.update_settings(ALLOWED_PRIVATE_SUBNET_CIDR={'ipv6': ['fc00::/9']})
@test.update_settings(
OPENSTACK_NEUTRON_NETWORK={'profile_support': 'cisco'})
def test_network_create_post_with_subnet_cidr_invalid_v6_range_w_profile(
self):
self.test_network_create_post_with_subnet_cidr_invalid_v6_range(
test_with_profile=True)
@test.update_settings(ALLOWED_PRIVATE_SUBNET_CIDR={'ipv6': ['fc00::/9']})
def test_network_create_post_with_subnet_cidr_invalid_v6_range_w_snpool(
self):
self.test_network_create_post_with_subnet_cidr_invalid_v4_range(
test_with_subnetpool=True)
@test.create_stubs({api.neutron: ('network_create',
'subnet_create',
'profile_list',
'is_extension_supported',
'subnetpool_list')})
def test_network_create_post_with_subnet_cidr_not_restrict(
self,
test_with_profile=False
):
network = self.networks.first()
subnet = self.subnets.first()
cidr = '30.30.30.0/24'
gateway_ip = '30.30.30.1'
params = {'name': network.name,
'admin_state_up': network.admin_state_up,
'shared': False}
subnet_params = {'network_id': network.id,
'name': subnet.name,
'cidr': cidr,
'ip_version': subnet.ip_version,
'gateway_ip': gateway_ip,
'enable_dhcp': subnet.enable_dhcp}
if test_with_profile:
net_profiles = self.net_profiles.list()
net_profile_id = self.net_profiles.first().id
api.neutron.profile_list(IsA(http.HttpRequest),
'network').AndReturn(net_profiles)
params['net_profile_id'] = net_profile_id
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'subnet_allocation').\
AndReturn(True)
api.neutron.subnetpool_list(IsA(http.HttpRequest)).\
AndReturn(self.subnetpools.list())
api.neutron.network_create(IsA(http.HttpRequest),
**params).AndReturn(network)
api.neutron.subnet_create(IsA(http.HttpRequest),
**subnet_params).AndReturn(subnet)
self.mox.ReplayAll()
form_data = {'net_name': network.name,
'admin_state': network.admin_state_up,
'shared': False,
'with_subnet': True}
if test_with_profile:
form_data['net_profile_id'] = net_profile_id
form_data.update(form_data_subnet(subnet, cidr=cidr,
gateway_ip=gateway_ip,
allocation_pools=[]))
url = reverse('horizon:project:networks:create')
res = self.client.post(url, form_data)
self.assertNoFormErrors(res)
self.assertRedirectsNoFollow(res, INDEX_URL)
@test.update_settings(
OPENSTACK_NEUTRON_NETWORK={'profile_support': 'cisco'})
def test_network_create_post_with_subnet_cidr_not_restrict_w_profile(self):
self.test_network_create_post_with_subnet_cidr_not_restrict(
test_with_profile=True)
@test.create_stubs({api.neutron: ('profile_list',
'is_extension_supported',
'subnetpool_list',)})

View File

@ -199,6 +199,8 @@ class CreateSubnetInfoAction(workflows.Action):
initial=False,
required=False)
check_subnet_range = True
class Meta(object):
name = _("Subnet")
help_text = _('Creates a subnet associated with the network.'
@ -268,6 +270,28 @@ class CreateSubnetInfoAction(workflows.Action):
self.fields['subnetpool'].widget = forms.HiddenInput()
self.fields['prefixlen'].widget = forms.HiddenInput()
def _check_subnet_range(self, subnet, allow_cidr):
allowed_net = netaddr.IPNetwork(allow_cidr)
return subnet in allowed_net
def _check_cidr_allowed(self, ip_version, subnet):
if not self.check_subnet_range:
return
allowed_cidr = getattr(settings, "ALLOWED_PRIVATE_SUBNET_CIDR", {})
version_str = 'ipv%s' % ip_version
allowed_ranges = allowed_cidr.get(version_str, [])
if allowed_ranges:
under_range = any(self._check_subnet_range(subnet, allowed_range)
for allowed_range in allowed_ranges)
if not under_range:
range_str = ', '.join(allowed_ranges)
msg = (_("CIDRs allowed for user private %(ip_ver)s "
"networks are %(allowed)s.") %
{'ip_ver': '%s' % version_str,
'allowed': range_str})
raise forms.ValidationError(msg)
def _check_subnet_data(self, cleaned_data, is_create=True):
cidr = cleaned_data.get('cidr')
ip_version = int(cleaned_data.get('ip_version'))
@ -295,6 +319,8 @@ class CreateSubnetInfoAction(workflows.Action):
msg = _("The subnet in the Network Address is "
"too small (/%s).") % subnet.prefixlen
self._errors['cidr'] = self.error_class([msg])
self._check_cidr_allowed(ip_version, subnet)
if not no_gateway and gateway_ip:
if netaddr.IPAddress(gateway_ip).version is not ip_version:
msg = _('Gateway IP and IP version are inconsistent.')

View File

@ -783,4 +783,16 @@ REST_API_REQUIRED_SETTINGS = ['OPENSTACK_HYPERVISOR_FEATURES',
# To allow operators to require admin users provide a search criteria first
# before loading any data into the admin views, set the following attribute to
# True
#ADMIN_FILTER_DATA_FIRST=False
#ADMIN_FILTER_DATA_FIRST=False
# Dict used to restrict user private subnet cidr range.
# An empty list means that user input will not be restricted
# for a corresponding IP version. By default, there is
# no restriction for IPv4 or IPv6. To restrict
# user private subnet cidr range set ALLOWED_PRIVATE_SUBNET_CIDR
# to something like
#ALLOWED_PRIVATE_SUBNET_CIDR = {
# 'ipv4': ['10.0.0.0/8', '192.168.0.0/16'],
# 'ipv6': ['fc00::/7']
#}
ALLOWED_PRIVATE_SUBNET_CIDR = {'ipv4': [], 'ipv6': []}

View File

@ -265,3 +265,5 @@ REST_API_SETTING_2 = 'bar'
REST_API_SECURITY = 'SECURITY'
REST_API_REQUIRED_SETTINGS = ['REST_API_SETTING_1']
REST_API_ADDITIONAL_SETTINGS = ['REST_API_SETTING_2']
ALLOWED_PRIVATE_SUBNET_CIDR = {'ipv4': [], 'ipv6': []}

View File

@ -0,0 +1,3 @@
---
features:
- Allows to restrict CIDR range for user private network <https://blueprints.launchpad.net/horizon/+spec/restrict-private-network-input>