[FrontPage] [TitleIndex] [WordIndex

This is a read-only archived version of wiki.centos.org

Amavisd-new、 ClamAV 及 SpamAssassin

<<TableOfContents: execution failed [Argument "maxdepth" must be an integer value, not "[1]"] (see also the log)>>

敬請注意:

/!\ 注意:本文檔是為 CentOS 5 而撰寫的。它的內容對 CentOS 6 或較後版本也許並不準確。

1. 引言

Amavisd-new 是個位於郵件伺服器(MTA)和病毒掃描器(ClamAV)及/或 SpamAssassin 等內容檢查器之間,可靠而高效能的介面。Amavisd-new 支援以 (E)SMTP 和 LMTP 協議,或以 UNIX 通訊端與 MTA 及內容檢查器溝通。此外,它也可運用專用的輔助程式如 Mail::SpamAssassin 的 Perl 模塊。

Amavisd-new 支援數個 MTA。但一如 Amavisd-new 的文檔所指,Amavisd-new 「與 Postfix 有最佳配搭,亦可配合雙 sendmail 的設置及 Exim v4,也能配合 sendmail/milter,或任何採用 MTA 的 SMTP 轉發站」。這份指南是針對 Postfix 及在其上測試的,它亦補充這份基本的 Postfix 指南。日後其它 MTA 或許會被加入。

我們將會設定 Amavisd-new 的常駐程式 amavisd,讓它接納來自我們的 MTA 的郵件,把它們傳給 ClamAV 及 SpamAssassin 來檢查內容,然後還給我們的 MTA 來發放。Amavisd 會利用 lmtp 在 TCP 埠 10024 上聆聽及接收來自我們的 MTA 的郵件,然後透過本地的 UNIX 通訊端把它傳給 ClamAV,和利用 Mail::SpamAssassin 的 Perl 模塊傳送給 SpamAssassin。經過掃描的郵件將會透過 smtp 在 TCP 埠 10025 上還給我們的 MTA 來發放。

Amavisd-new 不須與 MTA 處於同一台實體伺服器上,在高流量的環境下,Amavisd-new、ClamAV 與 SpamAssassin 往往都與 MTA 身處不同的伺服器。

2. 安裝

Amavisd-new 及 ClamAV 可從 RPMForge 軟件庫安裝。要啟用 RPMForge 軟件庫,請參閱 RPMForge 的指引。

SpamAssassin 屬於 CentOS 的 base 軟件庫,但 RPMForge 收錄了一個更新的版本。你應該考慮用 RPMForge 的版本。要這樣做(尤其假如你已採用 yum 的 priorities 插件),請在你的 etc/yum.repos.d/CentOS-Base.repo 檔內的 [base] 及 [updates] 部份加入以下內容:

[base]
exclude=spamassass*
...
[updates]
exclude=spamassass*
...

首先,安裝 amavisd-new、clamv 及 spamassassin 套件:

yum --enablerepo=rpmforge,rpmforge-extras install amavisd-new clamav clamav-devel clamd spamassassin

這樣多數亦會安裝一大堆依賴性套件,包括不同的 perl 模塊及壓縮套件。一切順利的話,amavis 及 clamav 這兩位新用戶應該已被安裝在系統上:

# cat /etc/passwd | grep "amavis\|clamav"
clamav:x:101:102:Clam Anti Virus Checker:/var/clamav:/sbin/nologin
amavis:x:102:103:Amavis email scan user:/var/amavis:/bin/sh

除此之外,clamav 這個用戶應該已經自動被加進 amavis 這個群組:

# groups clamav
clamav : clamav amavis

要不然,你可手動地把 clamav 加進 amavis 這個群組:

gpasswd -a clamav amavis

最後,三個新的服務應服已被加進系統內

# chkconfig --list | grep "amavisd\|clamd\|spamassassin"
amavisd         0:off   1:off   2:on    3:on    4:on    5:on    6:off
clamd           0:off   1:off   2:on    3:on    4:on    5:on    6:off
spamassassin    0:off   1:off   2:off   3:off   4:off   5:off   6:off

spamassassin 這個啟動 spamd 的服務可以被停用,因為 Amavisd-new 不會應用 spamassassin 的常駐程式(spamd),卻會直接載入 spamassassin 作為一個模塊。

3. 設定

事實上 SpamAssassin 並不須要特別的設定便可配合 Amavisd-new,它可即裝即用。這並不等於你無法通過 /etc/mail/spamassassin/local.cf 或該目錄內的自訂 cf 檔來設定它。

3.1. ClamAV

ClamAV 的設定存放在 /etc/clamd.conf 內。我們必須編輯 /etc/clamd.conf 讓 ClamAV 知道 Amavisd-new 將會利用本地的 UNIX 通訊端而不是 tcp 埠來與它溝通,以及該通訊端在那裡。請如下編輯 LocalSocket 的設定並備註掉 TCPSocket:

### /etc/clamd.conf
#
# 設定 clam 的 LocalSocket
# 留意它 *必須* 吻合 /etc/amavisd.conf 內的設定
#
LocalSocket /var/run/clamav/clamd.sock
#
# 備註掉 TCPSocket 這個設定:
# TCPSocket 3310

3.2. Amavisd-new

Amavisd-new 把它的設定放在 /etc/amavisd.conf 之內。

由於 Amavisd-new 功能強大及靈活性高,當中確實有很多東西可查閱,因此我們不開每次看數個比較重要的設定。

首先,我們可會通過解除以下數行的註釋來停止檢查病毒或垃圾郵件(由於下面數行是被註釋掉的,因此病毒及垃圾郵件在預設中是被啟用的):

### /etc/amavisd.conf:
#
# To disable virus or spam checks, uncomment the following:
#
# @bypass_virus_checks_maps = (1);  # controls running of anti-virus code
# @bypass_spam_checks_maps  = (1);  # controls running of anti-spam code
# $bypass_decode_parts = 1;         # controls running of decoders & dearchivers

接著,請留意以下數行,縱使它們無須被修改:

$max_servers = 2;                   # num of pre-forked children (2..30 is common), -m
$daemon_user  = "amavis";           # (no default;  customary: vscan or amavis), -u
$daemon_group = "amavis";           # (no default;  customary: vscan or amavis), -g
...
$inet_socket_port = 10024;          # listen on this local TCP port(s)
...
# $notify_method  = 'smtp:[127.0.0.1]:10025';
# $forward_method = 'smtp:[127.0.0.1]:10025';  # set to undef with milter!

$max_servers 設定同步執行的 Amavisd-new 進程數量,而且必須與 /etc/postfix/master.cf 內的 amavisfeed 服務的 maxproc 欄吻合(請參閱下面的 Postfix 設定)。

$daemon_user$daemon_group 應該吻合用來執行 Amavisd-new 的用戶及群組。

$inet_socket_port 定義 Amavisd-new 將會在那一個 tcp 埠接納來自 Postfix 的連線。

$notify_method$forward_method 定義 Amavisd-new 把郵件重新注入 Postfix 的途徑。

以下設定必須被修改(涉及 $mydomain 和 $myhostname 時)及解除註釋(移除行首的 # 號):

$mydomain = 'example.com';                  # 編輯:a convenient default for other settings
$MYHOME = '/var/amavis';                    # 解除註釋:a convenient default for other settings, -H
$helpers_home = "$MYHOME/var";              # 解除註釋:working directory for SpamAssassin, -S
$lock_file = "$MYHOME/var/amavisd.lock";    # 解除註釋, -L
$pid_file  = "$MYHOME/var/amavisd.pid";     # 解除註釋, -P
$myhostname = 'mail.example.com';           # 解除註釋及編輯:must be a fully-qualified domain name!

接著是一些 SpamAssassin 設定來置換預設的 SpamAssassin 設定:

$sa_tag_level_deflt  = 2.0;                 # add spam info headers if at, or above that level
$sa_tag2_level_deflt = 6.2;                 # add 'spam detected' headers at that level
$sa_kill_level_deflt = 6.9;                 # triggers spam evasive actions (e.g. blocks mail)
$sa_dsn_cutoff_level = 10;                  # spam level beyond which a DSN is not sent
# $sa_quarantine_cutoff_level = 25;         # spam level beyond which quarantine is off
$penpals_bonus_score = 8;                   # (no effect without a @storage_sql_dsn database)
$penpals_threshold_high = $sa_kill_level_deflt;         # don't waste time on hi spam
$sa_mail_body_size_limit = 400*1024;        # don't waste time on SA if mail is larger
$sa_local_tests_only = 0;                   # only tests which do not require internet access?

不一定要修改它們,但是你值得知道它們的存在,因為這裡是最方便修改垃圾郵件限制的地方。

$sa_tag_level_deflt 指定 Amavisd-new 由那一個級別開始寫入 X-Spam-Flag、X-Spma-Score、X-Spam-Status 等垃圾郵件資訊標頭。假如你想為所有郵件加入資訊標頭,請把此值設為 -999。

$sa_tag2_level_deflt 指定由那一個級別開始在垃圾郵件的標題上標籤它們。

$sa_kill_level_deflt 指定 Amavisd-new 由那一個級別開始攔截和扣留郵件。這個用途很大,因為 SpamAssassin 在預設情況下不會這樣做。

$sa_dsn_cutoff_level 指定由那一個級別開始寄件失敗通告不會被發送給寄件人。由於多數垃圾郵件寄件者的地址都是偽造的,不為明顯的垃圾郵件發送寄件失敗通告是最合理的,要不然你只會加劇反向散寄的問題。

$sa_quarantine_cutoff_level 指定那一個級別開始不必扣留垃圾郵件。這個選項預設是被註釋掉的,意思就是所有郵件都會被扣留。

接下來是發送通告的電郵地址:

$virus_admin               = "virusalert\@$mydomain";   # notifications recip.
$mailfrom_notify_admin     = "virusalert\@$mydomain";   # notifications sender
$mailfrom_notify_recip     = "virusalert\@$mydomain";   # notifications sender
$mailfrom_notify_spamadmin = "spam.police\@$mydomain";  # notifications sender

你大概會將它們設定為 postmaster\@$mydomain 或其它你想收到垃圾郵件通告的電郵地址。

最後,我們須要如下為 ClamAV 的部份解除註釋:

### http://www.clamav.net/
['ClamAV-clamd',
  \&ask_daemon, ["CONTSCAN {}\n", "/var/run/clamav/clamd.sock"],
  qr/\bOK$/, qr/\bFOUND$/,
  qr/^.*?: (?!Infected Archive)(.*) FOUND$/ ],
# # NOTE: run clamd under the same user as amavisd, or run it under its own
# #   uid such as clamav, add user clamav to the amavis group, and then add
# #   AllowSupplementaryGroups to clamd.conf;
# # NOTE: match socket name (LocalSocket) in clamav.conf to the socket name in
# #   this entry; when running chrooted one may prefer socket "$MYHOME/clamd".

請留意 /var/run/clamav/clamd.sock 這個設定必須與我們先前在 /etc/clamd.conf 內輸入的 LocalSocket /var/run/clamav/clamd.sock 設定吻合。

3.3. Postfix

然後我們須要設定 Postfix 內的服務(/etc/postfix/master.cf)好讓郵件會被傳給 Amavisd-new 進行過濾及再次注入 Postfix。

首先我們會設定 Amavisd-new 這個服務要接受來自 Postfix 的郵件。Amavisd-new 同時支援 lmtp 及 smtp,而在這個實例中我們選用 lmtp 協議。(FIXME: 我未知有任何理由要特定選用某個協議,因此我選擇 lmtp 純綷由於用 lmtp 標示本地派給 Amavisd-new 的郵件有助閱讀日誌)。

開啟 /etc/postfix/master.cf 並加入以下名為 amavisfeed 的服務:

# ==========================================================================
# service type  private unpriv  chroot  wakeup  maxproc command + args
#               (yes)   (yes)   (yes)   (never) (100)
# ==========================================================================
amavisfeed unix    -       -       n        -      2     lmtp
    -o lmtp_data_done_timeout=1200
    -o lmtp_send_xforward_command=yes
    -o disable_dns_lookups=yes
    -o max_use=20

請留意在 maxproc 欄內的數值(2)必須與 /etc/amavisd.conf 內的 $max_servers 設定吻合。有關各選項的詳細解釋請參閱 Amavisd-new 的文檔(/usr/share/doc/amavisd-new-2.5.4/README.postfix.html)。

然後我們定義一個專用的服務把郵件重新注入 Postfix。我們為此在 /etc/postfix/master.cf 內加入一個在 localhost(127.0.0.1)的 tcp 10025 埠(/etc/amavisd.conf 的預設值)上聆聽的 smtp 服務:

# ==========================================================================
# service type  private unpriv  chroot  wakeup  maxproc command + args
#               (yes)   (yes)   (yes)   (never) (100)
# ==========================================================================
127.0.0.1:10025 inet n    -       n       -       -     smtpd
    -o content_filter=
    -o smtpd_delay_reject=no
    -o smtpd_client_restrictions=permit_mynetworks,reject
    -o smtpd_helo_restrictions=
    -o smtpd_sender_restrictions=
    -o smtpd_recipient_restrictions=permit_mynetworks,reject
    -o smtpd_data_restrictions=reject_unauth_pipelining
    -o smtpd_end_of_data_restrictions=
    -o smtpd_restriction_classes=
    -o mynetworks=127.0.0.0/8
    -o smtpd_error_sleep_time=0
    -o smtpd_soft_error_limit=1001
    -o smtpd_hard_error_limit=1000
    -o smtpd_client_connection_count_limit=0
    -o smtpd_client_connection_rate_limit=0
    -o receive_override_options=no_header_body_checks,no_unknown_recipient_checks,no_milters,no_address_mappings
    -o local_header_rewrite_clients=
    -o smtpd_milters=
    -o local_recipient_maps=
    -o relay_recipient_maps=

有關各選項的詳細解釋請參閱 Amavisd-new 的文檔(/usr/share/doc/amavisd-new-2.5.4/README.postfix.html)。

在 /etc/postfix/master.cfg 作出改動後,我們必須重新載入 postfix,好讓這些改動能生效:

postfix reload

這個時候,你也許應該測試 Amavisd-new 及 Postfix 的常註程式是否在正確地聆聽(參閱下面「測試」部份)。

當一切已準備就緒,最後一步就是透過在 /etc/postfix/main.cf 加入以下設定來啟用郵件過濾:

content_filter=amavisfeed:[127.0.0.1]:10024

然後重新載入 postfix 讓這些改動生效:

postfix reload

接著檢視你的郵件日誌。

tail -f /var/log/maillog

3.4. 其它 MTA

除了 Postfix 外,Amavisd-new 也可以設定供其它 MTA 使用。有關其它 MTA 的 README 檔可以在這裡找到:

http://www.ijs.si/software/amavisd/#doc

有興趣撰寫關於如何為其它 MTA 進行設定的人仕,請參閱「如何參予」這一頁:

http://wiki.centos.org/zh-tw/HowToContribute

4. 測試

現在是個好時機來測試我們所定義的服務能如常運作。

首先,啟動 clamd 及 amavisd 服務:

# service clamd start
Starting Clam AntiVirus Daemon:                            [  OK  ]
# service amavisd start
Starting Mail Virus Scanner (amavisd):                     [  OK  ]

現在利用 telnet 測試 amavisd 這個服務正在 127.0.0.1:10024 上耹聽:

$ telnet localhost 10024
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
220 [127.0.0.1] ESMTP amavisd-new service ready
ehlo localhost
250-[127.0.0.1]
250-VRFY
250-PIPELINING
250-SIZE
250-ENHANCEDSTATUSCODES
250-8BITMIME
250-DSN
250 XFORWARD NAME ADDR PROTO HELO
quit
221 2.0.0 [127.0.0.1] amavisd-new closing transmission channel
Connection closed by foreign host.

假若一切正常的話,你應該如上看見成功的連線。

接下來測試 Postfix 的 smtpd 正在 127.0.0.1:10025 上聆聽:

$ telnet localhost 10025
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
220 mail.example.com ESMTP Postfix
ehlo localhost
250-mail.example.com
250-PIPELINING
250-SIZE 20480000
250-VRFY
250-ETRN
250-STARTTLS
250-ENHANCEDSTATUSCODES
250-8BITMIME
250 DSN
quit
221 2.0.0 Bye
Connection closed by foreign host.

同樣地我們應該如上看見成功的連線。現在我們可以透過發送特別的字串來測試掃描能否正常運作。

測試 SpamAssassinGTUBE(Generic Test for Unsolicited Bulk Email)字串。

測試 ClamAV 的 EICAR 字串。

請進入 /usr/share/doc/amavisd-new-2.5.4/test-messages 這個目錄,然後執行:

perl -pe 's/./chr(ord($&)^255)/sge' <sample.tar.gz.compl | zcat | tar xvf -

把測試的樣本郵件解壓出來。請利用這兩個指命來發送一個垃圾郵件樣本及一封有毒的郵件:

$ sendmail -i your-address@example.com < sample-virus-simple.txt
$ sendmail -i your-address@example.com < sample-spam-GTUBE-junk.txt

請以你的實際電郵地址取代 your-address@example.com 。在你的郵件日誌(即 /var/log/maillog)內查閱來自 amavis 的掃描結果,而你應該看見 Passed SPAMMY 或 Blocked INFECTED (Eicar-Test-Signature)。現在你應該檢查你的設定乎合你的期望。

5. SELinux

/!\ 解決 SELinux 問題的其中一個權宜之計就是在  /etc/selinux/config  內臨時以 permissive 替代 enforcing。這個做法還有一個好處,就是所須的資訊將會收錄在位於 /var/log/audit/ 的 SELinux 日誌內。詳情請參閱這篇有關 SELinux 的文章。

當 SELinux 已被啟用並在 enforcing 模式下,amavisd 及 ClamAV 須要一些額外的政策。下面的 SELinux 政策模塊是這樣製定的:在(已完整地更新)CentOS 5 上設定 SELinux 為 permissive 模式,然後按上面指引設置 amavisd/ClamAV/SpamAssassin,接著遵照 SELinux 教學文檔內的描述把 AVC 的錯誤日誌傳給 audit2allow。

我們將會為 amavisd 及 ClamAV 建立兩個自訂的 SELinux 政策模塊,它們分別是 amavisdlocal 及 clamlocal(SpamAssassin 並不須要一個自訂的 SELinux 政策)。請把以下內容分別剪貼到 amavisdlocal.te 及 clamlocal.te:

module amavisdlocal 1.0;

require {
        type traceroute_port_t;
        type pgpkeyserver_port_t;
        type amavis_var_lib_t;
        type amavis_t;
        type clockspeed_port_t;
        class udp_socket name_bind;
        class lnk_file { read create unlink getattr };
}

#============= amavis_t ==============
allow amavis_t clockspeed_port_t:udp_socket name_bind;
allow amavis_t pgpkeyserver_port_t:udp_socket name_bind;
allow amavis_t traceroute_port_t:udp_socket name_bind;
allow amavis_t amavis_var_lib_t:lnk_file { read create unlink getattr };

module clamlocal 1.0;

require {
        type proc_t;
        type var_t;
        type sysctl_kernel_t;
        type clamd_t;
        class file { read getattr };
        class dir { read search };
}

#============= clamd_t ==============
allow clamd_t proc_t:file { read getattr };
allow clamd_t sysctl_kernel_t:dir search;
allow clamd_t sysctl_kernel_t:file read;
allow clamd_t var_t:dir read;
allow clamd_t var_t:file { read getattr };

現在建立及載入 amavisdlocal 模塊:

# checkmodule -M -m -o amavisdlocal.mod amavisdlocal.te
checkmodule:  loading policy configuration from amavisdlocal.te
checkmodule:  policy configuration loaded
checkmodule:  writing binary representation (version 6) to amavisdlocal.mod
# semodule_package -o amavisdlocal.pp -m amavisdlocal.mod
# semodule -i amavisdlocal.pp

然後為 clamlocal 重複一次:

# checkmodule -M -m -o clamlocal.mod clamlocal.te
checkmodule:  loading policy configuration from clamlocal.te
checkmodule:  policy configuration loaded
checkmodule:  writing binary representation (version 6) to clamlocal.mod
# semodule_package -o clamlocal.pp -m clamlocal.mod
# semodule -i clamlocal.pp

最後,檢查我們自訂的本地 SELinux 模塊已被載入:

# semodule -l
amavis  1.1.0
amavisdlocal    1.0
ccs     1.0.0
clamav  1.1.0
clamlocal       1.0
dcc     1.1.0
evolution       1.1.0
iscsid  1.0.0
mozilla 1.1.0
mplayer 1.1.0
nagios  1.1.0
oddjob  1.0.1
pcscd   1.0.0
postgrey        1.0
pyzor   1.1.0
razor   1.1.0
ricci   1.0.0
smartmon        1.1.0

5.1. CentOS 6

CentOS 6 須要額外的步驟。多謝 http://lists.centos.org/pipermail/centos-docs/2012-October/004994.html 中的 Harald Oehlmann。

Amavis 把所有郵件內容及附件(子目錄「部件」)放進 /var/amavis/tmp 下的子目錄內。病毒掃描器會掃描這些檔案及把結果寫進這個目錄內。

SELinux 會阻止病毒掃描器存取這個目錄,導致 /var/log/mail 內出現 (!)run_av (ClamAV-clamscan) FAILED 等錯誤。

請完成以下步驟來容許 clam-av 使用此介面:

--se_clamav_amavis.te--
# ***HaO 2012-09-30: 新增規則容讓 clamav 存取 amavis 檔案
# 和寫出 ok 檔,與及建立臨時資料夾
module clamscanamavis 1.0;
require {
        type clamscan_t;
        type amavis_var_lib_t;
        class file {getattr read open write create unlink};
        class dir {search read getattr open write add_name create
setattr remove_name rmdir};
}
allow clamscan_t amavis_var_lib_t:file {getattr read open write create
unlink};
allow clamscan_t amavis_var_lib_t:dir {search read getattr open write
add_name create setattr remove_name rmdir};

接著

checkmodule -M -m -o se_clamav_amavis.mod se_clamav_amavis.te
semodule_package -o se_clamav_amavis.pp -m se_clamav_amavis.mod
semodule -i se_clamav_amavis.pp

6. 更新

6.1. SpamAssassin

垃圾郵件正在迅速地改變,新規則亦因應此時常被撰寫出來。通過 sa-update,這些規則可以非常快捷地(有可能在數分鐘內)被發行,並逮捕新的垃圾郵件。請先閱讀關於 sa-update 然後才繼續。要啟用自動更新,請利用你喜歡的編輯開啟 /etc/cron.d/sa-update 並解除內有項目的註釋,令它變為:

10 4 * * * root /usr/share/spamassassin/sa-update.cron 2>&1 | tee -a /var/log/sa-update.log

儲存並離開。這個 cron 項目將會在每日上午 4 時 10 分被執行。

6.2. ClamAV

ClamAV 利用 freshclam 來更新病毒的定義。它們是通過 /etc/cron.daily/freshclam 這個 cron 腳本進行自動更新的。你不必做任何事情。你可以查閱你的 /var/log/clamav/freshclam.log 日誌檔來確定你的更新是否成功。

7. 連結

Amavisd-new 的安裝在 /usr/share/doc/amavisd-new-2.5.4/ 內包括大量的文檔,而讀者尤其可參考 /usr/share/doc/amavisd-new-2.5.4/README.postfix.html。它的網上版本位於:http://www.ijs.si/software/amavisd/README.postfix.html

http://www.linuxjournal.com/article/7778

http://www200.pair.com/mecham/spam/clamav-redhat-amavis.html

http://www200.pair.com/mecham/spam/clamav-amavisd-new.html

...

Translation of revision 35


2023-09-11 07:23