这篇文章主要介绍怎么配置一个使用虚拟用户的邮件系统。例如:些邮件系统的发信人、收信人并不是linux的系统用户(不存在于/etc/passwd
中)。
简单来讲,本文使用Postfix提供邮件发送服务, 使用Dovecot提供IMAP接收服务, 使用Roundcube作为邮件的web前端, 使用PostfixAdmin作为管理界面来管控这一整套系统, 目前适配PHP8
本文提供的解决方案将允许您使用当前最好的安全机制,您将能够使用SMTP和SMTPS发送邮件,并使用POP3,POP3S,IMAP和IMAPS接收邮件。 此外,由于使用了PostfixAdmin,配置将很容易,用户将能够使用Roundcube登录。
特别说明:邮件系统需要稳定运行,至少需要1c1g内存的机器,否则会导致OOM
安装
开始这一步之前, 你需要按照链接中的页面安装好mysql和postfix
[root@mail ~]# pacman -S mysql postfix postfix-mysql postfix-lmdb dovecot
安装认证的软件包和webmail
[root@mail ~]# pacman -S php php-fpm nginx nginx-mod-cache_purge
[root@mail ~]# pacman -S postfixadmin roundcubemail
配置
Postfix
您需要将发往“root”的所有邮件映射到另一个帐户vmail
[root@mail ~]# nano -w /etc/postfix/aliases
root: vmail
检查配置 Check configuration
运行postfix check
命令来完成配置检查。它会输出所有你在配置文件中可能写错的东西。
运行postconf
命令可以查看所有的配置。运行postconf -n
命令可以查看与默认配置的区别。
加密SMTP发送
nano -w /etc/postfix/main.cf
# 发送加密
smtp_tls_security_level = may
smtp_tls_loglevel = 1
加密SMTP接收
nano -w /etc/postfix/main.cf
# 接收加密
smtpd_tls_security_level = may
smtpd_tls_cert_file = /etc/ssl/private/vmail.crt
smtpd_tls_key_file = /etc/ssl/private/vmail.key
smtpd_tls_loglevel = 1
设置SMTP服务
nano -w /etc/postfix/master.cf
# Choose one: enable submission for loopback clients only, or for any client.
#127.0.0.1:submission inet n - n - - smtpd
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_reject_unlisted_recipient=no
# Instead of specifying complex smtpd_<xxx>_restrictions here,
# specify "smtpd_<xxx>_restrictions=$mua_<xxx>_restrictions"
# here, and specify mua_<xxx>_restrictions in main.cf (where
# "<xxx>" is "client", "helo", "sender", "relay", or "recipient").
# -o smtpd_client_restrictions=
# -o smtpd_helo_restrictions=
# -o smtpd_sender_restrictions=
-o smtpd_relay_restrictions=
-o smtpd_recipient_restrictions=permit_sasl_authenticated,reject
-o milter_macro_daemon_name=ORIGINATING
# Choose one: enable submissions for loopback clients only, or for any client.
#127.0.0.1:submissions 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_reject_unlisted_recipient=no
# Instead of specifying complex smtpd_<xxx>_restrictions here,
# specify "smtpd_<xxx>_restrictions=$mua_<xxx>_restrictions"
# here, and specify mua_<xxx>_restrictions in main.cf (where
# "<xxx>" is "client", "helo", "sender", "relay", or "recipient").
# -o smtpd_client_restrictions=
# -o smtpd_helo_restrictions=
# -o smtpd_sender_restrictions=
-o smtpd_relay_restrictions=
-o smtpd_recipient_restrictions=permit_sasl_authenticated,reject
-o milter_macro_daemon_name=ORIGINATING
smtpd_*_restrictions
相关选项没有取消注释,是因为$mua_*_restrictions
并没有在main.cf 中定义.
检查服务定义
#nano -w /etc/services
:
#smtps 465/tcp # Secure SMTP
#smtps 465/udp # Secure SMTP
如果没有服务定义,postfix将无法启动
postfix/master[5309]: fatal: 0.0.0.0:smtps: Servname not supported for ai_socktype
nano -w /etc/postfix/main.cf
myhostname = mail.6us.cn
mydomain = 6us.cn
myorigin = $mydomain
inet_interfaces = all
mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain, mail.$mydomain
用户
出于安全原因,应创建一个新用户来存储邮件:
[root@mail ~]# groupadd -g 5000 vmail
[root@mail ~]# useradd -u 5000 -g vmail -s /usr/bin/nologin -d /srv/vmail -m vmail
# 否则无法调用doveadm pw
[root@mail ~]# usermod -aG dovecot postfixadmin
gid 和 uid 都使用 5000 ,这样可以避免和普通用户的冲突。所有你的邮件都会存储在 /srv/vmail
中。也可以将家目录更改到像是 /home/vmail/mail
这样的你自已定义的目录,需要注意的是下面的所有配置中都要对应修改。
数据库
新安装的数据库需要初始化
[root@mail etc]# mariadb-install-db --user=mysql --basedir=/usr --datadir=/srv/mysql
nano -w /etc/my.cnf.d/server.cnf
[mysqld]
datadir=/srv/mysql
你需要建立一个空数据库和相应的用户。 在这篇文章中,用户:postfix_user 有 读/写 这个数据库: postfix_db 的权限 我们将这个用户的密码设为: 12345678 。 你需要去创建数据库和用户,并给它们合适的权限。下面列出了操作命令:
$ mysql -u root mysql> CREATE DATABASE postfix_db; mysql> CREATE USER 'postfix_user'@'localhost' IDENTIFIED BY '12345678'; mysql> GRANT ALL PRIVILEGES ON postfix_db.* TO 'postfix_user'@'localhost' WITH GRANT OPTION; mysql> flush privileges;
新的数据库没有管理员,按照下面的方法创建
CREATE USER 'root'@'%' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION;
flush privileges;
ALTER USER 'root'@'localhost' IDENTIFIED BY 'password';
注意: mysql可以只能使用mariadb
通信加密证书
您将需要一个SSL证书来加密邮件通信(SMTPS / IMAPS / POP3S)。 如果您没有,请创建一个:
# cd /etc/ssl/private/
# openssl req -new -x509 -nodes -newkey rsa:4096 -keyout vmail.key -out vmail.crt -days 1460 #days are optional
# chmod 400 vmail.key
# chmod 444 vmail.crt
或者,使用Let’s Encrypt创建免费的可信证书。 私钥会生成在 /etc/letsencrypt/live/yourdomain/privkey.pem
, 证书会生成在 /etc/letsencrypt/live/yourdomain/fullchain.pem
。 相应地更改配置,或将键符号链接到 /etc/ssl/private
:
# ln -s /etc/letsencrypt/live/yourdomain/privkey.pem /etc/ssl/private/vmail.key # ln -s /etc/letsencrypt/live/yourdomain/fullchain.pem /etc/ssl/private/vmail.crt
使用 PostfixAdmin
nano -w /etc/webapps/postfixadmin/config.local.php
<?php
$CONF['configured'] = true;
// correspond to dovecot maildir path /srv/vmail/%d/%u
$CONF['domain_path'] = 'YES';
$CONF['domain_in_mailbox'] = 'NO';
$CONF['database_type'] = 'mysqli';
$CONF['database_host'] = 'localhost';
$CONF['database_user'] = 'postfix_user';
$CONF['database_password'] = '12345678';
$CONF['database_name'] = 'postfix_db';
// globally change all instances of ''change-this-to-your.domain.tld''
// to an appropriate value
$CONF['default_aliases'] = array (
'abuse' => 'abuse@6us.cn',
'hostmaster' => 'hostmaster@6us.cn',
'postmaster' => 'postmaster@6us.cn',
'webmaster' => 'webmaster@6us.cn'
);
$CONF['vacation_domain'] = 'autoreply.6us.cn';
$CONF['footer_text'] = 'Return to 6us.cn';
$CONF['footer_link'] = 'http://6us.cn';
$CONF['encrypt'] = 'dovecot:SHA512-CRYPT';
//
$CONF['dovecotpw'] = "/usr/sbin/doveadm pw";
// 安装密码
//$CONF['setup_password'] = 'yourhashhere';
使用 Nginx
安装相关软件
[root@mail etc]# pacman -S php php-fpm php-gd php-geoip php-imap php-intl php-tidy php-xsl
nano -w /etc/php/php-fpm.d/postfixadmin.conf
[postfixadmin]
user = postfixadmin
group = postfixadmin
listen = /run/postfixadmin/postfixadmin.sock
listen.owner = root
listen.group = http
listen.mode = 0660
pm = ondemand
pm.max_children = 4
php_admin_value['date.timezone'] = Shanghai/Asia
php_admin_value['session.save_path'] = /tmp
php_admin_value['open_basedir'] = /tmp/:/usr/share/webapps/postfixadmin/:/etc/webapps/postfixadmin/:/usr/bin/doveadm:/var/cache/postfixadmin
nano -w /etc/php/php.ini
extension=iconv
;extension=imap
extension=intl
extension=mysqli
extension=pdo_mysql
date.timezone = "UTC"
open_basedir = /srv/http/:/etc/webapps/postfixadmin:/var/cache/postfixadmin:/usr/share/webapps/postfixadmin/:/etc/webapps/roundcubemail:/var/cache/roundcubemail:/usr/share/webapps/roundcubemail:/srv/roundcubemail:/var/log/roundcubemail:/home/:/tmp/:/dev/urandom
nano -w /etc/nginx/vhost/postfixadmin.conf
server {
listen 8081;
server_name 6us.cn;
root /usr/share/webapps/postfixadmin/public/;
index index.php;
charset utf-8;
access_log /var/log/nginx/6us.cn-access.log;
error_log /var/log/nginx/6us.cn-error.log;
location / {
try_files $uri $uri/ index.php;
}
location ~* \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
include fastcgi_params;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/postfixadmin/postfixadmin.sock;
}
}
现在您可以转到PostfixAdmin的设置页面,让PostfixAdmin创建所需的表并在那里创建用户。
Postfix
nano -w /etc/postfix/main.cf
relay_domains = $mydestination
#virtual_alias_maps = proxy:mysql:/etc/postfix/virtual_alias_maps.cf
virtual_alias_maps = proxy:mysql:/etc/postfix/virtual_alias_maps.cf,proxy:mysql:/etc/postfix/virtual_alias_domains_maps.cf,proxy:mysql:/etc/postfix/virtual_alias_domains_catchall_maps.cf
virtual_mailbox_domains = proxy:mysql:/etc/postfix/virtual_mailbox_domains.cf
virtual_alias_domains = proxy:mysql:/etc/postfix/virtual_alias_domains.cf
virtual_mailbox_maps = proxy:mysql:/etc/postfix/virtual_mailbox_maps.cf
virtual_mailbox_base = /srv/vmail
virtual_mailbox_limit = 512000000
virtual_minimum_uid = 5000
virtual_transport = virtual
virtual_uid_maps = static:5000
virtual_gid_maps = static:5000
local_transport = virtual
local_recipient_maps = $virtual_mailbox_maps
transport_maps = lmdb:/etc/postfix/transport
smtpd_sasl_auth_enable = yes
smtpd_sasl_type = dovecot
smtpd_sasl_path = /var/run/dovecot/auth-client
smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination
smtpd_relay_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination
smtpd_sasl_security_options = noanonymous
smtpd_sasl_tls_security_options = $smtpd_sasl_security_options
smtpd_tls_auth_only = yes
smtpd_tls_received_header = yes
smtpd_sasl_local_domain = $mydomain
broken_sasl_auth_clients = yes
# 发送加密
smtp_tls_security_level = may
smtp_tls_loglevel = 1
# 接收加密
smtpd_tls_security_level = may
#smtpd_tls_auth_only = yes
#smtpd_tls_received_header = yes
smtpd_tls_cert_file = /etc/ssl/private/vmail.crt
smtpd_tls_key_file = /etc/ssl/private/vmail.key
smtpd_tls_loglevel = 1
注意
- 上面的
virtual_mailbox_domains
是你接收邮件的域名列表,并且不能包含你设置在mydestinati
on 中的域名. 所以我们把mydestination
保留为 localhost. virtual_mailbox_maps
包含所有的虚拟账户以及他的邮件路径.virtual_mailbox_base
是邮件基础目录.
nano -w /etc/postfix/virtual_alias_maps.cf
user = postfix_user
password = 12345678
hosts = localhost
dbname = postfix_db
table = alias
select_field = goto
where_field = address
nano -w /etc/postfix/virtual_mailbox_domains.cf
user = postfix_user
password = 123456
hosts = localhost
dbname = postfix_db
table = domain
select_field = domain
where_field = domain
nano -w /etc/postfix/virtual_mailbox_maps.cf
user = postfix_user
password = 12345678
hosts = localhost
dbname = postfix_db
table = mailbox
select_field = maildir
where_field = username
如果要修改映射关系
nano -w /etc/postfix/main.cf
virtual_alias_maps = proxy:mysql:/etc/postfix/virtual_alias_maps.cf,proxy:mysql:/etc/postfix/virtual_alias_domains_maps.cf
virtual_alias_domains = proxy:mysql:/etc/postfix/virtual_alias_domains.cf
nano -w /etc/postfix/virtual_alias_domains_maps.cf
user = postfix_user
password = 12345678
hosts = localhost
dbname = postfix_db
query = SELECT goto FROM alias,alias_domain WHERE alias_domain.alias_domain = '%d' and alias.address = CONCAT('%u', '@', alias_domain.target_domain) AND alias.active = 1 AND alias_domain.active='1'
nano -w /etc/postfix/virtual_alias_domains.cf
user = postfix_user
password = 12345678
hosts = localhost
dbname = postfix_db
query = SELECT alias_domain FROM alias_domain WHERE alias_domain='%s' AND active = '1'
nano -w /etc/postfix/virtual_alias_domains_catchall_maps.cf
user = postfix_user
password = 12345678
hosts = localhost
dbname = postfix_db
query = SELECT goto FROM alias,alias_domain WHERE alias_domain.alias_domain = '%d' and alias.address = CONCAT('@', alias_domain.target_domain) AND alias.active = 1 AND alias_domain.active='1'
更新对应关系
postmap /etc/postfix/transport
Dovecot
nano -w /etc/dovecot/dovecot.conf
protocols = imap pop3
auth_mechanisms = plain
passdb {
driver = sql
args = /etc/dovecot/dovecot-sql.conf
}
userdb {
driver = sql
args = /etc/dovecot/dovecot-sql.conf
}
service auth {
unix_listener auth-client {
group = postfix
mode = 0666
user = postfix
}
user = root
}
mail_home = /home/vmail/%d/%n
mail_location = maildir:~
ssl_cert = </etc/ssl/private/vmail.crt
ssl_key = </etc/ssl/private/vmail.key
ssl_dh = </etc/dovecot/dh.pem
nano -w /etc/dovecot/dovecot-sql.conf
driver = mysql
connect = host=localhost dbname=postfix_db user=postfix_user password=hunter2
# It is highly recommended to not use deprecated MD5-CRYPT. Read more at http://wiki2.dovecot.org/Authentication/PasswordSchemes
default_pass_scheme = SHA512-CRYPT
# Get the mailbox
user_query = SELECT '/home/vmail/%d/%n' as home, 'maildir:/home/vmail/%d/%n' as mail, 5000 AS uid, 5000 AS gid, concat('dirsize:storage=', quota) AS quota FROM mailbox WHERE username = '%u' AND active = '1'
# Get the password
password_query = SELECT username as user, password, '/home/vmail/%d/%n' as userdb_home, 'maildir:/home/vmail/%d/%n' as userdb_mail, 5000 as userdb_uid, 5000 as userdb_gid FROM mailbox WHERE username = '%u' AND active = '1'
# If using client certificates for authentication, comment the above and uncomment the following
#password_query = SELECT null AS password, ‘%u’ AS user
openssl dhparam -out /etc/dovecot/dh.pem 4096
不用与postfix公用数据库。 在这篇文章中,用户:roundcubemail 有 读/写 这个数据库: roundcubemail 的权限 我们将这个用户的密码设为: 12345678 。 你需要去创建数据库和用户,并给它们合适的权限。下面列出了操作命令:
$ mysql -u root mysql> CREATE DATABASE roundcubemail; mysql> CREATE USER 'roundcube'@'localhost' IDENTIFIED BY '12345678'; mysql> GRANT ALL PRIVILEGES ON roundcubemail.* TO 'roundcube'@'localhost' WITH GRANT OPTION; mysql> flush privileges;
nano -w /etc/nginx/vhost/roundcubemail.conf
server {
listen 80;
server_name 6us.cn;
root /usr/share/webapps/roundcubemail;
index index.php
access_log /var/log/nginx/roundcubemail-access.log;
error_log /var/log/nginx/roundcubemail-error.log;
location ^~ /.well-known/acme-challenge/ {
default_type "text/plain";
root /srv/http/letsencrypt;
}
location / {
index index.php index.html index.htm;
try_files $uri $uri/ /index.php?$args;
}
# 静态文件直接发送过期提示,并且关闭日志.
location ~* ^.+\.(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|atom|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
access_log off;
log_not_found off;
expires max;
}
# 转发所有的PHP请求.
location ~ \.php$ {
# 防御零日攻击,如果是通过socket转发到其他服务器的,请关闭
try_files $uri = 404;
#注意: 修改php.ini中的设置 "cgi.fix_pathinfo = 0;"
fastcgi_split_path_info ^(.+\.php)(/.+)$;
include fastcgi_params;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
# fastcgi_intercept_errors on;
fastcgi_pass php-handler;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
location ~ /\.ht {
deny all;
}
location ^~ /backup {
deny all;
}
location ~ ^/(README|INSTALL|LICENSE|CHANGELOG|UPGRADING)$ {
deny all;
}
location ~ ^/(bin|SQL|config|temp|logs)/ {
deny all;
}
}
nano -w /etc/webapps/roundcubemail/config/defaults.inc.php
$config['db_dsnw'] = 'mysql://roundcube:password@localhost/roundcubemail';
$config['plugins'] = array('password');
mkdir /srv/roundcubemail
chown -R http.http /srv/roundcubemail
防火墙需要开放下列端口
POP3: 110
POP3S: 995
SMTP: 25
SMTPS: 465
STARTTTLSSMTP: 587
IMAP: 143
IMAPS: 993
出现错误
[24-Oct-2020 17:14:44 UTC] PHP Warning: stream_socket_enable_crypto(): SSL operation failed with code 1. OpenSSL Error messages:
error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed in /usr/share/webapps/roundcubemail/program/lib/Roundcube/rcube_imap_generic.php on line 1087
一般是ssl 的证书过期了
/etc/ssl/private/
出现错误
[13-Jul-2022 09:00:26 UTC] PHP Fatal error: Uncaught Error: Class "Spoofchecker" not found in /usr/share/webapps/roundcubemail/program/lib/Roundcube/rcube_spoofchecker.php:50
出现错误
warning: lmdb:/etc/postfix/transport is unavailable. open database /etc/postfix/transport.db: No such file or directory
postmap /etc/postfix/transport
要么php-intl没有安装,要么不兼容,最好的做法就是跳过
return false;
// Spoofchecker is part of ext-intl (requires ICU >= 4.2)
$checker = new Spoofchecker();
匿名邮件服务器
所谓匿名邮件服务器,就是只能接受邮件,不能发送邮件,同时把系统中所有的没有接收人的邮件都汇总过来,然后显示出来
1.上传程序
2.设置catch-all email account
创建最后的匹配文件
nano -w proxy:mysql:/etc/postfix/virtual_alias_domains_catchall_maps.cf
参考上面的增加
在数据库中创建一个特殊的用户, 请设置好你的domain
INSERT INTO `postfix_db`.`alias` (`address`, `goto`, `domain`, `created`, `modified`) VALUES ('@$domain', 'unknown@$domain', '$domain', '2000-01-01 00:00:00', '2000-01-01 00:00:00')
创建对应domain的用户:unknown@$domain
这样所有的不在列表的邮件全部会发送给对应的邮箱