
foxcage — Updated!
rootless Podman コンテナ内で Firefox を実行し、capabilities を削除してネットワークを分離、一時ストレージを使用することで、サンドボックスエスケープを封じ込め、ホストの侵害を防ぎます。
foxcage
ルートレスPodmanコンテナでFirefoxを実行し、セキュリティを分離します。ブラウザはほぼすべてのLinuxケーパビリティを持たず、独自のユーザーおよびネットワーク名前空間でホストから分離された状態で動作します。それでもGPUアクセラレーション、オーディオ、DRMサポートは完全に利用できます。
foxcageの利点
Firefoxにはすでに、Linux名前空間とseccomp-bpfを使用してWebコンテンツレンダラーを分離するマルチプロセスサンドボックスがあります。ほとんどの脅威に対しては、これで効果的です。foxcageは第二の壁を追加します。攻撃者がFirefoxのサンドボックスを脱出する脆弱性(実際に存在し、CVEもあります)を悪用した場合、攻撃者は完全なユーザーセッションではなく、ロックダウンされたコンテナ内に着地します。
foxcageが防御するもの
- エクスプロイト後のファイルアクセス。 素のFirefoxでのサンドボックス脱出は、ユーザーが読み取れるすべてのもの(
~/.ssh、~/.gnupg、他のブラウザのプロファイル、パスワードマネージャーのデータベース、ドキュメント、ソースコード)へのアクセスを許します。foxcageでは、攻撃者は明示的にマウントしたものだけを見ることができます。 - ディスク上の追跡痕跡。
@tmp一時ケージは、ウィンドウを閉じた後にディスク上に痕跡を一切残しません。Firefoxのプライベートブラウジングが依然として保持する拡張機能、HSTS状態、TLSセッションキャッシュ、DNSキャッシュも含みます。複数の@tmpケージは互いに干渉せずに同時に実行できます。 - 永続化。 素のFirefoxでは、マルウェアは
~/.config/autostart、~/.bashrc、cron、その他の場所に書き込んで再起動後も生き残ることができます。foxcageの一時コンテナ(--rm)は、バインドマウントしない限り何も永続化しないことを意味します。 - ネットワークの横方向移動。 デフォルトでは、コンテナは
localhost上のサービスをプローブできません。素のFirefoxでは、サンドボックス脱出は完全なネットワークアクセスを持ちます。(ローカル開発などでケージがlocalhostアクセスを必要とする場合は[network] mode = "host"を使用してください。ただし「ネットワーク」の下の注意事項を参照してください。ホストモードはホストの抽象Unixソケットも公開します。) - 権限昇格。 コンテナは
CAP_SYS_CHROOTを除くすべてのLinuxケーパビリティをドロップし、新しい権限の取得をブロックします。Setuidバイナリ、不明瞭なシステムコールによるカーネルエクスプロイト、および同様の昇格経路は遮断されます。
foxcageが防御しないもの
- ブラウザレベルの攻撃。 フィッシング、悪意のある拡張機能、およびFirefoxの通常機能内で動作するものは影響を受けません。foxcageはコンテナをホストから分離するものであり、ユーザーをブラウザから分離するものではありません。
- バインドマウントされたディレクトリ。 マウントしたもの(
profile、downloads_dir、追加のバインドマウント)は、侵害されたブラウザから完全にアクセス可能です。ホストのプロファイルディレクトリをマウントした場合、攻撃者は素のFirefoxと同様にそれを改ざんできます。 - PulseAudioを介したオーディオキャプチャ。 PulseAudioソケットはコンテナにバインドマウントされます。ファイルシステムレベルでは読み取り専用でマウントされていますが、Unixドメインソケットは双方向です。侵害されたプロセスはソケットを通じて録音リクエストを送信できます。ブラウザのサンドボックス脱出は、ホストのマイクからオーディオを録音する可能性があります。
- Waylandコンポジターのエクスプロイト。 Waylandソケットは透過されます。Waylandコンポジターは設計上クライアントを互いに分離しますが、コンポジター自体の脆弱性には到達可能です。
セキュリティ設定
コンテナは以下で実行されます:
- すべてのLinuxケーパビリティをドロップ(Firefoxのコンテンツサンドボックス用に
CAP_SYS_CHROOTのみ追加で戻す。init.rootが設定されている場合はCAP_SETUID/CAP_SETGIDを一時的に追加) - 権限昇格を防ぐ
no-new-privileges - ルートレスユーザー名前空間(
--userns keep-id) - プライベートな
/dev/shm(ホストと共有しない)—shm_sizeでサイズ設定可能 - ホストのループバックをデフォルトでブロックするpastaによる分離ネットワーク
- DNSはデフォルトでホストDNSを使用(
network.dnsで設定可能) XDG_RUNTIME_DIRからの特定のソケットのみがバインドマウントされる(Wayland、PulseAudio、PipeWire、およびフィルタリングされたD-Busプロキシ)— ホストのランタイムディレクトリ全体が公開されることはありません- ホストのD-Busセッションバスへのアクセスは常に、ホスト上で実行されるフィルタリングされた
xdg-dbus-proxyによって仲介されます。org.freedesktop.Notifications、org.freedesktop.portal.Desktop、org.mozilla.*、および(フォークの場合)フォーク自身の名前空間(例:org.librewolf.*)のみが到達可能です。キーリングやSSH/GPGエージェントなどのセッションサービスはブロックされます - ポータルアクセスは広範です。
org.freedesktop.portal.Desktopは全体として許可されています。これはファイルピッカー、「リンクを別のアプリで開く」、画面共有が機能する方法だからです。また、RemoteDesktop(セッション全体の合成キーボード/マウス)、Camera、Locationも公開します。これらはfoxcageではなく、デスクトップ自身の承認ダイアログによってゲートされます。また、RemoteDesktopプロンプトは画面共有プロンプトに似ているため、承認ダイアログを読んでから受け入れてください。xdg-dbus-proxyには「1つのインターフェースを拒否する」ルールがないため、これを狭めるにはFirefoxが必要とするすべてのインターフェースを列挙する必要があります。デフォルトでこれを行わない理由についてはdocs/DESIGN.mdを参照してください - すべてのバインドマウント(
profile、downloads_dir、追加の[mounts] bind)はnosuid,noexecを使用 - ブラウザのダウンロードはGPG署名に対して検証:FirefoxはMozillaの署名付きSHA-512チェックサムに対して、LibreWolfはLibreWolf Maintainersの分離署名と兄弟SHA-256に対して検証。検証は
gpg --verifyよりも厳格です。gpg --verifyは失効したキーによる署名やキーリング内の任意のキーに対してexit 0を返します。foxcageはさらに、署名が固定されたプライマリキーにチェーンすることを要求し、所有者が侵害されたとして失効させたサブキーによって署名されたリリースを拒否します — 失効した署名キーを参照 - 一時コンテナ(
--rm)— ファイルシステムへの書き込みは終了時に失われます - 明示的に有効にしない限り、ホストデバイス(ウェブカメラ、セキュリティキー、プリンター)は透過されません
有効にする各[network]および[mounts]オプションは、利便性のために分離の一部を犠牲にします。デフォルトは、使用可能なブラウザを提供する最も制限的な設定です。
要件
- Python 3.11+
- Podman(ルートレス)
- Waylandコンポジター(X11はサポートされていません)
- pasta(
sudo apt install passt)—network.mode = "host"でない限り - xdg-dbus-proxy(
sudo apt install xdg-dbus-proxy) - PulseAudio互換性のあるPulseAudioまたはPipeWire(オーディオ用)
- DRIサポートのあるGPU — オプション。
/dev/driがない場合、foxcageは警告し、Firefoxはソフトウェアレンダリングになります。Intel、AMD、nouveau用のVA-APIドライバーがイメージにインストールされているため、ホストのドライバーパッケージなしでハードウェアビデオデコードが機能します — ハードウェアビデオデコードを参照
foxcageを通常のデスクトップユーザーとして実行してください。rootやsudo経由では実行しないでください。サンドボックスはユーザーをコンテナにマップし、rootとして実行するとfoxcageが存在する目的である分離が失われます。rootとしての起動は拒否されます。
テスト済み環境: GNOME 3を搭載したDebian 13(Trixie)。他のLinuxディストリビューションやWaylandコンポジターでも動作する可能性がありますが、テストされていません。
インストール
foxcageは、Python標準ライブラリ以外に依存関係のない単一のPythonスクリプトです。PATH内のディレクトリにコピーしてください:```sh
sudo cp foxcage /usr/local/bin/foxcage
またはユーザーローカルインストールの場合:```sh
cp foxcage ~/.local/bin/foxcage
スクリプトを実行可能にしてください(chmod +x foxcage)。
問題を報告する際に役立つので、foxcage --version でリビジョンを確認してください。foxcage は単一ファイルのコピーでインストールされるためです。
使用方法```sh
./foxcage
初回実行時、スクリプトはコンテナイメージをビルドし(MozillaからFirefoxをダウンロードし、最小限のDebian依存関係をインストール)、その後Firefoxを起動します。以降の実行では、foxcageはFirefoxのアップデートを確認し、新しいバージョンが利用可能な場合に自動的にイメージを再ビルドします。また、イメージは定期的に(デフォルトでは7日ごと)再ビルドされ、システムパッケージのアップデートを反映します。アップデート確認が失敗した場合(ネットワークエラー、タイムアウト)、警告がログに記録され、既存のイメージが使用されます — 起動がブロックされることはありません。
Firefoxに引数を渡す:```sh
./foxcage https://example.com
名前付きケージとFirefoxフラグを組み合わせる:```sh ./foxcage @work --kiosk https://example.com
既存のケージが実行中の場合は、2つ目のコンテナを起動する代わりに、既存のブラウザの新しいタブでURLが開きます。実行中のケージに対してURLなしで`foxcage`(または`foxcage @cage`)を実行すると、「cage is already running」というメッセージとともに正常に終了します。foxcageはコンテナの外部から既存のWaylandウィンドウを前面に出すことはできないため、試行は行われません。
起動時フラグは、ケージがすでに実行中の場合は**適用されません**。`--dns`、`--ipv4-only`、`--lifetime`、`--color`、`--fork`はコンテナ起動時に消費され、実行中のコンテナの設定は外部から変更できないため、警告付きで無視されます。これらを適用するには、ケージを閉じて再実行してください。
> プライベートモードのセッションには、`private_browsing`設定キーを使用してください。Firefoxの生の`--private-window`CLIフラグは使用しないでください。設定キーはセッション全体のプライベートモード(`browser.privatebrowsing.autostart`)を設定するため、後続の`foxcage @cage URL`呼び出しでタブ内に再度開くことができます。Firefoxのパススルーとしての`--private-window`は最初のウィンドウのみをプライベートにし、上記のタブ内再オープン動作を壊してしまいます。
>
> **注意:** `private_browsing = true`で有効化されたセッションでは、Firefoxの通常のプライベートウィンドウUI表示(紫色のアクセントバー、マスクアイコン、タイトル内の「(Private Browsing)」)は表示されません。これは、セッション内のすべてのウィンドウがプライベートであるため、Firefoxが視覚的に対比できる非プライベートウィンドウを持たず、インジケーターを抑制するためです。セッションは実際にプライベートです。確認したい場合は、ケージ内で`about:privatebrowsing`にアクセスするか(標準のプライベートブラウジング情報ページが表示されます)、`about:config`で`browser.privatebrowsing.autostart = true`を確認してください。
### `@tmp`による一時ブラウジング
痕跡を残さない一回限りのリンクには、予約済みの`tmp`ケージを使用してください。```sh
./foxcage @tmp https://somewhere-suspicious.example
Every @tmp 起動は、永続プロファイルを持たない使い捨ての Firefox です。ウィンドウを閉じると、すべてが消えます — Cookie、キャッシュ、履歴、拡張機能、HSTS 状態、TLS セッションキャッシュ、DNS キャッシュ、保存されたタブ状態。これは、拡張機能とかなりの量のディスク上の状態を保持する Firefox のプライベートブラウジングよりもさらに徹底しています。
複数の @tmp ケージは同時に実行され、それぞれが他から分離されています。メニューバーには FoxCage - tmp (<short id>) と表示されるため、同時実行中の一時的なウィンドウを区別できます。
一時的なケージは起動時に空白ページと空白の新しいタブを開きます — デフォルトの Firefox ホームページと新しいタブのコンテンツ(トップサイト、Pocket のおすすめ、アクティビティストリーム)は、破棄される予定の新しいプロファイルでは単なるノイズなので、抑制されます。永続的なケージは Firefox のデフォルトを維持します。
名前付き一時ケージ
使い捨てセッションに意味のある名前を付けたい場合(たとえば、新しいタブで再度開きたい調査の深掘りなど)、@tmp-<name> を使用します:```sh
./foxcage @tmp-research https://example.com # first call → new window
./foxcage @tmp-research https://another.example # second call → new tab in the existing window
`@tmp-<name>` は依然として一時的です。ウィンドウを閉じると、すべてが消えます。素の `@tmp` との違いは、同じ名前で2回目に起動すると**既存のウィンドウを再利用する**ことです(永続ケージと同じ)。そのため、後から並列コピーを開始せずにタブを追加できます。素の `@tmp` は「起動するたびに新しい使い捨て」という動作を維持します。
メニューバーのラベルには、選択した名前(`FoxCage - tmp-research`)が表示されるため、ウィンドウに意味のあるラベルが付きます。
#### 一時的デフォルトのカスタマイズ
`~/.config/foxcage/tmp.toml` を作成すると、すべての一時的ケージ(素の `@tmp` とすべての `@tmp-<name>` の両方)のデフォルトを設定できます。例:```toml
private_browsing = true
lifetime = "30m"
extensions = ["ublock-origin"]
[network]
dns = "cloudflare"
すべての一時起動は、プライベートウィンドウ、uBlock Origin、Cloudflare DoHを備え、30分後に自動終了します。完全な一時性はそのまま維持されます。名前付きの一時プロファイルは、デフォルトでtmp.tomlを継承します。名前ごとに上書きしたい場合は、~/.config/foxcage/tmp-<name>.tomlを作成してください。そのファイルはtmp.tomlの代わりに適用されます。マージは行われず、より具体的なファイルが完全に優先されます。共有のデフォルト設定が必要な場合は、そのファイルにコピーしてください。