Implement supported ingress and TLS profiles
Dependency Audit / dependency-audit (push) Successful in 1m38s
Deployment Installer / deployment-installer (push) Successful in 5s
Security Audit / security-audit (push) Successful in 10m2s

This commit is contained in:
2026-08-03 01:15:06 +02:00
parent b40f1428fd
commit 2c515f73c2
12 changed files with 1174 additions and 37 deletions
+60
View File
@@ -25,7 +25,9 @@ from govoplan_deploy.bundle import ( # noqa: E402
initial_secrets,
read_env,
reconcile_runtime_environment,
render_caddy_config,
render_compose,
render_existing_proxy_contract,
render_load_balancer_config,
write_env,
)
@@ -509,6 +511,62 @@ class DeploymentInstallerTests(unittest.TestCase):
self.assertEqual(1, compose["services"]["api"]["scale"])
self.assertEqual(1, compose["services"]["web"]["scale"])
def test_managed_ingress_persists_certificate_state_and_hides_upstream(
self,
) -> None:
spec = default_spec(
profile="self-hosted",
public_url="https://govoplan.example.test",
ingress_mode="managed",
acme_email="operator@example.test",
)
compose = render_compose(spec)
ingress = compose["services"]["ingress"]
self.assertNotIn("ports", compose["services"]["load-balancer"])
self.assertEqual(
["0.0.0.0:80:8080", "0.0.0.0:443:8443"],
ingress["ports"],
)
self.assertIn("caddy-data:/data", ingress["volumes"])
self.assertIn("caddy-config:/config", ingress["volumes"])
self.assertIn("reverse_proxy load-balancer:8080", render_caddy_config(spec))
self.assertNotIn("operator@example.test", json.dumps(compose))
def test_existing_proxy_contract_and_header_trust_are_exact(self) -> None:
spec = default_spec(
profile="self-hosted",
public_url="https://govoplan.example.test",
ingress_mode="existing-proxy",
trusted_proxy_cidrs=("172.20.0.7/32",),
)
contract = render_existing_proxy_contract(spec)
load_balancer = render_load_balancer_config(spec)
self.assertEqual("http://127.0.0.1:8080", contract["upstream"])
self.assertEqual(["172.20.0.7/32"], contract["trusted_proxy_cidrs"])
self.assertIn("acl trusted_forward_proxy src 172.20.0.7/32", load_balancer)
self.assertIn("del-header X-Forwarded-Proto", load_balancer)
self.assertIn(
"del-header X-Forwarded-For unless trusted_forward_proxy", load_balancer
)
def test_ingress_rejects_unsafe_proxy_ranges_and_managed_ip_hosts(self) -> None:
with self.assertRaisesRegex(SpecError, "/24 or narrower"):
default_spec(
profile="self-hosted",
public_url="https://govoplan.example.test",
ingress_mode="existing-proxy",
trusted_proxy_cidrs=("10.0.0.0/8",),
)
with self.assertRaisesRegex(SpecError, "DNS hostname"):
default_spec(
profile="self-hosted",
public_url="https://192.0.2.10",
ingress_mode="managed",
acme_email="operator@example.test",
)
def test_disabled_redis_removes_workers_and_sets_single_process_acknowledgement(
self,
) -> None:
@@ -713,6 +771,7 @@ class DeploymentInstallerTests(unittest.TestCase):
def test_legacy_spec_defaults_new_topology_fields(self) -> None:
raw = default_spec().to_dict()
raw.pop("replicas")
raw.pop("ingress")
raw["components"].pop("load_balancer")
raw["components"]["storage"].pop("image")
@@ -722,6 +781,7 @@ class DeploymentInstallerTests(unittest.TestCase):
self.assertEqual(1, parsed.replicas.web)
self.assertEqual(1, parsed.replicas.worker)
self.assertEqual("managed", parsed.components.load_balancer.mode)
self.assertEqual("local", parsed.ingress.mode)
def test_compose_contains_no_secret_values(self) -> None:
spec = default_spec()