在 jail 中執行您套件的服務:專屬帳號、沒有 root 權限,只能使用您允許的網路
QPKG 套件的服務指令稿以 root 執行。使用服務建構器的 Start in a jail 區塊後,它啟動的程式會在沙箱中以專屬帳號執行:沒有 capabilities,也無法取得,並套用危險系統呼叫篩選器與獨立網路。此範例提供一個網頁,並在頁面上顯示沙箱允許的操作。
- 設定檔執行內容與可見範圍
- 區塊帳號、資料夾與 jail
- 建置附帶 bvs-jail 與 bwrap
- 簽署情況 1,然後安裝
- 驗證網頁回報自己的環境
圖片來自 Bucket & Code Server IDE 1.0.0-49。此範例已在執行 QTS 6.1 的測試 NAS 上完成建置、簽署、安裝與驗證。帳號名稱為 ola。
01jail 提供的功能
- 服務以設定檔指定的帳號執行,絕不使用 root,沒有 capabilities,並設有
NoNewPrivs。seccomp 篩選器拒絕掛載、新命名空間、核心模組、bpf、io_uring、ptrace,以及其他核心漏洞攻擊會用到的呼叫。 - 它們只能唯讀存取應用程式,可讀寫自己的資料目錄,並看到私有的
/tmp、/proc、/dev與 NAS 的 glibc。看不到其他共用資料夾、/etc/config或其他套件。 - 它們的網路只有專屬回送介面。每個
inbound連接埠會從 NAS 轉送到內部連接埠。對外連線經過 HTTP Proxy,拒絕私人位址、回送位址與 NAS 自身位址。 - 記憶體上限、CPU 權重與處理程序數上限適用於整個 jail。
套件內含 jail 本身:bin/bvs-jail 與靜態連結的 bin/bwrap(bubblewrap),在建置時依各架構備妥。
02開始之前
- 您需要透過 New project 建立的 QPKG 專案、Toolchains 頁面的 QDK,以及 Ubuntu 24.04 建置環境。您自己的 App Center 說明設定步驟;此範例使用 Empty 服務預設範本。
- 您需要 QNAP 核發給您的程式碼簽署憑證檔案。App Center 只會安裝已簽署的套件。
03設定檔
設定檔是套件中的 JSON 檔案。此範例位於專案的 shared/etc/jail/jailbox.json,套件會將它安裝為安裝資料夾內的 etc/jail/jailbox.json。
# 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@與@QPKG_DATA@代表安裝資料夾與資料目錄,實際路徑只有在 NAS 上才會確定。每次啟動都會將路徑填入一份只有 root 能修改的副本,jail 使用這份副本。未知的鍵會被拒絕。app以唯讀方式繫結掛載。此處指定整個安裝資料夾,讓服務能使用套件自身的 BusyBox;實際應用程式則指定自己的目錄樹,例如@QPKG_ROOT@/app。services依序啟動。oneshot會先執行完畢;wait_port會等到服務開始接聽後才啟動下一個服務。至少有一個服務持續執行。inbound將 NAS 的連接埠 19975 轉送到內部的 8080。egress在127.0.0.1:3128為服務提供 Proxy,允許存取公開位址的連接埠 80 與 443;allow清單中指定的目的地則不受位址限制。limits.memory_mb未設定時為 2048;stop_seconds指定 jail 等待服務停止的時間。
第一個服務執行下列指令稿,套件中也包含此檔案,路徑為 shared/share/jailbox/checks.sh。它將沙箱允許的操作寫入第二個服務提供的資料夾。
#!/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"
04區塊
在專案頁面的 Service and package 卡片、Blocks 分頁中,於 At start 下使用 Add a block 五次,依以下順序加入區塊。
- Data folder on the volume,名稱
jailbox:建立資料夾$QPKG_DATA。 - Create an account,名稱
jailbox:設定檔中指定的帳號。 - Create a folder,
$QPKG_DATA/www,擁有者jailbox:jailbox,模式750:這是服務唯一能寫入的資料夾。 - Start in a jail,ID
jailbox,設定檔$QPKG_ROOT/etc/jail/jailbox.json。停止時的等待秒數至少須為設定檔的stop_seconds加上 15;此處預設的 90 已足夠。
- Wait for a port,
19975:轉送連接埠回應後,啟動程序才結束。
在 At a status check 下加入 ID 為 jailbox 的 Is it running?,以及指定 19975 的 Is the port open?,讓服務指令稿的 status 命令檢查 jail。Save 顯示每個檔案的差異,Commit 則將變更寫入專案的儲存庫。
05建置、簽署、安裝
專案頁面的 Build 會執行 QDK 的 qbuild。記錄會列出套件為 jail 附帶的檔案。
沒有憑證時,建置出的套件不含簽章;專案頁面會指出測試部署與儲存庫都需要已簽署的套件。
- 將憑證檔案
certificate、private_key與ca_certs放在專案資料夾內的qpkg.cfg旁,再次建置即可產生已簽署套件。範本的.gitignore會將這些檔案排除在 git 之外。CI 從儲存庫的機密資料取得檔案,詳見 您自己的 App Center。 - 安裝已簽署套件:使用專案頁面的 Test deploy(管理員)、App Center 的 Install Manually,或您 NAS 自己的 App Center 儲存庫。
06驗證隔離狀態
開啟 http://<NAS address>:19975/,頁面透過轉送連接埠顯示「Served from inside the jail.」。同一位置的 checks.txt 包含第一個服務的檢查結果:
# 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
- 磁碟區資料夾只包含套件自己的資料夾:
.qpkg下的安裝資料夾與資料目錄。沒有共用資料夾,也沒有其他套件。 - 在管理員的 SSH 工作階段中,對服務執行
grep -E '^(Uid|CapEff|NoNewPrivs|Seccomp):' /proc/<pid>/status會得到相同結果。從外部監看 jail 的兩個 bubblewrap 處理程序也沒有 capabilities,並設有NoNewPrivs,但沒有套用篩選器。 - jail 的記錄位於資料目錄的
log/jailbox.log,包含每個服務的啟動、連接埠與停止。當資料目錄屬於 root 時,記錄也會註明;服務只能寫入 Create a folder 區塊指派給該帳號的資料夾。
07停止、移除與保留項目
/etc/init.d/jailbox.sh stop或 App Center 的停止操作會以反向順序結束服務,jail 會記錄ended (0)。不會留下該帳號的處理程序、cgroup 或接聽連接埠。- 移除套件會停止 jail,並刪除安裝資料夾與服務指令稿。帳號與資料目錄會保留,因為沒有區塊負責刪除它們;您可在 At removal 使用 Your own shell lines 一併移除。
- jail 的執行階段資料夾
/var/run/bvs-jail/jailbox會保留到下次啟動時重寫。它位於 QTS 的記憶體檔案系統,下次重新啟動 NAS 時就會消失。