導入と設定

3つのレベルと、決まる順番

信頼・標準保護・最大ロックダウンを、設定ファイルを書き換えて実際に切り替えます。登録するネットワークの決め方と、端末に兆候があるときの信頼の引き下げも確かめます。

  • レベル: 入門
  • 約30分
  • 2026-11-12

#3つのレベルと、決まる順番

接続先ネットワークに応じた保護レベルの切り替え は、つないでいるネットワークに合わせて、ファイアウォールの強さを切り替えます。段階は3つです。この講座はクライアント版(roamswitch パッケージ)のものです。Server Edition には、この切り替えがありません。

レベル設定の値受信で通すもの(この講座で見た inet roamswitch の表)
信頼openすべて
標準保護balancedこちらから始めた通信の返り、ループバック、ping などの ICMP
最大ロックダウンlockdownこちらから始めた通信の返り、ループバック、IPv6 の近隣探索だけ。ping も落とします

どの段階でも、送信は止めません。デーモンは、次の順でレベルを決めます(ソースで確認)。

  1. Air-Gap の最中は、切り替えません
  2. 手動の固定(manual_override)があれば、それに従います
  3. ゲートウェイの MAC アドレスが登録済み(trusted_networks)なら、登録したレベルにします
  4. ネットワークにつながっていなければ、最大ロックダウンにします
  5. 登録のないネットワークは、away_protection_level(既定 lockdown)にします

設定は、利用者の ~/.config/roamswitch/config.json に書きます。入れた直後は、このファイルもフォルダーもありません。デーモンはこのファイルの書き換えを見ていて、数秒で反映します。この講座では、画面を使わずに、このファイルとコマンドで操作します。

練習は、使い捨ての環境で

この講座の操作は、ファイアウォールと SSH の状態を変えます。この講座の VM は tart exec で操作するので、SSH が止まっても作業を続けられます。実機で試すときは、SSH 以外の入り口(手元の画面など)を先に用意してください。

#いまのレベルを確かめる

クライアント版を入れます。Server Edition とは同時に入りません。この講座の VM では、roamswitch-server を外してから入れました。

入れて、最初のレベルを読む

パッケージを入れ、ネットワークの設定を足す操作です(変更を伴う操作です)。apt のリポジトリは、Server Edition と同じものです。この VM では、推奨パッケージを入れずに試しました。

sudo apt install --no-install-recommends roamswitch
systemctl is-active roamswitch
実行結果
active

レベルの確かめ方は、3つあります。デーモンが書く状態のファイル、ファイアウォールの表、共有サービスの状態です。

sudo cat /run/roamswitch/state.json
sudo nft list table inet roamswitch
roamswitch sharing status
実行結果
{"active_level":"lockdown","air_gap":false,"fanotify_ready":true,"network_trusted":false,"usb_zero_trust":true}
table inet roamswitch {
	chain input {
		type filter hook input priority filter; policy drop;
		ct state established,related accept
		iif "lo" accept
		icmpv6 type { nd-neighbor-solicit, nd-neighbor-advert } ip6 hoplimit 255 accept
		icmpv6 type nd-router-advert ip6 hoplimit 255 ip6 saddr fe80::/10 accept
	}

	chain output {
		type filter hook output priority filter; policy accept;
	}
}
  🔗 共有サービスの自動制御
  設定: 有効
  現在停止中: ssh.socket

まだ何も登録していないので、active_level は lockdown です。air_gap は、Air-Gap の最中かを示します。共有サービスの自動停止と復元 が働き、SSH の待ち受け(ssh.socket)も止まっています。

外から届くかを見るには、「LAN の別の機器」が要ります。VM の中にネットワーク名前空間 lan を作り、10.99.0.2 から VM の 10.99.0.1 へ、ping と SSH のポートを試します。

sudo ip netns add lan
sudo ip link add veth-h type veth peer name veth-l
sudo ip link set veth-l netns lan
sudo ip addr add 10.99.0.1/24 dev veth-h
sudo ip link set veth-h up
sudo ip netns exec lan ip addr add 10.99.0.2/24 dev veth-l
sudo ip netns exec lan ip link set veth-l up
sudo ip netns exec lan ping -c 2 -W 1 10.99.0.1 | grep transmitted
sudo ip netns exec lan nc -z -w 2 10.99.0.1 22; echo "rc=$?"
実行結果
2 packets transmitted, 0 received, 100% packet loss, time 1026ms
rc=1

ping も SSH のポートも届きません。最大ロックダウンでは、外から始まる通信をすべて落とします。

講座を終えたら、sudo ip netns del lan で名前空間を消します。対になる veth-h も、いっしょに消えます。

#自宅のネットワークを登録する

登録の目印は、ゲートウェイ(ルーター)の MAC アドレスです。ネットワークの名前ではありません。

ゲートウェイを「信頼」で登録する

設定ファイルを書き換える操作です(変更を伴う操作です)。元に戻すには、trusted_networks の項目を消して保存します。

ゲートウェイのアドレスと MAC アドレスを調べます。

ip route show default
ip neigh show 192.168.64.1
実行結果
default via 192.168.64.1 dev enp0s1 proto dhcp src 192.168.64.17 metric 100 
192.168.64.1 dev enp0s1 lladdr d2:c0:50:cd:95:66 PERMANENT 

PERMANENT は、RoamSwitch がゲートウェイの ARP を固定している印です。信頼以外のレベルでは、なりすましを防ぐために固定します。この MAC アドレスを、trusted_networks に書きます。

mkdir -p ~/.config/roamswitch
cat > ~/.config/roamswitch/config.json <<'J'
{
  "language": "ja",
  "trusted_networks": [
    {"name": "lab-home", "mac": "d2:c0:50:cd:95:66", "level": "open"}
  ]
}
J
sleep 8; sudo cat /run/roamswitch/state.json
実行結果
{"active_level":"open","air_gap":false,"fanotify_ready":true,"network_trusted":true,"usb_zero_trust":false}

保存してから数秒で、active_level が open になりました。デーモンの再起動は要りません。この VM では、書き換えから約 3 秒で切り替わりました。受信の表と SSH も見ます。

sudo nft list table inet roamswitch
systemctl is-active ssh.socket
sudo ip netns exec lan ping -c 2 -W 1 10.99.0.1 | grep transmitted
sudo ip netns exec lan nc -z -w 2 10.99.0.1 22
roamswitch notifications | sed -n 5,11p
出力の抜粋
table inet roamswitch {
	chain input {
		type filter hook input priority filter; policy accept;
	}

	chain output {
		type filter hook output priority filter; policy accept;
	}
}
active
2 packets transmitted, 2 received, 0% packet loss, time 1049ms
Connection to 10.99.0.1 22 port [tcp/ssh] succeeded!
  2026-11-12T03:10:12.399771910+00:00
  🛡️ RoamSwitch 保護プロファイル切替
    保護レベルを【信頼ネットワーク (オープン)】に変更しました。理由: 登録済みネットワーク「lab-home」の設定

  2026-11-12T03:10:12.378346718+00:00
  🔓 共有サービスを復元しました
    信頼ネットワークに接続したため、停止していた共有サービスを復元しました。

表は、何も落とさない形です。止めていた SSH が戻り、LAN から SSH のポートに届きます。信頼のレベルでは、ゲートウェイの ARP の固定も外れます(ip neigh の表示が PERMANENT から REACHABLE などに変わります)。

`level` を書き忘れる

trusted_networks の項目に level がないと、open(信頼)として扱われます。この講座の VM で、最大ロックダウンの状態から level のない項目を足すと、active_level が open になりました。登録するときは、level を必ず書いてください。

どのネットワークを、どのレベルで登録するか
  • 信頼(open): 自分だけが管理するルーターの、自宅のネットワーク。LAN の機器から、この端末の SSH やファイル共有を使う場合
  • 標準保護(balanced): 職場のように、管理者はいても、ほかの人の機器が同じ LAN にいるネットワーク。ping には応え、外から始まる接続は落とします
  • 登録しない: カフェ、ホテル、空港の Wi-Fi。未登録のネットワークは、既定で最大ロックダウンになります

迷ったら、登録しないままにします。未登録の扱いは、away_protection_level で変えられます。この VM では、balanced にすると標準保護、open にすると信頼になりました。open にすると、どこでも守りが外れます。

#標準保護と、共有サービスの停止

標準保護は、ping には応えますが、外から始まる接続は落とします。共有サービスの止め方も見ます。

レベルを標準保護にして、共有サービスを確かめる

設定ファイルを書き換える操作です(変更を伴う操作です)。level を open に書き戻すと、元に戻ります。

sed -i 's/"level": "open"/"level": "balanced"/' ~/.config/roamswitch/config.json
sleep 8; sudo cat /run/roamswitch/state.json
sudo nft list table inet roamswitch | sed -n 2,9p
systemctl is-active ssh.socket
sudo ip netns exec lan ping -c 2 -W 1 10.99.0.1 | grep transmitted
sudo ip netns exec lan nc -z -w 2 10.99.0.1 22; echo "rc=$?"
実行結果
{"active_level":"balanced","air_gap":false,"fanotify_ready":true,"network_trusted":false,"usb_zero_trust":false}
	chain input {
		type filter hook input priority filter; policy drop;
		ct state established,related accept
		iif "lo" accept
		ip protocol icmp accept
		ip6 nexthdr ipv6-icmp accept
	}

inactive
2 packets transmitted, 2 received, 0% packet loss, time 1032ms
rc=1

ping は通り、SSH のポートには届きません。network_trusted は、登録したネットワークでも、レベルが open でないと false です。共有サービスの自動制御は、信頼以外のレベルで SSH を止めます。

自動制御を切ると、SSH は動いたままになります。それでも、標準保護のファイアウォールが、外からの接続を落とします。

sudo roamswitch sharing off
systemctl is-active ssh.socket
sudo ip netns exec lan nc -z -w 2 10.99.0.1 22; echo "rc=$?"
sudo roamswitch sharing on
実行結果
  ✓ 共有サービスの自動制御を無効にしました。停止中のサービスは復元されます。

active
rc=1
  ✓ 共有サービスの自動制御を有効にしました。
  ⚠️  信頼(オープン)以外のレベル(未登録のネットワーク、標準保護・最大ロックダウンで登録したネットワーク)では SSH / Samba / リモートデスクトップが停止します(稼働中の SSH セッションは切断されます)。

守りは2層です。共有サービスの停止と、ファイアウォールです。sharing off は、設定ファイルの sharing_service_control_enabled を false に書きます。SSH から sudo なしで実行すると、「設定の変更は、root またはこの端末のローカルなログインセッションを持つユーザーだけが行えます」と出て、設定は変わりません(終了コード 1)。sudo を付けるか、端末のローカルなログインから実行します。

SSH で入っている端末で、自動制御を有効にするときは

sharing on の表示のとおり、未信頼のネットワークに移ると、動いている SSH のセッションが切れます。SSH でしか入れない端末では、先に、いまのネットワークを登録してください。

#期間を決めて固定する、すぐに全部止める

自動の判定より強くしたいときは、手動で固定します。固定は、設定ファイルの manual_override です。

キー値
manual_overrideopen、balanced、lockdown。null で自動に戻す
manual_override_duration_modeuntil_disconnect(既定。ネットワークが変わるまで)、1hour、2hours、4hours、today、permanent
manual_override_started_at固定を始めた時刻(Unix 秒)。期間の計算に使います

値の一覧は、ソースで確かめました。この講座で動かしたのは 1hour です。

1時間だけ最大ロックダウンに固定する

設定ファイルを書き換える操作です(変更を伴う操作です)。期間が過ぎると、デーモンが自分で元に戻します。

python3 - <<'X'
import json, os, time
p = os.path.expanduser('~/.config/roamswitch/config.json')
c = json.load(open(p))
c['manual_override'] = 'lockdown'
c['manual_override_duration_mode'] = '1hour'
c['manual_override_started_at'] = int(time.time())
json.dump(c, open(p, 'w'), indent=2)
X
sleep 6; sudo cat /run/roamswitch/state.json
sudo ip netns exec lan ping -c 2 -W 1 10.99.0.1 | grep transmitted
実行結果
{"active_level":"lockdown","air_gap":false,"fanotify_ready":true,"network_trusted":false,"usb_zero_trust":true}
2 packets transmitted, 0 received, 100% packet loss, time 1020ms

固定も、保存してから数秒で効きます。通知の履歴には「保護レベルを【最大ロックダウン】に変更しました。理由: 手動で固定したため(manual_override)」が残ります。1時間待つ代わりに、開始時刻を1時間前に書き換えて、期限切れを起こします。

python3 - <<'X'
import json, os, time
p = os.path.expanduser('~/.config/roamswitch/config.json')
c = json.load(open(p))
c['manual_override_started_at'] = int(time.time()) - 3600
json.dump(c, open(p, 'w'), indent=2)
X
sleep 8; sudo cat /run/roamswitch/state.json
grep manual_override ~/.config/roamswitch/config.json
roamswitch notifications | sed -n 6,7p
実行結果
{"active_level":"open","air_gap":false,"fanotify_ready":true,"network_trusted":true,"usb_zero_trust":false}
  "manual_override": null,
  "manual_override_duration_mode": "1hour",
  "manual_override_started_at": null,
  🔄 手動オーバーライドを解除しました
    設定した期間が経過したため、ネットワーク自動判定モードに戻しました。

デーモンが manual_override を null に書き戻し、登録したレベル(信頼)に戻しました。通知も残ります。

固定を期限の前にやめるときは、manual_override と manual_override_started_at を手で null にします。この VM では、保存から約 3 秒で、登録したレベル(信頼)に戻りました。

レベルの固定より強い手段が、Air-Gap(緊急の全遮断) です。外への通信も含めて、すべてを止めます。

Air-Gap を発動して、解除する

すべての通信を止める操作です(変更を伴う操作です)。解除は sudo roamswitch airgap disable です。

sudo roamswitch airgap enable
sudo ip netns exec lan ping -c 2 -W 1 10.99.0.1 | grep transmitted
curl -s -o /dev/null -m 5 -w '%{http_code}\n' https://lafine.net/updates/v1/manifest; echo "rc=$?"
出力の抜粋
🚨 Air-Gap緊急遮断を発動中...
  ✓ すべての外部ネットワーク通信を緊急遮断しました。
  画面がないため、緊急復旧ウィンドウは表示していません。解除するには、この端末で roamswitch airgap disable を実行してください(遮断中は SSH でも入れません)。

2 packets transmitted, 0 received, 100% packet loss, time 1038ms
000
rc=28

外からの ping も、外への HTTPS も止まりました(rc=28 は curl の時間切れです)。画面のない VM なので、復旧の画面は出さず、この端末で解除するよう案内します。遮断の間は、state.json の air_gap が true になります。引数なしの roamswitch airgap は、状態と使い方を出すだけで、遮断しません。

sudo roamswitch airgap disable
sudo cat /run/roamswitch/state.json
curl -s -o /dev/null -m 5 -w '%{http_code}\n' https://lafine.net/updates/v1/manifest
実行結果
  ✓ Air-Gap緊急遮断を解除し、通常通信を復旧しました。

{"active_level":"open","air_gap":false,"fanotify_ready":true,"network_trusted":true,"usb_zero_trust":false}
200

解除すると、いまのネットワークに合うレベル(ここでは信頼)に戻ります。state.json は、解除のコマンドが答えを返す前に書き直されます。直後に読んでも、air_gap は false です。この VM で 3 回続けて試し、3 回とも false でした。

#端末に兆候があるときは、信頼を下げる

信頼は、ネットワークについての判断です。端末そのものが乗っ取られているかもしれないときに、LAN へ開いたままでは困ります。端末の状態による信頼の引き下げ は、重大な検知が直近にあるあいだ、信頼の登録を標準保護に下げます。

下げるきっかけは、Canary、eBPF Guard、Honeytoken、Exec Recorder、Persistence の、重大度 high 以上の検知です。期間は既定で 6 時間です(device_posture_window_hours、1〜72)。最大ロックダウンまでは下げません。ソースでは、30 秒ごとに見直します。

自動起動の登録で、信頼を下げる

無害なダミーの自動起動ファイルを置きます。中身の /tmp/rs-lab-dummy は存在せず、何も実行されません。最後にファイルを消し、確認を記録します(変更を伴う操作です)。

mkdir -p ~/.config/autostart
printf '[Desktop Entry]\nType=Application\nName=rs-lab\nExec=/tmp/rs-lab-dummy\n' > ~/.config/autostart/rs-lab.desktop
sleep 45
sudo cat /run/roamswitch/state.json
roamswitch posture
実行結果
{"active_level":"balanced","air_gap":false,"fanotify_ready":true,"network_trusted":false,"usb_zero_trust":false}
=====================================================
  信頼ネットワークと端末の状態
=====================================================

  device_posture_trust  : 有効
  window                : 6 時間
  兆候のある検知: Persistence
  いちばん新しい検知: 0分前
  新しい検知がなければ自動で戻るまで: あと5時間59分
  いま、信頼(オープン)で登録したネットワークを標準保護で動かしています。
  検知の内容を調べて問題がなければ: sudo roamswitch posture ack

通知の履歴には、検知と、レベルを下げたこと、共有サービスを止めたことが残ります。

roamswitch notifications | sed -n 5,16p
実行結果
  2026-11-12T03:12:24.982893314+00:00
  信頼ネットワークを標準保護にしました
    Persistence の検知が直近6時間以内にあります。信頼ネットワークでも開いたままにせず、ファイアウォールを有効にしました。調べて問題がなければ `sudo roamswitch posture ack` で信頼に戻せます。新しい検知がなければ、6時間後に自動で戻ります。

  2026-11-12T03:12:24.961007861+00:00
  🔒 共有サービスを停止しました
    この端末の検知で信頼ネットワークを標準保護に下げたため、SSH / ファイル共有 / リモートデスクトップを停止しました。信頼に戻ると再開します。

  2026-11-12T03:12:12.774327903+00:00
  🚨 不審な自動起動の登録を検知
    /home/admin/.config/autostart/rs-lab.desktop に、新しい(または変更された)自動起動の登録があります。実行内容: /tmp/rs-lab-dummy / 理由: 一時フォルダー・隠しフォルダーのプログラムを実行。心当たりがなければ、そのファイルを確認して無効にしてください。(通知のみで、何も止めていません)
    変更したプロセスは特定できません(ファイルの状態を定期的に比べて見つけたため)

最後の行のとおり、この検知はファイルを定期的に比べて見つけたものです。書いたプロセスは分かりません。

検知の原因を片づけてから、確認を記録します。

rm ~/.config/autostart/rs-lab.desktop
sudo roamswitch posture ack
sleep 8; sudo cat /run/roamswitch/state.json
実行結果
  ✓ 確認しました。数秒以内に、信頼ネットワークは登録したレベルに戻ります。
  確認より後に新しい検知があれば、また標準保護になります。
{"active_level":"open","air_gap":false,"fanotify_ready":true,"network_trusted":true,"usb_zero_trust":false}

信頼に戻りました。ファイルを消しただけでは、6 時間たつまで戻りません。ack は、調べて問題がないと判断したときだけ使います。

同じ VM で、ack のあとに同じ中身のファイルを置き直しました。もう一度検知され、約 45 秒後に、また標準保護に下がりました。消して置き直す形も、新しい兆候として数えます。

#見えない部分

確かめたこと・確かめていないこと

使い捨ての Ubuntu 24.04 の VM(有線、画面なし)で確かめた範囲は、次のとおりです。

  • Wi-Fi は試していません。VM は有線です。Wi-Fi の名前ではなく、ゲートウェイの MAC アドレスで見分けることは、ソースと、この VM の動きで確かめました
  • 画面(GUI)の操作は扱っていません。この講座は、設定ファイルとコマンドだけで操作しました
  • ネットワークの移動は起こしていません。登録は、config.json を書き換えて読ませました。until_disconnect の固定が移動で切れることは、ソースで確かめただけです
  • 信頼でも、検知は働きます。レベルが変えるのは、受信の表、共有サービス、ゲートウェイの ARP の固定などです。この講座の Persistence の検知は、信頼のときに起きました
  • 「LAN の機器」は、VM の中のネットワーク名前空間です。本物の別の機器からの通信も、同じ受信の表を通ります
確認問題

自宅のルーターを trusted_networks に書き足しましたが、level を書き忘れました。保存して数秒後の active_level はどれですか

解説を見る

level のない項目は、open(信頼)として扱われます。書いた覚えのないまま、LAN に開くことになります。登録するときは、level を必ず書きます。

確認問題

信頼で登録した自宅で、レベルが標準保護に下がり、roamswitch posture に「兆候のある検知: Persistence」と出ました。正しい対応はどれですか

解説を見る

下がったのは、端末が乗っ取られている兆候があるためです。調べずに ack すると、兆候があるまま LAN に開きます。ファイアウォールを有効にするだけで、隔離はしないので、調べる時間はあります。

#次に学ぶこと

扱う機能
確認した内容

2026-11-12に確認しました。対象は Linux 1.17.39 です。使い捨ての Ubuntu 24.04(arm64、有線、画面なし)の VM で roamswitch-server を外し、公開の apt からクライアント版 roamswitch 1.17.39 を入れ、VM の中のネットワーク名前空間を LAN の機器に見立てて、記載のコマンドと、airgap disable の直後の state.json(3 回)、自動起動の置き直し・level の書き忘れ・away_protection_level・SSH からの sharing on を日本語と英語で実行しました。

コマンドの出力は、製品の版や環境によって変わります。違いが出たら、まず製品の版を確かめてください。