§1概要 & 設計原則
RoamSwitch Server Edition は、AWS、Google Cloud、Azure、各種 VPS やベアメタルで稼働する Linux サーバ基盤のために設計された、ヘッドレス常駐型の自律ネットワークセキュリティ&整合性防護スイートです。
Wi-Fi 移動追従を中心とするクライアント端末版とは異なり、サーバ版は 「インバウンド既定拒否 (Default Drop) による最小露出」「クリティカルパス改ざん即時検知 (Critical Path FIM)」「カーネル特権昇格脆弱性 (Frag Gap CVE-2026-53362) の予防的封鎖」「eBPF によるランタイム脅威監視」「自律的 Air-Gap 遮断と緊急通知」 を中核思想に据えています。
外部通信ライブラリをデーモン本体から完全排除する Zero Telemetry 原則 をコードレベルで貫き(既定で外部へ出るのは、無効にもできる、署名付きデータの受信(カーネル CVE データ、Web ルール、Ubuntu サーバーでは OS パッケージ CVE 照合用の Ubuntu のパッケージ CVE データ。マニフェストの確認は毎日、CVE データ本体は約 30 日に 1 回)だけで、ほかの例外はすべて運用者が明示的にオプトインしたものです。§1 コア設計原則)、機密情報(ボットトークンや設定)はパーミッション 0600 で保護されます。
クライアント版とサーバ版の決定的な差異
| 評価軸 | クライアント版 (Client Edition) | サーバ版 (Server Edition) |
|---|---|---|
| 稼働形態 | ラップトップ、持ち運びワークステーション | クラウド VM、ベアメタル、コンテナホスト |
| ネットワーク動作 | 接続先 Wi-Fi(SSID/MAC)に応じた動的切替 | 静的な既定拒否 (Default Drop)。必要最小限の公開ポートのみ明示許可 |
| インターフェース | GTK3 デスクトップ GUI + システムトレイ | 完全ヘッドレス。CUI コマンド + 対話型セットアップウィザード + ターミナル UI(roamswitch-server-tui) |
| インシデント通知 | デスクトップ通知 (libnotify) | Telegram, LINE Messaging API, 汎用 Webhook への即時配信 |
| 防護の重点 | 公衆Wi-Fi中間者攻撃 (ARP)、BadUSB、ランサムウェア暗号化 | ポートスキャン耐性、重要ファイル改ざん (FIM)、コンテナ脱出、カーネル特権昇格 (LPE) |
コア設計原則
- Zero Telemetry (ゼロ・テレメトリ): 既定では、利用者や環境に関する情報を外部へ一切送信しません。デーモン本体に HTTP/TLS の外部通信ライブラリは無く、運用者が設定した通知・転送のときだけ、システム標準の
curl(Telegram/LINE/Webhook。トークンや URL などの秘密は標準入力の設定で渡し、コマンドライン引数には載せません)とopenssl(TLS の syslog 転送)をサブプロセスとして実行します。既定で有効な外部通信は、パッケージマネージャーの更新を除いて、次の 1 つだけです。(1)cve_kernel_map_updates_enabled(既定 true)が有効な間、roamswitch-updaterが、署名付きのデータを lafine.net から匿名で受信します。受信するのは、コンテナ隔離監査用のカーネル CVE データ、Web アプリ層の検知ルール(Web ルール)、Ubuntu サーバーでは OS パッケージ CVE 照合用の Ubuntu のパッケージ CVE データです。署名付きマニフェストとその署名の確認は毎日(GET 2 回)、カーネル CVE データ本体と Ubuntu のパッケージ CVE データは前回の更新から約 30 日たったときだけ(Ubuntu のデータが未取得のサーバーは次の日次の確認で取得)、Web ルールはマニフェストの版が新しいときだけです。クエリ文字列と Cookie がなく、ソフトウェアを名乗るヘッダが固定のUser-Agent: RoamSwitch-Updaterだけの HTTPS GET を行い、Ed25519 署名を検証できたデータだけを採用し、このホストの情報は一切送りません。roamswitch server config set cve_kernel_map_updates_enabled false、セットアップウィザード、またはサーバー TUI で無効にでき、既存のserver.confに false が明示されていれば、そのまま尊重されます。無効にしても監査は同梱のベースラインで動作しますが、Ubuntu サーバーの OS パッケージ CVE 照合は、Ubuntu のデータが入るまで「CVE データ未取得」となり、結果を返しません。これ以外の例外は、すべて運用者が明示的にオプトインしたものです。(2) イベント転送(forward_enabled、既定 false)は、運用者が設定した syslog/JSON Lines ファイル/署名付き Webhook の宛先にだけ送ります。(3)npm_audit_signatures_enabled(既定 false)を有効にし、運用者がroamswitch audit-npm-signatures <DIR> --confirmを実行したときだけ、npm自身が registry.npmjs.org へ問い合わせます。(4)sensor_pairing_enabled(既定 false)を有効にした場合のみ、運用者が入力した Sensor のアドレスへ接続します(Sensor は別製品で、Zero Telemetry の対象ではありません)。(5) Fleet(Business 層)は端末側のコードのみで、登録していない端末は何も送らず名前解決もしません。管理サービスは未デプロイ・未出荷で、遠隔コマンドの経路は設計上ありません。(6) 同梱の別ツールaccessscope(アクセスログのセキュリティ診断)の LLM 連携は既定でオフです。有効にした場合(コマンドラインでは--llmを指定した場合)に限り、運用者が選んだ LLM のコマンドを通じて、診断の要約・根拠のログ行・環境の情報(ログを探す機能)が外部のサービスに送られることがあります。デーモン(roamswitch-server)はこの通信に関与せず、AccessScope は、root でも LLM 分析を実行でき(その場合、LLM は root の権限で動くので、権限のないユーザーでの実行をお勧めします)、Zero Telemetry の対象外です。同梱の別ツールpersonalsoc(ログと防御状況の確認)も同様で、--llmを指定したときに限り、診断の要約が、運用者が選んだ LLM のコマンドを通じて外部のサービスに送られることがあります(Zero Telemetry の対象外)。pathscopeのコマンドは LLM を呼び出しません。(7)clamav_enabled(既定 false)を有効にしたときだけ、デーモンがfreshclamを実行して ClamAV のシグネチャを受信します(シグネチャのデータベースがまだ無いときの初回と、clamav_scan_dirsを指定している間のclamav_freshclam_interval_secs(既定 7200 秒)ごと)。 - インバウンド既定拒否 (Inbound Default Drop)は、許可したポート以外(TCP)への新規の着信を
nftablesで破棄し、RSTすら返しません。ポートスキャンには、許可したポートしか見えません(ループバック、確立済みの接続、ICMP と ICMPv6 は通します)。 - フェイルクローズド & 自己修復は、次のとおりです。既定拒否と隔離のルールはカーネルに残るため、デーモンが停止・再起動しても維持されます(
Restart=always)。Docker の公開ポート保護は 60 秒ごと、Egress ブロックリストとポートスキャン遮断は 300 秒ごとに再確認して、消えたルールを再設置します。縮退(DEGRADED)の隔離ルールは 15 秒ごとの評価で確認し、消えていれば再設置します。既定拒否のルールそのものを外部から消された場合は自動では再適用しないので、診断「インバウンド既定拒否」(roamswitch server status)で確認してください。 - パーミッション 0600 による保護: 設定ファイル
/etc/roamswitch/server.confは作成・更新時に自動的にパーミッション 0600 が強制されます。トークンや Webhook URL などの秘密は、curlへ渡すときも標準入力の設定で渡し、コマンドライン引数(/proc/<pid>/cmdline経由で他のユーザーから見える)には載せません。
§2脅威モデル
常時インターネットに晒されている公開サーバが直面する現代の高度サイバー脅威をモデル化しています。
- 無差別ポートスキャン & 内部DB露出: 意図せず
0.0.0.0にバインドされた MySQL(3306) や Redis(6379) への即時攻撃。 - クリティカルパス改ざん (永続化バックドア): 侵入者が
/bin/loginや/etc/pam.d/*を改ざんしてバックドアを維持する試み。 - カーネル特権昇格 (Frag Gap CVE-2026-53362 等): 非特権 User Namespace を悪用したルート権限奪取。
- コンテナ脱出 (Container Breakout): ホストの
docker.sockマウントや特権コンテナを悪用したホスト乗っ取り。 - プロセスメモリからの秘密情報窃取: 他プロセスからの
ptraceやメモリ読み取りによる秘密鍵ダンプ。
§3システムアーキテクチャ
RoamSwitch Server Edition は、ヘッドレス環境に最適化された軽量な Rust ネイティブバイナリ群で構成されています。
┌─────────────────────────────────────────────────────────────┐
│ ユーザー & 管理者空間 │
│ │
│ roamswitch (CLI) roamswitch-mcp (AI Agent) │
│ ・server status (34項目診断) ・セキュリティ監査ツール群 │
│ ・server config / setup ・標準入出力 JSON-RPC │
│ ・fim verify / update │
│ │ │ │
└────────┼───────────────────────────────┼────────────────────┘
│ (CUI コマンド & 設定反映) │ (stdio)
┌────────▼───────────────────────────────▼────────────────────┐
│ システム基盤 (systemd + root 権限) │
│ │
│ roamswitch-server-daemon │
│ ├── CriticalPathFim (SHA-256 バックグラウンド巡回) │
│ ├── eBPF / Falco 連携モジュール │
│ ├── Notifier (Telegram / LINE / Webhook 即時発報) │
│ └── Air-Gap Controller (nftables 緊急遮断ルール展開) │
│ │
│ 設定管理: /etc/roamswitch/server.conf (パーミッション 0600) │
│ 整合性DB: /var/lib/roamswitch/fim.db │
└─────────────────────────────────────────────────────────────┘
§4コア防護機構の技術仕様
ネットワーク境界防護 (Inbound Default Drop)
nftables の inet roamswitch_server テーブルの input チェーンに policy drop を適用します。管理者指定ポート(ssh_ports, allowed_ports)への TCP の新規着信だけを許可し、未許可ポートへの通信はタイムアウト(ドロップ)させます。ループバック、確立済みの接続、ICMP と ICMPv6 は許可します。
万が一 Air-Gap(緊急隔離)が発動した場合でも、保守用 SSH ポート(ssh_ports)と登録済みの保守元 IP(whitelist_ips)の管理経路(新規・確立済み)だけはルール上保持され、リモートからの復旧作業が不能になる事態を防ぎます(preserve_ssh_on_isolation、既定 true)。それ以外の確立済み接続(攻撃者がすでに張っている C2 やリバースシェル等)は、default-drop で遮断されます。管理元の指定(whitelist_ips と guard.yaml の許可先)が空のときは、隔離中も保守 SSH ポートへの新規接続をすべての送信元から受け付けるため、サーバーの起動時に警告します。preserve_ssh_on_isolation=false にした場合は、SSH も含めて遮断します。1.10.4 以前のサーバー版には、隔離用ルールが SSH の応答を落とし、隔離中は管理者の SSH が通らない不具合がありました。1.10.5 で修正しています。
クリティカルパス改ざん監視 (Critical Path FIM)
OS 起動・認証・ネットワーク・システム管理に関わる最重要ファイルの SHA-256 ハッシュ整合性を監視します。対象は、固定の 23 か所(/etc/shadow・/etc/passwd・/etc/sudoers・/etc/ssh/sshd_config・/bin/login・/usr/bin/sudo・/usr/sbin/sshd・/usr/lib/systemd/systemd など)、/etc/pam.d・/etc/sudoers.d・/etc/ssh/sshd_config.d・/etc/cron.d と /etc/cron.{hourly,daily,weekly,monthly}・/etc/systemd/system・/etc/ld.so.conf.d・/etc/profile.d・/root/.ssh の 12 ディレクトリ直下のファイル(新規ファイルの出現も検知。/root/.ssh の known_hosts と known_hosts.old は、SSH で接続するたびに書き換わるため除きます)、RoamSwitch 自身のバイナリ、ログイン可能な各ユーザーの authorized_keys、および存在してはならない /etc/ld.so.preload(出現を検知)です。正規のパッケージ更新のあとは、正常ベースラインが自動で更新されます。Debian 系では /etc/apt/apt.conf.d/99roamswitch-fim フック(apt upgrade 等)が、RHEL 系と openSUSE では /var/lib/rpm を監視する systemd.path ユニット(roamswitch-fim-update.path)が、roamswitch fim update を実行します。
侵入経路の特定(誰が、どの SSH ログインから変更したか)
ファイルの変更を知らせる通知(クリティカルパス FIM とロックファイル FIM)に、変更したプロセス(アカウント・実行ファイル・コマンド)と、そのプロセスが属するSSH ログイン(接続元の IP とポート・ログイン時刻・認証方法・公開鍵の指紋)を添えます(既定で有効。fim_intrusion_context_enabled)。イベントを読んだその場で、書き込んだプロセスの /proc/<pid>(実行ファイル、ユーザー、loginuid、sessionid、cgroup、親の連鎖)を撮ります。tee や sed のように一瞬で終わるプロセスのためです。すでに終了していて撮れなかったプロセスは、実行レコーダー(有効なとき)の記録で補います。ログインへの結び付けは、SSH ガードのセッション表を先に使い、なければ logind と journald の Accepted の行から引きます。SSH ガードを無効にしていても動きます。外部には何も送りません。
信じるのは、カーネルが報告する値だけです。実行ファイルは /proc/<pid>/exe(argv[0] ではありません)、ログインは loginuid(ログイン時に一度だけ設定され、sudo や su を越えて引き継がれます)と cgroup の session-N.scope、それに親の連鎖です。comm・コマンドライン・環境変数のように、プロセス自身が変えられる値は、表示するだけで判定には使いません。sshd を名乗るだけのプロセスは、SSH のログインとは見なされません。通知は、次の三つを区別して書きます。見つかった(接続元・時刻・認証方法・鍵)、「SSH のログインは見つかりませんでした」(調べた結果、systemd のサービス、コンテナ、ローカルのログインセッション、ログインセッションに属さないプロセス、のどれかだったとき)、「ログインの経路は調べられませんでした」(プロセスがすでに無く実行レコーダーにも無い、logind が答えない、定期検査で見つけた変更でプロセスが分からない、など)。調べられなかったことを、ログインではないとは書きません。取得に失敗しても通知は必ず出ます。
/etc/ssh/sshd_config: modified
プロセス: root(ログインしたアカウント: admin) / /usr/bin/tee / コマンド: tee /etc/ssh/sshd_config (pid 4121)
SSH ログイン: 203.0.113.25:51544 から admin が 2026-10-17 03:12:08 にログインしたセッションです(認証: publickey, 鍵 SHA256:AbCdEf0123456789)
/etc/sudoers: modified
ログインの経路は調べられませんでした(定期検査で見つけた変更のため、変更したプロセスは分かりません)
install・sed -i・同じディレクトリでの cp と mv のように、新しいファイルを同じ名前の下に置く編集も検知します。ファイル単位の印は inode に付くため、以前はこの種の編集でイベントが出ませんでした。いまは、監視対象のファイルがあるディレクトリにも印を付け、同じディレクトリで別の名前のファイルが閉じられると、数ミリ秒後に名前を引き直します。限界もあります。別のディレクトリで書いたファイルを mv で持ち込む場合は、周期スキャン(fim_check_interval_secs)が見つけるまで分かりません。そのときは書き込んだプロセスが分からず、通知にもそう書かれます。
実測は、運用者の接続元を別のネットワーク名前空間(netns)に置いた Ubuntu 24.04 の VM で行いました。sudo tee、nohup で切り離したプロセス(ログアウトのあとでも接続元の IP を特定でき、「このセッションは既に終了しています」と付きます)、sudo -i の連鎖、ごく短命の cat のいずれでも、IP・ユーザー・認証方法が通知に出ました。ローカルのログインでは「SSH のログインは見つかりませんでした」と出ます。exec -a "sshd: …" で名前を偽装したプロセスは、SSH とは見なされませんでした。
記録は、通知のほかに、インシデントタイムライン(種別 fim)と /var/lib/roamswitch/fim_intrusions.json(root 専用、最新 100 件)に残ります。MCP の verify_fim には、追加のフィールド recentIntrusionContext として出ます(root で動かしたときだけ)。改ざんかどうかの判定は変わりません。限界もあります。実行レコーダーを無効にしていて、書き込んだプロセスが極端に短命なときは、実行ファイルが分からず、親プロセスの実行ファイルとコマンドラインを出します。持続化の監視(persistence_watch)の通知にはプロセスの情報がないので、侵入経路は付きません。侵入経路が示すのは、変更したプロセスとログインまでです。侵入の入口そのもの(突かれた脆弱性や、鍵が盗まれた経緯)は分かりません。止めるには fim_intrusion_context_enabled=false にします。
カーネル堅牢化 & コンテナ監査
Frag Gap (CVE-2026-53362) 対策として user.max_user_namespaces = 0 を適用し、非特権名前空間の作成を禁止。さらに kernel.yama.ptrace_scope = 2 により他プロセスへの ptrace アタッチを拒絶します。稼働中コンテナへの docker.sock 露出も常時監査します。サーバー版では、非特権名前空間の禁止は既定で有効です(harden_userns_enabled)。rootless の Podman などが動かなくなるため、使う場合は false にしてください。
Docker の公開ポート(-p)はホストの標準ファイアウォールをバイパスする既知の問題があるため、protect_docker_ports=true(既定)で、公開前の宛先ポートが許可ポート(ssh_ports・allowed_ports)に無い外部インターフェース発の通信を遮断します(戻り通信・ループバック・コンテナ間通信は対象外)。iptables/iptables-nft バックエンドでは DOCKER-USER チェーン(IPv4、Docker の IPv6 が有効なら ip6tables でも)に、Docker 29 以降などのネイティブ nftables バックエンド(DOCKER-USER が無い構成)では専用の inet roamswitch_docker_guard テーブル(forward フック、優先度 -10)に設置し、後者は 1 つのルールセットで IPv4 と IPv6 の両方を対象にします。デーモンは定期的に再確認して、消えたルールを再設置します。加えて、--privileged コンテナの起動や /var/run/docker.sock のマウントを docker events でリアルタイム検知し、コンテナ脱出リスクとして即座に通知する常時稼働のガードも備えています(設定不要、自動遮断は行わない通知専用)。メール中継や共有フォルダなど他者に渡すファイルを扱うホストでは、任意で有効化できる File Scan Guard(組み込み YARA + オプションの ClamAV)が定期スキャンと自動隔離を提供します。
コンテナ実行ガード(container_exec_guard_enabled、既定 true)。稼働中の Docker コンテナの overlay2 ルートに fanotify の FAN_OPEN_EXEC(通知クラス)を設定し、実行されたファイルの内容を組み込みの YARA ルールで検査して通知します。docker events を購読してから docker ps で稼働中のコンテナを列挙するため、デーモンの起動時にすでに動いているコンテナも対象になります。実行されたファイルの内容は fanotify が渡すファイルディスクリプタから読むので、実行直後に自己削除されても検査できます。通知専用で、実行を止めたり拒否したりはしません(Server Edition には許可クラスの fanotify は存在しません)。Docker 29 は docker events のメッセージから非推奨の Status を外したため、1.11.0 までは、購読がすぐ失敗し、ガードは購読を使えないまま5秒ごとに再接続を繰り返していました。1.11.1 から、古い Docker でも同じ値の Action を使う書式に変えました。購読が続けて失敗するときは、再接続の間隔を5秒から最大5分まで倍にし、同じ原因の警告は最初の1回だけ記録します。
eBPF ランタイム脅威監視 & ログ肥大化防止
Modern eBPF(CO-RE / BTF)プローブを用い、特権昇格やコンテナ脱出の予兆をミリ秒単位で検知。Falco 導入時に課題となりやすい「ログ肥大化・ディスク枯渇・CPU負荷」に対して、RoamSwitch は以下の最適化設定(/etc/falco/config.d/99-roamswitch-optimized.yaml)を標準配備します:
- 重要度フィルタリング (
priority: warning): 通常操作ノイズ(Info/Notice)を 95% 遮断し、真の脅威のみ抽出。 - カーネル事前ドロップ (
drop_failed_exit: true): 失敗システムコールをカーネルドライバで破棄し CPU 負荷を半減。 - メモリ最適化 (
cpus_for_each_buffer: 2): リングバッファを 8MB → 2MB へ縮小し、常駐メモリを 30〜50MB に抑制。 - ダイレクト UNIX ソケット IPC: ディスク I/O を介さず、メモリ上のソケット(
/run/roamswitch/events.sock)で即時連携。 - ログ自動上限 (
/etc/logrotate.d/roamswitch-falco)は、maxsize 50Mと 7世代の圧縮ローテーション(毎日実行)で、Falco のログの肥大化を抑えます。
eBPF 隔離の精度と速さ(Falco のルール任せにしない)
eBPF による隔離の精度と速さは、センサー(Falco や Tetragon)のルールに左右されます。実際、Falco 0.45.0 の標準ルール(29 件)には、ホスト上のリバースシェルを検知するものがありません。リバースシェル向けのルールはコンテナ専用で、一部は RoamSwitch の priority: warning の条件で捨てられます。RoamSwitch は、ルールの出来を前提にせず、次の三つで補います。隔離にかかった時間を測ること、警報の経路が生きていることを定期的に確かめること、リバースシェルを自分でも検知することです。
隔離の遅延の計測として、eBPF のインシデントごとに、Falco が検知してからデーモンが警報を読むまで(detect_to_receive_ms)、読んでから対処が終わるまで(receive_to_action_ms。隔離の呼び出しが戻ったあとに測ります。ホスト Air-Gap は、ルールセットを読み込み終えるまで)、その合計(detect_to_action_ms)を記録します。値は ebpf_incidents.json と、MCP の get_ebpf_incidents に、追加のフィールドとして出ます。プロセスを隔離したときの通知には、「検知から対処まで N ミリ秒」の 1 行が付きます。実測(Ubuntu 24.04 の VM、Falco 0.45.0、modern eBPF)は、検知から受信まで 1〜2 ミリ秒、受信からプロセス隔離と SIGKILL まで 41〜62 ミリ秒(合計 42〜64 ミリ秒)、ホスト Air-Gap は合計 109 ミリ秒でした(目標の 150 ミリ秒の内側)。値はこの VM のものです。Tetragon は受信時に時刻を付けるので、detect_to_receive_ms はありません。
ハートビート(ebpf_heartbeat_interval_secs。既定は 600 秒、0 で無効、有効にするときは 60〜86400)は次のように働きます。パッケージには、RoamSwitch 自身の Falco ルール 96-roamswitch-heartbeat.yaml が入っています。デーモンは定期的に、root 専用の使い捨てのパス(/run/roamswitch/heartbeat/ の下の乱数)を開き、そのルールの警報が Falco の出力から戻ってくるまでを測ります。1 回の往復で、ルールが読み込まれていること、Falco が動いていること、出力の経路が生きていることを、まとめて確かめられます。戻らない確認が 2 回続いたとき(1 回目の 1 分後に再確認)だけ、「ルールが読み込まれていない、または出力が届いていない」という警告を、24 時間に 1 回まで通知します。ハートビートの行は、隔離・通知・許可リスト・インシデント記録のどれにも通さずに捨てます。外部には何も送りません。実測の往復は 2〜6 ミリ秒で、ルールを外すと約 164 秒後に警告が出て、戻すと約 75 秒で正常に戻りました。
状態は /var/lib/roamswitch/ebpf_heartbeat.json に残り、MCP の get_ebpf_incidents の heartbeat と、診断 K-04(eBPF ランタイムエージェント。診断の項目数は 34 のままです)が読みます。Falco が動いていても、ハートビートが戻らなければ K-04 は不合格です。限界もあります。確認するのは 1 つのルール(open 系のシステムコール)の往復で、ほかのルールが個別に壊れていることまでは分かりません。確認の間隔(既定は 10 分)のあいだに壊れたものは、次の確認まで分かりません。Tetragon では行いません。Falco が動いていない、またはルールのファイルが無いホストでは、何も確認せず、何も警告しません(停止は従来の通知が担当します)。Falco は確認のたびに警告を 1 行、syslog に書きます(既定なら 1 日 144 行)。
リバースシェル検知(RoamSwitch 自身)
Falco のルールに頼らず、RoamSwitch 自身が 2 秒ごとに /proc を読み、標準入力と標準出力が、このホストから外部のアドレスへ張った 1 本の TCP 接続につながっているシェル・インタプリタ(sh・bash・python・perl・ruby・php・node など)を探します。bash -i >& /dev/tcp/…、python、perl、nc -e、socat exec: が対象です。2 回続けて見えたものだけを、プロセスごとに 1 回報告します。既定は外部の公開アドレス宛だけで、プライベートアドレス宛(RFC 1918・CGNAT・リンクローカル)は reverse_shell_watch_include_private=true のときだけ数えます(ssh の ProxyCommand、コンテナ、ローカルの開発に正規のものが多いためです)。ループバックは見ません。外から受け付けた接続(inetd や socat TCP-LISTEN が accept したもの)も対象外です。実測(Ubuntu 24.04 の VM)では、bash の /dev/tcp、python、perl、nc -e、socat exec: を、接続から約 2.4 秒で検知しました。ssh、ProxyCommand 付きの ssh、git over ssh、nc のクライアントは検知しません。
検知は、RoamSwitch Reverse Shell という Critical の eBPF イベントとして、Falco の警報と同じ経路(許可リスト、通知のクールダウン、guard.yaml の重大度ごとの動作、インシデント記録)に入ります。既定では、プロセスの隔離と終了(kill)を行います。root が所有するリバースシェルは、プロセス単位の隔離に失敗するため、既存の Critical の方針どおり、ホスト全体の Air-Gap まで進みます。正規のプログラムが誤って検知されるのが心配なときの逃げ道は二つあります。reverse_shell_watch_notify_only=true(Warning として通知だけを行い、隔離も kill もしません。既定は false)と、許可リストへの登録 sudo roamswitch server allowlist add "RoamSwitch Reverse Shell" <プロセス名> です。設定キーは reverse_shell_watch_enabled(既定 true)、reverse_shell_watch_include_private(既定 false)、reverse_shell_watch_notify_only(既定 false)の 3 つです。
限界もあります。名前(プロセス名と実行ファイル名)で絞るので、シェルやインタプリタ以外の名前で、標準入出力をソケットにつないだ自作のバイナリは見つかりません。ループバック経由(ssh -R の折り返しなど)の接続と、2 秒より短い接続は見ません。ホストのプロセス空間から見えないプロセスも見られません。この検知は Server Edition だけの機能で、Mac と Linux クライアント版にはありません。
自律的 Air-Gap 遮断 & 事前緊急通知
重大な改ざんやインシデント検知時、外部ネットワークを切断する直前に Telegram / LINE / Webhook へ同期的に最大3秒間の待機を行って通知パケットを確実に外部へ送出した上で、Air-Gap 遮断ルールを展開します。
🚨 [RoamSwitch IMMINENT AIR-GAP] 【緊急警告】ネットワーク完全遮断 (Air-Gap) を即時実行します (cve_2026_53362_lpe)
🚨 重大セキュリティインシデント検知: cve_2026_53362_lpe
ホスト: production-app-01 (IP: 192.168.1.10)
対象プロセス: exploit_payload (PID: 4921)
緊急対応: 被害拡大防止のため、ホストのネットワーク全遮断 (Air-Gap) を発動します (SSH管理ポートのみ維持)。
警告: この通知の送信完了直後にすべての外部インターネット通信が遮断されます。
時刻: 2026-09-06 18:00:00
⚠️ ネットワーク接続は直ちに遮断されます。解除条件はトリガーの確度で異なります。低確度のトリガー(eBPF の Error 階層以下など)は、短いタイマー(guard.yaml の server_safeguards.safety_timer_secs、既定 300 秒)で全面復旧します。高確度のトリガー(eBPF の Critical/Emergency/Alert、同一ルールの繰り返しによるホスト隔離へのエスカレーション、カナリア改ざんなど)は、黙って復旧しません。短いタイマー以降は原因(凍結した PID の生存、ルールの再発火など)を再確認し、原因の消滅を確認できたときだけ通知付きで解除します。確認できず、ACK も無いまま上限(isolation_hard_cap_secs、既定 3600 秒、0 = 上限なし)に達すると DEGRADED に移行します。DEGRADED は保守 SSH ポートと保守元 IP の管理経路(新規・確立済み)を保ち、新規の外向きは保守元 IP 宛てのみで、時間経過では絶対に自動で開きません。解除は、原因消滅の再確認、roamswitch server ack(DEGRADED 中の ACK は解除を意味します)、または roamswitch emergency-restore のみです。ACK 済みの隔離(FULL)は、運用者が引き継いだものとして、タイマーでの復旧も、原因の消滅を確認できたときの自動解除も、DEGRADED への移行もせず、emergency-restore で解除されるまで維持されます。各遷移はインシデントタイムラインに記録され、通知されます。
プロセス実行レコーダー & イベント転送
netlink のプロセスコネクタで exec を記録するローカル専用のレコーダーが、既定で有効に動作します(クライアント版と共通)。実行ファイルのパス・コマンドライン引数(長さを制限し、制御文字はエスケープ)・親プロセスの系譜・uid・コンテナ ID・実行ファイルの SHA-256 などを /var/lib/roamswitch/exec_log に、ハッシュチェーン付きの JSON Lines(0600)として保存します(既定 200 MB/14 日)。ハッシュチェーンは改ざんを検知できますが、防止はできません(root を奪った攻撃者はチェーン全体を書き換えられます)。通知専用の相関ルール EXR-001〜010(Web サービスからのシェル起動、/tmp・memfd 実行、攻撃ツールの連鎖など)は単独では隔離しません。exec_rules_isolation_optin(既定 false)を運用者が明示的に有効にした場合に限り、高重大度のルールがホスト隔離の高確度トリガーとして扱われます。
イベント転送(forward_enabled、既定 false)は、運用者が設定した宛先だけに送ります。宛先は、syslog(RFC 5424 または CEF、UDP/TCP/TLS)、JSON Lines ファイル、HMAC-SHA256 署名付きの Webhook です。TLS の syslog はシステムの openssl s_client、Webhook はシステムの curl(秘密は標準入力の設定で渡し、コマンドライン引数には載せません)で行い、デーモンに TLS/HTTP のクレートはありません。送られるのは、インシデントタイムラインと通知履歴に載る検知イベント(eBPF・隔離状態の遷移・FIM・カナリア等)と実行ルールの通知について、ホスト名・イベント ID・時刻・種別・重大度・ルール・処置・PID・プロセス名・要約の各項目と、実行記録のセグメント封印ハッシュだけです。実行記録の生ログは含みません。
DNS トンネリング/データ持ち出し検知
dns_tunnel_detect_enabled(既定 true)を有効にすると、この端末自身が送信する DNS クエリ(UDP/TCP の 53 番ポートのみ)を専用の NFQUEUE で監視します。ラベル長が 40 バイトを超え、かつシャノンエントロピーが 3.8 を超える場合、または質問レコードの qtype が TXT/NULL のいずれかである場合(ClamAV の署名更新が使う clamav.net 宛ての TXT だけは数えません)に、その質問を iodine・dnscat2 などの DNS トンネリングツールに特徴的な「疑わしい形状」として数え、直近 60 秒間に 10 件以上に達した場合にのみ通知します(同じドメインでの再通知は 300 秒空け、バーストが続く間は 600 秒、1200 秒と延ばして、最大 1800 秒間隔にします)。検査対象は DNS(UDP/TCP の 53 番ポート)のみで、他の通信は検査しません。通知専用で自動遮断は行いません。裏側の nft ルールには bypass が付与されており、監視スレッドが停止・無応答であっても DNS クエリはそのまま通過します。名前解決がこの機能の不調によって止まることはありません。設定変更の反映には roamswitch server restart(ライブリロード非対応)が必要です。
既知の限界。ハイフン区切りの長いホスト名(例: prod-us-east-1-web-server-node-0042)や、メールサーバーが多数の異なる送信元ドメインの SPF/DKIM TXT レコードを短時間に検証するような、サーバー用途では特に現実的な正当な処理が、宛先ドメインを区別しない単一のグローバルカウンタによる TXT/NULL バーストの集計によって誤検知を招くことを実測で確認していました。両方とも 2026-09-23 に対応済みです。設定可能な entropy 許可リストと、apex domain(QNAME 末尾 2 ラベルによる簡易近似で、Public Suffix List の厳密な実装ではありません)単位でのバーストカウンタのキー分離によって解消しています。許可リストは既定で主要 CDN/クラウドと PaaS のサフィックス 16 件(cloudfront.net・akamaiedge.net・akamaized.net・edgekey.net・edgesuite.net・fastly.net・azureedge.net・googleusercontent.com・amazonaws.com・r2.dev・workers.dev・vercel.app・netlify.app・supabase.co・firebaseapp.com・web.app)を組み込み済みで、運用者は ServerConfig.dns_tunnel_entropy_allowlist(server.conf のカンマ区切りキー、TUI からも編集可能)に自組織の内部ドメインを追加できます。許可リストが免除するのはラベル長+エントロピーによる疑わしさ判定のみで、TXT/NULL の qtype 判定は許可リストの有無によらず適用されるため、許可リスト配下にトンネルを偽装する回避は防いでいます(例外は、ClamAV の署名更新が使う clamav.net 宛ての TXT だけです。第三者がそのドメインの下にサブドメインを作れないので、数えません)。バーストカウンタは apex domain 単位にキー分離済みで、宛先を区別しない単一のグローバルカウンタによる誤検知は解消しています(60 秒のスライディングウィンドウと 10 件の閾値という判定ロジック自体は、この対応では変更していません。再通知の間隔は、その後、バーストが続く間は 300 秒から最大 1800 秒まで延ばす形に変えました)。iodine・dnscat2 などの実際の悪性トラフィックは単一の攻撃者管理ドメイン配下でラベル・qtype をローテーションするだけなので、apex domain 単位に絞っても検知力は低下しません。
SSH 総当たりの検知と遮断(ssh_guard)
ssh_guard_enabled(既定 true)で働く、RoamSwitch 自身のブルートフォース防御です。sshd のログを journald から journalctl -f で流し読みして(journald が無い環境だけ auth.log/secure を追尾します)、接続元ごとに失敗を数え、閾値(既定は 600 秒に 6 回)に達した接続元を、inet roamswitch_sshguard テーブルで SSH のポートへの新規接続(ct state new)だけ落とします。禁止と同時に、その接続元が SSH にすでに張っていた接続も切ります(接続の追跡エントリ(conntrack)を消し、応答方向のパケットを落とす規則を足して切ります)。禁止の対象は、認証済みのセッションも、公開鍵での成功の記録も、免除も持たない接続元だけで、切る直前にもう一度確かめます。したがって、認証済みのセッションと免除の接続元は切りません。禁止は 1 時間から始まり、再犯のたびに 4 倍、上限は 7 日で、期限は nftables のタイムアウトが処理します。Air-Gap(隔離)は起動せず、外部への通信もしません(journalctl・nft・loginctl などをローカルで実行するだけです)。fail2ban(sshd の jail が有効)か CrowdSec(firewall bouncer が稼働)がすでに sshd を守っているときは、遮断を譲って検知と通知だけを行います。
ログの行には、攻撃者が決められる文字列(ユーザー名)が入ります。そのため、接続元のアドレスは行の末尾の from <IP> port <N> からだけ読み、ユーザー名に偽の接続元を入れても第三者は禁止されません。journald では、送信元の資格情報(_UID、_COMM)が root の sshd のものである行だけを読み、logger -t sshd で書かれた行は読みません。ファイル(auth.log)の追尾には送信元を保証する欄がなく、syslog を使えるローカルユーザーが行を偽造できるため、既定にはしていません。運用者を締め出さないために、ゲートウェイ・DNS・自分自身・ループバック・whitelist_ips・ssh_guard_allow・認証済みの SSH セッションの接続元・過去 30 日に公開鍵認証で成功した接続元・ペアリング済みの Sensor は禁止しません。プライベートアドレスは免除しません。公開鍵の失敗(Failed publickey)は数えません。
実測(Ubuntu 24.04 の VM。本物の journald と systemd): 6 回目の失敗から禁止まで約 0.2 秒(0.17〜0.24 秒)でした。運用者の接続元は禁止されず、ユーザー名に偽の接続元を入れた試行でも、第三者は禁止されませんでした。本物の hydra(4 並列)は、6 回の失敗で止まりました。禁止がサーバーの再起動後に残り時間つきで復元されること、解除できること、fail2ban が sshd を守っているときは検知と通知だけになることも確かめています。この数値はこの VM の環境のものです。接続の切断を足したことの実測では、禁止のあとに通った失敗は、足す前が 16〜24 件、足したあとが 0〜4 件でした。接続の追跡エントリを消すだけでは接続は切れず、応答方向のパケットを落とす規則を足して初めて効きました。
Web アプリ層の検知ルール(Web シェル・注入コード)
Server Edition には、書き込みを見て検査する経路がなく、clamav_scan_dirs の定期スキャンだけでした。Web サーバー(Apache・nginx・Caddy)の設定から見つけたドキュメントルートを、通知専用の fanotify(FAN_CLASS_NOTIF。ほかのプロセスを止めることがない)で見張り、PHP・JavaScript・JSP・ASP などのファイルが書き込まれて閉じられた直後に、Web アプリ層の検知ルール(Web シェル、注入された難読化コード、画像に偽装した PHP、.htaccess の悪用)で検査します。取りこぼしと、起動前からあるファイルのために、6 時間ごとの定期スキャンも行います。ルールは、製品に同梱のもの、日次アップデータが署名検証のうえ設置するもの、/etc/roamswitch/rules.d/*.json の独自ルールの 3 層で、実際の検体から抜き出した署名は含まず、公開されている手口だけを記述した 18 本です。
Server Edition では、自動隔離は既定で critical だけです。high は通知と検知後フックだけで、運用中のサイトのファイルには触れません。通知(Telegram・LINE・Webhook)は 10 言語で、検知ごとにルール名と、隔離したか残したかを示します。ルールの取得は日次アップデータと同じ 1 回の取得にまとまり、新しい通信先は増えません。cve_kernel_map_updates_enabled を無効にすると通信は一切行われず、組み込みのルールで動作します。実機の fanotify で、書き込まれた Web シェルが 2 秒以内に隔離され、無害なファイルは残ることを確かめました。
検知後は、/etc/roamswitch/hooks/post-detection に実行ファイルを置くと、検知の内容(JSON)が標準入力で渡されます(root 所有・書き込み不可・親ディレクトリも安全なものだけを実行し、シェルを通さず、環境変数は最小限で、30 秒で打ち切る)。オプションのアカウント対処(既定オフ)は、隔離したファイルの持ち主のログインシェルを無効にします。root、UID が account_min_uid(既定 1000)未満のアカウント、ログイン中のユーザー、デーモン自身のユーザーには何もせず、元のシェルを記録して roamswitch web-rules unlock-account で戻せます。誤検知の実測は、WordPress 本体 2,409 ファイル、phpMyAdmin 3,356 ファイル、npm の依存パッケージ 14,743 ファイルで 0 件でした。限界として、読むのは先頭 256 KB(書き込み・実行時)と 4 MB(スキャン)だけで、変数名の組み立て('ev'.'al')のような分割は追えず、既知マルウェアの個別署名は、実際の検体がないため同梱していません。
ハニートークン(認証情報デコイ)
デーモン起動時に、攻撃者や自動化された認証情報窃取ツールが実際に走査する既知の実パス(/root/.aws/credentials・/root/.ssh/id_rsa・/root/.docker/config.json・/root/.pypirc・/root/.env.backup。デーモンは root で動作するため /root 配下)へ、ROAMSWITCH_HONEYTOKEN マーカーを含む偽の認証情報ファイルを冪等に配置します(既定で有効。honeytokens_enabled で止められ、配置先は honeytokens_home で変えられます)。当該パスに実在する本物のファイルは絶対に上書きしません(配置前に必ず存在確認)。ハニートークンへの実アクセスは、上限付きで関連する追加デコイ(AWS の追加プロファイル、SSH の config、kubeconfig 等)をカスケード配置するきっかけにもなります。1.10.4 までの配布パッケージには、この機能の実行に必要な roamswitch-honeytokens が同梱されておらず、パッケージから入れた環境では動作していませんでした。1.10.5 から同梱され、サーバー版で systemd のサンドボックスが /root への書き込みを止めていた問題も 1.10.5 で直りました。1.11.1 から、RoamSwitch 自身が実行する docker のコマンドは、空の専用ディレクトリを DOCKER_CONFIG に指定して起動するため、おとりの /root/.docker/config.json には反応しません。ほかのプログラムが読めば、これまでどおり検知します。
バックグラウンドの監視プロセス(roamswitch-honeytokens)が、通知クラスの fanotify(何も止めません)で各デコイの読み取り・オープンを監視します。イベントがアクセス元の PID を運ぶので、その場でアクセス元の実行ファイルを特定できます(fanotify が使えない環境では、inotify と fuser に切り替えます)。正規のプロセスがこれらのファイルを読む理由は本来無いため、RoamSwitch 自身のバイナリと、SSH のデコイに対する OpenSSH のクライアント(ssh・scp・sftp・ssh-add・ssh-agent など。ssh-keygen は含みません)を除くアクセスを、侵入者による侵害後の偵察(MITRE ATT&CK T1552, Unsecured Credentials)の強いシグナルとして扱い、通知します。アクセス元の実行ファイルを特定できないアクセスも、許可せずに検知として扱います。通知専用で、アクセス元プロセスの凍結や遮断などの追加対処は行いません。
既知の限界。/root/.ssh/id_rsa デコイの本体は可読テキストを含み、正規の OpenSSH 秘密鍵の base64 本体としては無効な形式のままです。ただし多くの秘密情報スキャナは -----BEGIN ... PRIVATE KEY----- ヘッダの正規表現一致のみでフラグを立てるため、実運用上の検知率への影響は限定的と判断しています。また、fanotify が使えず inotify に切り替わった環境では、アクセス元プロセスの特定(fuser 呼び出し)が、読み取り後ただちに終了する非常に短命なプロセスに対して PID・プロセス名を取得できないことがあります。その場合は不明として扱うのみで、アクセス自体の検知(イベント発火・通知)には影響しません。
証拠保全バンドル(フォレンジック自動採取)
証拠採取は、次の場合に起動します。eBPF ランタイム脅威センサーのイベントで、該当プロセスのper-process のネットワーク隔離に成功したとき、その隔離に失敗してホスト全体の Air-Gap に昇格したとき、ホスト全体を遮断する Air-Gap を実行したときです。ランサムウェアカナリアが改ざんを検知して、プロセスの凍結とホスト隔離を実行する経路でも起動します。ホスト全体を遮断する経路では、遮断の前に起動し、遮断を待たせません。1.10.4 までは、per-process の隔離に成功した場合だけで、1.10.5 で広げました。対象プロセスの PID が分からない場合、/proc のスナップショットは採取しません。roamswitch-incident-capture が起動し、証拠バンドルを <状態ディレクトリ>/incident-evidence/<タイムスタンプ>-<短縮ID>/ に保存します。採取するのは、既定のルーティングインターフェース上で tshark による境界付き 30 秒間のパケットキャプチャ(tshark 未導入時はスキップとして記録するのみ)と、対象プロセスの /proc/<pid> スナップショット(status/cmdline/maps、機密情報らしき値を [REDACTED] に置換した environ、開いているファイルディスクリプタのリンク先一覧(中身は取得しません)、祖先プロセスの系譜)です。個々のアーティファクトの採取失敗はバンドル全体を止めず、その旨を記録して残りの採取を続けます。1.10.4 までの配布パッケージには、この機能の実行に必要な roamswitch-incident-capture が同梱されておらず、パッケージから入れた環境では動作していませんでした。1.10.5 から同梱されています。
採取結果は manifest.json にまとめ、バンドル内の全アーティファクトの SHA-256 ハッシュを記録します(事後の改ざん検知のため)。バンドルは自動削除されません。sudo roamswitch-incident-capture prune [--keep-days N](既定の保持期間 30 日)を運用者が明示的に実行した場合にのみ古いバンドルが削除されます。任意のタイミングで手動採取するための sudo roamswitch-incident-capture snapshot [--pid PID] [--reason TEXT] [--json] コマンドも提供します。
採取結果はその場では検証されません。事後に改ざんの有無を確認するには sudo roamswitch-incident-capture verify <バンドルディレクトリ> [--json] を実行します。バンドル内の現在のファイルを再ハッシュして manifest.json の記録値と突き合わせ、変更あり・欠落・未記録(manifest に無いファイルの混入)のいずれかを個別に報告します。
既知の限界。この検証は、manifest.json の SHA-256 を、バンドルの外にある記録と照合します。記録は 2 か所です。バンドルの外の記録の鎖(evidence_anchor.log。各行が直前の行に連なり、追記専用の属性を付けます)と、システムログ(タグ roamswitch-evidence)です。取得後に manifest.json を書き換えると、verify が失敗します。記録の行を書き換えたり削除したりしても、鎖が途切れて失敗します。ただし、root 権限を持つ攻撃者が、鎖とシステムログの転送先の両方を書き換えられる場合は、防げません。システムログを別の機器へ転送する運用を推奨します。署名は実装していません。この記録がない(古い版で取得した)バンドルは、『記録なし』と表示し、外部の裏付けがないことを示します。
§534項目 総合セキュリティ診断スイート
Server Edition では、サーバ運用に特化した 34 項目の厳格なセキュリティ監査を即時実行できます。
| # | カテゴリ | 診断項目名 (日/英) | 防護基準 & 検査内容 |
|---|---|---|---|
| 1 | 認証・アクセス | SSH rootログイン・認証設定 (SSH Root Login & Password Auth) | PermitRootLogin no の検証。パスワード認証も無効なら公開鍵認証のみとして報告し、有効でも root ログインが禁止されていれば合格とします |
| 2 | 認証・アクセス | 空パスワードアカウント不在 (Empty Password Accounts) | /etc/shadow 内の空パスワードアカウントの不在検証 |
| 3 | 認証・アクセス | sudo権限・NOPASSWD監査 (Sudoers Privilege Hygiene) | /etc/sudoers と /etc/sudoers.d に、パスワードなしで全権限を与える NOPASSWD: ALL が無いかの監査 |
| 4 | ネットワーク | インバウンド既定拒否 (Inbound Default Deny) | nftables による未許可ポート既定ドロップの確認 |
| 5 | 整合性・カーネル | 非特権User Namespace (CVE-2026-53362対策) (Unprivileged User Namespaces) | user.max_user_namespaces=0(または kernel.unprivileged_userns_clone=0)による LPE 攻撃経路の遮断 |
| 6 | 整合性・カーネル | eBPF BTF サポート (eBPF BTF Support) | /sys/kernel/btf/vmlinux による Modern eBPF 稼働環境検証 |
| 7 | 整合性・カーネル | eBPF LSM 有効化 (eBPF LSM Active) | カーネル起動引数 lsm=...bpf の有効性確認 |
| 8 | ネットワーク | XDP起動時ゲート (XDP Boot-Time Gate) | Link Guard の nftables テーブルが設置される前の起動直後の空白を、XDP の既定拒否ゲートで埋めます(実験的・既定は未使用のオプトイン)。pin 済みリンクの有無を確認し、未使用でも不合格にはしません |
| 9 | 整合性・カーネル | eBPF ランタイムエージェント (eBPF Runtime Security Agent) | Falco / Tetragon 等のエージェントの稼働状態の確認。Falco が動いているときは、RoamSwitch 自身のルールの警報が戻ってくるか(ハートビート)も確認し、戻らなければ不合格(RoamSwitch の Falco ファイルが足りなければ、そのファイル名も示す) |
| 10 | 整合性・カーネル | 統合インシデントタイムライン (Unified Incident Timeline) | Link Guard・カナリア・eBPF Guard・Resource Guard のインシデントを時系列に統合するタイムラインが記録可能な状態(記録済みイベントの存在、または保存先への書き込み可否)かを確認し、直近の遮断所要時間を表示します |
| 11 | コンテナ | Dockerソケットマウント露出監査 (Docker Socket Mount Audit) | コンテナへの /var/run/docker.sock マウントの不在検証 |
| 12 | コンテナ | Docker 特権コンテナ監査 (Docker Privileged Containers) | --privileged フラグ付き高リスクコンテナの不在検証 |
| 13 | コンテナ | コンテナランタイム隔離レベル (runc/gVisor/Kata) (Container Runtime Isolation) | 既定のコンテナランタイムを確認します。runc 系(ホストカーネルを共有)は不合格、gVisor(runsc)または Kata Containers(VM 単位の隔離)を検出すると合格です。Docker / containerd 未検出の場合は該当なしとして扱います |
| 14 | コンテナ | Dockerファイアウォール迂回対策 (DOCKER-USER) (Docker Firewall Bypass Protection) | 公開コンテナポートが Docker 自身のルールでファイアウォールの既定拒否を迂回しないよう、必ず最初に評価される DOCKER-USER チェーン(または RoamSwitch 専用の Docker ガード nftables テーブル)に RoamSwitch のルールが実在するかを検証します。server.conf の protect_docker_ports が有効な場合のみ評価します |
| 15 | 整合性・カーネル | ClamAV 検知の実動作テスト (EICAR) (ClamAV Live Detection Test) | デーモンが起動 2 分後と 6 時間ごとに、自身の実行環境(systemd サンドボックス内など)から無害な EICAR テストファイルをスキャンし、ClamAV が実際に検知できることを確認します。server.conf の clamav_enabled が有効な場合のみ評価します |
| 16 | 整合性・カーネル | fanotify イベントの取りこぼし (キュー溢れ) (fanotify Missed Events / Queue Overflow) | fanotify のイベントキューが溢れてカーネルが破棄した、FIM・コンテナ実行監視のイベントの有無を検査します。溢れている間のファイル書き込みや実行はリアルタイム監視から漏れるため、頻発する場合は fs.fanotify.max_queued_events を引き上げます |
| 17 | 整合性・カーネル | 稼働中カーネルの既知CVE露出 (Known Kernel CVE Exposure) | 稼働中カーネルのバージョンを、ローカルの既知 CVE マップ(同梱ベースライン、または署名を検証した更新)と照合します。検査自体は外部へ通信せず、マップの更新取得は cve_kernel_map_updates_enabled(既定 true、無効にできます)で行います。確定できない場合は不合格ではなく「判定不能」として扱います |
| 18 | 整合性 | クリティカルパス改ざん監視 (FIM) (Critical Path File Integrity) | 固定 23 か所のクリティカルなファイルと、12 ディレクトリ直下の全ファイルなどの SHA-256 ハッシュ整合性検証 |
| 19 | 整合性・カーネル | Yama LSM プロセス保護 (Yama Process Protection) | kernel.yama.ptrace_scope が 2 または 3 であることによるメモリ盗聴防止 |
| 20 | ネットワーク | IPフォワーディング無効化 (IP Packet Forwarding Disabled) | net.ipv4.ip_forward = 0 による中継ルーター化防止 |
| 21 | ネットワーク | TCP SYN Cookie 有効化 (TCP SYN Cookie Protection) | net.ipv4.tcp_syncookies = 1 による DoS 耐性の確保 |
| 22 | ネットワーク | IPスプーフィング防止 (rp_filter) (Reverse Path Filtering) | net.ipv4.conf.all.rp_filter が 1(厳格)または 2(緩和。マルチホーム構成向け)であることによる偽装パケット破棄 |
| 23 | 整合性・カーネル | コアダンプ無効化 (Core Dump Restriction) | fs.suid_dumpable = 0 によるクラッシュ時の機密漏洩防止 |
| 24 | 運用・パッチ | 自動セキュリティ更新 (Unattended) (Automatic Security Updates) | unattended-upgrades(Debian / Ubuntu)や dnf-automatic(RHEL / Fedora)などによるパッチ自動適用設定の確認 |
| 25 | 運用・パッチ | NTP 時刻同期の正常性 (NTP Time Synchronization) | chrony 等による正確な監査ログ・証明書整合性の確保 |
| 26 | 運用・パッチ | SSH ブルートフォース防御 (SSH Brute-force Protection) | RoamSwitch 自身の SSH ガード(ssh_guard)が動いていて禁止する設定のとき、または fail2ban / crowdsec の常時稼働確認 |
| 27 | 整合性・カーネル | 非特権eBPF実行の禁止 (Unprivileged eBPF Disabled) | kernel.unprivileged_bpf_disabled が 1 または 2 であることによる Spectre/LPE 防止 |
| 28 | ネットワーク | ICMPリダイレクト無視 (ICMP Redirects Ignored) | accept_redirects = 0 による中間者経路偽装の防止 |
| 29 | ネットワーク | ブロードキャストPing拒否 (ICMP Echo Broadcasts Ignored) | icmp_echo_ignore_broadcasts = 1 による Smurf DoS 踏み台化防止 |
| 30 | 整合性・カーネル | 共有メモリマウント保護 (Shared Memory Hardening) | /dev/shm に nodev・nosuid・noexec のいずれかのマウントオプションが付いていることの確認 |
| 31 | 認証・アクセス | レガシー非暗号化サービス不在 (Legacy Insecure Protocols Absent) | telnetd、rshd、tftpd などの平文の待受サービスと、telnet、rsh、tftp などの平文のクライアントが入っていないこと(サービスとクライアントは分けて表示) |
| 32 | 整合性・カーネル | SUIDコアダンプ制限 (SUID Process Core Dumps Restricted) | SUID プロセスクラッシュ時のメモリダンプ生成阻止 |
| 33 | 整合性・カーネル | 補助プログラム (おとり・証拠採取) の導入 (Helper Programs Installed) | デーモンは、認証情報のおとり(ハニートークン)と、証拠バンドルの実行ファイルを、名前で起動します。実行ファイルが無いと、黙って何もしません。この2つが導入されているかを確かめます。 |
| 34 | ネットワーク | Web公開ディレクトリの機密ファイル露出 (Sensitive Files Exposed on the Web Server) | Apache / nginx / Caddy の設定から文書ルートを割り出し、そこにある .git・.env・.DS_Store・phpinfo・SQL ダンプ・バックアップ・秘密鍵が、Web サーバーから実際に読めるかを、サーバー自身への要求で確かめます。ディスク上のファイルの先頭と応答が一致したときだけ露出と判定するので、どのパスにも 200 を返すサイトや、すでに 403 にしているファイルは誤検知になりません。ファイルの中身は報告せず、パスだけを示します。Web サーバーが無い環境では対象外で、確認できなかった場合は「判定不能」として扱い、減点しません。 |
34 項目それぞれに、該当する場合は NIST CSF 2.0 のファンクション/カテゴリ識別子(例えば PR.DS-01、DE.CM-09)を付与し、確度の高い対応が取れる項目に限り CIS Controls v8 のコントロール番号(例えば 10.1、4.1)も併記します。対応が曖昧な項目には無理に付与せず、未マッピングのままにします。この対応表は CLI(roamswitch server status)と MCP(get_security_report)の出力の両方に含まれ、コンプライアンス対応が必要な運用者・監査担当者が、各チェック項目を既存フレームワークへ機械的に対応付けられるようにしています。
§6セルフチェック(Docker 環境での再現検証)
公式 APT リポジトリから導入した roamswitch-server のパッケージに対し、Docker 上の別コンテナを外部攻撃者として内部・外部からの攻撃を模したセルフチェックを実施します。2026-09-07 に v1.3.2 で行った実測は、全 18 項目 PASS でした。2026-09-25 には、1.10 系のソースからビルドしたパッケージ(arm64、Docker Desktop)で再実施し、19 のチェックが PASS でした。再実施では、製品側の不具合が 6 件見つかり、いずれも修正しました(1.10.5 に含まれます)。内訳は、パッケージへの補助実行ファイルの未同梱、FIM がハニートークンを新規ファイルとして報告する不具合、確認応答(ack)が無視される不具合、隔離中に管理用 SSH の応答が落とされて管理者が締め出される不具合、preserve_ssh_on_isolation=false でもポート 22 が開いていた不具合、systemd ユニットのサンドボックスがおとりの書き込みを止めていた不具合です。この検証はご自身のマシンでも再現できます。手順・スクリプトは下記「ご自身での再現方法」を参照してください。
| # | シナリオ | 攻撃手法 | 確認結果 |
|---|---|---|---|
| PENT-S1 | Nmap SYN スキャン | ポート 22, 80, 443, 3306, 8080 へのステルス探索 | nftables Default Drop により未許可の 3306 (MySQL) や 8080 は一切応答せず破棄(Filtered)。100% 遮断 |
| PENT-S2 | 未許可ポート接続 | curl によるポート 3306 / 8080 への直接アクセス | インバウンド既定拒否により許可ポート 80 のみ正常応答、他はタイムアウト遮断 |
| PENT-S3 | Egress / C2 遮断リスト | 悪性IPフィード登録済みアドレスへの outbound 接続試行 | egress_ip_blocklist_enabled(既定 true、nftables)により登録アドレスのみ遮断。非登録ホストへの通信は影響なし(一律遮断ではない) |
| PENT-S4 | FIM 改ざん攻撃 | /bin/login へのバックドアデータ注入 |
SHA-256 Critical Path FIM が改ざんを即時検知。正規復元後はベースラインが正しく再同期 |
| PENT-S5 | guard.yaml 重大度別封じ込め |
実在するプロセスを参照する Falco Critical イベントを注入 | on_critical: isolate_process + kill_process により該当プロセスをネットワーク隔離した上で SIGKILL |
| PENT-S6 | 保護プロセス安全機構 | 実在する sshd の PID を参照する同種イベントを注入 |
保護プロセスリスト(sshd/systemd/コンテナランタイム等)により sshd は継続稼働。誤検知として拒否ログのみ記録 |
| PENT-S7 | セーフティタイマー | Air-Gap 発動後、管理者確認応答(ACK)を送らず放置 | safety_timer_secs により無応答時は自動復帰。ACK 済みなら emergency-restore まで隔離継続 |
| PENT-S8 | カーネル LPE 悪用 | 一般ユーザー権限での unshare -U -r 実行(実機の VM で確認) |
製品なしでは許可される unshare -U -r が、起動後は拒否される。user.max_user_namespaces と kernel.unprivileged_userns_clone は 0、Yama の ptrace_scope は 2、非特権 eBPF は無効(2026-09-25、Ubuntu 24.04 の VM、v1.10.4 の公開版と修正版の両方で確認) |
注記: PENT-S7 の実測値は v1.3.2 時点のものでした。現行版では、高確度のトリガーの隔離は、原因の消滅を確認できたときに解除され、確認応答(ack)した隔離は運用者が解除するまで維持されます(ack が無視される不具合は、2026-09-25 の再実施で見つけて修正しました)。低確度のトリガーは、同じタイマーで復旧します。詳細は §4 です。
検証の種類 — PENT-S8 は、カーネルの機能(ユーザー名前空間の禁止)が効いていることの確認です。PENT-S1・S2 は、RoamSwitch が設定した nftables のルールの確認で、実際にパケットを破棄するのはカーネルです。RoamSwitch 独自のロジックを試した項目は、PENT-S3(フィードの適用)、S4(FIM)、S5・S6(封じ込めと保護プロセス)、S7(セーフティタイマー)です。
SSH ロックアウト防止機構
preserve_ssh_on_isolation=true(既定)は、Air-Gap(on_emergency)の発動中も管理用の SSH を残す設計です。ただし、2026-09-25 の再実施で、1.10.4 までは、隔離用ルールが SSH の応答を落としており、隔離中は管理者が締め出されることが分かりました。従来の確認は、隔離が解除された後に成功していただけでした。1.10.5 で修正し、隔離の原因が存続している条件で、隔離中の SSH が通ることを確認しています。
検証範囲の限界(正直な開示)
カーネル堅牢化(非特権 User Namespace の無効化、Yama ptrace_scope の引き上げ)は、コンテナ内から end-to-end で検証することができません。これらの sysctl はコンテナ単位の名前空間を持たないため、特権コンテナ(--privileged)からの書き込みは検証用のサンドボックスではなく検証を実行している実machine本体に影響してしまいます。そのため本検証はこの sysctl 書き込みが(読み取り専用ファイルシステムに対して)クラッシュせず正しく劣化動作することのみを確認しており、実際に値が反映されることの確認は、本製品が本来動作する対象である実machineまたはVM上で行う必要があります。この確認は、2026-09-25 に Ubuntu 24.04 の VM で 1 回だけ行いました(PENT-S8 の行を参照)。
ご自身での再現方法
上記の検証はすべて、公式サポートリポジトリ(roamswitch-support)で公開されている Docker テストスイートにより、公式配布パッケージだけを用いてお手元で 100% 再現できます(ソースコードへのアクセスや、弊社を信用していただく必要はありません)。
# 検証スイートの取得と再現実行(公式 APT リポジトリから roamswitch-server を自動導入)
git clone https://github.com/lafine1211/roamswitch-support.git
cd roamswitch-support/test/docker/server
./run.sh
Target・Attacker・C2 デコイの計 3 つの使い捨てコンテナを Docker の既定ブリッジネットワーク上に構築し、18 項目を自動検証してレポートを出力します。v1.3.2 での実行結果の全文は RESULTS-PENTEST-SERVER-2026-09-07.ja.md で公開しています(構築時に発見・修正したテストハーネス側の問題も透明性のため記載)。2026-09-25 の再実施の記録は RESULTS-PENTEST-2026-09-25.ja.md にあります。
§7運用・保守・ライフサイクル
CUI コマンド & セットアップウィザード
ヘッドレス運用のため、すべての管理操作は CUI から完結します:
roamswitch server status: 34項目の診断レポートを出力(10 言語に対応し、実行するユーザーの設定または OS のロケールに従います)。roamswitch server setup: 対話型ウィザードによるポート設定、Telegram/LINE/Webhook 設定。roamswitch server config show / set <key> <val>: 自動化スクリプト・CI/CD 用のワンライナー設定。roamswitch server test-notify [all|telegram|line|webhook]: 通知疎通テストの実行。roamswitch server config set language <ja|en|zh-Hans|zh-Hant|ko|de|fr|es|it|pt|system>: デーモンの定期診断レポート(1 時間ごと)とプッシュ通知(Telegram/LINE/Webhook、テスト送信を含む)の言語(1 ホストにつき 1 言語)。systemは OS のロケールに従い、判別できないロケールはjaになります。roamswitch server statusの表示言語は、この設定ではなく実行するユーザーの設定か OS のロケールに従います。roamswitch server bans [--json]/sudo roamswitch server unban <IP|IPv6 の /64|--all>: SSH 総当たりガードの状態と、禁止の一覧・解除。MCP のget_ssh_guard_incidentsは読み取り専用で、解除はできません。
Server TUI(roamswitch server tui)
SSH 越し・80x24・キーボードのみで Server Edition の機能全体を扱えるターミナル UI です(roamswitch server tui、または roamswitch-server-tui)。ダッシュボード・防御・インシデント・通知と転送・Sensor・設定・サービス・ツール・ログ監査・情報とヘルプ・Fleet の 11 画面があります。設定エディタは server.conf と guard.yaml のキー定義(スキーマ)から生成され、型・範囲・列挙値を検証し、再起動の要否を表示し、保存前に差分を示し、タイムスタンプ付きバックアップとアトミック書き込みで保存します(トークン等の秘匿値は伏せ字・非エコー入力)。隔離の復旧・ack・FIM ベースライン更新・サービス停止など影響の大きい操作は効果を明記した確認画面を出し、特に影響の大きいものは yes の入力を求めます(型付き確認)。root でない場合は読み取り専用で、必要な sudo roamswitch ... コマンドを案内します。設定の再読み込み(reload)は無く、反映にはサービスの再起動が必要です。表示は 10 言語に対応します。
パッケージング & 配布
Debian / Ubuntu 向け .deb パッケージと、RHEL / Fedora 向けおよび openSUSE 向けの .rpm パッケージにより提供されます。
§8まとめ
RoamSwitch Server Edition は、クラウドおよびエンタープライズの Linux インフラストラクチャに対して、「ゼロ・テレメトリ(既定の外部通信は、無効にできる署名付きデータの受信だけ)」「インバウンド既定拒否の強固な境界防護」「クリティカルパス改ざんの即時検知」「カーネル特権昇格の予防的遮断」 を単一の統合パッケージで提供します。
サイバー攻撃者が狙う攻撃ベクトルの主な段階(初期偵察、侵入、特権昇格、永続化)に対して、多層の検知と自律的な封鎖(Air-Gap)を組み合わせる Linux サーバ向けのセキュリティスイートです。