Merge ~ack/maas:1982315-use-rack-ip-if-external-dns-3.2 into maas:3.2

Proposed by Alberto Donato
Status: Merged
Approved by: Alberto Donato
Approved revision: 0b0b501a379151093776bc5b2a69ea64c462bc19
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~ack/maas:1982315-use-rack-ip-if-external-dns-3.2
Merge into: maas:3.2
Diff against target: 163 lines (+101/-21)
2 files modified
src/maasserver/compose_preseed.py (+23/-17)
src/maasserver/tests/test_compose_preseed.py (+78/-4)
Reviewer Review Type Date Requested Status
Alberto Donato (community) Approve
Review via email: mp+428997@code.launchpad.net

Commit message

LP:1982315 use rack IP instead of hostname in preseed if external DNS is used

This also makes handling of X-Forwarded-For a bit safer, since it ignores extra IPs that might come before the one from the rackd proxy (e.g. client-supplied values).

To post a comment you must log in.
Revision history for this message
Alberto Donato (ack) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/maasserver/compose_preseed.py b/src/maasserver/compose_preseed.py
2index c814a01..145efc8 100644
3--- a/src/maasserver/compose_preseed.py
4+++ b/src/maasserver/compose_preseed.py
5@@ -33,25 +33,36 @@ RSYSLOG_PORT = 5247
6 RACK_CONTROLLER_PORT = 5248
7
8
9+def _subnet_uses_dns(subnet):
10+ return (
11+ subnet is not None and not subnet.dns_servers and subnet.vlan.dhcp_on
12+ )
13+
14+
15+def _wrap_ipv6_address(ip):
16+ return str(ip) if ip_address(ip).version == 4 else f"[{ip}]"
17+
18+
19 def _get_anon_rack_host(request, rack_controller):
20- forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR")
21- if forwarded_for:
22- client_ip = forwarded_for.split(",")[0]
23- subnet = Subnet.objects.get_best_subnet_for_ip(client_ip)
24- if subnet:
25+ forwarded_ips = request.META.get("HTTP_X_FORWARDED_FOR", "").split(", ")
26+ if len(forwarded_ips) >= 2:
27+ # the reguest goes through the nginx proxy on the rack to the nginx
28+ # proxy on the region
29+ forwarded_ip = forwarded_ips[-2]
30+ subnet = Subnet.objects.get_best_subnet_for_ip(forwarded_ip)
31+ if _subnet_uses_dns(subnet):
32 internal_domain = Config.objects.get_config("maas_internal_domain")
33 return f"{get_resource_name_for_subnet(subnet)}.{internal_domain}"
34+ else:
35+ return _wrap_ipv6_address(forwarded_ip)
36 return rack_controller.fqdn if rack_controller else ""
37
38
39 def build_metadata_url(request, route, rack_controller, node=None, extra=""):
40- host = _get_anon_rack_host(request, rack_controller)
41 if node and node.boot_cluster_ip:
42- host = (
43- str(node.boot_cluster_ip)
44- if ip_address(node.boot_cluster_ip).version == 4
45- else f"[{node.boot_cluster_ip}]"
46- )
47+ host = _wrap_ipv6_address(node.boot_cluster_ip)
48+ else:
49+ host = _get_anon_rack_host(request, rack_controller)
50 return (
51 request.build_absolute_uri(route) + extra
52 if not host
53@@ -89,12 +100,7 @@ def get_apt_proxy(request, rack_controller=None, node=None):
54 remote_ip = get_remote_ip(request)
55 if remote_ip is not None:
56 subnet = Subnet.objects.get_best_subnet_for_ip(remote_ip)
57- use_dns = (
58- subnet is not None
59- and not subnet.dns_servers
60- and subnet.vlan.dhcp_on
61- )
62- if config["use_rack_proxy"] and use_dns:
63+ if config["use_rack_proxy"] and _subnet_uses_dns(subnet):
64 # Client can use the MAAS proxy on the rack controller with
65 # DNS resolution providing better HA.
66 return "http://%s.%s:%d/" % (
67diff --git a/src/maasserver/tests/test_compose_preseed.py b/src/maasserver/tests/test_compose_preseed.py
68index c5a65c1..b733447 100644
69--- a/src/maasserver/tests/test_compose_preseed.py
70+++ b/src/maasserver/tests/test_compose_preseed.py
71@@ -1273,9 +1273,12 @@ class TestBuildMetadataURL(MAASServerTestCase):
72 node.boot_cluster_ip = factory.make_ip_address()
73 node.save()
74 request = make_HttpRequest()
75- subnet = factory.make_Subnet()
76- original_ip = subnet.get_next_ip_for_allocation()
77- request.META["HTTP_X_FORWARDED_FOR"] = str(original_ip)
78+ subnet = factory.make_Subnet(dhcp_on=True, dns_servers=[])
79+ rack_proxy_ip = subnet.get_next_ip_for_allocation()
80+ region_proxy_ip = subnet.get_next_ip_for_allocation()
81+ request.META[
82+ "HTTP_X_FORWARDED_FOR"
83+ ] = f"{rack_proxy_ip}, {region_proxy_ip}"
84 route = "/MAAS"
85 Config.objects.set_config("maas_internal_domain", factory.make_name())
86 # not passing node to simulate anonymous request, i.e enlistment
87@@ -1285,4 +1288,75 @@ class TestBuildMetadataURL(MAASServerTestCase):
88 request, route, node.get_boot_rack_controller()
89 ),
90 )
91- route = "/MAAS"
92+
93+ def test_uses_rack_ipv4_if_external_dns(self):
94+ # regression test for LP:1982315
95+ node = factory.make_Node()
96+ request = make_HttpRequest()
97+ subnet = factory.make_Subnet(
98+ cidr=factory.make_ipv4_network(),
99+ dhcp_on=True,
100+ dns_servers=[factory.make_ip_address()],
101+ )
102+ rack_proxy_ip = subnet.get_next_ip_for_allocation()
103+ region_proxy_ip = subnet.get_next_ip_for_allocation()
104+ request.META[
105+ "HTTP_X_FORWARDED_FOR"
106+ ] = f"{rack_proxy_ip}, {region_proxy_ip}"
107+ self.assertEqual(
108+ f"{request.scheme}://{rack_proxy_ip}:5248/MAAS",
109+ build_metadata_url(
110+ request,
111+ "/MAAS",
112+ node.get_boot_rack_controller(),
113+ node=node,
114+ ),
115+ )
116+
117+ def test_uses_rack_ipv6_if_external_dns(self):
118+ # regression test for LP:1982315
119+ node = factory.make_Node()
120+ request = make_HttpRequest()
121+ subnet = factory.make_Subnet(
122+ cidr=factory.make_ipv6_network(),
123+ dhcp_on=True,
124+ dns_servers=[factory.make_ip_address()],
125+ )
126+ rack_proxy_ip = subnet.get_next_ip_for_allocation()
127+ region_proxy_ip = subnet.get_next_ip_for_allocation()
128+ request.META[
129+ "HTTP_X_FORWARDED_FOR"
130+ ] = f"{rack_proxy_ip}, {region_proxy_ip}"
131+ self.assertEqual(
132+ f"{request.scheme}://[{rack_proxy_ip}]:5248/MAAS",
133+ build_metadata_url(
134+ request,
135+ "/MAAS",
136+ node.get_boot_rack_controller(),
137+ node=node,
138+ ),
139+ )
140+
141+ def test_uses_rack_ip_with_multiple_forwarded_for(self):
142+ node = factory.make_Node()
143+ request = make_HttpRequest()
144+ subnet = factory.make_Subnet(
145+ cidr=factory.make_ipv4_network(),
146+ dhcp_on=True,
147+ dns_servers=[factory.make_ip_address()],
148+ )
149+ rack_proxy_ip = subnet.get_next_ip_for_allocation()
150+ region_proxy_ip = subnet.get_next_ip_for_allocation()
151+ other_ip = subnet.get_next_ip_for_allocation()
152+ request.META[
153+ "HTTP_X_FORWARDED_FOR"
154+ ] = f"{other_ip}, {rack_proxy_ip}, {region_proxy_ip}"
155+ self.assertEqual(
156+ f"{request.scheme}://{rack_proxy_ip}:5248/MAAS",
157+ build_metadata_url(
158+ request,
159+ "/MAAS",
160+ node.get_boot_rack_controller(),
161+ node=node,
162+ ),
163+ )

Subscribers

People subscribed via source and target branches