Disallow router interface out of subnet IP range
Currently, a non privileged tenant can add a router interface to a shared / external network's subnet with an IP address outside the subnet's allocation pool, creating a security risk. This patch prevents tenants who are not the subnet's owner or admin from assigning a router interface an IP address outside the subnet's allocation pool. Change-Id: I32e76a83443dd8e7d79b396499747f29b4762e92 Closes-Bug: #1757482
This commit is contained in:
parent
0dada3b26d
commit
54aa6e81cb
@ -692,6 +692,27 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase,
|
|||||||
raise n_exc.BadRequest(resource='router', msg=msg)
|
raise n_exc.BadRequest(resource='router', msg=msg)
|
||||||
return port
|
return port
|
||||||
|
|
||||||
|
def _validate_port_in_range_or_admin(self, context, subnets, port):
|
||||||
|
if context.is_admin:
|
||||||
|
return
|
||||||
|
subnets_by_id = {}
|
||||||
|
for s in subnets:
|
||||||
|
addr_set = netaddr.IPSet()
|
||||||
|
for range in s['allocation_pools']:
|
||||||
|
addr_set.add(netaddr.IPRange(netaddr.IPAddress(range['start']),
|
||||||
|
netaddr.IPAddress(range['end'])))
|
||||||
|
subnets_by_id[s['id']] = (addr_set, s['project_id'],)
|
||||||
|
for subnet_id, ip in [(fix_ip['subnet_id'], fix_ip['ip_address'],)
|
||||||
|
for fix_ip in port['fixed_ips']]:
|
||||||
|
if (ip not in subnets_by_id[subnet_id][0] and
|
||||||
|
context.project_id != subnets_by_id[subnet_id][1]):
|
||||||
|
msg = (_('Cannot add interface to router because specified '
|
||||||
|
'port %(port)s has an IP address out of the '
|
||||||
|
'allocation pool of subnet %(subnet)s, which is not '
|
||||||
|
'owned by the project making the request') %
|
||||||
|
{'port': port['id'], 'subnet': subnet_id})
|
||||||
|
raise n_exc.BadRequest(resource='router', msg=msg)
|
||||||
|
|
||||||
def _validate_router_port_info(self, context, router, port_id):
|
def _validate_router_port_info(self, context, router, port_id):
|
||||||
with db_api.autonested_transaction(context.session):
|
with db_api.autonested_transaction(context.session):
|
||||||
# check again within transaction to mitigate race
|
# check again within transaction to mitigate race
|
||||||
@ -727,6 +748,7 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase,
|
|||||||
msg = _("Cannot have multiple "
|
msg = _("Cannot have multiple "
|
||||||
"IPv4 subnets on router port")
|
"IPv4 subnets on router port")
|
||||||
raise n_exc.BadRequest(resource='router', msg=msg)
|
raise n_exc.BadRequest(resource='router', msg=msg)
|
||||||
|
self._validate_port_in_range_or_admin(context, subnets, port)
|
||||||
return port, subnets
|
return port, subnets
|
||||||
|
|
||||||
def _notify_attaching_interface(self, context, router_db, port,
|
def _notify_attaching_interface(self, context, router_db, port,
|
||||||
@ -788,6 +810,10 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase,
|
|||||||
if not subnet['gateway_ip']:
|
if not subnet['gateway_ip']:
|
||||||
msg = _('Subnet for router interface must have a gateway IP')
|
msg = _('Subnet for router interface must have a gateway IP')
|
||||||
raise n_exc.BadRequest(resource='router', msg=msg)
|
raise n_exc.BadRequest(resource='router', msg=msg)
|
||||||
|
if subnet['project_id'] != context.project_id and not context.is_admin:
|
||||||
|
msg = (_('Cannot add interface to router because subnet %s is not '
|
||||||
|
'owned by project making the request') % subnet_id)
|
||||||
|
raise n_exc.BadRequest(resource='router', msg=msg)
|
||||||
if (subnet['ip_version'] == 6 and subnet['ipv6_ra_mode'] is None and
|
if (subnet['ip_version'] == 6 and subnet['ipv6_ra_mode'] is None and
|
||||||
subnet['ipv6_address_mode'] is not None):
|
subnet['ipv6_address_mode'] is not None):
|
||||||
msg = (_('IPv6 subnet %s configured to receive RAs from an '
|
msg = (_('IPv6 subnet %s configured to receive RAs from an '
|
||||||
|
@ -1265,6 +1265,62 @@ class L3NatTestCaseBase(L3NatTestCaseMixin):
|
|||||||
expected_code=err_code,
|
expected_code=err_code,
|
||||||
tenant_id='bad_tenant')
|
tenant_id='bad_tenant')
|
||||||
|
|
||||||
|
def test_router_add_interface_by_subnet_other_tenant_subnet_returns_400(
|
||||||
|
self):
|
||||||
|
router_tenant_id = _uuid()
|
||||||
|
with self.router(tenant_id=router_tenant_id, set_context=True) as r:
|
||||||
|
with self.network(shared=True) as n:
|
||||||
|
with self.subnet(network=n) as s:
|
||||||
|
err_code = exc.HTTPBadRequest.code
|
||||||
|
self._router_interface_action('add',
|
||||||
|
r['router']['id'],
|
||||||
|
s['subnet']['id'],
|
||||||
|
None,
|
||||||
|
expected_code=err_code,
|
||||||
|
tenant_id=router_tenant_id)
|
||||||
|
|
||||||
|
def _test_router_add_interface_by_port_allocation_pool(
|
||||||
|
self, out_of_pool=False, router_action_as_admin=False,
|
||||||
|
expected_code=exc.HTTPOk.code):
|
||||||
|
router_tenant_id = _uuid()
|
||||||
|
with self.router(tenant_id=router_tenant_id, set_context=True) as r:
|
||||||
|
with self.network(shared=True) as n:
|
||||||
|
with self.subnet(network=n) as s1, (
|
||||||
|
self.subnet(network=n, cidr='fd00::/64',
|
||||||
|
ip_version=6)) as s2, (
|
||||||
|
self.subnet(network=n, cidr='fd01::/64',
|
||||||
|
ip_version=6)) as s3:
|
||||||
|
fixed_ips = [{'subnet_id': s1['subnet']['id']},
|
||||||
|
{'subnet_id': s2['subnet']['id']},
|
||||||
|
{'subnet_id': s3['subnet']['id']}]
|
||||||
|
if out_of_pool:
|
||||||
|
fixed_ips[1] = {'subnet_id': s2['subnet']['id'],
|
||||||
|
'ip_address':
|
||||||
|
s2['subnet']['gateway_ip']}
|
||||||
|
with self.port(subnet=s1, fixed_ips=fixed_ips,
|
||||||
|
tenant_id=router_tenant_id,
|
||||||
|
set_context=True) as p:
|
||||||
|
kwargs = {'expected_code': expected_code}
|
||||||
|
if not router_action_as_admin:
|
||||||
|
kwargs['tenant_id'] = router_tenant_id
|
||||||
|
self._router_interface_action(
|
||||||
|
'add', r['router']['id'], None, p['port']['id'],
|
||||||
|
**kwargs)
|
||||||
|
|
||||||
|
def test_router_add_interface_by_port_other_tenant_address_in_pool(
|
||||||
|
self):
|
||||||
|
self._test_router_add_interface_by_port_allocation_pool()
|
||||||
|
|
||||||
|
def test_router_add_interface_by_port_other_tenant_address_out_of_pool(
|
||||||
|
self):
|
||||||
|
self._test_router_add_interface_by_port_allocation_pool(
|
||||||
|
out_of_pool=True, expected_code=exc.HTTPBadRequest.code)
|
||||||
|
|
||||||
|
def test_router_add_interface_by_port_admin_address_out_of_pool(
|
||||||
|
self):
|
||||||
|
self._test_router_add_interface_by_port_allocation_pool(
|
||||||
|
out_of_pool=True, router_action_as_admin=True)
|
||||||
|
|
||||||
def test_router_add_interface_subnet_with_port_from_other_tenant(self):
|
def test_router_add_interface_subnet_with_port_from_other_tenant(self):
|
||||||
tenant_id = _uuid()
|
tenant_id = _uuid()
|
||||||
other_tenant_id = _uuid()
|
other_tenant_id = _uuid()
|
||||||
|
Loading…
Reference in New Issue
Block a user