Security & hardening
Identity and trust boundary
Section titled “Identity and trust boundary”LabPod maps each platform account to a Linux account on the host. Rootless Podman runs every workspace as the owner’s Linux user - not as root, not as a shared service account.
What the admin can and cannot do:
| Admin can | Admin cannot |
|---|---|
| View all workspaces and resource usage | Enter another user’s terminal session |
| Force-stop another user’s workspace | Start, edit, delete, or open another user’s workspace |
| Create and delete users | Access another user’s files via LabPod |
The admin boundary is intentional: force-stop reclaims the container and its reserved resources; anything deeper requires direct host access.
Root cannot own workspaces. The root account cannot create or start its own workspaces
because workspace containers must run as a non-root Linux user.
Container hardening
Section titled “Container hardening”Every workspace container launched by LabPod includes these flags:
| Flag | What it does |
|---|---|
--userns=keep-id | Host UID = container UID - files have natural ownership |
--cap-drop=ALL | Drops all Linux capabilities |
--security-opt no-new-privileges | Prevents setuid privilege escalation |
--init | Runs catatonit as PID 1 for zombie reaping |
--shm-size <half-of-memory> | Prevents PyTorch DataLoader from hitting the 64 MB default |
127.0.0.1:<port> bind | Workspace ports bind only to loopback - never directly exposed |
Workspace traffic is proxied through LabPod’s reverse proxy, which enforces session-cookie
authentication before forwarding. Template launchers use /ws/<id>/<launcher>/; user-declared
ports use /ws/<id>/_user/<name>/. The separate namespaces allow a launcher and user port to
share a name.
The workspace gateway also confines a web application’s service worker to that application’s URL prefix. A launcher or user-added app cannot register a broad worker that controls another app or workspace on the shared gateway origin.
Containers started directly with Podman are outside the workspace trust boundary. LabPod may
inspect them for read-only safety and capacity accounting, but it does not proxy into them or
offer launchers, terminals, logs, declared ports, or lifecycle actions. The legacy
/ws/c:<container>/ and /ws/_discovered/ forms return 404.
Execution model
Section titled “Execution model”The default systemd unit runs LabPod as root (User=root). Rootless workspaces still run as
their owner’s Linux account: the server builds argv arrays and executes Podman as
sudo -u <owner> / equivalent user-context commands. It never shells out through bash -c with
user input.
By default LabPod serves plain HTTP on port 24680. For HTTPS:
Option 1 - Self-signed certificate (for private LAN use):
# In /etc/labpod/labpod.env:LABPOD_TLS_SELF_SIGNED=trueLABPOD_TLS_HOSTS=192.168.1.10,labpod.lan # optional extra SANsOn first start, LabPod generates and caches a self-signed cert under /var/lib/labpod/tls/.
The cert renews automatically when it nears expiry.
Get the SHA-256 fingerprint for the desktop launcher’s trust-on-first-use pin:
sudo labpod admin tls-fingerprint# Run these as rootlabpod admin tls-fingerprintOption 2 - Operator-provided certificate:
LABPOD_TLS_CERT=/etc/labpod/tls/server.crtLABPOD_TLS_KEY=/etc/labpod/tls/server.keyBoth LABPOD_TLS_CERT and LABPOD_TLS_KEY must be set together.
Port guard (recommended on shared hosts)
Section titled “Port guard (recommended on shared hosts)”On a shared Linux host, enable the loopback port guard so workspace application traffic follows the authenticated LabPod gateway:
# In /etc/labpod/labpod.env:LABPOD_PORT_GUARD=nftThis installs an nftables rule at startup that restricts direct loopback connections to workspace ports. Normal launcher and user-port links continue to work. The server must run as root (which is the default).
doctor reports whether the port guard is installed correctly:
sudo labpod admin doctor# Run these as rootlabpod admin doctorReverse proxy and trusted headers
Section titled “Reverse proxy and trusted headers”If LabPod sits behind a reverse proxy (nginx, Caddy, Cloudflare Tunnel), enable proxy header forwarding:
LABPOD_TRUST_PROXY_HEADERS=trueOnly enable this if LabPod is not directly internet-accessible. With this flag off (the
default), X-Forwarded-Host and X-Forwarded-Proto from clients are ignored, which is the
correct behavior for a direct deployment.
For the WebSocket terminal endpoint, add trusted origins if users connect through a domain name that differs from the server’s address:
LABPOD_WS_ALLOWED_ORIGINS=https://labpod.example.com