CentOS6.7において、epelリポジトリからyumでインストールされる clamav-milterは、
そのままでは、一つのインスタンスしか起動しなかった。
一つのインスタンスは、一つの設定内容のみ反映していて、
当然、窓口となるソケットファイルも一つしか生成されない。
しかし、同一マシン上で、複数のPostfixインスタンスが存在していて、
それぞれのPostfixインスタンスが、clamav-milterに異なる挙動を望みたい場合には、
それぞれに対し、異なる設定が反映されたclamav-milterのインスタンスとソケットが必要になる。
そのため、互いに独立した設定ファイルを反映させた複数のclamav-milterを実体化する必要があった。
いわば、clamav-milterの、multiple instances を実現することである。
以下では、私の考えた方法とその具体的な手順について記載している。
■Clamav-milterのインストール
まずは、次のページの方法で、基本となるclamav-milterをインストールした。
http://akira-arets.blogspot.jp/2016/02/fetchmail-postfix-clamav-milter.html
■「複数インスタンス」設定のためのポイントを確認
冒頭で述べた環境において、clamav-milterを「複数インスタンス」に対応させるために、
clamav-milterの、上記の起動スクリプトと設定ファイル(2種類)を修正することになる。
設定は次に挙げることがポイントになっている。
・各インスタンスごとに、独自のpidを管理する。
・各インスタンスごとに、起動スクリプトを用意する。
・各インスタンスごとに、それぞれ独自の設定ファイルを用意する。
・各インスタンスごとに、異なるソケットファイルを結びつける。
<以下では、2つのインスタンスを動作させることを目的とする。>
■初期状態の clamav-milter の起動スクリプトが動作しないように設定
複数のインスタンスを動作させるので、初期の起動スクリプトは使用しない。
初期の起動スクリプトは、複数インスタンスに対応していないためだ。
(以降の手順では、複数のインスタンスに対応した起動スクリプトや設定ファイルを、インスタンスごとに作成する。)
サービスが動作中の場合、停止しておく。
# service stop clamav-milter
初期状態では次の様に、clamav-milter の起動スクリプトが登録されている。
# chkconfig --list | grep clam
clamav-milter 0:off 1:off 2:off 3:off 4:off 5:off 6:offこのclamav-milter の起動スクリプトが動作しないように設定した。
clamd 0:off 1:off 2:on 3:on 4:on 5:on 6:off
# chkconfig --del clamav-milter
設定を確認した。
# chkconfig --list | grep clam
clamd 0:off 1:off 2:on 3:on 4:on 5:on 6:off
■clamav-milterの設定ファイルを用意
/etc/clamav-milter.conf は、clamav-milter の挙動を設定するファイルである。
○起動するインスタンスごとに、設定ファイルをコピーして増やしておく。
まずは、バックアップを取っておく。
# cp /etc/clamav-milter.conf /etc/clamav-milter.conf.orig_ver0.99-3.el6
必要なインスタンス数の分だけ、設定ファイルを用意する。
オリジナルのものを雛型としてコピーして使う。
その際、各インスタンスの振る舞いに相応しい名前として作成する。
(この名称は、以降の設定でも、インスタンスを区別するために使用する。)
例えば、
bounceメールを返す方を、clamav-milter_local とし、
bounceメールを返さない方を、clamav-milter_global としている。
(以降では、ウイルス発見時にもbounceメールを返さないインスタンスとして、clamav-milter_global を設定していく。)
# cp /etc/clamav-milter.conf.orig_ver0.99-3.el6 /etc/clamav-milter_local.conf
# cp /etc/clamav-milter.conf.orig_ver0.99-3.el6 /etc/clamav-milter_global.conf
この段階で、次の二つの原型ファイルが存在することになる。
# ls /etc/clamav-milter_*.conf
/etc/clamav-milter_global.conf
/etc/clamav-milter_local.conf
○各インスタンス用の設定ファイルを修正する。
設定項目の意味や考え方は、次のページを参照
http://akira-arets.blogspot.jp/2016/02/fetchmail-postfix-clamav-milter.html
ここでは、複数インスタンスの動作を目的とした部分に限って設定例を挙げている。
(1つ目)
# vi /etc/clamav-milter_local.conf
(次の項目を見つけて変更する。他は必要に応じて変更する。)
MilterSocket /var/run/clamav/clamav-milter_local.sock
PidFile /var/run/clamav/clamav-milter_local.pid
OnInfected Reject
RejectMsg "REJECT - AntiVirus detected: %v"
AddHeader Add
LogFile /tmp/clamav-milter_local.log
(2つ目)
# vi /etc/clamav-milter_global.conf
(次の項目を見つけて変更する。他は必要に応じて変更する。)
MilterSocket /var/run/clamav/clamav-milter_global.sock
PidFile /var/run/clamav/clamav-milter_global.pid
OnInfected Quarantine
AddHeader Add
LogFile /tmp/clamav-milter_global.log
■clamav-milterの起動”パラメーター”ファイルを用意
clamav-milterバイナリが、起動スクリプトにより呼び出されるときに、
このファイルで指定されたパラメーターが引数として指定される。
ここに、各インスタンスに反映させるべき「設定ファイル(上記で作成したもの)」を明示する。
同様に、インスタンスごとに、用意する。
○まずはバックアップを取り、必要なインスタンス数の分だけ、設定ファイルを用意する。
# cp /etc/sysconfig/clamav-milter /etc/sysconfig/clamav-milter.orig_ver0.99-3.el6
# cp /etc/sysconfig/clamav-milter.orig_ver0.99-3.el6 /etc/sysconfig/clamav-milter_local
# cp /etc/sysconfig/clamav-milter.orig_ver0.99-3.el6 /etc/sysconfig/clamav-milter_global
○内容を表示した。
# cat /etc/sysconfig/clamav-milter.orig_ver0.99-3.el6
### Simple config file for clamav-milter, you should
### read the documentation and tweak it as you wish.
CLAMAV_FLAGS=""
○内容を修正した。
(1つ目)
# vi /etc/sysconfig/clamav-milter_local
(こちらは、デフォルトで、/etc/clamav-milter.conf が使用されるので、明示しなくてもよい。)
CLAMAV_FLAGS="--config-file /etc/clamav-milter_local.conf"(2つ目)
# vi /etc/sysconfig/clamav-milter_global
CLAMAV_FLAGS="--config-file /etc/clamav-milter_global.conf"
■起動スクリプト(複数インスタンス対応)の追加と修正
インスタンスごとに、独自の起動スクリプトを用意する。
<手順>
・オリジナルからコピーをとって修正し、複数インスタンス対応の「起動スクリプトの基本形」を作成する。
・この「起動スクリプトの基本形」を完成させた後、インスタンスごとに起動スクリプトとしてコピーし修正を行う。(インスタンスごとに必要)
○オリジナルの起動スクリプトからコピーを取った。
# cp /etc/init.d/clamav-milter /etc/init.d/clamav-milter--for-multiple.orig_ver0.99-3.el6
○「複数インスタンス起動スクリプトの雛形」にするために修正した。
留意したこと:
・追加した変数(INSTANCESUF)に、インスタンス名のサフィックス(アンダーバー以降の文字列)を設定するだけで、
個別のインスタンス用の起動スクリプトが出来上がるようにした。
・インスタンスを区別するが、バイナリファイルは共通のもの(usr/sbin/clamav-milter)を使う。
また、修正の目的は、主に次の3つである。
・PIDファイルを使って、プロセスを区別すること。(stop、statusに影響)
・インスタンスごとに、独自の設定ファイルを読み込ませて反映させること。
・プロセスごとに、ロックファイルを用意すること。 (condrestartに影響)
以下のように、色付き、アンダーラインで示される個所に注意して、修正し雛形を完成させる。
# vi /etc/init.d/clamav-milter--for-multiple.orig_ver0.99-3.el6
#!/bin/sh
#
# Startup script for the Clamav Milter Daemon ()
#
# chkconfig: - 77 23
# description: clamav-milter is a daemon which hooks into sendmail \
# and routes email messages to clamav.
# processname: clamav-milter
# pidfile: /var/run/clamav/clamav-milter_().pid
# config: /etc/sysconfig/clamav-milter_()
INSTANCESUF=
# Source function library.
. /etc/rc.d/init.d/functions
# Source networking configuration.
. /etc/sysconfig/network
[ -x /usr/sbin/clamav-milter ] || exit 0
# Local clamav-milter_() config
CLAMAV_FLAGS=
test -f /etc/sysconfig/clamav-milter_${INSTANCESUF} && . /etc/sysconfig/clamav-milter_${INSTANCESUF}
# See how we were called.
case "$1" in
start)
echo -n "Starting Clamav Milter Daemon( ${INSTANCESUF} ): "
daemon clamav-milter $CLAMAV_FLAGS
RETVAL=$?
echo
[ $RETVAL -eq 0 ] && touch /var/lock/subsys/clamav-milter_${INSTANCESUF}
#下2行はオリジナルの起動スクリプトにはそもそも存在しない。
#Postfixからこのソケットにアクセスできるようにするためのもの。(次のページを参照)
#http://akira-arets.blogspot.jp/2016/02/fetchmail-postfix-clamav-milter.html
[ $RETVAL -eq 0 ] && chown postfix:postfix /var/run/clamav/clamav-milter_${INSTANCESUF}.sock
[ $RETVAL -eq 0 ] && chmod 0660 /var/run/clamav/clamav-milter_${INSTANCESUF}.sock
;;
stop)
echo -n "Stopping Clamav Milter Daemon( ${INSTANCESUF} ): "
killproc -p /var/run/clamav/clamav-milter_${INSTANCESUF}.pid
RETVAL=$?
echo
[ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/clamav-milter_${INSTANCESUF}
;;
status)
status -p /var/run/clamav/clamav-milter_${INSTANCESUF}.pid
RETVAL=$?
;;
restart|reload)
$0 stop
$0 start
RETVAL=$?
;;
condrestart)
[ -e /var/lock/subsys/clamav-milter_${INSTANCESUF} ] && $0 restart
RETVAL=$?
;;
*)
echo "Usage: clamav-milter( ${INSTANCESUF} ) {start|stop|status|restart|reload|condrestart}"
exit 1
esac
exit $RETVAL
○必要なインスタンス数の分だけ、「起動スクリプトの基本形」を雛型としてコピーした。
# cp /etc/init.d/clamav-milter--for-multiple.orig_ver0.99-3.el6 /etc/init.d/clamav-milter_local
# cp /etc/init.d/clamav-milter--for-multiple.orig_ver0.99-3.el6 /etc/init.d/clamav-milter_global
○インスタンスごとの起動ファイルを完成させた。
(1つ目)
# vi /etc/init.d/clamav-milter_local
(下記のように、変数定義部分を修正)
INSTANCESUF=local
実行権限を与えた
# chmod +x /etc/init.d/clamav-milter_local
(2つ目)
# vi /etc/init.d/clamav-milter_global
(下記のように、変数定義部分を修正)
INSTANCESUF=global
実行権限を与えた
# chmod +x /etc/init.d/clamav-milter_global
■追加・修正した起動スクリプトをシステムに登録
serviceコマンドからそれぞれのインスタンスを扱える(start stop restart status condrestart)ように、
システム起動時に各インスタンスが実体化されるように、設定した。
○serviceの登録を行った。
# chkconfig --add /etc/init.d/clamav-milter_local
# chkconfig --add /etc/init.d/clamav-milter_global
(登録を削除した。オリジナルのものは複数インスタンス用ではないので用いるべきでない。)
# chkconfig --del /etc/init.d/clamav-milter
○システム起動時に、自動起動させるための設定を行った。
# chkconfig clamav-milter_local on
# chkconfig clamav-milter_global on
○登録状況を確認した。
# chkconfig --list | grep clamav-milter
clamav-milter_global 0:off 1:off 2:on 3:on 4:on 5:on 6:off
clamav-milter_local 0:off 1:off 2:on 3:on 4:on 5:on 6:off
■動作確認
○サービスの起動と状態確認を行った。
(1つ目)
# service clamav-milter_local start
Starting Clamav Milter Daemon( local ): [ OK ]# service clamav-milter_local status
(pid 22467) is running...# ps -A | grep 22467
22467 ? 00:00:00 clamav-milter
(2つ目)
# service clamav-milter_global start
Starting Clamav Milter Daemon( global ): [ OK ]# service clamav-milter_global status
(pid 22488) is running...# ps -A | grep 22488
22488 ? 00:00:00 clamav-milter
○ソケットやpidファイルが生成されているか確認した。
# ls -al /var/run/clamav/
total 20clamav-milterの異なるインスタンス由来のソケットファイルが作成されていることがわかる。
drwxr-xr-x 2 clam clam 4096 Feb 25 14:48 .
drwxr-xr-x. 13 root root 4096 Feb 25 10:52 ..
-rw-rw-r-- 1 clam clam 5 Feb 25 12:58 clamav-milter_global.pidsrw-rw---- 1 postfix postfix 0 Feb 25 12:58 clamav-milter_global.sock-rw-rw-r-- 1 clam clam 6 Feb 25 14:48 clamav-milter_local.pidsrw-rw---- 1 postfix postfix 0 Feb 25 14:48 clamav-milter_local.sock-rw-rw-r-- 1 clam clam 5 Feb 25 10:57 clamd.pid
srw-rw-rw- 1 clam clam 0 Feb 25 10:57 clamd.sock
Postfixなどには、clamav-milterのこれらのソケットへアクセスするように設定する。
○サービスの終了・再起動の動作を確認した。
<テスト1 start stop>
----------ともに動作している状態である。
# service clamav-milter_local status
(pid 22467) is running...# service clamav-milter_global status
(pid 22488) is running...このとき、一方のインスタンスを終了させる。
# service clamav-milter_global stop
Stopping Clamav Milter Daemon( global ): [ OK ]# ps -A | grep 22488
(結果が返らないことから、global の方は終了したことがわかる。)
このとき、他方のインスタンスを確認する。
# service clamav-milter_local status
(pid 22467) is running...# ps -A | grep 22467
(他方のプロセスは影響を受けていないことがわかる。)
22467 ? 00:00:00 clamav-milter
こちらも終了させる。
# service clamav-milter_local stop
Stopping Clamav Milter Daemon( local ): [ OK ]# ps -A | grep 22467
(結果が返らないことから、こちらも終了したことがわかる。)
<テスト2: condrestart>
----------ともに終了状態にある。
# service clamav-milter_local condrestart
# service clamav-milter_global condrestart
(いずれも、結果が返らないので、正常な動作である。)
ともに動作状態に切り替えた。
# service clamav-milter_local start
Starting Clamav Milter Daemon( local ): [ OK ]# service clamav-milter_global start
Starting Clamav Milter Daemon( global ): [ OK ]----------ともに動作している状態である。
# service clamav-milter_local condrestart
Stopping Clamav Milter Daemon( local ): [ OK ]# service clamav-milter_global condrestart
Starting Clamav Milter Daemon( local ): [ OK ]
Stopping Clamav Milter Daemon( global ): [ OK ]
Starting Clamav Milter Daemon( global ): [ OK ]
―以上で完了―
<おまけ: 今回のスクリプトの修正についてのメモ>
○起動スクリプトに通っているパスについて
daemon clamav-milter $CLAMAV_FLAGS 部分の clamav-milter へは、パスが通っている。
# less /etc/init.d/functions
(略)
# Set up a default search path.
PATH="/sbin:/usr/sbin:/bin:/usr/bin"
export PATH
(略)
○起動スクリプトと、functionsスクリプト内の、ファンクションについて
service clamav-milter stop (あるいは restart) コマンドを実行したとき、
/etc/init.d/clamav-milter の、上記 stop) 内が実行される。
<下記、既に述べた方法でインストールされた /etc/init.d/clamav-milter を上記に述べた方法で、修正したものの抜粋である。>
----------(省略)----------
stop)echo -n "Stopping Clamav Milter Daemon( ${INSTANCESUF} ): "
#killproc clamav-milter (コメントアウトして、下のようにした。)
killproc -p /var/run/clamav/clamav-milter_${INSTANCESUF}.pid
RETVAL=$?
echo
[ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/clamav-milter_${INSTANCESUF}
;;
----------(省略)----------
ここで、下記に内容を示している、killproc()ファンクションがコールされるわけだが、
修正前(上記のコメントアウトしている部分)の呼び出し方では、
killproc()ファンクション内において、pid_file変数が設定されない状態になる。
すると、インスタンスが区別されない。
(下記に、killproc()ファンクション の一部を抜粋した。)
この場合、# Find pid. の次の行で、プロセスIDを取得しようとして、
__pids_var_run() ローカルファンクションが呼ばれるものの、
動作中のプロセスIDを見つけられずに、return 3 で復帰してきてしまう。
ところが、killproc()ファンクションは、さらなる手立てで、プロセスIDを探索しようとする。
$pid は空のままなので、さらに下にある、__pids_pidof ローカルファンクションが呼び出される。
(これ以上は追跡していないが、)おそらく、この探索を経て、
「clam-milter_local」と「clam-milter_global」とが、区別されることなく共にプロセスが終了させられてしまう。
元は同じ実行ファイルから生じているプロセスだからだろう。
clam-milterの複数のプロセスが区別されないと、
service clamav-milter_local stop で、clamav-milter_global まで終了してしまう。
<下記、CentOS 6.7 64bit 版の、/etc/init.d/functions スクリプト から順不同で抜粋したものである>
----------(省略)----------
# A function to stop a program.
killproc() {
local RC killlevel= base pid pid_file= delay try binary=
RC=0; delay=3; try=0
# Test syntax.
if [ "$#" -eq 0 ]; then
echo $"Usage: killproc [-p pidfile] [ -d delay] {program} [-signal]"
return 1
fi
if [ "$1" = "-p" ]; then
pid_file=$2
shift 2
fi
if [ "$1" = "-b" ]; then
----------(省略)----------
# Find pid.
__pids_var_run "$1" "$pid_file" "$binary"
RC=$?
if [ -z "$pid" ]; then
if [ -z "$pid_file" ]; then
pid="$(__pids_pidof "$1")"
----------(省略)----------
__pids_var_run() {
local base=${1##*/}
local pid_file=${2:-/var/run/$base.pid}
----------(省略)----------
# Output PIDs of matching processes, found using pidof__pids_pidof() {
pidof -c -m -o $$ -o $PPID -o %PPID -x "$1" || \
pidof -c -m -o $$ -o $PPID -o %PPID -x "${1##*/}"
}
----------(省略)----------
対策は、__pids_var_run ローカルファンクションの段階で、
pidファイルに基づいてプロセスIDを見つけられるようにすることである。
そのために、既に設定してきたように、呼び出しの元の、/etc/init.d/clamav-milter で、
「killproc」を「-pオプションを付けて呼び出し、pidファイルのパスを明示したわけだ。
killproc -p /var/run/clamav/clamav-milter_${INSTANCE}.pidkillproc()ファンクション内で、条件(-pオプション有で)分岐して、
pid_file変数に、たとえば、/var/run/clamav/clamav-milter_local.pid が設定される。
これによって、__pids_var_run ローカルファンクションでは、このpidファイルが扱われるので、
固有のプロセスIDの取得に成功するようである。
以上