Ubuntu搭建邮件服务器

转载:原文链接

前言

关于邮件服务器的工作原理我就不再赘述了。Postfix是优秀的MTA,而Dovecot则是优秀的MDA。前者负责发信、收信,提供smtp服务;后者负责邮件保存到邮箱,提供pop3和imap服务。

这里介绍使用Postfix+Dovecot模式的邮件服务系统,同时Postfix使用Dovecot提供的SASL完成用户认证。用户使用MySQL存储的虚拟用户,而不是服务器的实际用户。同时使用opendkim完成邮件的dkim签名,SpamAssassin完成反垃圾邮件。也会阐述如何为smtp、pop3和imap配置SSL。

准备一台服务器

  1. 需要一台服务器,尽量使用有公网ip的服务器而不是使用内网穿透(否则发邮件的时候对方发现ip是非静态ip可能会当作垃圾邮件)。

  2. 要保证服务器的25端口开放。我选择了腾讯云的服务器,1核1G,申请开放了25端口(腾讯云申请比阿里云容易)。

  3. 主机名设置为你的域名或者mx.xxxx.com这样的子域名。

  4. 修改主机的时区,设为北京时间。

准备一个域名

准备一个域名,最好已经申请超过28天(否则容易被当作垃圾邮件)。

  1. 添加一个A记录(例如mx.xxxx.com)指向服务器的ip,添加一个mx记录(主机记录为@)指向上述添加的A记录(优先级可设定为5,具体作用可自行百度)。

  2. 可以为smtp、pop3和imap分别添加一些记录(例如smtp.xxxx.com),指向你的服务器(你也可以不这么做,这个看个人喜好,在登录邮箱的时候需要输入smtp服务器的地址,你可以输入ip也可以输入你现在设定的域名,如果要做SSL则最好是设定域名。你也可以smtp,pop3和imap都是用mx.xxxx.com这个域名)。

  3. 添加一个txt记录作为SPF记录(主机记录为@),内容可以写v=spf1 mx ~all。这表示当对方收到一封来自你这个域名的邮件时,先检查发件方对方的ip是否为mx记录中定义的ip(或A记录对应的ip),如果不是则标记为软拒绝。具体可以自行查询SPF记录的语法。如果你发邮件的机器没有公网ip,也就是你是通过本地机器内网穿透实现的邮件服务,那么SPF则写为v=spf1 +all表示接受所有的ip。

  4. 添加一个txt记录为DMARC记录(主机记录为_dmarc),内容可以写v=DMARC1; p=reject; fo=1; rua=mailto:你的邮箱。表示当出现仿造的邮件时,对方reject邮件,并通知你的邮箱。具体可以百度DMARC的语法。

申请SSL证书

如果不想使用SSL也可以跳过这一步骤。

  1. 申请一个SSL证书,包括上面步骤所创建的所有A记录(例如mx.xxxx.comsmtp.xxxx.com)。可以使用acme.sh在Let's或者ZeroSSL申请证书,我是在后者申请的。

  2. 证书的fullchain文件放到服务器的/etc/ssl/certs/xxxx.com.pem下。

  3. 证书的密钥文件放到服务器的/etc/ssl/private/xxxx.com.key下。

  4. 上述二者权限设置为400,属主设置为root:root

配置postfix和dovecot

  1. 安装postfix和dovecot
swift 复制代码
apt update
apt install postfix postfix-mysql dovecot-core dovecot-pop3d dovecot-imapd dovecot-lmtpd dovecot-mysql
  1. 安装配置MySQL,新建数据库mailserver(名字可以根据喜好来,下同),并创建用户mail,注意分配权限。

  2. 创建数据表:

sql 复制代码
CREATE TABLE `virtual_domains` (
    `id`  INT NOT NULL AUTO_INCREMENT,
    `name` VARCHAR(50) NOT NULL,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
CREATE TABLE `virtual_users` (
    `id` INT NOT NULL AUTO_INCREMENT,
    `domain_id` INT NOT NULL,
    `password` VARCHAR(106) NOT NULL,
    `email` VARCHAR(120) 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;
  1. 为数据库添加内容,具体按照自己喜好来
sql 复制代码
-- 插入一个域名(1表示序号, 注意递增)
insert into virtual_domains values(1,'xxxx.com')

-- 插入用户(1表示序号, 注意递增)
insert into virtual_users values(1,域名序号,md5('密码'),'yyyy@xxxx.com');

-- 为用户设置一个别名(1表示序号, 注意递增),发送给zzzz的邮件都会发给yyyy了。
-- 注意这一步好像要先为zzzz插入一个用户,你也可以试一下不这么做,具体要不要我没试验过
insert into virtual_aliases values(1,域名序号,'zzzz@xxxx.com','yyyy@xxxx.com')
  1. 配置/etc/postfix/main.cf,这是postfix的主配置文件。

  2. 添加SSL代码

ini 复制代码
smtpd_tls_cert_file=/etc/ssl/certs/xxxx.com.pem
smtpd_tls_key_file=/etc/ssl/private/xxxx.key
smtpd_use_tls=yes
#smtpd_tls_auth_only = yes
smtp_tls_cert_file=/etc/ssl/certs/xxxx.com.pem
smtp_tls_key_file=/etc/ssl/private/xxxx.com.key
smtp_use_tls=yes

smtpd_tls_CApath=/etc/ssl/certs
smtpd_tls_security_level=may
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
smtp_tls_CApath=/etc/ssl/certs
smtp_tls_security_level=may
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
  1. 使用dovecot来做身份认证
ini 复制代码
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
# smtpd_client_restrictions = permit_mynetworks permit_sasl_authenticated reject_rbl_client reject_rhsbl_client reject_unknown_client
# smtpd_sender_restrictions = reject_unknown_sender_domain
smtpd_recipient_restrictions = permit_sasl_authenticated permit_mynetworks reject_unauth_destination
smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination

restrictions结尾的是一些规则,表示smtp处理怎样的邮件。有兴趣的可以百度一下

  1. 其次是域名的一些配置和一些常规配置
ini 复制代码
myhostname = xxxx.com
myorigin = $myhostname
mydomain = $myhostname
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
mydestination = localhost
relayhost = 
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
mailbox_command = procmail -a "$EXTENSION"
mailbox_size_limit = 0
recipient_delimiter = +
inet_interfaces = all
inet_protocols = all
smtp_helo_name = xxxx.com

其中,myhostname设置为主机名,myorigin和mydomain设置为邮箱的域名(yyyy@xxxx.com的那个xxxx.com)。mynetworks指定来自哪里的ip允许直接发邮件,不需要SASL认证。如果你使用内网穿透,需要把mynetworks设置为空,否则经过内网穿透下来的请求来自的ip都会变成127.0.0.1,这样他们就统统不用走SASL就能发邮件了。

  1. 最后设置一些和Dovecot以及MySQL协同工作所需要的选项。
ini 复制代码
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
virtual_transport = lmtp:unix:private/dovecot-lmtp
  1. 配置Postfix的MySQL文件

  2. 创建/etc/postfix/mysql-virtual-mailbox-domains.cf,内容如下:

ini 复制代码
user = admin
password = 123456
hosts = 127.0.0.1:3306
dbname = mailserver
query = SELECT 1 FROM virtual_domains WHERE name='%s'
  1. 创建/etc/postfix/mysql-virtual-mailbox-maps.cf,内容如下
ini 复制代码
user = admin
password = 123456
hosts = 127.0.0.1:3306
dbname = mailserver
query = SELECT 1 FROM virtual_users WHERE email='%s'
  1. 最后创建/etc/postfix/mysql-virtual-alias-maps.cf,内容如下:
ini 复制代码
user = admin
password = 123456
hosts = 127.0.0.1:3306
dbname = mailserver
query = SELECT destination FROM virtual_aliases WHERE source='%s'
  1. 重启服务(systemctl restart postfix),然后做一些验证
perl 复制代码
postmap -q xxxx.com mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf
# 输出1

postmap -q yyyy@xxxx.com mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf
# 输出1

postmap -q zzzz@xxxx.com mysql:/etc/postfix/mysql-virtual-alias-maps.cf
# 输出zzzz是谁的别名
  1. 配置/etc/postfix/master.cf文件

  2. 取消注释submission,开启587端口的服务,后面的inet, n, y, -o等可看作是参数

ini 复制代码
submission inet n       -       y       -       -       smtpd
  -o syslog_name=postfix/submission
  1. 取消smtps,开启SSL的465端口的服务
ini 复制代码
smtps     inet  n       -       y       -       -       smtpd
  -o syslog_name=postfix/smtps
  -o smtpd_tls_wrappermode=yes
  1. 配置/etc/dovecot/dovecot.conf,确保有如下内容:
ini 复制代码
!include conf.d/*.conf
protocols = imap lmtp pop3
  1. 创建一个用户,用户名和组名可以自己喜好决定,Home目录可放在/var/mail或者/home/mail。下文以/var/mail为例。
yaml 复制代码
groupadd -g 5000 vmail
useradd -g vmail -u 5000 vmail -d /家目录
chown -R vmail:vmail /家目录
  1. 修改/etc/dovecot/conf.d/10-mail.conf,确保配置为(注意/var/mail为上文所说的家目录,mail_privileged_group的值为mail是固定的,不是上文所创建的组名):
ini 复制代码
mail_location = maildir:/var/mail/vhosts/%d/%n
mail_privileged_group = mail
  1. 修改/etc/dovecot/conf.d/10-auth.conf,确保配置为
ini 复制代码
disable_plaintext_auth = no
auth_mechanisms = plain login

有的教程把disable_plaintext_auth设置为yes,这表示拒绝明文密码登录,会使得一些客户端登陆不上,所以我选择为no。

拖动到最下方,确保启用SQL设置,其他注释掉。

makefile 复制代码
#!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
  1. 修改/etc/dovecot/conf.d/auth-sql.conf.ext,确保如下配置:
ini 复制代码
passdb {
  driver = sql
  args = /etc/dovecot/dovecot-sql.conf.ext
}
userdb {
  driver = static
  args = uid=vmail gid=vmail home=/var/mail/vhosts/%d/%n
}

其中,vmail、/var/mail为上面步骤创建的用户的相关信息。

  1. 修改/etc/dovecot/dovecot-sql.conf.ext,确保如下配置,connect表示mysql的登录信息
ini 复制代码
driver = mysql
connect = host=127.0.0.1 port=3306 dbname=mailserver user=xxxx password=xxxx
default_pass_scheme = MD5
password_query = SELECT email as user, password FROM virtual_users WHERE email='%u';
  1. 修改/etc/dovecot/conf.d/10-master.conf,确保存在配置:
ini 复制代码
service lmtp {
    unix_listener /var/spool/postfix/private/dovecot-lmtp {
    mode = 0600
    user = postfix
    group = postfix
    }

    # Create inet listener only if you can't use the above UNIX socket
    #inet_listener lmtp {
    # Avoid making LMTP visible for the entire internet
    #address =
    #port =
    #}
}

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 {
    # Auth worker process is run as root by default, so that it can access
    # /etc/shadow. If this isn't necessary, the user should be changed to
    # $default_internal_user.
    user = vmail
}
  1. 修改/etc/dovecot/conf.d/10-ssl.conf,以开启ssl认证,确保存在一下配置
ini 复制代码
ssl = required
ssl_cert = <fullchain位置
ssl_key = <密钥位置

ssl使用required应该是说ssl是必须的意思,也可以使用yes表示开启ssl也允许非ssl。

ssl_cert和ssl_key的<符号是必须的,位置写绝对路径

  1. ssl=no: SSL/TLS 完全禁用。

  2. ssl=yesdisable_plaintext_auth=no: SSL/TLS 提供给客户端,但客户端不需要使用它。即使在连接上未启用 SSL/TLS,客户端也可以使用纯文本身份验证登录。这是不安全的,因为明文密码暴露在互联网上。

  3. ssl=yesdisable_plaintext_auth=yes: SSL/TLS 提供给客户端,但客户端不需要使用它。不允许客户端使用明文身份验证,除非先启用 SSL/TLS。但是,如果启用了非明文身份验证机制,即使没有 SSL/TLS,它们仍然是允许的。根据它们的安全程度,身份验证要么是完全安全的,要么可能有一些方法使其受到攻击。

  4. ssl=required:即使使用非明文身份验证机制,也始终需要 SSL/TLS。任何在启用 SSL/TLS 之前进行身份验证的尝试都将导致身份验证失败。请注意,此设置与 STARTTLS 命令无关 - 允许使用隐式 SSL/TLS 或 STARTTLS 命令。

  5. 修改/etc/dovecot的权限,其中vmail为上面创建的用户

bash 复制代码
chown -R vmail:dovecot /etc/dovecot
chmod -R o-rwx /etc/dovecot
  1. 重启postfix和dovecot,如此一postfix和dovecot就配置完成了。

OpenDKIM配置

  1. 安装OpenDKIM

    sudo apt install opendkim opendkim-tools

  2. 修改配置文件/etc/opendkim.conf,确保有以下内容

bash 复制代码
Domain                  xxxx.com                                 
KeyFile         /etc/dkimkeys/dkim.key                                      
Selector                dkim                                                
SOCKET                  inet:8891@localhost

其中,SOCKET的端口可以使用8891,也有人使用8892,我没发现区别。

  1. 修改/etc/default/opendkim,确保有以下内容
ini 复制代码
SOCKET="inet:8891@localhost"
  1. 生成dkim密钥对(一般在用户的家目录下生成,再复制到指定位置)

    sudo opendkim-genkey -t -s dkim -d xxxx.com

然后执行移动操作

bash 复制代码
sudo mv dkim.key /etc/dkimkeys/
sudo chown opendkim:opendkim dkim.key
  1. 在postfix的配置文件/etc/postfix/main.cf添加如下配置
ini 复制代码
smtpd_milters = inet:localhost:8891
non_smtpd_milters = inet:localhost:8891
milter_protocol = 2
milter_default_action = accept
  1. 关注到第四步生成密钥对时产生的dkim.txt文件。
ini 复制代码
dkim._domainkey IN      TXT     ( "v=DKIM1; k=rsa; t=y; "
          "p=xxxxxxxxxx" )  ; ----- DKIM key dkim for Example Domain

复制双引号的内容,去除双引号和换行,整理为:v=DKIM1; k=rsa; t=y; p=xxxxxxxxxx的形式。在域名添加为txt记录(主机记录为dkim._domainkey)。

  1. 重启postfix和opendkim,如此一来就配置完成了。

配置SpamAssassin

SpamAssassin为Postfix实现反垃圾的功能。

  1. 安装SpamAssassin
arduino 复制代码
sudo apt-get install spamassassin spamc
  1. 添加用户
css 复制代码
sudo adduser spamd --disabled-login
  1. 修改配置文件/etc/default/spamassassin,确保有如下配置:
ini 复制代码
ENABLED=1
SAHOME="/var/log/spamassassin/"
OPTIONS="--create-prefs --max-children 5 --username spamd --helper-home-dir /home/spamd/ -s /home/spamd/spamd.log"
PIDFILE="/var/run/spamd.pid"
CRON=1
  1. 修改/etc/spamassassin/local.cf,确保有如下配置:
markdown 复制代码
rewrite_header Subject ***** SPAM _SCORE_  *****
required_score          5.0
use_bayes               1
bayes_auto_learn        1
  1. 修改/etc/postfix/master.cf文件

添加配置:

ruby 复制代码
spamassassin unix -     n       n       -       -       pipe
    user=spamd argv=/usr/bin/spamc -f -e
    /usr/sbin/sendmail -oi -f ${sender} ${recipient}

为smtp,smtps和submission添加参数

ini 复制代码
    -o content_filter=spamassassin

有些教程只为smtp添加参数,我认为应该都添加。

添加之后,应该看上去像:

ini 复制代码
smtp      inet  n       -       y       -       -       smtpd
   -o content_filter=spamassassin
  1. 执行update-rc.d spamassassin enable,然后重启postfix和spamassassin即可完成配置。

客户端连接服务器

客户端可以使用FoxMail或者雷鸟。

填写登录名为yyyy@xxxx.com以及密码,然后输入正确的smtp和pop3服务器地址以及端口号即可。

  1. smtp默认25端口,SSL则是465端口。

  2. pop3默认110端口,SSL则是995端口。

  3. imap默认143端口,SSL则是993端口。

写在最后

以上是根据我配置邮件服务器的过程所总结的文章,我也折腾了快两个星期了。可能会有错误,欢迎留言咨询也以便我更正错误。

相关推荐
gb421528729 分钟前
springboot中Jackson库和jsonpath库的区别和联系。
java·spring boot·后端
程序猿进阶30 分钟前
深入解析 Spring WebFlux:原理与应用
java·开发语言·后端·spring·面试·架构·springboot
颜淡慕潇1 小时前
【K8S问题系列 |19 】如何解决 Pod 无法挂载 PVC问题
后端·云原生·容器·kubernetes
向前看-8 小时前
验证码机制
前端·后端
超爱吃士力架10 小时前
邀请逻辑
java·linux·后端
AskHarries12 小时前
Spring Cloud OpenFeign快速入门demo
spring boot·后端
isolusion13 小时前
Springboot的创建方式
java·spring boot·后端
zjw_rp13 小时前
Spring-AOP
java·后端·spring·spring-aop
TodoCoder14 小时前
【编程思想】CopyOnWrite是如何解决高并发场景中的读写瓶颈?
java·后端·面试