Unofficial
このページの内容
  1. 01jail が提供するもの
  2. 02始める前に
  3. 03プロファイル
  4. 04ブロック
  5. 05ビルド、署名、インストール
  6. 06隔離されていることを確認する
  7. 07停止、削除、残るもの

パッケージのサービスを jail で実行する:専用アカウント、root 権限なし、許可したネットワークのみ

QPKG パッケージのサービススクリプトは root として実行されます。サービスビルダーの Start in a jail ブロックを使うと、起動するプログラムはサンドボックス内の専用アカウントで実行されます。ケーパビリティはなく、取得もできません。危険なシステムコールのフィルターと専用ネットワークが適用されます。この例ではページを配信し、サンドボックス内で許可される操作をそのページに表示します。

  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 では実行されず、ケーパビリティはなく、NoNewPrivs が設定されます。seccomp フィルターはマウント、新しい名前空間、カーネルモジュール、bpf、io_uring、ptrace など、カーネルの脆弱性攻撃に使われる呼び出しを拒否します。
  • アプリは読み取り専用、データフォルダーは読み書き可能で、専用の /tmp、/proc、/dev と NAS の glibc が見えます。他の共有、/etc/config、他のパッケージは見えません。
  • ネットワークは専用のループバックです。各 inbound ポートは NAS から内部ポートへ中継されます。外向き接続は HTTP プロキシを通り、プライベートアドレス、ループバック、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 はサービスが待ち受けを始めてから次のサービスを起動します。少なくとも 1 つのサービスは実行を続けます。
  • inbound は NAS のポート 19975 を内部ポート 8080 へ中継します。egress は 127.0.0.1:3128 でプロキシを提供し、公開アドレスのポート 80 と 443 への接続を許可します。allow リストで指定した宛先は、アドレスにかかわらず接続できます。
  • limits.memory_mb は指定しなければ 2048 です。stop_seconds は jail がサービスの停止を待つ時間です。

最初のサービスは、パッケージに shared/share/jailbox/checks.sh として含まれる次のスクリプトを実行します。サンドボックスで許可される操作を調べ、2 番目のサービスが配信するフォルダーへ結果を書き込みます。

#!/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 を使い、次の順に 5 個のブロックを追加します。

ブロック追加の 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 と、qpkg_data_dir、qpkg_user、qpkg_dir、qpkg_jail、qpkg_wait_port の行を含む shared/jailbox.sh

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 を外側から監視する 2 つの bubblewrap プロセスにもケーパビリティはなく、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 のメモリファイルシステム上にあるため、次の再起動で消えます。