一直以來
筆者的收信都是透過 Cloudflare 的 Email 轉發功能
將送來的信件轉發到自己的 Gmail 信箱
然而這個服務只能轉發收信 而不能發信
於是筆者決定在自己的伺服器上架設一個 MailServer
來一勞永逸的解決這個問題

事情總是想得很簡單
在我開始架設之前 看著各種教學文
以及官方寫得看起來很清楚的文檔
我以為事情會非常順利的進行下去
但實際上等待我的卻是一個又一個大坑

# 前提

在開始之前 先確認一下自己手邊的環境

# 環境

  • 系統: Ubuntu 22.04
  • Webserver: Nginx/1.26.1
  • UFW: ufw/0.36.1
  • DNS: Cloudflare
  • SSL: acme.sh

# 需求

使用 docker 架設一個 MailServer

# 前置準備

在開始前我們需要先搞好一些前置的設定

# Router Port Forwarding

首先 如果你是自己家的伺服器 或是你有網管權限
請先確認你的路由器有將以下的 port 打開

ports:
      - "25:25"    # SMTP  (explicit TLS => STARTTLS, Authentication is DISABLED => use port 465/587 instead)
      - "110:110"  # POP3  (explicit TLS => STARTTLS)
      - "143:143"  # IMAP4 (explicit TLS => STARTTLS)
      - "465:465"  # ESMTP (implicit TLS)
      - "587:587"  # ESMTP (explicit TLS => STARTTLS)
      - "993:993"  # IMAP4 (implicit TLS)
      - "995:995"  # POP3  (implicit TLS)

請務必要確定 port 是開的
筆者搞這個搞了兩天就是卡在 port 打不出去 ==

# DNS

接下來我們需要設定 DNS
由於我是使用 cloudflare 的 DNS
如果你沒有用 CF 家的服務而是使用網域商提供的 DNS 服務的話
那請自己變通一下

我們需要設定幾個紀錄

  • A 紀錄:這個應該大家都會設定,不如你的網址根本連不到伺服器
  • MX 紀錄:指定信件要導去哪裡
  • TXT 紀錄:
  • PTR 紀錄:做 rDNS 反向解析用的,可以從 IP 反向解析到 domain,這個是為了避免被標記為垃圾郵件

這邊的 example.com 請自行替換成你自己的網域,當然有引號的部分也可以選用 subdomain
例如 mail.example.com,只是筆者想要用主網域因此沒有選用

dig +short TXT miyago9267.com
  "google-site-verification=eWCbTPIIpTueaDXP5h1AjIB83xZTKmMAfgsSL9IgGs8"
  "v=spf1 a mx ip4:我的IP -all"
dig +short MX miyago9267.com
  10 miyago9267.com.
dig +short A miyago9267.com
  我的IP

IP 不給你看勒 ><(雖然你自己去戳就會寫了)

# 防火牆

確定你的防火牆有開放上面那些 port
如果你有加裝 ufw-docker 的話也要記得開

# Docker MailServer

確定好手邊的資源以及確定要做什麼了
接下來就是開始架設 MailServer 了

我們使用的是現成的 Docker 專案 Docker MailServer

# 部屬 docker container

我們不需要 clone 整個專案
需要用到的只有

  • compose.yaml
  • mailserver.env
  • setup.sh (選擇性)

這三個檔案而已

參考

DMS_GITHUB_URL="https://raw.githubusercontent.com/docker-mailserver/docker-mailserver/master"
curl -LO "${DMS_GITHUB_URL}/compose.yaml" && mv compose.yaml docker-compose.yaml
curl -LO "${DMS_GITHUB_URL}/mailserver.env"

# 編輯 mailserver.env

正常來說我們需要修改的地方只有這幾個

OVERRIDE_HOSTNAME=mail.example.com
ENABLE_POP3=1 <- 這行預設好樣找不到要自己加

# 編輯 docker-compose.yaml

docker-compose.yaml 裡面要動的東西比較多
我們一個一個來確認

hostname: miyago9267.com # 修改成你的網域
# 以下的 PORT 全部都要打出來 不然會有問題
ports:
      - "25:25"    # SMTP  (explicit TLS => STARTTLS, Authentication is DISABLED => use port 465/587 instead)
      - "110:110"  # POP3  (explicit TLS => STARTTLS)
      - "143:143"  # IMAP4 (explicit TLS => STARTTLS)
      - "465:465"  # ESMTP (implicit TLS)
      - "587:587"  # ESMTP (explicit TLS => STARTTLS)
      - "993:993"  # IMAP4 (implicit TLS)
      - "995:995"  # POP3  (implicit TLS)
# 在 volumes 裡面加上你的 SSL 證書映射的位置 我是放在 /etc/nginx/certs 裡面
# 請一定要記得自己「映射在容器內」的位置 之後在容器內設定是用那個路徑
volumes:
  - /etc/nginx/certs/miyago9267.com/:/etc/nginx/certs/miyago9267.com:ro

# 啟動

確認無誤後就可以啟動 stack 了

docker compose up -d # 我看有教學說測試階段不要用 - d,但我覺得沒差,看 log 就好

# 創建帳號

# 建立一個新的 admin 使用者 你可以有你自己的 但 admin 建議要有
docker exec -it mailserver setup email add admin@example.com "password"
# 建立一個新的 postmaster 使用者
# 這個 postmaster 是用來接收系統的通知信件 沒有的話可能會噴錯
docker exec -it mailserver setup alias add postmaster@example.com admin@example.com

# 安全性設定

# DKIM

DKIM 是一種防止郵件被偽造的驗證技術
可以將信件的標頭和內文都進行加密以及生成數位簽章
我們可以在 dns 設定裡面加入這個
而 docker mailserver 裡有預設提供 opendkim 來進行金鑰的生成

docker exec -it mailserver setup config dkim

由於我們在啟動 docker-compose 之前已經映射過路徑了
我們只需要進到 ./docker-data/dms/config/opendkim/ 內即可以看到生成的金鑰
底下會有 mail.privatemail.txt 兩個檔案
mail.private 是私鑰, mail.txt 是公鑰
mail.txt 的內容加入到 DNS 的 TXT 紀錄即可

上傳的規則看這個

上傳之後請記得重新啟動 docker

docker compose down
docker compose up -d

# SPF

SPF 是一種驗證發信人身份的技術
一開始我們前置作業所設定的 TXT 就是這個

# DMARC

DMARC 也是一種郵件驗證技術
主要是 SPF 和 DKIM 的補充
並且可以指定如何處理未經驗證或驗證失敗的信件

這裡有一個現成的 DMARC 產生器可以用

rua 和 ruf 要調整好,不然出錯的時候很難 debug

# 容器文件設定

接下來就是筆者覺得最麻煩的地方了
docker 裡面實際運行了兩個程式

  • dovecot :IMAP/POP3 伺服器
  • postfix :SMTP 伺服器

這兩個伺服器的設定檔案都在容器內
然後都超級零散
可是又互相依賴和驗證
我這裡直接提供筆者嘗試了三個晚上試出來的設定

注意: 這裡的片段只是部分的設定檔
代表這一個區塊需要調整和設定 沒提到的就別動

# postfix

nano /etc/postfix/main.cf

# 這裡有個大坑 如果你用預設的 $myhostname的話 他會和系統預設的vmail虛擬郵件位置衝突
mydestination = localhost.$mydomain, localhost

smtpd_sasl_type = dovecot
smtpd_sasl_path = /dev/shm/sasl-auth.sock
smtpd_sasl_auth_enable = yes
smtpd_tls_security_level = encrypt
smtpd_sasl_security_options = noanonymous
smtpd_recipient_restrictions = permit_sasl_authenticated,permit_mynetworks,reject_unauth_destination

virtual_alias_domains = miyago9267.com
virtual_alias_maps = hash:/etc/postfix/virtual

nano /etc/postfix/master.cf

submission inet n       -       n       -       -       smtpd
  -o syslog_name=postfix/submission
  -o smtpd_tls_security_level=encrypt
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
  -o milter_macro_daemon_name=ORIGINATING

# dovecot

/etc/dovecot/conf.d/10-ssl.conf

# 這裡是你容器內的證書位置 要填上fullchain和key pem或crt等格式都可以
ssl_cert = </etc/nginx/certs/miyago9267.com/fullchain.crt
ssl_key = </etc/nginx/certs/miyago9267.com/key.crt

/etc/dovecot/conf.d/10-auth.conf

unix_listener /dev/shm/sasl-auth.sock {
    mode = 0666
    user = postfix
    group = postfix
 }

以上設定大致上告一段落

# 測試

最後就是測試我們的功能有沒有成功了

# Telnet

可以使用 telnet 來測試協定的連線

telnet mail.example.com 25

# openssl

可以使用 openssl 來測試加密的連線是否正常,他其實有一些指令可以操作,但我沒有特別去研究

openssl s_client -connect localhost:995 -crlf
openssl s_client -connect localhost:587 -crlf

# Mailtest

有一個測試郵件健康的網站可以使用
Mail-tester

他有一些指標可以告訴你
你的 mailserver 以及發出去的信件有什麼需要調整的問題
蠻方便的 起碼有一個方向可以參考

測試結果

# 連結外部平台

自架的 MailServer 最大的問題是讀取信件的時候很不方便
誰有那個美國時間和閒工夫去登入自己的 MailServer 看信
讀個信還得打指令
因此我們可以將我們的 MailServer 連結到外部平台
筆者選擇的是 Gmail

首先

  1. 打開你的 Gmail
  2. 接著點擊右上角的大齒輪
  3. 點擊「查看所有設定」
  4. 點擊「帳戶和匯入」
  5. 點擊「新增郵件帳戶」
  6. 輸入你的信箱地址和帳號
  7. 輸入你的信箱密碼
  8. 噹啷 搞定

設定怎麼進去

設定的進入

如何添加帳號

添加帳號

這樣你就可以在 Gmail 裡面讀取你的 MailServer 的信件了

# 結語

由於這次架一個 mailserver 踩到太多坑了
因而才決定寫一篇半紀錄的心得來分享
我沒辦法保證完全按照教學走就一定會成功
然而應該可以做到幫忙指個路這樣

如果有遇到什麼需要幫忙看的問題再留言吧
我能夠幫忙就盡力幫忙