mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-07 03:15:56 +00:00
resolve_term follows a redirect source to its target so an alias inherits the TLS settings of the application it points at. A redirect to a domain another host serves ends that chain on a domain this host does not know, and the tls and cert lookups failed: galaxyserver full-1 stopped in web-opt-rdr-domains on 'tls: domain project.infinito.nexus not found' for its project.veen.world -> project.infinito.nexus redirect. When the chain ends outside the host's domains and web-opt-rdr-domains is deployed, the source now resolves to web-opt-rdr-domains with the source itself as primary domain, which is the role that serves its vhost and whose certificate bundle names it. Only the outermost call falls back, so the source asked for stays the primary domain across a longer chain; without the redirect role, or for a target this host serves, nothing changes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
450 lines
16 KiB
Python
450 lines
16 KiB
Python
import importlib
|
|
import sys
|
|
import unittest
|
|
|
|
from ansible.errors import AnsibleError
|
|
|
|
from utils.tls_common import (
|
|
AVAILABLE_FLAVORS,
|
|
align_domain_to_consumer,
|
|
as_str,
|
|
collect_domains_for_app,
|
|
collect_domains_global,
|
|
get_path,
|
|
is_domain_term,
|
|
is_onion_domain,
|
|
norm_domain,
|
|
override_san_list,
|
|
redirect_targets,
|
|
require,
|
|
resolve_app_id_from_domain,
|
|
resolve_enabled,
|
|
resolve_le_name,
|
|
resolve_mode,
|
|
resolve_primary_domain_from_app,
|
|
resolve_term,
|
|
uniq_preserve,
|
|
want_get,
|
|
)
|
|
|
|
_tls_common = importlib.import_module("utils.tls_common")
|
|
|
|
sys.modules.setdefault("ansible.module_utils.tls_common", _tls_common)
|
|
|
|
|
|
class TestTlsCommon(unittest.TestCase):
|
|
def setUp(self):
|
|
self.domains = {
|
|
"web-app-a": "a.example",
|
|
"web-app-b": ["b.example", "b-alt.example"],
|
|
"web-app-c": {"primary": "c.example", "api": "api.c.example"},
|
|
}
|
|
|
|
def test_as_str_and_norm_domain(self):
|
|
self.assertEqual(as_str(" x "), "x")
|
|
self.assertEqual(norm_domain(" A.Example "), "a.example")
|
|
|
|
def test_require_success_and_fail(self):
|
|
v = {"X": 1}
|
|
self.assertEqual(require(v, "X", int), 1)
|
|
with self.assertRaises(AnsibleError):
|
|
require(v, "MISSING", int)
|
|
with self.assertRaises(AnsibleError):
|
|
require(v, "X", str)
|
|
|
|
def test_get_path(self):
|
|
data = {"a": {"b": {"c": 1}}}
|
|
self.assertEqual(get_path(data, "a.b.c"), 1)
|
|
self.assertIsNone(get_path(data, "a.b.x"))
|
|
self.assertEqual(get_path(data, "a.b.x", 42), 42)
|
|
|
|
def test_want_get(self):
|
|
data = {"a": {"b": 1}}
|
|
self.assertEqual(want_get(data, "a.b"), 1)
|
|
with self.assertRaises(AnsibleError):
|
|
want_get(data, "a.x")
|
|
|
|
def test_uniq_preserve_normalizes_and_dedupes(self):
|
|
items = ["A.EXAMPLE", "a.example", "b.example", "B.EXAMPLE", " "]
|
|
self.assertEqual(uniq_preserve(items), ["a.example", "b.example"])
|
|
|
|
def test_resolve_primary_domain_from_app(self):
|
|
self.assertEqual(
|
|
resolve_primary_domain_from_app(self.domains, "web-app-a", err_prefix="t"),
|
|
"a.example",
|
|
)
|
|
self.assertEqual(
|
|
resolve_primary_domain_from_app(self.domains, "web-app-b", err_prefix="t"),
|
|
"b.example",
|
|
)
|
|
self.assertEqual(
|
|
resolve_primary_domain_from_app(self.domains, "web-app-c", err_prefix="t"),
|
|
"c.example",
|
|
)
|
|
with self.assertRaises(AnsibleError):
|
|
resolve_primary_domain_from_app(self.domains, "missing", err_prefix="t")
|
|
|
|
def test_resolve_app_id_from_domain(self):
|
|
self.assertEqual(
|
|
resolve_app_id_from_domain(self.domains, "a.example", err_prefix="t"),
|
|
"web-app-a",
|
|
)
|
|
self.assertEqual(
|
|
resolve_app_id_from_domain(self.domains, "API.C.EXAMPLE", err_prefix="t"),
|
|
"web-app-c",
|
|
)
|
|
with self.assertRaises(AnsibleError):
|
|
resolve_app_id_from_domain(self.domains, "nope.example", err_prefix="t")
|
|
|
|
def test_collect_domains_for_app(self):
|
|
self.assertEqual(
|
|
collect_domains_for_app(self.domains, "web-app-a", err_prefix="t"),
|
|
["a.example"],
|
|
)
|
|
self.assertEqual(
|
|
collect_domains_for_app(self.domains, "web-app-b", err_prefix="t"),
|
|
["b.example", "b-alt.example"],
|
|
)
|
|
self.assertEqual(
|
|
collect_domains_for_app(self.domains, "web-app-c", err_prefix="t"),
|
|
["c.example", "api.c.example"],
|
|
)
|
|
|
|
def test_collect_domains_global(self):
|
|
got = collect_domains_global(self.domains)
|
|
self.assertEqual(
|
|
got,
|
|
["a.example", "b.example", "b-alt.example", "c.example", "api.c.example"],
|
|
)
|
|
|
|
def test_resolve_term_domain_and_app(self):
|
|
app_id, primary = resolve_term(
|
|
"API.C.EXAMPLE", domains=self.domains, forced_mode="auto", err_prefix="t"
|
|
)
|
|
self.assertEqual(app_id, "web-app-c")
|
|
self.assertEqual(primary, "api.c.example")
|
|
|
|
app_id, primary = resolve_term(
|
|
"web-app-b", domains=self.domains, forced_mode="app", err_prefix="t"
|
|
)
|
|
self.assertEqual(app_id, "web-app-b")
|
|
self.assertEqual(primary, "b.example")
|
|
|
|
with self.assertRaises(AnsibleError):
|
|
resolve_term(
|
|
"x", domains=self.domains, forced_mode="invalid", err_prefix="t"
|
|
)
|
|
|
|
def test_resolve_term_redirect_source_resolves_to_target(self):
|
|
app_id, primary = resolve_term(
|
|
"B-Auto.Example",
|
|
domains=self.domains,
|
|
applications={},
|
|
redirects=redirect_targets(
|
|
{
|
|
"CURRENT_PLAY_REDIRECT_DOMAINS": [
|
|
{"source": "b-auto.example", "target": "B.example"}
|
|
]
|
|
}
|
|
),
|
|
forced_mode="auto",
|
|
err_prefix="t",
|
|
)
|
|
self.assertEqual(app_id, "web-app-b")
|
|
self.assertEqual(primary, "b.example")
|
|
|
|
def test_resolve_term_unknown_domain_still_fails_without_redirect(self):
|
|
for variables in ({}, {"CURRENT_PLAY_REDIRECT_DOMAINS": None}):
|
|
with self.assertRaises(AnsibleError):
|
|
resolve_term(
|
|
"b-auto.example",
|
|
domains=self.domains,
|
|
applications={},
|
|
redirects=redirect_targets(variables),
|
|
forced_mode="auto",
|
|
err_prefix="t",
|
|
)
|
|
|
|
def test_resolve_term_redirect_follows_a_chain(self):
|
|
app_id, primary = resolve_term(
|
|
"*.legacy.example",
|
|
domains=self.domains,
|
|
applications={},
|
|
redirects={"*.legacy.example": "two.example", "two.example": "b.example"},
|
|
forced_mode="auto",
|
|
err_prefix="t",
|
|
)
|
|
self.assertEqual((app_id, primary), ("web-app-b", "b.example"))
|
|
|
|
def test_resolve_term_redirect_to_another_host_is_served_by_the_redirect_role(self):
|
|
domains = dict(self.domains, **{"web-opt-rdr-domains": "rdr.example"})
|
|
app_id, primary = resolve_term(
|
|
"Project.Old.Example",
|
|
domains=domains,
|
|
applications={},
|
|
redirects={"project.old.example": "project.other-host.example"},
|
|
forced_mode="auto",
|
|
err_prefix="t",
|
|
)
|
|
self.assertEqual(
|
|
(app_id, primary), ("web-opt-rdr-domains", "project.old.example")
|
|
)
|
|
|
|
def test_resolve_term_foreign_chain_keeps_the_asked_source_as_primary(self):
|
|
domains = dict(self.domains, **{"web-opt-rdr-domains": "rdr.example"})
|
|
app_id, primary = resolve_term(
|
|
"one.example",
|
|
domains=domains,
|
|
applications={},
|
|
redirects={
|
|
"one.example": "two.example",
|
|
"two.example": "other-host.example",
|
|
},
|
|
forced_mode="auto",
|
|
err_prefix="t",
|
|
)
|
|
self.assertEqual((app_id, primary), ("web-opt-rdr-domains", "one.example"))
|
|
|
|
def test_resolve_term_foreign_target_fails_without_the_redirect_role(self):
|
|
with self.assertRaises(AnsibleError):
|
|
resolve_term(
|
|
"project.old.example",
|
|
domains=self.domains,
|
|
applications={},
|
|
redirects={"project.old.example": "project.other-host.example"},
|
|
forced_mode="auto",
|
|
err_prefix="t",
|
|
)
|
|
|
|
def test_resolve_term_redirect_cycle_fails(self):
|
|
with self.assertRaises(AnsibleError):
|
|
resolve_term(
|
|
"one.example",
|
|
domains=self.domains,
|
|
applications={},
|
|
redirects={"one.example": "two.example", "two.example": "one.example"},
|
|
forced_mode="auto",
|
|
err_prefix="t",
|
|
)
|
|
|
|
def test_is_domain_term(self):
|
|
self.assertTrue(is_domain_term("a.example", "auto", err_prefix="t"))
|
|
self.assertFalse(is_domain_term("web-app-a", "auto", err_prefix="t"))
|
|
self.assertTrue(is_domain_term("web-app-a", "domain", err_prefix="t"))
|
|
self.assertFalse(is_domain_term("a.example", "app", err_prefix="t"))
|
|
with self.assertRaises(AnsibleError):
|
|
is_domain_term("a.example", "other", err_prefix="t")
|
|
|
|
def test_resolve_enabled_and_mode(self):
|
|
app = {}
|
|
self.assertTrue(resolve_enabled(app, True))
|
|
self.assertFalse(resolve_enabled({"server": {"tls": {"enabled": False}}}, True))
|
|
|
|
self.assertEqual(
|
|
resolve_mode(app, True, "letsencrypt", err_prefix="t"), "letsencrypt"
|
|
)
|
|
self.assertEqual(resolve_mode(app, False, "letsencrypt", err_prefix="t"), "off")
|
|
|
|
app2 = {"server": {"tls": {"flavor": "self_signed"}}}
|
|
self.assertEqual(
|
|
resolve_mode(app2, True, "letsencrypt", err_prefix="t"), "self_signed"
|
|
)
|
|
|
|
with self.assertRaises(AnsibleError):
|
|
resolve_mode(
|
|
{"server": {"tls": {"flavor": "nope"}}},
|
|
True,
|
|
"letsencrypt",
|
|
err_prefix="t",
|
|
)
|
|
|
|
def test_is_onion_domain(self):
|
|
self.assertTrue(is_onion_domain("abc.onion"))
|
|
self.assertTrue(is_onion_domain("next.cloud.ABC123.ONION"))
|
|
self.assertFalse(is_onion_domain("example.com"))
|
|
self.assertFalse(is_onion_domain("onion.example.com"))
|
|
self.assertFalse(is_onion_domain(""))
|
|
|
|
def test_resolve_enabled_onion_always_off(self):
|
|
self.assertFalse(resolve_enabled({}, True, primary_domain="x.onion"))
|
|
self.assertFalse(
|
|
resolve_enabled(
|
|
{"server": {"tls": {"enabled": True}}},
|
|
True,
|
|
primary_domain="app.abc.onion",
|
|
)
|
|
)
|
|
self.assertTrue(resolve_enabled({}, True, primary_domain="example.com"))
|
|
|
|
def test_align_domain_to_consumer_clearnet_consumer_gets_clearnet(self):
|
|
domains = {
|
|
"web-svc-cdn": ["cdn.abc.onion", "cdn.example"],
|
|
"web-app-bigbluebutton": ["bbb.example"],
|
|
}
|
|
self.assertEqual(
|
|
align_domain_to_consumer(
|
|
domains,
|
|
"web-svc-cdn",
|
|
"cdn.abc.onion",
|
|
consumer="web-app-bigbluebutton",
|
|
),
|
|
"cdn.example",
|
|
)
|
|
|
|
def test_align_domain_to_consumer_reads_variables_application_id(self):
|
|
domains = {
|
|
"web-svc-cdn": ["cdn.abc.onion", "cdn.example"],
|
|
"web-app-bigbluebutton": ["bbb.example"],
|
|
}
|
|
self.assertEqual(
|
|
align_domain_to_consumer(
|
|
domains,
|
|
"web-svc-cdn",
|
|
"cdn.abc.onion",
|
|
variables={"application_id": "web-app-bigbluebutton"},
|
|
),
|
|
"cdn.example",
|
|
)
|
|
|
|
def test_align_domain_to_consumer_onion_consumer_unchanged(self):
|
|
domains = {
|
|
"web-svc-cdn": ["cdn.abc.onion", "cdn.example"],
|
|
"web-app-dashboard": ["dash.abc.onion"],
|
|
}
|
|
self.assertEqual(
|
|
align_domain_to_consumer(
|
|
domains, "web-svc-cdn", "cdn.abc.onion", consumer="web-app-dashboard"
|
|
),
|
|
"cdn.abc.onion",
|
|
)
|
|
|
|
def test_align_domain_to_consumer_onion_only_target_falls_back(self):
|
|
domains = {
|
|
"web-svc-cdn": ["cdn.abc.onion"],
|
|
"web-app-bigbluebutton": ["bbb.example"],
|
|
}
|
|
self.assertEqual(
|
|
align_domain_to_consumer(
|
|
domains,
|
|
"web-svc-cdn",
|
|
"cdn.abc.onion",
|
|
consumer="web-app-bigbluebutton",
|
|
),
|
|
"cdn.abc.onion",
|
|
)
|
|
|
|
def test_align_domain_to_consumer_clearnet_target_untouched(self):
|
|
domains = {
|
|
"web-svc-cdn": ["cdn.example"],
|
|
"web-app-bigbluebutton": ["bbb.example"],
|
|
}
|
|
self.assertEqual(
|
|
align_domain_to_consumer(
|
|
domains, "web-svc-cdn", "cdn.example", consumer="web-app-bigbluebutton"
|
|
),
|
|
"cdn.example",
|
|
)
|
|
|
|
def test_align_domain_to_consumer_no_or_self_or_unknown_consumer(self):
|
|
domains = {
|
|
"web-svc-cdn": ["cdn.abc.onion", "cdn.example"],
|
|
"web-app-bigbluebutton": ["bbb.example"],
|
|
}
|
|
self.assertEqual(
|
|
align_domain_to_consumer(domains, "web-svc-cdn", "cdn.abc.onion"),
|
|
"cdn.abc.onion",
|
|
)
|
|
self.assertEqual(
|
|
align_domain_to_consumer(
|
|
domains, "web-svc-cdn", "cdn.abc.onion", consumer="web-svc-cdn"
|
|
),
|
|
"cdn.abc.onion",
|
|
)
|
|
self.assertEqual(
|
|
align_domain_to_consumer(
|
|
domains, "web-svc-cdn", "cdn.abc.onion", consumer="svc-db-postgres"
|
|
),
|
|
"cdn.abc.onion",
|
|
)
|
|
|
|
def test_align_domain_to_consumer_templates_consumer(self):
|
|
class _FakeTemplar:
|
|
def template(self, value):
|
|
return {"{{ application_id }}": "web-app-bigbluebutton"}.get(
|
|
value, value
|
|
)
|
|
|
|
domains = {
|
|
"web-svc-cdn": ["cdn.abc.onion", "cdn.example"],
|
|
"web-app-bigbluebutton": ["bbb.example"],
|
|
}
|
|
self.assertEqual(
|
|
align_domain_to_consumer(
|
|
domains,
|
|
"web-svc-cdn",
|
|
"cdn.abc.onion",
|
|
variables={"application_id": "{{ application_id }}"},
|
|
templar=_FakeTemplar(),
|
|
),
|
|
"cdn.example",
|
|
)
|
|
|
|
def test_align_domain_to_consumer_dict_target_picks_clearnet_value(self):
|
|
domains = {
|
|
"web-app-matrix": {
|
|
"synapse_onion": "matrix.abc.onion",
|
|
"element_onion": "element.abc.onion",
|
|
"synapse": "matrix.example",
|
|
"element": "element.example",
|
|
},
|
|
"web-app-bigbluebutton": ["bbb.example"],
|
|
}
|
|
self.assertEqual(
|
|
align_domain_to_consumer(
|
|
domains,
|
|
"web-app-matrix",
|
|
"matrix.abc.onion",
|
|
consumer="web-app-bigbluebutton",
|
|
),
|
|
"matrix.example",
|
|
)
|
|
|
|
def test_resolve_mode_app_level_mode_over_flavor(self):
|
|
app = {"server": {"tls": {"mode": "self_signed", "flavor": "letsencrypt"}}}
|
|
self.assertEqual(
|
|
resolve_mode(app, True, "letsencrypt", err_prefix="t"), "self_signed"
|
|
)
|
|
app2 = {"server": {"tls": {"flavor": "self_signed"}}}
|
|
self.assertEqual(
|
|
resolve_mode(app2, True, "letsencrypt", err_prefix="t"), "self_signed"
|
|
)
|
|
|
|
def test_resolve_le_name(self):
|
|
app = {}
|
|
self.assertEqual(resolve_le_name(app, "x.example"), "x.example")
|
|
app2 = {"server": {"tls": {"letsencrypt_cert_name": "mycert"}}}
|
|
self.assertEqual(resolve_le_name(app2, "x.example"), "mycert")
|
|
|
|
def test_override_san_list(self):
|
|
self.assertIsNone(override_san_list({}))
|
|
self.assertEqual(
|
|
override_san_list({"server": {"tls": {"domains_san": "alt.example"}}}),
|
|
["alt.example"],
|
|
)
|
|
self.assertEqual(
|
|
override_san_list({"server": {"tls": {"domains_san": ["a", "b", ""]}}}),
|
|
["a", "b"],
|
|
)
|
|
self.assertEqual(
|
|
override_san_list({"server": {"tls": {"domains_san": {"x": "y"}}}}),
|
|
[],
|
|
)
|
|
|
|
def test_available_flavors(self):
|
|
self.assertIn("letsencrypt", AVAILABLE_FLAVORS)
|
|
self.assertIn("self_signed", AVAILABLE_FLAVORS)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|