Unofficial
In questa pagina
  1. 01Cosa offre la jail
  2. 02Prima di iniziare
  3. 03Il profilo
  4. 04I blocchi
  5. 05Compilare, firmare, installare
  6. 06Verificare l’isolamento
  7. 07Arresto, rimozione e dati conservati

Eseguire il servizio del proprio pacchetto in una jail: account dedicato, senza root, solo la rete consentita

Lo script di servizio di un pacchetto QPKG viene eseguito come root. Con il blocco Start in a jail del generatore di servizi, i programmi avviati usano un account dedicato in una sandbox: nessuna capability né modo di acquisirla, un filtro sulle chiamate di sistema pericolose e una rete propria. L’esempio serve una pagina che mostra ciò che la sandbox permette di fare.

  1. Profilocosa viene eseguito e cosa vede
  2. Blocchiaccount, cartelle, jail
  3. Compilarebvs-jail e bwrap sono inclusi
  4. Firmarecaso 1, poi installare
  5. Verificarela pagina descrive il suo ambiente

Le immagini provengono da Bucket & Code Server IDE 1.0.0-49. L’esempio è stato compilato, firmato, installato e verificato su un NAS di prova con QTS 6.1. L’account si chiama ola.

01Cosa offre la jail

  • I servizi usano l’account indicato nel profilo, mai root, senza capability e con NoNewPrivs attivo. Un filtro seccomp rifiuta mount, nuovi namespace, moduli del kernel, bpf, io_uring, ptrace e le altre chiamate usate dagli exploit del kernel.
  • Vedono l’app in sola lettura, le proprie cartelle dati in lettura e scrittura, /tmp, /proc e /dev privati e la glibc del NAS. Nessun’altra condivisione, nessun /etc/config, nessun altro pacchetto.
  • La loro rete ha un loopback dedicato. Ogni porta inbound viene inoltrata dal NAS a una porta interna. Le connessioni in uscita passano per un proxy HTTP che rifiuta indirizzi privati, di loopback e del NAS.
  • Un limite di memoria, una quota relativa di CPU e un limite di processi si applicano all’intera jail.

Il pacchetto include la jail stessa: bin/bvs-jail e un bin/bwrap statico (bubblewrap), preparati durante la compilazione per ogni architettura.

02Prima di iniziare

  • Servono un progetto QPKG creato con New project, QDK nella pagina Toolchains e l’ambiente di compilazione Ubuntu 24.04. Il proprio App Center ne descrive la configurazione; questo esempio usa il modello di servizio Empty.
  • Un certificato di firma del codice emesso da QNAP a proprio nome, sotto forma di file. App Center installa solo pacchetti firmati.

03Il profilo

Il profilo è un file JSON del pacchetto. Qui si trova in shared/etc/jail/jailbox.json nel progetto e viene installato come etc/jail/jailbox.json nella cartella di installazione del pacchetto.

# 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@ e @QPKG_DATA@ rappresentano le cartelle di installazione e dei dati, note solo sul NAS. Ogni avvio ne risolve i percorsi in una copia modificabile solo da root, che viene usata dalla jail. Le chiavi sconosciute vengono rifiutate.
  • app viene montato in sola lettura. Qui è l’intera cartella di installazione, così i servizi possono usare il BusyBox del pacchetto; un’app reale indica il proprio albero di cartelle, per esempio @QPKG_ROOT@/app.
  • I services partono in ordine. Un oneshot termina prima di proseguire; wait_port attende che il servizio sia in ascolto prima di avviare il successivo. Almeno un servizio rimane in esecuzione.
  • inbound inoltra la porta 19975 del NAS alla porta interna 8080. egress fornisce ai servizi un proxy su 127.0.0.1:3128 per le porte 80 e 443 degli indirizzi pubblici; un elenco allow ammette le destinazioni indicate qualunque sia il loro indirizzo.
  • limits.memory_mb vale 2048 se non viene impostato; stop_seconds indica quanto la jail attende l’arresto dei servizi.

Il primo servizio esegue questo script, anch’esso incluso nel pacchetto come shared/share/jailbox/checks.sh. Scrive ciò che la sandbox consente nella cartella servita dal secondo servizio.

#!/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"

04I blocchi

Nella pagina del progetto, scheda Service and package, sezione Blocks: usare Add a block sotto At start cinque volte, in questo ordine.

Aggiunta di un blocco, gruppo 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, nome jailbox: la cartella $QPKG_DATA.
  2. Create an account, nome jailbox: l’account indicato nel profilo.
  3. Create a folder, $QPKG_DATA/www, proprietario jailbox:jailbox, modalità 750: l’unica cartella in cui i servizi scrivono.
  4. Start in a jail, ID jailbox, profilo $QPKG_ROOT/etc/jail/jailbox.json. I secondi di attesa all’arresto devono essere almeno il valore stop_seconds del profilo più 15; il valore predefinito 90 è sufficiente. 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: l’avvio termina quando la porta inoltrata risponde.

Sotto At a status check, Is it running? con ID jailbox e Is the port open? con 19975 fanno rispondere il comando status dello script di servizio per la jail. Save mostra le differenze di ogni file e Commit le registra nel repository del progetto.

Conferma delle modifiche: service.json con i blocchi di avvio e shared/jailbox.sh con le righe qpkg_data_dir, qpkg_user, qpkg_dir, qpkg_jail e qpkg_wait_port

05Compilare, firmare, installare

Build nella pagina del progetto esegue qbuild di QDK. Il suo log elenca i componenti della jail inclusi nel pacchetto.

Log di compilazione: pacchetto x86_64 con bin/busybox, bin/chpst, share/busybox/, bin/bvs-jail, bin/bwrap e share/bubblewrap/; creato build/jailbox_0.1.0_x86_64.qpkg, senza firma del codice

Senza certificato il pacchetto viene compilato senza firma e la pagina del progetto segnala che il deployment di prova e il repository richiedono un pacchetto firmato.

Pipeline del progetto: Build completato; Test deploy richiede un pacchetto con firma del codice; Publish: non firmato, il repository accetta solo pacchetti firmati
  1. Mettere i file del certificato certificate, private_key e ca_certs accanto a qpkg.cfg nella cartella del progetto e ricompilare: il pacchetto sarà firmato. Il .gitignore del modello esclude questi file da git. In CI provengono dai segreti del repository, come mostra Il proprio App Center.
  2. Installare il pacchetto firmato: Test deploy nella pagina del progetto (admin), Install Manually in App Center oppure il repository App Center del proprio NAS.

06Verificare l’isolamento

Aprire http://<NAS address>:19975/: la pagina mostra “Served from inside the jail.” attraverso la porta inoltrata. Il file checks.txt accanto alla pagina contiene i risultati del primo servizio:

# 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
  • La cartella del volume contiene solo quelle del pacchetto: la cartella di installazione sotto .qpkg e quella dei dati. Nessuna condivisione, nessun altro pacchetto.
  • Da una sessione SSH come admin, grep -E '^(Uid|CapEff|NoNewPrivs|Seccomp):' /proc/<pid>/status del servizio mostra gli stessi valori. Anche i due processi bubblewrap che sorvegliano la jail dall’esterno non hanno capability e hanno NoNewPrivs attivo, senza il filtro.
  • Il log della jail è log/jailbox.log nella cartella dati: avvio di ogni servizio, relativa porta e arresto. Segnala quando la cartella dati appartiene a root; i servizi scrivono solo nella cartella assegnata all’account dal blocco Create a folder.

07Arresto, rimozione e dati conservati

  • /etc/init.d/jailbox.sh stop, o l’arresto da App Center, termina i servizi in ordine inverso e la jail registra ended (0). Non rimangono processi dell’account, cgroup o porte in ascolto.
  • La rimozione del pacchetto arresta la jail ed elimina la cartella di installazione e lo script di servizio. L’account e la cartella dati rimangono perché nessun blocco li rimuove: Your own shell lines sotto At removal può rimuovere anche questi.
  • La cartella di esecuzione della jail, /var/run/bvs-jail/jailbox, rimane finché l’avvio successivo non la riscrive. Si trova nel file system in memoria di QTS e scompare al prossimo riavvio.