用 Postal 無痛架設 Mail Server
大約在兩週前的 Ruby Weekly 中,看到了一個名為 Postal 的開源專案。
介紹是這樣寫的:
一個可以讓你建置類似於 Mandrill 或者 SendGrid 服務的開源專案。
因為是使用 Rails 開發,再加上這幾年因為垃圾信的氾濫,所以在 Production 環境都會選擇使用像是 AWS SES 或者 SendGrid 這類服務來發信,除了不容易退信以及有完善的 SMTP / HTTP API 之外,也能夠避免被當作是垃圾信。
不過 Postal 特別的地方在於,如果有完整的設定完成的話,在垃圾信的檢測中甚至能夠比 AWS SES 這類服務獲得更高的分數。
Postal 簡介
Postal 是由一家位於英國的公司 aTech Media 所開發,用於內部的發信處理工具。使用的是 Ruby 以及 Rails 進行開發,並且具備了市面大多發信類的 SaaS 服務所提供的功能。
環境準備
為了簡化架設流程,在五倍紅寶石所進行的架設方式是採用 Docker 的方式來架設,如果要採用實體機器安裝的話,則需要以下預先安裝好的環境。
目前並沒有官方版本的 Docker Image 主要是 aTech 沒有這方面的經驗,詳細可以參考 #8 這個 Issue 來追蹤中況狀。
- Ubuntu 16.04+ (並安裝
build-essential
) - Ruby 2.3+
- MySQL
- RabbmitMQ
- Node.js
- Git
- 兩組 Public IP (如果要使用 Fast Server)
大部分的套件官方都沒有指定版本,不過可以的話盡量以最新穩定版本為主會比較保險。
後面的文章會以 Docker 的方式來介紹,如果想要採用實體機器安裝的方式可以參考官方的 Wiki。
啟動容器
這篇文章採用的是 ALinuxNinja/docker-postal 的版本,在開始前請先安裝好 Docker Compose 以及最新版本的 Docker。
Step1
首先,先把 Repoistory Clone 到本機。
cd /home/deploy/docker
git clone https://github.com/ALinuxNinja/docker-postal postal
Step2
因為主機上還有不少其他服務,所以要先修改 docker-compose.yml
將對應的 Post 調整,再用 Nginx 做反向代理。
version: "3"
services:
postal:
build: .
image: postal
container_name: postal
ports:
- 9080:5000
- 2525:2525
- 9180:5010
- 9181:5011
volumes:
- ./data/postal:/opt/postal/config
- ./data/docker:/docker
environment:
- MYSQL_ROOT_PASSWORD=changeme
- MYSQL_DATABASE=postal
- RABBITMQ_DEFAULT_USER=postal
- RABBITMQ_DEFAULT_PASS=changeme
- RABBITMQ_DEFAULT_VHOST=postal
mysql:
image: mariadb:10
container_name: postal_mysql
volumes:
- ./data/mysql:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=changeme
- MYSQL_DATABASE=postal
rabbitmq:
image: rabbitmq:3-alpine
container_name: postal_rabbitmq
environment:
- RABBITMQ_DEFAULT_USER=postal
- RABBITMQ_DEFAULT_PASS=changeme
- RABBITMQ_DEFAULT_VHOST=/postal
資料庫的密碼請自行修改,這邊一共會用到四組 Port。
- 5000 - Web 介面用的 Port 之後會用 Nginx 做反向代理並且加上 SSL
- 2525 - SMTP 伺服器的 Port 之後會用 iptables 做 Forwording (也可以直接 Expose 到 25 上)
- 5010/5011 - Fast Server 用的 Port 用來追蹤是否有開信以及點擊連結
最後兩個 Port 如果沒有要使用的話,可以直接跳過。
Step3
完成設定後先跑一次 docker-compose up
將容器建立起來,在首次運作的時候會自動將 Postal 所需的設定檔生成。
Step4
生成完畢後,我們要修改 data/postal/postal.yml
這個檔案,將 Mail Server 的相關設定都配置到定位。
web:
# The host that the management interface will be available on
host: mail.5xruby.tw
# The protocol that requests to the management interface should happen on
protocol: https
fast_server:
# This can be enabled to enable click & open tracking on emails. It is disabled by
# default as it requires a separate static IP address on your server.
enabled: true
dns:
# Specifies the DNS record that you have configured. Refer to the documentation at
# https://github.com/atech/postal/wiki/Domains-&-DNS-Configuration for further
# information about these.
mx_records:
- mx.mail.5xruby.tw
smtp_server_hostname: smtp.5xruby.tw
spf_include: spf.mail.5xruby.tw
return_path: rp.mail.5xruby.tw
route_domain: routes.mail.5xruby.tw
track_domain: track.mail.5xruby.tw
主要需要修改的是 web
fast_server
dns
這三個部分,之後使用重新用 docker-compose up -d
的方式開啟伺服器,並且繼續接下來的動作。
Step5
原本我們可以直接用 postal make-user
來生成管理員,但是因為是封裝在 Docker Container 中的關係,需要稍微調整一下做法。
docker exec -it postal /opt/postal/bin/postal make-user
# 輸入管理員資訊
信箱請跟剛剛設定的 Domain 一樣,例如 example@5xruby.tw
這樣才會自動被判定成管理員,否則會被視為一般使用者,此時因為 SMTP 還沒有設定完成,所以會出現無法收到驗證碼的狀況。
Step6
接下來要設定 DNS 的紀錄,這邊假設 Public IP 是 192.168.1.3 & 192.168.0.4
mail.5xruby.tw. IN A 192.168.1.3
mx.mail.5xruby.tw. IN A 192.168.1.3
spf.mail.5xruby.tw. TXT "v=spf1 ip4:192.168.1.3 ~all"
rp.mail.5xruby.tw. IN MX 10 mx.mail.yourdomain.com
rp.mail.5xruby.tw TXT "v=spf1 a mx include:spf.mail.5xruby.tw ~all"
routes.mail.5xruby.tw. IN MX 10 mx.mail.5xruby.tw
track.mail.5xruby.tw. IN A 192.168.0.4
以及一個 DKIM 紀錄,以往這需要自己生成,在這邊 Postal 已經做好自動處理的動作,在正確設定 postal.yml
的設定後,用下面的方式生成。
docker exec -it postal /opt/postal/bin/postal default-dkim-record
之後再補上一筆 DKIM 的紀錄到 DNS 上。
postal._domainkey.rp.mail.5xruby.tw. IN TXT "v-DKIM1; ...."
Step7
有了管理員帳號後,就可以到 Postal 的管理介面新增一個 Mail Server。
首先第一步是新增 Organization 來管理一系列的 Mail Server。
接下來就可以新增 Mail Server 來使用。
有了 Mail Server 之後,就可以新增 Domain。可以針對 Server 新增只能在特定 Server 上使用的 Domain 或者整個 Organization 都可以通用的 Domain。
使用的限制跟 SendGrid 等類似,只有擁有權限的狀況才能夠透過該 Domain 發信。
設定成功後就會看到綠色的標記,非常容易懂。
如果使用 CloudFlare 來管理 DNS 的話,使用 CNAME rp.mail.5xruby.tw 會直接被轉回 IP 但是目前 Postal 無法判斷,可以的話請分開設定,或者用另一個 Domain 設定預設的 Reture Path DNS
Step8
有了 Mail Server 以及正確的 Domain 設定後,我們需要給 Postal 一個可用的 Certificate 來當作系統發信用的帳號密碼。
新增完畢後,把 Name
和 Key
複製到之前跳過的 postal.yml
設定檔中 SMTP 設定的部分。
smtp:
host: 127.0.0.1
port: 2525
username: system # Complete when Postal is running and you can
password: your_certificate_key # generate the credentials within the interface.
from_name: Postal
from_address: postal@yourdomain.com
設定完畢後,使用 docker-compose restart postal
重新啟動 Postal 就可以正常的發信。
Step9
完成上述設定後,可以到 Mail Tester 上面測試發信的狀況,剛設定好後大概可以得到 8.5/10
左右。
後續還有一些像是 rDNS 等設定,就需要透過網路服務商協助設定。如果是使用 AWS EC2 之類的,也可以透過提交表單進行申請。至於常見的 VPS 廠商大多會在後台提供修改設定的欄位,不過扣除掉因為運氣因素 IP 位置在黑名單中的情況,大多數時候都可以達到 8 分以上。
之前實測 AWS SES 獲得的分數大約是 8.5/10
左右,如果 IP 沒有被視為黑名單的話,達到 9/10
以上的分數,送達率基本上就非常高了。
Tracking
如果要啟用 Fast Server 來追蹤信件開啟狀況,則需要使用第二組 IP 位置來處理。
主要是因為 Nginx 正常情況下無法動態對應任意域名的 SSL 憑證,因此 Postal 直接使用 TCPSocket 模擬 HTTP 連線,直接進行處理。
iptables -t nat -I PREROUTING -d 192.128.0.4 -p tcp -m tcp --dport 80 -j DNAT --to-destination 192.128.0.4:5010
iptables -t nat -I PREROUTING -d 192.128.0.4 -p tcp -m tcp --dport 443 -j DNAT --to-destination 192.128.0.4:5011
iptables -I INPUT -p tcp -m tcp -d 192.128.0.4 --dport 5010 -j ACCEPT
iptables -I INPUT -p tcp -m tcp -d 192.128.0.4 --dport 5011 -j ACCEPT
這邊只需要透過 iptables
進行設定,將封包的流量轉換到對應的 Port 即可。
使用 Docker 可能會因為這樣處理而讓 OUTPUT 流量都被轉換,可以透過增加
-i
設定,只對對外網路的網卡轉換封包,即可避免這個問題。
完成封包轉換的設定後,還需要設定 Let’s Encrypt 負責生成憑證的帳號(信箱)這只是用來計算生成限制的,一般通常不會超過上限。
docker exec -it postal /opt/postal/bin/postal register-lets-encrypt postal@5xruby.tw
完成後可以到 Mail Server 中設定 Tracking Domain 如果 Fast Server 正常運作,則會發現 SSL 憑證呈現啟用的狀態,在未成功啟用的情況下會自動 Fallback 為普通的 HTTP 來做追蹤。
當順利追蹤之後,則可以看到 OPENED
的標記,以及在信件活動中看到開啟的紀錄。
小結
相較於以往架設 Mail Server 的經驗,使用 Postal 的難度相對地減少非常多。在更新以及後續升級上也相當容易,不過目前還在不穩定的狀態,還有很多預想外的問題,需要大家多加小心。
寫這篇文章前一天,會因為沒有啟用 SMTP TLS 的關係而無法使用 Fast Server 的 SSL 模式,不過 aTech 已經在今天修好,目前使用上應該是不會碰到什麼太大的困難。