BOBOBK

Using Postfix, Dovecot, MySQL, and roundcube to build a mail server under Debian

TECHNOLOGY

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

References

Best Second Also good

Related