Cross-posted from "Dedicated service user or not ?" by @mat@jlai.lu in !selfhosted@lemmy.world


Hi all !

As of today, I am running my services with rootless podman pods and containers. Each functional stack gets its dedicated user (user cloud runs a pod with nextcloud-fpm, nginx, postgresql...) with user mapping. Now, my thought were that if an attack can escape a container, it should be contained to a specific user.

Is it really meaningful ? With service users' home setup in /var/lib, it makes a lot of small stuff annoying and I wonder if the current setup is really worth it ?

you are viewing a single comment's thread
view the rest of the comments
[–] [B] 1 point 3 weeks ago

Short answer: yes, it's meaningful — the containment boundary you care about is exactly the one that survives a container escape.

What a dedicated user actually buys you:

  1. The escapee lands with no privileges. A rootless container's root maps to your service user via subuid/subgid. If a kernel bug or a misconfigured mount lets the process out, it's running as cloud, not as you — no ssh keys, no sudo, no access to the other stacks' files. That's the whole payoff, and it works even when podman itself has a bug.

  2. Kernel attack surface between users is smaller than you'd think. You don't need per-user MAC policy (though setpriv/systemd sandboxing knobs help) — plain DAC plus the fact that different users get different subuid ranges means stack A's containers can't even name stack B's files.

On the "small stuff is annoying" side, the fixes are mostly one-time:

  • Linger: loginctl enable-linger cloud so the pod survives without a login session (or better, run quadlets as the user: systemctl --user --machine=cloud@).
  • Home layout: put persistent data under /home/cloud/data (or a dedicated dataset/LV bind-mounted there) instead of fighting /var/lib. Backup jobs and disk quotas then line up with the user naturally.
  • Low ports: sysctl net.ipv4.ip_unprivileged_port_start=80 (or net.ipv4.ip_unprivileged_port_start=0 per-host) so nginx can bind 80/443 rootless.
  • Per-user resource limits: systemd user slices (logind user slicing) give you per-stack memory/CPU caps for free — hard to do cleanly when everything runs as one user.

Where the dedicated-user model stops helping: it doesn't isolate network namespaces beyond what podman already does, and if you share one PostgreSQL container across two stacks you've collapsed the boundary anyway — keep the DB per-stack if the isolation matters.

So: keep the per-stack users. The /var/lib friction is a setup-phase cost; the isolation is what protects you on the day something actually escapes.

  • source