postfixでのメール受信時ウィルスチェック(ClamAntiVirus)でメール削除、受信者への通知を行えるようにする
目的
postfixでメールを受信したときにClamAVのウィルスチェックを行い、チェックに引っかかったものはメール削除、および受信者に対してメールの送信があったことを通知できるようにする。
procmailからpythonを呼び出したのだが、procmail周りの情報が少なくてこずったのでメモを残しておきます。
○postfix設定
postfixがメール受信時にprocmailを実行するようにmaibox_commandを設定しておく
・/etc/postfix/main.cf
mailbox_command = /usr/bin/procmail
○procmail設定
受信者全員に対して処理を行うので/etc/procmailrcに設定を記述する。
メール受信時にclamAVの常駐プロセス(clamd)でスキャンし、スキャン結果を新規でメールヘッダ(x-virus)に追加する。
それからprocmailのレシピでメールヘッダ(x-virus)の値を見て、ウィルス判定されていたらメール削除、メール送信があったことを受信者へ通知できるようにする。
procmailからpythonスクリプトを呼び出す場合は、以下のようにprocmailのシェル呼び出しのアクションを実行する
|/usr/bin/python /var/procmail/sh/mail.python
python側では標準入力からメールヘッダ、本文の内容が入力されるので、これを使って処理する。
標準入力からの受け取り方を知らなかったのでここら辺でもちょっとてこずった、サンプルをあげると以下のようになる。
import sys if __name__ == "__main__": param = sys.argv print param f=open('/home/user/mail.txt', 'w') lines = sys.stdin.readlines() for line in lines: f.write(line.encode('utf-8')) f.close()
でprocmail側の処理をまとめると以下のようになる。
パスを設定 PATH=/bin:/usr/bin:/usr/local/bin # メールボックスの設定 MAILDIR=$HOME/Maildir DEFAULT=$MAILDIR/ # Procmailのログファイル出力先を指定 LOGFILE=$MAILDIR/procmaillog # ロックファイルのパスを指定 LOCKFILE=$HOME/.lockmail # Clam AntiVirusによるウィルスチェック AV_REPORT=`/usr/bin/clamdscan --no-summary - 2>&1| awk -F\n -v ORS=' ' '{print}'|awk '{print $NF}'` VIRUS=`if [ "$AV_REPORT" != "OK" ]; then echo Yes; else echo No;fi` :0fw | formail -i "X-Virus: $VIRUS" # Clam AntiVirusによるウィルスチェック # Clam AntiVirusがウィルス判定したメール通知後削除 :0 * ^X-Virus: Yes |/usr/bin/python /var/procmail/sh/mail.python `echo "$DEFAULT"` :0 * ^X-Virus: Yes /dev/null
pythonに対して環境変数DEFAULTを渡しているのがポイントで、これを通知メールの送付先の情報として使っています。
もともとはメールヘッダのTOを見て通知しようとしていたのですが、よくよく考えると複数人に対してメールの送信
があると1人ずつprocmail実行されるので問題だと思ってやめました。
でpython側は以下のようになるかと思います。
タイトルとか本文は日本語のデコードが必要になります。
# coding: UTF-8 import sys import codecs import smtplib import re from email.MIMEText import MIMEText from email.Header import Header from email.Utils import formatdate from datetime import datetime def mail_send( from_address, to_address, mail_text, charset): msg = MIMEText(mail_text.encode(charset),"plain",charset) msg["Subject"] = Header(u"ウィルスチェックによるメール削除のご連絡",charset) msg["From"] = from_address msg["To"] = to_address msg["Date"] = formatdate(localtime=True) smtp = smtplib.SMTP("localhost", 25) smtp.sendmail(from_address,to_address,msg.as_string()) smtp.close() def array2text(args, mail_info): msg = u"ウィルスチェックにより以下メールを削除しました。\n" msg += u"------------------------------------\n" #msg += u"送信者:" + args[1].encode('utf-8') + u"\n" msg += u"送信者:" + ",".join(mail_info[0]).encode('utf-8') + u"\n" msg += u"受信者:" for to_address in mail_info[1]: msg += to_address.encode('utf-8') msg += u"件名:" + mail_info[2].encode('utf-8') + "\n" msg += u"時間:" + datetime.now().strftime("%Y/%m/%d %H:%M:%S") + "\n" msg += u"------------------------------------\n" #msg += u"本文:\n" #for arg in args: # msg += arg.encode('utf-8') + "\n" #msg += u"------------------------------------\n" return msg def get_mail_info(lines): from_address = [] to_address = [] Info=[from_address, to_address, ""] find_to = False for line in lines: if find_to is True: if line.find(":") > 0: find_to = False else: to_address.append(line) if re.match("^To:", line) is not None: to_address.append(line[3:].strip()) find_to = True if re.match("^From:", line) is not None: from_address.append(line[5:].strip()) if re.match("^Subject:", line) is not None: Info[2] = line.encode("utf8") return Info if __name__ == "__main__": param = sys.argv print param lines = sys.stdin.readlines() mail_info = get_mail_info(lines) mail_text = array2text(param, mail_info) mail_receiver = param[1].split("/")[2] + "@○○○" mail_send( '○○○@○○○', mail_receiver, mail_text, "UTF8")