diff --git a/.changeset/issue-102-passthrough-missing-socket-warning.md b/.changeset/issue-102-passthrough-missing-socket-warning.md new file mode 100644 index 0000000..273ebaa --- /dev/null +++ b/.changeset/issue-102-passthrough-missing-socket-warning.md @@ -0,0 +1,22 @@ +--- +bump: patch +--- + +dind-box: warn when host-image passthrough is opted into but no host socket is +mounted (issue #102). + +Running `box-dind` with passthrough enabled (the default `public` mode) **and** +an explicit allowlist (`DIND_HOST_PASSTHROUGH_IMAGES=...`) but **without** the +host Docker socket bind-mounted used to be a silent no-op: the entrypoint copied +nothing, printed nothing, and the first nested `docker run` re-pulled the full +image from the registry with no hint why. Downstream this re-pulled a 30+ GB +image because of a forgotten `-v` flag (`link-assistant/hive-mind#1914`). + +A non-empty `DIND_HOST_PASSTHROUGH_IMAGES` is an unambiguous opt-in signal, so +`passthrough_host_images` now emits a single actionable warning in exactly that +case — enabled passthrough + allowlist set + no socket mounted — naming the +missing `-v /var/run/docker.sock:${DIND_HOST_DOCKER_SOCK}:ro` mount. The +present-but-unreachable socket already warned and still does; plain `box-dind` +containers that never set an allowlist stay silent so the default mode is not +spammed. Covered by new cases in `experiments/preload-unit-test.sh` and +documented in `docs/dind/USAGE.md`. diff --git a/docs/dind/USAGE.md b/docs/dind/USAGE.md index 114a898..c9f4557 100644 --- a/docs/dind/USAGE.md +++ b/docs/dind/USAGE.md @@ -263,6 +263,14 @@ gate, `public` mode still refuses a locally-built or private image even when it matches a pattern — the allowlist only ever *narrows* the eligible set, it never widens it past the security filter. +Setting `DIND_HOST_PASSTHROUGH_IMAGES` is an unambiguous "I expect these images +passed through" signal. So if it is set but **no host socket is mounted**, the +entrypoint no longer stays silent — it emits a single warning naming the +missing `-v /var/run/docker.sock:/var/run/host-docker.sock:ro` mount, because +the nested daemon will otherwise re-pull from the registry on the first +`docker run` with no hint as to why (issue #102). Plain `box-dind` containers +that never set an allowlist still see no extra noise when no socket is mounted. + ## Commit Cycles `DIND_SKIP_DAEMON=1` is useful for setup containers where you want to install or diff --git a/experiments/preload-unit-test.sh b/experiments/preload-unit-test.sh index 8cb700d..b251fc1 100755 --- a/experiments/preload-unit-test.sh +++ b/experiments/preload-unit-test.sh @@ -150,6 +150,33 @@ DIND_HOST_PASSTHROUGH=public DIND_HOST_DOCKER_SOCK="$WORK/absent.sock" \ check "no host save attempted without a socket" bash -c '! test -s "$DOCKER_SAVED"' check "no warning emitted when socket simply absent" bash -c '! test -s "$WORK/err.log"' +echo "== Case 8b: explicit allowlist + absent socket warns about the missing mount (issue #102) ==" +reset_state +# The operator named the images they expect passed through, but forgot the +# host-socket mount. That opt-in signal turns the otherwise-silent no-op into a +# single actionable warning. +DIND_HOST_PASSTHROUGH=public DIND_HOST_DOCKER_SOCK="$WORK/absent.sock" \ + DIND_HOST_PASSTHROUGH_IMAGES="hello-world" \ + DIND_PRELOAD_TARBALL="" DIND_PRELOAD_IMAGES="" DOCKER_INFO_OK=1 \ + preload_into_daemon 2>"$WORK/err.log" +check "no host save attempted without a socket" bash -c '! test -s "$DOCKER_SAVED"' +check "warning names DIND_HOST_PASSTHROUGH_IMAGES" grep -q "DIND_HOST_PASSTHROUGH_IMAGES is set" "$WORK/err.log" +check "warning suggests the -v mount remediation" grep -q -- "-v /var/run/docker.sock:" "$WORK/err.log" + +echo "== Case 8c: present-but-unreachable socket still wins over the allowlist warning ==" +reset_state +# When a socket file exists but is unreachable, the original (more specific) +# "not accessible" warning fires — even with an allowlist set — and the generic +# missing-mount hint does not. +touch "$WORK/dead.sock" +DIND_HOST_PASSTHROUGH=public DIND_HOST_DOCKER_SOCK="$WORK/dead.sock" \ + DIND_HOST_PASSTHROUGH_IMAGES="hello-world" \ + DIND_PRELOAD_TARBALL="" DIND_PRELOAD_IMAGES="" DOCKER_INFO_OK=1 HOST_DOCKER_OK=0 \ + preload_into_daemon 2>"$WORK/err.log" +check "unreachable-socket warning fires" grep -q "is not accessible; skipping passthrough" "$WORK/err.log" +check "missing-mount hint suppressed when a socket file exists" bash -c '! grep -q "DIND_HOST_PASSTHROUGH_IMAGES is set" "$WORK/err.log"' +rm -f "$WORK/dead.sock" + echo "== Case 9: public mode copies a Docker Hub image, skips a local one ==" reset_state # A hub image (has a docker.io RepoDigest) and a locally-built one (no digest): diff --git a/tests/dind/example-preload-images.sh b/tests/dind/example-preload-images.sh index b3c22ac..a1968f5 100755 --- a/tests/dind/example-preload-images.sh +++ b/tests/dind/example-preload-images.sh @@ -228,4 +228,23 @@ if ! docker logs "$images_container" 2>&1 | grep -q "images=${fixture_repo}"; th fi log "images-allowlist passthrough copied only the named repo and skipped the rest" +# --- Opt-in allowlist but no host socket mounted (issue #102) --------------- +# Setting DIND_HOST_PASSTHROUGH_IMAGES is an unambiguous "pass these through" +# signal. If the operator forgets the `-v` socket mount, passthrough used to be +# a silent no-op and the first nested `docker run` re-pulled from the registry +# with no hint why. The entrypoint must now surface a single actionable warning +# naming the missing mount. Note: NO -v host-sock mount here, on purpose. +no_sock_container="${DIND_EXAMPLE_ID}-passthrough-no-sock" +log "starting consumer with DIND_HOST_PASSTHROUGH_IMAGES set but NO host socket mounted" +run_dind_container "$no_sock_container" \ + -e DIND_HOST_PASSTHROUGH=public \ + -e "DIND_HOST_PASSTHROUGH_IMAGES=$fixture_repo" +wait_for_inner_docker "$no_sock_container" +wait_for_preload_complete "$no_sock_container" +if ! docker logs "$no_sock_container" 2>&1 | grep -q "DIND_HOST_PASSTHROUGH_IMAGES is set, but no host docker socket is mounted"; then + docker logs "$no_sock_container" >&2 || true + fail "expected a warning when DIND_HOST_PASSTHROUGH_IMAGES is set but no host socket is mounted" +fi +log "missing-socket warning surfaced the forgotten -v mount instead of failing silently" + log "preload example passed" diff --git a/ubuntu/24.04/dind/dind-entrypoint.sh b/ubuntu/24.04/dind/dind-entrypoint.sh index 3d4987d..44b7907 100644 --- a/ubuntu/24.04/dind/dind-entrypoint.sh +++ b/ubuntu/24.04/dind/dind-entrypoint.sh @@ -399,11 +399,17 @@ passthrough_host_images() { host_passthrough_enabled || return 0 if ! host_docker_available; then - # A socket file exists but is unreachable: surface it. Otherwise the common - # "no host socket mounted" case stays silent so the default mode is free. if [ -n "$DIND_HOST_DOCKER_SOCK" ] && [ -e "$DIND_HOST_DOCKER_SOCK" ]; then + # A socket file exists but is unreachable: surface it. warn "host docker socket at ${DIND_HOST_DOCKER_SOCK} is not accessible; skipping passthrough" + elif [ -n "$DIND_HOST_PASSTHROUGH_IMAGES" ]; then + # Operator opted in via an allowlist but no host socket is mounted: the + # nested daemon will NOT be seeded and the first nested 'docker run' will + # re-pull from the registry. Surface it instead of failing silently. (issue #102) + warn "host-image passthrough is enabled and DIND_HOST_PASSTHROUGH_IMAGES is set, but no host docker socket is mounted at ${DIND_HOST_DOCKER_SOCK}; the nested daemon will NOT be seeded from the host (first 'docker run' will pull from the registry). Mount it with: -v /var/run/docker.sock:${DIND_HOST_DOCKER_SOCK}:ro" fi + # Otherwise (no opt-in signal) the common "no host socket mounted" case stays + # silent so plain box-dind containers are not spammed. return 0 fi