Own mail server? Would be awesome! If you just ask ‘why’ this tutorial is not for you 😉
Goals:
- own postfix and dovecot server
- support for virtual accounts
- disable system accounts support
- use database as account backend
1. Instalation
#debian/ubuntu apt-get install postfix postfix-mysql dovecot-core dovecot-imapd dovecot-lmtpd dovecot-mysql sasl2-bin
General type of mail configuration: Internet Site
System mail name: domian.name
2. Mysql/MariaDB backend
First prepare backend:
mysqladmin -p create mailserver mysql -p mailserver GRANT SELECT ON mailserver.* TO 'mailuser'@'127.0.0.1' IDENTIFIED BY 'mailuserpass'; FLUSH PRIVILEGES; CREATE TABLE `virtual_domains` ( `id` int(11) NOT NULL auto_increment, `name` varchar(50) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 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; 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; INSERT INTO `mailserver`.`virtual_domains` (`id` ,`name`) VALUES ('1', 'example.com'), ('2', 'mail.example.com'); INSERT INTO `mailserver`.`virtual_users` (`id`, `domain_id`, `password` , `email`) VALUES ('1', '1', ENCRYPT('123456', CONCAT('$6$', SUBSTRING(SHA(RAND()), -16))), 'mail@example.com'); INSERT INTO `mailserver`.`virtual_aliases` (`id`, `domain_id`, `source`, `destination`) VALUES ('1', '1', 'admin@example.com', 'mail@example.com'), ('2', '1', 'webmaster@example.com', 'mail@example.com'), ('3', '1', 'root@example.com', 'mail@example.com');
Most important postfix config file main.cf
# See /usr/share/postfix/main.cf.dist for a commented, more complete version # Debian specific: Specifying a file name will cause the first # line of that file to be used as the name. The Debian default # is /etc/mailname. #myorigin = /etc/mailname smtpd_banner = $myhostname ESMTP $mail_name (Ubuntu) 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 # TLS parameters smtpd_tls_cert_file=/etc/dovecot/dovecot.pem smtpd_tls_key_file=/etc/dovecot/private/dovecot.pem smtpd_use_tls=yes smtpd_tls_auth_only = yes #Enabling SMTP for authenticated users, and handing off authentication to Dovecot smtpd_sasl_type = dovecot smtpd_sasl_path = private/auth smtpd_sasl_auth_enable = yes smtpd_recipient_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination # See /usr/share/doc/postfix/TLS_README.gz in the postfix-doc package for # information on enabling SSL in the smtp client. myhostname = hostname.example.com alias_maps = hash:/etc/aliases alias_database = hash:/etc/aliases myorigin = /etc/mailname #mydestination = example.com, hostname.example.com, localhost.example.com, localhost mydestination = 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 #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-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, mysql:/etc/postfix/mysql-virtual-email2email.cf
Next let us create configuration files for virtual account support:
#vim /etc/postfix/mysql-virtual-mailbox-domains.cf user = mailuser password = mailuserpass hosts = 127.0.0.1 dbname = mailserver query = SELECT 1 FROM virtual_domains WHERE name='%s' #vim /etc/postfix/mysql-virtual-mailbox-maps.cf user = mailuser password = mailuserpass hosts = 127.0.0.1 dbname = mailserver query = SELECT 1 FROM virtual_users WHERE email='%s' #vim /etc/postfix/mysql-virtual-alias-maps.cf user = mailuser password = mailuserpass hosts = 127.0.0.1 dbname = mailserver query = SELECT destination FROM virtual_aliases WHERE source='%s' #vim /etc/postfix/mysql-virtual-email2email.cf user = mailuser password = mailuserpass hosts = 127.0.0.1 dbname = mailserver query = SELECT email FROM virtual_users WHERE email='%s'
Restart postfix:
service postfix restart
This will test if postfix ‘see’ virtual account from database:
postmap -q example.com mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf postmap -q mail@example.com mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf postmap -q webmaster@example.com mysql:/etc/postfix/mysql-virtual-alias-maps.cf
Next we need to edit other postfix config file master.cf
vim /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"). # # Do not forget to execute "postfix reload" after editing this file. # # ========================================================================== # service type private unpriv chroot wakeup maxproc command + args # (yes) (yes) (yes) (never) (100) # ========================================================================== smtp inet n - - - - smtpd #smtp inet n - - - 1 postscreen #smtpd pass - - - - - smtpd #dnsblog unix - - - - 0 dnsblog #tlsproxy unix - - - - 0 tlsproxy submission inet n - - - - smtpd -o syslog_name=postfix/submission -o smtpd_tls_security_level=encrypt -o smtpd_sasl_auth_enable=yes -o smtpd_client_restrictions=permit_sasl_authenticated,reject -o milter_macro_daemon_name=ORIGINATING smtps inet n - - - - smtpd -o syslog_name=postfix/smtps -o smtpd_tls_wrappermode=yes -o smtpd_sasl_auth_enable=yes -o smtpd_client_restrictions=permit_sasl_authenticated,reject -o milter_macro_daemon_name=ORIGINATING #628 inet n - - - - qmqpd pickup unix n - - 60 1 pickup cleanup unix n - - - 0 cleanup qmgr unix n - n 300 1 qmgr #qmgr unix n - n 300 1 oqmgr tlsmgr unix - - - 1000? 1 tlsmgr rewrite unix - - - - - trivial-rewrite bounce unix - - - - 0 bounce defer unix - - - - 0 bounce trace unix - - - - 0 bounce verify unix - - - - 1 verify flush unix n - - 1000? 0 flush proxymap unix - - n - - proxymap proxywrite unix - - n - 1 proxymap smtp unix - - - - - smtp relay unix - - - - - smtp # -o smtp_helo_timeout=5 -o smtp_connect_timeout=5 showq unix n - - - - showq error unix - - - - - error retry unix - - - - - error discard unix - - - - - discard local unix - n n - - local virtual unix - n n - - virtual lmtp unix - - - - - lmtp anvil unix - - - - 1 anvil scache unix - - - - 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}
This looked scary but to be sure I included full config file, because there could be changes made in newest version to default config files.
Just reset postfix because we finish configure him 🙂
service postfix restart
3. Dovecot
First we make backup of files that we will edit:
cp /etc/dovecot/dovecot.conf /etc/dovecot/dovecot.conf.orig cp /etc/dovecot/conf.d/10-mail.conf /etc/dovecot/conf.d/10-mail.conf.orig cp /etc/dovecot/conf.d/10-auth.conf /etc/dovecot/conf.d/10-auth.conf.orig cp /etc/dovecot/dovecot-sql.conf.ext /etc/dovecot/dovecot-sql.conf.ext.orig cp /etc/dovecot/conf.d/10-master.conf /etc/dovecot/conf.d/10-master.conf.orig cp /etc/dovecot/conf.d/10-ssl.conf /etc/dovecot/conf.d/10-ssl.conf.orig
Edit dovecot.conf
vim /etc/dovecot/dovecot.conf --- ## Dovecot configuration file # If you're in a hurry, see http://wiki2.dovecot.org/QuickConfiguration # "doveconf -n" command gives a clean output of the changed settings. Use it # instead of copy&pasting files when posting to the Dovecot mailing list. # '#' character and everything after it is treated as comments. Extra spaces # and tabs are ignored. If you want to use either of these explicitly, put the # value inside quotes, eg.: key = "# char and trailing whitespace " # Default values are shown for each setting, it's not required to uncomment # those. These are exceptions to this though: No sections (e.g. namespace {}) # or plugin settings are added by default, they're listed only as examples. # Paths are also just examples with the real defaults being based on configure # options. The paths listed here are for configure --prefix=/usr # --sysconfdir=/etc --localstatedir=/var # Enable installed protocols !include_try /usr/share/dovecot/protocols.d/*.protocol protocols = imap lmtp # A comma separated list of IPs or hosts where to listen in for connections. # "*" listens in all IPv4 interfaces, "::" listens in all IPv6 interfaces. # If you want to specify non-default ports or anything more complex, # edit conf.d/master.conf. #listen = *, :: # Base directory where to store runtime data. #base_dir = /var/run/dovecot/ # Name of this instance. Used to prefix all Dovecot processes in ps output. #instance_name = dovecot # Greeting message for clients. #login_greeting = Dovecot ready. # Space separated list of trusted network ranges. Connections from these # IPs are allowed to override their IP addresses and ports (for logging and # for authentication checks). disable_plaintext_auth is also ignored for # these networks. Typically you'd specify the IMAP proxy servers here. #login_trusted_networks = # Sepace separated list of login access check sockets (e.g. tcpwrap) #login_access_sockets = # Show more verbose process titles (in ps). Currently shows user name and # IP address. Useful for seeing who are actually using the IMAP processes # (eg. shared mailboxes or if same uid is used for multiple accounts). #verbose_proctitle = no # Should all processes be killed when Dovecot master process shuts down. # Setting this to "no" means that Dovecot can be upgraded without # forcing existing client connections to close (although that could also be # a problem if the upgrade is e.g. because of a security fix). #shutdown_clients = yes # If non-zero, run mail commands via this many connections to doveadm server, # instead of running them directly in the same process. #doveadm_worker_count = 0 # UNIX socket or host:port used for connecting to doveadm server #doveadm_socket_path = doveadm-server # Space separated list of environment variables that are preserved on Dovecot # startup and passed down to all of its child processes. You can also give # key=value pairs to always set specific settings. #import_environment = TZ ## ## Dictionary server settings ## # Dictionary can be used to store key=value lists. This is used by several # plugins. The dictionary can be accessed either directly or though a # dictionary server. The following dict block maps dictionary names to URIs # when the server is used. These can then be referenced using URIs in format # "proxy::<name>". dict { #quota = mysql:/etc/dovecot/dovecot-dict-sql.conf.ext #expire = sqlite:/etc/dovecot/dovecot-dict-sql.conf.ext } # Most of the actual configuration gets included below. The filenames are # first sorted by their ASCII value and parsed in that order. The 00-prefixes # in filenames are intended to make it easier to understand the ordering. !include conf.d/*.conf # A config file can also tried to be included without giving an error if # it's not found: !include_try local.conf
Let us comment and uncomment lines according with this code:
vim /etc/dovecot/conf.d/10-mail.conf --- mail_location = maildir:/var/mail/vhosts/%d/%n ... mail_privileged_group = mail --- vim /etc/dovecot/conf.d/10-auth.conf -- disable_plaintext_auth = yes ... auth_mechanisms = plain login ... #!include auth-system.conf.ext !include auth-sql.conf.ext -- vim /etc/dovecot/conf.d/auth-sql.conf.ext --- passdb { driver = sql args = /etc/dovecot/dovecot-sql.conf.ext } userdb { driver = static args = uid=vmail gid=vmail home=/var/mail/vhosts/%d/%n } --- vim /etc/dovecot/dovecot-sql.conf.ext --- driver = mysql connect = host=127.0.0.1 dbname=mailserver user=mailuser password=mailuserpass default_pass_scheme = SHA512-CRYPT password_query = SELECT email as user, password FROM virtual_users WHERE email='%u'; --- vim /etc/dovecot/conf.d/10-master.conf --- #default_process_limit = 100 #default_client_limit = 1000 # Default VSZ (virtual memory size) limit for service processes. This is mainly # intended to catch and kill processes that leak memory before they eat up # everything. #default_vsz_limit = 256M # Login user is internally used by login processes. This is the most untrusted # user in Dovecot system. It shouldn't have access to anything at all. #default_login_user = dovenull # Internal user is used by unprivileged processes. It should be separate from # login user, so that login processes can't disturb other processes. #default_internal_user = dovecot service imap-login { inet_listener imap { #port = 0 } inet_listener imaps { port = 993 ssl = yes } # Number of connections to handle before starting a new process. Typically # the only useful values are 0 (unlimited) or 1. 1 is more secure, but 0 # is faster. <doc/wiki/LoginProcess.txt> #service_count = 1 # Number of processes to always keep waiting for more connections. #process_min_avail = 0 # If you set service_count=0, you probably need to grow this. #vsz_limit = 64M } service pop3-login { inet_listener pop3 { port = 0 } inet_listener pop3s { #port = 995 #ssl = yes } } 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 imap { # Most of the memory goes to mmap()ing files. You may need to increase this # limit if you have huge mailboxes. #vsz_limit = 256M # Max. number of IMAP processes (connections) #process_limit = 1024 } service pop3 { # Max. number of POP3 processes (connections) #process_limit = 1024 } service auth { # auth_socket_path points to this userdb socket by default. It's typically # used by dovecot-lda, doveadm, possibly imap process, etc. Its default # permissions make it readable only by root, but you may need to relax these # permissions. Users that have access to this socket are able to get a list # of all usernames and get results of everyone's userdb lookups. unix_listener /var/spool/postfix/private/auth { mode = 0666 user = postfix group = postfix } unix_listener auth-userdb { mode = 0600 user = vmail #group = vmail } # Postfix smtp-auth #unix_listener /var/spool/postfix/private/auth { # mode = 0666 #} # Auth process is run as this user. 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 } service dict { # If dict proxy is used, mail processes should have access to its socket. # For example: mode=0660, group=vmail and global mail_access_groups=vmail unix_listener dict { #mode = 0600 #user = #group = } } --- vim /etc/dovecot/conf.d/10-ssl.conf --- ssl = required ... ssl_cert = </etc/ssl/certs/mail.crt ssl_key = </etc/ssl/private/mail.key ---
We need to create vmail account
groupadd -g 5000 vmail useradd -g vmail -u 5000 vmail -d /var/mail mkdir -p /var/mail/vhosts/example.com/ chown -R vmail:vmail /var/mail chown -R vmail:dovecot /etc/dovecot chmod -R o-rwx /etc/dovecot
Restart dovecot
service dovecot restart
4. Adding more account to backend
mysql -u root -p mailserver #domena INSERT INTO `mailserver`.`virtual_domains` (`name`) VALUES ('newdomain.com'); #konto INSERT INTO `mailserver`.`virtual_users` (`domain_id`, `password` , `email`) VALUES ('5', ENCRYPT('newpassword', CONCAT('$6$', SUBSTRING(SHA(RAND()), -16))) , 'email3@newdomain.com'); #alias INSERT INTO `mailserver`.`virtual_aliases` (`domain_id`, `source`, `destination`) VALUES ('5', 'alias@newdomain.com', 'myemail@gmail.com'); #alias Å‚apiÄ…cy wszystko dla domeny INSERT INTO `mailserver`.`virtual_aliases` (`domain_id`, `source`, `destination`) VALUES ('5', '@newdomain.com', 'myemail@gmail.com');
5. SPAM
If you are interested, there are some post about way to fight with spam:
Fight against spam part 1 – Postfix SPF
Fight against spam part 2 – Postfix DKIM
Fight against spam part 3 – Postfix DMARC
Fight against spam part 4 – Postfix SpamAssassin
Fight against spam part 5 – Dovecot Sieve