Unofficial
Auf dieser Seite
  1. 01Was die Jail bietet
  2. 02Vor dem Start
  3. 03Das Profil
  4. 04Die Blöcke
  5. 05Bauen, signieren, installieren
  6. 06Die Abschottung prüfen
  7. 07Stoppen, entfernen und verbleibende Daten

Den Dienst Ihres Pakets in einer Jail ausführen: eigenes Konto, kein root, nur das erlaubte Netzwerk

Das Dienstskript eines QPKG-Pakets läuft als root. Mit dem Block Start in a jail des Dienst-Builders laufen die gestarteten Programme unter einem eigenen Konto in einer Sandbox: ohne Capabilities, ohne Möglichkeit, sie zu erlangen, mit einem Filter für gefährliche Systemaufrufe und mit eigenem Netzwerk. Das Beispiel stellt eine Seite bereit, die zeigt, was die Sandbox erlaubt.

  1. Profilwas läuft und was es sieht
  2. BlöckeKonto, Ordner, Jail
  3. Bauenbvs-jail und bwrap sind enthalten
  4. SignierenFall 1, dann installieren
  5. Prüfendie Seite zeigt ihre Umgebung

Die Bilder stammen aus Bucket & Code Server IDE 1.0.0-49. Das Beispiel wurde auf einem Test-NAS mit QTS 6.1 gebaut, signiert, installiert und geprüft. Das Konto heißt ola.

01Was die Jail bietet

  • Die Dienste laufen unter dem im Profil genannten Konto, niemals als root, ohne Capabilities und mit gesetztem NoNewPrivs. Ein seccomp-Filter verweigert Mounts, neue Namensräume, Kernelmodule, bpf, io_uring, ptrace und weitere Aufrufe, die Kernel-Exploits nutzen.
  • Sie sehen die App schreibgeschützt, ihre Datenordner mit Schreibzugriff, eigene /tmp, /proc und /dev sowie die glibc des NAS. Keine andere Freigabe, kein /etc/config, kein anderes Paket.
  • Ihr Netzwerk besteht aus einem eigenen Loopback. Jeder inbound-Port wird vom NAS an einen internen Port weitergeleitet. Ausgehende Verbindungen laufen über einen HTTP-Proxy, der private Adressen, Loopback-Adressen und die Adressen des NAS verweigert.
  • Ein Speicherlimit, eine CPU-Gewichtung und ein Prozesslimit gelten für die gesamte Jail.

Das Paket enthält die Jail selbst: bin/bvs-jail und ein statisches bin/bwrap (bubblewrap), die beim Build für jede Architektur bereitgestellt werden.

02Vor dem Start

  • Sie brauchen ein mit New project angelegtes QPKG-Projekt, QDK auf der Seite Toolchains und die Builder-Umgebung Ubuntu 24.04. Ihr eigenes App Center erklärt die Einrichtung; dieses Beispiel nutzt die Dienstvorlage Empty.
  • Ein Codesignaturzertifikat, das QNAP Ihnen ausgestellt hat, als Dateien. App Center installiert nur signierte Pakete.

03Das Profil

Das Profil ist eine JSON-Datei im Paket. Hier liegt sie im Projekt unter shared/etc/jail/jailbox.json; das Paket installiert sie als etc/jail/jailbox.json in seinem Installationsordner.

# shared/etc/jail/jailbox.json
{
  "name": "jailbox",
  "user": "jailbox",
  "app": "@QPKG_ROOT@",
  "data": ["@QPKG_DATA@"],
  "services": [
    {"name": "checks", "cmd": ["@QPKG_ROOT@/bin/busybox", "sh", "@QPKG_ROOT@/share/jailbox/checks.sh", "@QPKG_ROOT@", "@QPKG_DATA@"], "oneshot": true},
    {"name": "web", "cmd": ["@QPKG_ROOT@/bin/busybox", "httpd", "-f", "-p", "127.0.0.1:8080", "-h", "@QPKG_DATA@/www"], "wait_port": 8080}
  ],
  "inbound": [{"listen": "0.0.0.0:19975", "port": 8080}],
  "egress": {},
  "limits": {"memory_mb": 256},
  "stop_seconds": 10
}
  • @QPKG_ROOT@ und @QPKG_DATA@ stehen für den Installations- und den Datenordner, deren Pfade erst auf dem NAS feststehen. Bei jedem Start werden sie in einer Kopie aufgelöst, die nur root ändern kann. Die Jail verwendet diese Kopie. Unbekannte Schlüssel werden abgewiesen.
  • app wird schreibgeschützt eingebunden. Hier ist es der gesamte Installationsordner, damit die Dienste die BusyBox des Pakets verwenden können. Eine echte App verweist auf ihren eigenen Verzeichnisbaum, etwa @QPKG_ROOT@/app.
  • services werden der Reihe nach gestartet. Ein oneshot läuft zuerst vollständig durch; wait_port wartet auf den lauschenden Dienst, bevor der nächste startet. Mindestens ein Dienst läuft dauerhaft.
  • inbound leitet Port 19975 des NAS an den internen Port 8080 weiter. egress stellt den Diensten unter 127.0.0.1:3128 einen Proxy für die Ports 80 und 443 öffentlicher Adressen bereit. Eine allow-Liste lässt benannte Ziele unabhängig von ihrer Adresse zu.
  • limits.memory_mb ist standardmäßig 2048; stop_seconds gibt an, wie lange die Jail auf das Ende ihrer Dienste wartet.

Der erste Dienst führt dieses ebenfalls im Paket enthaltene Skript aus: shared/share/jailbox/checks.sh. Es schreibt die Ergebnisse der Sandbox-Prüfung in den Ordner, den der zweite Dienst bereitstellt.

#!/bin/sh
# Runs inside the jail and writes what the sandbox allows into the page folder.
R=$1 D=$2 B=$R/bin/busybox V=$($B dirname "$D")
{
  $B grep -E '^(Uid|CapEff|NoNewPrivs|Seccomp):' /proc/self/status
  if $B touch "$R/probe" 2>/dev/null; then echo 'write the install folder: allowed'; else echo 'write the install folder: refused'; fi
  if $B ls /etc/config >/dev/null 2>&1; then echo 'read /etc/config: allowed'; else echo 'read /etc/config: refused'; fi
  echo "what $V holds here: $($B ls -A "$V" | $B tr '\n' ' ')"
  echo "network interfaces: $($B tail -n +3 /proc/net/dev | $B cut -d: -f1 | $B tr -d ' ' | $B tr '\n' ' ')"
} >"$D/www/checks.txt"
echo 'Served from inside the jail.' >"$D/www/index.html"

04Die Blöcke

Auf der Projektseite in der Karte Service and package, Register Blocks: Fügen Sie unter At start mit Add a block fünf Blöcke in dieser Reihenfolge hinzu.

„Add a block“, Gruppe „The program“: Create a group, Create an account, Owner and mode, Start the program, Start in a jail, Wait for a port, Wait for a socket
  1. Data folder on the volume, Name jailbox: der Ordner $QPKG_DATA.
  2. Create an account, Name jailbox: das im Profil genannte Konto.
  3. Create a folder, $QPKG_DATA/www, Eigentümer jailbox:jailbox, Modus 750: der einzige Ordner, in den die Dienste schreiben.
  4. Start in a jail, ID jailbox, Profil $QPKG_ROOT/etc/jail/jailbox.json. Die Wartezeit beim Stoppen muss mindestens dem Wert stop_seconds des Profils plus 15 entsprechen; der Standardwert 90 reicht hier aus. The Start in a jail block: ID jailbox, Profile $QPKG_ROOT/etc/jail/jailbox.json, Seconds to wait at stop 90, Restart it when it stops off, Undo it at stop on, and the lines it writes: qpkg_jail and, at stop, qpkg_daemon_stop with bin/bvs-jail
  5. Wait for a port, 19975: Der Start endet, sobald der weitergeleitete Port antwortet.

Unter At a status check prüfen Is it running? mit der ID jailbox und Is the port open? mit 19975 die Jail für den Befehl status des Dienstskripts. Save zeigt die Änderungen jeder Datei; Commit schreibt sie in das Repository des Projekts.

„Commit these changes?“: service.json mit den Startblöcken sowie shared/jailbox.sh mit den Zeilen qpkg_data_dir, qpkg_user, qpkg_dir, qpkg_jail und qpkg_wait_port

05Bauen, signieren, installieren

Build auf der Projektseite führt qbuild von QDK aus. Das Protokoll nennt die Bestandteile der Jail im Paket.

Build-Protokoll: x86_64-Paket mit bin/busybox, bin/chpst, share/busybox/, bin/bvs-jail, bin/bwrap und share/bubblewrap/; erstellt: build/jailbox_0.1.0_x86_64.qpkg, ohne Codesignatur

Ohne Zertifikat wird das Paket unsigniert gebaut. Die Projektseite weist darauf hin, dass Testbereitstellung und Repository ein signiertes Paket benötigen.

Projektpipeline: Build abgeschlossen; Test deploy benötigt ein Paket mit Codesignatur; Publish: unsigniert, das Repository akzeptiert nur signierte Pakete
  1. Legen Sie die Zertifikatsdateien certificate, private_key und ca_certs im Projektordner neben qpkg.cfg ab und bauen Sie erneut: Das Paket ist dann signiert. Die .gitignore der Vorlage hält die Dateien aus git heraus. In CI stammen sie aus den Secrets des Repositorys, wie Ihr eigenes App Center zeigt.
  2. Installieren Sie das signierte Paket über Test deploy auf der Projektseite (Admin), Install Manually in App Center oder das eigene App-Center-Repository Ihres NAS.

06Die Abschottung prüfen

Öffnen Sie http://<NAS address>:19975/: Über den weitergeleiteten Port erscheint die Seite mit „Served from inside the jail.“. Die benachbarte Datei checks.txt enthält die Ergebnisse des ersten Dienstes:

# http://192.168.1.20:19975/checks.txt
Uid:	222	222	222	222
CapEff:	0000000000000000
NoNewPrivs:	1
Seccomp:	2
write the install folder: refused
read /etc/config: refused
what /share/CACHEDEV1_DATA holds here: .qpkg jailbox
network interfaces: lo
  • Der Ordner des Volumes enthält nur die Ordner des Pakets: den Installationsordner unter .qpkg und den Datenordner. Keine Freigabe, kein anderes Paket.
  • In einer SSH-Sitzung als Admin zeigt grep -E '^(Uid|CapEff|NoNewPrivs|Seccomp):' /proc/<pid>/status für den Dienst dasselbe. Auch die beiden bubblewrap-Prozesse, die die Jail von außen überwachen, haben keine Capabilities und gesetztes NoNewPrivs, allerdings keinen Filter.
  • Das Protokoll der Jail liegt als log/jailbox.log im Datenordner und erfasst jeden Dienststart, dessen Port und das Stoppen. Es vermerkt, wenn der Datenordner root gehört. Die Dienste schreiben nur in den Ordner, den der Block Create a folder dem Konto zugewiesen hat.

07Stoppen, entfernen und verbleibende Daten

  • /etc/init.d/jailbox.sh stop oder das Stoppen in App Center beendet die Dienste in umgekehrter Reihenfolge. Die Jail protokolliert ended (0). Es bleiben weder Prozesse des Kontos noch eine cgroup oder ein lauschender Port zurück.
  • Das Entfernen des Pakets stoppt die Jail und entfernt den Installationsordner und das Dienstskript. Das Konto und der Datenordner bleiben erhalten, weil kein Block sie entfernt. Your own shell lines unter At removal kann sie ebenfalls entfernen.
  • Das Laufzeitverzeichnis der Jail, /var/run/bvs-jail/jailbox, bleibt erhalten, bis der nächste Start es neu schreibt. Es liegt im Speicherdateisystem von QTS und verschwindet beim nächsten Neustart.