Files
computer-playbook/plugins/lookup/ca_injected.py
kevinveenbirkenbachandClaude Opus 5 09e6991076 fix(mail): trust the CA that is mounted, not a path measured on the wrong machine
roles/sys-svc-mail-msmtp/tasks/ca_bundle.yml stat'ed candidate bundle paths on
the Ansible target and wrote the winner into msmtprc. Moodle and WordSpress then
copied that file into a container, where the named path is a different file - or
none. Only update-ca-certificates populates a container's system bundle, and
that runs solely from the with-ca-trust.sh entrypoint, which compose_ca.py sets
only when 'docker image inspect' succeeded. On a fresh host the image is built
minutes later, so it never ran: job 94965457602 of run 31864448027 shows
Config.Entrypoint still moodle-entrypoint.sh while the CA sits mounted beside it,
and msmtp reporting 'The certificate issuer is unknown'.

An explicit tls_trust_file also outranks SSL_CERT_FILE, so the env injection the
override always performs could not compensate.

Name CA_TRUST.inject_cert_container when the CA is actually injected, and emit
no line at all otherwise - msmtp's own 'system' default is the right answer for
a letsencrypt relay, and it is what the container's stock bundle verifies. This
also removes a latent defect on letsencrypt: a RedHat host would have written
/etc/pki/... into a Debian container.

The removal of the probing apparatus is taken from 9a72d01eb on
fork/feature/ai-agent-employees, which diagnosed the wrong-machine measurement
correctly. Its own conclusion - drop the line entirely and rely on the trust
store - does not hold where the wrapper never runs, and in swarm it never does:
handlers/swarm.yml passes wrapper=False by design.

The three-term predicate behind all of this was hand-copied into four places.
lookup('ca_injected', application_id) is now its single source; the swarm and
compose gates and sys-svc-compose-ca's own var read it too.

compose_ca.py finally says when it degrades to mount-plus-env, naming the service
and the image it could not inspect.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-16 03:38:44 +02:00

60 lines
2.2 KiB
Python

"""Lookup `ca_injected`: whether the self-signed root CA is mounted into an
application's containers.
{{ lookup('ca_injected', application_id) }}
The CA override that bind-mounts ``CA_TRUST.inject_cert_container`` is emitted
under three conditions, and anything that reads the mounted file must ask the
same three or it will point at a path that was never mounted. The conditions
lived as four hand-copied expressions - both `sys-svc-compose` handlers,
`sys-svc-compose-ca/vars/main.yml` and the msmtp template - which is one copy
per place that can drift.
Returns a plain bool, so callers read as a predicate:
when: lookup('ca_injected', application_id)
{{ CA_TRUST.inject_cert_container if lookup('ca_injected', application_id) else ... }}
"""
from __future__ import annotations
from typing import Any
from ansible.errors import AnsibleError
from ansible.module_utils.parsing.convert_bool import boolean as _to_bool
from ansible.plugins.loader import lookup_loader
from ansible.plugins.lookup import LookupBase
from plugins.filter.has.domain import has_domain
SELF_SIGNED_MODE = "self_signed"
class LookupModule(LookupBase):
def run(
self,
terms: list[Any] | None,
variables: dict[str, Any] | None = None,
**kwargs: Any,
) -> list[bool]:
if not terms or len(terms) != 1:
raise AnsibleError(
"ca_injected lookup requires exactly one term: the application id"
)
templar = getattr(self, "_templar", None)
raw = templar.template(terms[0]) if templar is not None else terms[0]
application_id = str(raw).strip()
if not application_id:
raise AnsibleError("ca_injected lookup: application id must be non-empty")
def _run(name: str, args: list[Any]) -> Any:
return lookup_loader.get(name, loader=self._loader, templar=templar).run(
args, variables=variables or {}
)[0]
if not has_domain(_run("domains", []), application_id):
return [False]
if not _to_bool(_run("tls", [application_id, "enabled"])):
return [False]
return [_run("tls", [application_id, "mode"]) == SELF_SIGNED_MODE]