bubblewrap
bubblewrap は Flatpak から発展した軽量な setuid サンドボックスアプリケーションです。インストール容量が小さくリソース要件は最小限です。アプリケーションパッケージ自体は bubblewrap と呼ばれますが、実際の実行バイナリや man ページで使われている名前は bwrap(1) です。bubblewrap は将来的に Tor Browser (Linux 版) の サンドボックス機構 として使われることが期待されています。特徴として cgroup/IPC/マウント/ネットワーク/PID/ユーザー/UTS 名前空間や seccomp フィルタリングをサポートしています。bubblewrap はサンドボックス内の全てのケイパビリティを破棄して子タスクが親以上の権限を得られないようにします。ファイルパスベースのブラックリストやホワイトリストは対応していません。
目次
インストール
bubblewrap または bubblewrap-gitAUR をインストールしてください。
設定
bubblewrap はコマンドラインから直接起動したり、複雑なラッパー に含まれる シェルスクリプト で呼び出すことができます。サンドボックス内の /var
や /etc
を自動的に読み取り専用に設定する Firejail などのアプリケーションと違って、bubblewrap はそのような操作を行いません。サンドボックス化したいアプリケーションにあわせてどのような設定オプションを指定するかはユーザー次第です。setuid 特権で実行された場合でも bubblewrap は自動的にユーザー名前空間を作成しないので $HOME
や $USER
などの環境変数を利用することが可能です。
strace をダウンロードして、サンドボックス化しようとしているプログラムがどのファイルにアクセスする必要があるかを確認することを強くお勧めします。
使用例
No-op
以下のように何もせずに bubblewrap を呼び出すことができます:
$ bwrap --dev-bind / / bash
サンドボックスの外のように振る舞う bash プロセスが生成されます。サンドボックス化したプログラムがおかしくなってしまう場合、上記の no-op から段々と設定をセキュアにしていくことができます。
Bash
シンプルな Bash のサンドボックスを作成:
- 利用可能なカーネル名前空間を確認:
$ ls /proc/self/ns cgroup ipc mnt net pid user uts
$ bwrap --ro-bind / / --unshare-user --uid 256 --gid 512 bash bash-4.4$ id uid=256 gid=512 groups=512,65534(nobody) bash-4.4$ ls -l /usr/bin/bash -rwxr-xr-x 1 nobody nobody 811752 2017-01-01 04:20 /usr/bin/bash
dhcpcd
シンプルな dhcpcd のサンドボックスを作成:
- 利用可能なカーネル名前空間を確認:
$ ls /proc/self/ns cgroup ipc mnt net pid uts
- ホストの
/
ディレクトリ全体を読み書き可能でサンドボックスの/
にバインド。 - 新しい devtmpfs ファイルシステムをサンドボックスの
/dev
にマウント。 - 新しい IPC 名前空間とコントロールグループ名前空間を作成。
- 新しい UTS 名前空間を作成して
dhcpcd
をホストネームとして設定。
# /usr/bin/bwrap --bind / / --dev /dev --unshare-ipc --unshare-cgroup --unshare-uts --hostname dhcpcd /usr/bin/dhcpcd -q -b
Unbound
粒度の細かい複雑な Unbound のサンドボックスを作成:
- システムの
/usr
ディレクトリを読み取り専用でサンドボックスの/usr
にバインド。 - システムの
/usr/lib
ディレクトリからサンドボックスの/lib64
に対してシンボリックリンクを作成。 - システムの
/etc
ディレクトリを読み取り専用でサンドボックスの/etc
にバインド。 - サンドボックスの中に空の
/var
と/run
ディレクトリを作成。 - 新しい devtmpfs ファイルシステムをサンドボックスの
/dev
にマウント。 - 新しい IPC 名前空間と PID 名前空間とコントロールグループ名前空間を作成。
- 新しい UTS 名前空間を作成して
unbound
をホストネームとして設定。
# /usr/bin/bwrap --ro-bind /usr /usr --symlink usr/lib /lib64 --ro-bind /etc /etc --dir /var --dir /run --dev /dev --unshare-ipc --unshare-pid --unshare-cgroup --unshare-uts --hostname unbound /usr/bin/unbound -d
デスクトップ
デスクトップエントリで bubblewrap を活用:
- ホストの
/
ディレクトリ全体を読み書き可能でサンドボックスの/
にバインド。 - サンドボックスの
/var
と/etc
ディレクトリを読み取り専用で再バインド - 新しい devtmpfs ファイルシステムをサンドボックスの
/dev
にマウント。 - サンドボックスの
/run
ディレクトリに tmpfs ファイルシステムを作成。 - 新しいネットワーク名前空間を作成してネットワークアクセスを無効化。
[Desktop Entry] Name=nano Editor Exec=bwrap --bind / / --dev /dev --tmpfs /run --unshare-net st -e nano -o . %f Type=Application MimeType=text/plain;
mupdf.sh
シェルラッパーを組み込んだ MuPDF のデスクトップエントリの例:
[Desktop Entry] Name=MuPDF Exec=mupdf.sh %f Icon=application-pdf.svg Type=Application MimeType=application/pdf;application/x-pdf;
MuPDF
bwrap の能力と柔軟性が一番良くわかるのはシェルラッパーの中に環境を作成するときです:
- ホストの
/usr/bin
ディレクトリを読み取り専用でサンドボックスの/usr/bin
にバインド。 - ホストの
/usr/lib
ディレクトリを読み取り専用でサンドボックスの/usr/lib
にバインド。 - システムの
/usr/lib
ディレクトリからサンドボックスの/lib64
に対してシンボリックリンクを作成。 - サンドボックスの
/usr/lib/gcc
の上に tmpfs ファイルシステムを作成。- 上記の設定でサンドボックスから
/usr/lib/gcc
の中身を効率的に遮蔽できます。
- 上記の設定でサンドボックスから
- 新しい tmpfs ファイルシステムをサンドボックスの
$HOME
ディレクトリとして作成。 .Xauthority
ファイルと Documents ディレクトリをサンドボックスに読み取り専用としてバインド。- 上記の設定で
.Xauthority
ファイルと Documents ディレクトリが再帰的にホワイトリストに追加されます。
- 上記の設定で
- 新しい tmpfs ファイルシステムをサンドボックスの
/tmp
ディレクトリとして作成。 - X11 ソケットをサンドボックスに読み取り専用としてバインドしてホワイトリストに追加。
- 実行中のカーネルによってサポートされている全ての名前空間に対してプレイベートなコンテナを複製・作成。
- カーネルが非特権のユーザー名前空間をサポートしてなかった場合、作成がスキップされます。
- ネットワークコンポーネントをプライベート名前空間に配置しない。
- 上記の設定で URI ハイパーリンクを辿ってネットワークにアクセスできます。
#!/bin/sh #~/bwrap/mupdf.sh (exec bwrap \ --ro-bind /usr/bin /usr/bin \ --ro-bind /usr/lib /usr/lib \ --symlink usr/lib /lib64 \ --tmpfs /usr/lib/gcc \ --tmpfs $HOME \ --ro-bind $HOME/.Xauthority $HOME/.Xauthority \ --ro-bind $HOME/Documents $HOME/Documents \ --tmpfs /tmp \ --ro-bind /tmp/.X11-unix/X0 /tmp/.X11-unix/X0 \ --unshare-all \ --share-net \ /usr/bin/mupdf "$@")
$ bwrap \ --ro-bind /usr/bin /usr/bin \ --ro-bind /usr/lib /usr/lib \ --symlink usr/lib /lib64 \ --tmpfs /usr/lib/gcc \ --tmpfs $HOME \ --ro-bind $HOME/.Xauthority $HOME/.Xauthority \ --ro-bind $HOME/Desktop $HOME/Desktop \ --tmpfs /tmp \ --ro-bind /tmp/.X11-unix/X0 /tmp/.X11-unix/X0 \ --unshare-all \ --share-net \ /usr/bin/sh bash-4.4$ ls -AF .Xauthority Documents/
bubblewrap によるファイルシステムを作成するときに一番重要なルールは実行されるコマンドの順番です。上記の MuPDF の例の場合:
- tmpfs ファイルシステムを作成してから
.Xauthority
ファイルと Documents ディレクトリをバインドマウントした場合:
--tmpfs $HOME \ --ro-bind $HOME/.Xauthority $HOME/.Xauthority \ --ro-bind $HOME/Documents $HOME/Documents \
bash-4.4$ ls -a . .. .Xauthority Documents
.Xauthority
をバインドマウントしてから tmpfs ファイルシステムを作成して上書きするとサンドボックスからは Documents ディレクトリだけが見えます:
--ro-bind $HOME/.Xauthority $HOME/.Xauthority \ --tmpfs $HOME \ --ro-bind $HOME/Documents $HOME/Documents \
bash-4.4$ ls -a . .. Documents
p7zip
既知の脆弱性 に対してパッチが適用されていないアプリケーションは特に bubblewrap を使用する価値があります:
- ホストの
/usr/bin/7za
実行パスを読み取り専用でサンドボックスにバインド。 - システムの
/usr/lib
ディレクトリからサンドボックスの/lib64
に対してシンボリックリンクを作成。 - tmpfs オーバーレイでサンドボックス化した
/usr/lib/modules
と/usr/lib/systemd
の中身をブラックリストに追加。 - 新しい devtmpfs ファイルシステムをサンドボックスの
/dev
にマウント。 - ホストの
/sandbox
ディレクトリを読み書き可能でサンドボックスの/sandbox
ディレクトリにバインド。- シェルラッパーから呼び出すことで 7za はホストの
/sandbox
ディレクトリとそのサブディレクトリの中でしか実行されなくなります。
- シェルラッパーから呼び出すことで 7za はホストの
- アプリケーションとプロセスのために新しい cgroup/IPC/ネットワーク/PID/UTS 名前空間を作成。
- カーネルが非特権のユーザー名前空間をサポートしていなかった場合、作成はスキップされます。
- 新しいネットワーク名前空間を作成することでサンドボックスからネットワークにアクセスできなくなります。
p7zip
というホストネームをサンドボックスに追加。XAUTHORITY
環境変数の設定を解除して X11 の接続クッキーの場所を遮蔽。- 7za を動作させるのに X11 ディスプレイサーバーに接続する必要はありません。
- 新しいターミナルセッションを起動してキーボードの入力がサンドボックス外に流出することを防止。
#!/bin/sh #~/bwrap/pz7ip.sh (exec bwrap \ --ro-bind /usr/bin/7za /usr/bin/7za \ --symlink usr/lib /lib64 \ --tmpfs /usr/lib/modules \ --tmpfs /usr/lib/systemd \ --dev /dev \ --bind /sandbox /sandbox \ --unshare-all \ --hostname p7zip \ --unsetenv XAUTHORITY \ --new-session \ /usr/bin/7za "$@")
bwrap \ --ro-bind /usr/bin/7za /usr/bin/7za \ --ro-bind /usr/bin/ls /usr/bin/ls \ --ro-bind /usr/bin/sh /usr/bin/sh \ --symlink usr/lib /lib64 \ --tmpfs /usr/lib/modules \ --tmpfs /usr/lib/systemd \ --dev /dev \ --bind /sandbox /sandbox \ --unshare-all \ --hostname p7zip \ --unsetenv XAUTHORITY \ --new-session \ /usr/bin/sh bash: no job control in this shell bash-4.4$ ls -AF dev/ lib64@ usr/ bash-4.4$ ls -l /usr/lib/modules total 0 bash-4.4$ ls -l /usr/lib/systemd total 0 bash-4.4$ ls -AF /dev console full null ptmx@ pts/ random shm/ stderr@ stdin@ stdout@ tty urandom zero bash-4.4$ ls -A /usr/bin 7za ls sh
ファイルシステムの分離
(/var
, /usr/bin
, /usr/lib
の中身など) ファイルシステムの中身を遮蔽しながらソフトウェアのインストールをサンドボックス化したい場合、pacman で Arch のパッケージを分離したファイルシステムツリーにインストールすることができます。
pacman を使ってソフトウェアをファイルシステムツリーにインストールするには、fakeroot と fakechroot のインストールが必要です。
例として pacman で xterm
パッケージを隔離ファイルシステムツリーにインストールしたいとすると、ツリーを以下のように準備します:
$ MYPACKAGE=xterm $ mkdir -p ~/sandboxes/${MYPACKAGE}/files/var/lib/pacman $ mkdir -p ~/sandboxes/${MYPACKAGE}/files/etc $ cp /etc/pacman.conf ~/sandboxes/${MYPACKAGE}/files/etc/pacman.conf
~/sandboxes/${MYPACKAGE}/files/etc/pacman.conf
を編集して使用する pacman の設定を調整すると良いでしょう:
- 不必要なカスタムリポジトリやホスト環境でしか必要ない
IgnorePkg
,IgnoreGroup
,NoUpgrade
,NoExtract
の設定を削除。 CheckSpace
オプションを削除して pacman がディスク容量をチェックするために root ファイルシステムを検索するときのエラーを消す。
base
グループと必須の fakeroot を分離ファイルシステムツリーにインストール:
$ fakechroot fakeroot pacman -Syu \ --root ~/sandboxes/${MYPACKAGE}/files \ --dbpath ~/sandboxes/${MYPACKAGE}/files/var/lib/pacman \ --config ~/sandboxes/${MYPACKAGE}/files/etc/pacman.conf \ base fakeroot
同じオプションを使って bubblewrap を何度も呼び出すことになるので、以下のようにエイリアスを作成:
$ alias bw-install='bwrap \ --bind ~/sandboxes/${MYPACKAGE}/files/ / \ --ro-bind /etc/resolv.conf /etc/resolv.conf \ --tmpfs /tmp \ --proc /proc \ --dev /dev \ --chdir / '
ロケールを設定してください:
$ nano -w ~/sandboxes/${MYPACKAGE}/files/etc/locale.gen $ bw-install locale-gen
pacman のキーリングを設定:
$ bw-install fakeroot pacman-key --init $ bw-install fakeroot pacman-key --populate archlinux
以下のように xterm
パッケージをインストール:
$ bw-install fakeroot pacman -S ${MYPACKAGE}
pacman コマンドが失敗する場合、キーリングを生成するコマンドをもう一度実行してみてください。
これで xterm
を含む分離ファイルシステムツリーが作られました。bw-install
を使ってファイルシステムツリーをアップグレードすることができます。
bubblewrap でソフトウェアを実行することができます (上記の例では command
は xterm
になります):
$ bwrap \ --ro-bind ~/sandboxes/${MYPACKAGE}/files/ / \ --ro-bind /etc/resolv.conf /etc/resolv.conf \ --tmpfs /tmp \ --proc /proc \ --dev /dev \ --chdir / \ command
一部のファイルをパッケージ間で共有することもできます。既存のファイルシステムツリーのファイルのハードリンクを作成して新しいツリーで再利用することが可能です:
$ cp -al ~/sandboxes/${MYPARENTPACKAGE} ~/sandboxes/${MYPACKAGE}
bw-install fakechroot fakeroot pacman …
から pacman を呼び出して通常通りにインストールを行ってください。
トラブルシューティング
X11 を使う
ホストの X11 ソケットを別の X11 ソケットにバインドマウントしても上手く行かないことがあります:
--bind /tmp/.X11-unix/X0 /tmp/.X11-unix/X8 --setenv DISPLAY :8
ホストの X11 ソケットをサンドボックス内の同じソケットにバインドマウントすることで解決します:
--bind /tmp/.X11-unix/X0 /tmp/.X11-unix/X0 --setenv DISPLAY :0
X11 のサンドボックス化
bwrap はアプリケーションをサンドボックス化するための素晴らしい隔離機能を備えていますが、X11 ソケットにアクセスできてしまえば簡単に離脱することができます。X11 にはアプリケーション間の分離が存在しないためセキュリティ的に危険です。サンドボックスから X サーバーにアクセスできないようにする方法として Wayland コンポジタに切り替える方法があります。
また、xpra や xephyr を使って新しい X11 環境で実行させるという方法もあります。この方法で bwrap を使うこともできます。
X11 の隔離化をテストするには、xinput test <id>
を実行してください (<id> は xinput list
で確認できるキーボード ID に置き換えてください)。X11 が分離化されていない場合、X11 にアクセスできるアプリケーションはキーロガーのように他のアプリケーションのキーボード入力を盗聴することができてしまいます。
ラッピングしたアプリケーションから URL を開く
ラッピングした IRC やメールクライアントで URL を開くと、起動するブラウザプロセスも同じサンドボックス内で動作します。アプリケーションの分離が強固な場合、うまく機能しないことがあります。Firejail では ラッピングしたアプリケーションにブラウザと同じ権限を与える ことで解決しますが、パーミッションがゆるくなってしまいます。
開いた URL をサンドボックスの外と通信するほうが良手です。以下のように snapd-xdg-open
を使ってください:
- snapd-xdg-open-gitAUR をインストール
bwrap
コマンドラインで以下を追加:
$ bwrap ... \ --ro-bind /run/user/$UID/bus /run/user/$UID/bus \ --ro-bind /usr/lib/snapd-xdg-open/xdg-open /usr/bin/xdg-open \ --ro-bind /usr/lib/snapd-xdg-open/xdg-open /usr/bin/chromium \ ...
/usr/bin/chromium
のバインドは Mozilla Thunderbird など XDG に準拠していないプログラムでのみ必要です。
新規セッション
TIOCSTI にはサンドボックスから脱出できるセキュリティ問題が存在します (CVE-2017-5226)。この問題に対応するために、bubblewrap には setsid() を呼び出す新しいオプション '--new-session' が追加されています。ただしこのオプションはときとして扱いにくい挙動上の問題を起こすことがあります。例えば bwrap コマンドでシェルのジョブ制御が機能しなくなります。
可能であればオプションを使用することが推奨されていますが、そうでない場合は別の方法で問題を解決することが推奨されます。例えば flatpak は SECCOMP を使用します: https://github.com/flatpak/flatpak/commit/902fb713990a8f968ea4350c7c2a27ff46f1a6c4