Skip to content

Commit 8d70a80

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "allowed_cidr validation for additional_vips"
2 parents cd1646c + 60f579b commit 8d70a80

File tree

5 files changed

+107
-37
lines changed

5 files changed

+107
-37
lines changed

octavia/api/v2/controllers/listener.py

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -156,14 +156,18 @@ def _validate_insert_headers(self, insert_header_list, listener_protocol):
156156
value=headers,
157157
option='%s protocol listener.' % listener_protocol)
158158

159-
def _validate_cidr_compatible_with_vip(self, vip, allowed_cidrs):
159+
def _validate_cidr_compatible_with_vip(self, vips, allowed_cidrs):
160160
for cidr in allowed_cidrs:
161-
# Check if CIDR IP version matches VIP IP version
162-
if common_utils.is_cidr_ipv6(cidr) != common_utils.is_ipv6(vip):
163-
msg = _("CIDR %(cidr)s IP version incompatible with VIP "
164-
"%(vip)s IP version.")
161+
for vip in vips:
162+
# Check if CIDR IP version matches VIP IP version
163+
if (common_utils.is_cidr_ipv6(cidr) ==
164+
common_utils.is_ipv6(vip)):
165+
break
166+
else:
167+
msg = _("CIDR %(cidr)s IP version incompatible with all VIPs "
168+
"%(vips)s IP version.")
165169
raise exceptions.ValidationException(
166-
detail=msg % {'cidr': cidr, 'vip': vip})
170+
detail=msg % {'cidr': cidr, 'vips': vips})
167171

168172
def _validate_create_listener(self, lock_session, listener_dict):
169173
"""Validate listener for wrong protocol or duplicate listeners
@@ -306,10 +310,11 @@ def _validate_create_listener(self, lock_session, listener_dict):
306310
# Validate allowed CIDRs
307311
allowed_cidrs = listener_dict.get('allowed_cidrs', []) or []
308312
lb_id = listener_dict.get('load_balancer_id')
309-
vip_db = self.repositories.vip.get(
310-
lock_session, load_balancer_id=lb_id)
311-
vip_address = vip_db.ip_address
312-
self._validate_cidr_compatible_with_vip(vip_address, allowed_cidrs)
313+
lb_db = self.repositories.load_balancer.get(
314+
lock_session, id=lb_id)
315+
vip_addresses = [lb_db.vip.ip_address]
316+
vip_addresses.extend([vip.ip_address for vip in lb_db.additional_vips])
317+
self._validate_cidr_compatible_with_vip(vip_addresses, allowed_cidrs)
313318

314319
if _can_tls_offload:
315320
# Validate TLS version list
@@ -525,9 +530,13 @@ def _validate_listener_PUT(self, listener, db_listener):
525530

526531
# Validate allowed CIDRs
527532
if (listener.allowed_cidrs and listener.allowed_cidrs != wtypes.Unset):
528-
vip_address = db_listener.load_balancer.vip.ip_address
533+
vip_addresses = [db_listener.load_balancer.vip.ip_address]
534+
vip_addresses.extend(
535+
[vip.ip_address
536+
for vip in db_listener.load_balancer.additional_vips]
537+
)
529538
self._validate_cidr_compatible_with_vip(
530-
vip_address, listener.allowed_cidrs)
539+
vip_addresses, listener.allowed_cidrs)
531540

532541
# Check TLS cipher prohibit list
533542
if listener.tls_ciphers:

octavia/network/drivers/neutron/allowed_address_pairs.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,10 @@ def _get_ethertype_for_ip(self, ip):
147147
address = ipaddress.ip_address(ip)
148148
return 'IPv6' if address.version == 6 else 'IPv4'
149149

150+
def _get_ethertype_for_cidr(self, cidr):
151+
net = ipaddress.ip_network(cidr)
152+
return 'IPv6' if net.version == 6 else 'IPv4'
153+
150154
def _update_security_group_rules(self, load_balancer, sec_grp_id):
151155
rules = self.neutron_client.list_security_group_rules(
152156
security_group_id=sec_grp_id)
@@ -224,11 +228,15 @@ def _update_security_group_rules(self, load_balancer, sec_grp_id):
224228
ethertypes.add(self._get_ethertype_for_ip(add_vip.ip_address))
225229
for port_protocol in add_ports:
226230
for ethertype in ethertypes:
227-
self._create_security_group_rule(sec_grp_id, port_protocol[1],
228-
port_min=port_protocol[0],
229-
port_max=port_protocol[0],
230-
ethertype=ethertype,
231-
cidr=port_protocol[2])
231+
cidr = port_protocol[2]
232+
if not cidr or self._get_ethertype_for_cidr(cidr) == ethertype:
233+
self._create_security_group_rule(
234+
sec_grp_id, port_protocol[1],
235+
port_min=port_protocol[0],
236+
port_max=port_protocol[0],
237+
ethertype=ethertype,
238+
cidr=cidr,
239+
)
232240

233241
# Currently we are using the VIP network for VRRP
234242
# so we need to open up the protocols for it

octavia/tests/functional/api/v2/test_listener.py

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1207,9 +1207,9 @@ def test_create_with_bad_ca_cert(self):
12071207
"It must be a valid x509 PEM format certificate.",
12081208
response['faultstring'])
12091209

1210-
def _test_create_with_allowed_cidrs(self, allowed_cidrs):
1210+
def _test_create_with_allowed_cidrs(self, allowed_cidrs, lb_id):
12111211
listener = self.create_listener(constants.PROTOCOL_TCP,
1212-
80, self.lb_id,
1212+
80, lb_id,
12131213
allowed_cidrs=allowed_cidrs)
12141214
listener_path = self.LISTENER_PATH.format(
12151215
listener_id=listener['listener']['id'])
@@ -1218,14 +1218,17 @@ def _test_create_with_allowed_cidrs(self, allowed_cidrs):
12181218

12191219
def test_create_with_allowed_cidrs_ipv4(self):
12201220
allowed_cidrs = ['10.0.1.0/24', '172.16.55.0/25']
1221-
self._test_create_with_allowed_cidrs(allowed_cidrs)
1221+
self._test_create_with_allowed_cidrs(allowed_cidrs, self.lb_id)
12221222

12231223
def test_create_with_allowed_cidrs_ipv6(self):
1224+
lb_ipv6 = self.create_load_balancer(
1225+
uuidutils.generate_uuid(),
1226+
vip_address='2001:db9:a1b:13f0::1',
1227+
)
1228+
lb_id = lb_ipv6.get('loadbalancer').get('id')
1229+
self.set_lb_status(lb_id)
12241230
allowed_cidrs = ['2001:db8:a0b:12f0::/64', '2a02:8071:69e::/64']
1225-
with mock.patch('octavia.db.repositories.VipRepository.'
1226-
'get') as repo_mock:
1227-
repo_mock.return_value.ip_address = "2001:db9:a1b:13f0::1"
1228-
self._test_create_with_allowed_cidrs(allowed_cidrs)
1231+
self._test_create_with_allowed_cidrs(allowed_cidrs, lb_id)
12291232

12301233
def test_create_with_bad_allowed_cidrs(self):
12311234
allowed_cidrs = [u'10.0.1.0/33', u'172.16.55.1.0/25']
@@ -1251,24 +1254,42 @@ def test_create_with_incompatible_allowed_cidrs_ipv6(self):
12511254
body = self._build_body(lb_listener)
12521255
response = self.post(self.LISTENERS_PATH, body, status=400).json
12531256
self.assertIn("Validation failure: CIDR 2001:db8:a0b:12f0::/64 IP "
1254-
"version incompatible with VIP 198.0.2.5 IP version.",
1257+
"version incompatible with all VIPs ['198.0.2.5'] IP "
1258+
"version.",
12551259
response['faultstring'])
12561260

12571261
def test_create_with_incompatible_allowed_cidrs_ipv4(self):
1262+
lb_ipv6 = self.create_load_balancer(
1263+
uuidutils.generate_uuid(),
1264+
vip_address='2001:db9:a1b:13f0::1',
1265+
)
1266+
lb_id = lb_ipv6.get('loadbalancer').get('id')
1267+
self.set_lb_status(lb_id)
12581268
lb_listener = {
12591269
'protocol': constants.PROTOCOL_TCP,
12601270
'protocol_port': 80,
12611271
'project_id': self.project_id,
1262-
'loadbalancer_id': self.lb_id,
1272+
'loadbalancer_id': lb_id,
12631273
'allowed_cidrs': ['10.0.1.0/24']}
1264-
with mock.patch('octavia.db.repositories.VipRepository.'
1265-
'get') as repo_mock:
1266-
repo_mock.return_value.ip_address = "2001:db9:a1b:13f0::1"
1267-
body = self._build_body(lb_listener)
1268-
response = self.post(self.LISTENERS_PATH, body, status=400).json
1269-
self.assertIn("Validation failure: CIDR 10.0.1.0/24 IP version "
1270-
"incompatible with VIP 2001:db9:a1b:13f0::1 IP "
1271-
"version.", response['faultstring'])
1274+
body = self._build_body(lb_listener)
1275+
response = self.post(self.LISTENERS_PATH, body, status=400).json
1276+
self.assertIn("Validation failure: CIDR 10.0.1.0/24 IP version "
1277+
"incompatible with all VIPs "
1278+
"['2001:db9:a1b:13f0::1'] IP version.",
1279+
response['faultstring'])
1280+
1281+
def test_create_with_mixed_version_allowed_cidrs(self):
1282+
lb_dualstack = self.create_load_balancer(
1283+
uuidutils.generate_uuid(),
1284+
additional_vips=[{'subnet_id': uuidutils.generate_uuid(),
1285+
'ip_address': '2001:db9:a1b:13f0::1',
1286+
}],
1287+
)
1288+
lb_id = lb_dualstack.get('loadbalancer').get('id')
1289+
self.set_lb_status(lb_id)
1290+
self._test_create_with_allowed_cidrs(['10.0.1.0/24',
1291+
'2001:db9:a1b:13f0::/64'],
1292+
lb_id)
12721293

12731294
def test_create_with_duplicated_allowed_cidrs(self):
12741295
allowed_cidrs = ['10.0.1.0/24', '10.0.2.0/24', '10.0.2.0/24']

octavia/tests/unit/network/drivers/neutron/test_allowed_address_pairs.py

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1099,12 +1099,13 @@ def test_update_vip(self):
10991099
lc_1 = data_models.ListenerCidr('l1', '10.0.101.0/24')
11001100
lc_2 = data_models.ListenerCidr('l2', '10.0.102.0/24')
11011101
lc_3 = data_models.ListenerCidr('l2', '10.0.103.0/24')
1102+
lc_4 = data_models.ListenerCidr('l2', '2001:0DB8::/32')
11021103
listeners = [data_models.Listener(protocol_port=80, peer_port=1024,
11031104
protocol=constants.PROTOCOL_TCP,
11041105
allowed_cidrs=[lc_1]),
11051106
data_models.Listener(protocol_port=443, peer_port=1025,
11061107
protocol=constants.PROTOCOL_TCP,
1107-
allowed_cidrs=[lc_2, lc_3]),
1108+
allowed_cidrs=[lc_2, lc_3, lc_4]),
11081109
data_models.Listener(protocol_port=50, peer_port=1026,
11091110
protocol=constants.PROTOCOL_UDP)]
11101111
vip = data_models.Vip(ip_address='10.0.0.2')
@@ -1183,7 +1184,18 @@ def test_update_vip(self):
11831184
'remote_ip_prefix': '10.0.103.0/24'
11841185
}
11851186
}
1186-
expected_create_rule_udp = {
1187+
expected_create_rule_5 = {
1188+
'security_group_rule': {
1189+
'security_group_id': 'secgrp-1',
1190+
'direction': 'ingress',
1191+
'protocol': 'tcp',
1192+
'port_range_min': 443,
1193+
'port_range_max': 443,
1194+
'ethertype': 'IPv6',
1195+
'remote_ip_prefix': '2001:0DB8::/32'
1196+
}
1197+
}
1198+
expected_create_rule_udp_1 = {
11871199
'security_group_rule': {
11881200
'security_group_id': 'secgrp-1',
11891201
'direction': 'ingress',
@@ -1194,13 +1206,26 @@ def test_update_vip(self):
11941206
'remote_ip_prefix': None
11951207
}
11961208
}
1209+
expected_create_rule_udp_2 = {
1210+
'security_group_rule': {
1211+
'security_group_id': 'secgrp-1',
1212+
'direction': 'ingress',
1213+
'protocol': 'udp',
1214+
'port_range_min': 50,
1215+
'port_range_max': 50,
1216+
'ethertype': 'IPv6',
1217+
'remote_ip_prefix': None
1218+
}
1219+
}
11971220

11981221
create_rule.assert_has_calls([mock.call(expected_create_rule_1),
11991222
mock.call(expected_create_rule_udp_peer),
12001223
mock.call(expected_create_rule_2),
12011224
mock.call(expected_create_rule_3),
12021225
mock.call(expected_create_rule_4),
1203-
mock.call(expected_create_rule_udp)],
1226+
mock.call(expected_create_rule_5),
1227+
mock.call(expected_create_rule_udp_1),
1228+
mock.call(expected_create_rule_udp_2)],
12041229
any_order=True)
12051230

12061231
def test_update_vip_when_protocol_and_peer_ports_overlap(self):
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
fixes:
3+
- |
4+
The validation for the allowed_cidr parameter only took into account the
5+
IP version of the primary VIP. CIDRs which only matched the version of an
6+
additonal VIP were rejected. This if fixed and CIDRs are now matched
7+
against the IP version of all VIPs.

0 commit comments

Comments
 (0)