mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-08 11:46:35 +00:00
525 lines
18 KiB
Python
525 lines
18 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_onion_consumer_gets_onion_sibling(self):
|
|
domains = {
|
|
"web-svc-cdn": ["cdn.example", "cdn.abc.onion"],
|
|
"web-app-dashboard": ["dash.abc.onion"],
|
|
}
|
|
self.assertEqual(
|
|
align_domain_to_consumer(
|
|
domains, "web-svc-cdn", "cdn.example", consumer="web-app-dashboard"
|
|
),
|
|
"cdn.abc.onion",
|
|
)
|
|
|
|
def test_align_domain_to_consumer_follows_the_rendered_vhost(self):
|
|
domains = {
|
|
"web-svc-cdn": ["cdn.example", "cdn.abc.onion"],
|
|
"web-app-wazuh": ["wazuh.example", "wazuh.abc.onion"],
|
|
}
|
|
variables = {"application_id": "web-app-wazuh", "domain": "wazuh.abc.onion"}
|
|
self.assertEqual(
|
|
align_domain_to_consumer(
|
|
domains, "web-svc-cdn", "cdn.example", variables=variables
|
|
),
|
|
"cdn.abc.onion",
|
|
)
|
|
|
|
def test_align_domain_to_consumer_ignores_a_foreign_vhost(self):
|
|
domains = {
|
|
"web-svc-cdn": ["cdn.example", "cdn.abc.onion"],
|
|
"web-app-wazuh": ["wazuh.example", "wazuh.abc.onion"],
|
|
}
|
|
variables = {"application_id": "web-app-wazuh", "domain": "other.abc.onion"}
|
|
self.assertEqual(
|
|
align_domain_to_consumer(
|
|
domains, "web-svc-cdn", "cdn.example", variables=variables
|
|
),
|
|
"cdn.example",
|
|
)
|
|
|
|
def test_align_domain_to_consumer_renders_a_templated_vhost(self):
|
|
class _FakeTemplar:
|
|
def template(self, value):
|
|
return {"{{ front_proxy_domain }}": "wazuh.abc.onion"}.get(value, value)
|
|
|
|
domains = {
|
|
"web-svc-cdn": ["cdn.example", "cdn.abc.onion"],
|
|
"web-app-wazuh": ["wazuh.example", "wazuh.abc.onion"],
|
|
}
|
|
variables = {
|
|
"application_id": "web-app-wazuh",
|
|
"domain": "{{ front_proxy_domain }}",
|
|
}
|
|
self.assertEqual(
|
|
align_domain_to_consumer(
|
|
domains,
|
|
"web-svc-cdn",
|
|
"cdn.example",
|
|
variables=variables,
|
|
templar=_FakeTemplar(),
|
|
),
|
|
"cdn.abc.onion",
|
|
)
|
|
|
|
def test_align_domain_to_consumer_never_moves_the_sso_issuer(self):
|
|
domains = {
|
|
"web-app-keycloak": ["auth.example", "auth.abc.onion"],
|
|
"web-app-wazuh": ["wazuh.example", "wazuh.abc.onion"],
|
|
}
|
|
variables = {"application_id": "web-app-wazuh", "domain": "wazuh.abc.onion"}
|
|
self.assertEqual(
|
|
align_domain_to_consumer(
|
|
domains, "web-app-keycloak", "auth.example", variables=variables
|
|
),
|
|
"auth.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()
|