§1概要
RoamSwitch for Linux は、macOS 版 RoamSwitch のゼロトラスト・ネットワーク防衛思想を Linux(systemd + nftables 環境)向けに完全実装した、常駐型のネットワークセキュリティ&システム診断ソフトウェアです。
本エディションはクライアント端末(ワークステーション・ラップトップ・シングルボード)向けであり、ネットワーク移動に追従してプロファイルを切り替える自律動作を前提とします。サーバー/ヘッドレス基盤向けの強化版(既定拒否を基本とし、公開するサービスポートだけを明示許可してインバウンドを最小化、外向き通信も制限。ネットワーク移動への自律追従はせず、静的な署名付きポリシーとリモート管理・監査で運用)は別途、専用の RoamSwitch Server Edition として提供されます。
接続中の Wi-Fi / 有線ネットワークをゲートウェイ MAC アドレス単位で識別し、nftables のファイアウォールプロファイルを自律的に切り替えます。加えて、未信頼ネットワークでの中間者攻撃(MITM)対策を 3 層で提供します——VPN トンネル + キルスイッチ(WireGuard または Tailscale、本命)、予防的 ARP/NDP ロック、ARP スプーフィング検知。さらにランサムウェアの挙動検知(fanotify + シャノンエントロピー + カナリア)、フィッシング接続を警告・遮断する受動リンクガード(NFQUEUE + SNI)、ポート異常ガード、BadUSB 対策、カーネル特権昇格(LPE)の予防的封鎖(Frag Gap CVE-2026-53362 対策)、eBPF ランタイム脅威センサー(Falco / Tetragon)連携による攻撃プロセスのミリ秒凍結(SIGSTOP)と自動 Air-Gap 隔離、24 項目のセキュリティ健全性診断、Model Context Protocol(MCP)サーバーを標準搭載します。
本書は、アーキテクチャ・脅威モデル・各防衛機構の設計、そして 「完全ローカル処理。マシン外へ出るのは署名付き検出データの要求だけで、そこに利用者の情報は一切含まれない」 というプライバシー原則がどのようにコードレベルで担保されているかを説明します。宣伝的な表現は避け、記載内容は配布パッケージ(バイナリ)の実際の動作と、本書付録のセルフチェックで確認できるようにしています。RoamSwitch for Linux はプロプライエタリ・フリーウェアで、ソースコードは公開していません(§11)。
| 原則 | 内容 |
|---|---|
| Zero Telemetry | 製品は利用者・端末に関する情報を一切外部へ送信しません。テレメトリ・解析・クラッシュレポート・利用統計・リモート設定・ライセンス認証サーバーのいずれも存在しません。 |
| 受信専用の更新 | 製品が行う唯一の外向き通信は、1 日 1 回、固定 URL(lafine.net/updates/v1)の署名付きマニフェストを GET することだけです。取得するのはリンクガードの詐欺サイト一覧・ClamAV 定義バージョン・本体の最新バージョン。クエリ文字列・Cookie・識別ヘッダはなく、利用者に関する情報は一切含まれません。updates.enabled = false で完全に停止し、その場合は同梱データのみで動作し外部接触はゼロになります。 |
| 完全ローカル処理 | すべての診断・検知・監査はローカルで完結します。URL 安全性診断も秘密情報スキャンも、対象を外部に送信せずヒューリスティックで評価します。 |
| 最小権限 | 特権操作は root デーモンに限定し、UI・CLI・MCP は Unix ドメインソケット越しの IPC で依頼します。デーモンは TCP/UDP ソケットを開きません。リンクガードは AF_NETLINK の NFQUEUE verdict チャネル(カーネル ↔ ユーザー空間、パケット送出なし)のみを使用します。 |
| フェイルクローズド | Air-Gap(緊急遮断)はデーモン再起動をまたいで維持され、外部からルールを消去されても数秒で自動再適用されます。 |
| オフライン・ライセンス | Pro / Business ライセンスは Ed25519 署名の検証のみで完結します。認証サーバーへの問い合わせは発生しません。 |
§2脅威モデル
RoamSwitch for Linux が主に対象とするのは、信頼できないネットワークを渡り歩くラップトップ/モバイルワークステーション です。
想定する攻撃者
- 同一 LAN 上の攻撃者 — 共有 Wi-Fi でのポートスキャン、ARP/NDP スプーフィング(不正 RA を含む)による中間者攻撃、不正 DHCP、MAC フラッディング後の受動盗聴、誤って
0.0.0.0にバインドされた開発サーバー・ローカル LLM API への不正アクセス。 - 悪意ある USB デバイス(BadUSB) — キーボードを偽装し画面ロック前にキーストロークを流し込む物理攻撃。未承認 USB マスストレージによるデータ持ち出し・マルウェア持ち込み。
- ランサムウェア/ファイルレスマルウェア — ダウンロードファイル経由で侵入し、ドキュメントを一括暗号化。
- DNS 経由の C2 通信・フィッシング — マルウェアが名前解決で C2 サーバーやフィッシングサイトへ到達。
対象外
- ルート権限を既に奪取した攻撃者。カーネル・ファームウェアレベルのルートキット。
- 物理的にディスクを取り出す攻撃(ディスク暗号化の領域。RoamSwitch は暗号化の有無を診断しますが暗号化自体は行いません)。
- AirDrop 相当の近距離無線共有制御(Linux に OS 標準機構が存在しないため非対応)。
§3システムアーキテクチャ
RoamSwitch for Linux は Rust で実装され、7 つのクレートで構成されます。デーモンとの通信はすべて /run/roamswitch/roamswitch.sock(Unix ドメインソケット)を経由します。TCP/UDP のリスニングソケットはデーモン・アプリ・MCP のいずれにも存在しません。アプリが bind するのは二重起動防止用の Unix ソケット 1 つのみです。
# ユーザー空間(非特権)
roamswitch-app GTK3 + AppIndicator トレイ
roamswitch (CLI) シェル用の薄いフロントエンド
roamswitch-mcp stdio JSON-RPC(AI クライアントが起動)
│
│ AF_UNIX /run/roamswitch/roamswitch.sock (ローカル IPC のみ)
▼
roamswitch-daemon root / systemd Type=notify + WatchdogSec=30
・nftables プロファイル制御(inet roamswitch テーブル)
・sentinel ループ(ネットワーク判定・プロファイル適用)
・VPN トンネル + キルスイッチ(WireGuard: wg-quick / roamswitch_vpn、Tailscale: roamswitch_ts)
・予防的 ARP/NDP ロック(neigh テーブル固定) / 検知
・ポート異常ガード(roamswitch_dev_guard)
・fanotify ガード(ランサムウェア/マルウェア)
・リンクガード(NFQUEUE — inet roamswitch_linkguard)
・カナリアエンジン / 隔離 Vault
・カーネル LPE 堅牢化(userns 無効化・Yama LSM)
・eBPF ランタイム連携(/var/run/roamswitch/events.sock)
・攻撃プロセスのミリ秒 SIGSTOP 凍結 & Air-Gap 連動
・USB 監視(evdev EVIOCGRAB — BadUSB) / 承認キュー
・Ed25519 ライセンス検証
roamswitch-updater 単独の systemd タイマー ヘルパー。HTTP/TLS スタックを
持つ唯一のクレート(日次の署名付きマニフェスト GET)
roamswitch-core 純粋ロジックライブラリ(全クレート共通)
roamswitchkit MCP クライアント SDK
| コンポーネント | 権限 | 役割 |
|---|---|---|
| roamswitch-daemon | root(systemd、Type=notify + WatchdogSec=30) | すべての特権操作。単一の書き込み者として nftables・DNS・systemd ユニットを制御。 |
| roamswitch-app | ログインユーザー | GTK3 GUI + AppIndicator トレイ。設定編集と可視化のみ。特権操作はすべて IPC 経由。 |
| roamswitch (CLI) | ログインユーザー | シェルからの薄いフロントエンド。同じ IPC ソケットを使用。 |
| roamswitch-mcp | AI クライアントが起動するプロセス | stdio JSON-RPC。読み取り専用の診断情報を提供。 |
デーモンとの通信はすべて /run/roamswitch/roamswitch.sock(0700 相当のディレクトリ権限)を経由します。ユーザー設定は ~/.config/roamswitch/config.json に平文 JSON で保存されます。デーモンは root 権限で /home/*/.config/roamswitch/config.json を走査して読み込みます(デーモンは HOME=/root で動作するため)。設定はマシン外に出ることはなく、アカウント登録もクラウド同期も存在しません。
§4自律ネットワーク防衛
sentinel ループ(既定 5 秒周期)が、デフォルトゲートウェイの MAC アドレスを /proc/net/arp と ip neigh から取得し、config.json の登録済み MAC と照合します。SSID ではなく MAC を用いるのは、SSID が容易に偽装可能であるためです。プロファイル切替時のデスクトップ通知には切り替えの理由(「未登録ネットワークのため」/「登録済みネットワーク『〇〇』の設定」/「ネットワーク未接続のため」)を含めます。
| プロファイル | 状況 | nftables の挙動 |
|---|---|---|
| open(信頼) | 登録済みの自宅・社内ネットワーク | policy accept。ローカル通信を許可。 |
| balanced(標準保護) | 未登録だが比較的安全と設定した環境 | 受信をデフォルト拒否。確立済み接続と lo のみ許可。公開ポート露出を監視。 |
| lockdown(外出先保護) | 公衆 Wi-Fi・未登録ネットワーク | 全受信パケットをステルス遮断。共有サービス自動停止(オプトイン)。 |
すべてのルールは inet roamswitch という専用 nftables テーブルに隔離され、他のファイアウォール設定(ufw / firewalld 等)と衝突しません。手動オーバーライドの期限強制(1時間 / 2時間 / 4時間 / 今日いっぱい / 解除まで / 次回切断まで)はデーモンが単一の書き込み者として行います。GUI は期限切れの書き込みを一切行わず、デーモンが書き換えた config.json を次のティックで再読込して UI に反映するだけです(競合回避)。
Air-Gap(緊急ネットワーク遮断)
inet roamswitch テーブルの input・output 両フックに policy drop(優先度 -100)を設定し、lo のみを通す完全遮断モードです。
- フェイルクローズド — デーモン再起動時にマーカーファイル(
/run/roamswitch/airgap.active)を検出すると遮断を再適用。 - 自己修復 — 外部プロセスが
nft delete tableでルールを消去しても、sentinel ループが数秒以内に再適用。 - 自動失効 — 発動から 600 秒(macOS 版の
maxAirGapDurationと同一)で自動的に解除され、誤操作による恒久的なネットワーク喪失を防ぐ。
table inet roamswitch {
chain input {
type filter hook input priority -100; policy <accept|drop>;
iif "lo" accept
ct state established,related accept
# balanced/lockdown: それ以外を drop
}
chain output {
type filter hook output priority -100; policy <accept|drop>;
# Air-Gap 時のみ policy drop、lo と ct established のみ許可
}
}
受動リンクガード(フィッシング接続の検知)
デーモンが外向き接続の接続先を監視し、危険なホストへの接続を警告します(狭義の「明らかなフィッシング」に該当する場合は遮断します)。従来の「URL を貼り付けて診断する」機能——ユーザーの操作を要した——を置き換えます。
- 傍受 — 専用の nftables テーブル
inet roamswitch_linkguardがoutputフックにかかり、DNS 質問と新規/初期の Web 接続を NFQUEUE 経由でユーザー空間ワーカーへ送ります。NFQUEUE はAF_NETLINK上のカーネル ↔ ユーザー空間 verdict チャネルであり、デーモンはソケットを bind せずパケットも発生させません。キュー規則にはbypassが付いており、ワーカーが動作していない場合は通信がそのまま通ります(Air-Gap が別の hard stop)。 - ホスト名の取得元 — DNS 質問名、TLS ClientHello の SNI(DoH を使うブラウザでもホスト名は把握できる)、平文 HTTP の
Hostヘッダ。ECH(Encrypted ClientHello)が普及した場合はそのホストが盲点になります(既知の限界)。 - 判定(
roamswitch-coreの純ロジック)— ホストをローカルのフィッシング/脅威フィードと、オフラインのヒューリスティック(保護ブランドの IDN/混同ホモグラフ、非公式ドメインに含まれるブランド名、IP 直打ち、高リスク TLD)に照らします。
モード(linkGuard.mode) | フィード命中 or ブランドのホモグラフ | その他の疑わしいシグナル |
|---|---|---|
off | 検査しない | 検査しない |
warn | 接続を最大 8 秒保留 + 許可/ブロックの承認通知 | 通知のみ |
block(既定) | 接続を遮断 + 許可ダイアログ(ホスト単位に 1 回) | 通知のみ |
hard-block されるのはフィード命中またはブランドのホモグラフのホストだけで、ヒューリスティック単体が拾ったものはすべて警告に留まります。warn モードは危険なホストへの接続を 最大 8 秒保留 してユーザーに諮り、無応答のまま保留が切れた場合は fail-closed(パケットを drop、判定はキャッシュしない)——「確認に無応答」を「許可」とみなしません。次回アクセスで再度確認します。block モードでもホスト単位に 1 回だけ通知+許可ダイアログを表示します(同じホストへのブラウザの多数の接続で通知が氾濫しない)。危険な対象の承認ダイアログは既定ボタンが「遮断を維持」で、「許可」は目立たない配色です。遮断されたホストは通知から恒久的または 5 分間だけ許可できます(linkGuard.allowlist)。
DNS は検査するが偽造しない: ブロックリストに載った DNS 質問は通知のみを生成し、強制は後続の TCP 接続で行います。RoamSwitch が偽の DNS 応答を注入することはありません。フィードはフィッシング/詐欺ドメインをコンパイルした一覧で、パッケージに同梱され、日次アップデータ(§11)で更新されます。フィードが無くてもオフラインのヒューリスティックは動作します。フィードファイルの Ed25519 署名は読み込み前に検証され、不正・欠落時は「ヒューリスティックのみ」にフェイルクローズドします(「すべて許可」にはなりません)。
VPN トンネル + キルスイッチ(MITM 対策の本命)
L2 の完全性に依存しない中間者攻撃対策。未信頼ネットワークでは、信頼できるエンドポイントへの常時暗号化トンネルを張ることで、ローカルの ARP/NDP スプーフィング・不正 DHCP・受動盗聴をすべて無意味化します——攻撃者に見えるのは暗号文だけです。下記の ARP/NDP 固定と検知は、この機能の補助という位置づけです。
バックエンドは 2 つから選べます——「WireGuard(設定ファイル)」または「Tailscale(Exit Node)」。RoamSwitch 自身は暗号処理を実装せず、VPN 事業者にもなりません。いずれも任意依存で、未導入なら GUI が導入方法を案内します。
(A) WireGuard バックエンド
- VPN サーバーは提供しない — ユーザーが用意した WireGuard 設定を使います。Mullvad・IVPN・Proton VPN など WireGuard 対応プロバイダーの
.conf、または自前の WireGuard サーバー(VPS・自宅サーバー)を想定。同梱のデフォルト設定はありません。 - 設定 — 標準の
.confを読み込み、デーモンが検証して/etc/wireguard/roamswitch.conf(0600、IF 名roamswitch)に保存。/etc/wireguard/以外に置けないのは、Ubuntu 24.04+ / Debian 13 の AppArmorwg-quickプロファイルがそれ以外の.conf読み取りを(root でも)拒否するためです。AllowedIPsが0.0.0.0/0(::/0)でないスプリットトンネルは「全通信は保護されない」と警告します。 - キルスイッチ — 専用 nftables テーブル
inet roamswitch_vpn(policy drop、両フック)が ループバック・トンネル IF(roamswitch、oifname名照合)・固定したエンドポイント IP への UDP・DHCP・ICMP 以外をすべて遮断します。トンネルを張る前に(エンドポイントをgetent ahostsで固定 IP に解決してから)設置されるため漏洩ウィンドウがありません。wg-quick upが失敗してもキルスイッチは維持されます。
(B) Tailscale バックエンド
- 既に Tailscale を使っているユーザー向け。RoamSwitch は
tailscale up/ ログイン /tailscaledの導入は行いません——tailscale status --jsonを読み、tailscale set --exit-node=<ノード>/(解除)を実行するだけです。 - Exit Node 必須 — 中間者攻撃対策は「全通信を経由させる Exit Node」の指定で初めて成立します。指定が無い場合は保護を有効化せず「保護されていません」と警告します。選択したノードがオフラインになった場合は保護を自動解除して通知します(死んだノードへ全通信を流すブラックホールを避けるため)。
- キルスイッチ(
inet roamswitch_ts、policy drop) — Tailscale のトランスポートは単一エンドポイント IP に固定できない(DERP リレー・NAT 越えでピアのアドレスが動く)ため WireGuard よりゆるめです。許可するのは: ループバック、トンネル IF(tailscale0)、CGNAT レンジ100.64.0.0/10とfd7a:115c:a1e0::/48、STUN(udp/3478)、DERP(tcp/443)、udp/41641、DHCP、ICMP、MagicDNS(100.100.100.100:53)、確立済み接続。それ以外(任意 UDP、ローカルリゾルバへの DNS、SMB、mDNS、平文 HTTP、任意 TCP)は全遮断します。 - 既知の限界 — (1) WireGuard バックエンドより許可範囲が広い(
udp/3478・tcp/443・CGNAT /10 全体)。(2) 経路上の攻撃者はメタデータ(Tailscale 利用の事実、DERP リージョン、タイミング)を観測でき、遮断・遅延も可能——ただしトンネル内容は読めません。(3) 直結 UDP 遮断時の機密性は Tailscale 自身の DERP TLS(証明書ピン)に依存します。(4) MagicDNS オフかつローカルリゾルバ利用時は、キルスイッチ有効中に DNS が引けなくなります(GUI で警告)。(5) キャプティブポータルは有効化前にログインが必要です。
共通 — vpn_on_untrusted_enabled かつ未信頼(level != open)のときのみ作動。信頼済みネットワークでは Exit Node をクリアしキルスイッチを撤去します(tailscale down はしません)。既定 OFF・オプトイン。外向き通信の宛先は WireGuard/Tailscale ともにユーザー自身の VPN エンドポイント/tailnet であり、lafine のインフラとは無関係です(§9)。
予防的 ARP/NDP ロック
未信頼ネットワークに接続した時点で、中間者攻撃の攻撃者が「なりすます」必要のある同一リンク上のインフラ機器の MAC アドレスをカーネルの neigh テーブルに PERMANENT(静的)エントリとして固定します。固定後はカーネルが偽の ARP 応答/近隣広告を無視するため、攻撃者は poisoning を成立させられません。
- 固定対象 — (1) IPv4 デフォルトゲートウェイ、(2) IPv6 デフォルトルーター(多くは
fe80::リンクローカル)、(3) 同一リンク上の DNS リゾルバ(公開リゾルバはゲートウェイ経由のため対象外)。IPv6 はip -6 neigh replace ... nud permanentで NDP エントリを固定します。 - 信頼済みネットワークでは固定しません——自宅ルーターの再起動で通信不能になることを防ぐため。ネットワーク切替のたびに固定し直します(TOFU)。正規のフェイルオーバーは次回の再接続で追従します。固定エントリは
/var/lib/roamswitch/arp_lock.jsonに記録され、確実に解除されます。 - 既定 ON(Community Edition の方針。macOS Pro 版と同じ内訳)。設定
gateway_arp_lock_enabled。
ARP スプーフィング検知(notify-first)
/proc/net/arp を監視し、同一 IP に複数の MAC が対応する状態、およびゲートウェイ MAC の不審な急変を検知します。lockdown 中は通信を全遮断(Air-Gap)+通知。balanced/信頼済みネットワークではいきなり遮断せず通知のみを出し、ユーザーが RoamSwitch から緊急遮断を実行できます。これは、(a) 攻撃者が偽の ARP パケット 1 つでユーザーを自らオフラインにさせる DoS を防ぎ、(b) ルーター再起動・アクセスポイントのローミングでの誤発動を防ぐためです。設定 arp_spoof_guard_enabled。
ポート異常ガード
新たに外部 LAN に公開されたリスニングポート(バックドア・C2・誤って 0.0.0.0 にバインドされた開発サーバー/ローカル LLM API)を自動的に隔離します。「危険なポート番号」ではなく ベースライン相対 で判定します。有効化した時点で外部公開中の実行体を「既知」として学習し、以後新規に現れた公開実行体のみをフラグします(アイデンティティは実行体パス、snap は snap:<name> に正規化)。システムデーモンは対象外。隔離は inet roamswitch_dev_guard テーブルで「lo 以外からの当該 TCP ポート宛を drop」——マシン自身からの利用は継続可能。隔離時は即座にデスクトップ警告通知と確認ダイアログ(「このポートを許可する」「遮断を維持する」)を表示し、ワンクリックで恒久許可リストに登録できます。ポート診断画面からも個別許可・ベースライン再取得ができます。既定 ON。設定 port_anomaly_guard_enabled。macOS 版から移植。
Docker リスク検知 & ファイアウォール迂回対策
Docker の公開ポート(-p)はホストの標準ファイアウォールをバイパスする既知の問題があるため、DOCKER-USER チェーンに検査ルールを常時挿入し、許可されていない外部トラフィックを遮断します(設定不要、既定で常時有効)。加えて、--privileged コンテナの起動や /var/run/docker.sock のマウント(コンテナ脱出リスク)を docker events でリアルタイム検知し、検知した瞬間に通知します。リスクの高い設定であって確認された侵害ではないため、自動遮断は行わない通知専用です。
機密情報・APIキー漏洩スキャナ
OpenAI・Anthropic・AWS・GitHub・Slack・Stripe 等の API キーや SSH 秘密鍵をシャノンエントロピーとパターンマッチで検出します。roamswitch audit-secrets <text|ファイル|ディレクトリ> でテキスト・ファイルに加え、ディレクトリを再帰的にスキャン可能(.git・node_modules 等は自動除外、2MB 超・バイナリファイルはスキップ)。MCP ツール(audit_secrets)・Python SDK(audit_secrets_directory())からも同じ機能を呼び出せます。内容が外部へ送信されることはありません。
初回セットアップ ウィザードと確認 UI
パッケージが /etc/xdg/autostart/roamswitch.desktop を同梱し、インストール後の次回ログインでセットアップ ウィザードが自動起動します(完了後はトレイ常駐のみ)。セキュリティガードの有効化・無効化には、macOS 版と同じ対象(ポート異常・ARP 自動遮断・USB ストレージ・BadUSB キーボード・Bluetooth)で確認ダイアログを表示します。イベント発生時の判断(不明な USB キーボード、未登録 USB メモリ、未知ポートの自動遮断、緊急遮断)はモーダルダイアログとして提示されます。
§5多層マルウェア・ランサムウェア防御
シャノンエントロピー・バースト検知と YARA スキャン
書き込みイベントを PID 単位 で追跡し、単一プロセスが 5 秒以内にエントロピー 7.92 以上の相異なるファイルを 20 個以上生成した場合にランサムウェアの一括暗号化とみなし、SIGSTOP でプロセスを凍結して Air-Gap を発動します。
- YARA スキャンとエントロピー検知の完全分離 — YARA マルウェア検知は
~/.cache/やnode_modules/、各種アーカイブを含め、すべての書き込みファイルに対して漏れなく実行します(ゼロバイパス)。一方、エントロピー暗号化バースト判定からは、最初から圧縮されている形式(.zip,.tgz,.tar.gz,.gz,.png,.jpg,.mp4,.wasm等)や正規パッケージマネージャー(npm,npx,cargo,rustc,dpkg,apt,tar等)のキャッシュ作業を除外し、正規ツール展開時の誤検知による SIGSTOP 凍結を完全に防止します。 - 同一ファイルの複数回上書き(
shredの 3 パス等)は 1 件として数える。 - 重要プロセス保護 —
systemd/dockerd/NetworkManager/sshd/gnome-shell等をハードコードした NEVER_FREEZE リストに照合し、これらは決して凍結しない(マシン全体のハングを防ぐ)。
カナリア(おとりファイル)
ユーザーの Documents / Desktop / Downloads / Pictures および専用ディレクトリにおとりファイルを配置し、SHA-256 ベースラインと 3 秒周期で照合します。改名・削除・切り詰め・内容改変のいずれかを検知すると即座に Air-Gap を発動します。デーモンは root で動作するため、/home/* を走査して実在するユーザー全員のディレクトリにカナリアを展開します。
fanotify 監視・隔離 Vault・ClamAV
- デーモンは
FAN_CLASS_CONTENT(許可モード。不可ならFAN_CLASS_NOTIF)で/home・/var/tmp・/dev/shmをマウント単位、/をファイルシステム単位で監視。 - notify-first・確認前隔離なし——YARA/ClamAV の単純なヒットで無確認の隔離や
FAN_DENYは行いません。実シグネチャ検出は/run/roamswitch/approvals.jsonにモーダル要求(隔離/許可/後で)を積み、ユーザーが確認するまで Vault には入りません。実際のexecveのみpre_exec_blocking_enabledが有効なときFAN_DENYします。承認タイムアウトはフェイルオープン。 - EICAR 等のテスト署名は通知のみ——業界標準のテスト文字列は「無害」として情報通知を出すだけで、隔離も遮断もしません(fanotify ガード・ダウンロードガード双方で)。
- ユーザー設定の
scan_exclusions(配下も含む絶対パス。旧clamav_exclusionsを統合)を YARA・ClamAV 双方が尊重。承認ダイアログの「許可」と隔離タブの「復元」はどちらもこの一覧に追記され、同じファイルが再び拾われません。 - 検出・確認された実マルウェアは
~/.local/share/roamswitch/quarantine/へ退避。Vault ディレクトリは0700、検体は0400(読み取り専用・実行不可)。復元時はファイルを0644に戻し、元ディレクトリの所有者へ chown。 - 許可イベントループはファイル I/O を一切行いません——設定の読み取りは専用のバックグラウンドスレッドが
Mutex<GuardConfig>スナップショットへ行い、ホットパスはロック+コピーのみ。FAN_OPEN_PERMを遅延させてマシンを固まらせないための設計上の不変条件です。 - ClamAV がある場合、新規ダウンロード実行ファイル(
.sh・.deb・.elf等)と危険な AI モデル(.pkl・.pt)をローカル DB でスキャン。ClamAV は任意の依存で、未インストールでも他の防御は動作。インストールされている場合、日次アップデータ(§11)がfreshclamを実行しシグネチャ DB を最新に保つ。
カーネル特権昇格(LPE)防護とカーネル堅牢化
非特権ユーザー空間を取得した攻撃者やマルウェアが、Linux カーネルの脆弱性を悪用して root 権限を奪取することを多層防御で阻止します。
- 非特権 User Namespace の無効化(Frag Gap CVE-2026-53362 対策) — 現代の Linux カーネル特権昇格脆弱性の多くは、非特権ユーザーが
unshare(CLONE_NEWUSER)を呼び出して作成したユーザー名前空間内で、未初期化構造体や古いネットワークサブシステムへ不正アクセスすることに起因します。RoamSwitch はuser.max_user_namespaces = 0(またはkernel.unprivileged_userns_clone = 0)を推奨・診断し、ワンクリック堅牢化で適用。これにより未パッチのゼロデイ脆弱性であっても非特権プロセスからの攻撃経路を根本から遮断します。 - Yama LSM によるメモリ保護 —
kernel.yama.ptrace_scope = 1(または2)を適用し、一般プロセスが他のプロセスへptraceでアタッチしたり、/proc/$pid/mem経由でメモリ空間(ブラウザのセッションや SSH/GPG 秘密鍵)を読み取ることを拒絶します。 - コアダンプ抑止 —
fs.suid_dumpable = 0により、クラッシュしたプロセスのメモリイメージがディスク上に平文ダンプされることを防止します。 /tmp&/dev/shmのnoexecマウント防御 — 一時ディレクトリにペイロードを配置して実行することを防ぐため、外出先(未信頼ネットワーク)ではnoexec,nosuid,nodevでマウントする防御機構を提供します。
eBPF ランタイム脅威監視 & 自動プロセス凍結 (Desktop eBPF Guard)
RoamSwitch クライアントデーモンは、カーネル空間で動作する Modern eBPF センサー(Falco / Tetragon / BTF)との連携機構を標準搭載します。
# eBPF ランタイム脅威検知と自律迎撃フロー
[悪性プロセス (LPE/マルウェア)]
│ 不正システムコール実行 (Frag Gap 悪用試行)
▼
[Linux カーネル (eBPF プローブ)]
│ eBPF リングバッファ (2MB コンパクト設計)
▼
[Falco (RoamSwitch Optimized)]
│ メモリ上 UNIX ソケット直接通信 (/var/run/roamswitch/events.sock)
▼
[roamswitch-daemon (root)]
├─► ① 攻撃プロセスのミリ秒凍結: kill(pid, SIGSTOP) [状態: Ts]
├─► ② ネットワーク完全遮断: nftables input/output drop [Air-Gap 展開]
└─► ③ デスクトップ緊急通知: GTK3 モーダル & libnotify アラート
- UNIX ドメインソケット直接連携(
/var/run/roamswitch/events.sock) — Falco 等の eBPF センサーから、ディスクログを経由することなくメモリ上の UNIX ソケット経由でアラートを直接受信。ディスク I/O を発生させず、ミリ秒単位の超高速ハンドオフを実現します。 - ミリ秒単位の自律対応(プロセス凍結 & Air-Gap 展開) — 受信したイベントの優先度が
CriticalまたはEmergency(カーネル LPE、特権昇格、ランサムウェア暗号化など)に達した場合、デーモンは即座に当該 PID に対しSIGSTOPを送出してプロセスを完全停止(状態:Ts)させ、inet roamswitchテーブルにpolicy drop(Air-Gap)を展開してデスクトップ通知を発報します。 - デスクトップ向け低オーバーヘッド & ログ肥大化防止チューニング(RoamSwitch Optimized) — ラップトップやワークステーションのバッテリー消費・CPU 負荷・ディスク枯渇を抑止するため、最適化プロファイル(
99-roamswitch-optimized.yaml)を同梱:- 重要度フィルタリング(
priority: warning) — 日常操作のノイズを 95% 削減。 - カーネル事前破棄(
drop_failed_exit: true) — 失敗システムコールをカーネルドライバ段階で破棄し CPU 負荷を半減。 - 省メモリリングバッファ(
cpus_for_each_buffer: 2) — バッファサイズを 2MB に縮小し常駐メモリを 30〜50MB、CPU 負荷を 1% 未満に抑制。 - ログ上限強制ローテーション(
/etc/logrotate.d/roamswitch-falco) — 50MB 上限・7 世代圧縮保持。
- 重要度フィルタリング(
- スタンドアロン堅牢化 vs eBPF 拡張のハイブリッド耐性 — Falco 未導入時でもカーネル sysctl(
user.max_user_namespaces = 0)・nftables プロファイル・fanotify・カナリア監視で自律防衛。Falco 導入時はリアルタイム eBPF イベントによるゼロデイ迎撃・プロセス凍結が加わり、最高強度の防衛を実現します。
§6BadUSB 対策
USB イベントを監視し、キーボード(HID)クラスのデバイスと USB マスストレージを追跡します。既定 OFF(macOS 版の USBKeyboardGuard / USBStorageGuard と同じく isEnabled = false 出荷)。初回セットアップ ウィザードと USB タブからオプトインでき、有効化時には確認ダイアログを出します。
不正 USB キーボード(キーストローク注入)
- カーネル層でのポート物理切断は行いません(重要)。旧実装(≤1.0.6)は未登録キーボードに対して
/sys/bus/usb/devices/*/authorizedに0を書き、デバイスを物理的に切断していました。キーボードとマウスが一体の受信機(Raspberry Pi・ミニ PC で一般的)ではマウスも同時に切断し、内蔵入力の無いマシンを操作不能にします。現在は HID インターフェース(bInterfaceClass 03)を持つデバイスへのauthorized=0書き込みをハード拒否し、デーモンは起動時にauthorized==0の HID デバイスを再認可して過去バージョンで切断されたマシンを自己修復します。 - キーストロークの遮断はソフトウェアで行います。未登録キーボードを検知すると、そのデバイスの
/dev/input/eventN(/proc/bus/input/devicesのkbdハンドラを持つノードのみ、VID:PID 一致)に evdev のEVIOCGRABで排他グラブをかけます。マウスノードは触りません。macOS 版のCGEventTapに相当し、デバイスは給電されたままです。 - 安全策 — グラブをかけるのは「他に入力できるキーボードがある」場合(内蔵、または承認済みで接続中の USB キーボード)のみ。単一の新規キーボードしかないマシンを締め出しません。
- 承認ダイアログ — グラブと同時に
/run/roamswitch/approvals.jsonに承認要求を積み、GUI がモーダル(許可/拒否)を表示。許可でホワイトリスト登録+グラブ解除。3 分でフェイルオープン。
不正 USB マスストレージ
未登録の USB マスストレージ(HID を持たないもの)が挿入され、ガードが有効かつ未信頼ネットワーク/lockdown のとき、authorized=0 で保留し(HID を持たないので安全)、承認ダイアログを表示します。許可で再認可+ホワイトリスト登録、拒否で保留を継続。macOS 版は 1.7.4 でこの「読み取り専用で保留+承認プロンプト」方式に統一しました。
識別と grandfathering
HID の判定は bInterfaceClass == "03" と /proc/bus/input/devices の Handlers= 行。コンボ受信機(1 つの VID:PID で kbd と mouse の両セクション)は VID:PID でマージ。デーモン起動時に接続済みの未登録キーボードは grandfathering され、以降にホットプラグされたものだけがグラブ対象になります。
§7セキュリティ健全性診断(24 項目)
roamswitch-core の LinuxHealthChecker が 24 項目を評価し、0〜100 のスコアと A〜F の等級、各項目の改善アドバイスを生成します。GTK ダッシュボード・CLI(roamswitch status)・MCP(get_security_report)のいずれからも、全 24 項目のタイトル・詳細・推奨文が利用者の言語で 表示されます(10 言語対応)。
| 分類 | 項目例 |
|---|---|
| ディスク・ブート | LUKS / dm-crypt 暗号化、UEFI Secure Boot |
| アクセス制御 | LSM(AppArmor / SELinux)、SSH / sudo 設定監査 |
| カーネル堅牢化 | sysctl 強化、非特権 User Namespace 無効化(Frag Gap 対策)、コアダンプ制御、/tmp・/dev/shm の noexec |
| 更新 | 自動セキュリティアップデートの設定 |
| ネットワーク | ARP スプーフィング監視、ゲートウェイ ARP/NDP 固定状態、外部公開ポート監査、Wi-Fi 暗号化強度 |
| マルウェア | ClamAV / fanotify(ガードの実稼働も確認)/ エントロピー監視 / noexec |
| ブラウザ | Firefox / Chrome / Chromium の Safe Browsing 設定 |
| DNS / USB | 脅威保護 DNS、USB バスのゼロトラスト状態 |
診断はすべてローカルのファイル・コマンド出力(getenforce・mokutil・resolvectl・ss 等)の読み取りに基づきます。外部への問い合わせは一切ありません。
信頼ネットワークを踏まえた判定。 RoamSwitch が信頼(自宅)ネットワークで意図的に見送る堅牢化——ゲートウェイ MAC の PERMANENT 固定(ルーター再起動で LAN が不通になるのを防ぐ)、/tmp・/dev/shm の noexec 化(ビルドや一部インストーラの互換性のため)、既定スコープの脅威保護 DNS——は 🔴「未対策」ではなく 🟢+説明で表示し、未信頼ネットワーク接続時に自動適用される旨を明記します。ランタイムの真値はデーモンが毎サイクル書き出す /run/roamswitch/state.json(active_level/network_trusted/fanotify_ready)から取得します。fanotify 項目はカーネル対応の有無だけでなく、ガードが実際にマーク済みで稼働しているか(/run/roamswitch/fanotify.ready)を確認します。外部公開ポート項目はデーモンの実プロファイルを参照します(MCP 版のハードコードを廃止)。
項目ごとの個別強化。 要対策項目のうち自動修復が可能なもの(カーネル sysctl/Yama/コアダンプ、/tmp noexec、USB ゼロトラスト、ゲートウェイ ARP 固定、脅威保護 DNS、fanotify 停止時のデーモン再起動)には、GTK ダッシュボードに「🔧 個別強化」ボタンを表示します。デーモンが root で実行するため、再起動以外に pkexec は不要です。
§8Model Context Protocol(MCP)サーバー
roamswitch-mcp は stdio で JSON-RPC を話す MCP サーバーで、Claude Desktop・Cursor・Antigravity 等の AI クライアントから起動されます。提供ツールはすべて 読み取り専用かつローカル完結です。
| ツール | 内容 |
|---|---|
| get_security_report | 24 項目診断・スコア・改善案 |
| get_exposed_ports | 外部公開ポート一覧とファイアウォール遮断状態 |
| get_guard_status | 現在の保護レベルと各防衛モジュールの稼働状況 |
| audit_url_safety | URL のヒューリスティック安全性診断(対象を取得しない) |
| audit_secrets | テキスト中の API キー・秘密情報の検出(テキストを送信しない) |
| audit_security_logs | ローカルの journald / 認証ログの集計 |
| get_quarantine_status / get_canary_status | 隔離・カナリアの状態 |
| get_app_help | 内蔵ナレッジベースの検索 |
MCP サーバーはネットワークを一切使用せず、roamswitchkit 経由でデーモンの Unix ソケットに接続するか、roamswitch-core のロジックを直接呼び出すのみです。
§9外部通信の検証
RoamSwitch for Linux が行う外向き通信は 1 種類だけです——日次アップデータによる署名付きマニフェストの GET(§11)。それ以外の診断・検知・監査はすべてローカルで完結します。RoamSwitch for Linux はプロプライエタリ・フリーウェアでソースは公開していないため、下記の検査は lafine が実施したもので、結果を 9.2 に、付録 C の手順(配布パッケージだけで再現可能)を示します。
9.1 監査方法
- クレートの隔離 — HTTP/TLS スタックを許可するのは 1 クレートのみ、タイマー駆動のアップデータバイナリをビルドする
roamswitch-updater。scripts/audit_no_network.sh(CI でゲートあり実行)は、roamswitch-core・roamswitch-daemon・roamswitch-app・roamswitch-mcp・roamswitchkitのいずれかがreqwest/hyper/ureq/rustls/native-tls/opensslを引き込むか、roamswitch-updater以外の.rsがTcpStream/TcpListener/UdpSocket/to_socket_addrs/lookup_hostを使う場合にビルドを失敗させる。 - 依存ツリーの検査 —
Cargo.lock全体をテレメトリ/解析/クラッシュレポート SDK(sentry・opentelemetry等)について検索。 roamswitch-updaterのレビュー — 単一のリクエスト先、クエリ文字列・Cookie・識別ヘッダの不在、ダウンロードするすべてに対する Ed25519 検証。- サブプロセス呼び出しの列挙 —
Command::new(...)の全呼び出し箇所を確認。
9.2 結果
| 監査項目 | 結果 |
|---|---|
| HTTP / TLS スタック | roamswitch-updater のみに存在(ureq + rustls)。core / daemon / app / mcp / kit には無し |
| テレメトリ/解析/クラッシュレポート SDK | どこにも存在しない |
TcpStream / TcpListener / UdpSocket | roamswitch-updater 以外に 1 箇所も無し |
| 使用されるソケット API | AF_UNIX(ローカル IPC)/AF_NETLINK(NFQUEUE verdict チャネル・nftables・ip neigh 制御——送出なし)/AF_INET は roamswitch-updater のみ。VPN トンネル(§4)を有効にした場合は wg-quick(WireGuard バックエンド)または tailscaled(Tailscale バックエンド)——いずれもデーモンではなくサブプロセス——がユーザー自身のエンドポイント/tailnet へ送出 |
| 日次アップデータ | 約 24 時間ごとに GET https://lafine.net/updates/v1/manifest を 1 回。クエリ文字列なし・Cookie なし・汎用 User-Agent・識別子なし。全ペイロードを Ed25519 で検証。updates.enabled = false で完全停止 |
| VPN トンネル(§4) | 既定 OFF。有効時、wg-quick がユーザーの .conf に書かれた自分の VPN エンドポイントへトンネルを張る(lafine とは無関係)。ホスト名解決は getent ahosts(glibc NSS)で、Rust のソケット API は不使用 |
| ライセンス認証 | Ed25519 署名のオフライン検証。認証サーバーへの通信なし |
RoamSwitch for Linux が開始する外向き通信は 1 種類だけ——利用者・端末に関する情報を一切含まない、日次・受信専用の署名付き検出データのダウンロードです。製品のそれ以外の部分が外部サーバーへ接触することはありません。
9.3 外部に触れる動作の全リスト
直接・間接を問わず、ローカルマシン外へパケットが出る可能性のある動作の全て:
| # | 動作 | 通信先 | 発生条件 | 送信内容 |
|---|---|---|---|---|
| 1 | 日次アップデータ — GET /v1/manifest、およびフィードのバージョンが変わった時はフィードファイル | lafine.net/updates/v1(CDN) | systemd タイマー、約 24 時間ごと(ランダム遅延あり)。updates.enabled = false または roamswitch-update.timer 無効時は発生しない | 素の HTTPS GET:クエリ文字列なし・Cookie なし・汎用 User-Agent・利用者や端末に関する情報は一切なし。署名付きマニフェスト+フィードを受信 |
| 1a | freshclam(ClamAV 自身の更新ツール)。ClamAV 導入時に日次アップデータが起動 | ClamAV プロジェクトの CDN | #1 の一部として | ClamAV 本体(別プロジェクト)の通信。RoamSwitch は起動するだけ |
| 2 | ping -c1 -W1 <ゲートウェイIP> | LAN のデフォルトゲートウェイ(リンクローカル) | ARP スプーフィング検知の際に ARP キャッシュを更新するため | ICMP エコー要求のみ。DNS 解決なし、ペイロードなし |
| 3 | システムブラウザの起動(xdg-open) | https://lafine.net/...(製品サイト) | 「リリースノートを開く」等のボタンをユーザーが押した時のみ | RoamSwitch プロセスはソケットを開かない。URL を OS のブラウザに渡すだけ |
| 4 | resolvectl dns <iface> … | — (通信は発生しない) | 未信頼ネットワークでの脅威保護 DNS(オプトイン)。<iface> はデフォルトルートを持つ物理 NIC 全て(有線+無線の同時接続に対応。lo/tailscale*/wg*/docker*/veth* 等は除外)。dns_scope = always_on なら信頼ネットワークでも適用。sentinel が毎サイクル reconcile | OS のリゾルバ設定を変更するのみ。RoamSwitch 自身は DNS クエリを送らない |
| 5 | パッケージマネージャによる更新 | ディストリの apt / dnf / zypper リポジトリ | ユーザーが apt upgrade 等を実行、または GUI の「今すぐアップグレード」ボタン(pkexec で apt-get / dnf / zypper)を押した時 | RoamSwitch はローカルの apt-cache policy 出力を読み、パッケージマネージャを起動するのみ。lafine とは無関係 |
| 6 | VPN トンネル(§4) — WireGuard バックエンド: wg-quick が張るトンネル/Tailscale バックエンド: tailscale set --exit-node で全通信を Exit Node 経由に | ユーザー自身の VPN エンドポイント/tailnet(WireGuard は .conf で指定、Tailscale は選んだ Exit Node と DERP リレー) | vpn_on_untrusted_enabled かつ未信頼ネットワーク接続時。既定 OFF | 暗号化された WireGuard/Tailscale トラフィック。宛先はユーザー自身の VPN サーバー/tailnet であり lafine とは無関係。WireGuard のホスト名解決は getent ahosts(glibc NSS)、Tailscale は tailscaled(別プロセス)が制御 |
上記以外の RoamSwitch のすべての機能(nftables 制御、neigh テーブルの ARP/NDP 固定(ip neigh、パケット送出なし)、リンクガード(NFQUEUE、AF_NETLINK のみ)、ポートスキャン(ss の出力解析)、ARP 監視、健全性診断、エントロピー監視、カナリア、fanotify、USB 監視(evdev グラブ)、隔離、ログ監査、URL・秘密情報の監査)は ローカルのファイル読み取りとサブプロセス出力の解析のみ で完結します。§4 の VPN トンネルは唯一の「ユーザーが明示的に設定した外向き通信」ですが、その宛先はユーザー自身の VPN サーバーであり lafine のインフラには一切データが流れません。
§10プライバシーとデータの取り扱い
- アカウント不要 — ユーザー登録・ログイン・メールアドレスの入力は一切ありません。
- 設定はローカルのみ —
~/.config/roamswitch/config.jsonに平文で保存され、マシン外に出ません。クラウド同期は存在しません。 - 診断結果はローカルのみ — セキュリティレポート・ログ監査結果は画面表示と(ユーザーが選べば)ローカルファイルへのエクスポートのみ。
- ライセンスキーはオフライン検証 — Pro / Business のライセンストークンは Ed25519 署名を含み、公開鍵で検証されます。lafine のサーバーへ問い合わせません。
- クラッシュレポートなし — パニック時もスタックトレースを外部送信しません。
- 日次アップデータは利用者情報を一切送らない — クエリ文字列なし・Cookie なし・端末やインストールの識別子なし・汎用
User-Agent。これはレポートではなくダウンロードです。リンクガードの判定結果・遮断したホスト・スキャン結果はローカルのアクティビティビュー/journald にのみ記録され、送信されません。
§11ライセンスと配布
| エディション | 対象 | 価格 | ライセンス |
|---|---|---|---|
| Community Edition | Linux 個人利用 | 無料 | プロプライエタリ・フリーウェア(同梱の EULA) |
| Business(予定) | 組織での集中管理・ポリシー配布・署名済み内部 apt・SLA | 有償 | 商用ライセンス。同一バイナリ+ティアゲート方式 |
macOS 版 Pro Lifetime 購入者には、本人の Linux マシンにおける Business 機能を無償付与します。
配布とサプライチェーン
| 経路 | 内容 |
|---|---|
| APT | https://lafine.net/apt(stable main)。RSA-4096 鍵で Release を署名 |
| RPM | https://lafine.net/rpm(Fedora / openSUSE 別)。repomd.xml を GPG 署名 |
| AUR | roamswitch-bin(GitHub Releases の tarball を取得、sha256 検証) |
| tarball | GitHub Releases に amd64 / aarch64 の .tar.gz + .sha256 |
- glibc 下限の固定 — リリースバイナリは Debian 12(bookworm、glibc 2.36)のコンテナ内でビルドされ、Debian 12 / Ubuntu 22.04 以降でシンボルバージョン不整合が起きない。CI に「glibc フロア検証」ステップを設け、2.36 を超える依存が混入した場合はビルドを失敗させる。
- 署名鍵の管理 — apt/rpm リポジトリ署名の秘密鍵は GitHub Actions の暗号化シークレットにのみ存在し、リポジトリには公開鍵のみをコミットする。
11.3 日次アップデータ
roamswitch-updater は systemd タイマー(roamswitch-update.timer、OnCalendar=daily+ランダムな 3 時間の遅延、Persistent=true)で実行される単独バイナリです。ネットワークに接触する唯一のコンポーネントです。各実行で:
GET https://lafine.net/updates/v1/manifestとそのデタッチ署名。- マニフェストの Ed25519 署名を検証。脅威フィード専用の署名鍵を用いる(本体アップデート用の Sparkle 鍵とは別鍵)。フィードはサーバ側 CI が毎日再署名するため、その秘密鍵は CI シークレットに置かれる。鍵をフィードに限定するのが要点で、万一漏れても攻撃者が配れるのは不正なブロックリストのみ(sinkhole/警告のみ。コード実行経路も本体更新経路もない)。本体アップデートの署名鍵は macOS キーチェーンから出ない。検証できないマニフェストは破棄され、無署名データにフォールバックすることはない。
- 脅威フィードのバージョンが変わっていれば、フィードを取得し、SHA-256 と Ed25519 署名を検証してリンクガード(§4)向けにアトミックに設置する。
- ClamAV が導入されていれば
freshclamを起動する。 - 本体の最新バージョンを通知用に記録する(アップデータは本体をインストールしない。Linux ではユーザーが
apt upgradeを実行する)。
いずれかの手順の失敗(オフライン、署名不正、freshclam 不在)は updates.lastError に記録され、プロセスは常に exit 0 で終了するため、タイマー実行が systemd の失敗通知を出すことはありません。機構全体は updates.json の updates.enabled = false、または systemctl disable --now roamswitch-update.timer で無効化でき、その場合はパッケージ同梱のデータのみで動作します。脅威フィードはパッケージに同梱もされているため、新規インストール直後から機能し、アップデータを無効にしていても apt upgrade で更新されます。
§12動作環境
- OS: Ubuntu 22.04 / 24.04 以降、Debian 12 以降、Linux Mint / Pop!_OS / elementary / Zorin、Raspberry Pi OS 64bit(Bookworm)、Ubuntu for Raspberry Pi
- アーキテクチャ: x86_64 / aarch64
- 必須:
systemd、nftables、iproute2(ip neigh— ARP/NDP 固定)(.deb/.rpmの依存で自動導入) - 任意:
clamav(マルウェアスキャン)、nmcli(Wi-Fi 暗号化判定)、libnotify(デスクトップ通知)、wireguard-tools(VPN トンネル + キルスイッチの WireGuard バックエンド、§4)、tailscale(同 Tailscale バックエンド、§4)、polkit/pkexec(GUI からのアップグレード実行) - 非対応: 非 systemd 系ディストリ(Alpine / Void / Devuan)
- 画面: 1280×720 以上(画面サイズに応じてウィンドウとフォントを自動スケール。Raspberry Pi の小型ディスプレイに対応)
付録 A直接依存クレート一覧
| クレート | 用途 | ネットワーク |
|---|---|---|
| serde / serde_json | 設定・IPC のシリアライズ | なし |
| tokio | 非同期ランタイム(features = ["full"] だが使用は Unix ソケットのみ) | なし(TCP 未使用) |
| tracing / tracing-subscriber | ログ出力(ローカル・journald) | なし |
| thiserror / anyhow | エラー処理 | なし |
| regex | 文字列解析(診断・CSS スケーリング) | なし |
| url | URL のパース(audit_url_safety のヒューリスティック) | なし(取得しない) |
| chrono | 日時処理 | なし |
| ed25519-dalek | ライセンス署名のオフライン検証 | なし |
| base64 / sha2 | ライセンストークン・カナリアのハッシュ | なし |
| libc | chown / kill / システムコール | なし |
| idna | ホモグラフ検知のための IDN/punycode ホストのデコード | なし |
| nfq(daemon のみ) | リンクガードの NFQUEUE バインディング——純 Rust、AF_NETLINK | なし(verdict チャネル、送出なし) |
| open | 「リリースノートを開く」で xdg-open を呼ぶ | プロセス自体は通信しない |
| GTK3 / ksni(app のみ) | GUI とトレイ | なし |
| ureq + rustls(roamswitch-updater のみ) | 日次アップデータの HTTPS GET 1 回 | 依存ツリー中で唯一の HTTP/TLS スタック。アップデータバイナリに限定 |
roamswitch-core・roamswitch-daemon・roamswitch-app・roamswitch-mcp・roamswitchkit には HTTP クライアント・TLS スタック・テレメトリ SDK は現れません。ureq + rustls は roamswitch-updater にのみ存在します(CI の scripts/audit_no_network.sh が強制)。テレメトリ SDK はどこにも無し。
付録 Bnftables テーブル構造
table inet roamswitch { # プロファイル ファイアウォール(Air-Gap / balanced / lockdown)
chain input {
type filter hook input priority -100; policy <accept|drop>;
iif "lo" accept
ct state established,related accept
# balanced/lockdown: それ以外を drop
}
chain output {
type filter hook output priority -100; policy <accept|drop>;
# Air-Gap 時のみ policy drop、lo と ct established のみ許可
}
}
table inet roamswitch_linkguard { # §4.5 — プロファイルテーブルとは独立
chain output {
type filter hook output priority 0; policy accept;
udp dport 53 queue num 92 bypass
tcp dport 53 queue num 92 bypass
tcp dport { 80, 443 } ct state new,established queue num 92 bypass
}
}
table inet roamswitch_dev_guard { # §4 ポート異常ガード/手動のポート隔離、別テーブル
chain input {
type filter hook input priority -10; policy accept;
iif != "lo" tcp dport { … } drop
}
}
table inet roamswitch_vpn { # §4 WireGuard バックエンドのキルスイッチ
chain output {
type filter hook output priority -150; policy drop;
oifname "lo" accept
oifname "roamswitch" accept
meta l4proto { icmp, ipv6-icmp } accept
udp sport 68 udp dport 67 accept # DHCP
ip daddr { <endpoint v4> } udp dport <port> accept
ip6 daddr { <endpoint v6> } udp dport <port> accept
ct state established,related accept
}
chain input { type filter hook input priority -150; policy drop; … } # 対称
}
table inet roamswitch_ts { # §4 Tailscale バックエンドのキルスイッチ(よりゆるい)
chain output {
type filter hook output priority -150; policy drop;
oifname "lo" accept
oifname "tailscale0" accept
meta l4proto { icmp, ipv6-icmp } accept
udp sport 68 udp dport 67 accept # DHCP(v6 も対称)
ip daddr 100.64.0.0/10 accept # tailnet CGNAT
ip6 daddr fd7a:115c:a1e0::/48 accept
ip daddr 100.100.100.100 udp dport 53 accept # MagicDNS
ip daddr 100.100.100.100 tcp dport 53 accept
udp dport 3478 accept # STUN
udp dport 41641 accept # WireGuard 直結
tcp dport 443 accept # DERP フォールバック
ct state established,related accept
}
chain input { type filter hook input priority -150; policy drop; … } # 対称
}
ゲートウェイ/IPv6 ルーター/DNS の ARP・NDP 固定(§4)は nftables ではなく カーネルの neigh テーブル(ip neigh replace … nud permanent)で行われます。固定エントリの記録は /var/lib/roamswitch/arp_lock.json。
付録 Cセルフチェック
本書の主張は、いずれも利用者自身の手元で確認できます。以下は roamswitch をインストールした環境で実行できます。
C.1 通信の検証
daemon / app / MCP バイナリに HTTP / TLS ライブラリがリンクされていないこと:
for b in roamswitch-daemon roamswitch-app roamswitch-mcp; do
ldd "$(command -v $b)" | grep -iE 'ssl|crypto|nghttp|curl' && echo " ^ in $b"
done
# → 出力なし。HTTP/TLS スタックは roamswitch-updater にのみ静的リンクされている。
稼働中のデーモンがインターネットソケットを開いていないこと:
sudo lsof -p "$(pgrep -x roamswitch-daemon)" -a -i
# → 出力なし = TCP/UDP のインターネット接続は 1 つも無い
sudo lsof -p "$(pgrep -x roamswitch-daemon)" -a -U | grep roamswitch
# → /run/roamswitch/roamswitch.sock のみ(リンクガードは AF_NETLINK ソケットを
# 追加するが、これはカーネルチャネルでありインターネットソケットではない)
RoamSwitch 由来の外向き通信は日次アップデータだけであること:
watch -n 5 'sudo ss -tupn | grep -E "roamswitch-(daemon|app|mcp)" || echo "(none)"'
# → 常に "(none)"。この 3 つはインターネットソケットを一切開かない。
systemctl list-timers roamswitch-update.timer
sudo systemctl start roamswitch-update.service
journalctl -u roamswitch-update.service -n 20 --no-pager
# → lafine.net/updates/v1 への HTTPS GET が 1 回だけ、その後終了。
無効化して完全な沈黙を確認する:
sudo systemctl disable --now roamswitch-update.timer
# または /var/lib/roamswitch/updates.json を "enabled": false に編集
sudo strace -f -e trace=network -p "$(pgrep -x roamswitch-daemon)" 2>&1 | grep -i 'connect('
# → AF_UNIX / AF_NETLINK への connect のみ。AF_INET / AF_INET6 は一切現れない。
C.2 防御機構の再現検証(Docker テストスイート)
RoamSwitch の各種防衛機構(Air-Gap 遮断、ファイアウォール強制消去時の自己修復、ランサムウェア検知、/tmp noexec、Yama LSM、ホモグラフ検知など全 17 項目)は、ホストマシンの通信環境や設定を一切損なうことなく、公式サポートリポジトリ(roamswitch-support)で公開されている Docker テストスイートを用いて手元で 100% 安全に再現実行できます。
# 検証スイートの取得と再現実行(公式配布パッケージを用いて隔離コンテナ内で自動検証)
git clone https://github.com/lafine1211/roamswitch-support.git
cd roamswitch-support/test/docker
docker build -t roamswitch-test .
docker run --rm --privileged roamswitch-test
テストコンテナは公式 APT リポジトリ(lafine.net/apt)から配布パッケージを自動取得し、独立した名前空間内で以下の体系的検証項目(SP-1〜11、PENT-1〜7)を自動実行・判定します。
(1) 自律ネットワーク防衛 & Air-Gap 自己修復
| ID | シナリオ | 検証手法 | 判定基準 |
|---|---|---|---|
| SP-3 | Air-Gap 有効化 → 外部遮断 | enable_air_gap IPC 送信 | output フックに policy drop が適用され、外部通信が完全遮断 |
| SP-3b | 防火壁強制消去時の自己修復 | nft delete table inet roamswitch による強制ルール削除 | Anti-Clobber Self-Healing が 5 秒以内に遮断ルールを自動再展開 |
| SP-3c | デーモン再起動時のフェイルクローズド | Air-Gap 中にデーモンを強制終了(SIGKILL)して再起動 | 再起動後も Air-Gap 遮断状態が自動で引き継がれる |
| SP-3d | Air-Gap 正規解除 → 通信復旧 | disable_air_gap IPC 送信 | drop ポリシー解除、全マーカーファイル削除、外部通信が即時復旧 |
| SP-8 | 予防的 ARP/NDP 固定 | 独立 netns ゲートウェイ環境で有効化 → 偽の gratuitous ARP 送出 | ゲートウェイ neigh エントリが PERMANENT を維持し、偽 ARP を拒否 |
| SP-9 | VPN キルスイッチ(WireGuard) | 実 WireGuard サーバー(別 netns)を立て .conf import → vpn_up | inet roamswitch_vpn(policy drop)下でハンドシェイク成立。トンネル死後も漏洩ゼロ |
| SP-10 | VPN キルスイッチ(Tailscale) | スタブ tailscale CLI +独立 netns、tailscale0 IF | トンネル外の平文 TCP/80 とローカル DNS を遮断し、DERP(TCP/443)と MagicDNS を許可 |
(2) カーネル & マウント堅牢化(Kernel Hardening)
| ID | シナリオ | 検証手法 | 判定基準 |
|---|---|---|---|
| SP-11a | ネットワーク sysctl 堅牢化 | apply_kernel_sysctl_hardening IPC 実行 | rp_filter=1, tcp_syncookies=1, accept_redirects=0 等の 7 項目が目標値に設定 |
| SP-11b | メモリ保護 & Yama LSM | ptrace_scope および suid_dumpable の設定検証 | kernel.yama.ptrace_scope=1(他プロセス盗聴阻止)、fs.suid_dumpable=0(コアダンプ抑止) |
| SP-11c | /tmp & /dev/shm noexec 防御 | apply_mount_hardening IPC 実行 | /tmp と /dev/shm が noexec,nosuid,nodev で再マウントされ、シェル/バイナリ実行が拒否 |
| SP-11d | ユーザー領域の実行性維持 | 正当なホームディレクトリ(~/)でのスクリプト実行 | 影響を受けず正常に実行可能(開発・日常運用に副作用を与えない) |
(3) 多層マルウェア・ランサムウェア防御 & カナリア
| ID | シナリオ | 検証手法 | 判定基準 |
|---|---|---|---|
| SP-1 | ランサムウェアおとり(カナリア)改ざん検知 | ~/Documents/ のカナリアファイル(.xlsx)を truncate / rename | 数秒以内に改ざん・移動を検知し Air-Gap を即時発動 |
| SP-2 | ランサムウェア一括暗号化のバースト検知 | 単一プロセスが短時間に多数の高エントロピーファイルを生成 | 当該プロセスを SIGSTOP で即時凍結し Air-Gap を発動 |
| SP-2b | shred 等の正当ツールの誤検知防止 | shred -n 3 -u を複数ファイルに対し実行 | ホワイトリスト照合により誤検知・凍結が発生しない |
| SP-2c | 重要システムプロセスの非凍結 | バースト検知時の凍結対象ロジック検証 | dockerd, systemd, containerd 等の重要インフラプロセスを絶対に凍結しない |
| SP-4 | EICAR テスト文字列の非隔離 | EICAR テスト文字列を Downloads に書き込み | 情報通知のみを発報し、ファイルを勝手に隔離・削除しない |
| SP-4b | 実シグネチャ検知時の確認制隔離 | 実マルウェアシグネチャを書き込み | 即時無断隔離せず、承認ダイアログを発行。ユーザー承認(quarantine_path)で退避 |
| SP-4c | 隔離 Vault の権限堅牢化 | 隔離 Vault ディレクトリおよび検体のパーミッション確認 | Vault ディレクトリ 0700、隔離検体 0400(読み取り専用・実行不可) |
| SP-4d | 原本安全復元(特権 IPC) | restore_quarantine_file IPC 送信 | 元ディレクトリの所有者権限(0644)で安全に復元し、除外リストへ登録 |
(4) 受動リンクガード・フィッシング防御
| ID | シナリオ | 検証手法 | 判定基準 |
|---|---|---|---|
| SP-7 | mode = block 強制遮断 | フィード掲載ホストおよび blocklistExtra ホストへ接続試行 | TLS 層(SNI パース)で即時 DROP。未掲載の正常ホストは HTTP 200 到達 |
| SP-7b | mode = warn fail-closed 動作 | 警告対象ホストへ接続試行し、ユーザー無応答でタイムアウト(8秒) | タイムアウト時に fail-closed(drop)。ユーザー明示「許可」時のみ通過 |
| SP-7c | mode = off 完全解除 | リンクガードを無効化 | inet roamswitch_linkguard テーブルが撤去されオーバーヘッドなし |
(5) BadUSB 対策・デバイス制御
| ID | シナリオ | 検証手法 | 判定基準 |
|---|---|---|---|
| SP-6 | 起動時接続キーボードの grandfathering | デーモン起動時に接続済みのキーボードを確認 | 既存デバイスを正常認識。起動後の新規ホットプラグのみグラブ対象 |
(6) Docker 隔離環境での網羅的ペンテスト(侵入テスト 17 項目)
| ID | 攻撃手法 | シミュレーション内容 | 防御・検知判定基準 |
|---|---|---|---|
| PENT-1 | C2 Exfiltration | Air-Gap 下での C2 宛て外部 HTTP/TCP 通信試行 | パケットフィルタにより全パケット即時 DROP |
| PENT-1b | FW 強制消去攻撃 | nft delete table inet roamswitch による防御破壊 | Anti-Clobber Self-Healing が数秒以内に自動再展開 |
| PENT-1c | Air-Gap 正規復帰 | 認証済み disable_air_gap IPC 送信 | DROP ルール撤去・全マーカー消去・通信完全復旧 |
| PENT-2 | /tmp バックドア実行 | /tmp に設置した悪意あるシェルの実行試行 | noexec マウント防御により Permission denied 拒否 |
| PENT-2b | /dev/shm ペイロード実行 | 共有メモリ /dev/shm に配置したバイナリの実行試行 | noexec マウント防御により実行遮断 |
| PENT-2c | ユーザー通常実行性 | 正当なユーザー領域(/home/tester/)でのスクリプト実行 | 開発・日常作業への影響ゼロ(正常実行) |
| PENT-3 | ptrace メモリ奪取 | PTRACE_ATTACH でプロセスから秘密鍵・トークン抽出試行 | Yama LSM ptrace_scope=1 によりアタッチ拒否 |
| PENT-3b | コアダンプメモリ抽出 | プロセスクラッシュ時のコアダンプ生成を試行 | fs.suid_dumpable=0 によりコアダンプ生成抑止 |
| PENT-4 | カナリア改ざん攻撃 | ~/Documents/ のカナリアファイル(.xlsx)をゼロクリア | サイズ異常・ハッシュ改変を即時検知 |
| PENT-4b | カナリア一括抹消 | カナリアファイルの完全削除を試行 | ファイル消失をリアルタイム検知しインシデント発報 |
| PENT-5 | IP スプーフィング | 送信元 IP アドレスを偽装したパケットを送信 | rp_filter=1(リバースパスフィルタ)が自動破棄 |
| PENT-5b | SYN Flood DoS | 未完了接続を大量送信して接続テーブル枯渇を試行 | tcp_syncookies=1(SYN Cookies)でテーブル枯渇防護 |
| PENT-5c | ICMP リダイレクト奪取 | 不正な ICMP Redirect でルーティングハイジャック試行 | accept_redirects=0 によりリダイレクトを無視 |
| PENT-6 | キリル文字ホモグラフ詐欺 | gооgle.com 等の同形異字を用いたフィッシング誘導 | LinkGuard が偽装特定、40点(DANGEROUS / block)判定 |
| PENT-6b | 生 IP 直打ち・平文通信 | 未暗号化の生 IP 直打ち接続試行 | リスクファクターとして正確に検知・警告 |
| PENT-7 | OpenAI API キー漏洩 | コードやログに sk-proj-... トークンが含まれる事象 | エントロピー解析で検知し安全にマスク(sk-pro...YZ12) |
| PENT-7b | GitHub トークン漏洩 | コードやログに ghp_... トークンが含まれる事象 | シグネチャ・エントロピー解析で正確に特定 |
| PENT-8 | カーネル LPE (Frag Gap) 悪用阻止 | 非特権ユーザーによる unshare -U -r(CVE-2026-53362 悪用試行) | user.max_user_namespaces = 0 により Operation not permitted で即時拒絶 |
| PENT-9 | eBPF 脅威検知 → プロセス凍結 & 遮断 | Falco eBPF ソケット経由での Critical/Emergency アラート注入 | 当該 PID を SIGSTOP でミリ秒凍結(状態: Ts)し Air-Gap を即時展開 |
(7) 侵入テスト実証総括(Falco なし [スタンドアロン] & Falco あり [eBPF 連携])
独立した Docker 侵入テスト環境(Target: Ubuntu 24.04 + RoamSwitch Client Edition、Attacker: Nmap / curl / hping3 等)にて実施した包括的ペンテスト(詳細は公式報告書 docs/CLIENT_PENTEST_REPORT.ja.md 参照)の結果総括:
| 検証項目 | Falco なし (スタンドアロン堅牢化) | Falco あり (eBPF ランタイム防護) | 防御判定 |
|---|---|---|---|
| 外部ポートスキャン耐性 (Nmap SYN) | 合格(Lockdown で 100% Filtered / DROP) | 合格(Lockdown で 100% Filtered / DROP) | 外部完全ステルス化 |
| 外部直接アクセス遮断 (TCP/80, 3306) | 合格(タイムアウト遮断・無応答) | 合格(タイムアウト遮断・無応答) | 外部露出ゼロ |
| カーネル LPE (Frag Gap) 悪用防止 | 合格(sysctl 堅牢化で unshare 根本遮断) | 合格(eBPF 即時検知 + SIGSTOP 凍結 + Air-Gap) | 多層防御確立 |
| 未許可ポート検知 (アノマリー) | 合格(0.0.0.0 公開プロセスを高リスク検出) | 合格(0.0.0.0 公開プロセスを高リスク検出) | 100% 検知・警告 |
| ログ肥大化・CPU負荷対策 | ログ発生なし(ゼロオーバーヘッド) | 合格(ノイズ95%カット / バッファ2MB / 50MB上限) | デスクトップ常駐最適化 |
C.3 既知の制約
| 制約 | 影響 | 対応 |
|---|---|---|
一部カーネルで tmpfs(/tmp)への FAN_MARK_MOUNT がイベントを配送しない | /tmp のファイル書き込みは fanotify で監視されない | /tmp は揮発性かつ監視対象ユーザーフォルダではない。ランサムウェアの標的は ~/。マーク失敗時はログに明示 |
| コンテナ overlayfs / tmpfs での fanotify 非配送 | Docker 内でのエントロピーバースト検知が検証不可 | 実機(ext4)で検証。ハーネスがプローブで自動 SKIP |
RoamSwitch の nftables 操作と Docker の iptables-nft が同一 netfilter 空間を共有 | RoamSwitch のルール操作が Docker の NAT ルールと干渉し得る | 運用上の注意点として文書化。専用テーブル inet roamswitch で分離するが、nft flush ruleset 等の全体操作は避ける |
| リンクガードはブラウザの DoH を TLS SNI 経由でしか把握できず、ECH(Encrypted Client Hello)はそれも隠す | ECH が普及すると、ECH 対応サイトへの ECH 接続はホスト名で識別できない | ローカルのブロックリストとヒューリスティックは DNS/HTTP と非 ECH の TLS には引き続き作用する。リゾルバ経路は脅威保護 DNS(§4.5・オプトイン)でカバー |
updates.enabled = false の場合、フィードの鮮度はリリース間隔に依存する | 更新を切ると、フィードは直近の apt upgrade 時点のものになる | 最重篤の検出(ブランドのホモグラフ)はフィード不要。常時最新が必要なら更新を再度有効化するか脅威保護 DNS を使う |