Exim 應該將失敗消息的 id 保存到數據庫(或至少將其通過管道傳輸到 shell 腳本)
設置
我們在 Debian Squeeze 6 (Squeeze) (LTS) 上執行 exim 版本 4.72。
Exim 僅用於外發電子郵件- 它不處理來自其他伺服器/域的電子郵件 -它只處理來自本地執行的 java (web) 應用程序的(外發)消息。
對於匿名化,假設我們用於公司電子郵件地址的域以及執行我們的 Java Web 應用程序的域稱為example.com。
的背景
我們有一個用 java 框架編寫的 Web 應用程序,它允許它的使用者發送電子郵件。這些電子郵件的
envelope-from
地址屬於我們的域:support@example.com。從技術上講,這些電子郵件是在本地創建並從 java Web 應用程序(我們使用JavaMail API)發送到 exim,然後最終將它們發送到他們的目標地址。
傳入我們域的電子郵件由 Google Apps/Gmail 處理(我們還設置了 SPF 記錄等)
。到目前為止,此設置執行良好,沒有任何問題。
為了完成,這是我們的(匿名)
update-exim4.conf.conf
:root@server:~# cat /etc/exim4/update-exim4.conf.conf # ... comments are skipped ... dc_eximconfig_configtype='internet' dc_other_hostnames='node1.example.com' dc_local_interfaces='127.0.0.1 ; ::1' dc_readhost='' dc_relay_domains='' dc_minimaldns='false' dc_relay_nets='' dc_smarthost='' CFILEMODE='644' dc_use_split_config='true' dc_hide_mailname='' dc_mailname_in_oh='true' dc_localdelivery='maildir_home'
問題
最近我們收到退回郵件,因為我們的 Java Web 應用程序的使用者試圖向不存在的電子郵件地址發送電子郵件。
讓我們看看(匿名)日誌文件中的這種情況:
root@server:~# cat /var/log/exim4/mainlog # try to send an email to an unrouteable address 2014-06-01 17:34:07 1Wr7lv-0000CR-G0 <= support@example.com H=localhost (node1.example.com) [127.0.0.1] P=esmtp S=620363 id=484648301.663.1401636847496.JavaMail.webapp@node1 2014-06-01 17:34:07 1Wr7lv-0000CR-G0 ** doesnotexist@anotherdomain.com: Unrouteable address 2014-06-01 17:34:07 1Wr7lv-0000CR-G0 Completed # Immediately a bounce message is generated and gets send to the the origin sender 2014-06-01 17:34:07 1Wr7lv-0000CU-OZ <= <> R=1Wr7lv-0000CR-G0 U=Debian-exim P=local S=108065 2014-06-01 17:34:09 1Wr7lv-0000CU-OZ => support@example.com R=dnslookup T=remote_smtp H=aspmx.l.google.com [74.125.136.26] X=TLS1.0:RSA_ARCFOUR_SHA1:16 DN="C=US,ST=California,L=Mountain View,O=Google Inc,CN=mx.google.com" 2014-06-01 17:34:09 1Wr7lv-0000CU-OZ Completed
我們想要達到的目標
我們現在想要向 Java Web 應用程序中的每個 Java Web 應用程序使用者顯示(或通知 - 無論如何)他/她想要發送的電子郵件實際上未能發送。
(請記住:退回的電子郵件目前被發送回support@example.com -而不是Web 應用程序使用者的電子郵件地址 - 所以這些使用者不知道他們的電子郵件沒有被發送)
我們現在擁有的基本(和簡單)想法:
讓 exim 盡快將
id
每條失敗消息(在上述情況下為484648301.663.1401636847496.JavaMail.webapp@node1)寫入本地執行的 PostgreSQL 數據庫中,我們知道消息傳遞失敗(或:盡快生成和/或發送相應的退回消息)。如果這是可能的,我們可以很容易地從我們的 java web 應用程序中的數據庫中讀取這些“失敗” ,並且 - 瞧!
id
- 我們可以向使用者顯示他們的消息已被退回。簡單的想法,不是嗎?
只是為了更好地理解:在 JavaMail API 將消息發送到 exim之前
id
,Java Web 應用程序中的每條消息已經生成了所提到的(唯一)s - 所以我們可以確保我們的 Java Web 應用程序知道每條消息的每個消息(以及當然也知道哪個使用者發出了特定的消息)。id
可能的解決方案?
我對如何解決這個問題進行了一些研究,這就是我現在想出的(如果我錯了,請糾正我 - 這些只是我現在的想法):
**方法 1:**將失敗的消息傳遞
id
給 bash 腳本。然後,這樣的 bash 腳本可以輕鬆地將其儲存id
在本地 PostgreSQL 數據庫中。這可能嗎?如何實現?我看到的問題是,一旦消息失敗,就不會執行其他 exim 路由器 - 這意味著我無法編寫處理失敗消息的路由器 - 失敗的消息永遠不會到達路由器。還是我錯了?方法2:我正在考慮的另一種可能性是不查看失敗的原始消息,而是查看新生成的退回消息。據我了解,這條新退回的消息也會通過所有路由器,直到有人接受它為止。所以也許我們可以編寫一個路由器來檢查消息是否是退回消息,然後將其通過管道傳輸到 bash 腳本?但是我怎麼知道一條消息是否是退回消息?它有一個特殊的標題或其他重要的東西嗎?(請記住:由於我們只將 exim 用於外發電子郵件,因此我們可以確定這始終是由我們的 java Web 應用程序使用者“生成”的退回郵件)。
**方法 3:**與方法 2 類似,但我們可以使用系統過濾器來代替編寫路由器,它會處理退回消息,然後執行命令/管道…
**方法 4:**是否可以配置
error_copy
或什error_to
至不是電子郵件地址,而是一個到 shell 腳本的管道(它當然收到了 id 和/或整個失敗的消息)?還有兩件事:似乎 exim 也可以直接與數據庫對話(我在某處讀過這個)——但我不知道如何利用它來解決我所描述的問題。另外:但是這已經解決了,我不想修改任何在 exim 更新時被覆蓋的配置文件
apt-get
(我不想在每次更新後重新配置 exim)。需要你的幫助
你將如何解決這個問題(將失敗
id
的消息保存到數據庫中)?請糾正我如果我在某個地方錯了 - 我對 exim 配置的了解並不深。此外,正如您所看到的,我考慮的解決方案更具理論性 - 如果有人可以向我展示真實世界的配置以及將這些配置放在哪里以使其工作,那就太好了。非常感謝您的幫助!
Exim 版本 4.82.1 有一個稱為 TPDA(Transport Post Delivery Action)的功能,可以讓您完全按照自己的意願行事。您應該能夠切換您的儲存庫源並安裝新版本的 exim,這將為您提供具有該功能的版本。
如果您堅持使用目前發行版提供的版本,我認為這些都不是好的選擇。
- 最好的解決方案是為每條消息創建一個唯一的發件人。例如,獲取收件人,附加時間,然後創建一個 MD5 雜湊,並將其設為發件人@support.example.com。將該資訊放在一個表格中,其中包含有關發送給誰以及誰發送電子郵件的資訊。當您處理入站到域 support.example.com 的電子郵件時,如果收件人與其中一個雜湊值完全匹配,您可以進行查找以查看是誰發送的、通知發件人、禁止收件人將來發送等。
- 下一個方法是將 support@ 電子郵件地址發送到兩個地方:一個是您的支持人員可以閱讀的郵箱,第二個是您編寫的腳本,該腳本確定誰發送了消息,通知發件人,並禁用收件人來自未來的發送等
- 您沒有建議的一個選擇是編寫一個應用程序來跟踪您的日誌文件,解析行並提取傳遞資訊並將其放入數據庫中,然後將問題通知發件人,並禁止收件人將來發送等。