volto/policy.rs
1//! Destination policy: which targets this proxy is willing to reach.
2//!
3//! RFC 9298 §7 and RFC 9114 §4.4 both warn about the same thing from different
4//! angles: a proxy that will dial anything is a reflector, a port scanner, and a
5//! way to borrow the proxy's own source address. That last one is the sharpest —
6//! plenty of services trust `127.0.0.1` or a private range without further
7//! authentication, and the proxy is *inside* that perimeter.
8//!
9//! So the defaults deny private address space, and the operator can open it with
10//! `security.allow_private_networks` for the deployments where the point of the
11//! proxy is to reach a private network.
12//!
13//! # Normalization comes first
14//!
15//! `::ffff:127.0.0.1` is loopback wearing an IPv6 hat: the kernel routes it to
16//! 127.0.0.1, while a naive matcher sees an IPv6 address that matches none of the
17//! IPv4 rules. Every check therefore starts by canonicalizing
18//! ([`canonical`]), and the deprecated `::/96` compatible range is folded in for
19//! the same reason.
20//!
21//! # Two buckets
22//!
23//! * **Never allowed** — the unspecified address, the IPv4 broadcast address and
24//! all multicast. These are not unicast targets at all; sending to them is an
25//! amplification primitive, so `allow_private_networks` does not unlock them.
26//! * **Private** — everything RFC 6890 calls special-purpose and this proxy
27//! might actually reach: `0.0.0.0/8`, loopback, RFC 1918, link-local, shared
28//! address space, the benchmarking and documentation ranges, reserved space,
29//! 6to4 relay anycast, ULA, ORCHID, the local-use NAT64 prefix and the
30//! deprecated IPv4-compatible and IPv6 site-local spaces. Denied by default,
31//! unlocked by `allow_private_networks`.
32//!
33//! # Transition addresses are judged as IPv4
34//!
35//! The well-known NAT64 prefix, 6to4 and Teredo addresses embed an IPv4 address
36//! at a fixed place, and a host that routes them reaches exactly that address.
37//! `64:ff9b::7f00:1` is therefore 127.0.0.1 with three extra steps, and letting
38//! it past because it is syntactically a global IPv6 address would undo the
39//! whole IPv4 half of this module. `embedded_ipv4` unwraps them before any rule
40//! is applied.
41//!
42//! The local-use NAT64 prefix `64:ff9b:1::/48` (RFC 8215) is the one transition
43//! form that is not unwrapped. Its operator chooses the layout, RFC 8215 §5
44//! forbids a reader assuming one, and a guess can read a private target as
45//! public. The whole prefix sits in the private bucket instead.
46
47use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
48
49use crate::config;
50
51/// The destination rules in force for a connection.
52pub struct Policy {
53 /// Whether loopback and other private ranges may be dialled.
54 allow_private_networks: bool,
55 /// Target ports that are refused regardless of address.
56 ///
57 /// A `Vec` scanned linearly: realistic deny lists hold a handful of ports, so
58 /// a set would cost more in hashing than it saves in comparisons.
59 denied_ports: Vec<u16>,
60}
61
62impl Policy {
63 /// Builds the policy described by the `[security]` section.
64 pub fn new(security: &config::Security) -> Self {
65 Self {
66 allow_private_networks: security.allow_private_networks,
67 denied_ports: security.denied_ports.clone(),
68 }
69 }
70
71 /// Whether a target port may be reached at all.
72 ///
73 /// Checked before name resolution: it needs no address, and refusing early
74 /// means a denied port cannot be used to make the proxy run DNS lookups.
75 pub fn allows_port(&self, port: u16) -> bool {
76 !self.denied_ports.contains(&port)
77 }
78
79 /// Whether one resolved address may be dialled.
80 pub fn allows_address(&self, ip: IpAddr) -> bool {
81 let ip = canonical(ip);
82 // A transition address is judged as the IPv4 address it carries, since
83 // that is what a host routing it actually reaches. Done here rather than
84 // inside the two rules below because the verdict may be either of them:
85 // an embedded multicast or unspecified address is never allowed, an
86 // embedded private one is private.
87 let ip = embedded_ipv4(ip).map_or(ip, IpAddr::V4);
88
89 if is_never_allowed(ip) {
90 return false;
91 }
92 if is_private(ip) {
93 return self.allow_private_networks;
94 }
95
96 true
97 }
98
99 /// The subset of `addresses` this proxy may dial.
100 ///
101 /// A name that resolves to both a public and a private address keeps only the
102 /// public ones, which is what makes DNS rebinding onto loopback pointless.
103 pub fn allowed_addresses(&self, addresses: &[SocketAddr]) -> Vec<SocketAddr> {
104 addresses
105 .iter()
106 .copied()
107 .filter(|address| self.allows_address(address.ip()))
108 .collect()
109 }
110}
111
112/// Whether every resolved address is the unspecified address (`0.0.0.0` / `::`).
113///
114/// This is the shape a filtering resolver uses to say "no": ad and telemetry
115/// blockers answer a blocked name with the unspecified address rather than with
116/// NXDOMAIN. Those answers are refused by [`Policy::allows_address`] like any
117/// other unroutable target, but the refusal is routine housekeeping rather than
118/// evidence of anything — on a host whose resolver filters, it is the bulk of all
119/// refusals.
120///
121/// Callers use this to decide two things (decision D49). It picks the log level:
122/// a blackhole is an INFO, every other refusal a WARN. And it picks the answer:
123/// a blackholed name gets a 200 whose stream closes immediately, so the client
124/// sees a tunnel that opened and died — the same thing a transport without an
125/// in-band refusal channel shows it — while every other refusal keeps its 403
126/// and its RFC 9209 `destination_ip_prohibited` reason. The split exists because
127/// the blackhole decision was made by the resolver upstream, not by this proxy,
128/// and a refusal from this proxy would misattribute it.
129///
130/// The test is deliberately all-or-nothing, and deliberately narrow to the
131/// unspecified address:
132///
133/// * **All**, because a name resolving to `0.0.0.0` *and* `10.0.0.1` is not a
134/// blackhole. The private address is a real target that the policy just
135/// refused, which is exactly the SSRF-shaped evidence worth keeping loud.
136/// * **Unspecified only**, because loopback, RFC 1918 and the rest of the private
137/// space are what an attacker aims at, and a probe of them must stay visible.
138/// The unspecified address is the one entry in the deny list that reaches
139/// nothing at all.
140///
141/// An empty list is not a blackhole. It cannot arise on the paths that call this
142/// (resolution either fails or yields addresses), and "no addresses at all" is no
143/// reason to quieten a warning.
144pub fn is_dns_blackhole(addresses: &[SocketAddr]) -> bool {
145 !addresses.is_empty()
146 && addresses
147 .iter()
148 .all(|address| canonical(address.ip()).is_unspecified())
149}
150
151/// Normalizes an address into the form the kernel will actually route.
152///
153/// IPv4-mapped IPv6 (`::ffff:a.b.c.d`) becomes the IPv4 address it stands for.
154/// The deprecated IPv4-compatible range (`::a.b.c.d`, RFC 4291 §2.5.5.1) is left
155/// as IPv6 on purpose and handled by `is_private`, because mapping it to IPv4
156/// would turn `::1` into `0.0.0.1` and quietly lose the loopback meaning.
157pub fn canonical(ip: IpAddr) -> IpAddr {
158 match ip {
159 IpAddr::V6(v6) => v6.to_canonical(),
160 v4 => v4,
161 }
162}
163
164/// Addresses that are never a legitimate tunnel target.
165///
166/// Not affected by `allow_private_networks`: these are not unicast destinations,
167/// and a proxy that forwards to them is an amplifier (RFC 9298 §7).
168fn is_never_allowed(ip: IpAddr) -> bool {
169 match ip {
170 IpAddr::V4(v4) => v4.is_unspecified() || v4.is_broadcast() || v4.is_multicast(),
171 IpAddr::V6(v6) => v6.is_unspecified() || v6.is_multicast(),
172 }
173}
174
175/// Addresses only reachable when `allow_private_networks` is on.
176///
177/// The list is RFC 6890's special-purpose registry minus the entries
178/// [`is_never_allowed`] has already claimed. Everything on it is either not
179/// globally reachable or not a destination an internet-facing proxy has any
180/// business dialling, and every one of them is a way past
181/// `allow_private_networks = false` if it is missing: `100.64.0.0/10` is where a
182/// carrier-grade NAT keeps its subscribers, `198.18.0.0/15` is what some
183/// networks number their own infrastructure with, `192.0.0.0/24` holds protocol
184/// assignments including the DS-Lite `192.0.0.0/29` link, and `240.0.0.0/4` is
185/// routed inside more than one large private network in practice.
186///
187/// One entry is here despite having left the registry: `fec0::/10`, the IPv6
188/// site-local prefix RFC 3879 deprecated. New stacks treat it as global unicast,
189/// which is exactly why it is worth denying — a network that numbered its
190/// interior with it before 2004 still routes it, and nothing on the public
191/// internet answers there, so a request for it is either a mistake or a way in.
192///
193/// One entry is here because its contents cannot be read: `64:ff9b:1::/48`, the
194/// local-use NAT64 prefix (RFC 8215). It carries an IPv4 address, but the
195/// operator picks the layout and RFC 8215 §5 forbids assuming one, so the whole
196/// prefix follows the switch rather than the address it might contain
197/// ([`is_local_use_nat64`], and [`embedded_ipv4`] for the reasoning).
198///
199/// One entry is deliberately absent: `2001::/23`, RFC 6890 Table 22's IETF
200/// Protocol Assignments (from RFC 2928) — the IPv6 counterpart of the
201/// `192.0.0.0/24` this list does claim. Every one of its Source, Destination,
202/// Forwardable and Global attributes is "False\[1\]", and footnote \[1\] is "Unless
203/// allowed by a more specific allocation": inside the /23 sit allocations that
204/// are routed services rather than reserved space, `2001:1::1` and `2001:1::2`
205/// (the PCP and STUN anycast addresses) and `2001:4:112::/48` (AS112). Denying
206/// the whole block would take those with it, so it is left public and only the
207/// more-specific allocations worth denying are claimed one at a time — the
208/// benchmarking range, ORCHID and Teredo below.
209fn is_private(ip: IpAddr) -> bool {
210 match ip {
211 IpAddr::V4(v4) => {
212 let octets = v4.octets();
213
214 // `is_private` is 10/8, 172.16/12 and 192.168/16; `is_link_local` is
215 // 169.254/16.
216 // 0.0.0.0/8 — "this host on this network" (RFC 6890 Table 1, from
217 // RFC 1122). Only the all-zero address at the bottom of the block is
218 // `is_never_allowed`, and that check runs first; the rest of it is a
219 // source address a host uses before it has one, never a destination.
220 (octets[0] == 0)
221 || v4.is_loopback()
222 || v4.is_private()
223 || v4.is_link_local()
224 // 100.64.0.0/10 — shared address space (RFC 6598), i.e. the
225 // inside of a carrier-grade NAT.
226 || (octets[0] == 100 && octets[1] & 0xc0 == 64)
227 // 192.0.0.0/24 — IETF protocol assignments (RFC 6890).
228 || (octets[0] == 192 && octets[1] == 0 && octets[2] == 0)
229 // 198.18.0.0/15 — benchmarking (RFC 2544).
230 || (octets[0] == 198 && octets[1] & 0xfe == 18)
231 // 192.88.99.0/24 — 6to4 relay anycast (RFC 6890 Table 10, from
232 // RFC 3068). RFC 7526 deprecated the relays and the registry
233 // kept the entry, so the prefix now reaches whoever still
234 // announces it rather than any relay this proxy meant to use.
235 || (octets[0] == 192 && octets[1] == 88 && octets[2] == 99)
236 // 240.0.0.0/4 — reserved (RFC 1112 §4). The broadcast address at
237 // the top of it is already `is_never_allowed`, which runs first.
238 || octets[0] & 0xf0 == 240
239 || is_ipv4_documentation(octets)
240 }
241 IpAddr::V6(v6) => {
242 v6.is_loopback()
243 || is_unique_local(v6)
244 || is_unicast_link_local(v6)
245 || is_site_local(v6)
246 || is_ipv4_compatible(v6)
247 || is_ipv6_documentation(v6)
248 || is_ipv6_benchmarking(v6)
249 || is_orchid(v6)
250 || is_discard_only(v6)
251 || is_local_use_nat64(v6)
252 }
253 }
254}
255
256/// The three IPv4 documentation ranges (RFC 5737).
257///
258/// Reachable nowhere by definition, and the ranges examples and test fixtures
259/// are written with — so a request for one is a misconfiguration rather than a
260/// destination, and it should not turn into a connection attempt against
261/// whoever happens to announce them.
262fn is_ipv4_documentation(octets: [u8; 4]) -> bool {
263 // 192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24.
264 (octets[0] == 192 && octets[1] == 0 && octets[2] == 2)
265 || (octets[0] == 198 && octets[1] == 51 && octets[2] == 100)
266 || (octets[0] == 203 && octets[1] == 0 && octets[2] == 113)
267}
268
269/// `2001:db8::/32` — the IPv6 documentation range (RFC 3849).
270fn is_ipv6_documentation(v6: Ipv6Addr) -> bool {
271 let segments = v6.segments();
272 segments[0] == 0x2001 && segments[1] == 0x0db8
273}
274
275/// `2001:2::/48` — the IPv6 benchmarking range (RFC 6890 Table 24, from
276/// RFC 5180).
277///
278/// The IPv6 counterpart of `198.18.0.0/15`, and there for the same reason: it is
279/// what a lab numbers a device under test with, so a request for it is either a
280/// misconfiguration or an attempt to reach that lab.
281fn is_ipv6_benchmarking(v6: Ipv6Addr) -> bool {
282 let segments = v6.segments();
283 segments[0] == 0x2001 && segments[1] == 0x0002 && segments[2] == 0x0000
284}
285
286/// `2001:10::/28` — ORCHID (RFC 6890 Table 26, from RFC 4843).
287///
288/// An ORCHID is a hash of a host identity, not a locator: nothing forwards one,
289/// so a tunnel to it can only ever hold a slot. The registry entry carries a
290/// termination date of March 2014 and its successor prefix `2001:20::/28` is not
291/// in RFC 6890, so only the original block is claimed here.
292fn is_orchid(v6: Ipv6Addr) -> bool {
293 let segments = v6.segments();
294 segments[0] == 0x2001 && segments[1] & 0xfff0 == 0x0010
295}
296
297/// `100::/64` — the discard-only prefix (RFC 6666).
298///
299/// Traffic to it is meant to be black-holed, which makes it a way to ask this
300/// proxy to open a tunnel that can only ever hold a slot.
301fn is_discard_only(v6: Ipv6Addr) -> bool {
302 let segments = v6.segments();
303 segments[0] == 0x0100 && segments[1..4].iter().all(|segment| *segment == 0)
304}
305
306/// `64:ff9b:1::/48`, the local-use NAT64 prefix (RFC 8215).
307///
308/// An address here is a translation of some IPv4 address, and the operator
309/// chooses where in the address that IPv4 address sits. RFC 8215 §5 says a
310/// reader must not guess: "they must not make any assumptions regarding the
311/// syntax or properties of those addresses (e.g., the existence and location of
312/// embedded IPv4 addresses)". So the whole prefix is private, and it is
313/// `allow_private_networks` that decides whether it may be dialled. See
314/// [`embedded_ipv4`] for what reading a layout would cost.
315fn is_local_use_nat64(v6: Ipv6Addr) -> bool {
316 v6.octets()[..6] == [0x00, 0x64, 0xff, 0x9b, 0x00, 0x01]
317}
318
319/// The IPv4 address an IPv6 transition address carries, if it carries one.
320///
321/// A host that routes any of these reaches the embedded IPv4 address, so this is
322/// what the IPv4 rules must be applied to — otherwise `2002:0a00:0001::` is a
323/// route to 10.0.0.1 that `allow_private_networks = false` never sees.
324///
325/// The three forms that matter here:
326///
327/// * `64:ff9b::/96` — the well-known NAT64 prefix (RFC 6052 §2.1), with the
328/// address in the last 32 bits;
329/// * `2002::/16` — 6to4 (RFC 3056), with the address at bits 16-47;
330/// * `2001::/32` — Teredo (RFC 4380 §4). The client's own IPv4 address is the
331/// last 32 bits with every bit inverted, which is the one this proxy would
332/// reach.
333///
334/// The deprecated `::/96` IPv4-compatible range is deliberately **not** here:
335/// unwrapping it would turn `::1` into 0.0.0.1 and lose the loopback meaning, so
336/// [`is_ipv4_compatible`] keeps claiming it wholesale instead.
337///
338/// # The local-use prefix is refused whole rather than read
339///
340/// The well-known prefix is fixed at /96 by RFC 6052 §3.1, so there is one
341/// answer for it. `64:ff9b:1::/48` is not like that. RFC 8215 §5: "64:ff9b:1::/48
342/// is intended as a technology-agnostic and generic reservation. A network
343/// operator may freely use it in combination with any kind of IPv4/IPv6
344/// translation mechanism deployed within their network." The same section
345/// forbids reading one of these addresses: "By default, IPv6 nodes and
346/// applications must not treat IPv6 addresses within 64:ff9b:1::/48 differently
347/// from other globally scoped IPv6 addresses. In particular, they must not make
348/// any assumptions regarding the syntax or properties of those addresses (e.g.,
349/// the existence and location of embedded IPv4 addresses)". RFC 6052 §2.2
350/// allows prefix lengths of 32, 40, 48, 56, 64 and 96, and the four that are /48
351/// or longer fit inside this reservation, each with the address in a different
352/// place. Nothing in an address says which of them produced it.
353///
354/// Picking one layout judges an address deployed at another by octets no
355/// translator will send to, and the direction of that error is not always the
356/// refusing one: at the /64 layout the operator's subnet octets are read as the
357/// first half of the address, so a subnet that spells a public IPv4 prefix hides
358/// a private target from `allow_private_networks = false`. So this function
359/// reads no layout at all, and [`is_local_use_nat64`] puts the whole /48 in the
360/// private bucket instead.
361fn embedded_ipv4(ip: IpAddr) -> Option<Ipv4Addr> {
362 let IpAddr::V6(v6) = ip else {
363 return None;
364 };
365 let octets = v6.octets();
366
367 // 64:ff9b::/96
368 if octets[..4] == [0x00, 0x64, 0xff, 0x9b] && octets[4..12].iter().all(|byte| *byte == 0) {
369 return Some(Ipv4Addr::new(
370 octets[12], octets[13], octets[14], octets[15],
371 ));
372 }
373
374 // 2002::/16
375 if octets[..2] == [0x20, 0x02] {
376 return Some(Ipv4Addr::new(octets[2], octets[3], octets[4], octets[5]));
377 }
378
379 // 2001::/32
380 if octets[..4] == [0x20, 0x01, 0x00, 0x00] {
381 return Some(Ipv4Addr::new(
382 octets[12] ^ 0xff,
383 octets[13] ^ 0xff,
384 octets[14] ^ 0xff,
385 octets[15] ^ 0xff,
386 ));
387 }
388
389 None
390}
391
392/// `fc00::/7` — unique local addresses (RFC 4193).
393///
394/// Hand-rolled because `Ipv6Addr::is_unique_local` is still unstable.
395fn is_unique_local(v6: Ipv6Addr) -> bool {
396 v6.segments()[0] & 0xfe00 == 0xfc00
397}
398
399/// `fe80::/10` — link-local unicast (RFC 4291 §2.5.6).
400///
401/// Hand-rolled because `Ipv6Addr::is_unicast_link_local` is still unstable.
402fn is_unicast_link_local(v6: Ipv6Addr) -> bool {
403 v6.segments()[0] & 0xffc0 == 0xfe80
404}
405
406/// `fec0::/10` — the deprecated site-local unicast prefix (RFC 3879).
407///
408/// Adjacent to link-local and, on a network old enough to still use it, just as
409/// internal. Nothing routes it globally, so denying it costs no legitimate
410/// destination.
411fn is_site_local(v6: Ipv6Addr) -> bool {
412 v6.segments()[0] & 0xffc0 == 0xfec0
413}
414
415/// `::/96` — the deprecated IPv4-compatible range, and `::1` with it.
416///
417/// Treated as private rather than as ordinary IPv6: stacks that still honour the
418/// range route `::127.0.0.1` to loopback, which would otherwise be a second way
419/// around the IPv4 rules. The unspecified address is in here too, but
420/// [`is_never_allowed`] has already claimed it.
421fn is_ipv4_compatible(v6: Ipv6Addr) -> bool {
422 v6.octets()[..12].iter().all(|byte| *byte == 0)
423}
424
425#[cfg(test)]
426mod tests {
427 use super::*;
428
429 fn policy(allow_private: bool, denied_ports: &[u16]) -> Policy {
430 Policy::new(&config::Security {
431 allow_private_networks: allow_private,
432 denied_ports: denied_ports.to_vec(),
433 ..Default::default()
434 })
435 }
436
437 fn ip(text: &str) -> IpAddr {
438 text.parse().expect("address literal")
439 }
440
441 /// Every range the spec names, in both address families.
442 const PRIVATE: &[&str] = &[
443 // IPv4
444 // RFC 6890 Table 1, "this host on this network". 0.0.0.0 itself is in
445 // NEVER, so the representative is the address above it.
446 "0.0.0.1",
447 "0.255.255.255",
448 "127.0.0.1",
449 "127.255.255.254",
450 "10.0.0.1",
451 "10.255.255.255",
452 "172.16.0.1",
453 "172.31.255.255",
454 "192.168.0.1",
455 "192.168.255.255",
456 "169.254.169.254", // the cloud metadata address, the classic prize
457 // RFC 6598 shared address space: the inside of a carrier-grade NAT.
458 "100.64.0.0",
459 "100.127.255.255",
460 // RFC 6890 IETF protocol assignments, including the DS-Lite link.
461 "192.0.0.0",
462 "192.0.0.255",
463 // RFC 2544 benchmarking, which some networks number infrastructure with.
464 "198.18.0.0",
465 "198.19.255.255",
466 // RFC 6890 Table 10, 6to4 relay anycast, deprecated by RFC 7526.
467 "192.88.99.0",
468 "192.88.99.255",
469 // RFC 5737 documentation.
470 "192.0.2.1",
471 "198.51.100.1",
472 "203.0.113.1",
473 // RFC 1112 §4 reserved space, routed inside some large networks.
474 "240.0.0.0",
475 "255.255.255.254",
476 // IPv6
477 "::1",
478 "fc00::1",
479 "fdff::1",
480 "fe80::1",
481 "febf::1",
482 // RFC 3879 deprecated site-local, still routed on networks that predate
483 // the deprecation.
484 "fec0::1",
485 "feff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
486 // RFC 3849 documentation and the RFC 6666 discard prefix.
487 "2001:db8::1",
488 "100::1",
489 "100:0:0:0:ffff::",
490 // RFC 6890 Table 24, IPv6 benchmarking (RFC 5180).
491 "2001:2::1",
492 "2001:2:0:ffff:ffff:ffff:ffff:ffff",
493 // RFC 6890 Table 26, ORCHID (RFC 4843).
494 "2001:10::1",
495 "2001:1f:ffff:ffff:ffff:ffff:ffff:ffff",
496 ];
497
498 const PUBLIC: &[&str] = &[
499 "1.1.1.1",
500 "8.8.8.8",
501 "93.184.216.34",
502 "172.32.0.1", // just outside 172.16/12
503 "172.15.0.1", // just below it
504 "169.253.0.1", // just outside 169.254/16
505 "11.0.0.1",
506 "192.169.0.1",
507 // The neighbours of every range added for RFC 6890, on both sides.
508 "100.63.255.255", // just below 100.64/10
509 "100.128.0.0", // just above it
510 "192.0.1.1", // just above 192.0.0/24, and not 192.0.2/24
511 "198.17.255.255", // just below 198.18/15
512 "198.20.0.0", // just above it
513 "1.0.0.0", // just above 0.0.0.0/8, which has nothing below it
514 "192.88.98.255", // just below 192.88.99/24
515 "192.88.100.0", // just above it
516 "192.0.3.1", // just above 192.0.2/24
517 "198.51.101.1", // just above 198.51.100/24
518 "203.0.114.1", // just above 203.0.113/24
519 // 224/4 through 239/8 is multicast and sits in NEVER, so the last public
520 // address below the reserved block is the one at the top of 223/8.
521 "223.255.255.255",
522 "2001:4860:4860::8888",
523 "2606:4700::1111",
524 "fbff::1", // just below fc00::/7
525 "fe7f::1", // just below fe80::/10; above it fec0::/10 runs into ff00::/8
526 "2001:db9::1", // just above 2001:db8::/32
527 "100:0:0:1::1", // just outside 100::/64
528 // Teredo is 2001::/32 and is unwrapped before the rules run, so the
529 // address just below 2001:2::/48 is taken from 2001:1::/32.
530 "2001:1:ffff:ffff:ffff:ffff:ffff:ffff", // just below 2001:2::/48
531 "2001:2:1::1", // just above it
532 "2001:f:ffff:ffff:ffff:ffff:ffff:ffff", // just below 2001:10::/28
533 "2001:20::1", // just above it, ORCHIDv2
534 ];
535
536 const NEVER: &[&str] = &[
537 "0.0.0.0",
538 "255.255.255.255",
539 "224.0.0.1",
540 "239.255.255.255",
541 "::",
542 "ff02::1",
543 "ff05::1:3",
544 ];
545
546 #[test]
547 fn private_ranges_are_denied_by_default() {
548 let policy = policy(false, &[]);
549 for address in PRIVATE {
550 assert!(
551 !policy.allows_address(ip(address)),
552 "{address} must be denied by default"
553 );
554 }
555 }
556
557 #[test]
558 fn public_addresses_are_allowed() {
559 for allow_private in [false, true] {
560 let policy = policy(allow_private, &[]);
561 for address in PUBLIC {
562 assert!(
563 policy.allows_address(ip(address)),
564 "{address} must be allowed (allow_private={allow_private})"
565 );
566 }
567 }
568 }
569
570 #[test]
571 fn private_ranges_can_be_opened_up() {
572 let policy = policy(true, &[]);
573 for address in PRIVATE {
574 assert!(
575 policy.allows_address(ip(address)),
576 "{address} must be allowed once private networks are"
577 );
578 }
579 }
580
581 /// Multicast, broadcast and the unspecified address are amplification
582 /// primitives, not destinations: no switch opens them.
583 #[test]
584 fn unroutable_and_multicast_addresses_are_never_allowed() {
585 for allow_private in [false, true] {
586 let policy = policy(allow_private, &[]);
587 for address in NEVER {
588 assert!(
589 !policy.allows_address(ip(address)),
590 "{address} must never be allowed (allow_private={allow_private})"
591 );
592 }
593 }
594 }
595
596 /// The classic bypass: an IPv4 address in IPv6 clothing.
597 #[test]
598 fn ipv4_mapped_addresses_are_matched_as_ipv4() {
599 let strict = policy(false, &[]);
600
601 for address in [
602 "::ffff:127.0.0.1",
603 "::ffff:10.0.0.1",
604 "::ffff:192.168.1.1",
605 "::ffff:169.254.169.254",
606 // The same addresses written the way a resolver hands them over.
607 "::ffff:7f00:1",
608 "::ffff:a00:1",
609 ] {
610 assert!(
611 !strict.allows_address(ip(address)),
612 "{address} is loopback/private in disguise and must be denied"
613 );
614 }
615
616 // A mapped *public* address is still allowed: normalization must not turn
617 // into a blanket ban on the mapped form.
618 assert!(strict.allows_address(ip("::ffff:8.8.8.8")));
619
620 // And the mapped form follows the switch exactly like the bare form.
621 let permissive = policy(true, &[]);
622 assert!(permissive.allows_address(ip("::ffff:127.0.0.1")));
623 }
624
625 /// Every added range at both of its edges, so a mask typo cannot pass.
626 ///
627 /// The `PRIVATE`/`PUBLIC` lists above already carry these; this states the
628 /// adjacency directly, because "first address in, neighbour out" is the
629 /// property a wrong mask breaks and a list of literals does not say out loud.
630 #[test]
631 fn the_special_purpose_ranges_stop_where_they_should() {
632 let strict = policy(false, &[]);
633
634 for (last_public, first_private, last_private, first_public) in [
635 // 0.0.0.0/8 starts at the bottom of the address space, so it has no
636 // public neighbour below it and the address above it stands in for
637 // both ends. Its first address is `is_never_allowed`, which runs
638 // first, hence 0.0.0.1 as the first private one.
639 ("1.0.0.0", "0.0.0.1", "0.255.255.255", "1.0.0.0"),
640 (
641 "100.63.255.255",
642 "100.64.0.0",
643 "100.127.255.255",
644 "100.128.0.0",
645 ),
646 ("191.255.255.255", "192.0.0.0", "192.0.0.255", "192.0.1.0"),
647 (
648 "198.17.255.255",
649 "198.18.0.0",
650 "198.19.255.255",
651 "198.20.0.0",
652 ),
653 (
654 "192.88.98.255",
655 "192.88.99.0",
656 "192.88.99.255",
657 "192.88.100.0",
658 ),
659 ("192.0.1.255", "192.0.2.0", "192.0.2.255", "192.0.3.0"),
660 (
661 "198.51.99.255",
662 "198.51.100.0",
663 "198.51.100.255",
664 "198.51.101.0",
665 ),
666 (
667 "203.0.112.255",
668 "203.0.113.0",
669 "203.0.113.255",
670 "203.0.114.0",
671 ),
672 // 240/4 runs to the broadcast address, which `is_never_allowed`
673 // claims first — so there is no "first public" above it. The address
674 // just below the range is multicast for the same reason, hence
675 // 223.255.255.255 as the public neighbour.
676 (
677 "223.255.255.255",
678 "240.0.0.0",
679 "255.255.255.254",
680 "223.255.255.255",
681 ),
682 ] {
683 for public in [last_public, first_public] {
684 assert!(
685 strict.allows_address(ip(public)),
686 "{public} is outside the range and must stay reachable"
687 );
688 }
689 for private in [first_private, last_private] {
690 assert!(
691 !strict.allows_address(ip(private)),
692 "{private} is inside the range and must be denied"
693 );
694 }
695 }
696
697 // The broadcast address is inside 240/4 but `is_never_allowed` wins, so
698 // opening private space does not open it. The unspecified address sits
699 // the same way inside 0.0.0.0/8.
700 assert!(!policy(true, &[]).allows_address(ip("255.255.255.255")));
701 assert!(policy(true, &[]).allows_address(ip("240.0.0.1")));
702 assert!(!policy(true, &[]).allows_address(ip("0.0.0.0")));
703 assert!(policy(true, &[]).allows_address(ip("0.0.0.1")));
704
705 // fec0::/10 has no public neighbour on either side: below it is fe80::/10
706 // (link-local, private) and above it ff00::/8 (multicast, never), so the
707 // edges are stated against those instead. The nearest public address is
708 // the one just below fe80::/10.
709 assert!(strict.allows_address(ip("fe7f:ffff:ffff:ffff:ffff:ffff:ffff:ffff")));
710 for private in [
711 "fe80::",
712 "febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
713 "fec0::",
714 "feff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
715 ] {
716 assert!(
717 !strict.allows_address(ip(private)),
718 "{private} is inside a private range and must be denied"
719 );
720 assert!(
721 policy(true, &[]).allows_address(ip(private)),
722 "{private} must follow the switch"
723 );
724 }
725 assert!(!policy(true, &[]).allows_address(ip("ff00::")));
726
727 // The two IPv6 blocks carved out of 2001::/23. Their neighbours are
728 // picked to miss Teredo (2001::/32), which is unwrapped before any of
729 // these rules run, and ORCHIDv2 (2001:20::/28) is deliberately public:
730 // RFC 6890's registry does not carry it.
731 for (last_public, first_private, last_private, first_public) in [
732 (
733 "2001:1:ffff:ffff:ffff:ffff:ffff:ffff",
734 "2001:2::",
735 "2001:2:0:ffff:ffff:ffff:ffff:ffff",
736 "2001:2:1::",
737 ),
738 (
739 "2001:f:ffff:ffff:ffff:ffff:ffff:ffff",
740 "2001:10::",
741 "2001:1f:ffff:ffff:ffff:ffff:ffff:ffff",
742 "2001:20::",
743 ),
744 ] {
745 for public in [last_public, first_public] {
746 assert!(
747 strict.allows_address(ip(public)),
748 "{public} is outside the range and must stay reachable"
749 );
750 }
751 for private in [first_private, last_private] {
752 assert!(
753 !strict.allows_address(ip(private)),
754 "{private} is inside the range and must be denied"
755 );
756 assert!(
757 policy(true, &[]).allows_address(ip(private)),
758 "{private} must follow the switch"
759 );
760 }
761 }
762 }
763
764 /// NAT64, 6to4 and Teredo addresses are routes to an IPv4 address, so they
765 /// are judged as that address and not as the global-looking IPv6 they are
766 /// written as.
767 #[test]
768 fn transition_addresses_are_judged_by_the_ipv4_they_carry() {
769 let strict = policy(false, &[]);
770 let permissive = policy(true, &[]);
771
772 // Embedding something private: denied by default, reachable once private
773 // space is opened, exactly like the bare IPv4 address would be.
774 for address in [
775 "64:ff9b::7f00:1", // NAT64 well-known prefix, 127.0.0.1
776 "64:ff9b::a00:1", // 10.0.0.1
777 "64:ff9b::a9fe:a9fe", // 169.254.169.254, the metadata address
778 "2002:a00:1::", // 6to4, 10.0.0.1
779 "2002:7f00:1::", // 6to4, 127.0.0.1
780 "2001:0:0:0:0:0:f5ff:fffe", // Teredo, 10.0.0.1 inverted
781 ] {
782 assert!(
783 !strict.allows_address(ip(address)),
784 "{address} carries a private IPv4 address and must be denied"
785 );
786 assert!(
787 permissive.allows_address(ip(address)),
788 "{address} must follow the switch like the address it carries"
789 );
790 }
791
792 // Embedding something public stays public: unwrapping must not become a
793 // blanket ban on the transition forms.
794 for address in [
795 "64:ff9b::808:808", // NAT64, 8.8.8.8
796 "2002:808:808::", // 6to4, 8.8.8.8
797 "2001:0:0:0:0:0:f7f7:f7f7", // Teredo, 8.8.8.8 inverted
798 ] {
799 assert!(
800 strict.allows_address(ip(address)),
801 "{address} carries a public IPv4 address and must be reachable"
802 );
803 }
804
805 // And an embedded address that is never allowed stays never allowed, so
806 // the switch cannot open it.
807 for address in ["64:ff9b::", "2002::", "64:ff9b::e000:1"] {
808 for policy in [&strict, &permissive] {
809 assert!(
810 !policy.allows_address(ip(address)),
811 "{address} carries an address that is never a destination"
812 );
813 }
814 }
815 }
816
817 /// The local-use NAT64 prefix is private as a whole, at every layout.
818 ///
819 /// RFC 8215 §5 forbids assuming where the IPv4 address sits inside
820 /// `64:ff9b:1::/48`, so this proxy does not look for one. Each address below
821 /// is a translation of 10.0.0.1 at one of the RFC 6052 §2.2 layouts that fit
822 /// inside that /48, and in each of them the four octets a /48 reader takes
823 /// spell a public address instead. Reading the layout would therefore let a
824 /// private target past `allow_private_networks = false`.
825 #[test]
826 fn the_local_use_nat64_prefix_is_private_at_every_layout() {
827 let strict = policy(false, &[]);
828 let permissive = policy(true, &[]);
829
830 for (address, layout, misread) in [
831 ("64:ff9b:1:80a:0:1::", "/56", "8.10.0.0"),
832 ("64:ff9b:1:808:a:0:100:0", "/64", "8.8.10.0"),
833 ("64:ff9b:1:808:8:800:a00:1", "/96", "8.8.8.8"),
834 ] {
835 assert!(
836 !strict.allows_address(ip(address)),
837 "{address} translates 10.0.0.1 at the {layout} layout \
838 and must be denied rather than read as {misread}"
839 );
840 assert!(
841 permissive.allows_address(ip(address)),
842 "{address} is private and must follow the switch"
843 );
844 }
845 }
846
847 /// The extraction itself, address by address.
848 ///
849 /// The bucket assertions above would pass on a near-miss — 169.254.0.169 is
850 /// as link-local as 169.254.169.254 — so the exact value is asserted here.
851 #[test]
852 fn the_embedded_ipv4_is_extracted_exactly() {
853 for (address, expected) in [
854 // RFC 6052 §2.1: the well-known prefix, address in the last 32 bits.
855 ("64:ff9b::7f00:1", "127.0.0.1"),
856 ("64:ff9b::808:808", "8.8.8.8"),
857 ("64:ff9b::", "0.0.0.0"),
858 // 6to4: bits 16-47.
859 ("2002:a00:1::", "10.0.0.1"),
860 ("2002:808:808::1", "8.8.8.8"),
861 // Teredo: the last 32 bits, every bit inverted.
862 ("2001:0:0:0:0:0:f5ff:fffe", "10.0.0.1"),
863 ("2001:0:808:808:0:0:f7f7:f7f7", "8.8.8.8"),
864 ] {
865 assert_eq!(
866 embedded_ipv4(ip(address)).map(IpAddr::V4),
867 Some(ip(expected)),
868 "{address} carries {expected}"
869 );
870 }
871
872 // Everything else carries nothing: the local-use prefix whose layout must
873 // not be guessed, the ranges that merely look adjacent, and the
874 // deprecated `::/96` form that must stay whole.
875 for address in [
876 "64:ff9b:1::1", // the local-use prefix, read at no layout
877 "64:ff9b:1:a9fe:a9:fe00::", // the /48 layout, once read as 169.254.169.254
878 "64:ff9b:1:808:8:800:a00:1", // the /96 layout, once read as 8.8.8.8
879 "2001:db8::1", // documentation, not Teredo
880 "2003::1", // not 6to4
881 "64:ff9c::1", // not the NAT64 prefix
882 "64:ff9b:2::1", // outside the local-use prefix
883 "::1",
884 "::127.0.0.1",
885 "2606:4700::1111",
886 "8.8.8.8",
887 ] {
888 assert_eq!(embedded_ipv4(ip(address)), None, "{address}");
889 }
890 }
891
892 /// A Teredo address's *client* IPv4 is the inverted last 32 bits, and the
893 /// bits in between are a server address this proxy never dials — so the
894 /// verdict must come from the right half of the address.
895 #[test]
896 fn a_teredo_address_is_read_from_its_client_field() {
897 // 2001:0:<server>:<flags+port>:<client>. The server field here is a
898 // public address and the client field a private one; the verdict follows
899 // the client field.
900 let strict = policy(false, &[]);
901 assert!(!strict.allows_address(ip("2001:0:808:808:0:0:f5ff:fffe")));
902
903 // The mirror image: private-looking server field, public client field.
904 assert!(strict.allows_address(ip("2001:0:a00:1:0:0:f7f7:f7f7")));
905 }
906
907 /// The deprecated sibling of the mapped form, `::a.b.c.d`.
908 #[test]
909 fn ipv4_compatible_addresses_are_denied_by_default() {
910 let policy = policy(false, &[]);
911 for address in ["::127.0.0.1", "::10.0.0.1", "::8.8.8.8", "::0.0.0.1"] {
912 assert!(
913 !policy.allows_address(ip(address)),
914 "{address} is deprecated IPv4-compatible space and must be denied"
915 );
916 }
917 }
918
919 #[test]
920 fn canonicalization_unwraps_only_the_mapped_form() {
921 assert_eq!(canonical(ip("::ffff:127.0.0.1")), ip("127.0.0.1"));
922 assert_eq!(canonical(ip("127.0.0.1")), ip("127.0.0.1"));
923 assert_eq!(canonical(ip("2001:db8::1")), ip("2001:db8::1"));
924 // `::1` must keep its loopback meaning rather than becoming 0.0.0.1.
925 assert_eq!(canonical(ip("::1")), ip("::1"));
926 }
927
928 #[test]
929 fn denied_ports_are_refused() {
930 let policy = policy(false, &[25]);
931 assert!(!policy.allows_port(25));
932 assert!(policy.allows_port(443));
933 assert!(policy.allows_port(80));
934 }
935
936 /// Surge's UDP availability test is a DNS query through the tunnel, so 53 has
937 /// to work with a stock configuration.
938 #[test]
939 fn port_53_is_allowed_with_the_default_deny_list() {
940 let policy = Policy::new(&config::Security::default());
941 assert!(
942 policy.allows_port(53),
943 "Surge tests UDP by resolving a name"
944 );
945 assert!(!policy.allows_port(25));
946 }
947
948 /// A name that resolves to a mix keeps only what may be dialled — the reason
949 /// resolution is explicit rather than left to `TcpStream::connect`.
950 #[test]
951 fn filtering_keeps_the_dialable_addresses() {
952 let policy = policy(false, &[]);
953 let addresses: Vec<SocketAddr> = [
954 "127.0.0.1:443",
955 "8.8.8.8:443",
956 "[::1]:443",
957 "[2606:4700::1111]:443",
958 "[::ffff:10.0.0.1]:443",
959 ]
960 .iter()
961 .map(|a| a.parse().expect("socket address"))
962 .collect();
963
964 let allowed = policy.allowed_addresses(&addresses);
965 assert_eq!(
966 allowed,
967 vec![
968 "8.8.8.8:443".parse().unwrap(),
969 "[2606:4700::1111]:443".parse().unwrap()
970 ]
971 );
972 }
973
974 fn addresses(literals: &[&str]) -> Vec<SocketAddr> {
975 literals
976 .iter()
977 .map(|a| a.parse().expect("socket address"))
978 .collect()
979 }
980
981 /// The answer a filtering resolver gives, in every spelling it gives it in.
982 #[test]
983 fn an_all_unspecified_answer_is_a_blackhole() {
984 for literals in [
985 &["0.0.0.0:443"][..],
986 &["0.0.0.0:53", "0.0.0.0:53"][..],
987 &["[::]:443"][..],
988 // IPv4-mapped: unspecified once canonicalized, like everywhere else.
989 &["[::ffff:0.0.0.0]:443"][..],
990 &["0.0.0.0:443", "[::]:443", "[::ffff:0.0.0.0]:443"][..],
991 ] {
992 assert!(
993 is_dns_blackhole(&addresses(literals)),
994 "{literals:?} is a filtered answer and must be recognised as one"
995 );
996 }
997 }
998
999 /// The half of the rule that keeps SSRF probes loud: anything that is not
1000 /// *only* the unspecified address stays an ordinary policy refusal.
1001 #[test]
1002 fn private_and_mixed_answers_are_not_blackholes() {
1003 for literals in [
1004 // A private address alongside the blackhole is still a private
1005 // address, and reaching for it is the thing worth warning about.
1006 &["0.0.0.0:443", "10.0.0.1:443"][..],
1007 &["[::]:443", "[::1]:443"][..],
1008 // Pure loopback / RFC 1918 / link-local: refused, never quietly.
1009 &["127.0.0.1:443"][..],
1010 &["10.0.0.1:443", "192.168.1.1:443"][..],
1011 &["169.254.169.254:80"][..],
1012 &["[::1]:443"][..],
1013 // Broadcast and multicast are refused for their own reasons.
1014 &["255.255.255.255:443"][..],
1015 &["224.0.0.1:443"][..],
1016 // A public address is not a blackhole either, mixed in or alone.
1017 &["0.0.0.0:443", "8.8.8.8:443"][..],
1018 ] {
1019 assert!(
1020 !is_dns_blackhole(&addresses(literals)),
1021 "{literals:?} must stay an ordinary policy refusal"
1022 );
1023 }
1024 }
1025
1026 /// Unreachable on the calling paths, but "nothing resolved" is not evidence
1027 /// of filtering, so it takes the loud branch.
1028 #[test]
1029 fn no_addresses_is_not_a_blackhole() {
1030 assert!(!is_dns_blackhole(&[]));
1031 }
1032
1033 #[test]
1034 fn filtering_everything_out_yields_an_empty_list() {
1035 let policy = policy(false, &[]);
1036 let addresses: Vec<SocketAddr> = ["127.0.0.1:443", "[::ffff:127.0.0.1]:443"]
1037 .iter()
1038 .map(|a| a.parse().expect("socket address"))
1039 .collect();
1040
1041 assert!(policy.allowed_addresses(&addresses).is_empty());
1042 }
1043}