From 27bc2ebf70d3df644a1fe134e0cc992ee701c04c Mon Sep 17 00:00:00 2001 From: ChatGPT MCP Date: Fri, 28 Aug 2026 18:12:58 +0000 Subject: [PATCH] ci: support isolated TCP Docker scanners --- scripts/generate_container_sbom.sh | 32 +++++++++++++++++++++++----- scripts/scan_container_image.sh | 34 +++++++++++++++++++++++++----- 2 files changed, 56 insertions(+), 10 deletions(-) diff --git a/scripts/generate_container_sbom.sh b/scripts/generate_container_sbom.sh index 806db209..71834d18 100644 --- a/scripts/generate_container_sbom.sh +++ b/scripts/generate_container_sbom.sh @@ -5,6 +5,7 @@ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" TARGET_IMAGE="${1:-geointel-ci:local}" OUTPUT="${2:-artifacts/geointel-sbom.spdx.json}" SYFT_IMAGE="anchore/syft:v1.44.0@sha256:86fde6445b483d902fe011dd9f68c4987dd94e07da1e9edc004e3c2422650de6" +SCANNER_ENGINE_ARGS=() case "$OUTPUT" in /*|*..*) @@ -13,17 +14,38 @@ case "$OUTPUT" in ;; esac +configure_scanner_engine() { + local docker_host="${DOCKER_HOST:-}" + if [[ "$docker_host" == tcp://* ]]; then + local port="${docker_host##*:}" + if ! [[ "$port" =~ ^[0-9]+$ ]] || (( port < 1 || port > 65535 )); then + echo "Unsupported TCP Docker host for nested scanner: $docker_host" >&2 + exit 1 + fi + SCANNER_ENGINE_ARGS=( + --add-host host.docker.internal:host-gateway + -e "DOCKER_HOST=tcp://host.docker.internal:${port}" + ) + elif [[ -z "$docker_host" || "$docker_host" == unix:///var/run/docker.sock ]]; then + SCANNER_ENGINE_ARGS=(-v /var/run/docker.sock:/var/run/docker.sock) + else + echo "Unsupported Docker host for nested scanner: $docker_host" >&2 + exit 1 + fi +} + docker image inspect "$TARGET_IMAGE" >/dev/null mkdir -p "$ROOT/$(dirname "$OUTPUT")" +configure_scanner_engine -# Gitea/act executes jobs inside a container while Docker commands target the -# host daemon. Host-path binds to $ROOT therefore do not point at the checkout. -# Stream the SPDX document over stdout instead; only the Docker socket crosses -# the nested-runner boundary. +# Native/local runs use the normal Unix socket. Gitea jobs can target the +# isolated TCP DinD daemon; scanner containers then reach that same daemon via +# Docker's host-gateway alias instead of receiving the Unraid host socket. docker run --rm \ - -v /var/run/docker.sock:/var/run/docker.sock \ + "${SCANNER_ENGINE_ARGS[@]}" \ "$SYFT_IMAGE" \ "$TARGET_IMAGE" \ + --from docker \ -o spdx-json > "$ROOT/$OUTPUT" test -s "$ROOT/$OUTPUT" diff --git a/scripts/scan_container_image.sh b/scripts/scan_container_image.sh index 36ab025f..d9e4e35f 100644 --- a/scripts/scan_container_image.sh +++ b/scripts/scan_container_image.sh @@ -10,6 +10,7 @@ PYTHON_CMD="${PYTHON_BIN:-python3}" IGNORE_FILE="$(mktemp)" CONTAINER_IGNORE_FILE="/tmp/geointel-trivy-ignore.txt" POLICY_CONTAINER="" +SCANNER_ENGINE_ARGS=() cleanup() { rm -f "$IGNORE_FILE" @@ -26,22 +27,44 @@ case "$OUTPUT" in ;; esac +configure_scanner_engine() { + local docker_host="${DOCKER_HOST:-}" + if [[ "$docker_host" == tcp://* ]]; then + local port="${docker_host##*:}" + if ! [[ "$port" =~ ^[0-9]+$ ]] || (( port < 1 || port > 65535 )); then + echo "Unsupported TCP Docker host for nested scanner: $docker_host" >&2 + exit 1 + fi + SCANNER_ENGINE_ARGS=( + --add-host host.docker.internal:host-gateway + -e "DOCKER_HOST=tcp://host.docker.internal:${port}" + ) + elif [[ -z "$docker_host" || "$docker_host" == unix:///var/run/docker.sock ]]; then + SCANNER_ENGINE_ARGS=(-v /var/run/docker.sock:/var/run/docker.sock) + else + echo "Unsupported Docker host for nested scanner: $docker_host" >&2 + exit 1 + fi +} + docker image inspect "$TARGET_IMAGE" >/dev/null mkdir -p "$ROOT/$(dirname "$OUTPUT")" "$PYTHON_CMD" "$ROOT/scripts/verify_security_exceptions.py" "$PYTHON_CMD" "$ROOT/scripts/verify_security_exceptions.py" \ --print-container-ids > "$IGNORE_FILE" +configure_scanner_engine docker volume create "$CACHE_VOLUME" >/dev/null # Keep the complete report, including vulnerabilities without an available fix. -# Stream JSON over stdout so no checkout path has to be mounted through the -# nested Gitea/act container boundary. +# The pinned scanner receives access only to the selected Docker engine. Native +# runs use the local socket; Gitea TCP-DinD runs use the daemon's host-gateway. docker run --rm \ - -v /var/run/docker.sock:/var/run/docker.sock \ + "${SCANNER_ENGINE_ARGS[@]}" \ -v "$CACHE_VOLUME:/root/.cache/trivy" \ "$TRIVY_IMAGE" \ image \ + --image-src docker \ --scanners vuln \ --timeout 20m \ --skip-version-check \ @@ -51,12 +74,13 @@ docker run --rm \ # Release policy: fixed HIGH/CRITICAL findings block the build. Unfixed findings # remain visible in the full report and must be reviewed before release. Copy # the verified exception IDs into a stopped scanner container before starting -# it; `docker cp` works when the job checkout itself lives in another container. +# it; `docker cp` avoids mounting the job checkout through the nested runner. POLICY_CONTAINER="$(docker create \ - -v /var/run/docker.sock:/var/run/docker.sock \ + "${SCANNER_ENGINE_ARGS[@]}" \ -v "$CACHE_VOLUME:/root/.cache/trivy" \ "$TRIVY_IMAGE" \ image \ + --image-src docker \ --scanners vuln \ --timeout 20m \ --skip-version-check \