Security and Hardening¶
Read this page before exposing any Selkies container beyond your local machine.
The threat model in one paragraph¶
This container provides privileged access to the host system
The web interface includes a terminal with passwordless sudo. Any user who can reach the GUI can gain root inside the container, install arbitrary software, and probe your local network. Do not expose it to the internet unless you have secured it properly.
Treat a Selkies session like an SSH login to a machine on your network, because functionally that is what it is.
HTTPS is required¶
Modern browser features the client depends on, WebCodecs for video and audio in particular, only work in a secure context. That is why:
- Port
3001serves HTTPS with a self signed certificate and is the port you browse to directly. - Port
3000serves plain HTTP and exists only to sit behind a reverse proxy that terminates TLS.
Authentication layers¶
By default there is no authentication. Your options, from weakest to strongest:
- HTTP basic auth: set
CUSTOM_USERandPASSWORD. Adequate for a trusted home LAN, nothing more. - Reverse proxy with real auth: for anything internet facing, put the container behind a reverse proxy such as SWAG with a robust authentication mechanism (Authelia, Authentik, OAuth2 proxy, client certificates). See Reverse Proxy.
- SealSkin: if you are serving multiple users or apps, SealSkin handles authentication with public key cryptography and only proxies sessions to their owners.
Container isolation options¶
- Avoid
--privilegedunless you specifically want the Docker in Docker feature. It dramatically widens the blast radius. - Mounting
/var/run/docker.sockhands the container control of the host Docker daemon, which is root on the host in practical terms. Only do this deliberately. --security-opt seccomp=unconfineddisables a key Docker security layer. Use it only when a legacy host kernel or old libseccomp genuinely requires it, and treat it as a temporary workaround.
Hardening variables¶
For kiosk deployments, single app terminals, classrooms, or any situation where the person at the keyboard is not the administrator, the baseimage ships lockdown variables.
Note that the window manager level switches (HARDEN_OPENBOX, DISABLE_CLOSE_BUTTON, DISABLE_MOUSE_BUTTONS, HARDEN_KEYBINDS) only apply to single application containers, which run under labwc or Openbox. Full desktop Webtops manage their own windows, so only the desktop wide switches (HARDEN_DESKTOP and the SELKIES_* locks) matter there. The mechanics are documented in depth in the developer guide.
Umbrella switches¶
| Variable | Description |
|---|---|
HARDEN_DESKTOP | Enables DISABLE_OPEN_TOOLS, DISABLE_SUDO, and DISABLE_TERMINALS. Also sets the related client settings (SELKIES_FILE_TRANSFERS, SELKIES_COMMAND_ENABLED, SELKIES_UI_SIDEBAR_SHOW_FILES, SELKIES_UI_SIDEBAR_SHOW_APPS) unless you set them explicitly yourself |
HARDEN_OPENBOX | Window manager lockdown for single app containers. Enables DISABLE_CLOSE_BUTTON, DISABLE_MOUSE_BUTTONS, and HARDEN_KEYBINDS, and flags RESTART_APP unless you set it, so the primary application restarts automatically if closed |
Individual switches¶
| Variable | Description |
|---|---|
DISABLE_OPEN_TOOLS | Disables the xdg-open and exo-open binaries by removing their execute permissions, so apps cannot spawn arbitrary helpers |
DISABLE_SUDO | Disables sudo by removing execute permissions and invalidating the passwordless sudo configuration |
DISABLE_TERMINALS | Disables common terminal emulators and hides them from the right click menu |
DISABLE_CLOSE_BUTTON | Removes the close button from window title bars |
DISABLE_MOUSE_BUTTONS | Disables right click and middle click context menus and actions in the window manager |
HARDEN_KEYBINDS | Disables window manager keybinds that could bypass the other options, such as alt+f4 to close windows or alt+escape for the root menu |
RESTART_APP | Watchdog that restarts the main application if it is closed. The user's autostart script is made read only and root owned to prevent tampering |
Locking client settings¶
Any boolean SELKIES_* setting can be pinned so the user cannot change it in the sidebar by appending |locked:
-e SELKIES_CLIPBOARD_ENABLED="false|locked"
-e SELKIES_FILE_TRANSFERS="none"
-e SELKIES_UI_SHOW_SIDEBAR="false|locked"
See the Configuration Reference for the full list and value syntax.
Example: locked down kiosk browser¶
---
services:
kiosk:
image: lscr.io/linuxserver/chromium:latest
shm_size: 1gb
environment:
- HARDEN_DESKTOP=true
- HARDEN_OPENBOX=true
- "SELKIES_CLIPBOARD_ENABLED=false|locked"
- SELKIES_FILE_TRANSFERS=none
- "SELKIES_UI_SHOW_SIDEBAR=false|locked"
- NO_DECOR=true
- "CHROME_CLI=--kiosk https://youtube.com"
ports:
- 3001:3001
restart: unless-stopped
Locking the sidebar off entirely (SELKIES_UI_SHOW_SIDEBAR=false|locked) is the finishing touch for a true kiosk, the visitor sees nothing but the app.
Sharing links¶
The sharing feature generates links that grant access to the running session: full collaboration, view only, or gamepad only for players 2 through 4. The built in links are a demo of the multi user protocol, they carry no authentication of their own. Proper shared sessions need the token based access control an orchestrator like SealSkin wires up. Remember that anyone with a collaboration link has the same power as the primary user, including that passwordless sudo terminal. Disable what you do not need:
Pelorus and the computer use API¶
Enabling PELORUS=true starts an agent control API inside the container and forces the accessibility bridge on for all applications, which means any process in the session can read the full UI text of every app. The Pelorus API itself has no authentication and relies on the container's Nginx and network isolation. Treat a Pelorus enabled container as fully readable and controllable by anything that can reach it on its internal port, and never publish its ports directly. Details on the Pelorus page.