Fix managed ingress loopback publication

This commit is contained in:
2026-08-03 19:28:10 +02:00
parent eb04804d36
commit 909862afdb
3 changed files with 43 additions and 2 deletions
@@ -176,6 +176,8 @@ Ingress acceptance streams generated configuration into Docker-managed
volumes before starting the read-only containers. It therefore also works when volumes before starting the read-only containers. It therefore also works when
an Actions job reaches a host or remote Docker daemon through a mounted socket; an Actions job reaches a host or remote Docker daemon through a mounted socket;
the drill never assumes that a job-container path is visible to that daemon. the drill never assumes that a job-container path is visible to that daemon.
The drill allocates explicit loopback-only host ports and verifies Docker's
published mappings, avoiding daemon-specific random-port shorthand behavior.
The bounded setup helper writes only generated public configuration as root so The bounded setup helper writes only generated public configuration as root so
it can initialize a new volume; the actual HAProxy process retains the image's it can initialize a new volume; the actual HAProxy process retains the image's
non-root identity and runs read-only with all capabilities dropped. non-root identity and runs read-only with all capabilities dropped.
+19
View File
@@ -60,6 +60,25 @@ class ManagedIngressDrillTests(unittest.TestCase):
self.assertNotIn("type=bind", source) self.assertNotIn("type=bind", source)
self.assertIn('"--network-alias",\n "load-balancer"', source) self.assertIn('"--network-alias",\n "load-balancer"', source)
self.assertNotIn('"127.0.0.1::8080"', source)
self.assertIn("requested_http_port", source)
self.assertIn("requested_https_port", source)
def test_published_port_reads_the_docker_mapping(self) -> None:
completed = subprocess.CompletedProcess(
[],
0,
"127.0.0.1:49152\n",
"",
)
with patch.object(INGRESS, "_run", return_value=completed) as run:
port = INGRESS._published_port("ingress", 8443)
self.assertEqual(49152, port)
self.assertEqual(
["docker", "port", "ingress", "8443/tcp"],
run.call_args.args[0],
)
if __name__ == "__main__": if __name__ == "__main__":
+22 -2
View File
@@ -7,6 +7,7 @@ import argparse
from pathlib import Path from pathlib import Path
import re import re
import shutil import shutil
import socket
import subprocess import subprocess
import sys import sys
import tempfile import tempfile
@@ -86,6 +87,16 @@ def _published_port(container: str, target: int) -> int:
raise RuntimeError(f"cannot determine published port from {output!r}") from exc raise RuntimeError(f"cannot determine published port from {output!r}") from exc
def _available_loopback_port(*, exclude: frozenset[int] = frozenset()) -> int:
for _attempt in range(10):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as listener:
listener.bind(("127.0.0.1", 0))
port = int(listener.getsockname()[1])
if port not in exclude:
return port
raise RuntimeError("cannot allocate distinct loopback ports for ingress drill")
def _curl(url: str, *, headers: bool = False) -> str: def _curl(url: str, *, headers: bool = False) -> str:
argv = ["curl", "--silent", "--show-error", "--insecure"] argv = ["curl", "--silent", "--show-error", "--insecure"]
if headers: if headers:
@@ -253,6 +264,10 @@ def main() -> int:
"/govoplan-config/Caddyfile", "/govoplan-config/Caddyfile",
] ]
) )
requested_http_port = _available_loopback_port()
requested_https_port = _available_loopback_port(
exclude=frozenset({requested_http_port})
)
ingress_command = [ ingress_command = [
"docker", "docker",
"run", "run",
@@ -269,9 +284,9 @@ def main() -> int:
"--cap-drop", "--cap-drop",
"ALL", "ALL",
"--publish", "--publish",
"127.0.0.1::8080", f"127.0.0.1:{requested_http_port}:8080/tcp",
"--publish", "--publish",
"127.0.0.1::8443", f"127.0.0.1:{requested_https_port}:8443/tcp",
"--mount", "--mount",
( (
"type=volume," "type=volume,"
@@ -291,6 +306,11 @@ def main() -> int:
_run(ingress_command) _run(ingress_command)
http_port = _published_port(ingress, 8080) http_port = _published_port(ingress, 8080)
https_port = _published_port(ingress, 8443) https_port = _published_port(ingress, 8443)
if (http_port, https_port) != (
requested_http_port,
requested_https_port,
):
raise RuntimeError("Docker published unexpected ingress ports")
body = _wait_for_https(https_port) body = _wait_for_https(https_port)
if body.strip() != "proto=https": if body.strip() != "proto=https":
raise RuntimeError(f"forwarded protocol was not normalized: {body!r}") raise RuntimeError(f"forwarded protocol was not normalized: {body!r}")