2017年7月10日 星期一

如何防止某段時間寄同一人大量信件 (postfix 利用 postfwd2 達到限制寄件數量)

http://postfwd.org/quick.html

https://ssorc.tw/4296

我今天要來限制一個 Email 帳號一天只能寄 1000 封,超過了就會被擋掉無法再寄
Postfix 官網有提供幾個外掛使用,我拿 postfwd 來使用,postfwd 俗稱 postfix firewall daemon
另一個 policyd 可以參考 http://signalboxes.net/howto/policyd/
以下是 policyd 與 postfwd2 比較表
項目
Policyd v2 (cluebringer)
postfwd2
程式語言 Perl Perl
規則存放 Database FIle
參數調整 設定檔 程式參數
限制信件量 (per user, per domain) V V
ACL V V
Greylisting V V
Check HELO V
Support Amavis V
Check SPF V
WebUI V (沒有帳密驗證)
Date/time based rules
V
DNSBL
V
IPv6 出現格式錯誤 正常
文件 較為清楚 敘述較為模糊、複雜
安裝方式 RPM、手動 手動
設定 看似簡單,但需操作 DB 看似複雜,但邏輯容易理解
記錄檔 符合 Syslog 標準 符合 Syslog 標準
最後更新日期 2013-10-26 (2.014)
(與前一版幾乎沒有差異)
2013-04-18 (1.35)

postfwd 需要的 perl 套件
cpan -i Net::Server::Daemonize
cpan -i Net::Server::Multiplex
cpan -i Net::Server::PreFork
cpan -i Net::DNS
cpan -i IO::Multiplex
再來至 postfwd 下載套件,裡面包含 v1 與 v2 ,我要用 v2 版
解開後放到 /usr/local/postfwd (裡面就有 bin doc etc man plugins sbin tools 等目錄)
編輯 /usr/local/postfwd/bin/postfwd-script.sh,這是啟動檔
# 用 postfwd2
#PFWCMD=/usr/local/postfwd/sbin/postfwd
PFWCMD=/usr/local/postfwd/sbin/postfwd2
# rulesetconfig file 改名
PFWCFG=/etc/postfix/postfwd2.cf
# pidfile 改名
PFWPID=/tmp/postfwd2.pid
# 使用者換成 postfix
PFWUSER=postfix
PFWGROUP=postfix
# port 改成 10045
#PFWPORT=10040
PFWPORT=10045
# --shortlog 拿掉,因為 v2 不支援,加入 --keep_rates --save_rates /tmp/postfwd2.cache,才能有計數器功能,並額外記錄於 syslog
PFWARG="--summary=600 --cache=600 --cache-rbl-timeout=3600 --cleanup-requests=1200 --cleanup-rbls=1800 --cleanup-rates=1200 --keep_rates --save_rates /tmp/postfwd2.cache --logname postfwd2 --facility local0"
# 在 reload 的地方,把 -- reload 改成 --reload
放到 init.d 裡啟動
ln -s /usr/local/postfwd/bin/postfwd-script.sh /etc/init.d/postfwd2
編輯 /etc/postfix/main.cf,整合到 postfix 裡
# 如果有其它參數,這個一定要放在最前面
smtpd_recipient_restrictions = check_policy_service inet:127.0.0.1:10045
編輯 /etc/postfix/postfwd2.cf,這是放 rules 的地方,每設定一次就要 restart postfwd2
# 每天只允許同一寄件者只能寄 1000 封,多了就阻擋
# sender 是固定參數
# 1000 就是 1000 封
# 86400 就是 一天的時間 (秒)
# 後面的字串就是退信訊息
# Reject sender sends over 1000 per day
id=R01; action=rate(sender/1000/86400/REJECT sender $$sender exceeded limit of 1000 per day)
# 每天只允許同一來源 IP 只能寄 2000 封
# Reject client sends over 2000 per day
id=R02; action=rate(client_address/2000/86400/REJECT client $$client_address exceeded limit of 2000 per day)
編輯 /etc/rsyslog.conf,我把 log 額外放,預設是會放在 maillog 裡
*.info;mail.none;authpriv.none;cron.none;local0.none /var/log/messages
local0.* /var/log/postfwd2
service rsyslog restart
編輯 /etc/logrotate.d/syslog,把 log 檔作輪替
# 加入
/var/log/postfwd2
秀出定義在 conf 裡的 rule
/usr/local/postfwd/sbin/postfwd2 -f /etc/postfix/postfwd2.cf -C
查看計算結果
/usr/local/postfwd/sbin/postfwd2 --dumpcache
%rate_cache -> %client_address=127.0.0.1 -> @list -> 'R02+2000_86400'
%rate_cache -> %client_address=127.0.0.1 -> %R02+2000_86400 -> @action -> 'REJECT client 127.0.0.1 exceeded limit of 2000 per day'
%rate_cache -> %client_address=127.0.0.1 -> %R02+2000_86400 -> @count -> '1'
%rate_cache -> %client_address=127.0.0.1 -> %R02+2000_86400 -> @maxcount -> '2000'
%rate_cache -> %client_address=127.0.0.1 -> %R02+2000_86400 -> @rule -> 'R02'
%rate_cache -> %client_address=127.0.0.1 -> %R02+2000_86400 -> @time -> '1391677750.95566'
%rate_cache -> %client_address=127.0.0.1 -> %R02+2000_86400 -> @ttl -> '86400'
%rate_cache -> %client_address=127.0.0.1 -> %R02+2000_86400 -> @type -> 'rate'
%rate_cache -> %client_address=127.0.0.1 -> %R02+2000_86400 -> @until -> '1391764150.95566'
%rate_cache -> %sender=cross@ssorc.tw -> @list -> 'R01+10_86400'
%rate_cache -> %sender=cross@ssorc.tw -> %R01+10_86400 -> @action -> 'REJECT sender cross@ssorc.tw exceeded limit of 1000 per day'
%rate_cache -> %sender=cross@ssorc.tw -> %R01+10_86400 -> @count -> '1'
%rate_cache -> %sender=cross@ssorc.tw -> %R01+10_86400 -> @maxcount -> '1000'
%rate_cache -> %sender=cross@ssorc.tw -> %R01+10_86400 -> @rule -> 'R01'
%rate_cache -> %sender=cross@ssorc.tw -> %R01+10_86400 -> @time -> '1391677750.95566'
%rate_cache -> %sender=cross@ssorc.tw -> %R01+10_86400 -> @ttl -> '86400'
%rate_cache -> %sender=cross@ssorc.tw -> %R01+10_86400 -> @type -> 'rate'
%rate_cache -> %sender=cross@ssorc.tw -> %R01+10_86400 -> @until -> '1391764150.95566'
刪除計數結果,重新計算
/usr/local/postfwd/sbin/postfwd2 --delrate="sender=cross@ssorc.tw"
rate cache item 'sender=cross@ssorc.tw' removed
以上是清 sender,下面的就是清來源 IP 歸零
/usr/local/postfwd/sbin/postfwd2 --delrate="client_address=127.0.0.1"
rate cache item 'client_address=127.0.0.1' removed
如果成功外阻擋的話,maillog 會看到
Feb 6 16:51:35 ssorc postfix/smtpd[24633]: NOQUEUE: reject: RCPT from localhost[127.0.0.1]: 554 5.7.1 <cross@ssorc.tw>: Recipient address rejected: sender cross@ssorc.tw exceeded limit of 1000 per day; from=<cross@ssorc.tw> to=<cross@ssorc.tw> proto=ESMTP helo=<ssorc.tw>
至於 postfix 要怎麼新增 virtual domain
編輯 /etc/postfix/main.cf
smtpd_recipient_restrictions = check_policy_service inet:127.0.0.1:10045,permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination
virtual_mailbox_domains = ssorc.tw
virtual_mailbox_maps = hash:/etc/postfix/virtual_mailbox_maps
virtual_mailbox_base = /var/vmail
virtual_uid_maps = static:1000
virtual_gid_maps = static:1000
編輯 /etc/postfix/virtual_mailbox_maps
cross@ssorc.tw ssorc.tw/cross/Maildir/
接著作完以下
postmap /etc/postfix/virtual_mailbox_maps
useradd -u 1000 -d /var/vmail -s /sbin/nologin vuser
mkdir /var/vmail/ssorc.tw/
chown -R vuser.vuser /var/vmail

postfwd 提供一個流程圖 可以看一下了解運作方式

http://postfwd.org/postfwd-ARCH.png

其它的限制也可以參考 postfwd rate limit examples
我上面的測試是針對所有 domain 的,但假如你要針對某個也是可以的
像它範例有提到 client_name=unknown 就針對來源是未知的
收件者是某某某 recipient_domain=XXX.tw
除了針對 sender 與 client_address 也可以針對 收件者名稱
id=R03; action=rate(recipient/20/86400/REJECT recipient $$recipient exceeded limit of 20 per day)
也可以針對信件大小 (不過我試不出來,從 cache 裡看不到相關資訊)
id=R04; action=size(recipient/2048000/86400/REJECT only allow 2mb per day)

Postfix 郵件過濾

https://blog.mikuru.tw/archives/36

這次我們講到如何利用 postfix 提供的種種過濾機制把討厭的 SPAM 擋在門外。
過濾的目標包括:來源、寄件者、收信者、附加檔、本文內容, etc.

[說明]
Postfix 本身就具備了兩種郵件過濾的機制:分別是標頭過濾(header)與本體過濾(body)
要使這兩個功能生效,你必須在 /usr/local/etc/postfix/main.cf 中加入:
header_checks = regexp:/usr/local/etc/postfix/header_checks
body_checks = regexp:/usr/local/etc/postfix/body_checks
header_checksbody_checks 分別代表了你的標頭過濾檔及本過濾檔。
過濾檢查的描述型態可以有三種方式
*hash:雜湊
*regexp:正規表示式(Regular Expression)
*pcre:相容於Perl之正規表示式(PCRE, Perl Compatable Regular Expression)
在本文中,我們將以 regexp 作為過濾檢查的描述型態。
[標頭過濾]
標頭,就是信件附帶的資訊,包含標題(Subject)、送件者 (From:)、收件者 (To:)及其他相關資訊,
甚至連你用什麼 mail client 來發送這封信件,都可以在標頭中看出端倪。
要設定標頭過濾的判斷條件,我們必須編輯 /usr/local/etc/postfix/header_checks
*對於發信者來源的過濾,我們可以加上:
/^From:.*badguy\.com/       DISCARD
*對於郵件標題的過濾,我們可以加上:
/^Subject:.*香豔刺激/        DISCARD
*對於途中經過轉寄主機的過濾,我們可以加上:
/^Received.*relay\.asshole\.com/  DISCARD
*對於郵件附加檔案的過濾,我們可以加上:
/filename\=.*\.pif/         DISCARD
/name\=.*\.pif/          DISCARD
[本體過濾]
本體,就是信件本身的內容囉。也就是發信者要呈現給收件者的訊息。
要設定本體過濾的判斷條件,我們必須編輯 /usr/local/etc/postfix/body_checks
*對於特定字串的過濾,我們可以加上:
/^dirty_words/           DISCARD