アンチウィルス

Qmail-ScannerClamAV を組み合わせて、ウィルスを含むメールをメールサーバ内でストップする。ウィルス検出処理の本体業務を行うのが、 GNU プログラムであるフリーの ClamAV 。検出力では商用プログラムに引けを取るかも知れないが或る程度でもブロックし、残りはクライアントに任せるという考え方を採ることにする。Qmail-Scanner は、メールの内容をアンチウィルスプログラムに渡す役割をする。処理体系はだいたい下のチャートのようになる:

チャートの補足説明を少し。qmail の本来の動作では、メール内容は qmail-inject あるいは qmail-smtpd から直接 qmail-queue プロセスに送られる。ここで、環境変数 "QMAILQUEUE" をチェックするように qmail を作り替えておくと、別のプロセス/プログラムへ処理を迂回せさることができるようになる。それが QMAILQUEUE パッチだ。

Qmail-Scanner は、メールのヘッダや内容を走査するための perl スクリプト。それ自体では、ウィルス検出はおろか、添付ファイルの解凍能力も MIME 処理能力も持たず、それらの処理はすべて外部プログラムの手を借りることとなる。MIME 処理には、標準では Maildrop というパッケージの中の reformime バイナリが使われるので、これは別途インストールが必要だ。また、解凍は unzip などのパッケージを別途インストールしておかなくてはならない。しかも、Qmail-Scanner はあまり多くのアーカイブフォーマットを解凍対象にしていない。しかし、解凍できたファイルとともに、生 (raw) のままのメールコンテンツ全体もテンポラリファイルに書き出されるように設定できるので、そこはウィルス検出プログラム側でカバーできる。一方、Qmail-Scanner は文字列検索機能には力が入っているので、Qmail-Scanner 自体で単純に添付ファイルの拡張子やヘッダ内容によってメールを隔離することも可能だ。この機能を Qmail-Scanner では perl-scanner と呼び、 perl-scanner のフィルタリングルールを "ポリシー (policy)" と呼んでいる。

ちなみに、Qmail-Scanner がメールコンテンツの処理を「外注」する先はアンチウィルスに限られるわけではなく、SPAM 処理でも良いし、その両方も可能で、SpamAsssassin との併用もよく行われている。

ClamAV にはいくつかの使い方がある。clamscan という単体の検出プログラムもあるが、これをメールスキャンに使おうとすると、呼び出す度にウィルスデータベースを読み込むので、ひとつのメールを処理するだけでも 1-2 秒ほどかかって大変遅い。そこで、デーモンとして常駐する clamd を活用したい。これなら、データベースを読み込むのはデーモン起動の際の1度だけなので、各々のメール走査はずっと高速。そして clamd デーモンのフロントエンドとして実際に走査命令を受けるのが clamdscan だ。ただしどちらも一長一短。 clamd + clamdscan の組み合わせは、高速である反面、単体の clamscan に比べて対応アーカイブフォーマットの種類が限られるのが難点だ。

各々が対応可能なアーカイブ形式 (Qmail-Scanner 1.25, ClamAV 0.86.x 時点)

  標準でサポート オプション指定または外部利用可能
Qmail-Scanner   zip, TNEF
clamscan zip, gzip, bzip2, tar, tgz, rar2.0, MS-OLE2, MS-cab, MS-CHM, MS-SZDD, UPX, FSG1.3-2.0, Petite2.x rar3.0, ace, arj, zoo, lzh, jar, deb
clamd + clamdscan zip, gzip, bzip2, tar, tgz, rar2.0, MS-OLE2, MS-cab, MS-CHM, MS-SZDD, UPX, FSG1.3-2.0, Petite2.x  

なお、ClamAV のウィルスパターン (signature) のアップデートに関しては、日本国内も含めて、数十のデータベースミラーサイトがあり、アップデートプログラム freshclamhttp://database.clamav.net にアクセスすれば、こちらの IPアドレスに基づいて最寄りのミラーが選択されるようになっている。より正確に言うとこれは DNS による仕掛けで、database.clamav.net を名前解決する時点で複数の「候補」 IPアドレスが返ってくる。同時に、回答の中の序列は毎回ランダムに変わるので、ラウンドロビンにもなる。メインサイトとミラーサイトとのデータベース同期は自動化されており、タイムラグは1分以内だという。

qmailへのQMAILQUEUEパッチ

qmail-1.03 でなく netqmail-1.05 でインストールしたのなら、既に QMAILQUEUE パッチは当たっている。qmail-1.03 からのインストールの場合で、当てずにインストールしいていたら、qmailorg.data-hotel.netwww.qmail.org でパッチをダウンロードして qmail-1.03 ソースに patch し (もちろん他に必要なパッチも) 再度インストールを行う。とはいっても、既に一度インストール済みなら、あとは "make setup check" でパーツを上書きするだけで良く、"./config-fast" は行ってはいけない。やってしまったら control/ 下のファイルまで上書きされて設定が台無しになる。一番楽なのは、この際、netqamil-1.05 で刷新してしまうことかもしれない。その時も要領は qmail-1.03 の上書きと同じ。本稿の1ページ目を参考に。

ClamAV

ClamAV が 0.90 へメジャーバージョンアップされた時を機会に、パッケージの RPM化を図ってみた。ここでは、ソースインストールする場合と、筆者の SPECファイルから RPMパッケージをビルドしてからインストールする方法の、ふた手を解説する。 RedHat系のディストリビューション上で動かすなら、RPMパッケージをビルドしたほうがいい。

必要なもの:
clamav-x.xx.x.tar.gz ClamAV パッケージ。 ClamAV オフィシャルサイトからダウンロード。詳しいことは同サイトの Doc & Wiki を探ってみるとよい。
clamav.init RedHat系ディストリビューション用の起動スクリプト。上記ソースの contrib ディレクトリに同梱されていたものをカスタマイズした。下記のSPECファイルを使って RPMパッケージをビルドする際にも必要。

RPMパッケージをビルドする場合は上記に加えて;

clamav.spec RPMforgeのレポジトリからダウンロードしたソースRPMに同梱されていたSPECファイル (Dag Wieers製) を元に、以降で述べるほとんどのカスタマイズを盛り込んだ筆者の特製SPECファイル。

ClamAV の動作条件として以下のものも必要だが、たいていはインストールされているものばかりだ:
zlib, zlib-devel, gcc-2.9.x または 3.x, bzip2, bzip2-devel, GNU MP 3 または 4 (RedHat系rpm でのパッケージ名は "gmp")。蛇足だが、MP は音楽圧縮ファイルの mp3 のことではなく、浮動小数点演算ライブラリで、ウィルスパターンアップデート時のファイルの正当性確認に (絶対ではないが) 必要。

Fedora Core 3 でのコンパイル時には、「 /usr/lib/libidn.so が無い」 というエラーで make が途中で exit してしまった。 libidn は国際化ドメイン名 (IDN) を扱うライブラリのようで、 Fedora Core 3 の rpm では libidn-devel に含まれている。筆者の環境では libidn パッケージはインストールされていたが、開発環境のほうは未導入だったため起こったエラーだと判明した。

さらに、極めてオプショナルな外部プログラムとして Dazuko というものもある。これは、WINDOWS のクライアントPCのようにシステム全体をリアルタイムスキャンする clamuko プログラムが動作するために必要らしいが、今回のメール検索では clamuko は無用なので要らない。

なお、このセクションでは clamd + clamdscan のみ網羅し、単体版 clamscan の動作オプションについては次の Qmail-Scanner のセクションで簡単に触れるだけに留める。デーモンでない clamscan はとにかく動作が遅いのだ。

ClamAV のインストール

ユーザの作成

RPMインストールの場合は自動的に作成されるので必須ではないが、 UID, GID を決め打ちしたい場合は clamav グループとユーザを作成しておく (ID 800 は例に過ぎない);

root# groupadd -g 800 clamav
root# useradd -u 800 -g clamav -s /sbin/nologin \
    -d /var/clamav -m -k /dev/null clamav

こちらは RPMインストールの時にも必要。ここで、本来 Qmail-Scanner のユーザ/グループである qscand も作っておく。Qmail-Scanner が書き出すテンポラリファイルを読ませるには、clamdqscand 権限で起動する必要があるため。

root# groupadd -g 801 qscand
root# useradd -u 801 -g qscand -s /sbin/nologin -m -k /dev/null qscand 

RPMビルドする場合

clamav.spec を環境に合わせて調整。最低でも;

Version:clamav-x.xx.x.tar.gz のバージョンに合わせる。
Release: を自分のディストリビューションに合わせる。(例: 1.fc5)

その他、上記のSPECファイルでは、ビルドオプションに --enable-check が含まれている (ClamAVでは推奨されている) ので、OSにあらかじめ check-devel パッケージをインストールしておく必要がある。そんなパッケージの存在しない古いリリースの OS (Fedora Core 3 など) の場合には、SPECファイルの %build セクションにある `--enable-check \' を削除しておかないとコンパイルできない。check というのは、コンパイル時のユニットテストのフレームワークのようだ。

当ページに掲載している SPECファイルでは、configure 行程での clamuko の無効化と、ソースの docs/html/ 配下をパッケージの docs に含める定義を加えている。clamuko が必要な人は %configure セクションの `--disable-clamuko \' を削除していただきたい。

さて、clamav-x.xx.x.tar.gzclamav.init をビルドツリーの SOURCES にコピー、clamav.specSPECS にコピーしたらパッケージをビルドする;

root# rpmbuild -bb --clean --without milter \
      --target i686-redhat-linux SPECS/clamav.spec

32bit プラットフォームでは、上記のように、デフォルトの i386 でなく i686 用に最適化する `--target i686-redhat-linux' オプションも渡したほうがいい (x86_64 プラットフォームでは不要)。`--without milter' は DAG の SPECファイルに仕込まれたマクロに対するオプションで、これを付けないと make--enable-milter が渡されるため、rpmbuildclamav-milter もコンパイルしようとする。 clamav-milter は sendmail 発祥のメールフィルタAPI に対応したもので、sendmail や Postfix には使えるが qmail では無用。コンパイルしようとすると sendmail-devel パッケージを要求してくるのでそれのない環境ではビルドが成り立たない。

DAG の SPECファイルの仕様により、RPMパッケージは以下のように分割して生成される;

この中で通常必要なのは上の3つ (赤字のもの) のみ。それらをインストールすると、主なパスは以下のようになる。元の DAG の SPECファイルとは少々異なる。 qmail-scanner と協調させるためには下記の赤字のパーミションを設定すること。

/etc/
    \_ clamd.conf              clamd デーモン設定ファイル
       freshclam.conf          パターンアップデータ freshclam 設定
/usr/
    \_ bin/
         \_ freshclam
            clamdscan          clamd デーモンのフロントエンド
            clamscan           デーモンでない単独の検索プログラム
            clamdtop           clamdのステータスをtopに似た画面で表示
    \_ sbin/
         \_ clamd              clamd デーモン
    \_ lib/
         \_ libclamav.so.*     など
    \_ share/
         \_ clamav/            ウィルスDBディレクトリ clamav.clamav 755
                 \_ daily.cvd  最近ダウンロードされたウィルスパターン
                    main.cvd   日数が経つと daily.cvd からこちらに移る
         \_ man/man.*/
                 \_ *          manページ類
/var/
    \_ clamav/                 ユーザ clamav の HOME。特に使用はしない clamav.clamav 700
    \_ run/clamav/             clamd の tmp ディレクトリ兼ソケット作成場所 qscand.clamav 775
                 \_ clamd.pid  clamd デーモンの PID ファイル (勝手にできる)
                    clamd.sock clamdscan との通信に使うUNIXソケット (勝手にできる)
    \_ log/clamav/             ログ用ディレクトリ clamav.qscand 770
    \_ tmp/                    走査時のデータ一時展開先

ソースインストールの場合

アーカイブをほどいてソースディレクトリにて:

root# ./configure options

指定できるオプションは ./configure --help すれば列挙されるが、RedHat的にするなら下のような塩梅。前述の通り、メールスキャンには clamuko は必要ないので --disable-clamuko を足してもいい。 ClamAV のバージョンとシステムの状態によっては、"installed zlib version may contain a security bug" というエラーが出て configure が中止されることがある。 RedHat, Fedora など OS のディストリビューターが配布するアップデートバイナリパッケージは、バグフィクスパッチなどを独自に当ててリリースバージョンだけを上げている場合があるので、zlib のバージョンチェックに引っ掛かることがあるのだ。システムをきちんとアップデートしているのにこのエラーが出る時には、オプションに --disable-zlib-vcheck を加えることで回避できる。その他のオプションについては、前に挙げた SPECファイル%build セクションを参考にするといいだろう。

./configure --sysconfdir=/etc --localstatedir=/var --mandir=/usr/share/man

さて、続いてビルドだが、ClamAV の Makefile は行儀がいいので、 目的の SYSCONFDIR に既に設定ファイル (clamd.conf, freshclam.conf) があると、新しい設定ファイルをコピーしない。よって、アップグレードの場合には既存の設定ファイルをあらかじめリネームしておこう。

user$ make
user$ make check
user$ su
root# make install

前述のオプションを指定した場合にインストールされるもの。/var 下のディレクトリは手動で作成。赤字のパーミションを間違いなく設定すること。

/etc/
    \_ clamd.conf              clamd デーモン設定ファイル
       freshclam.conf          パターンアップデータ freshclam 設定
/usr/
    \_ local/
          \_ bin/
               \_ freshclam
                  clamdscan    clamd デーモンのフロントエンド
                  clamscan     デーモンでない単独の検索プログラム
                  clamdtop     clamdのステータスをtopに似た画面で表示
          \_ sbin/
               \_ clamd        clamd デーモン
          \_ lib/
             \_ libclamav.so.*
          \_ share/
             \_ clamav/          ウィルスDBディレクトリ clamav.clamav 755
                    \_ daily.cvd 最近ダウンロードされたウィルスパターン
                       main.cvd  日数が経つと daily.cvd からこちらに移る
    \_ share/
          \_ man/man.*/
                 \_ *          manページ類
/var/
    \_ clamav/                 clamd のソケット作成場所兼走査データ展開用 qscand.clamav 775
            \_ clamd.sock      clamdscan との通信に使うUNIXソケット (勝手にできる)
    \_ run/clamav/             手動で作成 qscand.clamav 775
               \_ clamd.pid    clamd デーモンの PID ファイル (勝手にできる)
    \_ log/clamav/             手動で作成。ログ用ディレクトリ qscand.clamav 770

clamd設定ファイル clamd.conf の調整

主な設定ディレクティブ。例は、筆者のお勧めする設定値。

設定句 説明 設定例
example 設定せずにそのまま使おうとするウツケモノ避け。必ず取り除くかコメントアウト #example
LogFile clamdに自力でログファイルを書かせる場合のログファイルパス。LogSyslogと併用することも可能だが、ふたつのファイルへ書くことになるので無駄 #LogFile
LogFileMaxSize LogFileで指定したファイルの最大サイズ。これを超えるとclamdはログ書き出しを無効にする。0は制限なしの意 0
LogTime ログの頭に日時を付けるか。LogSyslogを使う場合はsyslogが付けるので要らない no
LogSyslog syslogサービス経由でログを出力する yes
LogFacility LogSyslogの場合のファシリティ。内部デフォルトはLOG_LOCAL6 LOG_LOCAL6
PidFile clamdが自分で吐き出すPIDファイルのパス /var/run/clamav/clamd.pid
TemporaryDirectory clamdが走査時に使用するテンポラリディレクトリ /var/tmp または
/var/clamav
TCPSocket clamdにTCP経由でアクセスする場合のポートナンバーだが、clamdがclamdscanを実行するのと同じマシン上にあるならコメントアウトしておくのが安全 3310
LocalSocket clamdにUNIXドメインソケットでアクセスする場合のソケットファイルパス /var/run/clamav/clamd.sock
あるいは
/var/clamav/clamd.sock
User Qmail-Scannerと協調させるには qscand に変更しておく qscand
Foreground (※) エラーの原因を突き止めたい時、下のDebugとともに有効にするとよい  
Debug (※) Foreground とともに使用すると大量にデバグメッセージを吐く  
ArchiveMaxFileSize 廃止された。MaxScanSize および MaxFileSize を使用のこと  
ArchiveMaxRecursion 廃止された。MaxRecursion を使用のこと  
ArchiveMaxFiles 廃止された。MaxFiles を使用のこと  
ArchiveMaxCompressionRatio 廃止された  
ArchiveBlockMax 廃止された  
MaxScanSize ファイルのうち頭からどれだけの部位を走査するか。通常ファイルなら文字通り。アーカイブファイルでは、この制限値に達するところまでで再帰的走査が打ち切られる。0 は制限なし 30M
MaxFileSize この指定サイズよりも大きなファイルは走査しない。通常ファイルならそのサイズ、アーカイブファイルではそのアーカイブに含まれる個々のファイルのサイズ。0 は制限なしを意味する 20M
MaxRecursion 多重圧縮アーカイブを何層目まで走査するか 10
MaxFiles アーカイブなどコンテナ様式のファイルで、中に含まれる先頭から何個までのファイルを走査するか。0 は制限なし 1000

`## Clamuko settings' 以降の行は必要ないのですべてコメントアウトしておこう。

※ clamd が正常に走らなかったり、後述の Qmail-Scanner のテストでエラーが出る時、これらのコメント記号を外して有効にした上で、ターミナルから直接 "/usr(/local)/sbin/clamd" で起動させると、ステータスをふんだんにエコーしながらフォアグラウンドで走るので、原因究明に役立つ。その際、clamd を止めるには普通に Ctrl + c

最後に、clamd デーモンの起動テストをしておこう。 /etc/rc.d/init.d/clamd start してみて、ちゃんとプロセスが上がるようであれば、chkconfig で必要な run レベルに登録しておくべし。

clamd デーモンの起動スクリプト

筆者の clamav.init ファイル含めて RPMビルドした場合は、カスタマイズ済みなので作業不要。

ClamAV ソースディレクトリの contrib/init/ ディレクトリに同梱されている <distribution>/clamd ファイルを、 /etc/rc.d/init.d/ にコピーすればいいが、筆者としてはちょっと気に入らないので、前述の clamav.init を名前を変えてコピーしてもらうのがいいだろう。もちろん、MONIT などのデーモン管理ツールを使っているのならそれなりのディレクトリに置くべし。改造した部分はこうだ。まず、新規に加えたファンクション:

reloaddb() {
         echo -n $"Reloading AV database: "
         killproc clamd -USR2
         RETVAL=$?
         echo
         return $RETVAL
}

そして、case 分岐も変更しておく:

reloaddb)
       reloaddb
       ;;
reload)
       reloaddb
       reload
       ;;

つまり、こういうことだ。デフォルトの reload ファンクションは clamd に HUP シグナルを送っている。しかし、オフィシャルドキュメントによると、HUP を送っても clamd はログファイルを開き直すだけ。対して、新たな reloaddb ファンクションで送っている USR2 シグナルは、clamd にウィルスパターンファイルを再読込させる働きがある。

パターンファイルのアップデートとデーモン起動確認

freshclam.conf の調整とテスト

clamd と異なり、freshclam はデフォルトの clamav 権限で動作させる。 /etc/freshclam.conf で最低限調整の必要な箇所は以下。筆者の SPECファイルを使って RPMパッケージをビルドした場合、DatabaseMirror ディレクティブ群についてはインストール時に下記のとおりに自動設定される。

#Example    設定せずにそのまま使おうとするウツケモノ避け。必ず取り除くかコメントアウト
DatabaseDirectory /usr/share/clamav    clamd.conf と同じに
LogSyslog
LogFacility LOG_LOCAL6
DatabaseMirror db.jp.clamav.net       一番近いアップデートサーバに合わせ XY 部分の国コードを書き換える
DatabaseMirror db.local.clamav.net    抑えのアップデートサーバ
DatabaseMirror database.clamav.net    上記ミラーがいずれも不通の時のバックアップサーバ (デフォルトのまま。いじるな)

環境が整ったら、テストを兼ねて初アップデートを行う。freshclam-v オプションを付けると出力が少しだけ賑やかになる。逆に --quiet とすればエラー時以外は黙る:

root# freshclam -v

cronによるアップデートスケジュール

上記でエラーらしき出力がなければ、cronスケジュールに組み込む。ドキュメントでは 2時間に1回程度、更新版のチェックを行うことが推奨されている()。

アップデートスクリプト (仮に /etc/cron.misc/freshclam とする) は以下のようになる。筆者の SPECファイルでビルドした場合は、スクリプトが既に /etc/cron.misc/ に作成されているはずなので、最後の crontab 編集のみ行えばいい。

#!/bin/bash
PIDFILE="/var/run/clamav/clamd.pid"
/usr/local/bin/freshclam --quiet --datadir="/usr/local/share/clamav"
if [ $? -eq 0 -a -f $PIDFILE ] ; then
  /etc/rc.d/init.d/clamd reloaddb >/dev/null
fi
exit 0

あるいは、パターンが実際にアップデートされた場合にデーモンに再読込を促す --deamon-notify=<clamd_conf_path> オプションを使い、下記のようにする手もある;

#!/bin/bash
/usr/bin/freshclam --quiet --deamon-notify=/etc/clamd.conf
exit 0 

こちらの場合は、clamd デーモンが立ち上がっていなかった場合 「clamd への通知失敗」 というエラーが cron を通じて管理者にメールされるのでウットウシい。

そして、 /etc/crontab ファイルには以下の記述を追加しておく。00, 10, 20, ..など 10 で割り切れる "分" には ClamAV データベースサーバに世界中からアクセスが集中しているので、この例の x時08分 のように、半端な時間を選んでほしい、と曰くドキュメント:

8 */2 * * * root /etc/cron.misc/freshclam

※ freshclam は、-d オプション付きで呼ぶとデーモンモードで常駐させることもできる (アップデート間隔は設定ファイルに項目あり)。しかし、これだけのためにずっと起こしておくのはリソースの無駄に思える。