There are many articles on the Internet about using Postfix to build a mail server, but none of them can be built successfully. After many destructions and reconstructions, the mail system was finally completed and can send and receive mails smoothly. This article will introduce how to use Postfix+Dovecot+MySQL+roundcube to build a mail server in a Debian system.
First, the basic requirements are introduced: domain name, server with public IP, the server can open mail ports 25, 110, 143, 465, 587, 993, 995. If it is not open, you can give up. Of course, there are also essential Linux usage skills. If you don’t know anything about vi, forget it, because the setting is very complicated, and I also completed it after a lot of search and practice.
The purpose of each component:
Postfix: is a standard MTA “Mail Transfer Agent” server, which is responsible for managing mail sent to the local machine and mail sent from the local machine to the outside world through the SMTP protocol. In this case, Postfix will hand over the local delivery of mail (archiving mail to local disk after receiving mail) to Dovecot’s LMTP service “Local Mail Transfer Protocol service” for processing.
Dovecot: It is an IMAP/POP server that receives emails sent to the local machine from the outside. Dovecot’s job includes: verifying the user’s identity to ensure that the email is not leaked. In this example, Dovecot will be responsible for account and password verification. We will configure Dovecot to query the local MySQL database to confirm the email account identity.
MySQL: Database, used to store all domain names and user information, including: domain name information to be monitored, user email address, login password, email alias “alias”, etc.
The following directly introduces the process of setting up a mail server in Jun Ge’s lnmp environment.
DNS configuration
First, the domain name. Here, bobobk.com is used as an example. In addition, a server for sending and receiving emails is also prepared. Then go to the domain name provider to set up DNS. Taking cloudflare as an example, Here you need to set up mx records, mail servers and spf anti-spam txt settings. Here, you also need to set up an mx a record to record resolution that does not go through cdn. Taking this domain name as an example
A imap 107.148.0.0 DNS only Auto
A mx 107.148.0.0 DNS only Auto
A pop 107.148.0.0 DNS only Auto
A smtp 107.148.0.0 DNS only Auto
MX bobobk.com mx.bobobk.xyz DNS only Auto
TXT bobobk.com v=spf1 mx mx:mx.bobobk.com ip4:107.148.0.0 ~all DNS only Auto
Set hostname
Set the server’s hostname and hosts file First, /etc/hostname
mx.bobobk.com
file hosts,/etc/hosts
localhost mx.bobobk.com bobobk.com
update hostname
hostname -F /etc/hostname
Installation of postfix and dovecot
Installation
apt update -y
apt upgrade -y
apt install postfix postfix-mysql dovecot-core dovecot-imapd dovecot-pop3d dovecot-lmtpd dovecot-mysql -y
When installing Postfix, select Internet Site and set system mail name to mx.bobobk.com.
mysql initialization, user settings
Two databases are needed here, one is postfix, which is used for user account and password verification. One is roundcube, which is used for email web login to send and receive emails .
First, the postfix database and creation
CREATE DATABASE postfix;
grant all privileges on postfix.* to 'postfixadmin'@'localhost' IDENTIFIED BY 'postfixadminp';
use postfix;
## domain
CREATE TABLE `virtual_domains` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(50) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
## user
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;
## alias
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;
Next, insert the necessary data
# domain
insert into virtual_domains(id,name) values(1,'bobobk.com');
insert into virtual_domains(id,name) values(2,'mx.bobobk.com');
# user
insert into virtual_users(id,domain_id,password,email) values(1,1,'testpassword','tech@bobobk.com');
insert into virtual_users(id,domain_id,password,email) values(2,1,'testpassword','tech2@bobobk.com');
# alias
insert into virtual_aliases(id,domain_id,source,destination) values values (1,2,'ali@bobobk.com','tech@bobobk.com');
By the way, check the inserted data
select * from virtual_domains;
select * from virtual_users;
select * from virtual_aliases;
Postfix configuration modification
Next, modify the postfix configuration First, replace the /etc/postfix/main.cf file directly, and remember to change bobobk.com to your domain name
smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU)
biff = no
# appending .domain is the MUA's job.
append_dot_mydomain = no
# Uncomment the next line to generate "delayed mail" warnings
#delay_warning_time = 4h
readme_directory = no
# See http://www.postfix.org/COMPATIBILITY_README.html -- default to 2 on
# fresh installs.
compatibility_level = 2
# TLS parameters
smtpd_tls_cert_file=/usr/local/nginx/conf/ssl/mx.bobobk.com/fullchain.cer
smtpd_tls_key_file=/usr/local/nginx/conf/ssl/mx.bobobk.com/mx.bobobk.com.key
smtpd_use_tls=yes
smtpd_tls_auth_only = yes
smtp_sasl_auth_enable = yes
smtpd_sasl_auth_enable = yes
smtp_tls_security_level = may
smtpd_tls_security_level = may
smtpd_sasl_security_options = noanonymous, noplaintext
smtpd_sasl_tls_security_options = noanonymous
# See /usr/share/doc/postfix/TLS_README.gz in the postfix-doc package for
# information on enabling SSL in the smtp client.
# Authentication
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
#
smtpd_helo_restrictions =
permit_mynetworks,
permit_sasl_authenticated,
reject_invalid_helo_hostname,
reject_non_fqdn_helo_hostname
smtpd_recipient_restrictions =
permit_mynetworks,
permit_sasl_authenticated,
reject_non_fqdn_recipient,
reject_unknown_recipient_domain,
reject_unlisted_recipient,
reject_unauth_destination
smtpd_sender_restrictions =
permit_mynetworks,
permit_sasl_authenticated,
reject_non_fqdn_sender,
reject_unknown_sender_domain
smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination
myhostname = mx.bobobk.com
#alias_maps = hash:/etc/aliases
#alias_database = hash:/etc/aliases
mydomain = bobobk.com
myorigin = $mydomain
mydestination = localhost
#mydestination = $myhostname, mx.bobobk.com, debian.debian, localhost.debian, localhost
relayhost =
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
mailbox_size_limit = 0
recipient_delimiter = +
inet_interfaces = all
inet_protocols = all
# Handing off local delivery to Dovecot's LMTP, and telling it where to store mail
virtual_transport = lmtp:unix:private/dovecot-lmtp
# Virtual domains, users, and aliases
virtual_mailbox_domains = mysql:/etc/postfix/mysql-virtual-domains.cf
virtual_mailbox_maps = mysql:/etc/postfix/mysql-virtual-users.cf
virtual_alias_maps = mysql:/etc/postfix/mysql-virtual-alias.cf,
# Even more Restrictions and MTA params
disable_vrfy_command = yes
strict_rfc821_envelopes = yes
#smtpd_etrn_restrictions = reject
#smtpd_reject_unlisted_sender = yes
#smtpd_reject_unlisted_recipient = yes
smtpd_delay_reject = yes
smtpd_helo_required = yes
smtp_always_send_ehlo = yes
#smtpd_hard_error_limit = 1
smtpd_timeout = 30s
smtp_helo_timeout = 15s
smtp_rcpt_timeout = 15s
smtpd_recipient_limit = 40
minimal_backoff_time = 180s
maximal_backoff_time = 3h
# Reply Rejection Codes
invalid_hostname_reject_code = 550
non_fqdn_reject_code = 550
unknown_address_reject_code = 550
unknown_client_reject_code = 550
unknown_hostname_reject_code = 550
unverified_recipient_reject_code = 550
unverified_sender_reject_code = 550
Then the /etc/postfix/mysql-virtual-domains.cf file
user = postfixadmin
password = postfixadminp
hosts = 127.0.0.1
dbname = postfix
query = SELECT 1 FROM virtual_domains WHERE name='%s'
/etc/postfix/mysql-virtual-users.cf file
user = postfixadmin
password = postfixadminp
hosts = 127.0.0.1
dbname = postfix
query = SELECT 1 FROM virtual_users WHERE email='%s'
/etc/postfix/mysql-virtual-alias.cf file
user = postfixadmin
password = postfixadminp
hosts = 127.0.0.1
dbname = postfix
query = SELECT destination FROM virtual_aliases WHERE source='%s'
Then file /etc/postfix/master.cf
#
# Postfix master process configuration file. For details on the format
# of the file, see the master(5) manual page (command: "man 5 master" or
# on-line: http://www.postfix.org/master.5.html).
#
# Do not forget to execute "postfix reload" after editing this file.
#
# ==========================================================================
# service type private unpriv chroot wakeup maxproc command + args
# (yes) (yes) (no) (never) (100)
# ==========================================================================
smtp inet n - y - - smtpd
#smtp inet n - y - 1 postscreen
#smtpd pass - - y - - smtpd
#dnsblog unix - - y - 0 dnsblog
#tlsproxy unix - - y - 0 tlsproxy
submission inet n - y - - smtpd
-o syslog_name=postfix/submission
-o smtpd_tls_security_level=encrypt
-o smtpd_sasl_auth_enable=yes
-o smtpd_reject_unlisted_recipient=no
-o smtpd_sasl_type=dovecot
-o smtpd_sasl_path=private/auth
-o smtpd_client_restrictions=permit_sasl_authenticated,reject #$mua_client_restrictions
# -o smtpd_helo_restrictions=$mua_helo_restrictions
# -o smtpd_sender_restrictions=$mua_sender_restrictions
# -o smtpd_recipient_restrictions=
# -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
-o milter_macro_daemon_name=ORIGINATING
smtps inet n - y - - smtpd
-o syslog_name=postfix/smtps
-o smtpd_tls_wrappermode=yes
-o smtpd_sasl_auth_enable=yes
-o smtpd_sasl_type=dovecot
-o smtpd_sasl_path=private/auth
# -o smtpd_reject_unlisted_recipient=no
# -o smtpd_client_restrictions=$mua_client_restrictions
# -o smtpd_helo_restrictions=$mua_helo_restrictions
# -o smtpd_sender_restrictions=$mua_sender_restrictions
# -o smtpd_recipient_restrictions=
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject
-o milter_macro_daemon_name=ORIGINATING
#628 inet n - y - - qmqpd
pickup unix n - y 60 1 pickup
cleanup unix n - y - 0 cleanup
qmgr unix n - n 300 1 qmgr
#qmgr unix n - n 300 1 oqmgr
tlsmgr unix - - y 1000? 1 tlsmgr
rewrite unix - - y - - trivial-rewrite
bounce unix - - y - 0 bounce
defer unix - - y - 0 bounce
trace unix - - y - 0 bounce
verify unix - - y - 1 verify
flush unix n - y 1000? 0 flush
proxymap unix - - n - - proxymap
proxywrite unix - - n - 1 proxymap
smtp unix - - y - - smtp
relay unix - - y - - smtp
# -o smtp_helo_timeout=5 -o smtp_connect_timeout=5
showq unix n - y - - showq
error unix - - y - - error
retry unix - - y - - error
discard unix - - y - - discard
local unix - n n - - local
virtual unix - n n - - virtual
lmtp unix - - y - - lmtp
anvil unix - - y - 1 anvil
scache unix - - y - 1 scache
#
# ====================================================================
# Interfaces to non-Postfix software. Be sure to examine the manual
# pages of the non-Postfix software to find out what options it wants.
#
# Many of the following services use the Postfix pipe(8) delivery
# agent. See the pipe(8) man page for information about ${recipient}
# and other message envelope options.
# ====================================================================
#
# maildrop. See the Postfix MAILDROP_README file for details.
# Also specify in main.cf: maildrop_destination_recipient_limit=1
#
maildrop unix - n n - - pipe
flags=DRhu user=vmail argv=/usr/bin/maildrop -d ${recipient}
#
# ====================================================================
#
# Recent Cyrus versions can use the existing "lmtp" master.cf entry.
#
# Specify in cyrus.conf:
# lmtp cmd="lmtpd -a" listen="localhost:lmtp" proto=tcp4
#
# Specify in main.cf one or more of the following:
# mailbox_transport = lmtp:inet:localhost
# virtual_transport = lmtp:inet:localhost
#
# ====================================================================
#
# Cyrus 2.1.5 (Amos Gouaux)
# Also specify in main.cf: cyrus_destination_recipient_limit=1
#
#cyrus unix - n n - - pipe
# user=cyrus argv=/cyrus/bin/deliver -e -r ${sender} -m ${extension} ${user}
#
# ====================================================================
# Old example of delivery via Cyrus.
#
#old-cyrus unix - n n - - pipe
# flags=R user=cyrus argv=/cyrus/bin/deliver -e -m ${extension} ${user}
#
# ====================================================================
#
# See the Postfix UUCP_README file for configuration details.
#
uucp unix - n n - - pipe
flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient)
#
# Other external delivery methods.
#
ifmail unix - n n - - pipe
flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient)
bsmtp unix - n n - - pipe
flags=Fq. user=bsmtp argv=/usr/lib/bsmtp/bsmtp -t$nexthop -f$sender $recipient
scalemail-backend unix - n n - 2 pipe
flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store ${nexthop} ${user} ${extension}
mailman unix - n n - - pipe
flags=FR user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py
${nexthop} ${user}
postfix test
Restart postfix
systemctl restart postfix
Start testing the mysql data set previously
postmap -q bobobk.com mysql:/etc/postfix/mysql-virtual-domains.cf
1
postmap -q tech@bobobk.com mysql:/etc/postfix/mysql-virtual-users.cf
1
postmap -q ali@bobobk.com mysql:/etc/postfix/mysql-virtual-alias.cf
tech@bobobk.com
The first two items return 1, and the latter return the corresponding domain name, indicating that the test is successful.
dovecot settings
First, the following three items in the /etc/dovecot/dovecot.conf file need to be modified
!include_try /usr/share/dovecot/protocols.d/*.protocol
protocols = imap pop3 lmtp
postmaster_address = postmaster at bobobk.com
then /etc/dovecot/conf.d/10-mail.conf
mail_location = maildir:/home/mail/%d/%n/
mail_privileged_group = mail
and /etc/dovecot/conf.d/10-auth.conf,Modify the following items
disable_plaintext_auth = yes
auth_mechanisms = plain login
!include auth-system.conf.ext
!include auth-sql.conf.ext
Next is /etc/dovecot/conf.d/auth-sql.conf.ext
passdb {
driver = sql
# Path for SQL configuration file, see example-config/dovecot-sql.conf.ext
args = /etc/dovecot/dovecot-sql.conf.ext
}
userdb {
driver = static
args = uid=vmail gid=vmail home=/home/mail/%d/%n
}
file /etc/dovecot/dovecot-sql.conf.ext
driver = mysql
default_pass_scheme = PLAIN
password_query = \
SELECT email as user, password FROM virtual_users WHERE email='%u';
connect = host=127.0.0.1 dbname=postfix user=postfixadmin password=postfixadminp
file /etc/dovecot/conf.d/10-master.conf
service imap-login {
inet_listener imap {
port = 143
}
inet_listener imaps {
port = 993
ssl = yes
}
service pop3-login {
inet_listener pop3 {
port = 110
}
inet_listener pop3s {
port = 995
ssl = yes
}
}
service lmtp {
unix_listener /var/spool/postfix/private/dovecot-lmtp {
#mode = 0666i
mode = 0600
user = postfix
group = postfix
}
}
service imap {
}
service pop3 {
}
service auth {
unix_listener /var/spool/postfix/private/auth {
mode = 0660
user = postfix
group = postfix
}
unix_listener auth-userdb {
mode = 0666
user = vmail
#group =
}
user = dovecot
}
service auth-worker {
user = vmail
}
service dict {
unix_listener dict {
}
}
file /etc/dovecot/conf.d/10-ssl.conf
ssl = yes
ssl_cert = </usr/local/nginx/conf/ssl/mx.bobobk.com/fullchain.cer
ssl_key = </usr/local/nginx/conf/ssl/mx.bobobk.com/mx.bobobk.com.key
Set permissions
chown -R vmail:dovecot /etc/dovecot
restart dovecot
systemctl restart dovecot
Installation and configuration of roundcube
First, make sure that the DNS is set to the local IP address, and then download roundcube to the corresponding site folder, such as mail.bobobk.com
## lnmp vhost add Add the email domain name and then run
cd /home/wwwroot/mail.bobobk.com
wget https://github.com/roundcube/roundcubemail/archive/refs/heads/master.zip
unzip master.zip
mv roun*/* .
rmdir -rf roundcube*
chown -R www:www /home/wwwroot/mail.bobobk.com
Next, install roundcube Enter mail.bobobk.com in your browser Install step by step, and pay attention to the environment required by PHP
cURL: OK
FileInfo: OK
Exif: OK
Iconv: OK
LDAP: OK
GD: OK
Imagick: OK
Zip: OK
That is, FileInfo, Exif, LDAP, and Imagick need to be installed. Currently, lnmp1.9 can be added through addon.sh, but 1.8 seems not to be added, so you need to install it with the latest lnmp script. If it is Baota, I don’t know without testing. After installation, you can delete the installer folder.
Connection test
Mainly test the port, open the port, I used ufw, and be careful not to lock yourself out.
apt install ufw -y
ufw allow 22
ufw allow 80
ufw allow 443
ufw allow 110
ufw allow 143
ufw allow 993
ufw allow 995
Install port testing software net-tools, telnet, mailutils
apt install net-tools -y
apt install telnet -y
apt install mailutils -y
Port open detection
netstat -nlp|grep -v unix
tcp 0 0 0.0.0.0:143 0.0.0.0:* LISTEN 25043/dovecot
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 20750/nginx: worker
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 20750/nginx: worker
tcp 0 0 0.0.0.0:465 0.0.0.0:* LISTEN 11857/master
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 5790/sshd
tcp 0 0 0.0.0.0:25 0.0.0.0:* LISTEN 11857/master
tcp 0 0 0.0.0.0:443 0.0.0.0:* LISTEN 20750/nginx: worker
tcp 0 0 0.0.0.0:993 0.0.0.0:* LISTEN 25043/dovecot
tcp 0 0 0.0.0.0:995 0.0.0.0:* LISTEN 25043/dovecot
tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN 31374/mysqld
tcp 0 0 0.0.0.0:587 0.0.0.0:* LISTEN 11857/master
tcp 0 0 0.0.0.0:110 0.0.0.0:* LISTEN 25043/dovecot
You can see that ports 25, 465, and 587 monitored by postfix and ports 110, 143, 993, and 995 monitored by dovecot are open, but the ufw firewall does not open the postfix port to prevent email abuse.
dovecot login detection
Test using port 110, use the plain text password of the previous database, user and pass are the account and password
telnet 127.0.0.1 110
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
+OK Dovecot ready.
user tech@xrayr.xyz
+OK
pass testpassword
+OK Logged in.
quit
+OK Logging out.
Connection closed by foreign host.
Check the received emails
Then you can proceed with the email receiving operation. Enter roundcude directly for verification. The email password set earlier is tech@bobobk.com and the password is testpassword. Then send an email to Google mailbox and it turns out to be successful.
my new server test mail
rom
Stored with zero-access encryption
tech<tech@bobobk.com>
Star message
Inbox
PM 12:14
To
.....@gmail.com
this is the email from bobobk.com
However, if you want to send emails smoothly, you need to set up PTR. My server does not allow it. When sending emails to Google Mail, an error message will be prompted and the email will be rejected. So it is likely that it can only be used to receive emails.
Summary
This article will briefly explain the configuration process and files after successfully building a mail server on Debian, because the meaning of each configuration is really a lot, you can read more if you have needs The materials I refer to have problems, please use this article together