mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-08 03:38:23 +00:00
fix(sys-svc-compose-ca): escape only the image's argv in the CA override
render_override takes a service's entrypoint and command from the output of `docker compose config`, which already prints a literal `$` as `$$`, and the image's Entrypoint and Cmd from `docker image inspect`, which are raw. It escaped the combined argv once more, so a service command reading a variable at runtime (`$$REDIS_PASSWORD` in compose.yml) reached the override as `$$$$` and the container as `$$`, which the shell expands to its PID. redis then required "<pid>REDIS_PASSWORD" and every authenticated ping failed with WRONGPASS. Only the image-derived parts are escaped now; the service's own argv is written as compose config printed it. A new test keeps a service command's `$$` intact next to an image entrypoint; the existing tests still require an image Cmd's `$VAR` to become `$$VAR`. Local Nextcloud update deploy: the redis override holds `$$`, the container Cmd `$REDIS_PASSWORD`, and the authenticated ping answers PONG on the first attempt. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
82a695257f
commit
e53a8c7d75
2 files changed
+38
-6
No files matched your search
@@ -211,8 +211,9 @@ def escape_compose_vars(argv: list[str]) -> list[str]:
|
||||
Compose escaping rule:
|
||||
- $$ => literal $ (no interpolation)
|
||||
|
||||
Therefore: replace every "$" with "$$" in exec-form argv strings that we write into
|
||||
compose override YAML (entrypoint/command).
|
||||
Therefore: replace every "$" with "$$" in the image's exec-form argv before it is
|
||||
written into compose override YAML; a service's own argv comes from `compose config`,
|
||||
which already prints a literal "$" as "$$".
|
||||
"""
|
||||
return [s.replace("$", "$$") for s in argv]
|
||||
|
||||
@@ -581,7 +582,7 @@ def render_override(
|
||||
|
||||
IMPORTANT:
|
||||
Docker Compose interpolates $VARS in YAML strings on the host.
|
||||
We escape any $ in the command argv with $$ so container-side expansion works.
|
||||
We escape any $ in the image's argv with $$ so container-side expansion works.
|
||||
"""
|
||||
out_services: dict[str, Any] = {}
|
||||
extra_ca_host = _cache_frontend_ca_host()
|
||||
@@ -677,8 +678,8 @@ def render_override(
|
||||
img_ep = normalize_entrypoint(raw_ep)
|
||||
img_cmd = normalize_cmd(raw_cmd)
|
||||
|
||||
final_ep = svc_ep or img_ep
|
||||
final_cmd = svc_cmd or img_cmd
|
||||
final_ep = svc_ep or escape_compose_vars(img_ep)
|
||||
final_cmd = svc_cmd or escape_compose_vars(img_cmd)
|
||||
|
||||
if _is_shell_form(final_ep):
|
||||
final_cmd = [_shell_payload(final_cmd)]
|
||||
@@ -687,7 +688,7 @@ def render_override(
|
||||
|
||||
if has_sh and effective_cmd:
|
||||
override_svc["entrypoint"] = [wrapper_container]
|
||||
override_svc["command"] = escape_compose_vars(effective_cmd)
|
||||
override_svc["command"] = effective_cmd
|
||||
|
||||
out_services[name] = override_svc
|
||||
|
||||
|
||||
@@ -978,6 +978,37 @@ class TestComposeCaInject(unittest.TestCase):
|
||||
["sh", "-lc", 'exec "$$CHESS_ENTRYPOINT_INT"'],
|
||||
)
|
||||
|
||||
def test_render_override_keeps_the_compose_config_escape_of_a_service_command(self):
|
||||
services = {
|
||||
"svc": {
|
||||
"image": "img:1",
|
||||
"command": ["sh", "-c", 'exec server --pass "$$SERVER_PASSWORD"'],
|
||||
}
|
||||
}
|
||||
service_to_cmd = {"svc": ["docker", "compose", "-p", "p", "-f", "compose.yml"]}
|
||||
|
||||
with patch.object(
|
||||
self.m,
|
||||
"gather_image_meta",
|
||||
return_value={"img:1": (True, ["/entry.sh"], ["serve"], True)},
|
||||
):
|
||||
doc = self.m.render_override(
|
||||
services,
|
||||
service_to_cmd,
|
||||
cwd=Path("/tmp"),
|
||||
env={},
|
||||
ca_host="/host/ca.crt",
|
||||
wrapper_host="/host/with-ca-trust.sh",
|
||||
ca_container=CA_CERT_CONTAINER,
|
||||
wrapper_container=CA_WRAPPER_CONTAINER,
|
||||
trust_name="infinito.local",
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
doc["services"]["svc"].get("command"),
|
||||
["/entry.sh", "sh", "-c", 'exec server --pass "$$SERVER_PASSWORD"'],
|
||||
)
|
||||
|
||||
def test_render_override_points_the_replacing_vars_at_the_bundle(self):
|
||||
"""A host that has a CA bundle gets the verifying vars pointed at it.
|
||||
|
||||
|
||||
Reference in new issue
Block a user