From 29a0957cb7ea36473b76aa65267d0482ddf8c671 Mon Sep 17 00:00:00 2001 From: bitdeals Date: Wed, 9 Sep 2026 13:45:09 +0000 Subject: [PATCH] feat: refuse to start as root The image runs as its own unprivileged user, and everything in run.sh now assumes it: keys.dat is worked on by its owner, no privilege is dropped anywhere, and what confines the container is whatever the caller passed. Started as root by a `user:` override, none of that holds and the container looks identical from outside -- a silent loss of every property this image was changed to have. Four lines at the top of run.sh, and the reason is then the first line of `docker logs`. It is the same bargain as the build-time checks: a wrong posture should fail loudly rather than pass for a right one. --- README.md | 3 ++- README.ru-RU.md | 4 +++- docker/run.sh | 13 +++++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 830dfc0..5ed0848 100644 --- a/README.md +++ b/README.md @@ -167,7 +167,8 @@ Container images are configured using parameters passed at runtime. it creates in its own home. Nothing is dropped at run time because nothing privileged is held in the first place. No file in the image carries a setuid or setgid bit either, so a daemon that has been taken over has nothing left to - climb. + climb. A caller that puts the user back to root gets a refusal on the first + line of the log, not a container that looks the same and confines nothing. The price is a bind mount. `/home/bitmessage` mounted from the host has to be owned by uid 2000, because the container can no longer chown it on the way in; diff --git a/README.ru-RU.md b/README.ru-RU.md index d845458..886697f 100644 --- a/README.ru-RU.md +++ b/README.ru-RU.md @@ -165,7 +165,9 @@ docker run -d \ которые заводит сам демон — `knownnodes.dat` и `messages.dat`, — он заводит в своём домашнем каталоге. Ничего не понижается на старте, потому что повышенных прав изначально нет. Ни на одном файле образа нет бита setuid или setgid, так - что захваченному демону не по чему подниматься. + что захваченному демону не по чему подниматься. Вызывающая сторона, вернувшая + пользователя в root, получит отказ первой строкой журнала, а не контейнер, + который выглядит так же и не ограничивает ничего. Плата за это — bind-монтирование. Каталог `/home/bitmessage`, приехавший с хоста, должен принадлежать uid 2000: сменить владельца на входе контейнер diff --git a/docker/run.sh b/docker/run.sh index a86d11f..61520dd 100644 --- a/docker/run.sh +++ b/docker/run.sh @@ -2,6 +2,19 @@ set -eu +# The image runs as its own unprivileged user and everything here assumes it: +# keys.dat is worked on by its owner, nothing drops a privilege anywhere, and +# what confines the container is whatever the caller passed -- cap_drop, +# no-new-privileges, read_only. Started as root instead, by a `user:` override +# or a `docker run -u 0`, none of that is true any more and the container looks +# exactly the same from outside. Refuse, where the reason is the first line of +# `docker logs`, rather than run on quietly with the wrong properties. +if [ "$(id -u)" = 0 ] +then + echo "run.sh: this image must not be run as root; drop the user override" >&2 + exit 1 +fi + export BITMESSAGE_API_USER="${BITMESSAGE_API_USER:-bitmessage_api_user}" export BITMESSAGE_API_PASSWORD="${BITMESSAGE_API_PASSWORD:-bitmessage_api_password}" export BITMESSAGE_SEED_ADDRESSES="${BITMESSAGE_SEED_ADDRESSES:-0}"