サーバー運用者の危機対応

状況

CPU を使い続けるプロセスとメモリの上限で落ち続けるサービスを起こし、通知の読み方、マイナーの見分け方、制限と停止の順番を学びます。

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

#状況

午前 11 時、通知が2件届きました

どちらも RoamSwitch の通知で、「リソース枯渇ガード: 持続的な高CPU使用を検知」です。続けて、同僚から連絡が来ます。「サーバーが重くて、画面がなかなか返ってきません。」

CPU やメモリが尽きる原因は、さまざまです。夜間の集計が長引いただけのこともあります。メモリの上限に当たって、落ちては起き上がるサービスのこともあります。入り込んだ攻撃者が置いた、暗号資産の採掘プログラム(マイナー)のこともあります。

運用者が決めるのは、次の3つです。どのプロセスが原因か。正当な処理か、置かれたものか。止めるか、制限するか。

この講座は、その判断と手順を、使い捨ての VM の中で実際に起こして確かめる手順書です。

順番すること使うもの
1. 起こす無害な負荷を3つ動かすsystemd-run、Python
2. 見る通知と記録を読むリソース枯渇とクラッシュの検知、通知の履歴、インシデントのタイムライン
3. 調べるどのプロセスか、親は何か、いつからかを決めるps、ss、プロセス実行の記録と攻撃手口の検知
4. 対応する止める、制限する、再発を防ぐsystemctl、kill、ユニットの設定
5. 元に戻す練習の場を片付ける

検知の仕組みと、メモリの増え続け・コンテナのクラッシュの起こし方は、コンテナとサーバー で扱いました。この講座は、通知が来たあとの判断と手順に絞ります。

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

この講座は、CPU を使い切るプロセスと、メモリを使い切るサービスを、わざと動かします。メモリを使うサービスには、MemoryMax=100M の上限を付けます。上限の中で止まるので、VM 全体のメモリは尽きません。負荷はすべて VM の中だけで動かし、外には何も送りません。

#練習の場を用意する

リソース枯渇とクラッシュの検知 は、通知だけをします。プロセスを止めることはありません。CPU の判定の既定は、次のとおりです(/etc/roamswitch/server.conf.example と、ソースの既定値)。

キー既定意味
resource_guard_cpu_enabledtrueCPU を使い続けるプロセスを見る
resource_guard_cpu_sample_interval_secs60CPU を測る間隔(秒)
resource_guard_cpu_window_secs600判定する区間(秒)
resource_guard_cpu_min_samples8判定に要る測定の回数
resource_guard_cpu_pct_threshold801コアあたり、この % 以上を「高い」と数える

区間の中の測定の 9 割以上が 80% 以上だと、通知が出ます。メモリの判定と違い、待ち受けていないプロセスも対象です。採掘のプログラムは、外へつなぐだけで、待ち受けないためです。

測る間隔を縮める

既定では、判定に 8 分ほどかかります。演習では、測る間隔を 10 秒にします。クラッシュの繰り返しを読むログの監査も、許される最小の 300 秒にします(変更を伴う操作です。最後の演習で戻します)。どれも、反映には再起動が要ります。

sudo roamswitch server config set resource_guard_rss_sample_interval_secs 10
sudo roamswitch server config set resource_guard_cpu_sample_interval_secs 10
sudo roamswitch server config set log_audit_interval_secs 300
sudo roamswitch server restart
出力の抜粋
  ✓ サーバー設定を更新・保存しました。 (resource_guard_rss_sample_interval_secs = 10)
  反映するには: sudo systemctl restart roamswitch-server または sudo roamswitch server restart

  ✓ サーバー設定を更新・保存しました。 (resource_guard_cpu_sample_interval_secs = 10)
  ✓ サーバー設定を更新・保存しました。 (log_audit_interval_secs = 300)
  🔄 roamswitch-server サービスを再起動中...
  ✓ roamswitch-server サービスを正常に再起動しました。

メモリと CPU の、両方の間隔を縮めます。再起動の直後は、実行の記録がまだ始まっていないことがあります。次の演習は、30 秒ほど待ってから始めます。

縮めた間隔を、そのままにしない

間隔が短いと、デーモンが /proc を読む回数が増えます。ビルドのような短い山でも、通知が出やすくなります。演習のあとは、既定に戻してください。

#CPU を使い続けるプロセスを見つける

無害な負荷を、2つの形で動かします。

  • 夜間の集計: lab-report.service として、nobody で動く。CPU を回すだけ
  • 置かれたマイナー: python3 を /var/tmp/.cache/kswapd0 にコピーして動かす。カーネルのスレッドに似せた名前で、127.0.0.1:3333 につなぎ続ける。止められても、親のシェルが 5 秒後に動かし直す

3333 番は、採掘のプールがよく使う番号です。VM の中に、つながりを受けるだけの lab-pool.service を置き、外のプールの代わりにします。

負荷を起こし、通知を読む

プロセスを動かす操作です(変更を伴う操作です)。止め方は、次の章からの演習にあります。

sudo mkdir -p /opt/lab
sudo tee /opt/lab/busy.py >/dev/null <<'PY'
import socket, sys
if len(sys.argv) > 1:
    conn = socket.create_connection(("127.0.0.1", int(sys.argv[1])))
while True:
    pass
PY
sudo tee /opt/lab/pool.py >/dev/null <<'PY'
import socket
s = socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(("127.0.0.1", 3333)); s.listen()
held = []
while True:
    c, _ = s.accept(); held.append(c)
PY
sudo systemd-run --unit=lab-report -p User=nobody python3 /opt/lab/busy.py
sudo systemd-run --unit=lab-pool python3 /opt/lab/pool.py
mkdir -p /var/tmp/.cache
cp /usr/bin/python3 /var/tmp/.cache/kswapd0
setsid sh -c 'while true; do /var/tmp/.cache/kswapd0 /opt/lab/busy.py 3333; sleep 5; done' </dev/null >/dev/null 2>&1 &
実行結果
Running as unit: lab-report.service; invocation ID: 9a18360536564b29bab468bc026e5c66
Running as unit: lab-pool.service; invocation ID: 2c3ceffb947347bf964fd9ff620b47ef

この VM では、1 分半ほどで、通知が2件出ました。

sudo roamswitch notifications | grep -A11 "持続的な高CPU"
出力の抜粋
  [相関あり] リソース枯渇ガード: 持続的な高CPU使用を検知 — kswapd0
    🚨 プロセスが持続的にCPUを高使用率のまま維持しています(暗号資産マイニングの可能性)
    ホスト: ubuntu (IP: 192.168.64.27)
    対象プロセス: kswapd0 (PID: 1545)
    コンテナ: コンテナ外
    区間内の平均CPU使用率: 100%
    相関シグナル: mining_port_signature
    時刻: 2026-11-12 02:00:30
    プロセス: admin / /var/tmp/.cache/kswapd0 / コマンド: /var/tmp/.cache/kswapd0 /opt/lab/busy.py 3333 (pid 1545)
    親プロセス: dash ← dash ← tart-guest-agent ← systemd / サービス: tart-guest-agent.service

  2026-11-12T02:00:30.751925030+00:00
  [単独検知] リソース枯渇ガード: 持続的な高CPU使用を検知 — python3
    ⚠️ プロセスが長時間にわたり高いCPU使用率を維持しています(コンパイル・スキャン・バッチ処理などでよく見られる挙動です。心当たりがなければ確認してください)
    ホスト: ubuntu (IP: 192.168.64.27)
    対象プロセス: python3 (PID: 1534)
    コンテナ: コンテナ外
    区間内の平均CPU使用率: 100%
    相関シグナル: なし
    時刻: 2026-11-12 02:00:30
    プロセス: nobody / /usr/bin/python3.12 / コマンド: /usr/bin/python3 /opt/lab/busy.py (pid 1534)
    親プロセス: systemd / サービス: lab-report.service

同じ CPU の使い方でも、2件の見出しは違います。

  • [単独検知] と「バッチ処理などでよく見られる」: CPU の使い方のほかに、裏づけがありません
  • [相関あり] と「暗号資産マイニングの可能性」: mining_port_signature が付きました。プールのよく使う番号に、つながりを持っていたという意味です
  • 時刻: UTC です。日本時間では 11:00 です

通知のほかに、統合のタイムラインにも、同じ検知が並びます。

sudo roamswitch timeline | head -22
出力の抜粋
  2026-11-12T02:00:30.822472804+00:00  [Resource Guard]  記録のみ
    持続的高CPU使用率検知: CPU の平均 100% が、見ていた期間ずっと続きました
    プロセス: kswapd0 (pid 1545)
    親プロセス系譜: dash ← dash ← tart-guest-agent ← systemd
    実施した対応: 記録のみ(通知だけの設定のため、遮断しない)

  2026-11-12T02:00:30.752334119+00:00  [Resource Guard]  記録のみ
    持続的高CPU使用率検知: CPU の平均 100% が、見ていた期間ずっと続きました
    プロセス: python3 (pid 1534)
    親プロセス系譜: systemd
    実施した対応: 記録のみ(通知だけの設定のため、遮断しない)

  2026-11-12T01:59:01.807877518+00:00  [Exec Recorder]  記録のみ
    EXR-002: 誰でも書き込めるディレクトリからの実行です。分類: tmp、パス: /var/tmp/.cache/kswapd0
    プロセス: /var/tmp/.cache/kswapd0 (pid 1545)
    親プロセス系譜: dash ← dash ← tart-guest-agent ← systemd
    MITRE ATT&CK: T1059 (Command and Scripting Interpreter)
    実施した対応: 記録して通知

kswapd0 には、1 分半ほど前の 01:59:01 に、もう1件の検知があります。動き始めた直後です。誰でも書き込める /var/tmp から実行された、という プロセス実行の記録と攻撃手口の検知 の検知です。「実施した対応」の行のとおり、どちらのガードも、プロセスを止めていません(実行の記録は「記録して通知」、Resource Guard は「記録のみ」)。

#どのプロセスか、どこから来たかを調べる

通知の PID を手がかりに、4つを確かめます。実行ファイルの場所、親、つながり、動き始めた時刻です。どれも読むだけで、何も変えません。

プロセスの素性を調べる

2つの PID を並べて見ます。

uptime
ps -o pid,ppid,user,lstart,pcpu,args -p 1534,1545
sudo ls -l /proc/1534/exe /proc/1545/exe
sudo ss -tnpH state established '( dport = :3333 )'
cat /proc/1534/cgroup /proc/1545/cgroup
実行結果
 02:05:58 up 8 min,  0 user,  load average: 2.15, 1.69, 0.84
    PID    PPID USER                      STARTED %CPU COMMAND
   1534       1 nobody   Thu Nov 12 01:59:01 2026 99.9 /usr/bin/python3 /opt/lab/busy.py
   1545    1540 admin    Thu Nov 12 01:59:01 2026 99.9 /var/tmp/.cache/kswapd0 /opt/lab/busy.py 3333
lrwxrwxrwx 1 nobody nogroup 0 Nov 12 02:05 /proc/1534/exe -> /usr/bin/python3.12
lrwxrwxrwx 1 admin  admin   0 Nov 12 02:05 /proc/1545/exe -> /var/tmp/.cache/kswapd0
0      0      127.0.0.1:58382 127.0.0.1:3333 users:(("kswapd0",pid=1545,fd=3))
0::/system.slice/lab-report.service
0::/system.slice/tart-guest-agent.service

読み方は次のとおりです。

  • load average: 2.15: 4 コアの VM で、2 つのプロセスが 1 コアずつ使い切っています。動き始めて 7 分なので、15 分の平均はまだ低いままです
  • 1534: 実行ファイルは /usr/bin/python3.12、所属は lab-report.service です。systemd が動かす、名前のついたサービスです
  • 1545: 実行ファイルは /var/tmp/.cache/kswapd0 です。3333 番につながり、親は PID 1540 です。所属の tart-guest-agent.service は、この VM で tart exec を受けるサービスです。実機なら、SSH のセッションや cron の下に出ます

本物の kswapd0 は、カーネルのスレッドです。名前で探すと、違いが分かります。

ps -C kswapd0 -o pid,ppid,user,args
実行結果
    PID    PPID USER     COMMAND
     67       2 root     [kswapd0]
   1545    1540 admin    /var/tmp/.cache/kswapd0 /opt/lab/busy.py 3333

カーネルのスレッドは、親が PID 2 で、名前が [ ] で囲まれます。実行ファイルを持つ kswapd0 は、名前を似せたものです。

親の PID 1540 が、どう動き始めたかは、実行の記録でたどります。

sudo roamswitch events tree 1545
sudo roamswitch events search --since 1h --exe kswapd0
sudo roamswitch events search --since 1h --pid 1540
実行結果
1 /usr/lib/systemd/systemd
686 /usr/bin/tart-guest-agent
878 /usr/bin/dash
1540 /usr/bin/dash
          1545 /var/tmp/.cache/kswapd0 /opt/lab/busy.py 3333
2026-11-12T01:59:01.734Z pid=1545 ppid=1540 admin /var/tmp/.cache/kswapd0 /opt/lab/busy.py 3333
2026-11-12T01:59:01.734Z pid=1540 ppid=878 admin /usr/bin/dash -c while true; do /var/tmp/.cache/kswapd0 /opt/lab/busy.py 3333; sleep 5; done
2026-11-12T01:59:01.734Z pid=1540 ppid=878 admin /usr/bin/setsid sh -c while true; do /var/tmp/.cache/kswapd0 /opt/lab/busy.py 3333; sleep 5; done

--pid 1540 で検索すると、親の引数が出ます。setsid で始まり、while true; do /var/tmp/.cache/kswapd0 …; sleep 5; done を繰り返すシェルでした。子を止めても、5 秒後に動かし直します。

正当な処理か、置かれたものか

通知と調べた結果から決めます。

  • systemd のユニットや cron に属し、実行ファイルがパッケージの場所にある、担当者が分かる: 正当な処理の見込みが高いです。次の章で「制限する」を選びます
  • 実行ファイルが /tmp、/var/tmp、/dev/shm にある、名前がカーネルのスレッドやシステムのプロセスに似ている、mining_port_signature が付いた: 置かれたものとして扱います。証拠を残してから止めます
  • どちらとも言えない: 担当者に確かめるまで、制限をかけて待ちます。止めると、正当な処理の結果が失われることがあります

mining_port_signature は、3333、4444、5555、7777、9999、14444、17777、19999、45560 番への TCP のつながりで付きます(ソースの一覧)。8080 番は、アプリのサーバーや上流によく使われるので、一覧から外してあります。一覧の番号も、ほかの用途に使われることがあります。この印だけでは決めず、実行ファイルの場所と親を合わせて見ます。

#止める・制限する

正当な処理には、CPU の上限をかけます。置かれたものは、証拠を残してから、親から止めます。

集計に上限をかけ、マイナーを親から止める

プロセスを制限し、止める操作です(変更を伴う操作です)。--runtime の上限は、再起動で消えます。

まず、集計の lab-report.service に、1 コアの 25% までの上限をかけます。止めないので、集計は続きます。

sudo systemctl set-property --runtime lab-report.service CPUQuota=25%
systemctl show lab-report.service -p CPUQuotaPerSecUSec
top -b -n 2 -d 3 -p 1534 | tail -2
実行結果
CPUQuotaPerSecUSec=250ms
    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
   1534 nobody    20   0   17836  10356   6104 R  25.0   0.3   6:58.18 python3

すぐに 25.0% まで下がりました。

次は、マイナーです。止める前に、実行ファイルと引数を残します。実行ファイルが消されても、/proc/<PID>/exe からは写せます。

sudo mkdir -p /root/evidence
sudo cp /proc/1545/exe /root/evidence/kswapd0.bin
sudo sha256sum /root/evidence/kswapd0.bin /usr/bin/python3.12
tr '\0' ' ' < /proc/1545/cmdline; echo
実行結果
c86f21d0da9290ece2d7d49a3f7ed52aed59cb029abdf0ea25c79634f6cbe960  /root/evidence/kswapd0.bin
c86f21d0da9290ece2d7d49a3f7ed52aed59cb029abdf0ea25c79634f6cbe960  /usr/bin/python3.12
/var/tmp/.cache/kswapd0 /opt/lab/busy.py 3333 

ハッシュは、python3.12 と同じでした。この演習では、コピーした python3 だからです。実機では、このハッシュを記録に残します。子だけを止めてみます。

kill 1545
sleep 6
ps -C kswapd0 -o pid,ppid,user,args
実行結果
    PID    PPID USER     COMMAND
     67       2 root     [kswapd0]
   4147    1540 admin    /var/tmp/.cache/kswapd0 /opt/lab/busy.py 3333

PID 4147 として、同じ親の下に戻ってきました。親を止めてから、子を止めます。

kill 1540
pkill -x kswapd0 -u admin
sleep 6
ps -C kswapd0 -o pid,ppid,user,args
crontab -l
sudo find /etc/systemd/system /etc/cron.d /home/admin/.config/systemd -type f -mmin -30 2>/dev/null
実行結果
    PID    PPID USER     COMMAND
     67       2 root     [kswapd0]
no crontab for admin

今度は戻りません。cron と systemd の設定にも、この 30 分に変わったファイルはありません。実機では、再起動のあとに戻る仕掛けを、居座りと改ざん の手順で調べます。

子だけを止めて、終わりにする

上のとおり、親が残っていると、5 秒で戻りました。kill -9 を先に打つのも避けます。実行ファイルと引数を残す前にプロセスが消えると、/proc/<PID>/exe から写せなくなります。順番は、記録する、親を止める、子を止める、戻らないことを確かめる、です。

制限で済ませるか、止めるか
  • 正当な処理で、急ぎでない: CPUQuota= で上限をかけ、処理は続けます。恒久にするなら、--runtime を外して set-property を実行するか、ユニットの設定に書きます
  • 正当な処理で、ほかのサービスを止めている: いったん止め、時間帯や上限を決めてから動かし直します
  • 置かれたもの: 証拠を残して止めます。入り口(パスワード、Web のアプリケーション、鍵)を調べるまで、同じ利用者のセッションと鍵を疑います

#メモリが尽きて、落ち続けるサービス

次は、メモリです。MemoryMax=100M を付けたサービスが、0.1 秒ごとに 5 MB ずつ確保します。上限に届くと、カーネルがそのサービスのプロセスだけを止めます。Restart=always なので、systemd が 10 秒後に動かし直し、また止まります。

リソース枯渇とクラッシュの検知 は、systemd のこの繰り返しを、journal の「Scheduled restart job」の行から数えます。既定では、300 秒の中で 3 回の再起動で知らせます(resource_guard_crash_loop_window_secs、resource_guard_crash_loop_min_restarts)。journal を読むのは、ログの監査の間隔(log_audit_interval_secs、既定 1800 秒)ごとです。log_audit_enabled=false にすると、この数え方は動きません。

上限に当たって落ち続けるサービスを起こし、止める

サービスを動かし、止める操作です(変更を伴う操作です)。

sudo tee /opt/lab/hog.py >/dev/null <<'PY'
import time
hold = []
while True:
    hold.append(bytearray(5 * 1024 * 1024))
    time.sleep(0.1)
PY
sudo systemd-run --unit=lab-cache -p MemoryMax=100M -p MemorySwapMax=0 -p Restart=always -p RestartSec=10 python3 /opt/lab/hog.py
sleep 30
sudo journalctl -u lab-cache --no-pager | tail -4
実行結果
Nov 12 02:06:47 ubuntu systemd[1]: lab-cache.service: Scheduled restart job, restart counter is at 2.
Nov 12 02:06:47 ubuntu systemd[1]: Started lab-cache.service - /usr/bin/python3 /opt/lab/hog.py.
Nov 12 02:06:49 ubuntu systemd[1]: lab-cache.service: Main process exited, code=killed, status=9/KILL
Nov 12 02:06:49 ubuntu systemd[1]: lab-cache.service: Failed with result 'signal'.

1〜2 秒で止まり、10 秒後に起き上がる、を繰り返しています。systemd の行は、シグナル 9 で止められた(status=9/KILL)とだけ言います。だれが止めたかは、このあとカーネルのログで確かめます。次のログの監査のあとに、通知が出ました。この VM では、起こしてから約 2 分でした。

sudo roamswitch notifications | grep -A8 "クラッシュループ"
実行結果
  [単独検知] リソース枯渇ガード: クラッシュループ — lab-cache.service
    ⚠️ OOM killer がユニットのプロセスを止めて、再起動を繰り返しています(ユニットの MemoryMax などのメモリ上限か、ホストのメモリ不足です。攻撃の形ではありません。上限と使用量を確認してください)
    ホスト: ubuntu (IP: 192.168.64.27)
    対象ユニット/コンテナ: lab-cache.service
    対象プロセス: 不明 (PID: なし)
    区間内の再起動回数: 3回 (区間: 300秒)
    相関シグナル: unit_oom_killed
    時刻: 2026-11-12 02:08:31

通知はユニットの名前を示し、PID はありません。プロセスが入れ替わり続けるためです。相関シグナルの unit_oom_killed は、そのユニットのプロセスを OOM killer が止めていた、という意味です。見出しの文も、攻撃の形ではないと言っています。裏づけは、ユニットとカーネルのログで確かめます。

systemctl show lab-cache.service -p NRestarts -p Result
sudo journalctl -k --no-pager | grep "Memory cgroup out of memory" | tail -1
実行結果
Result=signal
NRestarts=10
Nov 12 02:08:25 ubuntu kernel: Memory cgroup out of memory: Killed process 4870 (python3) total-vm:120116kB, anon-rss:102104kB, file-rss:5924kB, shmem-rss:0kB, UID:0 pgtables:272kB oom_score_adj:0
  • NRestarts=10: 約 2 分で 10 回、起き上がっています。通知の「3回」は、しきい値に届いた時点の数です
  • Memory cgroup out of memory: サービスに付けた上限(メモリの cgroup)に当たって、カーネルが止めました。VM 全体のメモリが尽きたのではありません

止めます。

sudo systemctl stop lab-cache.service
systemctl is-active lab-cache.service
実行結果
inactive
クラッシュの繰り返しを、どう読むか

通知の相関シグナルと見出しの文が、最初の手がかりです。unit_oom_killed が付けば、メモリの上限か不足です。付かないときも、見出しだけで決めず、カーネルのログで分けます。

  • Memory cgroup out of memory が、そのユニットで出ている: 上限に当たっています。メモリの使い方(リーク、急な負荷)か、上限の小ささを疑います
  • そのユニットの Memory cgroup の行がなく、ほかのプロセスも止められている: ホスト全体のメモリの取り合いを疑います。大きいプロセスを ps -eo pid,rss,args --sort=-rss で見ます
  • メモリの記録がなく、code=dumped や status=11/SEGV で落ちている: 異常な入力で落ちている見込みがあります。そのサービスへのアクセスのログと、同じ時刻のほかの検知を合わせて見ます

止めたあとは、再発を防ぎます。上限を付けたまま、何度も起き上がらないようにします。

再発を防ぐ設定を書き、練習の場を片付ける

ユニットのファイルを置く操作です(変更を伴う操作です)。この演習の最後に、すべて元に戻します。

StartLimitIntervalSec=300 と StartLimitBurst=3 で、300 秒の中の起動を 3 回までにします。

sudo tee /etc/systemd/system/lab-cache.service >/dev/null <<'UNIT'
[Unit]
Description=lab cache (practice)
StartLimitIntervalSec=300
StartLimitBurst=3

[Service]
ExecStart=/usr/bin/python3 /opt/lab/hog.py
MemoryMax=100M
MemorySwapMax=0
Restart=on-failure
RestartSec=10
UNIT
sudo systemctl daemon-reload
sudo systemctl start lab-cache.service
sleep 50
systemctl show lab-cache.service -p NRestarts -p Result -p ActiveState
sudo journalctl -u lab-cache.service --no-pager --since "-1min" | tail -3
実行結果
Result=signal
NRestarts=3
ActiveState=failed
Nov 12 02:09:26 ubuntu systemd[1]: lab-cache.service: Start request repeated too quickly.
Nov 12 02:09:26 ubuntu systemd[1]: lab-cache.service: Failed with result 'signal'.
Nov 12 02:09:26 ubuntu systemd[1]: Failed to start lab-cache.service - lab cache (practice).

3 回の再起動で、systemd が起動をあきらめ、failed で止まりました。落ち続けて CPU やログを使い続けることはなくなります。実機では、failed になったユニットを見落とさないよう、systemctl --failed を見回りに入れます。

最後に、練習の場を片付けます。設定は、既定に戻します。

sudo systemctl stop lab-report.service lab-pool.service
sudo rm /etc/systemd/system/lab-cache.service
sudo systemctl daemon-reload
sudo systemctl reset-failed
rm -rf /var/tmp/.cache
sudo rm -rf /opt/lab /root/evidence
sudo roamswitch server config set resource_guard_rss_sample_interval_secs 60
sudo roamswitch server config set resource_guard_cpu_sample_interval_secs 60
sudo roamswitch server config set log_audit_interval_secs 1800
sudo roamswitch server restart
systemctl list-units --all 'lab-*' --no-legend
ps -eo pid,pcpu,args --sort=-pcpu | head -3
出力の抜粋
  ✓ サーバー設定を更新・保存しました。 (resource_guard_rss_sample_interval_secs = 60)
  ✓ サーバー設定を更新・保存しました。 (resource_guard_cpu_sample_interval_secs = 60)
  ✓ サーバー設定を更新・保存しました。 (log_audit_interval_secs = 1800)
  🔄 roamswitch-server サービスを再起動中...
  ✓ roamswitch-server サービスを正常に再起動しました。

    PID %CPU COMMAND
   5463 64.5 /usr/lib/roamswitch/roamswitch-server-daemon
      1  0.1 /sbin/init

lab- のユニットは残っていません。CPU の上位は、起動したばかりのデーモンと、systemd(PID 1)です。ps の %CPU は、動き始めてからの平均なので、起動の直後は高く出ます。実機の証拠(/root/evidence)は、消さずに残します。

#見えない部分

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

使い捨ての Ubuntu 24.04 の VM(4 コア、メモリ 4 GB)で確かめた範囲は、次のとおりです。

  • リソース枯渇とクラッシュの検知 は、通知と記録だけをします。プロセスを止めること、上限をかけることは、運用者が systemctl や kill で行います
  • CPU の判定は、プロセスごとです。小さなプロセスを多数に分けて、どれも 1 コアの 80% に届かない負荷は、この判定に入りません。この講座では、その形は起こしていません
  • mining_port_signature は、番号の一覧で付きます。一覧にない番号のプールや、Web の 443 番を通る採掘には付きません
  • クラッシュの繰り返しの通知は、ログの監査の間隔ごとです。既定の 1800 秒では、通知までに最大 30 分ほどかかります
  • ロードアベレージとゾンビの数の増え方(resource_guard_load_enabled、resource_guard_zombie_min_count)も、設定ファイルの注記では見ます。この講座では起こしていません
  • サーバーが本当に応答しなくなった状態では、SSH も重くなります。この VM は tart exec で操作しました。実機では、コンソールのような SSH 以外の入り口を、先に確かめておきます
確認問題

[相関あり] の CPU の通知が出ました。実行ファイルは /dev/shm/.x/sshd で、kill で止めても、数秒後に別の PID で戻ってきます。まず何をしますか

解説を見る

戻ってくるのは、親が動かし直しているためです。子だけを止めても終わりません。止める前に /proc/<PID>/exe から実行ファイルを写し、ハッシュを記録します。しきい値を上げると、次の採掘にも気づけなくなります。

確認問題

クラッシュループの通知が出ました。journalctl -k には、そのユニットの Memory cgroup out of memory が並んでいます。どう読みますか

解説を見る

Memory cgroup の行は、サービスに付けた上限で止まったことを示します。通知の相関シグナルも unit_oom_killed になり、見出しの文も攻撃の形ではないと言います。ホストの再起動も隔離も要りません。攻撃を疑うのは、メモリの記録がなく、異常終了(SEGV など)で落ちるときです。

#次に学ぶこと

扱う機能

対応する攻撃手法 (ATT&CK): T1059.004 T1027 T1105 T1553.001

確認した内容

2026-11-12に確認しました。対象は Linux 1.17.39 です。公開版 1.17.39 の入った使い捨ての Ubuntu 24.04 の VM で、測る間隔を縮めて、無害な CPU の負荷・名前を似せたマイナー・MemoryMax で落ち続けるサービスを動かし、通知・タイムライン・実行の記録・systemd とカーネルの出力を、日本語と英語で別の VM で上から順に取りました。

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