added rootless docker setup

This commit is contained in:
2026-06-07 16:57:12 +02:00
parent 09eae69041
commit 997fc2989a
+77
View File
@@ -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/<uid>/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 <your-repo-url> ~/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: