|
15 | 15 | # See the License for the specific language governing permissions and
|
16 | 16 | # limitations under the License.
|
17 | 17 |
|
| 18 | +import itertools |
18 | 19 | import logging
|
19 | 20 | import os.path
|
20 | 21 | import re
|
|
23 | 24 |
|
24 | 25 | import attr
|
25 | 26 | import yaml
|
26 |
| -from netaddr import IPSet |
| 27 | +from netaddr import AddrFormatError, IPNetwork, IPSet |
27 | 28 |
|
28 | 29 | from synapse.api.room_versions import KNOWN_ROOM_VERSIONS
|
29 | 30 | from synapse.util.stringutils import parse_and_validate_server_name
|
|
40 | 41 | # in the list.
|
41 | 42 | DEFAULT_BIND_ADDRESSES = ["::", "0.0.0.0"]
|
42 | 43 |
|
| 44 | + |
| 45 | +def _6to4(network: IPNetwork) -> IPNetwork: |
| 46 | + """Convert an IPv4 network into a 6to4 IPv6 network per RFC 3056.""" |
| 47 | + |
| 48 | + # 6to4 networks consist of: |
| 49 | + # * 2002 as the first 16 bits |
| 50 | + # * The first IPv4 address in the network hex-encoded as the next 32 bits |
| 51 | + # * The new prefix length needs to include the bits from the 2002 prefix. |
| 52 | + hex_network = hex(network.first)[2:] |
| 53 | + hex_network = ("0" * (8 - len(hex_network))) + hex_network |
| 54 | + return IPNetwork( |
| 55 | + "2002:%s:%s::/%d" % (hex_network[:4], hex_network[4:], 16 + network.prefixlen,) |
| 56 | + ) |
| 57 | + |
| 58 | + |
| 59 | +def generate_ip_set( |
| 60 | + ip_addresses: Optional[Iterable[str]], |
| 61 | + extra_addresses: Optional[Iterable[str]] = None, |
| 62 | + config_path: Optional[Iterable[str]] = None, |
| 63 | +) -> IPSet: |
| 64 | + """ |
| 65 | + Generate an IPSet from a list of IP addresses or CIDRs. |
| 66 | +
|
| 67 | + Additionally, for each IPv4 network in the list of IP addresses, also |
| 68 | + includes the corresponding IPv6 networks. |
| 69 | +
|
| 70 | + This includes: |
| 71 | +
|
| 72 | + * IPv4-Compatible IPv6 Address (see RFC 4291, section 2.5.5.1) |
| 73 | + * IPv4-Mapped IPv6 Address (see RFC 4291, section 2.5.5.2) |
| 74 | + * 6to4 Address (see RFC 3056, section 2) |
| 75 | +
|
| 76 | + Args: |
| 77 | + ip_addresses: An iterable of IP addresses or CIDRs. |
| 78 | + extra_addresses: An iterable of IP addresses or CIDRs. |
| 79 | + config_path: The path in the configuration for error messages. |
| 80 | +
|
| 81 | + Returns: |
| 82 | + A new IP set. |
| 83 | + """ |
| 84 | + result = IPSet() |
| 85 | + for ip in itertools.chain(ip_addresses or (), extra_addresses or ()): |
| 86 | + try: |
| 87 | + network = IPNetwork(ip) |
| 88 | + except AddrFormatError as e: |
| 89 | + raise ConfigError( |
| 90 | + "Invalid IP range provided: %s." % (ip,), config_path |
| 91 | + ) from e |
| 92 | + result.add(network) |
| 93 | + |
| 94 | + # It is possible that these already exist in the set, but that's OK. |
| 95 | + if ":" not in str(network): |
| 96 | + result.add(IPNetwork(network).ipv6(ipv4_compatible=True)) |
| 97 | + result.add(IPNetwork(network).ipv6(ipv4_compatible=False)) |
| 98 | + result.add(_6to4(network)) |
| 99 | + |
| 100 | + return result |
| 101 | + |
| 102 | + |
| 103 | +# IP ranges that are considered private / unroutable / don't make sense. |
43 | 104 | DEFAULT_IP_RANGE_BLACKLIST = [
|
44 | 105 | # Localhost
|
45 | 106 | "127.0.0.0/8",
|
|
53 | 114 | "192.0.0.0/24",
|
54 | 115 | # Link-local networks.
|
55 | 116 | "169.254.0.0/16",
|
| 117 | + # Formerly used for 6to4 relay. |
| 118 | + "192.88.99.0/24", |
56 | 119 | # Testing networks.
|
57 | 120 | "198.18.0.0/15",
|
58 | 121 | "192.0.2.0/24",
|
|
66 | 129 | "fe80::/10",
|
67 | 130 | # Unique local addresses.
|
68 | 131 | "fc00::/7",
|
| 132 | + # Testing networks. |
| 133 | + "2001:db8::/32", |
| 134 | + # Multicast. |
| 135 | + "ff00::/8", |
| 136 | + # Site-local addresses |
| 137 | + "fec0::/10", |
69 | 138 | ]
|
70 | 139 |
|
71 | 140 | DEFAULT_ROOM_VERSION = "6"
|
@@ -294,32 +363,28 @@ def read_config(self, config, **kwargs):
|
294 | 363 | )
|
295 | 364 |
|
296 | 365 | # Attempt to create an IPSet from the given ranges
|
297 |
| - try: |
298 |
| - self.ip_range_blacklist = IPSet(ip_range_blacklist) |
299 |
| - except Exception as e: |
300 |
| - raise ConfigError("Invalid range(s) provided in ip_range_blacklist.") from e |
| 366 | + |
301 | 367 | # Always blacklist 0.0.0.0, ::
|
302 |
| - self.ip_range_blacklist.update(["0.0.0.0", "::"]) |
| 368 | + self.ip_range_blacklist = generate_ip_set( |
| 369 | + ip_range_blacklist, ["0.0.0.0", "::"], config_path=("ip_range_blacklist",) |
| 370 | + ) |
303 | 371 |
|
304 |
| - try: |
305 |
| - self.ip_range_whitelist = IPSet(config.get("ip_range_whitelist", ())) |
306 |
| - except Exception as e: |
307 |
| - raise ConfigError("Invalid range(s) provided in ip_range_whitelist.") from e |
| 372 | + self.ip_range_whitelist = generate_ip_set( |
| 373 | + config.get("ip_range_whitelist", ()), config_path=("ip_range_whitelist",) |
| 374 | + ) |
308 | 375 |
|
309 | 376 | # The federation_ip_range_blacklist is used for backwards-compatibility
|
310 | 377 | # and only applies to federation and identity servers. If it is not given,
|
311 | 378 | # default to ip_range_blacklist.
|
312 | 379 | federation_ip_range_blacklist = config.get(
|
313 | 380 | "federation_ip_range_blacklist", ip_range_blacklist
|
314 | 381 | )
|
315 |
| - try: |
316 |
| - self.federation_ip_range_blacklist = IPSet(federation_ip_range_blacklist) |
317 |
| - except Exception as e: |
318 |
| - raise ConfigError( |
319 |
| - "Invalid range(s) provided in federation_ip_range_blacklist." |
320 |
| - ) from e |
321 | 382 | # Always blacklist 0.0.0.0, ::
|
322 |
| - self.federation_ip_range_blacklist.update(["0.0.0.0", "::"]) |
| 383 | + self.federation_ip_range_blacklist = generate_ip_set( |
| 384 | + federation_ip_range_blacklist, |
| 385 | + ["0.0.0.0", "::"], |
| 386 | + config_path=("federation_ip_range_blacklist",), |
| 387 | + ) |
323 | 388 |
|
324 | 389 | self.start_pushers = config.get("start_pushers", True)
|
325 | 390 |
|
|
0 commit comments