Merge pull request 'Add steam headless apps with security docs and verification guide' (#2) from docker-ip-addr-manager/initial/fastapi-vue-ip-manager into main

Reviewed-on: phirna/zima-apps#2
This commit is contained in:
2026-03-18 21:55:48 +01:00
8 changed files with 670 additions and 0 deletions
+78
View File
@@ -0,0 +1,78 @@
# Steam Headless
Steam Headless kör en webbaserad Linux-desktop med Steam i container, baserat på LinuxServer image `lscr.io/linuxserver/steam`.
Imagepinning i denna app:
- Compose använder immutable referens: `lscr.io/linuxserver/steam:version-f4f48542@sha256:d7b9fbf302e05ae79248d1171fe9751b354f8397eafa1e13a3df0aa6a75de0b4`
- `latest` finns i registryt men används inte i repo enligt policy.
- Vid verifieringstillfället pekade `latest` på samma release (`version-f4f48542`).
## Syfte
- Ge enkel Steam-access via webbläsare i ZimaOS.
- Hålla v1 med minsta möjliga privilegier.
- Förbereda en separat senare fas för Moonlight-fokuserad streaming.
## Portar
- `3000/tcp` (HTTP desktop): `${STEAM_HTTP_PORT:-3000}`
- `3001/tcp` (HTTPS desktop): `${STEAM_HTTPS_PORT:-3001}`
## Volymer
- `/DATA/AppData/$AppID/config -> /config`
All Steam-data (profil, cache, installerade spel) lagras under appens egna AppData-sökväg.
## Privilegier och säkerhet
Aktiva säkerhetsinställningar i denna app:
- `security_opt: ["seccomp:unconfined", "no-new-privileges:true"]`
- `cap_drop: ["ALL"]`
- Ingen `privileged: true`
- Ingen `network_mode: host`
- Ingen mount av `/var/run/docker.sock`
Motivering:
- LinuxServer Steam använder sandbox/bubblewrap-mönster som normalt kräver `seccomp:unconfined` för att spel/launcher ska fungera stabilt.
- `no-new-privileges:true` och `cap_drop: ["ALL"]` används för att kompensera med lägsta möjliga capability-yta i övrigt.
Kända tradeoffs:
- På vissa Debian/Ubuntu-hostar kan även `apparmor:unconfined` behövas. Detta är inte default här av least-privilege-skäl.
- Browser-vägen (KasmVNC) är enkel men ger inte samma latens/gamepad-egenskaper som Moonlight.
## Säkerhetsavvikelser
Denna app använder en avvikelse från strikt seccomp-default:
- `seccomp:unconfined`
Varför det behövs:
- För kompatibilitet med LinuxServer Steam runtime och dess sandboxade processer.
Alternativ som utvärderats:
- Standard seccomp-profil: blockar delar av förväntad processmodell för Steam/spel.
- Full `privileged: true`: avvisat på grund av större attackyta.
Risker:
- Minskad syscall-filtrering jämfört med default seccomp-profil.
- Om container komprometteras finns större möjlighet att anropa kernel-funktioner än med strikt seccomp.
Riskreducering:
- Inga host-network eller docker-socket mounts.
- Capability-surface minimerad med `cap_drop: ["ALL"]`.
- Isolerad data-path under `/DATA/AppData/$AppID/...`.
## Driftnoteringar
- För GPU-acceleration kan extra device-mounts krävas beroende på host och drivrutiner.
- Om HTTPS används på `3001` kan webbläsaren visa certifikatvarning vid första anslutning.
- Rekommenderad nästa fas: separat Moonlight/Sunshine-spår som opt-in, med egen riskprofil.
+81
View File
@@ -0,0 +1,81 @@
name: steam-headless
services:
steam:
image: lscr.io/linuxserver/steam:version-f4f48542@sha256:d7b9fbf302e05ae79248d1171fe9751b354f8397eafa1e13a3df0aa6a75de0b4
container_name: steam-headless
restart: unless-stopped
shm_size: "1gb"
environment:
TZ: ${TZ}
PUID: ${PUID}
PGID: ${PGID}
STEAM_HTTP_PORT: ${STEAM_HTTP_PORT:-3000}
STEAM_HTTPS_PORT: ${STEAM_HTTPS_PORT:-3001}
ports:
- target: 3000
published: ${STEAM_HTTP_PORT:-3000}
protocol: tcp
- target: 3001
published: ${STEAM_HTTPS_PORT:-3001}
protocol: tcp
volumes:
- type: bind
source: /DATA/AppData/$AppID/config
target: /config
# Required by LinuxServer Steam for bubblewrap/game namespaces.
security_opt:
- seccomp:unconfined
- no-new-privileges:true
# Keep capability surface minimal unless a specific game requires otherwise.
cap_drop:
- ALL
x-casaos:
envs:
- container: TZ
description:
en_us: Timezone, for example Europe/Stockholm
- container: PUID
description:
en_us: User ID for filesystem permissions
- container: PGID
description:
en_us: Group ID for filesystem permissions
ports:
- container: "3000"
description:
en_us: Steam desktop GUI over HTTP
- container: "3001"
description:
en_us: Steam desktop GUI over HTTPS
volumes:
- container: /config
description:
en_us: Steam home, configuration, and game files
x-casaos:
architectures:
- amd64
main: steam
category: Games
author: Zima Apps Team
developer: linuxserver.io
icon: https://cdn.simpleicons.org/steam
tagline:
en_us: Browser-based Steam desktop container for ZimaOS
description:
en_us: >-
Runs LinuxServer Steam as a web-accessible desktop session.
Optimized for amd64 and least-privilege defaults, with optional future
Moonlight-focused expansion in a later phase.
title:
en_us: Steam Headless
index: /
port_map: ${STEAM_HTTPS_PORT:-3001}
scheme: https
@@ -0,0 +1,51 @@
# Moonlight Runtime Checklist
Strikt checklista för att aktivera `steam-moonlight`-profilen säkert.
## Preflight (måste vara grönt)
1. Bekräfta att du medvetet accepterar högriskprofil (`network_mode: host`, extra capabilities, device passthrough).
2. Sätt starkt lösenord i `SUNSHINE_PASS` (inte `change-me`, inte återanvänt).
3. Verifiera GPU-device mapping:
- `GPU_CARD_DEVICE` pekar på korrekt `/dev/dri/card*`
- `GPU_RENDER_DEVICE` pekar på korrekt `/dev/dri/renderD*`
4. Verifiera att hosten har:
- `/dev/fuse`
- `/dev/uinput`
- korrekt `/dev/dri/*`
5. Bekräfta att Sunshine-portar inte exponeras publikt mot internet.
6. Verifiera att volymerna är under `/DATA/AppData/$AppID/...`.
7. Verifiera compose-rendering:
- `docker compose -f Apps/steam-moonlight/docker-compose.yaml --profile moonlight config`
## Startsekvens
1. Starta endast Moonlight-profilen:
- `docker compose -f Apps/steam-moonlight/docker-compose.yaml --profile moonlight up -d steam-moonlight`
2. Kontrollera containerstatus:
- `docker compose -f Apps/steam-moonlight/docker-compose.yaml ps`
3. Kontrollera initiala logs:
- `docker logs --tail=200 steam-moonlight-profile`
## Post-start verifiering
1. Verifiera att Sunshine kräver autentisering.
2. Verifiera att streaming fungerar från avsedd klient (LAN/VPN).
3. Verifiera controller-input utan att aktivera fler capabilities än definierat.
4. Verifiera att inga oväntade portar exponeras.
5. Verifiera att defaultprofil fortfarande kan köras separat vid rollback.
## Driftregler
1. Kör inte Moonlight-profilen permanent om den inte används.
2. Roterar `SUNSHINE_PASS` regelbundet och alltid efter incident.
3. Uppdatera image-pin via digest i kontrollerade change windows.
4. Undvik ad-hoc ändringar av capabilities eller device mounts.
## Snabb rollback
1. Stoppa Moonlight-profil:
- `docker compose -f Apps/steam-moonlight/docker-compose.yaml --profile moonlight stop steam-moonlight`
2. Kör defaultprofil:
- `docker compose -f Apps/steam-moonlight/docker-compose.yaml up -d steam`
3. Bekräfta återställd låg-risk drift via logs och funktionstest.
+102
View File
@@ -0,0 +1,102 @@
# Steam Moonlight (Scaffold)
Detta är en scaffold för hybridspåret (browser + Moonlight) baserat på `josh5/steam-headless`.
Kompletterande säkerhetsdokument:
- `SECURITY.md`
- `MOONLIGHT-RUNTIME-CHECKLIST.md`
## Syfte
- Ge en låg-risk default för browserbaserad Steam-desktop.
- Ge en separat opt-in-profil för Moonlight/Sunshine.
- Isolera högriskinställningar till en explicit profil (`moonlight`).
## Imagepinning
- Compose använder immutable referens:
- `josh5/steam-headless:debian-0.2.0@sha256:540366bee31297c5679a5006a84dbca039ca62aaab695852b51b5f62dffd2c14`
- Repon kräver att `latest` inte används i compose.
## Profiler
- `steam` (default): browser-first, lägre risk, ingen host network.
- `steam-moonlight` (`profiles: ["moonlight"]`): aktiverar Sunshine + controller/GPU passthrough med högre privilegier.
## Körning
- Default (rekommenderad start):
- `docker compose -f Apps/steam-moonlight/docker-compose.yaml up -d steam`
- Moonlight (opt-in):
- `docker compose -f Apps/steam-moonlight/docker-compose.yaml --profile moonlight up -d steam-moonlight`
- Innan Moonlight aktiveras:
- byt `SUNSHINE_PASS` till ett starkt lösenord,
- verifiera `GPU_CARD_DEVICE` och `GPU_RENDER_DEVICE` för rätt GPU.
## Portar
- Defaultprofil (`steam`):
- `${STEAM_WEB_PORT:-8083}/tcp` för webdesktop.
Moonlightprofilen använder `network_mode: host` och tar därför nätverk direkt från host.
## Volymer
- Defaultprofil:
- `/DATA/AppData/$AppID/home -> /home/default`
- `/DATA/AppData/$AppID/games -> /mnt/games`
- Moonlightprofil:
- `/DATA/AppData/$AppID/moonlight-home -> /home/default`
- `/DATA/AppData/$AppID/moonlight-games -> /mnt/games`
## Privilegier och säkerhet
Gemensamt:
- `no-new-privileges:true`
Defaultprofil (`steam`, lägre risk):
- `cap_drop: ["ALL"]`
- Ingen `network_mode: host`
- Inga device mounts
- Ingen Sunshine-aktivering som default
Moonlightprofil (högrisk):
- `ipc: host`
- `security_opt: ["seccomp:unconfined", "apparmor:unconfined", "no-new-privileges:true"]`
- `cap_drop: ["ALL"]` + `cap_add: [NET_ADMIN, SYS_ADMIN, SYS_NICE]`
- `network_mode: host`
- Device mounts: `/dev/fuse`, `/dev/uinput`, `/dev/dri/*`
- `device_cgroup_rules: ['c 13:* rmw']`
## Säkerhetsavvikelser
Denna app innehåller avsiktliga avvikelser, primärt i `moonlight`-profilen.
Varför det behövs:
- Moonlight/Sunshine och fysisk controller-input kräver i praktiken host-nära åtkomst i denna containerfamilj.
Alternativ som utvärderats:
- Browser-only (`steam` default): lägre risk men sämre latens/gamepad.
- Full `privileged: true`: avvisat i scaffolden för att begränsa attackytan.
Risker:
- Host network minskar nätverksisolering.
- Extra capabilities och device-passthrough ökar konsekvensen vid containerkompromettering.
Riskreducering:
- Högrisk är opt-in via profil, inte default.
- Ingen docker socket-mount används.
- Persistent data är begränsad till `/DATA/AppData/$AppID/...`.
- Defaultprofilen droppar alla Linux capabilities.
## Status
Scaffolden är avsedd för kontrollerad vidareutveckling och verifiering, inte som slutlig hardened release.
+50
View File
@@ -0,0 +1,50 @@
# Roadmap
## Current State (2026-03-18)
- Browser-first service (`steam`) är testbar med låg-risk baseline.
- Moonlight service (`steam-moonlight`) finns som opt-in scaffold med dokumenterade risker.
- Operativa säkerhetskrav finns i `SECURITY.md` och `MOONLIGHT-RUNTIME-CHECKLIST.md`.
## Next Milestone: Moonlight Test-Ready (Fail-Closed)
Mål: Moonlight-profilen ska vägra starta vid osäker konfiguration.
1. Enforce credentials
- Blockera start om `SUNSHINE_PASS` är tomt eller default (`change-me`).
- Blockera start om `SUNSHINE_USER` är tomt.
2. Enforce GPU/device prerequisites
- Blockera start om `GPU_CARD_DEVICE` eller `GPU_RENDER_DEVICE` saknas på host.
- Blockera start om `/dev/fuse` eller `/dev/uinput` saknas.
3. Enforce network safety baseline
- Tydlig varning + explicit opt-in env för internet-exponering.
- Defaultantagande: LAN/VPN-only drift.
4. Preflight command and runbook
- Lägg till en verifierbar preflight-sekvens som måste passera före `up`.
- Uppdatera checklistan med pass/fail-kriterier.
## Hardening Milestone
1. Secrets hygiene
- Dokumentera rotationsintervall för Sunshine-credentials.
- Säkerställ att exempel aldrig innehåller riktiga credentials.
2. Capability minimization
- Verifiera om någon `cap_add` kan tas bort utan funktionsförlust.
- Dokumentera minsta uppsättning capabilities per use-case.
3. Upgrade discipline
- Definiera rutin för image-pin uppdatering (`tag + digest`) och rollback.
## Release Criteria (Moonlight)
För att kalla Moonlight-profilen releaseklar ska följande vara uppfyllt:
1. Fail-closed enforcement implementerat och verifierat.
2. Positiv test av Moonlight streaming med korrekt auth.
3. Negativ test: start blockeras vid osäkra defaults/missing devices.
4. `./scripts/validate-appstore.sh --enforce-risk-docs` passerar.
5. README + security docs uppdaterade med slutlig driftmodell.
+70
View File
@@ -0,0 +1,70 @@
# Security Model
Detta dokument beskriver säkerhetsmodellen för `steam-moonlight` och hur risker begränsas mellan default- och Moonlight-profil.
## Mål
- Hålla default-profilen (`steam`) så nära least-privilege som möjligt.
- Göra Moonlight-profil (`steam-moonlight`) explicit opt-in med tydlig riskaccept.
- Hålla state/data begränsad till `/DATA/AppData/$AppID/...`.
## Tillitsgränser
- Host OS och Docker daemon är högsta trust-zon.
- Containern är lägre trust-zon.
- Moonlight-klienter och LAN-trafik är extern yta.
## Threat Model
Primära hot:
- Kontoövertagande via svagt `SUNSHINE_PASS`.
- Lateral movement via `network_mode: host` (Moonlight-profil).
- Ökad impact vid containerkompromiss p.g.a. extra capabilities och device passthrough.
- Dataförlust vid felaktig mount-path.
## Säkerhetskontroller
Gemensamt:
- Immutable image pin (`tag + digest`).
- Ingen docker socket mount.
- Ingen `privileged: true`.
- Data paths begränsade till `/DATA/AppData/$AppID/...`.
Defaultprofil (`steam`):
- `cap_drop: ["ALL"]`
- `no-new-privileges:true`
- Ingen `host` network
- Inga device mounts
- Sunshine avstängd som default (`ENABLE_SUNSHINE=false`)
Moonlightprofil (`steam-moonlight`):
- Högriskkontroller isolerade till `profiles: ["moonlight"]`
- `cap_drop: ["ALL"]` + minsta kända `cap_add`
- Explicit device lista (`/dev/fuse`, `/dev/uinput`, `/dev/dri/*`)
- `seccomp:unconfined` och `apparmor:unconfined` endast i Moonlight-profilen
## Fail-Closed Regler
- Moonlight ska inte startas om `SUNSHINE_PASS` är default eller tomt.
- Moonlight ska inte startas om GPU-device mapping saknas eller är fel.
- Vid osäkerhet, kör endast defaultprofilen (`steam`).
## Operativa krav
- Exponera inte Sunshine admin/UI mot internet.
- Begränsa åtkomst till LAN/VPN och pålitliga klienter.
- Rota image-pins kontrollerat och verifiera digest före uppdatering.
- Följ checklistan i `MOONLIGHT-RUNTIME-CHECKLIST.md` före varje aktivering.
## Incidentrespons (minimum)
Vid misstänkt kompromiss:
1. Stoppa Moonlight-profilen direkt.
2. Roterar `SUNSHINE_PASS` och övriga credentials.
3. Granska hostens nätverksexponering och Docker logs.
4. Byt image till känd god pin och starta om endast defaultprofil.
+131
View File
@@ -0,0 +1,131 @@
name: steam-moonlight
x-steam-common: &steam-common
image: josh5/steam-headless:debian-0.2.0@sha256:540366bee31297c5679a5006a84dbca039ca62aaab695852b51b5f62dffd2c14
restart: unless-stopped
shm_size: ${SHM_SIZE:-2G}
environment:
TZ: ${TZ}
PUID: ${PUID}
PGID: ${PGID}
UMASK: ${UMASK:-000}
USER_PASSWORD: ${USER_PASSWORD:-change-me}
MODE: ${MODE:-primary}
WEB_UI_MODE: ${WEB_UI_MODE:-vnc}
PORT_NOVNC_WEB: ${STEAM_WEB_PORT:-8083}
ENABLE_STEAM: ${ENABLE_STEAM:-true}
STEAM_ARGS: ${STEAM_ARGS:--silent}
ENABLE_SUNSHINE: ${ENABLE_SUNSHINE:-false}
SUNSHINE_USER: ${SUNSHINE_USER:-admin}
SUNSHINE_PASS: ${SUNSHINE_PASS:-change-me}
services:
steam:
<<: *steam-common
container_name: steam-moonlight
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
ports:
- target: 8083
published: ${STEAM_WEB_PORT:-8083}
protocol: tcp
volumes:
- type: bind
source: /DATA/AppData/$AppID/home
target: /home/default
- type: bind
source: /DATA/AppData/$AppID/games
target: /mnt/games
x-casaos:
envs:
- container: TZ
description:
en_us: Timezone, for example Europe/Stockholm
- container: PUID
description:
en_us: User ID for filesystem permissions
- container: PGID
description:
en_us: Group ID for filesystem permissions
- container: STEAM_WEB_PORT
description:
en_us: Browser desktop port
ports:
- container: "8083"
description:
en_us: Steam desktop over web browser
volumes:
- container: /home/default
description:
en_us: Persistent user home and runtime state
- container: /mnt/games
description:
en_us: Persistent Steam game library
steam-moonlight:
<<: *steam-common
container_name: steam-moonlight-profile
profiles: ["moonlight"]
network_mode: host
ipc: host
security_opt:
- seccomp:unconfined
- apparmor:unconfined
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- NET_ADMIN
- SYS_ADMIN
- SYS_NICE
devices:
- /dev/fuse
- /dev/uinput
- ${GPU_CARD_DEVICE:-/dev/dri/card0}
- ${GPU_RENDER_DEVICE:-/dev/dri/renderD128}
device_cgroup_rules:
- 'c 13:* rmw'
environment:
TZ: ${TZ}
PUID: ${PUID}
PGID: ${PGID}
UMASK: ${UMASK:-000}
USER_PASSWORD: ${USER_PASSWORD:-change-me}
MODE: ${MODE:-primary}
WEB_UI_MODE: ${WEB_UI_MODE:-vnc}
PORT_NOVNC_WEB: ${STEAM_WEB_PORT:-8083}
ENABLE_STEAM: ${ENABLE_STEAM:-true}
STEAM_ARGS: ${STEAM_ARGS:--silent}
ENABLE_SUNSHINE: "true"
SUNSHINE_USER: ${SUNSHINE_USER:-admin}
SUNSHINE_PASS: ${SUNSHINE_PASS:-change-me}
volumes:
- type: bind
source: /DATA/AppData/$AppID/moonlight-home
target: /home/default
- type: bind
source: /DATA/AppData/$AppID/moonlight-games
target: /mnt/games
x-casaos:
architectures:
- amd64
main: steam
category: Games
author: Zima Apps Team
developer: Steam-Headless community
icon: https://cdn.simpleicons.org/steam
tagline:
en_us: Steam web desktop with optional Moonlight profile
description:
en_us: >-
Browser-first Steam container with an explicit moonlight profile for higher
compatibility and controller support. The moonlight profile is opt-in and
carries additional security risk.
title:
en_us: Steam Moonlight (Scaffold)
index: /
port_map: ${STEAM_WEB_PORT:-8083}
scheme: http
+107
View File
@@ -0,0 +1,107 @@
# How To Verify
Detta dokument verifierar båda Steam-apparna i repo:
- `Apps/steam-headless`
- `Apps/steam-moonlight`
## 1) Repo-validering
Kör från repo-roten:
```bash
./scripts/validate-appstore.sh --enforce-risk-docs
```
Förväntat: `Validation OK` eller `Validation OK with ... warning(s)`.
## 2) Verifiera steam-headless (browser-first)
Rendera compose:
```bash
docker compose -f Apps/steam-headless/docker-compose.yaml config
```
Starta:
```bash
docker compose -f Apps/steam-headless/docker-compose.yaml up -d steam
```
Kontroller:
1. `docker compose -f Apps/steam-headless/docker-compose.yaml ps` visar `steam` som running.
2. Web UI nås på `${STEAM_HTTP_PORT:-3000}` eller `${STEAM_HTTPS_PORT:-3001}`.
3. Inga extra högriskflaggor används (`privileged`, `host network`, `docker.sock`).
Stoppa:
```bash
docker compose -f Apps/steam-headless/docker-compose.yaml down
```
## 3) Verifiera steam-moonlight defaultprofil
Rendera compose (default):
```bash
docker compose -f Apps/steam-moonlight/docker-compose.yaml config
```
Starta default service:
```bash
docker compose -f Apps/steam-moonlight/docker-compose.yaml up -d steam
```
Kontroller:
1. `steam` är running.
2. Webdesktop nås på `${STEAM_WEB_PORT:-8083}`.
3. Defaultprofilen kör med låg-risk baseline (`cap_drop: ALL`, ingen `host network`).
Stoppa:
```bash
docker compose -f Apps/steam-moonlight/docker-compose.yaml down
```
## 4) Verifiera steam-moonlight moonlight-profil (opt-in)
Preflight:
1. Sätt starkt `SUNSHINE_PASS`.
2. Verifiera GPU devices (`GPU_CARD_DEVICE`, `GPU_RENDER_DEVICE`).
3. Verifiera `/dev/fuse` och `/dev/uinput` på host.
Rendera moonlight-profil:
```bash
docker compose -f Apps/steam-moonlight/docker-compose.yaml --profile moonlight config
```
Starta:
```bash
docker compose -f Apps/steam-moonlight/docker-compose.yaml --profile moonlight up -d steam-moonlight
```
Kontroller:
1. `steam-moonlight` är running.
2. Sunshine kräver autentisering.
3. Moonlight-klient kan ansluta från LAN/VPN.
4. Ingen oavsiktlig internetexponering av Sunshine-portar.
Stoppa/rollback:
```bash
docker compose -f Apps/steam-moonlight/docker-compose.yaml --profile moonlight down
```
Vid problem, återgå till defaultprofil:
```bash
docker compose -f Apps/steam-moonlight/docker-compose.yaml up -d steam
```