図書館で本を予約したら、タイミングが被ってしまい一気に5冊も届いてしまいました...。

全部読み切れるかな... そんなyuku_tasです。

Hackerの皆様はいかがお過ごしでしょうか。

経緯

Linux用のアンチウィルスソフト ClamAVPHPプロジェクトに組み込みたくなりました。

Laravelをよく使うので、Validationでついでにウィルスチェックできる clamav-validatorを見つけて、試行錯誤してみたのですが 動く感じがせず。

Github Issueでは 「これは打ち捨てられてるレポジトリじゃないか?」言われてしまっていました...。


ないものは仕方ありません。ClamAVをライブラリを経由せず直接利用し、アップロードされたファイルをその場でスキャンして、不備がある場合は削除する。という設計思想で運用しようと思います。

実装

clamav本体をインストール

私のLinuxサーバのカーネルは

ですが、以下のコマンドで入りました。Amazon Linux2やCentOSならばyum でインストールしましょう。

sudo dnf update
sudo dnf install -y clamav clamav-update clamd
clamdが今回の方法のポイント

本体はあくまでclamavですが、clamdデーモンとして常時待機 させ、clamscan [file or dir]で、clamd経由でファイルスキャンを行う。という形で運用したいと思います。

定義ファイルを最新に

ウィルスをブロックするための定義ファイルを以下のコマンドで 最新のものに更新しておきます。

sudo freshclam

定義ファイルを自動更新にしておく

cronで以下を設定します。わたしは、ログイン中のsshユーザからcrontab -eで入れました。

N * * * *   /usr/bin/freshclam --quiet

Nのところは、3~57の間にして欲しいと。10の倍数は選択しないで欲しいと公式ドキュメントにも書いてあります 理由は、00分に実行されるスクリプトが多いので、とのことでした。そちらに従って、適当(ちゃんとした意味で)な数字を設定します。

to check for a new database every hour. N should be a number between 3 and 57 of your choice. Please don’t choose any multiple of 10, because there are already too many clients using those time slots.

1時間ごとに新しいデータベースをチェックすることになります。Nは、3から57の間で好きな数字を選んでください。10 の倍数は選択しないでください。これらのタイムスロットを使用するクライアントがすでに多すぎるからです。

freshclam.confを修正

/etc/freshclam.confを変更します。

下記の部分を探し、

# Send the RELOAD command to clamd.
# Default: no
#NotifyClamd /path/to/clamd.conf
NotifyClamd /etc/clamd.d/scan.conf

こちらのように修正して、定義ファイルの更新後に、clamd(デーモンスクリプト)に設定がすぐに伝わるように します。

/etc/clamd.d/scan.confを修正

# ログ出力設定
LogFile /var/log/clamd.scan
LogFileMaxSize 2M
LogSyslog yes
LogRotate yes

# TCPのソケットを使う
#LocalSocket /run/clamd.scan/clamd.sock

TCPSocket 3310
TCPAddr 127.0.0.1

このように設定してログを出したり、clamdが待ち受けるポートやIPなどを定義しておきます。

他、コメントアウトしてある箇所はそのまま変更なしでセーブします。

clamdの設定をしておく

下記がsystemdで管理されているclamdのユニット設定ファイルです。起動後、こう動きなさいというレシピみたいなもの ですね。

/usr/lib/systemd/system/clamd@.service

これをviで開いて、以下のように変更しておきます。(変更点のみ書きます)

sudo vi /usr/lib/systemd/system/clamd@.service

[Unit]
...

[Service]
...
TimeoutStartSec=600
MemoryLimit=512M
CPUQuota=30%

[Install]
...

後述しますが clamscanの処理が重いので、clamdは軽く動くようにメモリの上限値・CPUの上限値を指定して起動する ことにします。

clamdを立ち上げる

ここまで設定を行ってきたclamdsystemctlで起動時に立ち上がるプログラムとして登録し、実際にここで開始しておきます。

sudo systemctl enable clamd@scan
sudo systemctl start clamd@scan

これで、ファイルスキャンが実行できる準備が整いました。

実行してみたが、メモリを使いすぎる

こちらからテスト用のウィルス定義ファイルeicar.comをダウンロードしてFTPを経由して設置します。

もしくは、下記のcurlコマンドを実行して、Linuxサーバに直接落としてくることも可能です。

 curl -O https://secure.eicar.org/eicar.com

定義ファイルを入れたディレクトリに移動し、clamscanコマンドを実行したところマシンが固まってしまいました。

cd /path/to/eicar.com/exist
clamscan eicar.com

どうも、このインスタンスの2GB ではメモリが足りないようです。

スケールアップして、メモリ4GBのマシンで再度試す

そのままでは何もできないので、実験用にマシンをスケールアップしました。

その結果、実行中に3GBくらいはclamscanの実行にメモリを使ってしまう感じですが、30秒くらいで結果が出ました。

$ clamscan eicar.com
/home/ec2-user/work/eicar.com: Win.Test.EICAR_HDB-1 FOUND

----------- SCAN SUMMARY -----------
Known viruses: 8683058
Engine version: 0.103.9
Scanned directories: 0
Scanned files: 1
Infected files: 1
Data scanned: 0.00 MB
Data read: 0.00 MB (ratio 0.00:1)
Time: 27.969 sec (0 m 27 s)
Start Date: 2024:01:23 02:32:20
End Date:   2024:01:23 02:32:48

成功?してウィルスとして判定されたようです。

PHPからウィルススキャンソフトとして使う時のユースケースは?

clamscanコマンドをシェルなどを経由して叩き、標準出力に出た結果を受け取って、preg_matchなどの正規表現で判定する形になるかな、と思っています。

例えば、Laravelのファイル置き場はstorage/app/data以下などになりますので、

clamscan /storage/app/data/upload/xxxx.zip

呼び出すシェルスクリプトをLaravelのバッチから呼ぶといったイメージでしょうか。「Infected files」が1以上ならウィルスとして削除する、という判定はできるでしょう。

本当はkissit/php-clamav-scanのようなライブラリがあるといいのですが 残念ながらメンテされていないようですので、直接呼ぶ方向になりそうです。

今回は実験まで

ここまでの経緯でご説明した通り、2GBのマシンで動かないのはキツく、気軽に導入するわけには行かなそうでした。よって、別の方法でウィルススキャンする方法を考えるようにしました。

参考URL

以下のサイト様を参考にさせていただきました。(他にもあるかもです、気づき次第追加させていただきます。)

https://www.fumibu.com/use_clam-anti-virus_debian-package/#toc4
https://souiunogaii.hatenablog.com/entry/clamd-memory-limit
http://safe-linux.homeip.net/mail/clamav/clamd.html
https://blog.kamata-net.com/archives/12891.html

まとめ

実行ユーザーは今回は実用しないということでrootでやってしまいましたが、商用利用する場合は clamavユーザーなどを作り、rootで実行させないようにした方が安全だと公式ドキュメントに書いてありました。 実際に使う機会があればその方向でユーザーを作る形で考えたいと思います。

その場合、パーミッションの設定は残念ながらちょっと面倒そうでしたね。


この記事が何かのお役に立てれば幸いです。
最後までお読みいただきありがとうございました!