From b438b6a053dde74f5f2c3eed5d9fd1db6fd94098 Mon Sep 17 00:00:00 2001 From: Kit OC5 Date: Wed, 23 Sep 2026 12:41:42 -0400 Subject: [PATCH] ci: run dind under Sysbox, not privileged (rollback override kept) dind was privileged: true, so a job that escaped into dind was root on Veron 1, which is Grant's workstation. Under sysbox-runc (sysbox-ce 0.7.1, installed 09-23 with no docker restart) dind root is an unprivileged host uid. Smoke-tested standalone: nested containers, internet, a services-style postgres on a private network and a python image all pass unprivileged. Fresh volume dind-storage-sysbox; the old dind-storage stays for docker-compose.privileged.yml, the one-command rollback. Co-Authored-By: Claude Opus 5.5 --- deploy/runner/docker-compose.privileged.yml | 15 +++++++++++++++ deploy/runner/docker-compose.yml | 15 +++++++++++---- 2 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 deploy/runner/docker-compose.privileged.yml diff --git a/deploy/runner/docker-compose.privileged.yml b/deploy/runner/docker-compose.privileged.yml new file mode 100644 index 0000000..012c013 --- /dev/null +++ b/deploy/runner/docker-compose.privileged.yml @@ -0,0 +1,15 @@ +# ROLLBACK ONLY: the pre-Sysbox dind (privileged: true), kept one command away. +# Use it if CI breaks under Sysbox: +# +# cd /srv/windygit/src/deploy/runner +# sudo docker compose -f docker-compose.yml -f docker-compose.privileged.yml up -d dind +# +# (then restart the runners while idle). Compose merges `volumes` by container +# path, so this puts back the old `dind-storage` volume with its image cache. +# Going forward again: the same command without the second -f. +services: + dind: + runtime: runc + privileged: true + volumes: + - dind-storage:/var/lib/docker diff --git a/deploy/runner/docker-compose.yml b/deploy/runner/docker-compose.yml index ab7295f..01eaa63 100644 --- a/deploy/runner/docker-compose.yml +++ b/deploy/runner/docker-compose.yml @@ -26,8 +26,12 @@ # * `dind` and every job container it spawns are UNTRUSTED. They are on a # private network with no access to the forge, its database, or its .env. # -# dind itself is privileged — that is the cost, and it is the reason a job -# escape lands in a disposable daemon rather than on Grant's workstation. +# dind is NOT privileged (2026-09-23): it runs under the Sysbox runtime +# (sysbox-ce on Veron, `runtime: sysbox-runc`), a user-namespaced system +# container whose root is an unprivileged host uid. A job that escapes its own +# container lands in dind as a nobody on the host, not as root on Grant's +# workstation. Before Sysbox, dind was `privileged: true`; that config is kept +# as docker-compose.privileged.yml (ROLLBACK ONLY, one command, see that file). # # ⚠️ Do NOT "simplify" this by mounting the host docker socket. @@ -36,13 +40,15 @@ name: windy-git-runner services: dind: image: docker.io/library/docker:27-dind - privileged: true + runtime: sysbox-runc # NOT privileged: see the I-5 note above environment: DOCKER_TLS_CERTDIR: "" # plain TCP on an isolated network, no host route command: ["dockerd", "--host=tcp://0.0.0.0:2375", "--tls=false"] networks: [jobs] volumes: - - dind-storage:/var/lib/docker + # A fresh volume: Sysbox shifts ownership to its own uid range. The old + # `dind-storage` is kept untouched for the privileged rollback. + - dind-storage-sysbox:/var/lib/docker # G1.5 — bounded so a fork-bomb workflow cannot starve Grant's interactive # session. Veron 1 is his workstation, not a dedicated build box. cpus: 12.0 # 12 of 24 cores @@ -159,6 +165,7 @@ networks: volumes: dind-storage: + dind-storage-sysbox: runner-data: runner-data-2: runner-data-3: