【2026年11月1日時点の情報です】
自宅サーバーには、ルートキット(侵入者が隠れるための道具)の跡を探す rkhunter を入れています(入れ方は「rkhunter を入れたときの記事」)。毎朝、その rkhunter からメールが届いていました。中身は長く、「Warning」の文字が並んでいて、正直なところ、大丈夫なのか分からないまま読み流していました。
調べてみると、危ないものは何もありませんでした。原因は、rkhunter の記録が、入れたとき(6月)のまま一度も新しくなっていなかった ことです。毎朝の dnf の自動更新でファイルが入れ替わるたびに、記録とちがうファイルが増え、それが全部警告になっていました。
組み込みでいえば、出荷のときに取った ROM のチェックサムの表を、ファームを何度も書き換えたあとも使い続けて、毎回「不一致」と言われていたようなものです。
この記事では、警告を数えて分け、本当に更新の分かをパッケージの記録で確かめ、記録を取り直して、これからは自動更新のあとに記録を取り直す形にするまでを、実際に動かしたまま書きます。
登場するサーバー名やユーザー名は、セキュリティのため実際とは異なる架空のものに置き換えています。
場面1:メールは「警告がある日だけ」のはずだった
まず、毎朝のメールがどこから来ているかを見ました。rkhunter は、毎日動かす仕組みの置き場所(/etc/cron.daily/)にスクリプトを置いて動いています。
myserver で実行したコマンドです。
sudo cat /etc/cron.daily/rkhunter | grep -vE "^\s*#|^\s*$" | head -30
sudo cat /etc/sysconfig/rkhunter | grep -vE "^\s*#|^\s*$"sudo cat /etc/cron.daily/rkhunter: rkhunter を毎日動かしているスクリプトの中身を出すgrep -vE "^\s*#|^\s*$": 説明の行(#で始まる行)と空の行を除くhead -30: 最初の30行だけにするsudo cat /etc/sysconfig/rkhunter: そのスクリプトが読む設定(メールの宛先、調べ方)を出す
結果の、大事なところです。
if [ "$DIAG_SCAN" = "yes" ]; then
RKHUNTER_FLAGS="--checkall --skip-keypress --nocolors --quiet --appendlog --display-logfile"
else
RKHUNTER_FLAGS="--cronjob --nocolors --report-warnings-only"
fi
…
/bin/nice -n 10 $RKHUNTER $RKHUNTER_FLAGS 2>&1 >> $TMPFILE1
XITVAL=$?
…
if [ $XITVAL != 0 ]; then
/bin/cat $TMPFILE1 | /bin/mail -s "rkhunter Daily Run on $(hostname)" $MAILTO
fi
MAILTO=root@localhost
DIAG_SCAN=no| No. | 行 | 読み方 |
|---|---|---|
| 1 | DIAG_SCAN=no | ふだんの調べ方(--cronjob --report-warnings-only)で動く |
| 2 | --report-warnings-only | 結果のうち、警告だけを出す |
| 3 | XITVAL=$?、if [ $XITVAL != 0 ] | rkhunter の終わりの番号が 0 でない(警告がある)ときだけ、メールを出す |
つまり、この仕組みは最初から 「警告がある日だけメールが来る」 作りでした。それなのに毎朝メールが来ていたのは、毎日、何かの警告が出ていたということです。
場面2:警告を数えて、分ける
rkhunter の記録(/var/log/rkhunter/rkhunter.log)から、いちばん最後に動いた回の警告を取り出しました。最初の部分です。
Warning: The file properties have changed:
File: /usr/sbin/chroot
Current inode: 100663630 Stored inode: 100918921
Warning: The file '/usr/sbin/ifconfig' exists on the system, but it is not present in the 'rkhunter.dat' file.
Warning: The file properties have changed:
File: /usr/sbin/init
Current inode: 102840008 Stored inode: 101112687
Warning: The file properties have changed:
File: /usr/sbin/sshd
Current inode: 101384421 Stored inode: 101283336
Warning: The file properties have changed:
File: /usr/bin/cat
Current inode: 67242468 Stored inode: 67765169警告は3種類ありました。数を出すため、種類ごとに一覧のファイルを作りました。
myserver で実行したコマンドです。
mkdir -p ~/work/rkhunter
sudo awk '/Start Rootkit Hunter Scan/{buf=""} {buf=buf"\n"$0} END{print buf}' /var/log/rkhunter/rkhunter.log > ~/work/rkhunter/last-scan.txt
grep -E "^\s+File: " ~/work/rkhunter/last-scan.txt | awk '{print $2}' | sort -u > ~/work/rkhunter/changed-files.txt
grep -oE "The file '[^']+' exists" ~/work/rkhunter/last-scan.txt | cut -d"'" -f2 | sort -u > ~/work/rkhunter/new-files.txt
echo "警告の数:$(grep -c 'Warning:' ~/work/rkhunter/last-scan.txt)"
echo "属性が変わったファイル:$(wc -l < ~/work/rkhunter/changed-files.txt)"
echo "記録にないファイル:$(wc -l < ~/work/rkhunter/new-files.txt)"sudo awk '/Start Rootkit Hunter Scan/{buf=""} {buf=buf"\n"$0} END{print buf}': 記録のファイルを上から読み、「Start Rootkit Hunter Scan」の行が来るたびにためた中身を空にする。最後まで読んだら、ためた中身(=いちばん最後の回)を出すgrep -E "^\s+File: " … | awk '{print $2}' | sort -u: 「属性が変わった」の警告の下のFile:の行から、ファイルの名前だけを、重なりなしで一覧にするgrep -oE "The file '[^']+' exists" … | cut -d"'" -f2: 「記録にない」の警告から、ファイルの名前だけを取り出すgrep -c、wc -l <: 警告の数と、2つの一覧の数を出す
結果です。
警告の数:64
属性が変わったファイル:56
記録にないファイル:7| No. | 警告 | 数 | 中身 |
|---|---|---|---|
| 1 | The file properties have changed | 56 | ファイルの属性が変わった。どれも、変わったのは inode(ファイルの番号)だけ |
| 2 | … is not present in the ‘rkhunter.dat’ file | 7 | rkhunter の記録(rkhunter.dat)にないファイルがある |
| 3 | The SSH and rkhunter configuration options should be the same | 1 | SSH と rkhunter の設定が合っていない |
inode は、ファイルを置いた場所の番号のようなものです。dnf で更新すると、ファイルは書き換えではなく「新しいファイルを置いて入れ替える」ので、中身が正しくても番号が変わります。sshd や cat のように、よく更新されるパッケージのコマンドばかりが並んでいるのも、それと合います。
場面3:本当に更新の分かを、パッケージの記録で確かめる
「dnf の更新の分だろう」と思っても、確かめずに記録を取り直すと、本当に書き換えられたファイルまで「正しい」として記録してしまいます。そこで、パッケージの記録(rpm)とくらべました。rpm は、入れたパッケージのファイルごとに、大きさ・指紋(ハッシュ)・持ち主・権限などを覚えています。
myserver で実行したコマンドです。
while read -r f; do sudo rpm -Vf "$f" 2>&1 | grep -F "$f" | grep -vE "^\.{8}\s+c "; done < ~/work/rkhunter/changed-files.txt
while read -r f; do printf '%s : %s\n' "$f" "$(rpm -qf "$f" 2>&1)"; done < ~/work/rkhunter/new-files.txtwhile read -r f; do … done < …changed-files.txt: 属性が変わった56のファイルを、1つずつ$fに入れて調べるsudo rpm -Vf "$f": そのファイルを入れたパッケージについて、パッケージの記録と今のファイルをくらべる。合っていれば何も出ない。違えば、違う点の印(5は指紋、Sは大きさなど)とファイル名が出るgrep -F "$f" | grep -vE "^\.{8}\s+c ": そのファイルの行だけにし、設定ファイル(c)の細かい違いは外すrpm -qf "$f": 記録にない7つのファイルが、どのパッケージのものかを出す
1つ目のくらべは、何も出ませんでした。56のファイルは、どれも入れたパッケージの中身のとおりです。2つ目の結果です。
/usr/bin/ed : ed-1.14.2-12.el9.x86_64
/usr/bin/locate : mlocate-0.26-30.el9.x86_64
/usr/bin/netstat : net-tools-2.0-0.64.20160912git.el9.x86_64
/usr/bin/strace : strace-6.12-1.el9.x86_64
/usr/local/bin/perl : ファイル /usr/local/bin/perl はどのパッケージにも属していません。
/usr/sbin/ifconfig : net-tools-2.0-0.64.20160912git.el9.x86_64
/usr/sbin/route : net-tools-2.0-0.64.20160912git.el9.x86_647つのうち6つは、rkhunter を入れたあとに入れたパッケージ(ed、mlocate、net-tools、strace)のものでした。あとから入れたので、6月の記録にないだけです。
残りの /usr/local/bin/perl だけは、どのパッケージのものでもありません。中を見ると、/usr/bin/perl へのリンクでした。
myserver で実行したコマンドです。
ls -l /usr/local/bin/perlls -l /usr/local/bin/perl: ファイルの種類・持ち主・日時と、リンクならリンク先を出す
結果です。
lrwxrwxrwx. 1 root root 13 6月 6 15:05 /usr/local/bin/perl -> /usr/bin/perl作られたのは、Tripwire を入れた日でした。そのときに作ったものと見て、そのまま残し、次の記録に入れて見張りの対象にしました。
これで、64の警告のうち63は「dnf の更新」と「あとから入れたもの」で説明がつき、危ないものはないと分かりました。
場面4:SSH の設定を合わせ、記録を取り直す
残りの1つ、「SSH と rkhunter の設定が合っていない」を見ました。
myserver で実行したコマンドです。
sudo sshd -T | grep -i "^permitrootlogin"
sudo grep -E "^ALLOW_SSH_ROOT_USER" /etc/rkhunter.confsudo sshd -T | grep -i "^permitrootlogin": SSH の今の設定から、root でのログインを許すかの行を出すsudo grep -E "^ALLOW_SSH_ROOT_USER" /etc/rkhunter.conf: rkhunter が「SSH はこうなっているはず」と思っている設定の行を出す
結果です。
permitrootlogin no
ALLOW_SSH_ROOT_USER=unsetSSH は root のログインを止めている(no)のに、rkhunter の設定は「決めていない」(unset)のままでした。rkhunter は、この2つが違うと警告を出します。rkhunter の設定を、SSH に合わせて no にしました。
設定を直し、記録を取り直して、もう一度調べます。直す前の設定と記録の控えを先に取りました。調べるのに数分かかるので、tmux の中で動かしています。
myserver で実行したコマンドです。
sudo cp -p /etc/rkhunter.conf /etc/rkhunter.conf.20261101
sudo cp -p /var/lib/rkhunter/db/rkhunter.dat /var/lib/rkhunter/db/rkhunter.dat.20261101
sudo sed -i 's/^ALLOW_SSH_ROOT_USER=unset$/ALLOW_SSH_ROOT_USER=no/' /etc/rkhunter.conf
sudo diff /etc/rkhunter.conf.20261101 /etc/rkhunter.conf
sudo rkhunter --config-check && echo "config OK"
sudo rkhunter --propupd --nocolors
sudo rkhunter --check --skip-keypress --report-warnings-only --nocolors > ~/work/rkhunter/check-after.txt 2>&1; echo "rc=$?"
cat ~/work/rkhunter/check-after.txtsudo cp -p …rkhunter.conf …、…rkhunter.dat …: 直す前の設定と、6月の記録の控えを取るsudo sed -i 's/^ALLOW_SSH_ROOT_USER=unset$/ALLOW_SSH_ROOT_USER=no/': 設定の1行だけを書き換えるsudo diff: 変わった行だけを出すsudo rkhunter --config-check: 設定の書き方に誤りがないかを調べるsudo rkhunter --propupd --nocolors: 見張るファイルの今の属性を、記録し直すsudo rkhunter --check --skip-keypress --report-warnings-only --nocolors > … ; echo "rc=$?": 毎朝と同じ形で、もう一度調べる。警告だけをファイルに書き、終わりの番号を出す(0 なら警告なし)cat …check-after.txt: 警告の中身を出す
結果です。
314c314
< ALLOW_SSH_ROOT_USER=unset
---
> ALLOW_SSH_ROOT_USER=no
config OK
[ Rootkit Hunter version 1.4.6 ]
File updated: searched for 176 files, found 134
rc=0--propupd は、見張る176のファイルのうち、このサーバーにある134を記録しました。そのあとの調べは rc=0 で、警告のファイルは空でした。警告が 0 になりました。
1つ、記録を取り直すときに大事な設定があります。このサーバーの rkhunter は PKGMGR=RPM になっていて、パッケージのファイルは、rpm の記録の値で記録します。今のファイルをそのまま正しいとするのではなく、「パッケージが本来持っている値」で記録するので、もし書き換えられたファイルがあれば、次の調べで警告が出ます。
myserver で実行したコマンドです。
sudo grep -E "^PKGMGR" /etc/rkhunter.confsudo grep -E "^PKGMGR" /etc/rkhunter.conf: rkhunter が、パッケージの記録を使う設定になっているかを見る
結果です。
PKGMGR=RPM場面5:これからは、自動更新のあとに記録を取り直す
このままでは、次の朝の自動更新でファイルが入れ替わり、また警告が出始めます。入れたときの記事でも「警告が来たら、更新のファイルか確かめてから --propupd で記録し直す」と書いていましたが、手でやる形だったので、結局一度もやらないまま、警告がたまっていました。
そこで、dnf の自動更新のサービスが終わったあとに、--propupd を自動で動かすようにしました。自動更新のサービスには、更新のあとに Apache や cloudflared を再起動する設定がすでにあったので、それには触らず、ファイルを1つ足します。
myserver で実行したコマンドです。
D=/etc/systemd/system/dnf-automatic-install.service.d
printf '[Service]\nExecStartPost=/usr/bin/rkhunter --propupd --nocolors --quiet\n' | sudo tee $D/rkhunter-propupd.conf
sudo systemctl daemon-reload
systemctl cat dnf-automatic-install.service | grep -E "^# /|ExecStart"D=/etc/systemd/system/dnf-automatic-install.service.d: 自動更新のサービスに、設定を足すためのフォルダprintf '[Service]\nExecStartPost=…\n' | sudo tee $D/rkhunter-propupd.conf: 「サービスが終わったあとに、rkhunter の記録を取り直す」という2行のファイルを作る。--quietは画面に何も出さないsudo systemctl daemon-reload: systemd に、足した設定を読み直させるsystemctl cat … | grep -E "^# /|ExecStart": サービスの設定を、足したファイルも含めて出し、ファイルの名前と、動かすものの行だけにする
結果です。
[Service]
ExecStartPost=/usr/bin/rkhunter --propupd --nocolors --quiet
# /usr/lib/systemd/system/dnf-automatic-install.service
ExecStart=/usr/bin/dnf-automatic /etc/dnf/automatic.conf --timer --installupdates
# /etc/systemd/system/dnf-automatic-install.service.d/override.conf
ExecStartPost=/usr/bin/systemctl try-restart httpd php-fpm
# /etc/systemd/system/dnf-automatic-install.service.d/restart-cloudflared.conf
ExecStartPost=/usr/local/sbin/cloudflared-restart-if-updated
# /etc/systemd/system/dnf-automatic-install.service.d/rkhunter-propupd.conf
ExecStartPost=/usr/bin/rkhunter --propupd --nocolors --quiet更新のあとに動かすものが3つ並びました。Apache・PHP の再起動、cloudflared の再起動、rkhunter の記録の取り直しの順です。
1つ知っておくことがあります。systemd は、「終わったあとに動かすもの」のどれかが失敗すると、その後ろのものを動かしません。そのときは記録が取り直されず、次の朝に rkhunter のメールが来ます。メールが来たら、何かがおかしいと気づける形です。
最後に、毎朝動いているスクリプトそのものを手で動かして、メールが来ないのを確かめました。
myserver で実行したコマンドです。
sudo /etc/cron.daily/rkhunter; echo "rc=$?"
sudo tail -n 3 /var/log/rkhunter/rkhunter.logsudo /etc/cron.daily/rkhunter; echo "rc=$?": 毎朝のスクリプトを、今そのまま動かす。警告が 0 なら、メールは出ないsudo tail -n 3 /var/log/rkhunter/rkhunter.log: 記録の最後の3行を出す(調べの始まりと終わりの間に、警告がないかを見る)
結果です。
rc=0
---------------------- Start Rootkit Hunter Scan ----------------------
----------------------- End Rootkit Hunter Scan -----------------------調べの始まりと終わりの間に何もなく、メールも届きませんでした。
場面6:もう1台も同じだった
家には、myserver を仮想マシンとして動かしている土台のサーバー(kvmhost)もあります。こちらにも同じ rkhunter を入れていて、同じように毎朝メールが来ていました。場面2・3と同じコマンドで調べた結果です。
ALLOW_SSH_ROOT_USER=unset
permitrootlogin no
警告の数:57
属性が変わったファイル:56
記録にないファイル:0
=== ほかの種類の警告
Warning: The SSH and rkhunter configuration options should be the same:
=== パッケージと中身が合わないファイル(何も出なければ全部合っている)
=== 記録にないファイルの持ち主のパッケージ属性が変わった56のファイルは、どれもパッケージのとおりで、SSH の設定も同じずれでした。原因はまったく同じです。
kvmhost には、自動更新のサービスに設定を足すフォルダがまだなかったので、フォルダから作りました。そのほかは、場面4・5と同じ手順です。
kvmhost で実行したコマンドです(tmux の中で)。
sudo cp -p /etc/rkhunter.conf /etc/rkhunter.conf.20261101
sudo cp -p /var/lib/rkhunter/db/rkhunter.dat /var/lib/rkhunter/db/rkhunter.dat.20261101
sudo sed -i 's/^ALLOW_SSH_ROOT_USER=unset$/ALLOW_SSH_ROOT_USER=no/' /etc/rkhunter.conf
sudo rkhunter --config-check && echo "config OK"
sudo rkhunter --propupd --nocolors
sudo /etc/cron.daily/rkhunter; echo "rc=$?"
sudo tail -n 3 /var/log/rkhunter/rkhunter.log
D=/etc/systemd/system/dnf-automatic-install.service.d
sudo mkdir -p $D
printf '[Service]\nExecStartPost=/usr/bin/rkhunter --propupd --nocolors --quiet\n' | sudo tee $D/rkhunter-propupd.conf
sudo systemctl daemon-reloadsudo cp -p、sudo sed -i、--config-check、--propupd: 場面4と同じ。控えを取り、SSH の設定を合わせ、記録を取り直すsudo /etc/cron.daily/rkhunter; echo "rc=$?"、tail -n 3: 場面5の最後と同じ。毎朝のスクリプトを手で動かして、警告とメールが出ないかを見るsudo mkdir -p $D: 自動更新のサービスに設定を足すフォルダを作る(kvmhost にはまだなかった)printf … | sudo tee $D/rkhunter-propupd.conf、sudo systemctl daemon-reload: 場面5と同じ1行のファイルを作り、読み直させる
結果です。
config OK
[ Rootkit Hunter version 1.4.6 ]
File updated: searched for 176 files, found 133
rc=0
---------------------- Start Rootkit Hunter Scan ----------------------
----------------------- End Rootkit Hunter Scan -----------------------
[Service]
ExecStartPost=/usr/bin/rkhunter --propupd --nocolors --quietkvmhost も警告が 0 になり、メールは届きませんでした。
まとめ
| No. | 分かったこと・やったこと | 中身 |
|---|---|---|
| 1 | 毎朝のメールの原因 | rkhunter の記録が入れたときのまま。dnf の自動更新で入れ替わったファイルが、毎日全部警告になっていた(myserver 64件、kvmhost 57件) |
| 2 | 危なくないかの確かめ | 属性が変わったファイルは rpm -Vf で全部パッケージのとおり。記録にないファイルは、あとから入れたパッケージのものと、自分で作ったリンク |
| 3 | 設定のずれ | SSH は root のログインを止めているのに、rkhunter は ALLOW_SSH_ROOT_USER=unset。no に合わせた |
| 4 | 記録の取り直し | --propupd。PKGMGR=RPM なので、パッケージの記録の値で取る |
| 5 | これから | 自動更新のサービスが終わったあとに --propupd を動かす1行を足した。警告がない日は、メールが来ない |
見張りの道具は、入れたら終わりではありませんでした。「記録を新しくし続ける仕組み」まで作って、はじめて「メールが来たら何かある」と言える ようになります。毎朝の長いメールを読み流すのが当たり前になると、本当に危ないメールが来たときにも気づけません。
rkhunter を入れたときの手順は「rkhunter を入れたときの記事」にあり、この記事の内容も追記しました。dnf の自動更新の設定は「AlmaLinux 9 の初期設定」に、更新のあとに再起動が要るときの知らせは「再起動が必要なときの知らせ」に、自宅サーバーの記事の一覧は「自宅サーバー構築メモ」にまとめています。



コメント