Qmail-Scanner (アンチウィルス後編)

必要なもの

Qmail-Scanner ホームページ: Qmail-Scanner - An Content Scanner for Qmail または SourceForge.net: Project Info - Qmail-Scanner: Content/Anti-virus Scanne 。2005/1/28, Ver.1.25 が出た。 ClamAV-0.80 以降と協調させるなら、それ用のパラメータ・ルーティンがきちんとアップデートされた 1.24 以上を使わなければならない
maildrop maildrop は本来、グループウェア/IMAPサーバである Courier のメール配送/フィルタリングエージェント部分。Qmail-Scanner が利用するのは、そのさらに一部である reformime だけで、これはMIMEをほどいたり再構成するのに使われる。Courier 本家の Download リンクから辿るか、直接 Sourceforge.net で入手
unzip Qmail-Scanner が添付 zip ファイルをほどくのに利用する。RedHat系なら RPM を利用。オフィシャルサイトは Info-ZIP Home Page。インストール説明は省く
TNEF MS の Outlook と Exchange サーバが使いたがる application/ms-tnef 添付を開くのに必要
suidperl Perl を RPM などでバイナリインストールした場合には /usr/bin/suidperl がないかも知れない。RedHat系では perl-suidperl という rpm パッケージで配布されている
qmail-scanner-1.25_jp.patch

qmail-scanner-1.23_jp.patch
筆者が作った日本語化補完パッチ。1.24, 1.23 にも使用できる。Qmail-Scanner は、警告メールを日本語にするところまでは面倒を見てくれるが、ヘッダを MIME エンコードするところまで手が回っていないので、警告メールのタイトルなどが文字化けする。そこで、メインスクリプトにコードを追加した。ついでに、日本語訳が少々雑だった報告メールの文面も少し手直しした。Perl モジュール Jcode および MIME::Base64 が必要となる。
[2005/4/8 更新: 1.25_jp パッチでは警告メール本文を元の EUC から ISO-2022-JP (= jis) に変換してから送るように改良。ただし、これによりコードの変更箇所が多くなったため、他のパッチが当たらなくなる。それでは困る人は、本文を EUC のまま送る 1.23_jp.patch を使うか、下記の 1.25_jp_lsender 統合パッチを利用のこと]
qmail-scanner-1.23_lsender.patch

qmail-scanner-1.25_jp_lsndr.patch
筆者が作ったパッチ (2004/08/20 更新)。1.25, 1.24 にも使用できる。現在のところ Qmail-Scanner は、送信者への警告メールについては、外部も含めた誰にでも送るか、全く送らないかのどちらかしか選択できない (他にポリシーがらみは1つあるが)。このパッチを使えば、変数 notify の設定如何で、ローカルに属する送信者、あるいは、なお且つポリシーによりメールがブロックされた場合だけに警告を絞ることも可能となる。
[2005/4/8 追加: 下のパッチは前述の 1.25_jp との統合パッチ]
以下は Perl モジュール www.perl.org/CPAN/
主な CPAN FTP サイト: ftp.cpan.org, ftp.ring.gr.jp, ftp.kddlabs.co.jp, ftp.jaist.ac.jp, ftp.ayamura.org
Time::HiRes ナノセカンド精度のタイマーモジュール。モジュール分類は Time。Fedora-Core の場合は perl-Time-HiRes という rpm も配布されている
DB_File バークレーDB のハンドリング。たいていはインストールされている。モジュール分類は DB_File
Sys::Syslog たいていはインストールされている。(モジュール分類不明)
Jcode 前出の jp パッチで使用している、かの有名な日本語処理モジュール。分類 Encode の DANKOGAI ディレクトリにある
MIME::Base64 前出の jp パッチで必要。分類は MIME

Perl 関係のインストール

suidperl の確認

"which suidperl" で実行ファイルのパスを確認し、そのパーミションが root 所有の rws--x--x(4711) であることを確認。違ったら調整。Fedora Core 3 ではデフォルトでは 0755 になっており、そのままで問題なく動いた。なければインストールしておく。

モジュールの追加

各モジュールがインストールされているか調べる。"perl -M<module_name>" してみてエラーが出るかどうか見てみる手もあるが、CPAN シェルを起こして調べるのがスマート。実は、CPAN シェルを使えば、 Debian の apt ばりに CPAN サイトからモジュールソースが簡単に取得/インストールできるので、個々にダウンロードするという手間も省ける。なお、CPAN シェルを初めて使用する時には、初期設定ウィザートが走るので驚かないように。 CPAN シェルでモジュールの有無を調べるには以下:

root# perl -MCPAN -e shell
cpan> i Time::HiRes
cpan> i DB_File
cpan> i Sys::Syslog
cpan> i Jcode
cpan> i MIME::Base64

上記 i コマンドはモジュールの情報を見るものだが、INST_FILE の項目が (not installed) になったものは、未インストールということなので、インストールが必要だ。要領は:

 まず、モジュールの依存関係を満たすために必要な関連モジュールもインストールするよう cpan に指示
cpan> o conf prerequisites_policy follow
 無かったものをダウンロード及びインストール
cpan> install Time::HiRes
 確認
cpan> i Time::HiRes
 以下、他のモジュールも同様に
別途ダウンロードして手動インストールする場合

一般的共通的な流れは下記 (詳しくはそれぞれのソースに付属する README などを参照)。モジュールソースを解凍して cd して、

root# perl Makefile.PL
root# make
root# make test
root# make install 

maildropのインストール

ソースをほどいて cd して su して、

root# ./configure --mandir=/usr/share/man
root# make
root# make install-strip
root# make install-man

肝心の reformime は /usr/local/bin/ にインストールされる。

TNEFのインストール

root# ./configure --mandir=/usr/share/man
root# make
root# make check
root# make install

インストールされるのは /usr/local/bin/tnef バイナリと tnef man ひとつとのみ。

Qmail-Scannerのインストール

やっと御本尊のインストールに取りかかれる。
まず、qscand ユーザ/グループが必要。本稿では ClamAV のインストール時に作成済み。

日本語化補完パッチの適用

qmail-scanner-1.2x_jp.patch は Qmail-Scanner ソースディレクトリにあるファイルのうち:

に修正を掛ける。よって、Qmail-Scanner ソースディレクトリに cd して、

patch -p1 < PATH_to_qamil-scanner-1.2x_jp.patch

lsenderパッチの適用

昨今、ウィルスメールの送信源はウィルスそのものである場合がほとんどで、そうしたメールは送信元を詐称しているので、警告メールを返信するのは、無駄であるばかりか、警告メール自体が SPAM ばりの迷惑となるし、最悪の場合、メーリングリストに送る結果となり一種のメール爆弾と化してしまう可能性も高い。それならと送信者への警告をしないように設定すると、今度はサーバの正規ユーザが発信したメールまでもが、何の通知もなくブロックされてしまう。現時点の Qmail-Scanner では、このどちらかしか設定できない。困った欠陥だ。

そこで作成したのが qmail-scanner-1.23_lsender.patch (04/08/20 更新)。これは、新たな notify (警告通知先) パラメータとして "lsndr" と "phsndr" を使用可能にする。詳しい説明は次の項目に預けるが、非常におすすめ。スキャンのオンオフに関する課題にも関連する。このパッチは qmail-scanner-queue.template へのパッチ処理を行うが、configure 時の help 記述追加とオプションパラメータチェック変更のために configure スクリプトにも記述を追加している。パッチの当て方は前述の jp パッチと同じ。 1.25_jp パッチとの多重適用はソースの都合上難しいので、両方掛けたい場合は 2 つを統合した qmail-scanner-1.25_jp_lsndr.patch を使用していただきたい。

コンパイルとインストール

Qmail-Scanner は Perl スクリプトなので、正確には "コンパイル"ではない。実際に行われるのは ./configure シェルスクリプトが、渡した引数に応じた locale/ 下の警告メール文や sub-<Anti-Virus_prog_name>.pl を、根幹部分である qmail-scanner-queue.tamplate と合体させて qmail-scanner-queue.pl を完成させるという仕業だ。

./configure は root になってからでないと走らないようにできている。また、システムにインストールされているアンチウィルスプログラムを自動検出するルーティンが組まれているので、clamd などのデーモンは走らせておき、単独プログラムの類はきちんと起動できる状態に整備しておかなくてはいけない。 ./configure で指定すべき引数は --help オプションで呼んでやれば見られるが、一般的には以下の引数を与えればいいだろう。一部を除いては、あとで qmail-scanner-queue.pl 内の変数定義を直接編集すれば直せるのであまり神経質になる必要はない。

--lang ja_JP.EUC --admin antivir --notify recips,lsndr,nmladm --domain mail.hoge.cxm --local-domains mail.hoge.cxm,host1.hoge.cxm,hoge.cxm --install

カンマで区切るような値は間にスペースを入れてはならず、また、値は " " でくくらないほうがいい。--lang は、ソースの警告メール文が収められている locale/ 下のディレクトリ名に呼応する (ここはあとで直すわけにはいかない重要項目)。--admin と --domain は、組み合わせられて警告メールの差し出し人 (完成スクリプトの $V_FROM 変数) とウィルス検出時のログメールの送信先 ($QUARANTINE_CC) に反映。--notify は、ウィルス検出時に警告メールを誰に送るかをカンマで連ねたもの ($NOTIFY_ADDRS) で、recips は元のメールを受信するはずだった宛先。この「受信するはずだった宛先」は、--local-domains (@local_domains_array) に属するユーザだけが対象となる。admin は $V_FROM とイコールだが nmladm とすると、宛先がメーリングリストなど自動システムでなかった場合のみ報告が来る。

psender は、送信者だが、ポリシーによってメールが差し止められた場合のみ通知する。すべての送信者を指す "sender" だけはやめたほうがいい -- ウィルス自体が自動送信してきたメールは送信元を詐称しているので返信不可能で、こちらのメールキューが再送待ちメールで溢れてしまうからだ。これらに加えて、lsndr (local sender の略) は前述の lsender パッチで新たに追加したパラメータで、送信元のうちでも、recips などと同様に @local_domains_array に属するメールユーザだけに絞ることができる。もうひとつ phsndr も使えるようになり、こちらは "policy home sender" から作ったパラメータ名で、lsndr の条件プラス、ポリシーによってブロックされた場合のみの通知となる。これら追加のパラメータは、どちらも、nmladm 同様に自動化システムには返信しない (nmladm などが使用している元々のルーティンを使っている)。

注: admin のアドレスは qmail の alias ユーザとして作成するなどして実際に受け取れるようにしておくか、警告メールの文面に 「この警告メールに返信しても無駄」 の文言が入るようにメインスクリプトを改造したほうがいいだろう。

上記のように --install も指定していれば、メインスクリプトのパースとともにインストールも行われる。インストール後、念のため /var/qmail/bin/qmail-scanner-queue.pl をエディタで開いて、109 行目あたりの @scanner_array 変数を確認しておこう。 ("clamdscan_scanner") となっていれば、clamdscan がきちんと検出されている。この変数 (配列) の値は、configure で指定できるアンチウィルスプログラム名に _scanner を付けたもので、複数の場合は ("*_scanner","*_scanner") のようになる。うまくいっていない場合は、もう一度、引数に --scanners clamdscan を加えて configure を行ってみるべし。

補足: "--scanners clamscan,clamdscan" と指定することはできない。Qmail-Scanner の configure スクリプトは 「clamsdscan のほうが良いから clamscan は使わないぞ」 とカタクナだからだ。これについては後で手当てをする。

/var/qmail/bin/qmail-scanner-queue.pl の他に、/var/spool/qmailscan/ ディレクトリ ($scandir 変数で定義) の下にもいくつかのものがインストールされる。

Qmail-Scannerインストール後の動作確認

qmail-scanner-queue.pl は後でいじることになるが、確認のため、まずはそのまま動作テスト。ソースディレクトリの contrib/test_installation.sh を走らせると、4通のテストメールが処理され $V_FROM 宛てにメールが届くはずだ。同時に、処理のログが、デフォルト設定のままの現時点では $scandir/qmail-scanner.log に書き出される。

qmail-scanner-queue.pl の調整

ClamAV のセクションで触れたが、clamd + clamdscan は扱えるアーカイブ形式にかなりの制限があるため、単体のほうの clamscan に切り替えたくなる可能性もあるかもしれない。その場合は変数 $scanner_array の値を ("clamscan_scanner") に変えればいいわけだが、実はそれだけでは済まない。Qmail-Scanner は、完成するスクリプトのサイズを抑えるために、各アンチウィルスプログラム専用のサブルーティンについては configure 時に有効と判断されたプログラムの分だけしかパースしない。つまり、現時点でのスクリプトには、そもそも clamscan_scanner という必要なサブルーティンが欠けているのだ。しかし手当ては簡単。ソースディレクトリ直下にある sub_clamscan.pl の内容を丸ごと、qmail-scanner-queue.pl に貼り付ければよい。末尾附近にある clamdscan_scanner サブルーティンの近く (つまりスクリプトの最後) に置くのが自然ではなかろうか。

Qmail-Scannerの設定

つまり、qmail-scanner-queue.pl 自体の変数定義セクションを編集する。ブーリアン値は真が 1, 偽が 0 。主な変数は:

変数名 意味 推奨設定例
$V_FROMNAME 警告メールの差出人の表示名 "hoge.cxm Anti-Virus System"
$BLOCK_PASSWORD_PROTECTED_ARCHIVES パスワードのかかった zip ファイルが添付されていた場合に、否応なしにウィルスとして扱うか '0'
$redundant_scanning 解凍できたファイルとともに、未解凍のファイルと生のメールコンテンツもウイルス走査プログラムに渡すか '1'
$log_details 感染メールを検出した際にログを syslog で処理するなら指定 'syslog'
$clamscan_options clamscan を掛ける際のオプションを記述 ※下記
$clamdscan_options clamdscan に渡すオプション。 1.23 までは、デフォルトでは無効なオプションがてんこ盛りなので、右記のように修正すべし。 その他の clamd 詳細設定は clamav.conf で行う "--no-summary"
$force_unzip zip ファイルは必ず解凍してからアンチウィルスに渡すか '0'
$DEBUG TRUE にしておくと、$scandir/qmail-queue.log に処理内容が吐き出される。このログは無尽蔵に大きくなっていくので、初期テストが終わったら、ログファイルを削除するとともに、この変数は FALSE に '0'
clamscanに渡すオプションについて

clamscan のオプションは ClamAV のバージョンによってどんどん変わっているようで、Qmail-Scanner にデフォルトで書いてあるものが正しいとは限らない。長期間の検証はできていないが、$clamscan_options は以下のような記述でよさそうだ:

"-r --tempdir=/var/clamd --unzip --unrar --unzoo --lha --no-summary --max-recursion=10 --max-space=10M"

主なclamscanオプション一覧

-r, --recursive ディレクトリの階層を再帰的に走査するかどうか。多重圧縮されたファイルの検査にも影響する
--mbox mbox形式、Maildir, 生 (raw) メールファイルのスキャンを可能に。ClamAV 0.80 ではこのオプションは廃止され、メールスキャンは規定動作となった
--no-ole2 OLE2 添付を走査しない
--max-files=n 各圧縮ファイルの先頭から最大何個を解凍/走査するか
--max-space=n 各圧縮ファイルの先頭から最大何Kバイトを解凍するか。 "nM" と指定するとメガバイト単位で指定可能
--max-recursion=n 多重圧縮されたファイルを再帰的に解凍・展開する際の最大深度
--no-summary スキャン結果の詳細を return しない
--tempdir=dir テンポラリファイルの書き出しに使用するディレクトリを指定
以下はアーカイブ形式の追加サポート指定 "=path" は指定するならそのバイナリのフルパスを記述
--unzip[=path] info-zip を解凍。zip ファイルは標準でサポートされているので通常は指定する必要はない。うまくいかない場合に指定
--unrar[=path] rar を解凍。 rar 2.0 は標準でサポートしているので、それだけなら指定は不要だ。 path に unrar 3.0 バイナリを指定しておくと、内蔵の rar コードでの解凍に失敗した場合にそれが使われる
--unace[=path] ace を解凍
--unarj[=path] arj を解凍
--unzoo[=path] zoo を解凍
--lha[=path] lha を解凍
--jar[=path] unzip を使って jar を解凍
--deb[=path] ar を使って deb を解凍。これを有効にすると暗黙的に下記の --tgz も有効となるが、両方指定しても問題はない
--tar[=path] tar を解凍
--tgz[=path] tar を使って tgz, tar.gz を解凍

ポリシーファイル quarantine-attachments.txt の調整

Qmail-Scanner には、ClamAV など外部の検査プログラムにメールコンテンツを処理させる他に、各種ヘッダ内容や添付ファイルのファイル名や拡張子に基づいてメールをストップさせる perlscanner (perlscan_scanner) というルーティンを持っている。このルーティンは、外部の検査プログラムを (複数指定してあればすべて) 実行した結果、不正の烙印を押されなかったメールに対して実行される。perlscanner のためのルールを書いておくファイルが $scandir/quarantine-attachments.txt で、実際の使用に際しては、このテキストファイルを元に生成したバークレーDB形式のバイナリファイル quarantine-attachments.db が読み込まれる。

quarantine-attachments.txt の文法には大まかに分けて 2 種類ある。フィールド間はスペースでなく必ずタブalarm_description は警告メールの本文に記述される文言だ:

添付ファイルのフィルタ
file_name    size    alarm_description

例 1:

 Happy99.exe    10000    Happy99 Trojan 

例 2 - すべての .exe ファイルにマッチ。この形式ではサイズフィールドは無視されるので 0 に

 .exe    0    You can not attach EXE files!
ヘッダによるフィルタ
REGEX_string    Virus-header_type    alarm_description

第1フィールドの正規表現句は、例えば [Cc]reate ([Yy]our)* [Ll]ogo とした場合、^[Cc]reate ([Yy]our)* [Ll]ogo$ つまり「頭から終わりまで」と解釈されるので注意。第2フィールドは、例えば From: ヘッダの語句をフィルタに掛けるつもりなら Virus-From:Reply-To: ヘッダなら Virus-Reply-To: といった具合。

変更を加えたら必ず "/var/qmail/bin/qmail-scanner-queue.pl -g" を実行して quarantine-attachments.db を再構築する。

QMAILQUEUE環境変数のセット

どこでセットするかは様々だ。tcpserver だけか、daemontool を使っているか、すべてのメールを Qmail-Scanner に掛けるかによっても違ってくる。qmail を当ホームページでの説明に沿って設定しているなら、tcpserver (ucsip-tcp) 経由でセットするのが妥当だ。つまり /etc/service/tcp.smtp ファイルを以下のように編集する:

127.0.0.1:allow,RELAYCLIENT=""
:allow,QMAILQUEUE="/var/qmail/bin/qmail-scanner-queue.pl"

上記は、外からの SMTP セッションの場合のみキュープロセスを Qmail-Scanner に迂回させる。セットしたくない場合には、例えば:

127.0.0.1:allow,RELAYCLIENT="",QMAILQUEUE=""

ではダメ で、明示的にキュープロセスの迂回をやめさせたいなら:

127.0.0.1:allow,RELAYCLIENT="",QMIALQUEUE="/var/qmail/bin/qmail-queue"

と本来のキュープロセスを指定しなければならない。

もちろん、cdb を更新しておくのをお忘れなく。

Qmail-Scanner 使用/不使用 の切替えに関する課題

本当ならば、受信メールだけスキャンして、送信メールはスキャンさせたくない。というのも、送信メールをブロックした際の通知に困るからだ。しかし、上記の設定では、どうしても両方ともスキャンされてしまう。 Qmail-Scanner にもそういった選別機能はない。Qmail-Scanner の FAQ で少し触れられているのは、 qmail-smtpd をふたつ立ち上げておき、メインのほうはスキャンを有効にし、もう一方はスキャンなしに設定し、DNS の MX レコードでIPを使い分けるという手段だが、IP をひとつしか持てないダイナミックDNS環境ではそうもいかない。可能性は、ポートによる使い分け....?

※ 通知に関しては自作パッチにて解決した。

隔離された不正メールの処置

Qmail-Scanner も、そしてこの使い方での ClamAV も、汚染されたメールを勝手に闇に葬り去るわけではない。それらのメールは Qmail-Scanner が $scandir/quarantine/ に保管し、放っておけばどんどん溜まってしまう。このディレクトリは qmail でおなじみの Maildir そのものだ。よって、内容を確認したりユーザに転送する必要が生じた場合には、例えば Mutt であれば (フォルダのパーミション上 root になってから):

mutt -m Maildir -f /var/spool/qmailscan/quarantine

すれば簡単に作業ができる (-m Mairdir は Mutt が Maildir 用にきちんと設定してあれば不要)。削除も Mutt 上からやれば比較的簡単だが、いちいち面倒なので、cron に登録して自動化しておくのが常套手段。その際のスクリプトには find コマンドを使うと良い。(小技集 のところでもっと詳しく書いていて、汎用スクリプトも作った)

find /var/spool/qmailscan/quarantine/ -type f -mtime +10 -exec rm -f '{}' ';'

上記のコマンドは、作られてから10日以上経過したファイルを quarantine ディレクトリ内のすべてのサブディレクトリから見つけて削除する。1日1回実行されるように cron に登録しておけば良い。