UPDATE: I will not be answering questions as this guide is outdated and just copied and pasted into a single page with improved order (not redoing steps or editing the same file over and over in different steps).
Please check out the original article which is being updated frequently:
LinuxBabe - Build Your Own Email Server on Ubuntu: Basic Postfix Setup
Steps were re-written from below links for faster setup.
This guide assumes you are using Ubuntu 18 and Nginx.
Part 1: Build Your Own Email Server on Ubuntu: Basic Postfix Setup - LinuxBabe
Part 2: Install Dovecot IMAP server on Ubuntu and Enable TLS Encryption
Part 3: How to Set up SPF and DKIM with Postfix on Ubuntu Server
Part 4: Creating DMARC Record to Protect Your Domain Name From Email Spoofing
Check hostname:
$ hostname -f
Change hostname:
$ hostnamectl set-hostname example.com
$ nano /etc/hostname # change to example.com
Change hosts:
$ nano /etc/hosts
127.0.1.1 example.com example
127.0.0.1 localhost
127.0.0.1 example.com
Check PTR record:
$ dig -x example.com +short
Create user to send mails from:
$ adduser noreply # can be hello, info, sales etc
Open SMTP ports on firewall:
$ ufw allow smtp
Install mailutils, Postfix, Dovecot and Open DKIM:
$ apt-get update
$ apt-get install mailutils -y
$ apt-get install postfix postfix-policyd-spf-python -y
$ apt-get install dovecot-core dovecot-imapd dovecot-lmtpd -y
$ apt-get install opendkim opendkim-tools -y
$ apt autoremove
Edit Postfix config:
$ nano /etc/postfix/main.cf
Add mydomain
and update myhostname
& mydestination
:
...
mydomain = example.com
myhostname = mail.example.com
...
mydestination = $myhostname, $mydomain, localhost.localdomain, , localhost
...
Add the following lines at the end of the file:
mailbox_transport = lmtp:unix:private/dovecot-lmtp
smtputf8_enable = no
policyd-spf_time_limit = 3600
smtpd_recipient_restrictions =
permit_mynetworks,
permit_sasl_authenticated,
reject_unauth_destination,
check_policy_service unix:private/policyd-spf
# Milter configuration
milter_default_action = accept
milter_protocol = 6
smtpd_milters = local:/opendkim/opendkim.sock
non_smtpd_milters = $smtpd_milters
Save and close, then restart Postfix:
$ systemctl restart postfix
Create email aliases:
$ nano /etc/aliases
# See man 5 aliases for format
postmaster: root
root: noreply # <-- your mail username
Rebuild aliases:
$ newaliases
Open IMAP ports on firewall
$ ufw allow 587/tcp
$ ufw allow 465/tcp
$ ufw allow 143/tcp
$ ufw allow 993/tcp
Install Let’s Encrypt client (certbot):
$ apt install software-properties-common -y
$ add-apt-repository ppa:certbot/certbot
$ apt update
$ apt install certbot python3-certbot-nginx -y
Create virtual host for mail.example.com
:
$ mkdir /var/www/mail
$ touch /etc/nginx/sites-available/mail
$ ln -s /etc/nginx/sites-available/mail /etc/nginx/sites-enabled/mail
$ nano /etc/nginx/sites-enabled/mail
server {
listen 80;
server_name mail.example.com;
root /var/www/mail;
location ~ /.well-known/acme-challenge {
allow all;
}
}
$ mkdir /var/www/mail
$ chown www-data:www-data /var/www/mail -R
# replace www-data with your nginx user if it is different
Test then restart nginx:
$ nginx -t
# or
$ service nginx configtest
# if successful, restart
$ service nginx restart # or reload
Obtain Let’s Encrypt
SSL/TLS certificate:
$ certbot --nginx --agree-tos --redirect --hsts --staple-ocsp -d mail.example.com
Configure Postfix
$ nano /etc/postfix/master.cf
Default code example (commented out):
...
#tlsproxy unix - - y - 0 tlsproxy
#submission inet n - y - - smtpd
# -o syslog_name=postfix/submission
# -o smtpd_tls_security_level=encrypt
...
UNcomment and ADD the following, so that it looks like this:
...
#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_tls_auth_only=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=permit_mynetworks,permit_sasl_authenticated,reject
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject
# -o milter_macro_daemon_name=ORIGINATING
-o smtpd_tls_wrappermode=no
-o smtpd_sasl_type=dovecot
-o smtpd_sasl_path=private/auth
#smtps inet n - y - - smtpd
...
Microsoft outlook only supports submission over port 465. If you are going to use Microsoft outlook mail client, then you also need to enable submission service on port 465 by adding the following lines in the file:
smtps inet n - y - - smtpd
-o syslog_name=postfix/smtps
-o smtpd_tls_wrappermode=yes
-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=permit_mynetworks,permit_sasl_authenticated,reject
# -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
# -o milter_macro_daemon_name=ORIGINATING
-o smtpd_sasl_type=dovecot
-o smtpd_sasl_path=private/auth
Add this at the end of the file:
policyd-spf unix - n n - 0 spawn
user=policyd-spf argv=/usr/bin/policyd-spf
Save and close, then edit the main postfix config:
$ nano /etc/postfix/main.cf
Change TLS parameters from:
# TLS parameters
smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
smtpd_use_tls=yes
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
To the following:
# TLS parameters
smtpd_tls_cert_file=/etc/letsencrypt/live/example.com/fullchain.pem
smtpd_tls_key_file=/etc/letsencrypt/live/example.com/privkey.pem
smtpd_use_tls=yes
smtpd_tls_security_level = encrypt
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1
smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1
smtpd_tls_loglevel = 1
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
smtp_use_tls=yes
smtp_tls_security_level = may
smtp_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1
smtp_tls_protocols = !SSLv2, !SSLv3, !TLSv1
smtp_tls_loglevel = 1
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
NOTE: Remember to change example.com to your folder name.
To check that folder, use: $ ls -la /etc/letsencrypt/live
Restart Postfix and verify success:
$ service postfix restart
$ netstat -lnpt
You should see these:
tcp 0 0 0.0.0.0:587 0.0.0.0:* LISTEN 23511/master
tcp 0 0 0.0.0.0:465 0.0.0.0:* LISTEN 23511/master
tcp 0 0 0.0.0.0:25 0.0.0.0:* LISTEN 23511/master
Configure Dovecot:
$ nano /etc/dovecot/dovecot.conf
Add the following line, then save and close:
protocols = imap lmtp
Add dovecot to the mail group:
$ adduser dovecot mail
Edit the auth config file:
$ nano /etc/dovecot/conf.d/10-auth.conf
Uncomment the following line:
disable_plaintext_auth = yes
Change username format to use full email address:
auth_username_format = %n
Support older email clients:
auth_mechanisms = plain login
Save and close, then edit the SSL/TLS config:
$ nano /etc/dovecot/conf.d/10-ssl.conf
Change yes
to required
ssl = required
Change SSL paths from:
ssl_cert = </etc/dovecot/private/dovecot.pem
ssl_key = </etc/dovecot/private/dovecot.key
To the following:
ssl_cert = </etc/letsencrypt/live/example.com/fullchain.pem
ssl_key = </etc/letsencrypt/live/example.com/privkey.pem
NOTE: Remember to change example.com to your folder name.
To check that folder, use: $ ls -la /etc/letsencrypt/live
Save and close, then edit the master config:
$ nano /etc/dovecot/conf.d/10-master.conf
Change service lmtp
section to the following:
service lmtp {
unix_listener /var/spool/postfix/private/dovecot-lmtp {
mode = 0600
user = postfix
group = postfix
}
}
Change service auth
section to the following:
service auth {
unix_listener /var/spool/postfix/private/auth {
mode = 0660
user = postfix
group = postfix
}
}
Save and close, then edit the mailbox config:
$ nano /etc/dovecot/conf.d/15-mailboxes.conf
To auto-create folders, add the following into every mailbox section:
auto = create
Example:
mailbox Trash {
auto = create
special_use = \Trash
}
Restart Postfix and Dovecot:
$ systemctl restart postfix dovecot
Create a TXT
record on your DNS for @
:
TXT @ 3600 "v=spf1 a mx ip4:<ip4_server_ip> ip6:<ip6_server_ip> ~all"
Note: ip6 is optional.
Test TXT
DNS records:
$ dig example.com txt
Add Postfix user to opendkim group:
$ gpasswd -a postfix opendkim
Edit OpenDKIM config:
$ nano /etc/opendkim.conf
Uncomment the #Commonly-used options;
part and paste in this block below it:
# Commonly-used options; the commented-out versions show the defaults.
Canonicalization simple
Mode sv
SubDomains no
AutoRestart yes
AutoRestartRate 10/1M
Background yes
DNSTimeout 5
SignatureAlgorithm rsa-sha256
Add the following lines at the end of this file:
# Map domains in From addresses to keys used to sign messages
KeyTable refile:/etc/opendkim/key.table
SigningTable refile:/etc/opendkim/signing.table
# Hosts to ignore when verifying signatures
ExternalIgnoreList /etc/opendkim/trusted.hosts
# A set of internal hosts whose mail should be signed
InternalHosts /etc/opendkim/trusted.hosts
Save and close, then create a directory structure for OpenDKIM:
$ mkdir /etc/opendkim
$ mkdir /etc/opendkim/keys
$ chown -R opendkim:opendkim /etc/opendkim
$ chmod go-rw /etc/opendkim/keys
$ nano /etc/opendkim/signing.table
Add this line:
*@example.com default._domainkey.example.com
Save and close, then create the key table:
$ nano /etc/opendkim/key.table
Paste the following:
default._domainkey.example.com example.com:default:/etc/opendkim/keys/example.com/default.private
Save and close, then create the trusted hosts file:
$ nano /etc/opendkim/trusted.hosts
Add the following, then save and close:
127.0.0.1
localhost
*.example.com
Create a folder for your domain:
$ mkdir /etc/opendkim/keys/example.com
Generate OpenDKIM keys:
$ sudo opendkim-genkey -b 2048 -d example.com -D /etc/opendkim/keys/example.com -s default -v
Make OpenDKIM the owner of the key:
$ chown opendkim:opendkim /etc/opendkim/keys/example.com/default.private
Display public key:
$ cat /etc/opendkim/keys/your-domain.com/default.txt
Note: You have to remove the concatenation and make it a single string, example:
"v=DKIM1; h=sha256; k=rsa; p=MIIBIjANBgkqhkDG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr1dZwz3zuCy+5Fym2JjX5/dqtAlo6ejnn3bzr25xI2IYfNxBrVCqQ8N6qa0DKjlQ0jyskNQl/luHA97X3Fh6hbQEiIuPqQm1nNNbXIfZVTQDHzW1F1Bu6jMvu4SSS27jgtveSnHG4PK8jpYJePkead8BV9mg8jVNMYVM4kA4sN+Ux5pvFHUb5V+AUqx64bpR86sPOw6sX1ES/vOkCVvgx15qEYaGAPaZBXwkyJ/2qi1fNmsFTUOn+0rmkDeizCqVcDDdttdpPdujNwKpONuB+6x0MIvIcjbWl9vWDZZgp7kvKiBVXuGi/IBOkZtFdK/2HkvUw7IVZ5lqCji9ZIGllwIDAQAB"
Create a new TXT
DNS record with the above string:
TXT default._domainkey 3600 "v=DKIM1; h=sha256; k=rsa; p=<key>"
Test your key:
$ opendkim-testkey -d example.com -s default -vvv
Create a directory to hold the OpenDKIM socket file:
$ mkdir /var/spool/postfix/opendkim
$ chown opendkim:postfix /var/spool/postfix/opendkim
$ nano /etc/opendkim.conf
Replace:
Socket local:/var/run/opendkim/opendkim.sock
With the following:
Socket local:/var/spool/postfix/opendkim/opendkim.sock
Restart services:
$ systemctl restart opendkim postfix dovecot
Send test mail:
$ echo 'Testing mail server' | mail [email protected] -s 'Test subject' -r noreply
First of all, thank you for such a useful document.
Can you include Roundcube upload in this document?