攻撃を知り、防ぐ

攻撃者より先に、弱いところを見つける

総合診断の [合格]/[要対策]/[確認不能]/[該当なし] と点数の数え方を読み、設定が弱くなったときの通知、ドメインの点検、127.0.0.1 の実証型の診断と終了コード、doctor による RoamSwitch 自身の点検を、使い捨ての VM で動かします。

  • レベル: 基礎
  • 約40分
  • 2026-11-12

#攻撃者より先に、弱いところを見つける

サーバーに入ろうとする攻撃者は、まず弱いところを探します。パスワードで入れる SSH、外から届く開発用のサーバー、横取りできる名前解決などです。入ったあとは、自分が動きやすいように設定を緩めることもあります。

この講座では、同じ弱いところを、守る側が先に見つけます。扱う機能は次のとおりです。

機能見るもの動かし方
セキュリティ総合診断SSH、sudo、ファイアウォール、カーネルの保護設定などsudo roamswitch status --server。デーモンも1時間ごとに実行
設定が弱くなったときの通知前回は合格だった設定が、いま不合格になったか1時間ごとの診断のたびに自動
LLMNR・NBNS の応答の診断同じネットワークの誰でも答えられる名前解決に、参加していないか総合診断の1項目
自分のドメインの点検自分のドメインの SPF、DMARC、MTA-STS、有効期限ドメインを設定したときだけ
実証型の脆弱性診断127.0.0.1 で動く開発用サーバーの、CORS の設定ミスなどroamswitch scan-vulns --confirm。手で実行
RoamSwitch 自身の点検(doctor)RoamSwitch 自身のファイルが、入れたときのままかsudo roamswitch doctor。手で実行
練習は使い捨ての環境で

この講座の演習は、設定を弱め、DNS の向き先を変え、RoamSwitch のファイルを書き換えます。本番のサーバーではなく、使い捨ての VM で試してください。どの演習にも、元に戻す手順を書いています。出力の時刻は UTC です。日本時間は 9 時間足して読みます。

#総合診断を読む

総合診断は、何も変えずに、このサーバーの設定を項目ごとに調べます。結果は点数と、項目ごとの判定で出ます。

診断を実行して、合格でない項目だけを見る

root と、ふつうのユーザーで、1回ずつ実行します。

sudo roamswitch status --server | head -6
sudo roamswitch status --server | grep -E "\[(要対策|確認不能)\]"
roamswitch status --server | grep -E "\[(要対策|確認不能)\]"
出力の抜粋(root)
  スコア: 72/100 (B (注意))
  適合項目: 24/33

  [要対策] SSH rootログイン・認証設定 - ⚠️ 稼働中 (Root禁止・パスワード許可)
  [要対策] sudo権限・NOPASSWD監査 - ⚠️ パスワードなし全権限Sudo設定を検出
  [要対策] eBPF ランタイムセキュリティエージェント - eBPFエージェント未稼働
  [要対策] 稼働中カーネルの既知CVE露出 - 既知のカーネルCVEに該当する可能性: CVE-2026-31589 (CRITICAL), CVE-2026-43493 (CRITICAL), CVE-2026-43501 (CRITICAL), CVE-2026-45988 (CRITICAL), CVE-2026-46043 (CRITICAL), CVE-2026-46115 (CRITICAL), CVE-2026-46119 (CRITICAL), CVE-2026-46135 (CRITICAL), CVE-2026-46137 (CRITICAL), CVE-2026-46195 (CRITICAL), CVE-2026-46244 (CRITICAL), CVE-2026-46289 (CRITICAL), … (+505) (パッケージマネージャー上で該当カーネルの更新が保留中です。適用・再起動を推奨します)
  [要対策] 悪用される恐れの高い脆弱性 (入っているソフトウェア) - 悪用の恐れが高い脆弱性が2件あります (brotli CVE-2026-111497, openssh CVE-2026-123158)
  [要対策] /tmp・/dev/shm の noexec マウント - noexec なし: /tmp (専用のマウントではなく、/ と同じファイルシステム), /dev/shm
  [要対策] レガシー非暗号化サービス (Telnet/RSH) 不在 - 平文のクライアントを検出(接続を受けるサービスではありません): /usr/bin/telnet
  [要対策] バックアップの最新性 - バックアップの仕組みが見つかりません
  [要対策] 保護が実際に働いているか: 緊急通知の経路 - ⚠️ 働いていません
出力の抜粋(ふつうのユーザー。root のときと違う行)
  スコア: 76/100 (B (注意))
  適合項目: 23/30
  [確認不能] SSH rootログイン・認証設定 - 確認不能 (読み取れません。root権限が必要な可能性があります)
  [確認不能] sudo権限・NOPASSWD監査 - 確認不能 (読み取れません。root権限が必要な可能性があります)
  [確認不能] インバウンド既定拒否 (Default Drop) - 確認不能 (権限が足りません。読み取りには root 権限が必要です)

判定は4種類です。

  • 合格: 決まった形になっています
  • 要対策: 直す候補です。各項目の ↳ の行に、直し方が出ます
  • 確認不能: 調べられませんでした。安全という意味ではありません
  • 該当なし: このサーバーには関係のない項目です(Docker が入っていない、など)

点数と「適合項目」は、判定できた項目だけで数えます。該当なしと確認不能は、分母にも入りません。root では 33 項目を判定し、24 項目が合格でした。

ふつうのユーザーで実行すると、SSH の設定、/etc/sudoers.d、ファイアウォールの規則を読めず、確認不能が増えます。分母が 30 に減るので、点数は 76 に上がりました。点数が高いのは、読めなかったからです。診断は sudo を付けて実行します。

上の要対策は、どれも対応が別です。

  1. SSH: パスワードでのログインを許したままです。鍵で入れることを確かめてから、PasswordAuthentication no にします
  2. sudo: パスワードなしで何でも sudo できる設定です。この VM では、cloud-init が作った /etc/sudoers.d/90-cloud-init-users です
  3. eBPF エージェント: ↳ の直し方は、Falco か Tetragon を入れることです。コンテナの中の動きを見る仕組みです
  4. カーネルの既知 CVE: 動いているカーネルの版が、既知の CVE に当たる可能性があります。この VM では、新しいカーネルの更新が保留中でした。更新を入れて、OS を再起動します
  5. 悪用される恐れの高い脆弱性: 入っているパッケージ(brotli と openssh)に、悪用が確認された、または恐れの高い CVE が残っています。sudo roamswitch scan-packages で詳細を見て、更新します
  6. noexec: /tmp と /dev/shm で、置かれたプログラムをそのまま動かせます。インストーラーが壊れることがあるため、RoamSwitch は自動では付けません
  7. telnet: 接続を受けるサービスではなく、クライアントが入っているだけです。使わないなら、パッケージを消します
  8. バックアップ: 定期的に動くバックアップが見つかりません。ほかの方法で取っているなら、無視して構いません
  9. 緊急通知の経路: 通知の届け先がありません。設定の手順は、Linux Server の導入と、最初の設定 にあります

スクリプトで使うときは、sudo roamswitch status --server --json で同じ結果を JSON で受け取れます。判定した数は totalChecks、該当なしは notApplicableChecks、確認不能は inconclusiveChecks です。

合格でない項目を、どう扱うか
  1. 要対策で、このサーバーに要らないものは、↳ の直し方に従って直します
  2. 要対策でも、理由があって残すもの(ルーターとして使う、など)は、理由を運用の記録に残します
  3. 確認不能 は、調べられる条件をそろえます。sudo を付ける、足りないデータを入れる、などです。パッケージの脆弱性データは、更新の仕組み(roamswitch-updater)で入ります。その読み方は 悪用が確認された脆弱性に、修正版がない で扱います

名前解決の横取りに、参加していないか

名前が DNS で見つからないとき、LLMNR や NBNS は、同じネットワークの全員に問い合わせます。最初に答えた機器が勝つため、攻撃者は偽の答えを返して、接続を横取りできます。LLMNR / NBT-NS ポイズニングと呼ばれる手法です。

LLMNR・NBNS の応答の診断 は、この問い合わせに使うポート(LLMNR は UDP 5355、NBNS は UDP 137)で待ち受けているプロセスがあるかを調べます。読むのは /proc/net/udp と /proc/net/udp6 だけで、通信はしません。mDNS(UDP 5353)は一覧に出しますが、プリンターの検出などに使うため、不合格にはしません。

LLMNR を有効にして、診断を見る

変更を伴う操作です。systemd-resolved の設定に、ファイルを1つ足します。

resolvectl llmnr
sudo mkdir -p /etc/systemd/resolved.conf.d
printf '[Resolve]\nLLMNR=yes\n' | sudo tee /etc/systemd/resolved.conf.d/llmnr-lab.conf
sudo systemctl restart systemd-resolved
sudo ss -lunp "sport = :5355"
sudo roamswitch status --server | grep -A3 "LLMNR"
出力の抜粋
Global: no
Link 2 (enp0s1): no
[Resolve]
LLMNR=yes
State  Recv-Q Send-Q Local Address:Port Peer Address:PortProcess
UNCONN 0      0            0.0.0.0:5355      0.0.0.0:*    users:(("systemd-resolve",pid=3367,fd=11))
UNCONN 0      0               [::]:5355         [::]:*    users:(("systemd-resolve",pid=3367,fd=13))
  [要対策] LLMNR・NBNS・mDNS の応答 - ⚠️ 名前解決の応答を受け付けています: LLMNR (systemd-resolve)
         (説明の行は省略)
         ↳ 不要なら止めてください。 LLMNR: /etc/systemd/resolved.conf に LLMNR=no を書いて sudo systemctl restart systemd-resolved を実行します(NetworkManager の接続ごとの設定は nmcli connection modify <接続名> connection.llmnr no)。

この VM では、LLMNR は最初オフでした(最初の resolvectl llmnr の出力)。待ち受けを始めたのは systemd-resolve で、診断は、そのプロセスの名前まで示します。

元に戻します。

sudo rm /etc/systemd/resolved.conf.d/llmnr-lab.conf
sudo systemctl restart systemd-resolved
sudo roamswitch status --server | grep "LLMNR"
実行結果
  [合格] LLMNR・NBNS・mDNS の応答 - LLMNR・NBNS・mDNS の応答なし

#設定が弱くなったら、知らせる

診断を読むのは、開いた人だけです。誰かが設定を緩めても、次に誰かが開くまで気づきません。設定が弱くなったときの通知 は、前回の診断で合格だった設定が、いま不合格になったときに、1通で知らせます。

決まりは次のとおりです(ソースのコメントと、設定の既定値で確かめました)。

  • デーモンは、起動の 90 秒後に1回、その後は1時間ごとに診断します
  • 対象は、SSH、sudo、ファイアウォールの既定、カーネルの保護設定など、Server Edition では 27 項目です
  • 最初の診断は、記録するだけです。前回の記録がない項目は、変化として数えません
  • 同じ項目は、1日以内には繰り返しません
  • 設定のキーは config_regression_notice_enabled です。既定は true です。変えたら、デーモンの再起動が要ります
IP フォワーディングを有効にして、通知を受け取る

変更を伴う操作です。net.ipv4.ip_forward を 1 にすると、このサーバーがほかの機器の通信を中継するようになります。攻撃者が、奥のネットワークへの踏み台にするときに使う設定です。

演習では、1時間を待たずに診断を走らせるため、デーモンを再起動します。

sudo roamswitch server config get config_regression_notice_enabled
sudo sysctl -w net.ipv4.ip_forward=1
roamswitch status --server | grep -A2 "IPフォワーディング"
sudo roamswitch server restart
# 90 秒あまり待ってから
sudo roamswitch notifications | grep -A6 "セキュリティ設定が弱くなりました"
出力の抜粋
true
net.ipv4.ip_forward = 1
  [要対策] IPフォワーディング無効化 - 有効 (ルーター動作)
         ホストが意図せずルーターとして通信中継しないよう防護します。
         ↳ ルーターでない場合は net.ipv4.ip_forward = 0 を設定してください。
  ✓ roamswitch-server サービスを正常に再起動しました。

  セキュリティ設定が弱くなりました
    ⚠️ 前回の点検では問題がなかった設定が、いま問題ありになっています(1件)
    ホスト: ubuntu (IP: 192.168.64.3)
    - IPフォワーディング無効化: 有効 (ルーター動作)
    自分で変えたのでなければ、変えた人やプロセスを確認してください。詳細と直し方は `roamswitch status --server` で見られます。

通知は、通知の履歴に残り、設定した届け先(Webhook など)にも送られます。本文は、どの設定が、どう変わったかだけを書いています。記録したときと違う言語で読んでも、設定の行は値を訳して出ます(英語では - IP Packet Forwarding Disabled: Enabled (Router mode))。

元に戻します。sysctl -w の変更は一時的で、OS を再起動しても消えます。

sudo sysctl -w net.ipv4.ip_forward=0
実行結果
net.ipv4.ip_forward = 0
やってはいけないこと
  • 通知を止めるために、config_regression_notice_enabled を false にする。設定が緩んでも、誰も気づかなくなります
  • 「自分で変えた覚えがない」通知を、そのまま閉じる。変えた人やプロセスを、ログで確かめます

#自分のドメインを点検する

SPF と DMARC がないドメインは、メールの送信元として偽装されます。登録が切れたドメインは、ほかの人に取られ、そのドメインを信頼していたメールやログインまで奪われます。どちらも、サーバーの中を見ても分かりません。

自分のドメインの点検 は、設定 domain_check_domains に書いたドメイン(最大5つ)だけを調べます。既定は空で、そのときは何も問い合わせません。書いたときに外へ出るのは、次の3つだけです(診断の説明文とソースで確かめました)。

  1. dig が、/etc/resolv.conf のリゾルバーに、TXT・CAA・DS・MX を問い合わせます
  2. curl が、IANA の公開リスト(ドメイン名は含まれません)と、登録局の RDAP サーバーに、有効期限を問い合わせます
  3. MX と _mta-sts の TXT があるときだけ、curl がそのドメインの mta-sts ホストから方針ファイルを読みます
練習用の DNS を立てて、ドメインを点検する

変更を伴う操作です。外のサーバーに何も送らないよう、VM の中に練習用の DNS(dnsmasq)を立て、名前解決をそこへ向けます。example.com は、例のために予約されたドメインです。

最初は、SPF が誰にでも送信を許していて(+all)、DMARC がない状態にします。

sudo apt install -y dnsmasq-base
sudo ip addr add 127.0.0.2/8 dev lo
mkdir -p ~/lab
cat > ~/lab/zone.conf <<'Z'
port=53
listen-address=127.0.0.2
bind-interfaces
no-resolv
no-hosts
local=/example.com/
txt-record=example.com,"v=spf1 +all"
Z
sudo dnsmasq --conf-file=$HOME/lab/zone.conf --pid-file=/run/dnsmasq-lab.pid
IF=$(ip route show default | awk '{print $5; exit}')
sudo resolvectl dns "$IF" 127.0.0.2
dig +short TXT example.com
実行結果
"v=spf1 +all"

ドメインを設定して、診断します。

sudo roamswitch server config set domain_check_domains example.com
sudo roamswitch status --server | grep -A2 "SPF"
出力の抜粋
  ✓ サーバー設定を更新・保存しました。 (domain_check_domains = example.com)
  再起動は要りません。次の診断(roamswitch server status など)から使われます。

  [要対策] 自分のドメインの SPF・DMARC・MTA-STS・有効期限 - example.com: SPFが誰からの送信も許可している (+all か ?all), DMARCレコードがない / 参考: example.com: CAAレコードなし, 有効期限を取得できず
         ↳ DNSに足してください。メールを送るドメインの SPF は `v=spf1 include:(送信サービス) ~all`、メールを使わないドメインは `v=spf1 -all` です。SPF のTXTは1つだけにします。DMARC は `_dmarc` に `v=DMARC1; p=none; rua=mailto:(受信先)` から始め、レポートで正規の送信元を確認してから `p=quarantine`、`p=reject` へ上げます。(以下略)

この設定は、再起動なしで、次の診断から使われます。有効期限は、練習用の DNS が RDAP の問い合わせ先を答えないため、取れません。取れないときは「参考」に出るだけで、不合格にはなりません。

メールを使わないドメインとして直します。SPF を -all にし、DMARC を足します。

sed -i 's/v=spf1 +all/v=spf1 -all/' ~/lab/zone.conf
echo 'txt-record=_dmarc.example.com,"v=DMARC1; p=reject"' >> ~/lab/zone.conf
sudo kill $(cat /run/dnsmasq-lab.pid); sleep 1
sudo dnsmasq --conf-file=$HOME/lab/zone.conf --pid-file=/run/dnsmasq-lab.pid
sudo resolvectl flush-caches
sudo roamswitch status --server | grep "SPF"
実行結果
  [合格] 自分のドメインの SPF・DMARC・MTA-STS・有効期限 - 1個のドメインを確認しました (SPF・DMARC に問題なし。有効期限を取得できなかったドメイン: 1件) / 参考: example.com: CAAレコードなし, 有効期限を取得できず

DNS を書き換えた直後の診断に、新しい結果が出ました。診断の説明のとおり、roamswitch status --server などのコマンドは、実行のたびに調べ直します。結果を 6 時間覚えるのは、動き続けるデーモンと MCP サーバーです。

次に、MX を足し、MTA-STS を宣言します。ただし、方針ファイルは置きません。

cat >> ~/lab/zone.conf <<'Z'
mx-host=example.com,mail.example.com,10
txt-record=_mta-sts.example.com,"v=STSv1; id=20261104"
address=/mta-sts.example.com/127.0.0.1
Z
sudo kill $(cat /run/dnsmasq-lab.pid); sleep 1
sudo dnsmasq --conf-file=$HOME/lab/zone.conf --pid-file=/run/dnsmasq-lab.pid
sudo resolvectl flush-caches
sudo roamswitch status --server | grep "SPF"
実行結果
  [要対策] 自分のドメインの SPF・DMARC・MTA-STS・有効期限 - example.com: MTA-STSを宣言しているが、方針ファイルを取得できない / 参考: example.com: CAAレコードなし, 有効期限を取得できず

MTA-STS を使っていないだけでは、不合格になりません。宣言しているのに方針ファイルが取れないと、不合格です。送る側のメールサーバーが、宣言を信じて配送に失敗するおそれがあるためです。

元に戻します。

sudo roamswitch server config set domain_check_domains ""
sudo kill $(cat /run/dnsmasq-lab.pid)
sudo resolvectl revert "$IF"
sudo ip addr del 127.0.0.2/8 dev lo
出力の抜粋
  ✓ サーバー設定を更新・保存しました。 (domain_check_domains = )

#開発用のサーバーを、実際に確かめる

ポートが開いていることと、そこに弱点があることは、別のことです。実証型の脆弱性診断 は、127.0.0.1 で待ち受けるサービスに、実際に確認の要求を送ります。送るのは読み取りの要求だけで、壊すものは送りません(--help の説明)。宛先は 127.0.0.1 に限られ、ほかのホストは指定できません。

既定はオフです。~/.config/roamswitch/config.json に "active_vuln_scan_enabled": true を書き、--confirm を付けたときだけ送ります。--confirm がなければ、宛先の一覧を出すだけです。

CORS の設定ミスがある開発用サーバーを確かめる

練習用に、どの Origin にも許可を返し、認証情報つきの読み取りまで許す、小さなサーバーを立てます。悪意のあるサイトが、利用者のブラウザを通して、このサーバーの応答を読めてしまう設定です。

cat > /tmp/devsrv.py <<'PY'
import http.server
class H(http.server.BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        o = self.headers.get('Origin')
        if o:
            self.send_header('Access-Control-Allow-Origin', o)
            self.send_header('Access-Control-Allow-Credentials', 'true')
        self.end_headers(); self.wfile.write(b'ok\n')
    def log_message(self, *a): pass
http.server.HTTPServer(('127.0.0.1', 3000), H).serve_forever()
PY
nohup python3 /tmp/devsrv.py >/dev/null 2>&1 &
roamswitch scan-vulns; echo "exit=$?"
出力の抜粋(--confirm なし)
  以下へ、実際に非破壊的な到達確認・検証リクエストを送信します:
    • 127.0.0.1:3000 (python3) — 既知サービス確認
    • 127.0.0.1:3000 (python3) — CORS / パストラバーサル / リダイレクト / 反射型XSS検証

  これはドライラン(未送信)です。実行するには --confirm を付けてください。
  また ~/.config/roamswitch/config.json に "active_vuln_scan_enabled": true が必要です(既定OFF)。

  ドライランのため、何も送らず、何も確かめていません(終了コード 2)。
exit=2

ドライランは、何も確かめていないので、終了コード 2 で終わります。設定がオフのまま --confirm を付けたときも 2 です。そのときは、Server Edition では sudo で動かすなら /root/.config/roamswitch/config.json に書くよう案内します。

設定をオンにして、実際に確かめます。

mkdir -p ~/.config/roamswitch
echo '{"active_vuln_scan_enabled": true}' > ~/.config/roamswitch/config.json
roamswitch scan-vulns --confirm; echo "exit=$?"
出力の抜粋
  ✗ Port 3000 (python3)
    CORS 設定ミス(認証情報付きクロスオリジン許可)
    任意のOriginヘッダを送信したところ、そのOriginがAccess-Control-Allow-Originに反映(または*が返却)され、かつAccess-Control-Allow-Credentials: trueが同時に返されました。この組み合わせは、悪意あるWebサイトが被害者のブラウザ経由でこのサーバーへ認証済みリクエストを送信し、レスポンスを読み取れることを意味します。
    ↳ Access-Control-Allow-Origin を信頼できる特定のオリジンのみに限定し、Access-Control-Allow-Credentials は本当に必要な場合のみ有効にしてください。
  ℹ Port 22 (systemd (ssh.socket))
    nmap NSE所見: ssh2-enum-algos
    (略)
    ↳ nmap自身の検知結果です(RoamSwitch独自の検証は行っていません)。
  ⚠ 確認できなかった項目が1件あります(安全であることの確認ではありません):
    • 127.0.0.1:3000 (python3) — 反射型XSS(入力値が無害化されずにHTMLへ出力される)
  ℹ ℹ の 1 件は情報です(脆弱性の確認ではないため、終了コードに数えません)。

exit=1

出力の記号は、次のように読みます。

  • ✗: 送った要求への応答で、弱点を確かめました
  • ℹ: nmap 自身の結果です。RoamSwitch は検証していません。この回は、22 番の SSH の暗号の一覧(ssh2-enum-algos)の1件でした。22 番の持ち主は、ssh.socket の systemd (ssh.socket) と出ます
  • ⚠ 確認できなかった項目: 確かめられませんでした。安全の確認ではありません

終了コードは、✗ が1件以上で 1 です。ℹ は数えません。

許可するオリジンを1つに絞って、もう一度確かめます。

P=$(ss -ltnpH 'sport = :3000' | sed -n 's/.*pid=\([0-9]*\).*/\1/p'); kill $P
sed -i "s|if o:|if o == 'http://127.0.0.1:3000':|" /tmp/devsrv.py
nohup python3 /tmp/devsrv.py >/dev/null 2>&1 &
roamswitch scan-vulns --confirm | grep -c "✗"
roamswitch scan-vulns --confirm > /dev/null; echo "exit=$?"
実行結果
0
exit=0

CORS の行が消え、終了コードは 0 になりました。スクリプトでは、0 は ✗ なし、1 は ✗ あり、2 は確かめられなかった、と分けて扱えます。所見があっても止めずに続けるなら、roamswitch scan-vulns --confirm || [ $? -eq 1 ] のように 1 を扱います。

元に戻します。サーバーを止め、設定をオフにします。

P=$(ss -ltnpH 'sport = :3000' | sed -n 's/.*pid=\([0-9]*\).*/\1/p'); kill $P
echo '{"active_vuln_scan_enabled": false}' > ~/.config/roamswitch/config.json

#RoamSwitch 自身が、書き換えられていないか

守りの仕組みそのものが書き換えられていれば、診断の結果も信じられません。RoamSwitch 自身の点検(doctor) は、RoamSwitch のファイルと、防御に使うツールを点検します。何も変更しません。

見るのは、次の点です(roamswitch doctor --help の説明と、実際の出力)。

  • 入っている RoamSwitch のファイル(/usr/lib/roamswitch のデーモンと、systemd のユニットを含む)と、防御に使うツール(nft など)が、root の所有で、root 以外に書き込めないか
  • パッケージ管理の検証(dpkg --verify)で、インストール時と同じ中身か
  • 手で足された認証局の証明書がないか(/usr/local/share/ca-certificates など)
実行ファイルを書き換えて、doctor で見つける

変更を伴う操作です。使っていない Web ゲートの実行ファイルに 1 バイト足し、誰でも書ける権限にします。あわせて、練習用の認証局の証明書を1つ置きます。通信を中継して中身を読むときに、攻撃者が置くものです。

sudo cp -p /usr/bin/roamswitch-webgate /root/webgate.orig
printf '\0' | sudo tee -a /usr/bin/roamswitch-webgate >/dev/null
sudo chmod o+w /usr/bin/roamswitch-webgate
openssl req -x509 -newkey rsa:2048 -nodes -keyout /tmp/dummy-ca.key -out /tmp/dummy-ca.crt -days 1 -subj "/CN=Dummy Training CA"
sudo cp /tmp/dummy-ca.crt /usr/local/share/ca-certificates/dummy-training-ca.crt
sudo roamswitch doctor; echo "exit=$?"
出力の抜粋
[失敗] roamswitch-webgate (/usr/bin/roamswitch-webgate) に root 以外が書き込めます(権限 757)。別のものに差し替えられるおそれがあります
[失敗] パッケージ roamswitch-server の 1 個のファイルが、インストール時と違います: /usr/bin/roamswitch-webgate
[注意] 追加の認証局の証明書が 1 個、このホストに手で入れてあります(dummy-training-ca.crt)。通信を中継して読める認証局が混ざっていないか確認してください

失敗が 2 件あります(注意 5 件)
exit=1

失敗が1件でもあると、終了コードは 1 です。定期的なジョブで doctor を動かし、終了コードで気づく使い方ができます。権限と中身は、別の行で示されます。権限だけ戻しても、中身の違いは残ります。

元に戻します。

sudo cp -p /root/webgate.orig /usr/bin/roamswitch-webgate
sudo chmod 755 /usr/bin/roamswitch-webgate
sudo rm /usr/local/share/ca-certificates/dummy-training-ca.crt /root/webgate.orig
sudo roamswitch doctor | tail -1
sudo dpkg --verify roamswitch-server; echo "verify=$?"
実行結果
失敗はありません(注意 4 件)
verify=0

権限の点検は、/usr/bin の実行ファイルだけでなく、/usr/lib/roamswitch のデーモンと、systemd のユニットも見ます。どちらも、書き換えられると、次の起動で別のものが root で動きます。権限を変える操作です(変更を伴う操作です)。

U=$(systemctl show -p FragmentPath --value roamswitch-server)
sudo chmod o+w /usr/lib/roamswitch/roamswitch-server-daemon "$U"
sudo roamswitch doctor | grep "失敗"
sudo chmod o-w /usr/lib/roamswitch/roamswitch-server-daemon "$U"
sudo roamswitch doctor | tail -1
実行結果
[失敗] roamswitch-server-daemon (/usr/lib/roamswitch/roamswitch-server-daemon) に root 以外が書き込めます(権限 757)。別のものに差し替えられるおそれがあります
[失敗] roamswitch-server.service (/usr/lib/systemd/system/roamswitch-server.service) に root 以外が書き込めます(権限 646)。別のものに差し替えられるおそれがあります
失敗が 2 件あります(注意 4 件)
失敗はありません(注意 4 件)

本当の運用で中身の違いが出たら、まず誰がいつ書き換えたかを調べます。書き換えは、侵入の跡のことがあります。

#見えない部分

確かめたこと・見えないこと

実機(使い捨ての Ubuntu 24.04 の VM)で、上の演習をすべて実行しました。そのうえで、次のことは、この講座の機能では分かりません。

  • 総合診断の合格は、侵入されていない証明ではありません。設定が決まった形になっているかを見ています
  • 設定が弱くなったときの通知は、決まった 27 項目だけです。ほかの変化(ポートが開いた、ガードが止まった、など)は、それぞれのガードが知らせます。同じ項目は1日以内に繰り返さないので、1日のうちに2回緩められても、通知は1通です
  • 名前解決の診断は、待ち受けているかを見ます。ネットワークに流れる偽の答えは見ません
  • ドメインの点検は、DNS の答えを信じます。リゾルバーがうそを返せば、結果もうそになります。この講座の演習も、その仕組みを使って練習しました
  • 実証型の診断は、127.0.0.1 だけです。同じ LAN の別の機器からの見え方は、LAN に置く専用ノード(RoamSwitch Sensor)で確かめます
  • doctor は、RoamSwitch のファイルを見ます。ほかのプログラムの改ざんは、ファイルの改ざん監視(FIM)が受け持ちます
確認問題

総合診断で「[確認不能] 悪用される恐れの高い脆弱性 - 確認不能 (パッケージの脆弱性データがまだありません)」と出ました。正しい受け取り方はどれですか

解説を見る

確認不能は、調べられなかったという意味で、安全の確認ではありません。点数にも数えません。データは更新の仕組み(roamswitch-updater)で入ります。入ってから、もう一度診断します。

確認問題

sudo roamswitch doctor が「失敗が 1 件あります」と出て、パッケージのファイルがインストール時と違うと言っています。最初にすることはどれですか

解説を見る

中身の違いは、権限を直しても消えません。書き換えは、侵入の跡のことがあります。ログで経緯を調べてから、入れたときと同じパッケージで入れ直します。

#次に学ぶこと

扱う機能
確認した内容

2026-11-12に確認しました。対象は Linux 1.17.39 です。公開の apt の 1.17.39 を入れた使い捨ての Ubuntu 24.04 の VM で、全演習を root とふつうのユーザー、日本語と英語で実行しました。設定の弱化の通知は、既定の言語の VM で起こし、日本語と英語(LANG)で読みました。

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