mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-08 11:46:35 +00:00
Some servers build every absolute URL from one site URL, so a second
network cannot be answered under its own origin. reachability.handover
keeps the siblings but turns each one outside the canonical network into a
302 to the same path on the canonical URL (basic and ws_generic vhosts).
The redirect target comes from the canonical domain, not the application
id, because lookup('tls', application_id) follows the network of the vhost
being rendered and would point the sibling at itself.
The flag also drops Onion-Location for the role (Tor Browser would bounce
between the two), skips those siblings in the CSP crawl, expects 302 from
the webserver health check and keeps Playwright on the canonical domain.
The key needs a nocheck reason like single_mode.
Mattermost opts in: it builds every OAuth callback from its one SiteURL, so
an onion login came back on the clearnet callback and failed with 400.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
196 lines
6.8 KiB
Python
196 lines
6.8 KiB
Python
"""Contract of the network registry and the per-role reachability resolution."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import unittest
|
|
|
|
from utils.domains.default_primary import default_domain_primary
|
|
from utils.networks.reachability import (
|
|
CLEARNET,
|
|
MULTI,
|
|
TOR,
|
|
allowed_networks,
|
|
browser_proxy_bypass,
|
|
effective_networks,
|
|
is_network,
|
|
network,
|
|
network_of,
|
|
resolve_node_mode,
|
|
role_handover,
|
|
role_reachability,
|
|
sibling_domain,
|
|
)
|
|
|
|
ONION = "abcdefghijklmnopqrstuvwxyz234567abcdefghijklmnopqrstuvw.onion"
|
|
DOMAIN = default_domain_primary()
|
|
|
|
|
|
def _cfg(**reachability) -> dict:
|
|
return {"networks": {"reachability": reachability}} if reachability else {}
|
|
|
|
|
|
class TestBrowserProxyBypass(unittest.TestCase):
|
|
HOSTS = (f"auth.{DOMAIN}", f"cdn.{DOMAIN}.", DOMAIN, f"cdn.{ONION}", "")
|
|
|
|
def test_an_onion_probe_on_a_multi_node_reaches_the_clearnet_hosts_directly(self):
|
|
self.assertEqual(
|
|
browser_proxy_bypass(TOR, MULTI, self.HOSTS),
|
|
sorted([f"auth.{DOMAIN}", f"cdn.{DOMAIN}", DOMAIN]),
|
|
)
|
|
|
|
def test_a_tor_only_node_sends_nothing_around_the_proxy(self):
|
|
self.assertEqual(browser_proxy_bypass(TOR, TOR, self.HOSTS), [])
|
|
|
|
def test_a_probe_without_a_proxy_bypasses_nothing(self):
|
|
self.assertEqual(browser_proxy_bypass(CLEARNET, MULTI, self.HOSTS), [])
|
|
|
|
|
|
class TestNetworkOf(unittest.TestCase):
|
|
def test_onion_suffix_is_tor(self) -> None:
|
|
self.assertEqual(network_of(f"wazuh.{ONION}"), TOR)
|
|
|
|
def test_trailing_dot_and_case_are_ignored(self) -> None:
|
|
self.assertEqual(network_of(f"Wazuh.{ONION.upper()}."), TOR)
|
|
|
|
def test_everything_else_is_clearnet(self) -> None:
|
|
self.assertEqual(network_of(f"wazuh.{DOMAIN}"), CLEARNET)
|
|
|
|
def test_is_network(self) -> None:
|
|
self.assertTrue(is_network(ONION, TOR))
|
|
self.assertFalse(is_network(DOMAIN, TOR))
|
|
|
|
def test_reserved_network_is_refused(self) -> None:
|
|
with self.assertRaises(ValueError):
|
|
network("handshake")
|
|
|
|
def test_unknown_network_is_refused(self) -> None:
|
|
with self.assertRaises(ValueError):
|
|
network("i2p")
|
|
|
|
|
|
class TestSiblingDomain(unittest.TestCase):
|
|
def test_clearnet_subdomain_to_tor(self) -> None:
|
|
self.assertEqual(
|
|
sibling_domain(f"wazuh.{DOMAIN}", TOR, DOMAIN, ONION),
|
|
f"wazuh.{ONION}",
|
|
)
|
|
|
|
def test_apex_to_tor_is_the_node_onion(self) -> None:
|
|
self.assertEqual(sibling_domain(DOMAIN, TOR, DOMAIN, ONION), ONION)
|
|
|
|
def test_tor_subdomain_to_clearnet(self) -> None:
|
|
self.assertEqual(
|
|
sibling_domain(f"wazuh.{ONION}", CLEARNET, DOMAIN, ONION),
|
|
f"wazuh.{DOMAIN}",
|
|
)
|
|
|
|
def test_same_network_returns_itself(self) -> None:
|
|
self.assertEqual(
|
|
sibling_domain(f"wazuh.{DOMAIN}", CLEARNET, DOMAIN, ONION),
|
|
f"wazuh.{DOMAIN}",
|
|
)
|
|
|
|
def test_foreign_domain_has_no_sibling(self) -> None:
|
|
self.assertIsNone(sibling_domain("example.org", TOR, DOMAIN, ONION))
|
|
|
|
def test_no_node_address_has_no_sibling(self) -> None:
|
|
self.assertIsNone(sibling_domain(f"wazuh.{DOMAIN}", TOR, DOMAIN, ""))
|
|
|
|
|
|
class TestResolveNodeMode(unittest.TestCase):
|
|
def test_empty_derives_multi_with_tor(self) -> None:
|
|
self.assertEqual(resolve_node_mode("", tor_provided=True), MULTI)
|
|
|
|
def test_empty_derives_clearnet_without_tor(self) -> None:
|
|
self.assertEqual(resolve_node_mode(None, tor_provided=False), CLEARNET)
|
|
|
|
def test_explicit_tor(self) -> None:
|
|
self.assertEqual(resolve_node_mode("tor", tor_provided=True), TOR)
|
|
|
|
def test_tor_mode_without_provider_fails(self) -> None:
|
|
with self.assertRaises(ValueError):
|
|
resolve_node_mode("multi", tor_provided=False)
|
|
|
|
def test_unknown_mode_fails(self) -> None:
|
|
with self.assertRaises(ValueError):
|
|
resolve_node_mode("dual", tor_provided=True)
|
|
|
|
|
|
class TestRoleReachability(unittest.TestCase):
|
|
def test_missing_means_every_network(self) -> None:
|
|
self.assertEqual(role_reachability({}), ((), False))
|
|
|
|
def test_empty_list_means_every_network(self) -> None:
|
|
self.assertEqual(role_reachability(_cfg(modes=[])), ((), False))
|
|
|
|
def test_multi_is_not_a_list_value(self) -> None:
|
|
with self.assertRaises(ValueError):
|
|
role_reachability(_cfg(modes=[CLEARNET, MULTI]))
|
|
|
|
def test_single_mode_must_be_boolean(self) -> None:
|
|
with self.assertRaises(TypeError):
|
|
role_reachability(_cfg(single_mode="yes"))
|
|
|
|
def test_handover_defaults_to_false(self) -> None:
|
|
self.assertFalse(role_handover({}))
|
|
self.assertTrue(role_handover(_cfg(handover=True)))
|
|
|
|
def test_handover_must_be_boolean(self) -> None:
|
|
with self.assertRaises(TypeError):
|
|
role_handover(_cfg(handover="yes"))
|
|
|
|
def test_tor_disabled_role_loses_tor(self) -> None:
|
|
self.assertEqual(allowed_networks({}, tor_enabled=False), ((CLEARNET,), False))
|
|
|
|
|
|
class TestEffectiveNetworks(unittest.TestCase):
|
|
"""Every combination of node mode, ``modes`` and ``single_mode``."""
|
|
|
|
CASES = (
|
|
(CLEARNET, (), False, (CLEARNET,)),
|
|
(TOR, (), False, (TOR,)),
|
|
(MULTI, (), False, (CLEARNET, TOR)),
|
|
(CLEARNET, (), True, (CLEARNET,)),
|
|
(TOR, (), True, (TOR,)),
|
|
(MULTI, (), True, (TOR,)),
|
|
(CLEARNET, (CLEARNET,), False, (CLEARNET,)),
|
|
(MULTI, (CLEARNET,), False, (CLEARNET,)),
|
|
(MULTI, (CLEARNET,), True, (CLEARNET,)),
|
|
(TOR, (TOR,), False, (TOR,)),
|
|
(MULTI, (TOR,), False, (TOR,)),
|
|
(MULTI, (CLEARNET, TOR), False, (CLEARNET, TOR)),
|
|
(MULTI, (CLEARNET, TOR), True, (TOR,)),
|
|
)
|
|
|
|
FAILURES = (
|
|
(TOR, (CLEARNET,), False),
|
|
(TOR, (CLEARNET,), True),
|
|
(CLEARNET, (TOR,), False),
|
|
(CLEARNET, (TOR,), True),
|
|
)
|
|
|
|
def test_served_networks(self) -> None:
|
|
for node_mode, modes, single, expected in self.CASES:
|
|
allowed, parsed_single = allowed_networks(
|
|
_cfg(modes=list(modes), single_mode=single), tor_enabled=True
|
|
)
|
|
with self.subTest(node=node_mode, modes=modes, single=single):
|
|
self.assertEqual(
|
|
effective_networks(node_mode, allowed, parsed_single), expected
|
|
)
|
|
|
|
def test_mismatch_fails_loudly(self) -> None:
|
|
for node_mode, modes, single in self.FAILURES:
|
|
allowed, parsed_single = allowed_networks(
|
|
_cfg(modes=list(modes), single_mode=single), tor_enabled=True
|
|
)
|
|
with (
|
|
self.subTest(node=node_mode, modes=modes, single=single),
|
|
self.assertRaisesRegex(ValueError, "web-app-x"),
|
|
):
|
|
effective_networks(node_mode, allowed, parsed_single, app="web-app-x")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|