本文詳細記錄基於 CentOS/RHEL 環境部署 Postfix (SMTP 發送) + Dovecot (IMAP/POP3 接收) 郵件系統的完整流程,包含核心配置解析、多域名虛擬用戶、別名群組、反垃圾郵件策略與常見故障排查。
| 組件 | 角色 | 協定/埠 | 說明 |
|---|---|---|---|
| Postfix | MTA (郵件傳輸代理) | SMTP (25/465/587) | 負責郵件路由、隊列、投遞,模組化設計、安全性強、效能優於 Sendmail |
| Dovecot | MDA/MRA (投遞/檢索代理) | IMAP (143/993), POP3 (110/995) | 支援 Maildir/mbox、虛擬用戶、SSL/TLS、高併發 |
| MariaDB/MySQL | 帳號域名存儲 | 3306 | 虛擬域名、用戶、別名、配額資料持久化 |
郵件流向:MUA (Outlook/Foxmail) → SMTP (Postfix) → 隊列 → 本地投遞 (Dovecot LDA/LMTP) → Maildir → MUA 透過 IMAP/POP3 讀取
# 1. 移除衝突套件 yum remove -y sendmail # 2. 安裝核心套件 yum install -y postfix dovecot mariadb-server # 3. 設定預設 MTA alternatives --config mta # 選擇 postfix # 4. 啟動服務 systemctl enable --now postfix dovecot mariadb systemctl status postfix dovecot
# 基礎身份
myhostname = mail.example.com
mydomain = example.com
myorigin = $mydomain
# 網路監聽
inet_interfaces = all
inet_protocols = ipv4
# 信任網路 (允許無認證轉發)
mynetworks = 127.0.0.0/8, 192.168.1.0/24
# 郵件隊列目錄
queue_directory = /var/spool/postfix
# 本地投遞交給 Dovecot LMTP
mailbox_transport = lmtp:unix:private/dovecot-lmtp
virtual_transport = lmtp:unix:private/dovecot-lmtp
# 虛擬域名/用戶/別名映射 (MySQL)
virtual_mailbox_domains = mysql:/etc/postfix/mysql-virtual-domains.cf
virtual_mailbox_maps = mysql:/etc/postfix/mysql-virtual-mailboxes.cf
virtual_alias_maps = mysql:/etc/postfix/mysql-virtual-aliases.cf
# 認證與加密
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
smtpd_tls_security_level = may
smtpd_tls_cert_file = /etc/ssl/certs/mail.pem
smtpd_tls_key_file = /etc/ssl/private/mail.key
# 反垃圾郵件基礎策略
smtpd_recipient_restrictions =
permit_mynetworks,
permit_sasl_authenticated,
reject_unauth_destination,
reject_invalid_helo_hostname,
reject_non_fqdn_sender,
reject_unknown_sender_domain,
check_policy_service inet:127.0.0.1:10031 # policyd/spf/dkim
# 標誌
smtpd_banner = $myhostname ESMTP "Secure Mail Server"
protocols = imap pop3 lmtp listen = *, :: base_dir = /var/run/dovecot/ instance_name = dovecot login_trusted_networks = 192.168.1.0/24 # 信任內網跳過驗證 (可選)
mail_location = maildir:/var/vmail/%d/%n
mail_privileged_group = vmail
namespace inbox {
inbox = yes
location =
mailbox Drafts { special_use = \Drafts }
mailbox Sent { special_use = \Sent }
mailbox Trash { special_use = \Trash }
mailbox Junk { special_use = \Junk }
}
disable_plaintext_auth = yes auth_mechanisms = plain login !include auth-sql.conf.ext # 使用 SQL 驗證
passdb {
driver = sql
args = /etc/dovecot/dovecot-sql.conf.ext
}
userdb {
driver = sql
args = /etc/dovecot/dovecot-sql.conf.ext
}
driver = mysql connect = host=127.0.0.1 dbname=mailserver user=mailuser password=secret default_pass_scheme = SHA512-CRYPT password_query = SELECT email AS user, password FROM virtual_users WHERE email='%u' AND active=1 user_query = SELECT '/var/vmail/%d/%n' AS home, 'maildir:/var/vmail/%d/%n' AS mail, 5000 AS uid, 5000 AS gid FROM virtual_users WHERE email='%u' AND active=1
service auth {
unix_listener /var/spool/postfix/private/auth {
mode = 0666
user = postfix
group = postfix
}
}
service lmtp {
unix_listener /var/spool/postfix/private/dovecot-lmtp {
mode = 0600
user = postfix
group = postfix
}
}
CREATE DATABASE mailserver CHARACTER SET utf8mb4; CREATE USER 'mailuser'@'localhost' IDENTIFIED BY 'strong_password'; GRANT ALL ON mailserver.* TO 'mailuser'@'localhost'; USE mailserver; CREATE TABLE virtual_domains ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL UNIQUE, active BOOLEAN DEFAULT TRUE ); CREATE TABLE virtual_users ( id INT PRIMARY KEY AUTO_INCREMENT, domain_id INT NOT NULL, email VARCHAR(100) NOT NULL UNIQUE, password VARCHAR(255) NOT NULL, -- SHA512-CRYPT quota INT DEFAULT 1024000, -- KB active BOOLEAN DEFAULT TRUE, FOREIGN KEY (domain_id) REFERENCES virtual_domains(id) ON DELETE CASCADE ); CREATE TABLE virtual_aliases ( id INT PRIMARY KEY AUTO_INCREMENT, domain_id INT NOT NULL, source VARCHAR(100) NOT NULL, -- 別名 destination VARCHAR(100) NOT NULL, -- 實際郵箱 active BOOLEAN DEFAULT TRUE, FOREIGN KEY (domain_id) REFERENCES virtual_domains(id) ON DELETE CASCADE );
# /etc/postfix/mysql-virtual-domains.cf user = mailuser password = secret hosts = 127.0.0.1 dbname = mailserver query = SELECT 1 FROM virtual_domains WHERE name='%s' AND active=1 # /etc/postfix/mysql-virtual-mailboxes.cf query = SELECT 1 FROM virtual_users WHERE email='%s' AND active=1 # /etc/postfix/mysql-virtual-aliases.cf query = SELECT destination FROM virtual_aliases WHERE source='%s' AND active=1
# 使用 Let's Encrypt 自動續簽 certbot certonly --standalone -d mail.example.com --preferred-challenges http # 憑證路徑: /etc/letsencrypt/live/mail.example.com/fullchain.pem (cert) # 私鑰路徑: /etc/letsencrypt/live/mail.example.com/privkey.pem (key) # 修改 main.cf / dovecot 10-ssl.conf 指向上述路徑 # 設定 cron 自動續簽並 reload postfix dovecot
| 記錄類型 | 名稱 | 值 | 用途 |
|---|---|---|---|
| A | 伺服器公網 IP | 主機解析 | |
| MX | @ | mail.example.com (優先級 10) | 指定收件伺服器 |
| TXT (SPF) | @ | v=spf1 mx ip4:你的IP ~all | 授權發件 IP |
| TXT (DKIM) | default._domainkey | v=DKIM1; k=rsa; p=公鑰... | 數位簽名驗證 |
| TXT (DMARC) | _dmarc | v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com | 策略回報 |
| PTR (rDNS) | IP 反解 | mail.example.com | ISP 設定,防拒收 |
# 1. 檢查埠監聽 ss -lntp | grep -E ':25|:143|:993|:110|:995|:587|:465' # 2. SMTP 手動測試 telnet localhost 25 EHLO localhost MAIL FROM:<test@example.com> RCPT TO:<user@example.com> DATA Subject: Test Hello World . QUIT # 3. 發送測試郵件 echo "Body" | mail -s "Test" -r "sender@example.com" user@example.com # 4. 查看隊列與日誌 postqueue -p tail -f /var/log/maillog # 5. Dovecot 測試 doveadm auth test user@example.com password doveadm mailbox list -u user@example.com