本文最后更新于:2025年4月14日 凌晨
在 Debian 上用 MySQL 跟™杀人了一样
0x00. 一切开始之前 一切说来话长但长话短说,总而言之笔者正在搭建一个独立可用的邮件系统,因为有一个自己的域名作为后缀来发送邮件的邮箱是非常炫酷的一件事情(?)
在技术选型上笔者经过考虑之后还是选择传统的 Postfix + Dovecot + MariaDB 方案,因为这是被广泛应用在各个企业当中的成熟解决方案,至于为什么选择 MariaDB 而不是 MySQL 则是因为笔者服务器使用的 Debian 发行版对 MySQL 的支持不太好,导致配置过程中出现了一系列事故,使得笔者最终转向 Debian 推荐的 MariaDB :(
话不多说,下面我们来看配置过程:)
至于邮件系统相关原理还有 SMTP、IMAP 协议一类的就不是这篇博客所包含的内容了,请同学们课后自行了解
0x01. 邮件系统搭建 这里我们选择使用传统的 Postfix + Dovecot 方案,服务器用的是笔者之前屯的一台便宜 Debian VPS
DNS 配置 我们主要需要添加三条记录:
A 记录:邮件服务器的域名,指向邮件服务器的 IP,一般可以是 mail.主域名
MX 记录:指向邮件服务器的域名(上面的 A 记录),用于设定邮件交给哪个服务器处理,例如名称设为 mail
、内容设为 mail-server.域名
则意味着 mail.域名
的邮件交给 mail-server.域名
处理;这里笔者的选择是将 主域名
的邮件交给 mail.主域名
处理
TXT(SPF) 记录:发信使用,用于证明该 IP 送出的该 domain 的信不是伪造的,内容应当为 v=spf1 ip4:服务器IP -all
安装 MariaDB 首先安装 MariaDB,用来存放邮件和用户数据
注意这里 极度不推荐使用 MySQL,因为会遇到非常难以解决的问题 ,在 Debian 上用 MySQL 跟™杀人了一样
1 2 $ sudo apt-get update $ sudo apt install mariadb-server mariadb-client
然后就可以使用了
在 MariaDB 中创建对应的数据库 首先登录 root 账户:
创建一个新的数据库并使用:
1 2 CREATE DATABASE IF NOT EXISTS mail_db; USE mail_db;
创建一个表存放认证的域名,并插入一条新记录:
1 2 3 4 5 6 7 8 9 # 创建一个表CREATE TABLE `virtual_domains` ( `id` INT NOT NULL AUTO_INCREMENT, `name` VARCHAR (256 ) NOT NULL , PRIMARY KEY (`id`) ) ENGINE= InnoDB DEFAULT CHARSET= utf8; # 添加域名INSERT INTO virtual_domains VALUE (1 ,'test.com' );
创建一个表存放邮箱与密码(推荐存放哈希值,例如使用 sha256),并添加上一个用户,注意这里的 domain_id
为前面的表中域名的序号(下同):
1 2 3 4 5 6 7 8 9 10 11 12 13 # 创建一个表CREATE TABLE `virtual_users` ( `id` INT NOT NULL AUTO_INCREMENT, `domain_id` INT NOT NULL , `password` VARCHAR (128 ) NOT NULL , `email` VARCHAR (256 ) 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; # 创建一个新用户INSERT INTO virtual_users VALUE (1 , 1 , "后续使用doveadm pw -s SHA256-CRYPT -p '密码' 创建的密码", "arttnba3@test.com");
创建一个表存放邮箱别名,主要用于邮件转发:
1 2 3 4 5 6 7 8 9 10 11 12 # 创建一个表CREATE TABLE `virtual_aliases` ( `id` INT NOT NULL auto_increment, `domain_id` INT NOT NULL , `source` VARCHAR (256 ) NOT NULL , `destination` VARCHAR (256 ) NOT NULL , PRIMARY KEY (`id`), FOREIGN KEY (domain_id) REFERENCES virtual_domains(id) ON DELETE CASCADE ) ENGINE= InnoDB DEFAULT CHARSET= utf8; # 如果要创建转发,参照下面的写法 # INSERT INTO virtual_aliases VALUE (1 , 1 , "rat3bant@test.com", "arttnba3@test.com");
最后创建一个新的用户并给予这些表的权限,后续我们使用该用户访问数据库而非直接使用 root:
1 2 3 4 CREATE USER 'mailuser' @'localhost' IDENTIFIED BY '你的密码' ;GRANT ALL PRIVILEGES ON mail_db.* TO 'mailuser' @'localhost' ; FLUSH PRIVILEGES; EXIT;
Postfix + Dovecot 安装 首先安装 Postfix:
1 $ sudo apt-get install -y postfix postfix-mysql libmariadb-dev libmariadb3
这里会出现一个选择配置的选项,我们先选 Internet Site
,也就是由该服务器通过 SMTP 协议直接收发邮件:
然后配置 system mail name,简而言之就是邮件所使用的域名,以 arttnba3@example.com
为例这里应当填 example.com
:
接下来安装 Dovecot:
1 $ sudo apt-get install -y dovecot-core dovecot-pop3d dovecot-imapd dovecot-lmtpd dovecot-mysql
配置 Postfix 配置 SSL 证书 笔者直接用的 Cloudflare 提供的证书,将其保存到服务器本地中,例如:
/etc/ssl/certs/证书.pem
/etc/ssl/private/证书.key
然后修改 Postfix 的主配置文件 /etc/postfix/main.cf
,确保如下配置:
1 2 3 4 5 6 7 8 smtpd_tls_cert_file=/etc/ssl/certs/你的证书.pem smtpd_tls_key_file=/etc/ssl/private/你的密钥.key smtpd_use_tls=yes smtpd_tls_security_level=encrypt smtpd_tls_mandatory_protocols = TLSv1, TLSv1.1, TLSv1.2, TLSv1.3 smtpd_tls_mandatory_ciphers = high smtp_tls_security_level=encrypt
配置使用 Dovecot 进行身份认证 修改 Postfix 的主配置文件 /etc/postfix/main.cf
,确保如下配置:
1 2 3 4 5 6 smtpd_sasl_type = dovecot smtpd_sasl_path = private/auth smtpd_sasl_auth_enable = yes smtpd_tls_auth_only = yes smtpd_recipient_restrictions = permit_sasl_authenticated permit_mynetworks reject_unauth_destination smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination
域名等常规配置 修改 Postfix 的主配置文件 /etc/postfix/main.cf
,确保如下配置:
1 2 3 4 5 myhostname = 你的mail域名 myorigin = $myhostname mydomain = $myhostname mydestination = localhost smtp_helo_name = $myhostname
配置 MariaDB 与 Dovecot 相关
暂时先不考虑 SQL 注入…因为👴太懒了,反正问了问 ChatGPT 大概是没问题的:(
修改 Postfix 的主配置文件 /etc/postfix/main.cf
,添加这些项:
1 2 3 4 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
在 /etc/postfix/mysql-virtual-mailbox-domains.cf
中按照前面的配置写入如下内容:
1 2 3 4 5 user = mailuser password = 前面设置的密码hosts = 127.0 .0.1 :3306 dbname = mail_db query = SELECT 1 FROM virtual_domains WHERE name='%s'
在 /etc/postfix/mysql-virtual-mailbox-maps.cf
中写入如下内容:
1 2 3 4 5 user = mailuser password = 前面设置的密码hosts = 127.0 .0.1 :3306 dbname = mail_db query = SELECT 1 FROM virtual_users WHERE email='%s'
在 /etc/postfix/mysql-virtual-alias-maps.cf
中写入如下内容:
1 2 3 4 5 user = mailuser password = 前面设置的密码hosts = 127.0 .0.1 :3306 dbname = mail_db query = SELECT destination FROM virtual_aliases WHERE source='%s'
最后重启 Postfix:
1 $ sudo systemctl restart postfix
此时我们执行如下指令进行测试时,结果都应当返回 1
:
1 2 3 4 $ sudo postmap -q 前面添加的域名 mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf $ sudo postmap -q 前面添加的邮箱 mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf # 如果你添加了邮箱别名,运行下面的测试命令: $ sudo postmap -q 前面添加的邮箱 mysql:/etc/postfix/mysql-virtual-alias-maps.cf
开启邮件提交
简而言之用来给客户端向服务器提交请求,一个是常规的 587 端口的提交,另一个是如 outlook 等邮件客户端通过 465 端口的 SMTPS 提交邮件
编辑 /etc/postfix/master.cf
文件,添加如下配置:
1 2 3 4 5 6 7 8 9 submission inet n - y - - smtpd -o syslog_name =postfix/submission -o smtpd_tls_security_level =encrypt -o smtpd_sasl_auth_enable =yes -o smtpd_tls_auth_only =yes smtps inet n - y - - smtpd -o syslog_name =postfix/smtps -o smtpd_tls_wrappermode =yes -o smtpd_sasl_auth_enable =yes
配置 Dovecot 配置协议 在 /etc/dovecot/dovecot.conf
中添加如下内容:
1 protocols = imap lmtp pop3
创建虚拟用户 接下来我们创建一个新的虚拟用户作为 Dovecot 的后端,这里我们创建一个 vmail
用户:
1 2 3 $ sudo groupadd -g 5000 vmail $ sudo useradd --home-dir /var/mail/ --shell /usr/sbin/nologin -g vmail -u 5000 vmail $ sudo chown -R vmail:vmail /var/mail
修改 /etc/dovecot/conf.d/10-mail.conf
,确保如下两条配置:
1 2 mail_location = maildir:/var/mail/vhosts/%d/%nmail_privileged_group = mail
配置认证方式 修改 /etc/dovecot/conf.d/10-auth.conf
,配置如下:
1 auth_mechanisms = plain login
来到文件末尾,注释掉其他 auth 方式,开启 SQL:
1 2 3 4 5 6 #!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-static.conf.ext
不开启明文密码登录:
1 disable_plaintext_auth = yes
接下来修改 /etc/dovecot/conf.d/auth-sql.conf.ext
文件,确保如下配置:
1 2 3 4 5 6 7 8 9 10 passdb { driver = sql args = /etc/dovecot/dovecot-sql.conf.ext } userdb { driver = sql args = /etc/dovecot/dovecot-sql.conf.ext }
配置 SQL 认证过程 接下来修改 /etc/dovecot/dovecot-sql.conf.ext
,确保如下配置:
1 2 3 4 5 driver = mysqlconnect = host=127.0 .0 .1 port=3306 dbname=mail_db user =mailuser password =前面设置的密码 default_pass_scheme = SHA256-CRYPT user_query = SELECT email AS username, '/var/mail/vhosts/%d/%n' AS home, 5000 AS uid, 5000 AS gid FROM virtual_users WHERE email = '%n@%d' ; password_query = SELECT email as user , password FROM virtual_users WHERE email='%u' ;
配置 lmtp、auth、postfix 相关 修改 /etc/dovecot/conf.d/10-master.conf
,确保如如下配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 service lmtp { unix_listener /var/spool/postfix/private/dovecot-lmtp { mode = 0600 user = postfix group = postfix } } service auth { unix_listener auth-userdb { mode = 0600 user = vmail } unix_listener /var/spool/postfix/private/auth { mode = 0666 user = postfix group = postfix } user = dovecot } service auth-worker { user = vmail }
配置 SSL 认证 接下来修改 /etc/dovecot/conf.d/10-ssl.conf
文件,确保如下配置:
1 2 3 ssl = requiredssl_cert = <证书位置ssl_key = <密钥位置
配置文件夹权限 修改 /etc/dovecot
的权限:
1 2 $ sudo chown -R vmail:dovecot /etc/dovecot $ sudo chmod -R o-rwx /etc/dovecot
最后重启 postfix 与 dovecot:
1 2 $ sudo systemctl restart postfix $ sudo systemctl restart dovecot
配置 OpenDKIM 首先安装 OpenDKIM:
1 $ sudo apt install opendkim opendkim-tools
接下来修改 /etc/opendkim.conf
,确保如下配置:
原文件中可能会有一条 Socket
开头的默认配置,需要注释掉
1 2 3 4 Domain 你的域名 Selector dkim KeyFile /etc/dkimkeys/dkim.private SOCKET inet:8891@ localhost
接下来修改 /etc/default/opendkim
,确保如下配置:
1 SOCKET = "inet:8891@localhost"
接下来生成 dkim 密钥对:
1 2 3 $ sudo opendkim-genkey -t -s dkim -d 你的域名 $ sudo mv dkim.private /etc/dkimkeys/ $ sudo chown opendkim:opendkim /etc/dkimkeys/dkim.private
接下来修改 /etc/postfix/main.cf
,确保如下配置:
1 2 3 4 smtpd_milters = inet:localhost:8891 non_smtpd_milters = inet:localhost:8891 milter_protocol = 2 milter_default_action = accept
接下来重启 opendkim 与 postfix:
1 2 $ sudo systemctl restart opendkim $ sudo systemctl restart postfix
最后添加一条格式为 "v=DKIM1;k=rsa;t=y;p=一个字符串"
的 DNS 记录,类型为 TXT
, 记录名为 dkim._domainkey
,p 的内容参照前面 opendkim-genkey
命令在当前目录下生成的 dkim.txt
:
0x02. 测试与登录邮件系统 使用 Outlook/Thunderbird 等软件进行登录 Thunderbird 是一个开源的邮件客户端,可以直接识别并登录,比较方便:
Outlook 电脑端也是可以直接识别出 IMAP 服务器并登录,不过你可能还会需要根据自己的服务器配置手动修改一些东西:
不过 Outlook 会把你的邮件给偷一份到微软自己的 Outlook 服务器上,这个就酌情考虑了:
移动端同电脑端
使用 OpenSSL 单独测试本地端口
🕊
0x03. 可能遇到的问题 没有办法接收/发送邮件 一般是云服务器厂商做了限制,例如 VMISS 就限制了不少端口:
可以 在另一台设备上 用 nmap 检查端口可访问状态,例如笔者在 VMISS 上买的服务器的 25 端口就有点问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 $ nmap -Pn 你的邮件域名 Starting Nmap 7.93 ( https://nmap.org ) at 2025-04-12 18:40 EDT Nmap scan report for 你的邮件域名 (你的IP) Host is up (0.16s latency). Not shown: 992 closed tcp ports (conn-refused) PORT STATE SERVICE 22/tcp open ssh 25/tcp filtered smtp 110/tcp open pop3 143/tcp open imap 465/tcp open smtps 587/tcp open submission 993/tcp open imaps 995/tcp open pop3s Nmap done: 1 IP address (1 host up) scanned in 10.55 seconds
这个时候就只能要么换服务器要么想办法让厂商开放端口了,没啥好的解决方案:(
发信时遇到 451 4.3.0 Temporary lookup failure 这个问题往往出现在你尝试在 Debian 上使用 MySQL 而非 MariaDB 作为你的数据库的场景下
,这个时候你查看邮件日志可能如下:
1 2 3 4 5 6 7 8 $ cat /var/log/mail.log # ... Apr 12 16:49:42 mail postfix/trivial-rewrite[41805]: warning: connect to mysql server 127.0.0.1:3306: Plugin caching_sha2_password could not be loaded: /usr/lib/x86_64-linux-gnu/libmariadb3/plugin/caching_sha2_password.so: cannot open shared object file: No such file or directory Apr 12 16:49:42 mail postfix/trivial-rewrite[41805]: warning: virtual_mailbox_domains: mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf: table lookup problem Apr 12 16:49:42 mail postfix/trivial-rewrite[41805]: warning: virtual_mailbox_domains lookup failure Apr 12 16:49:42 mail postfix/trivial-rewrite[41805]: warning: virtual_mailbox_domains: mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf: table lookup problem Apr 12 16:49:42 mail postfix/trivial-rewrite[41805]: warning: virtual_mailbox_domains lookup failure Apr 12 16:49:42 mail postfix/submission/smtpd[41799]: NOQUEUE: reject: RCPT from localhost[::1]: 451 4.3.0 <arttnba3@👴的域名>: Temporary lookup failure; from=<邮箱地址> to=<邮箱地址> proto=ESMTP helo=<主机名>
简而言之就是 Debian 的 Postfix 默认链接到 MariaDB 相关的库而非 MySQL ,因此默认使用 MariaDB Client(哪怕你安装了 libmysqlclient 包,Postfix 也不会去用),MySQL 默认用的 chaching_sha2_password
在 MariaDB 里 没有 ,自然连接不上:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 $ ldd /usr/lib/postfix/postfix-mysql.so linux-vdso.so.1 (0x00007fff9f9b5000) libmariadb.so.3 => /lib/x86_64-linux-gnu/libmariadb.so.3 (0x00007fcb2df44000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fcb2dd63000) libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007fcb2dd44000) libssl.so.3 => /lib/x86_64-linux-gnu/libssl.so.3 (0x00007fcb2dc9b000) libcrypto.so.3 => /lib/x86_64-linux-gnu/libcrypto.so.3 (0x00007fcb2d800000) /lib64/ld-linux-x86-64.so.2 (0x00007fcb2dfab000)$ ldd /usr/lib/postfix/sbin/trivial-rewrite linux-vdso.so.1 (0x00007ffd4faf3000) libpostfix-master.so => /usr/lib/postfix/libpostfix-master.so (0x00007f709beff000) libpostfix-global.so => /usr/lib/postfix/libpostfix-global.so (0x00007f709beb2000) libpostfix-util.so => /usr/lib/postfix/libpostfix-util.so (0x00007f709be68000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f709bc83000) libdb-5.3.so => /lib/x86_64-linux-gnu/libdb-5.3.so (0x00007f709bac1000) libnsl.so.2 => /lib/x86_64-linux-gnu/libnsl.so.2 (0x00007f709baa4000) libicuuc.so.72 => /lib/x86_64-linux-gnu/libicuuc.so.72 (0x00007f709b8a6000) /lib64/ld-linux-x86-64.so.2 (0x00007f709bf17000) libtirpc.so.3 => /lib/x86_64-linux-gnu/libtirpc.so.3 (0x00007f709b878000) libicudata.so.72 => /lib/x86_64-linux-gnu/libicudata.so.72 (0x00007f7099a00000) libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f7099600000) libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f7099920000) libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f709b856000) libgssapi_krb5.so.2 => /lib/x86_64-linux-gnu/libgssapi_krb5.so.2 (0x00007f709b803000) libkrb5.so.3 => /lib/x86_64-linux-gnu/libkrb5.so.3 (0x00007f7099846000) libk5crypto.so.3 => /lib/x86_64-linux-gnu/libk5crypto.so.3 (0x00007f709b7d6000) libcom_err.so.2 => /lib/x86_64-linux-gnu/libcom_err.so.2 (0x00007f7099840000) libkrb5support.so.0 => /lib/x86_64-linux-gnu/libkrb5support.so.0 (0x00007f7099832000) libkeyutils.so.1 => /lib/x86_64-linux-gnu/libkeyutils.so.1 (0x00007f709982b000) libresolv.so.2 => /lib/x86_64-linux-gnu/libresolv.so.2 (0x00007f709981a000)
最简单的解决方案就是 不使用 MySQL 而是使用 MariaDB,这样一切问题便能迎刃而解,笔者不建议大家去折腾其他解决方案,因为会非常麻烦
可能会有人想到获取可以更改 MySQL 的认证插件为 mysql_native_password
使得 MariaDB 能兼容,但是 Debian 上会遇到一系列的各种问题,简而言之笔者想了很多方法都未能成功:(
以及似乎在 Debian 上也没有简单的办法能让 Postfix 默认使用 MySQL 而非 MariaDB, 这时就体现出 Gentoo 的 USE flag 的优越性所在了
Undelivered Mail Returned to Sender
出错了,但我们做对了.jpg
有的时候在你发完邮件之后可能会收到这样一封邮件:
这种情况下一般而言 你的配置是没有问题的,只是你的服务器供应商的网段被对方给拉黑了 ,因此你发出去的邮件会被拒收
解决方案除了通过换 VPS 供应商的方式更换到不同 IP 段以外应该没有别的办法:(