mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-08 11:46:35 +00:00
fix(ca_injected): give the CA to a ca_client whose own TLS is off
Funkwhale is served on onion only, so resolve_enabled returns False for its primary and it resolves no TLS mode of its own. Its api container still signs and fetches media against the object store over https, and without the root CA botocore ends the theming converge with 'SSL validation failed for https://api.seaweedfs.s3.<domain>/funkwhale/dynamic_preferences/ui__logo/... CERTIFICATE_VERIFY_FAILED: unable to get local issuer certificate'. That is the job's only fatal.
The ca_client declaration already answered an application with no domain at all on the deployment mode. Resolving no mode of its own has a second shape, an application that has a domain but whose own TLS is disabled, and the onion is exactly that. Both branches now share _declares_ca_client and _on_the_deployment_mode. An application whose own TLS is enabled keeps deciding on its own resolved mode, which test_a_ca_client_with_a_domain_follows_its_own_tls_mode still pins.
web-app-funkwhale declares ca_client on its api service, the container env.j2 hands AWS_CA_BUNDLE to.
feature/network/multi-network-serving carries 69aee158f6, which answers a ca_client on the resolved mode of the SSO issuer it calls so Synapse reaches a clearnet Keycloak. An issuer is not an object store, so the two cover different peers and a merge that keeps only one side drops the other's coverage.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
3bd0b62b97
commit
3a3e9ed2c9
3 files changed
+50
-7
No files matched your search
@@ -11,11 +11,19 @@ lived as four hand-copied expressions - both `sys-svc-compose` handlers,
|
||||
per place that can drift.
|
||||
|
||||
Those three ask whether the application is *served* over the self-signed CA.
|
||||
An application that only *calls* one has no domain of its own and would be
|
||||
An application that only *calls* one resolves no mode of its own and would be
|
||||
skipped, so a service may declare ``ca_client: true`` and be answered on the
|
||||
deployment's TLS mode alone. Without it such a container verifies against an
|
||||
empty trust store and fails every outbound call at the TLS handshake.
|
||||
|
||||
Resolving no mode of its own covers two shapes, and the declaration answers
|
||||
for both: an application with no domain at all, and one whose own TLS is
|
||||
disabled. The second is the onion case, where ``resolve_enabled`` returns
|
||||
False for a ``.onion`` primary; the application has a domain, is served
|
||||
without TLS, and still needs the CA for its outbound calls. An application
|
||||
whose own TLS *is* enabled keeps deciding on its own resolved mode, which may
|
||||
override the deployment default, so the declaration never answers for it.
|
||||
|
||||
Returns a plain bool, so callers read as a predicate:
|
||||
|
||||
when: lookup('ca_injected', application_id)
|
||||
@@ -59,22 +67,30 @@ class LookupModule(LookupBase):
|
||||
args, variables=variables or {}
|
||||
)[0]
|
||||
|
||||
if not has_domain(_run("domains", []), application_id):
|
||||
def _declares_ca_client() -> bool:
|
||||
services = _run("config", [application_id, "services", {}]) or {}
|
||||
if not any(
|
||||
return any(
|
||||
_to_bool(service.get("ca_client", False))
|
||||
for service in services.values()
|
||||
if isinstance(service, dict)
|
||||
):
|
||||
return [False]
|
||||
)
|
||||
|
||||
def _on_the_deployment_mode() -> bool:
|
||||
mode = as_str(require(variables or {}, "TLS_MODE", str))
|
||||
if mode not in AVAILABLE_FLAVORS:
|
||||
raise AnsibleError(
|
||||
f"ca_injected: TLS_MODE must be one of "
|
||||
f"{sorted(AVAILABLE_FLAVORS)}, got '{mode}'"
|
||||
)
|
||||
return [mode == SELF_SIGNED_MODE]
|
||||
return mode == SELF_SIGNED_MODE
|
||||
|
||||
if not has_domain(_run("domains", []), application_id):
|
||||
if not _declares_ca_client():
|
||||
return [False]
|
||||
return [_on_the_deployment_mode()]
|
||||
|
||||
if not _to_bool(_run("tls", [application_id, "enabled"])):
|
||||
return [False]
|
||||
if not _declares_ca_client():
|
||||
return [False]
|
||||
return [_on_the_deployment_mode()]
|
||||
return [_run("tls", [application_id, "mode"]) == SELF_SIGNED_MODE]
|
||||
@@ -37,6 +37,7 @@ api:
|
||||
path: api/v1/instance/nodeinfo/2.0
|
||||
name: funkwhale-api
|
||||
image: funkwhale/api
|
||||
ca_client: true
|
||||
version: 2.0.12
|
||||
min_storage: 588MB
|
||||
ports:
|
||||
|
||||
@@ -140,6 +140,32 @@ class TestCaInjectedLookup(unittest.TestCase):
|
||||
)
|
||||
self.assertEqual([name for name, _ in calls], ["domains", "tls", "tls"])
|
||||
|
||||
def test_an_onion_served_ca_client_gets_the_ca_on_the_deployment_mode(self):
|
||||
verdict, _ = self._resolve(
|
||||
domains={"web-app-moodle": "abc.onion"},
|
||||
enabled=False,
|
||||
mode="self_signed",
|
||||
services={"api": {"ca_client": True}},
|
||||
tls_mode="self_signed",
|
||||
)
|
||||
self.assertIs(
|
||||
verdict,
|
||||
True,
|
||||
"resolve_enabled returns False for a .onion primary, so an onion app "
|
||||
"has a domain and no TLS of its own; without the CA its outbound call "
|
||||
"to a self-signed endpoint dies at the handshake",
|
||||
)
|
||||
|
||||
def test_an_onion_served_app_without_the_declaration_still_gets_nothing(self):
|
||||
verdict, _ = self._resolve(
|
||||
domains={"web-app-moodle": "abc.onion"},
|
||||
enabled=False,
|
||||
mode="self_signed",
|
||||
services={"api": {}},
|
||||
tls_mode="self_signed",
|
||||
)
|
||||
self.assertIs(verdict, False)
|
||||
|
||||
def test_a_ca_client_gets_nothing_when_the_ca_is_not_self_signed(self):
|
||||
verdict, _ = self._resolve(
|
||||
domains={},
|
||||
|
||||
Reference in new issue
Block a user