Are you sick of $BigDataCorporation spying on your emails?

Let's set up our own mail server using Postfix, Dovecot, and OpenDKIM!

Postfix is the actual mail service itself. It receives mail and sends mail to and from external servers.

Dovecot is a mail connector essentially. It allows you to store and access mail from remote devices via IMAP and SMTP, like from your phone or laptop.

OpenDKIM is a mail signature verification system. Emails signed with DKIM tell the recipient that the email is genuine and originates from the real domain name. Google's Gmail will block all mail from unknown sources that do not have a DKIM signature, so we need to have this enabled.

-Initial Setup

First let's to install postfix, dovecot, and the MySQL connectors for them.

# apt install postfix postfix-mysql dovecot-core dovecot-imapd dovecot-lmtpd dovecot-mysql mysql-server

When postfix asks you, enter your domain name, and setup a root password for MySQL.

-MySQL

Go ahead and login to MySQL:

# mysql -u root -p

Now let’s create the MySQL databases.

This will create the mail database. Change "securepassword" to a secure password for the mail user to connect to the database:

mysql> create database mail; mysql> grant select on mail.* to 'mail'@'localhost' identified by 'securepassword'; mysql> flush privileges; mysql> use mail; mysql> CREATE TABLE `virtual_domains` ( `id` INT NOT NULL AUTO_INCREMENT, `name` VARCHAR(50) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

This will create the users database:

mysql> CREATE TABLE `virtual_users` ( `id` INT NOT NULL AUTO_INCREMENT, `domain_id` INT NOT NULL, `password` VARCHAR(106) NOT NULL, `email` VARCHAR(120) 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;

This will create the aliases database:

mysql> CREATE TABLE `virtual_aliases` ( `id` INT NOT NULL AUTO_INCREMENT, `domain_id` INT 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;

Now we can create our first email address. In this next command make sure to change "somepassword" to a secure password for your email account, and change the "[email protected]" to your name with your domain name.

mysql> INSERT INTO `mail`.`virtual_domains` (`id` ,`name`) VALUES ('1', 'yourdomain.com'); mysql> INSERT INTO `mail`.`virtual_users` (`id`, `domain_id`, `password` , `email`) VALUES ('1', '1', ENCRYPT('somepassword', CONCAT('$6$', SUBSTRING(SHA(RAND()), -16))), '[email protected]');

And exit MySQL

mysql> exit

-Postfix

Start by editing the main configuration:

# nano /etc/postfix/main.cf

Delete everything in here. Hold CTRL and K to delete entire lines to make it go faster.

Put this in the file:

smtpd_tls_cert_file = /etc/letsencrypt/live/yourdomain.com/fullchain.pem smtpd_tls_key_file = /etc/letsencrypt/live/yourdomain.com/privkey.pem smtpd_use_tls = yes smtpd_tls_auth_only = yes smtpd_sasl_type = dovecot smtpd_sasl_path = private/auth smtpd_sasl_auth_enable = yes smtp_tls_security_level = may myhostname = vm-1.yourdomain.com mydestination = yourdomain.com, vm-1.yourdomain.com, localhost.yourdomain.com, 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 milter_protocol = 2 milter_default_action = accept smtpd_milters = inet:localhost:12301 non_smtpd_milters = inet:localhost:12301 alias_maps = hash:/etc/aliases alias_database = hash:/etc/aliases myorigin = /etc/mailname virtual_transport = lmtp:unix:private/dovecot-lmtp 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

As always change "yourdomain" to your actual domain name.

Now do the same for these next few files. In these change "securepassword" to the password you used for the mail user to connect to the database earlier in the MySQL setup.

# nano /etc/postfix/mysql-virtual-mailbox-domains.cf

user = mail password = securepassword hosts = 127.0.0.1 dbname = mail query = SELECT 1 FROM virtual_domains WHERE name='%s'

# nano /etc/postfix/mysql-virtual-mailbox-maps.cf

user = mail password = securepassword hosts = 127.0.0.1 dbname = mail query = SELECT 1 FROM virtual_users WHERE email='%s'

# nano /etc/postfix/mysql-virtual-alias-maps.cf

user = mail password = securepassword hosts = 127.0.0.1 dbname = mail query = SELECT destination FROM virtual_aliases WHERE source='%s'

# nano /etc/postfix/mysql-virtual-email2email.cf

user = mail password = securepassword hosts = 127.0.0.1 dbname = mail query = SELECT email FROM virtual_users WHERE email='%s'

And restart Postfix:

# service postfix restart

Now let’s test the connection. Run this command with the email address you created earlier. If it returns a “1” then it was successful.

# postmap -q [email protected] mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf

Now let’s allow clients to connect with TLS for full encryption.

# nano /etc/postfix/master.cf

Uncomment these lines:

submission inet n - - - - 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_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

Now append this to the recipient_restrictions line before the closing quotes:

permit_mynetworks,permit_sasl_authenticated,reject

-Dovecot

First let’s make a user for it to run as (change "yourdomain"):

# useradd -m yourdomain

Edit Dovecot's main config:

# nano /etc/dovecot/dovecot.conf

Replace everything in here with this changing "yourdomain" as always:

!include_try /usr/share/dovecot/protocols.d/*.protocol protocols = imap lmtp service imap-login { inet_listener imap { port = 0 } } service lmtp { unix_listener /var/spool/postfix/private/dovecot-lmtp { mode = 0600 user = postfix group = postfix } } service auth { unix_listener /var/spool/postfix/private/auth { mode = 0666 user = postfix group = postfix } unix_listener auth-userdb { mode = 0600 user = yourdomain #group = } #unix_listener /var/spool/postfix/private/auth { # mode = 0666 #} user = dovecot } service auth-worker { user = yourdomain } disable_plaintext_auth = yes auth_mechanisms = plain login ssl = required ssl_cert = </etc/letsencrypt/live/yourdomain.com/fullchain.pem ssl_key = </etc/letsencrypt/live/yourdomain.com/privkey.pem mail_location = maildir:/home/yourdomain/mail/%d/%n mail_privileged_group = yourdomain passdb { driver = sql args = /etc/dovecot/dovecot-sql.conf.ext } userdb { driver = static args = uid=yourdomain gid=yourdomain home=/home/yourdomain/mail/%d/%n }

Now let’s make the folders for the domain and set the permissions on it:

# mkdir /home/yourdomain/mail

# mkdir /home/yourdomain/mail/yourdomain.com

# chown -R yourdomain:yourdomain /home/yourdomain/mail

Connect it to the MySQL database:

# nano /etc/dovecot/dovecot-sql.conf.ext

Add this into the file, changing "securepassword" to the mail database user password :

driver = mysql connect = host=localhost dbname=mail user=mail password=securepassword default_pass_scheme = SHA512-CRYPT password_query = SELECT email as user, password FROM virtual_users WHERE email='%u';

Now we set permissions on the dovecot folder itself and the auth daemon:

# chown -R yourdomain:dovecot /etc/dovecot

# chown -R yourdomain:dovecot /var/run/dovecot/auth-userdb

Restart dovecot:

# service dovecot restart

Now we need to open some ports:

# ufw allow 25

# ufw allow 587

# ufw allow 993

# ufw reload

-OpenDKIM

# apt install opendkim opendkim-tools

Edit the config:

# nano /etc/opendkim.conf

Replace everything with:

AutoRestart Yes AutoRestartRate 10/1h UMask 002 Syslog yes SyslogSuccess Yes LogWhy Yes Canonicalization relaxed/simple ExternalIgnoreList refile:/etc/opendkim/TrustedHosts InternalHosts refile:/etc/opendkim/TrustedHosts KeyTable refile:/etc/opendkim/KeyTable SigningTable refile:/etc/opendkim/SigningTable Mode sv PidFile /var/run/opendkim/opendkim.pid SignatureAlgorithm rsa-sha256 UserID opendkim:opendkim Socket inet:[email protected] OversignHeaders From

Edit the next config:

# nano /etc/default/opendkim

And add this to the bottom:

SOCKET="inet:[email protected]"

Now let’s make folders to store keys in (change "yourdomain"):

# mkdir /etc/opendkim

# mkdir /etc/opendkim/keys

# mkdir /etc/opendkim/keys/yourdomain.com

Now to specify trusted hosts:

# nano /etc/opendkim/TrustedHosts

Add this in the file (change "yourdomain"):

127.0.0.1 localhost ::1 *.yourdomain.com

Edit the key table:

# nano /etc/opendkim/KeyTable

Add (change "yourdomain"):

mail._domainkey.yourdomain.com yourdomain.comt:mail:/etc/opendkim/keys/yourdomain.com/mail.private

Now let’s create the keys:

# cd /etc/opendkim/keys/yourdomain.com

# opendkim-genkey -s mail -d yourdomain.com

Set ownership on the key:

# chown opendkim:opendkim mail.private

Now we need to read the key and add it to our DNS record for this domain:

# cat mail.txt

Copy from “mail” to the closing parenthesis

Edit the DNS zone for your domain name:

# nano /etc/bind/zones/db.yourdomain.com

Paste the key into the very bottom of the zone file. Remove the opening and closing parenthesis, and make sure it is all on a single line. It should look like this:

mail._domainkey IN TXT "v=DKIM1; k=rsa; p=AKLJBVEWBlKjbfhlkse1237098vfnkuNJHYGFASD978290NLNo920293480KBN8oy0324lnawlskn213098"

Now increment the serial number on line 6 of the zone file by 1.

Restart bind9, postfix, and opendkim:

# service bind9 reload

# service postfix restart

# service opendkim restart

-Closing notes

If everything works you can now setup your email client. Use the full email address as the username, and connect to your domain name for the server. Make sure to enable SSL/TLS for inbound messages through IMAP, and STARTTLS for outbound messages through SMTP.

Now you have your own custom configured email server!

To add more email accounts, run the same command when you added the first email account. Make sure to increase the line number each time. This is the first number on the last line of this command. In the example below it is "2" for adding a second account. To add a third account run the same command with the 3rd email address and password you want and set this number to "3".

mysql> INSERT INTO `mail`.`virtual_users` (`id`, `domain_id`, `password` , `email`) VALUES ('2', '1', ENCRYPT('somepassword', CONCAT('$6$', SUBSTRING(SHA(RAND()), -16))), '[email protected]');

Thanks for reading! If you have any feedback let me know, or if you have any questions just ask.