Mail server on Fedora 26

備註:這篇是修改自三年前的「Mail server on CentOS 6」,加上些新的內容和補充

本文開始~~

一般來說email是由三個部分組成,Mail (Transfer/Delivery/User) Agent,為什麼wiki上說有五種呢?因為MSA和MRA通常是包含在前面三種裡

在伺服端的重點是MTA跟MDA,負責收發跟儲存信的兩個部分,這邊我們用postfix和dovecot來實作

套件

在開始之前,先安裝必須的軟體

dnf install postfix postfix-mysql
dnf install dovecot dovecot-mysql
dnf install mariadb-server

SSL憑證

由於這個教學會設定成提供加密連線,憑證是必需品

dovecot在安裝後會自動產生一組self-signed cert,放在/etc/pki/dovecot裡面,不過還是建議向CA申請,避免使用者在連線時會出現錯誤

向CA拿到憑證之後,就直接放在/etc/pki/dovecot/certs/裡面,私鑰則是/etc/pki/dovecot/private/

如果CA有中繼憑證(intermediate certificate)的話,只需把中繼憑證以倒敘的方式append在自己的憑證後面即可

最後,檢查憑證跟私鑰的selinux context和存取權限,權限建議是root:root 600

selinux在enforcing的情況下,context如果不是「unconfined_u:object_r:dovecot_cert_t:s0」,會造成dovecot無法啟動的狀況(permission denied)

如果使用Let's encrypt作為CA的話,可以不用搬到/etc/pki/dovecot/裡面,直接指向/etc/letsencrypt底下的憑證即可

MySQL

首先是產生所需的db和table,使用mysql root來執行以下語句

# 產生DB
CREATE DATABASE mailserver;

# 產生使用者,設定密碼為mailuserpass,並提供mailserver的select權限
GRANT SELECT ON mailserver.* TO 'mailuser'@'127.0.0.1' IDENTIFIED BY 'mailuserpass';

# 更新權限表
FLUSH PRIVILEGES;

# 切換DB
USE mailserver;

# 產生網域表
CREATE TABLE `virtual_domains` (
  `id` int(11) NOT NULL auto_increment,
  `name` varchar(50) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

# 產生使用者表
CREATE TABLE `virtual_users` (
  `id` int(11) NOT NULL auto_increment,
  `domain_id` int(11) NOT NULL,
  `password` varchar(106) NOT NULL,
  `email` varchar(100) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `email` (`email`),
  FOREIGN KEY (domain_id) REFERENCES virtual_domains(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

# 產生別名表
CREATE TABLE `virtual_aliases` (
  `id` int(11) NOT NULL auto_increment,
  `domain_id` int(11) NOT NULL,
  `source` varchar(100) NOT NULL,
  `destination` varchar(100) NOT NULL,
  PRIMARY KEY (`id`),
  FOREIGN KEY (domain_id) REFERENCES virtual_domains(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

# 新增網域
INSERT INTO `virtual_domains`
  (`name`)
VALUES
  ('example.com');

# 新增使用者
# domain_id就是網域表中example.com的id
# firstpassword是email1@example.com的密碼
# 亂數可以用`base64 --wrap=0 /dev/urandom`來產生
INSERT INTO `virtual_users`
  (`domain_id`, `password` , `email`)
VALUES
  ('1', ENCRYPT('firstpassword', CONCAT('$6$', '這邊用亂數做salt')), 'email1@example.com'),
  ('1', ENCRYPT('secondpassword', CONCAT('$6$', '這邊用亂數做salt')), 'email2@example.com');

# 新增別名
# domain_id就是網域表中example.com的id
INSERT INTO `virtual_aliases`
  (`domain_id`, `source`, `destination`)
VALUES
  ('1', 'alias@example.com', 'email1@example.com'),
  ('1', 'alias2@example.com', 'somewhere@other.domain.com');

到這邊就新增完成了,記得先用select看一下DB的內容是否正確

Postfix

首先到/etc/postfix/main.cf去看看

# /etc/postfix/main.cf

### 基本設定 ###
# 這台mail server對外的名字
myhostname = mail.example.com

# localhost的網域,預設是把myhostname減掉第一組主機名
mydomain = example.com

# 從localhost寄信的時候,要附上的網域
myorigin = $mydomain

# localhost收信的網域,不要和mysql裡的一樣,mysql裡面寫的是virtual domain
mydestination = $myhostname, localhost.$mydomain, localhost

# 監聽所有介面
inet_interfaces = all

# 信任的區域網路只有自己,如果有LAN要信任就改成subnet
mynetworks_style = host

基本設定主要是和本機(localhost)有關,我們的信箱會使用虛擬網域來收發信

在這之前,先把驗證和加密的功能設定好

# /etc/postfix/main.cf

### TLS ###
# 憑證和私鑰
smtpd_tls_cert_file=/etc/pki/dovecot/certs/mailer.crt
smtpd_tls_key_file=/etc/pki/dovecot/private/mailer.key

# 啟用TLS,但為了相容性只有驗證時強制加密
smtpd_tls_auth_only = yes
smtpd_tls_security_level = may
smtp_tls_security_level = may

# 不要用DH來做key exchange
smtpd_tls_exclude_ciphers = kEDH

### SMTP auth ###
# 使用dovecot做SASL(Simple Authentication and Security Layer)驗證
smtpd_sasl_type = dovecot

# dovecot驗證用的socket,/var/spool/postfix/的相對位置
smtpd_sasl_path = private/auth

# 啟用SASL驗證
smtpd_sasl_auth_enable = yes

# 允許通過SASL驗證和信任網路的來源,並拒絕其他不是寄到本機的信,然後對client檢查RBL
smtpd_recipient_restrictions =
  permit_mynetworks,
  permit_sasl_authenticated,
  reject_non_fqdn_hostname,
  reject_non_fqdn_sender,
  reject_non_fqdn_recipient,
  reject_unauth_destination,
  reject_unauth_pipelining,
  reject_invalid_hostname,
  reject_rbl_client bl.spamcop.net,
  reject_rbl_client b.barracudacentral.org,
  reject_rbl_client cbl.abuseat.org,
  reject_rbl_client dnsbl.sorbs.net,
  reject_rbl_client zen.spamhaus.org

# 使用者帳號和網域的對應表(檔案稍後加入)
smtpd_sender_login_maps =  mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf

# 拒絕非法網域和與使用者帳號不合的網域
smtpd_sender_restrictions = reject_unknown_sender_domain, reject_sender_login_mismatch

再來是設定虛擬信箱

# /etc/postfix/main.cf

### incoming email handling ###

# 將寄來的信以lmtp協定轉給dovecot儲存
virtual_transport = lmtp:unix:private/dovecot-lmtp

### Virtual mailbox ###

# 定義要進行virtual_transport的網域和信箱
virtual_mailbox_domains = mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf
virtual_mailbox_maps = mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf
virtual_alias_maps = mysql:/etc/postfix/mysql-virtual-alias-maps.cf

馬上就來新增剛剛寫的mysql檔案,總共有三個

# /etc/postfix/mysql-virtual-mailbox-domains.cf

user = mailuser
password = mailuserpass
hosts = 127.0.0.1
dbname = mailserver
query = SELECT `name` FROM virtual_domains WHERE name='%s'
# /etc/postfix/mysql-virtual-mailbox-maps.cf

user = mailuser
password = mailuserpass
hosts = 127.0.0.1
dbname = mailserver
query = SELECT `email` FROM virtual_users WHERE email='%s'
# /etc/postfix/mysql-virtual-alias-maps.cf

user = mailuser
password = mailuserpass
hosts = 127.0.0.1
dbname = mailserver
query = SELECT `destination` FROM virtual_aliases WHERE source='%s'

存好之後使用chmod修改存取權限為600以免DB密碼被看到

然後使用以下指令來驗證上述三個檔案是不是正確設定

postmap -q example.com mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf
postmap -q email1@example.com mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf
postmap -q alias@example.com mysql:/etc/postfix/mysql-virtual-alias-maps.cf

如果設定正確,應該會看到網域或是信箱,什麼都沒有代表失敗,就要檢查設定或是資料庫是否有問題

最後修改/etc/postfix/master.cf,開放465和587的port

# /etc/postfix/master.cf

# 將以下兩行註解拿掉即可
submission inet n       -       n       -       -       smtpd
smtps     inet  n       -       n       -       -       smtpd

Postfix到這邊就完成了,記得要替防火牆開port 25, 465, 587,然後重開postfix服務

Dovecot

dovecot的主要設定都在conf.d裡,我們逐步來修改

# /etc/dovecot/conf.d/10-mail.conf

# 修改以下兩行
mail_location = maildir:/var/mail/vhosts/%d/%n
mail_privileged_group = mail

來弄資料夾的部分

#!/bin/bash

# 新增email資料夾
mkdir -p /var/mail/vhosts/example.com

# 新增使用者
groupadd -g 5000 vmail
useradd -g vmail -u 5000 vmail -d /var/mail -s /sbin/nologin

# 調整email資料夾權限
chown -R vmail:vmail /var/mail/vhosts

# 調整dovecot資料夾權限
chown -R vmail:dovecot /etc/dovecot
chmod -R o-rwx /etc/dovecot

過來是驗證的部分

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

# 先修改底下兩個驗證參數
disable_plaintext_auth = yes
auth_mechanisms = plain login

### 檔案最底下有這一排,把auth-sql以外的都註解掉

#!include auth-system.conf.ext
!include auth-sql.conf.ext
#!include auth-ldap.conf.ext
#!include auth-passwdfile.conf.ext
#!include auth-checkpassword.conf.ext
#!include auth-vpopmail.conf.ext
#!include auth-static.conf.ext

來到剛剛引入的auth-sql.conf.ext

# /etc/dovecot/conf.d/auth-sql.conf.ext

# args那個檔案下一步驟會做
passdb {
  driver = sql
  args = /etc/dovecot/dovecot-sql.conf.ext
}
userdb {
  driver = static
  args = uid=vmail gid=vmail home=/var/mail/vhosts/%d/%n
}

沿著剛剛的args繼續往下做回到上一層新增dovecot-sql.conf.ext

# /etc/dovecot/dovecot-sql.conf.ext

driver = mysql
connect = host=127.0.0.1 dbname=mailserver user=mailuser password=mailuserpass
default_pass_scheme = SHA512-CRYPT
password_query = SELECT email as user, password FROM virtual_users WHERE email='%u';

pass_scheme選SHA512-CRYPT的原因是插入使用者資料時使用$6$作為參數呼叫ENCRYPT(),libc的crypt()在hash時會知道我們要使用SHA512

進入倒數第二個部分,dovecot的socket

# /etc/dovecot/conf.d/10-master.conf

# lmtp協定的socket
service lmtp {
  unix_listener /var/spool/postfix/private/dovecot-lmtp {
     mode = 0600
     user = postfix
     group = postfix
  }
}

# 驗證用的socket
service auth {
  unix_listener /var/spool/postfix/private/auth {
    mode = 0666
    user = postfix
    group = postfix
  }

  unix_listener auth-userdb {
    mode = 0600
    user = vmail
    #group =
  }

  user = dovecot
}

# 驗證使用的帳號
service auth-worker {
  user = vmail
}

最後是SSL的設定

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

# 強迫加密
ssl = required

# 憑證跟私鑰
ssl_cert = </etc/pki/dovecot/certs/mailer.crt
ssl_key = </etc/pki/dovecot/private/mailer.key

# 一樣不要用DH做key exchange
ssl_cipher_list = ALL:!LOW:!SSLv2:!EXP:!aNULL:!kEDH

大功告成!

一樣記得開防火牆的port 110,143,993,995,然後重啟dovecot服務

如果要讓寄信更順不會被擋,可以再設定SPF、DKIM、DMARC、IP反解

留言

  1. 要先跟ISP搞定Reverse DNS哦
    不然信出去八成也是直接被吃掉 {116}

    • @malsvent
      用DKIM和SPF就還好啦 {124}
      可以從key跟ip確定發信來源

粗體斜體刪除線連結引用圖片程式碼

注意:您的電子信箱將不會被公開,且網站連結不會被搜尋引擎採計

{124} {123} {122} {121} {120} {119} {118} {117} {116} {115} {114} {113} {112} {111} {100} {025} {024} {023} {022} {021} {020} {019} {018} {017} {016} {015} {014} {013} {012} {011} {010} {009} {008} {007} {006} {005} {004} {003} {002} {001}