Exécuter le service de votre paquet dans une jail : compte dédié, sans root, avec le seul réseau autorisé
Le script de service d’un paquet QPKG s’exécute en root. Avec le bloc Start in a jail du constructeur de services, les programmes lancés utilisent leur propre compte dans un bac à sable : aucune capacité, aucun moyen d’en acquérir, un filtre des appels système dangereux et un réseau propre. Cet exemple sert une page qui indique ce que le bac à sable lui permet de faire.
- Profilce qui tourne et ce qui est visible
- Blocscompte, dossiers, jail
- Compilerbvs-jail et bwrap sont inclus
- Signercas 1, puis installer
- Vérifierla page décrit son environnement
Les images proviennent de Bucket & Code Server IDE 1.0.0-49. L’exemple a été compilé, signé, installé et vérifié sur un NAS de test sous QTS 6.1. Le compte s’appelle ola.
01Ce que fournit la jail
- Les services utilisent le compte indiqué dans le profil, jamais root, sans capacités et avec
NoNewPrivsactivé. Un filtre seccomp refuse les montages, les nouveaux espaces de noms, les modules du noyau,bpf,io_uring,ptraceet les autres appels utilisés par les exploits du noyau. - Ils voient l’application en lecture seule, leurs dossiers de données en lecture-écriture, des
/tmp,/procet/devprivés, ainsi que la glibc du NAS. Aucun autre partage, aucun/etc/config, aucun autre paquet. - Leur réseau possède sa propre boucle locale. Chaque port
inboundest relayé du NAS vers un port interne. Les connexions sortantes passent par un proxy HTTP qui refuse les adresses privées, celles de boucle locale et celles du NAS. - Une limite de mémoire, une pondération CPU et une limite de processus s’appliquent à toute la jail.
Le paquet embarque la jail : bin/bvs-jail et un bin/bwrap statique (bubblewrap), préparés lors de la compilation pour chaque architecture.
02Avant de commencer
- Il faut un projet QPKG créé avec New project, QDK sur la page Toolchains et l’environnement de compilation Ubuntu 24.04. Votre propre App Center explique leur configuration ; cet exemple utilise le modèle de service Empty.
- Un certificat de signature de code délivré par QNAP, sous forme de fichiers. App Center installe uniquement les paquets signés.
03Le profil
Le profil est un fichier JSON du paquet. Ici, il se trouve dans shared/etc/jail/jailbox.json dans le projet, puis est installé sous etc/jail/jailbox.json dans le dossier d’installation du paquet.
# 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@et@QPKG_DATA@représentent les dossiers d’installation et de données, dont les chemins ne sont connus que sur le NAS. Chaque démarrage les remplace dans une copie modifiable uniquement par root, puis la jail utilise cette copie. Les clés inconnues sont refusées.appest monté en lecture seule. Ici, il désigne tout le dossier d’installation pour permettre aux services d’utiliser le BusyBox du paquet ; une véritable application le fait pointer vers sa propre arborescence, par exemple@QPKG_ROOT@/app.- Les
servicesdémarrent dans l’ordre. Unoneshotse termine d’abord ;wait_portattend que le service écoute avant de lancer le suivant. Au moins un service reste en cours d’exécution. inboundrelaie le port 19975 du NAS vers le port interne 8080.egressfournit aux services un proxy sur127.0.0.1:3128pour les ports 80 et 443 des adresses publiques ; une listeallowautorise les destinations nommées quelle que soit leur adresse.limits.memory_mbvaut 2048 par défaut ;stop_secondsindique combien de temps la jail attend l’arrêt de ses services.
Le premier service exécute ce script, également inclus dans le paquet sous shared/share/jailbox/checks.sh. Il écrit ce que permet le bac à sable dans le dossier servi par le second service.
#!/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"
04Les blocs
Sur la page du projet, carte Service and package, onglet Blocks : utilisez Add a block sous At start cinq fois, dans cet ordre.
- Data folder on the volume, nom
jailbox: le dossier$QPKG_DATA. - Create an account, nom
jailbox: le compte indiqué dans le profil. - Create a folder,
$QPKG_DATA/www, propriétairejailbox:jailbox, mode750: le seul dossier dans lequel les services écrivent. - Start in a jail, ID
jailbox, profil$QPKG_ROOT/etc/jail/jailbox.json. Le délai d’arrêt doit être au moins égal austop_secondsdu profil plus 15 ; la valeur par défaut de 90 suffit ici.
- Wait for a port,
19975: le démarrage se termine lorsque le port relayé répond.
Sous At a status check, Is it running? avec l’ID jailbox et Is the port open? avec 19975 permettent au verbe status du script de service de contrôler la jail. Save affiche les différences de chaque fichier et Commit les enregistre dans le dépôt du projet.
05Compiler, signer, installer
Build sur la page du projet lance qbuild de QDK. Son journal indique ce que le paquet embarque pour la jail.
Sans certificat, le paquet est compilé sans signature. La page du projet indique que le déploiement de test et le dépôt exigent un paquet signé.
- Placez les fichiers du certificat,
certificate,private_keyetca_certs, à côté deqpkg.cfgdans le dossier du projet, puis recompilez : le paquet sera signé. Le.gitignoredu modèle exclut ces fichiers de git. En CI, ils proviennent des secrets du dépôt, comme l’explique Votre propre App Center. - Installez le paquet signé : Test deploy sur la page du projet (admin), Install Manually dans App Center ou le dépôt App Center de votre NAS.
06Vérifier le confinement
Ouvrez http://<NAS address>:19975/ : la page affiche « Served from inside the jail. » via le port relayé. Le fichier checks.txt voisin contient les résultats du premier service :
# 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
- Le dossier du volume contient uniquement les dossiers du paquet : son dossier d’installation sous
.qpkget son dossier de données. Aucun partage, aucun autre paquet. - Depuis une session SSH en admin,
grep -E '^(Uid|CapEff|NoNewPrivs|Seccomp):' /proc/<pid>/statusappliqué au service donne le même résultat. Les deux processus bubblewrap qui surveillent la jail de l’extérieur n’ont eux aussi aucune capacité et ontNoNewPrivsactivé, sans le filtre. - Le journal de la jail est
log/jailbox.logdans le dossier de données : démarrage de chaque service, son port et son arrêt. Il signale que le dossier de données appartient à root ; les services écrivent uniquement dans le dossier attribué au compte par le bloc Create a folder.
07Arrêt, suppression et éléments conservés
/etc/init.d/jailbox.sh stop, ou l’arrêt depuis App Center, termine les services dans l’ordre inverse et la jail écritended (0)dans son journal. Aucun processus du compte, aucun cgroup ni aucun port en écoute ne reste.- La suppression du paquet arrête la jail et supprime le dossier d’installation ainsi que le script de service. Le compte et le dossier de données restent, car aucun bloc ne les supprime : Your own shell lines sous At removal peut aussi les supprimer.
- Le dossier d’exécution de la jail,
/var/run/bvs-jail/jailbox, reste jusqu’à ce que le prochain démarrage le réécrive. Il se trouve dans le système de fichiers en mémoire de QTS et disparaît au prochain redémarrage.