Mail Server 設定筆記
- OS : CentOS 7
- SMTP : postfix port 25, 465, 587
- POP3 : dovecot port 995
- IMAP : dovecot port 993
- 虛擬帳號設定 : 虛擬網域 & 虛擬使用者
- GCP 鎖 port 25,只能用第三方寄信服務,我選用 Mailgun
- DKIM 跟 SPF 設定去看 Mailgun 文件,這兩個東西我沒有自己做:D
- 後面有附加一些方便操作的 Shell Script,用的時候要用 root 或 sudo
DNS 設定
A record
mail.example.com A xxx.xxx.xxx.xxx
MX record
example.com MX 10 mail.example.com
台北時區
timedatectl set-timezone Asia/Taipei
# or
ln -sf /usr/share/zoneinfo/Asia/Taipei /etc/localtime
關閉 ipv6
開 ipv6 可能會遇到 DNS反解 的問題
- localhost
vi /etc/hosts
# 註解下面 ipv6 這行
#::1 localhost
:wq
- 網卡
systemctl stop httpd
vi /etc/sysctl.conf
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
:wq
sysctl -p
- sshd
# 確保 sshd 只監聽 ipv4,SSH 掛了就要靠 serial port 連進來了
vi /etc/ssh/sshd_config
AddressFamily inet
:wq
systemctl restart sshd
netstat -tpln
Mail Service 安裝
- 完整 email address: john@example.com
- Mail Server
FQDN
: mail.example.com替換後面[FQDN]的地方
- Root Domain: example.com
yum -y install wget telnet git socat
yum -y remove sendmail # 刪除 sendmail
yum -y install mailx # 寄信測試工具
yum -y install postfix # SMTP
yum -y install dovecot # POP3
yum -y install cyrus-sasl-plain cyrus-sasl-md5 # SASL驗證
alternatives --config mta # 選擇 mta sendmail.postfix
TLS 證書申請
cd ~
git clone https://github.com/acmesh-official/acme.sh.git
cd acme.sh
./acme.sh --install
# 重新登入 shell
acme.sh --version
acme.sh --set-default-ca --server letsencrypt
# 如果 server 上有開 web server
# 可把 --standalone 換成 --nginx or --apache
# 會自動修改 web config 驗證完再改回來
acme.sh --issue --standalone -d [FQDN]
mkdir -p /etc/ssl/certs/[FQDN]
acme.sh --install-cert -d [FQDN] \
--key-file /etc/ssl/certs/[FQDN]/private.key \
--fullchain-file /etc/ssl/certs/[FQDN]/fullchain.cer
# 確認證書安裝成功,要有 fullchain.cer, private.key 兩個檔案
ls /etc/ssl/certs/[FQDN]
# 等整個 mail service 設定完再跑一次包含 --reloadcmd 的 --install-cert
# 這樣自動更新證書時就會順便重新載入 Mail Service
#acme.sh --install-cert -d [FQDN] \
# --key-file /etc/ssl/certs/[FQDN]/private.key \
# --fullchain-file /etc/ssl/certs/[FQDN]/fullchain.cer \
# --reloadcmd "systemctl reload postfix dovecot"
建立一個實體使用者給 service 操作虛擬信箱
- username : vmail
- uid : 5000
- group name: vmail
- gid : 5000
- virtual domain base : /var/mail/vhosts
groupadd -g 5000 vmail
useradd -u 5000 -g vmail -s /sbin/nologin vmail
mkdir -p /var/mail/vhosts
chown vmail. /var/mail/vhosts
postfix 設定
# 建立資料檔 後面設定會用到
touch /etc/postfix/virtual_domains
touch /etc/postfix/virtual_aliases \
&& echo -e "root@mail.example.com\troot@localhost" >> /etc/postfix/virtual_aliases \
&& postmap /etc/postfix/virtual_aliases
touch /etc/postfix/virtual_mailbox \
&& postmap /etc/postfix/virtual_mailbox
# DH parameters
openssl dhparam -out /etc/postfix/dh512.pem 512
openssl dhparam -out /etc/postfix/dh2048.pem 2048
# smtp auth 帳密檔
# 下面是 mailgun 設定
touch /etc/postfix/sasl_passwd
echo [smtp.mailgun.org]:2525 [YOUR_SMTP_USRNAME]:[YOUR_SMTP_PASSWORD] >> /etc/postfix/sasl_passwd
postmap /etc/postfix/sasl_passwd
vi /etc/postfix/main.cf
### 以下需搜尋各自變數的位置
default_privs = vmail
myhostname = mail.example.com
mydomain = example.com
myorigin = $myhostname
inet_interfaces = all
inet_protocols = ipv4
mydestination = $myhostname, localhost.$mydomain, localhost
mynetworks = 127.0.0.0/8
home_mailbox = Maildir/
smtpd_banner = SMTP ready.
# GCE 封鎖 port 25 , 必須使用外部SMTP且要用 25 以外的port
# 下面是 mailgun 設定 port 2525
relayhost = [smtp.mailgun.org]:2525
### 以下用 append 方式寫入
# 每封信不得超過30MB
message_size_limit = 31457280
# 每人信箱總量不得超過400MB, 0 表示無限
# mailbox_size_limit = 419430400
mailbox_size_limit = 0
# 同時可以收發總共多少封郵件, default : 100
# process限制, 機器爛可以設小一點
# default_process_limit = 10
# 每封郵件的標頭大小不得超過 4000 KB
header_size_limit = 4096000
# 防vrfy探測
disable_vrfy_command = yes
# ssl/tls
smtp_tls_CAfile = /etc/ssl/certs/ca-bundle.crt
smtp_tls_loglevel = 1
smtp_tls_security_level = encrypt
smtpd_use_tls = yes
smtpd_tls_cert_file = /etc/ssl/certs/[FQDN]/fullchain.cer
smtpd_tls_key_file = /etc/ssl/certs/[FQDN]/private.key
smtpd_tls_loglevel = 1
smtpd_tls_session_cache_database = btree:/var/lib/postfix/smtpd_scache
smtpd_tls_session_cache_timeout = 3600s
# Postfix >= 3.6 此參數將被忽略
smtpd_tls_dh512_param_file = /etc/postfix/dh512.pem
# Postfix >= 3.7 + OpenSSL >= 3.0.0 建議為空
# Postfix <= 3.7 建議使用 2048 位元
smtpd_tls_dh1024_param_file = /etc/postfix/dh2048.pem
smtp_tls_protocols = >=TLSv1
smtp_tls_mandatory_protocols = >=TLSv1
smtpd_tls_protocols = >=TLSv1
smtpd_tls_mandatory_protocols = >=TLSv1
tls_preempt_cipherlist = yes
smtp_tls_ciphers = high
smtpd_tls_ciphers = high
# smtp auth
broken_sasl_auth_clients = yes
smtp_sasl_security_options = noanonymous
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtpd_helo_required = yes
smtpd_sasl_auth_enable = yes
smtpd_sasl_security_options = noanonymous
smtpd_sasl_local_domain = $mydomain
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
# smtpd antispam
### 參數對應
### smtpd_client_restrictions ()
### smtpd_helo_restrictions (HELO)
### smtpd_sender_restrictions (MAIL FROM:)
### smtpd_recipient_restrictions (RCPT TO:)
### smtpd_data_restrictions (DATA)
### header_checks (DATA)
### body_checks (DATA)
smtpd_client_restrictions =
permit_mynetworks,
reject_non_fqdn_sender,
reject_non_fqdn_recipient,
permit
smtpd_helo_restrictions =
permit_mynetworks,
reject_invalid_helo_hostname,
permit
smtpd_sender_restrictions =
permit_mynetworks,
reject_unknown_sender_domain,
reject_non_fqdn_sender,
permit
# permit_mynetworks, 允許來自 $mynetworks 的「寄信人」
# permit_sasl_authenticated, 允許經本機 SASL 驗證過的「寄信人」
# permit_auth_destination 只允許「收件人」在 Postfix 所管轄的網域
# (由$mydestination定義),此選項避免你的伺服器變成跳板
smtpd_recipient_restrictions =
permit_mynetworks,
permit_sasl_authenticated,
reject_unauth_destination,
permit_auth_destination,
reject
# virtual host
virtual_mailbox_base = /var/mail/vhosts
virtual_mailbox_domains = /etc/postfix/virtual_domains
virtual_mailbox_maps = hash:/etc/postfix/virtual_mailbox
virtual_alias_maps = hash:/etc/postfix/virtual_aliases
virtual_uid_maps = static:5000
virtual_gid_maps = static:5000
# 每人信箱總量不得超過400MB, 0 表示無限
# virtual_mailbox_limit = 419430400
virtual_mailbox_limit = 0
:wq
vi /etc/postfix/master.cf
smtp inet n - n - - smtpd
# port 465
# postfix 舊版叫 smtps 新版叫 submissions
#smtps inet n - n - - smtpd
submissions inet n - n - - smtpd
-o syslog_name=postfix/submissions
-o smtpd_tls_wrappermode=yes
-o smtpd_sasl_auth_enable=yes
-o smtpd_client_restrictions=permit_sasl_authenticated,reject
# port 587
submission inet n - n - - smtpd
-o syslog_name=postfix/submission
-o smtpd_tls_security_level=encrypt
-o smtpd_sasl_auth_enable=yes
-o smtpd_tls_auth_only=yes
-o smtpd_client_restrictions=permit_sasl_authenticated,reject
:wq
dovecot 設定
vi /etc/dovecot/dovecot.conf
#protocols = imap pop3 lmtp
protocols = imap pop3
# listen = *, ::
listen = *
:wq
vi /etc/dovecot/conf.d/10-master.conf
service imap-login {
# 關閉 imap
inet_listener imap {
#port = 143
port = 0
}
inet_listener imaps {
#port = 993
#ssl = yes
}
}
service pop3-login {
# 關閉 pop3
inet_listener pop3 {
#port = 110
port = 0
}
inet_listener pop3s {
#port = 995
#ssl = yes
}
}
service auth {
# Postfix smtp-auth
# SMTP 登入使用 dovecot 的 passwd 驗證
unix_listener /var/spool/postfix/private/auth {
mode = 0666
user = postfix
group = postfix
}
}
:wq
# 登入驗證
vi /etc/dovecot/conf.d/10-auth.conf
#disable_plaintext_auth = yes
disable_plaintext_auth = no
#auth_mechanisms = plain
auth_mechanisms = cram-md5 plain login
#!include auth-system.conf.ext # 加註這行
!include auth-passwdfile.conf.ext
!include auth-static.conf.ext
:wq
# 虛擬使用者密碼檔
touch /etc/dovecot/passwd
chown dovecot. /etc/dovecot/passwd
vi /etc/dovecot/conf.d/auth-passwdfile.conf.ext
# 清除其他設定,只留下面的
passdb {
driver = passwd-file
args = /etc/dovecot/passwd
}
:wq
# 虛擬網域 & 虛擬使用者 Home路徑
vi /etc/dovecot/conf.d/auth-static.conf.ext
# 清除其他設定,只留下面的
# %d = domain, %n = username
userdb {
driver = static
args = uid=vmail gid=vmail home=/var/mail/vhosts/%d/%n
}
:wq
# log 路徑 and 格式
mkdir /var/log/dovecot
vi /etc/dovecot/conf.d/10-logging.conf
log_path = /var/log/dovecot/dovecot.log
syslog_facility = local0
log_timestamp = "%Y-%m-%d %H:%M:%S "
:wq
# logrotate setting 每日 log 整理時 reload
# 每日任務 /etc/cron.daily/
# logrotate conf /etc/logrotate.conf
# logrotate include conf /etc/logrotate.d/
cat <<EOL > /etc/logrotate.d/dovecot
/var/log/dovecot/dovecot.log {
sharedscripts
postrotate
/bin/systemctl reload dovecot.service > /dev/null 2>/dev/null || true
endscript
}
EOL
# 使用者信箱資料夾
# Maildir 底下會自動建立 {cur,new,tmp} 三個資料夾
vi /etc/dovecot/conf.d/10-mail.conf
#mail_location =
mail_location = maildir:~/Maildir
:wq
# SSL證書設定
vi /etc/dovecot/conf.d/10-ssl.conf
ssl = required
#ssl_cert = </etc/pki/dovecot/certs/dovecot.pem
ssl_cert = </etc/ssl/certs/[FQDN]/fullchain.cer
#ssl_key = </etc/pki/dovecot/private/dovecot.pem
ssl_key = </etc/ssl/certs/[FQDN]/private.key
ssl_prefer_server_ciphers = yes
# 舊版
#ssl_dh_parameters_length = 2048
#ssl_protocols = !SSLv2 !SSLv3
#ssl_cipher_list = kEECDH:+kEECDH+SHA:kEDH:+kEDH+SHA:+kEDH+CAMELLIA:kECDH:+kECDH+SHA:kRSA:+kRSA+SHA:+kRSA+CAMELLIA:!aNULL:!eNULL:!SSLv2:!RC4:!MD5:!DES:!EXP:!SEED:!IDEA:!3DES
# 新版
ssl_min_protocol = TLSv1
ssl_cipher_list = PROFILE=SYSTEM
:wq
Service 啟動
# config 檢查
postconf -n
postfix check
# 開機自動啟動
systemctl enable postfix
systemctl enable dovecot
# 立即啟動服務
systemctl start postfix
systemctl start dovecot
# 檢查 25、465、587、995、993 監聽
netstat -tpln
測試
- Shell Scripts 在文章最後面
# 新增網域
sh postfix_add_domain.sh "example.com"
# 新增使用者
sh postfix_add_user.sh "test@example.com" "mypassword"
- CRAM-MD5 登入
# smtps
openssl s_client -connect localhost:465
# or submission
# openssl s_client -starttls smtp -connect localhost:587
# 先送 helo 跟 AUTH
EHLO test
AUTH CRAM-MD5
# server 會回 334 跟著後面一大串的字是 [challenge] 複製起來
# 下面的 [challenge] 是我亂打的 正常沒有那麼短
334 gg3be0+7788999 <- [challenge]
# 在另一個 terminal 用 shell 產出密碼
$ sh gen-cram-md5.sh "test@example.com" "mypassword" 'gg3be0+7788999'
dGVzdEBleGFtcGxlLmNvbSAoc3RkaW4pPSBhZTdlZjMxNDJlYTQ0ZTQzODI4YzdmODdhZWE1OWY5Ng== # <= 這是密碼
# 複製 echo 出來的密碼 貼到 SMTP 登入的 terminal 送出
235 2.7.0 Authentication successful # 登入成功
- PLAIN 登入
# AUTH PLAIN 密碼 copy起來
printf "\0%s\0%s" "test@example.com" "mypassword" | base64
AHRlc3RAZXhhbXBsZS5jb20AbXlwYXNzd29yZA== # <= 這是密碼
# smtp 登入
telnet localhost 25
EHLO test
#AUTH PLAIN [密碼]
AUTH PLAIN AHRlc3RAZXhhbXBsZS5jb20AbXlwYXNzd29yZA==
235 2.7.0 Authentication successful # 登入成功
- 寄信測試
# 也可以測試寄到gmail之類的信箱
echo 'TEST MESSAGE BODY' | mail -s 'THIS IS DEMO SUBJECT' test@example.com
Shell Scripts
新增虛擬網域
- postfix_add_domain.sh
#!/usr/bin/env bash
set -e
#
# postfix - add virtual domain
#
# Author: heatlai
#
# check inputs
if [ -z "$1" ]; then
echo "usage:$0 [domain-name]"
exit 128
fi
DOMAIN=$1
# 環境變數
VHOST=/var/mail/vhosts
VHOSTUSER=vmail
VHOSTGROUP=vmail
# 確認DOMAIN是否已存在
if grep -v "#" /etc/postfix/virtual_domains | grep -sq -e "^${DOMAIN}$"; then
echo "error:'${DOMAIN}' is exists!"
else
mkdir -p ${VHOST}/${DOMAIN}
chown -R ${VHOSTUSER}.${VHOSTGROUP} ${VHOST}/${DOMAIN}
echo "${DOMAIN}" >> /etc/postfix/virtual_domains
systemctl reload postfix
fi
查看使用者密碼
- postfix_show_passwd.sh
#!/usr/bin/env bash
set -e
#
# postfix - show virtual user password
#
# Author: heatlai
#
# check inputs
if [ -z "$1" ]; then
echo "usage:$0 [email]"
exit 128
fi
EMAIL=$1
if ! echo "${EMAIL}" | grep -sq "@"; then
echo "error:'${EMAIL}' is not email address!"
exit 128
fi
if grep -sq "#${EMAIL}:" /etc/dovecot/passwd; then
PASSWORD=$(grep "#${EMAIL}:" /etc/dovecot/passwd | cut -d':' -f2)
echo "PLAIN Password: ${PASSWORD}"
else
echo "error:'${EMAIL}' is not exists!"
exit 1
fi
新增虛擬帳號
- postfix_add_user.sh
#!/usr/bin/env bash
set -e
#
# postfix - add virtual user
#
# Author: heatlai
#
# check inputs
if [ -z "$1" ]; then
echo "usage:$0 [email] [password]"
exit 128
fi
EMAIL=$1
if ! echo "${EMAIL}" | grep -sq "@"; then
echo "error:'${EMAIL}' is not email address!"
exit 128
fi
PASSWORD=$2
# check PASSWORD 沒輸入時自動產生密碼
if [ -z "${PASSWORD}" ]; then
PASSWORD=$(openssl rand -base64 36 | tr -dc 'a-zA-Z0-9' | fold -w 16 | head -n 1)
fi
USERNAME=$(echo "${EMAIL}" | cut -d'@' -f1)
DOMAIN=$(echo "${EMAIL}" | cut -d'@' -f2)
if grep -v "#" /etc/dovecot/passwd | grep -sq "${EMAIL}"; then
echo "error:'${EMAIL}'is exists!"
exit 1
fi
if ! grep -v "#" /etc/postfix/virtual_domains | grep -sq -e "^${DOMAIN}$"; then
echo "error:'${DOMAIN}' is not exists!"
exit 1
fi
# 密碼加密
ENCRYPT_PASSWORD=$(doveadm pw -s CRAM-MD5 -p "${PASSWORD}")
# 寫入密碼
echo "${EMAIL}:${ENCRYPT_PASSWORD}" >> /etc/dovecot/passwd
# 保留明文密碼
echo "#${EMAIL}:${PASSWORD}" >> /etc/dovecot/passwd
# 寫入使用者信箱對應表
echo -e "${EMAIL}\t${DOMAIN}/${USERNAME}/Maildir/" >> /etc/postfix/virtual_mailbox
postmap /etc/postfix/virtual_mailbox
systemctl reload postfix
變更使用者密碼
- postfix_change_passwd.sh
#!/usr/bin/env bash
set -e
#
# postfix - change virtual user password
#
# Author: heatlai
#
# check inputs
if [ -z "$1" ]; then
echo "usage:$0 [email] [password]"
exit 128
fi
EMAIL=$1
if ! grep -v "#" /etc/dovecot/passwd | grep -sq "${EMAIL}"; then
echo "error:'${EMAIL}' is not exists!"
exit 1
fi
PASSWORD=$2
# check PASSWORD 沒輸入時自動產生密碼
if [ -z "${PASSWORD}" ]; then
PASSWORD=$(openssl rand -base64 36 | tr -dc 'a-zA-Z0-9' | fold -w 16 | head -n 1)
fi
# 密碼加密
ENCRYPT_PASSWORD=$(doveadm pw -s CRAM-MD5 -p "${PASSWORD}")
# 刪除舊密碼
sed -i -e "/${EMAIL}/d" /etc/dovecot/passwd
# 寫入新密碼
echo "${EMAIL}:${ENCRYPT_PASSWORD}" >> /etc/dovecot/passwd
# 保留明文密碼
echo "#${EMAIL}:${PASSWORD}" >> /etc/dovecot/passwd
虛擬帳號列表
- postfix_user_list.sh
#!/usr/bin/env bash
set -e
#
# postfix - list virtual user
#
# Author: heatlai
#
grep -v "#" /etc/dovecot/passwd | cut -d':' -f1
刪除虛擬網域
- postfix_delete_domain.sh
#!/usr/bin/env bash
set -e
#
# postfix - delete virtual domain
#
# Author: heatlai
#
# check inputs
if [ -z "$1" ]; then
echo "usage:$0 [domain-name]"
exit 128
fi
DOMAIN=$1
VHOST=/var/mail/vhosts
if ! grep -v "#" /etc/postfix/virtual_domains | grep -sq -e "^${DOMAIN}$"; then
echo "error:'${DOMAIN}' is not exists!"
exit 1
fi
# 此網域的帳密刪除
sed -i -e "/@${DOMAIN}/d" /etc/dovecot/passwd
# 虛擬網域清單刪除
sed -i -e "/^${DOMAIN}/d" /etc/postfix/virtual_domains
# 虛擬帳號信箱對應表刪除
sed -i -e "/@${DOMAIN}/d" /etc/postfix/virtual_mailbox
# 信箱資料夾刪除
rm -rf ${VHOST}/${DOMAIN}
# 虛擬信箱別名刪除
sed -i -e "/@${DOMAIN}/d" /etc/postfix/virtual_aliases
# 套用新設定
postmap /etc/postfix/virtual_mailbox
postmap /etc/postfix/virtual_aliases
systemctl reload postfix
刪除虛擬帳號
- postfix_delete_user.sh
#!/usr/bin/env bash
set -e
#
# postfix - delete virtual user
#
# Author: heatlai
#
# check inputs
if [ -z "$1" ]; then
echo "usage:$0 [email]"
exit 128
fi
EMAIL=$1
if ! echo "${EMAIL}" | grep -sq "@"; then
echo "error:'${EMAIL}' is not email address!"
exit 128
fi
if ! grep -sq "${EMAIL}" /etc/dovecot/passwd; then
echo "error:'${EMAIL}' is not exists!"
exit 1
fi
USERNAME=$(echo "${EMAIL}" | cut -d'@' -f1)
DOMAIN=$(echo "${EMAIL}" | cut -d'@' -f2)
VHOST=/var/mail/vhosts
# 帳密刪除
sed -i -e "/${EMAIL}/d" /etc/dovecot/passwd
# 虛擬帳號信箱對應表刪除
sed -i -e "/${EMAIL}/d" /etc/postfix/virtual_mailbox
# 信箱資料夾刪除
rm -rf "${VHOST:?}/${DOMAIN:?}/${USERNAME:?}"
# 虛擬信箱別名刪除
sed -i -e "/${EMAIL}/d" /etc/postfix/virtual_aliases
# 套用新設定
postmap /etc/postfix/virtual_mailbox
postmap /etc/postfix/virtual_aliases
systemctl reload postfix
SMTP AUTH CRAM-MD5 登入密碼產生器
- gen-cram-md5.sh
#!/usr/bin/env bash
#
# SMTP AUTH CRAM-MD5 password generator
#
# Author: heatlai
#
if [ $# -lt 3 ] ; then
echo "usage:$0 [email] [password] [challenge]"
exit 128
fi
#
# like PHP hash_hmac()
#
function hash_hmac {
local digest="$1"
local data="$2"
local key="$3"
printf "${data}" | openssl dgst "-${digest}" -hmac "${key}" -binary | xxd -p
}
EMAIL=$1
PASSWORD=$2
CHALLENGE=$3
HASH_PASSWORD=$(hash_hmac 'md5' "$(printf "${CHALLENGE}" | base64 --decode)" "${PASSWORD}")
if [[ "$OSTYPE" == "darwin"* ]]; then
# MacOS
echo $(printf "${EMAIL} ${HASH_PASSWORD}" | base64)
else
# Linux
echo $(printf "${EMAIL} ${HASH_PASSWORD}" | base64 -w 0)
fi
其他設定
黑洞信箱 ( no-reply@example.com )
- 設定一個 address 名稱
devnull
儲存路徑指向/dev/null
丟棄
vi /etc/aliases
# 增加下面這個
devnull: /dev/null
:wq
# 套用 alias
newaliases
- 設定
no-reply@example.com
轉寄至devnull@localhost
# 檔名來源 main.cf :
# virtual_alias_maps = hash:/etc/postfix/virtual_aliases
vi /etc/postfix/virtual_aliases
no-reply@example.com devnull
:wq
postmap /etc/postfix/virtual_aliases
# 重啟
systemctl reload postfix
系統郵件轉寄至私人信箱
- 設定
root@localhost
轉寄至john@example.com
# 檔名來源 main.cf :
# virtual_alias_maps = hash:/etc/postfix/virtual_aliases
vi /etc/postfix/virtual_aliases
root john@example.com
:wq
postmap /etc/postfix/virtual_aliases
# 重啟
systemctl reload postfix
SPF (Sender Policy Framework)
# SPF DNS Record
example.com. IN TXT "v=spf1 mx a ~all"
# Mailgun
example.com. IN TXT "v=spf1 include:mailgun.org ~all"
# Gmail
example.com. IN TXT "v=spf1 include:_spf.google.com ~all"
# 參數說明
# ip4:x.x.x.x 可指定 IP 或 CIDR x.x.x.x/24
# a:mail.example.com 不指定網域時就是指 example.com 本身
# mx:mail.example.com 不指定網域時就是指 example.com 本身
# include:mailgun.org 包含 mailgun.org 的 SPF 設定
# ~all SoftFail 錯誤但不拒收
# -all Fail 錯誤要拒收
DKIM (DomainKeys Identified Mail) (自己發信時才需要)
安裝設定
yum install -y opendkim
vim /etc/opendkim.conf
Mode sv
# 加註 KeyFile
#KeyFile /etc/opendkim/keys/default.private
KeyTable /etc/opendkim/KeyTable
SigningTable refile:/etc/opendkim/SigningTable
ExternalIgnoreList refile:/etc/opendkim/TrustedHosts
InternalHosts refile:/etc/opendkim/TrustedHosts
Postfix 設定
vim /etc/postfix/main.cf
#
# opendkim
#
smtpd_milters = inet:localhost:8891
non_smtpd_milters = $smtpd_milters
milter_default_action = accept
DKIM 新增 Shell Script
#!/usr/bin/env bash
set -e
#
# DKIM - add domain key
#
# Author: heatlai
#
# check inputs
if [ -z "$1" ]; then
echo "usage:$0 [domain]"
exit 128
fi
if ! hash opendkim-genkey 2>/dev/null; then
echo "command \"opendkim-genkey\" not exists. it's required."
exit 1
fi
if ! hash opendkim 2>/dev/null; then
echo "command \"opendkim\" not exists. it's required."
exit 1
fi
if [ ! -f /etc/opendkim/keys/${DOMAIN}/default.private ]; then
mkdir -p /etc/opendkim/keys/${DOMAIN}
opendkim-genkey -D /etc/opendkim/keys/${DOMAIN}/ -d ${DOMAIN} -s default
chown -R opendkim. /etc/opendkim/keys/${DOMAIN}
echo "default._domainkey.${DOMAIN} ${DOMAIN}:default:/etc/opendkim/keys/${DOMAIN}/default.private" >> /etc/opendkim/KeyTable
sed -i -e "/#\*@example.com/a\\*@${DOMAIN} default._domainkey.${DOMAIN}" /etc/opendkim/SigningTable
opendkim reload
fi
- 執行後將 Public Key 加到 DNS Record
cat /etc/opendkim/keys/${DOMAIN}/default.txt
# 把 default.txt 的內容加到 DNS,像是下面這樣
# default._domainkey IN TXT "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDucWOlzIiyahWtQ9jgPjnfZp7x6K4L8Y20wFlmASr+eUlimC0w52SKIFancZILZeliPPjmaab4fHsvM1VJfF80EhMgTUKYQy7lOBBTjZVLmDfHAFzmUd3KOoaNbZ0tDiuayjrV1jAB82wHlrroGFBYQncpHbCUxlm31bu4WjLElwIDAQAB"
DKIM 刪除 Shell Script
- 執行後把 Public Key 從 DNS Record 上刪除
#!/usr/bin/env bash
set -e
#
# DKIM - delete domain key
#
# Author: heatlai
#
rm -rf "/etc/opendkim/keys/${DOMAIN}"
sed -i -e "/${DOMAIN}:/d" /etc/opendkim/KeyTable
sed -i -e "/@${DOMAIN} /d" /etc/opendkim/SigningTable
opendkim reload
DMARC (Domain-based Message Authentication, Reporting & Conformance)
- 須先設定 SPF + DKIM
# DMARC DNS Record
_dmarc.example.com. IN TXT "v=DMARC1; p=none; rua=mailto:postmaster@example.com"
# rua 錯誤時送信給誰