最後更新: 2018-12-26
介紹
Sieve 是一種 script language 來, 它是專為處理 E-Mail 而設的.
而且它具有防止帳戶執行任何指令的好處, 所以很安全.
目錄
- Enable sieve plugin support for dovecot-lmtp
- managesieve
- Script Compiling
- 自動回覆(autoresponder) - vacation
- Filter Spam Rules
- If
- match 的方式
- Action
- Dovecot 與 Sieve
- Plugin - include
- Plugin - address
- Plugin - header
- Plugin - date
-
Tips
- sieve file link
- ":is" vs ":contains"
- Case insensitively - doc
補充
* ISPConfig 係透過 cron jobs 去發動修改的
Enable sieve plugin support for dovecot-lmtp
Install
apt-get install dovecot-sieve dovecot-managesieved
Configure
/etc/dovecot/conf.d/15-lda.conf
protocol lda {
# Space separated list of plugins to load (default is global mail_plugins).
mail_plugins = $mail_plugins sieve
}
/etc/dovecot/conf.d/20-lmtp.conf
protocol lmtp { postmaster_address = [email protected] mail_plugins = $mail_plugins }
/etc/dovecot/conf.d/90-sieve.conf
plugin { # Per-user sieve settings sieve_dir = /%Lh/sieve # sieve=/var/vmail/%d/%n/.sieve sieve = /%Lh/sieve/dovecot.sieve # Global setting sieve_global_path = /var/lib/dovecot/sieve/default.sieve sieve_global_dir = /var/lib/dovecot/sieve/ }
managesieve
Package: dovecot-pigeonhole
Port: 4190/TCP
Config
# Default: 0.0.0.0:4190
protocols = ... sieve
# Service 改 listen 127.0.0.1
service managesieve-login {
inet_listener sieve {
# Default: 0.0.0.0
address = 127.0.0.1
port = 4190
}
}
# 派信時發動 sieve
protocol lmtp {
mail_plugins = $mail_plugins sieve
...
}
Test
telnet localhost 4190
"IMPLEMENTATION" "Dovecot Pigeonhole" "SIEVE" "fileinto reject envelope encoded-character vacation subaddress comparator-i; ... "NOTIFY" "mailto" "SASL" "PLAIN LOGIN" "VERSION" "1.0" OK "Dovecot ready."
Log
2022-06-13 10:26:50 managesieve-login: Info: Disconnected: Too many invalid commands. (no auth attempts): rip=127.0.0.1, lip=127.0.0.1, secured
Script Compiling
雖然 sieve 是 script 來, 但為了提升執行速度,
它會在第一次執時時 compile 成 binary 的格式存放
P.S.
當 Script 有問題而不能執行時, E-Mail 會被直接放入 Mailbox
log
Dec 31 15:27:17 vm dovecot: lda([email protected]): sieve: Failed to compile script `/var/vmail/datahunter.org/postmaster/sieve' (view user logfile `/var/vmail/datahunter.org/postmaster/sieve.log' for more information)
自動回覆(autoresponder) - vacation
回覆次數:
:days 是用來設定 n 天內同一個寄件者來信時, sieve 不再執行 auto reply
- min: 1
- default: 7
會保存回覆過的記錄: .dovecot.lda-dupes
dovecot.sieve:
# 亦可以寫成 require ["vacation"]; require "vacation"; if true { vacation :subject "text string" :days 1 # 只有 "To" Email1, Email2 才會解發自動回覆, 不填即所有人都有 :addresses ["Email1", "Email2"] "要自動回覆的內容, 可以多行, 但別縮排 直到遇到它 "; # Default action 係會 keep 的, 所以不用加 "keep" #keep; }
收信後會 compile 成 dovecot.svbin
P.S.
# 有時段的回覆
#### Autoreply if allof(currentdate :value "ge" "iso8601" "2022-08-20T00:00:00", currentdate :value "le" "iso8601" "2022-09-15T00:00:00") { .... }
Filter Spam Rule
dovecot.sieve
# fileinto: for putting mail into a imap folder # mailbox: for creating imap folder if not exists require ["fileinto", "mailbox"]; # rule:[Move Spam to Junk Folder] if header :matches ["X-Spam-Flag"] ["YES"] { #### 對於 hit 中的 mail 處理 #1 Keep this mail in INBOX. #keep; <-- 沒有 stop, reject, discard 的話, 它就是 Deault "keep" #2 move it to 'Junk', and stop fileinto :create "Junk"; #3 If you ensure they are spam, you can discard it. #discard; # 不執行之後的 sieve rule, 跳出 sieve stop; }
Notes
1) "X-Spam-Flag" 係 Amavisd 加上去的, Value 得 YES / NO
If
if ... { ... } elsif ... { ... } else { keep; }
match 的方式
":is"
an absolute match
* The empty key ("") only ":is" matches with the empty value.
":contains"
A sub-string match, useful if we know a part of a string
":matches"
A wildcard match using the characters "*" and "?"
The entire value must be matched.
- "?" for one unknown character and the asterisk
- "*" for zero or more unknown characters
e.g.
if header :matches "subject" ["*test*1234*", "*test*5678*"] { ... }
* "?" and "*" may be escaped as "\\?" and "\\*"
":regex"
- .
- ?
- *
- +
- [a-z]
- [^ ]
- {n,m}
- ^
- $
- |
- ( )
Action
- redirect
- copy
- fileinto
- reject
- discard
"redirect" extension
# One email one line. redirect "[email protected]"; redirect "[email protected]";
"redirect" 後自己係沒有 copy 的
redirect 人數限制
cat /var/vmail/Mydomain/MyUser/.sieve.log
main script: line 8: error: number of redirect actions exceeds policy limit (5 > 4).
Setting
# 當 over 時, 全部人都不 forward sieve_max_redirects = 10
防 loop 死
if allof (header :contains "X-Sieve-Redirected-From" "me@domain", exists "X-Sieve")
{
discard;
}
P.S.
redirect 後會加了 "X-Sieve" header
X-Sieve: Pigeonhole Sieve 0.5.8 (b7b03ba2) X-Sieve-Redirected-From: recipient@domain
server log
先在 t1 時間完成後再在 t2 時間在 u2@ 派信
t1 dovecot: lda(u1@DOMAIN): sieve: msgid=<ID@u22>: forwarded to <u2@DOMAIN> t1 dovecot: lda(u1@DOMAIN): sieve: msgid=<ID@u22>: stored mail into mailbox 'INBOX' t2 dovecot: lda(u2@DOMAIN): sieve: msgid=<ID@u22>: stored mail into mailbox 'INBOX'
discarded duplicate forward
# user1 -> user2 log
... dovecot: lda(u1@domain): sieve: msgid=...: discarded duplicate forward to <u2@domain>
已經有此 Message ID 存在
Config
plugin { ... # Default: 12h sieve_redirect_duplicate_period = 300 }
* The period is specified in s(econds), unless followed by a d(ay), h(our) or m(inute)
* If an account forwards many messages, it may be necessary to lower this setting.
(To prevent the ~/.dovecot.lda-dupes database file from growing to an impractical size)
vmail: /home/vmail/DOMAIN/USER
它的作用: halt potential mail loops
Source Code: pigeonhole/src/lib-sieve/cmd-redirect.c
"copy"
defined in RFC 3894 adds the ":copy" parameter to permit "redirect" to take effect
without cancelling the default action of saving the message to the "INBOX".
e.g.
if allof (header :contains "to" "me@domain")
{
redirect :copy "another@domain";
}
"fileinto"
require ["fileinto", "reject"];
e.g.
fileinto ".trash";
"reject"
reject "彈回不收";
"discard"
將信件直接 Delete, 不是放入垃圾筒 (入垃圾筒係透過 fileinto 實現)
Dovecot 與 Sieve
dovecot 是其中一個用 sieve 去處理 E-Mail 的 E-Mail Server
設定如下
plugin { # 限制 Source 檔大小 sieve_max_script_size = 1M sieve_max_actions = 32 sieve_max_redirects = 4 sieve = /var/sieve-scripts/%u.sieve }
global sieve:
sieve_global_dir = /var/vmail/sieve sieve_global_path = /var/vmail/sieve/dovecot.sieve
如果用户有(存在) 自己 sieve 文件, 那 Gobal 的將不會被執行 !!
Plugin - include
注意:
只有 Dovecot v2.x 支援此 plugin
功能:
在 sieve script 上 include 其他 sieve script
用法:
The "include" command may appear anywhere in the script where a control structure is legal.
Example:
dovecot.sieve:
require ["include"]; include :global "admin"; include :personal "managesieve";
global 與 personal
- global: sieve_global_dir = /var/vmail/sieve
- personal: sieve_global_path = /var/vmail/sieve/dovecot.sieve
/etc/dovecot/dovecot.conf:
plugin { # Directory for :personal include scripts. The default is to use home directory. sieve_dir = %h/sieve # Directory for :global include scripts (not to be confused with sieve_global_path). # If unset, the include fails. sieve_global_dir = /etc/dovecot/sieve/ }
Usage: return
The "return" command stops processing of the currently included script only and returns processing control to the script which includes it.
If used in the main script (i.e. not in an included script), it has the same effect as the "stop" command, including the appropriate "keep" action if no other actions have been executed up to that point.
Roundcube Plugin:
// List of reserved script names (without extension).
// Scripts listed here will be not presented to the user.
// Can not create
$rcmail_config['managesieve_filename_exceptions'] = array('admin');
/var/log/sieve.log
Aug 20 17:48:02 lda(test@receive): Error: sieve: failed to open script //var/vmail/receive/test//sieve/dovecot.sieve (view user logfile //var/vmail/receive/test//sieve/dovecot.sieve.log for more information) Aug 20 17:48:02 lda(test@receive): Info: msgid=<ID@SENDER>: saved mail to INBOX
Plugin - address
* address 支援的 feild 有 "from", "to"
(對比起 header , address 額外支援 ":localpart", ":domain")
# 某 Domain
if address :is :domain "to" "example.com" { fileinto "examplecom"; }
# 某些人
if address :is ["from", "sender"] ["[email protected]", "[email protected]", "[email protected]"] { fileinto "friends"; }
Plugin - header
空格
Each header line is allowed to have whitespace nearly anywhere in the line
Extra spaces between the header name and the ":" in a header field are ignored.
"From:XXX" 等價 "From : XXX"
所以 header 的關鍵是 ":"
某人
if header :regex ["from"] ["test@domain$"] { discard; stop; }
* 預設不分有小寫
to 與 cc
if header :contains ["to", "cc"] [ "user1@domain", "user2@domain", ] { redirect "usera@domain"; redirect "userb@domain"; stop; }
Plugin - date
Extension 功能
- provides a new date test to extract and match date/time information from structured header fields
- provides a currentdate test that operates
The type of match defaults to ":is" and the default comparator is "i;ascii-casemap"
Usage:
date [<":zone" <time-zone: string>> / ":originalzone"] [COMPARATOR] [MATCH-TYPE] <header-name: string> <date-part: string> <key-list: string-list>
Zone and Originalzone Arguments
:originalzone
argument specifies that the time zone offset originally in the extracted date-time value should be retained.
:zone
specifies a specific time zone offset that the date-time value is to be shifted to prior to testing.
* If both the :zone and :originalzone arguments are omitted, the local time zone MUST be used.
* It is an error to specify both :zone and :originalzone.
Time format:
- "date" => "yyyy-mm-dd"
- "hour" => "00" .. "23".
- "minute" => "00" .. "59"
Example
[1]
require ["date", "relational", "fileinto"]; if allof(header :is "from" "[email protected]", date :value "ge" :originalzone "date" "hour" "09", date :value "lt" :originalzone "date" "hour" "17") { fileinto "urgent"; }
[2]
require ["date", "relational", "vacation"];
if allof(currentdate :value "ge" "date" "2007-06-30",
currentdate :value "le" "date" "2007-07-07")
{ vacation :days 7 "I'm away during the first week in July."; }
Tips
sieve file link
dovecot.sieve --soft_link--> managesieve.sieve
":is" vs ":contains"
不知為何 discard 不到 system@ 的 "Info: " 來信
# rule:[Delete eRO mail] if allof (header :contains "subject" "Info: ", header :is "from" "[email protected]") { discard; }
原因: From 不是純 email address, 所以不能用 ":is" 去匹配
From: Alert System <[email protected]>
Case insensitively
在 ":is", ":contains", ":matches" 及 ":regex" 後加上 ":comparator" tag
:comparator "i;ascii-casemap"
mapping uppercase characters to lowercase. This is a case-insensitive comparison.
Notes
* Folder names are case-sensitive