From 997fc2989a535134b6fd5e4e36619065deede797 Mon Sep 17 00:00:00 2001 From: mace Date: Sun, 7 Jun 2026 16:57:12 +0200 Subject: [PATCH] added rootless docker setup --- README.md | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/README.md b/README.md index c81bdcc..8ca36d3 100644 --- a/README.md +++ b/README.md @@ -145,6 +145,83 @@ docker compose down -v # stop and wipe all data — careful! docker compose up --build -d # rebuild after pulling code changes ``` +### Optional: hardened deployment — isolated user + rootless Docker + +Anyone who can run `docker` commands effectively has root on the host (container volume mounts can reach the whole filesystem) — being in the `docker` group is root-equivalent. For a production server, it's worth confining this stack to a dedicated, unprivileged system user running its own **rootless Docker** daemon, instead of using a system-wide install or adding the user to the `docker` group. + +Rootless Docker runs as a completely independent daemon — separate socket (`/run/user//docker.sock`) and storage (`~/.local/share/docker` vs. `/var/lib/docker`) — so it coexists fine with an existing system-wide Docker install on the same host. Just make sure `DOCKER_HOST`/`PATH` point at the rootless daemon when operating on this stack, and that the ports you publish in step 4 below aren't already in use elsewhere. + +**1. Create the user and enable lingering** (so its services keep running without an active login session): + +```sh +sudo adduser --disabled-password --gecos "" rss-svc +sudo loginctl enable-linger rss-svc +``` + +**2. Install rootless Docker for that user:** + +```sh +sudo apt install -y uidmap dbus-user-session +sudo machinectl shell rss-svc@ +curl -fsSL https://get.docker.com/rootless | sh +``` + +Add to `~/.bashrc` (as `rss-svc`): + +```sh +export PATH=/home/rss-svc/bin:$PATH +export DOCKER_HOST=unix:///run/user/$(id -u)/docker.sock +``` + +Enable the daemon as a persistent user service, then re-login (or `exec $SHELL`) so the exports above take effect: + +```sh +systemctl --user enable docker +systemctl --user start docker +``` + +**3. Deploy the stack as `rss-svc`:** + +```sh +git clone ~/rss-reader +cd ~/rss-reader +cp .env.example .env +chmod 600 .env +``` + +Fill in `.env` with strong, unique secrets — `openssl rand -hex 32` is a convenient way to generate `JWT_SECRET`/`POSTGRES_PASSWORD`. + +**4. Bind published ports to localhost only.** The only thing that needs to reach this stack from outside is the reverse proxy below, and it runs on the same host. Edit `docker-compose.yml`: + +```yaml + postgres: + ports: + - "127.0.0.1:5432:5432" + backend: + ports: + - "127.0.0.1:8001:8001" + frontend: + ports: + - "127.0.0.1:8080:80" +``` + +**5. Bring it up:** + +```sh +docker compose up --build -d +``` + +**6. Firewall** (run as your normal sudo-capable user — not `rss-svc`): + +```sh +sudo ufw allow OpenSSH +sudo ufw allow 80/tcp +sudo ufw allow 443/tcp +sudo ufw enable +``` + +With this in place: only SSH and HTTPS are reachable from the network, the reverse proxy is the sole entry point into the app, the whole stack runs in its own user/container/network namespaces with no elevated privileges, and secrets live in a `chmod 600` `.env` owned by an account that can't do anything else on the system. + ### Optional: Apache reverse proxy (TLS termination) If you want to expose the app under a domain with HTTPS, put Apache in front of the `frontend` container (which keeps listening on `localhost:8080`) and let Apache handle TLS. Enable the required modules first: