Unofficial
本頁內容
  1. 01jail 提供的功能
  2. 02開始之前
  3. 03設定檔
  4. 04區塊
  5. 05建置、簽署、安裝
  6. 06驗證隔離狀態
  7. 07停止、移除與保留項目

在 jail 中執行您套件的服務:專屬帳號、沒有 root 權限,只能使用您允許的網路

QPKG 套件的服務指令稿以 root 執行。使用服務建構器的 Start in a jail 區塊後,它啟動的程式會在沙箱中以專屬帳號執行:沒有 capabilities,也無法取得,並套用危險系統呼叫篩選器與獨立網路。此範例提供一個網頁,並在頁面上顯示沙箱允許的操作。

  1. 設定檔執行內容與可見範圍
  2. 區塊帳號、資料夾與 jail
  3. 建置附帶 bvs-jail 與 bwrap
  4. 簽署情況 1,然後安裝
  5. 驗證網頁回報自己的環境

圖片來自 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 五次,依以下順序加入區塊。

新增區塊的 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,名稱 jailbox:建立資料夾 $QPKG_DATA。
  2. Create an account,名稱 jailbox:設定檔中指定的帳號。
  3. Create a folder,$QPKG_DATA/www,擁有者 jailbox:jailbox,模式 750:這是服務唯一能寫入的資料夾。
  4. Start in a jail,ID jailbox,設定檔 $QPKG_ROOT/etc/jail/jailbox.json。停止時的等待秒數至少須為設定檔的 stop_seconds 加上 15;此處預設的 90 已足夠。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:轉送連接埠回應後,啟動程序才結束。

在 At a status check 下加入 ID 為 jailbox 的 Is it running?,以及指定 19975 的 Is the port open?,讓服務指令稿的 status 命令檢查 jail。Save 顯示每個檔案的差異,Commit 則將變更寫入專案的儲存庫。

確認提交變更:service.json 包含啟動區塊,shared/jailbox.sh 包含 qpkg_data_dir、qpkg_user、qpkg_dir、qpkg_jail 與 qpkg_wait_port 各行

05建置、簽署、安裝

專案頁面的 Build 會執行 QDK 的 qbuild。記錄會列出套件為 jail 附帶的檔案。

建置記錄:x86_64 套件內含 bin/busybox、bin/chpst、share/busybox/、bin/bvs-jail、bin/bwrap 與 share/bubblewrap/;產生 build/jailbox_0.1.0_x86_64.qpkg,沒有程式碼簽章

沒有憑證時,建置出的套件不含簽章;專案頁面會指出測試部署與儲存庫都需要已簽署的套件。

專案流程:Build 完成;Test deploy 需要程式碼簽署的套件;Publish 顯示未簽署,儲存庫只接受已簽署套件
  1. 將憑證檔案 certificate、private_key 與 ca_certs 放在專案資料夾內的 qpkg.cfg 旁,再次建置即可產生已簽署套件。範本的 .gitignore 會將這些檔案排除在 git 之外。CI 從儲存庫的機密資料取得檔案,詳見 您自己的 App Center。
  2. 安裝已簽署套件:使用專案頁面的 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 時就會消失。