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 |
| Manage global workspace templates | List, pull, build, delete, or prune workspace images |
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. Root also cannot remove another
user’s images. Image usage and operation history are observable to administrators, but image
lifecycle remains owner-only.
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. Workspace-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
Section titled “Port guard”The owner-aware loopback port guard is enabled by default. It lets a workspace owner use their
own assigned localhost or SSH-forwarded ports while rejecting access by other Linux users, so
workspace application traffic follows the authenticated LabPod gateway. The server must run as
root (the default) and nft must be available.
# Disable only when you intentionally accept unguarded same-host access:LABPOD_PORT_GUARD=noneIf nftables is missing or unsupported, LabPod warns the administrator and continues rather than blocking startup or workspace lifecycle. Normal launcher and user-port links continue to work.
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, enable proxy header forwarding and declare the direct peer that connects to each LabPod listener:
# Same-host nginx, Caddy, or OpenResty:LABPOD_TRUST_PROXY_HEADERS=trueLABPOD_TRUSTED_PROXY_PEERS=loopback,unix
# A separate edge network: use the source address or CIDR LabPod sees.# LABPOD_TRUSTED_PROXY_PEERS=10.40.8.0/24,2001:db8:40::/64Only enable this if LabPod is not directly internet-accessible. With the flag off, forwarded
headers are ignored. With it on, only an exact IP address, CIDR, loopback, or unix peer may
supply forwarded metadata; hostnames are not accepted. Keep the backend ports firewalled to those
peers and configure the proxy to replace, not append, client-supplied forwarding headers. Restart
labpod.service after changing either setting.
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