Roundcube mail app and SPF, DKIM & DMARC on Ubuntu 20.04
Author: Paul Sueno
Created: 9/23/2021
Set up a web mail SaaS using Roundcube and access mail anywhere. Finally, verify your email server using SPF, DKIM & DMARC.
Not all mail apps are accessible across multiple platforms. The best way around this is setting up your own mail web app. This is especially useful when you have your own email server set up. This is like having Outlook, Gmail or Thunderbird but for your own server. This tutorial will show you how to install Roundcube and send verified mail by setting up SPF, DKIM and DMARC.
Up to this point, you should already have a fully functional Ubuntu 20.04 LTS server. It should be set up with LEMP stack, PostgreSQL and Postfix+Dovecot+Virtual users email server.
If you haven't already:
- Install a fully encrypted Ubuntu 20.04 server on the cloud
- Start a LEMP stack with Nginx
- Complete the LEMP stack with PostgreSQL and PHP
- Set up a virtual mail box email server with Postfix and Dovecot
If you're looking for the older version, go to the Ubuntu 18.04 version of the blog.
Install Roundcube
Roundcube is an open source web app that allows you to access your emails through the IMAP protocol. The current stable release of Roundcube is 1.4.11. This version utilizes responsive design principles. The cost of this convenience is speed. If you want the older version of Roundcube (non-responsive, much faster), then consider their current LTS version 1.2. These versions are current as of the publication of the blog (current versions found here). I'll let you decide which one to download, but I'll show you the stable version. Be sure to replace the version you want in the example below. Other than which version you download, the rest of the installation is the same. For full installation instructions, feel free to read the official Roundcube document.
cd ~
wget https://github.com/roundcube/roundcubemail/releases/download/1.4.11/roundcubemail-1.4.11-complete.tar.gz
tar xvf roundcubemail-1.4.11-complete.tar.gz
sudo rsync -Aavx roundcubemail-1.4.11/ /usr/share/roundcube
sudo chown -R www-data:www-data /usr/share/roundcube
Dependencies and PHP configuration
Before using the Roundcube web app, we have to install some modules it depends on. We'll start with some apt packages.
sudo apt install php-cli php-gd php-imagick php-net-ldap2 php-net-ldap3 php-mbstring php-pear php-zip php-imap php-intl php-ldap php-soap php-xmlrpc git unzip
The documentation (INSTALL file) also recommends a few PHP tweaks. Run sudo nano /etc/php/7.4/fpm/php.ini
. A couple settings in the file have already been deprecated or removed by default, whether by Ubuntu or PHP.
[Uncomment line, around 1033] pcre.backtrack_limit=100000
Now reload the PHP settings by running sudo service php7.4-fpm reload
.
Next up is the basic database set up.
PostgreSQL for Roundcube
Let's create the database for our email web app. You can use the command line example for SQL commands or use phpPgAdmin. I'll give the first example for CLI, but all subsequent SQL commands will be as is. For the password, I'd recommend a random alpha-numeric string, e.g., cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 20 | head -n 1
.
sudo -i -u postgres psql -c "CREATE USER roundcube WITH PASSWORD 'password';"
If using phpPgAdmin, be sure to execute these commands separately.
CREATE DATABASE roundcube OWNER roundcube ENCODING unicode;
GRANT ALL ON DATABASE roundcube TO roundcube;
Load the initial database entries, using the template downloaded with Roundcube.
sudo -u postgres psql --host=localhost --dbname=roundcube --username=roundcube -f /usr/share/roundcube/SQL/postgres.initial.sql
Next up is modifying our web server.
Nginx virtual host and TLS
To access the email web app, we will utilize our Nginx web server and virtual host capabilities. I like putting all my hosted sites in the main nginx directory. I do this by using symbolic links. I also create the directory for Certbot Let's Encrypt webroot verification method.
sudo ln -s /usr/share/roundcube /usr/share/nginx/mail
sudo mkdir -p /usr/share/nginx/mail/.well-known/acme-challenge
sudo chown -R www-data:www-data /usr/share/nginx
I'll assume you have your own domain. This tutorial will use the convention domain.com
. Please replace your own domain in domain.com
, and be sure to do this throughout this tutorial. Let's forward (or redirect) calls to the subdirectory mail
to our virtual host. Run sudo nano /etc/nginx/conf.d/default.conf
. Add the location block where the other virtual host redirects are within the https (443) block.
location /mail { return 301 https://mail.domain.com; }
Create the mail
subdomain virtual host configuration file. Run sudo nano /etc/nginx/sites-available/mail.domain.com.conf
.
server {
listen 80;
listen [::]:80;
server_name mail.domain.com;
# Following lines are for Certbot Webroot plugin
location ^~ /.well-known/acme-challenge/ {
default_type "text/plain";
root /usr/share/nginx/mail;
}
# All other pages will go to 443
location / { return 301 https://mail.domain.com$request_uri; }
}
server {
listen 443 ssl;
listen [::]:443 ssl http2;
index index.html index.php;
server_name mail.domain.com;
ssl_certificate /etc/letsencrypt/live/www.domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/www.domain.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/www.domain.com/chain.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_stapling on;
ssl_stapling_verify on;
root /usr/share/nginx/mail;
location ~ ^/(README\.md|INSTALL|LICENSE|CHANGELOG|UPGRADING)$ { return 404; }
location ~ ^/(bin|SQL|config|temp|logs)/ { return 404; }
location / {
try_files $uri $uri/ =404;
add_header X-Robots-Tag noindex,nofollow,noarchive,nosnippet,notranslate,noimageindex;
}
location ~ \.php$ {
fastcgi_pass unix:/run/php/php7.4-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
include snippets/fastcgi-php.conf;
}
add_header X-Robots-Tag noindex,nofollow,noarchive,nosnippet,notranslate,noimageindex;
}
Now we have to tell Nginx that this configuration file is enabled by creating a symbolic link. And then we reload the Nginx service.
sudo ln -s /etc/nginx/sites-available/mail.domain.com.conf /etc/nginx/sites-enabled/
sudo service nginx reload
Before we access the email web app, our TLS certificate has to be renewed to include the mail
subdomain. You must be sure to include any subdomains you want to include with this certificate, each time you request a certificate from Certbot. If you don't specifically list each subdomain, then it gets dropped from the renewed certificate. If you've followed along in my tutorials, the only other subdomain is db
for phpPgAdmin. We'll now add mail
as well.
sudo certbot certonly --expand --webroot \
-w /usr/share/nginx/html -d www.domain.com -d domain.com -d host.domain.com \
-w /usr/share/nginx/db -d db.domain.com \
-w /usr/share/nginx/mail -d mail.domain.com
Next up we load the Roundcube installer.
Roundcube installer on browser
To test if your redirection works, open up a new tab or browser window. Load the site http://domain.com/mail
. It should redirect to https://mail.domain.com
. For the installer, go to https://mail.domain.com/installer
.
There are many options to set up here. The most important are the following:
enable_spellcheck: uncheck
log_driver: syslog
language: en_US
identities_level: one identity with possibility to edit all params BUT NOT EMAIL ADDRESS
postgresql: database= roundcube, user= roundcube, password= [password]
IMAP default_host: ssl://mail.domain.com
IMAP default_port: 993
smtp_server: ssl://mail.domain.com
SMTP default_port: 465
smtp_user/smtp_pass: Use the current IMAP username and pass ...
Do all the checks that Roundcube requires. Then click Create Config
. If the www-data
is the owner of /usr/share/roundcube
, then the installer should have created configuration file. If not, then the installer gives you instructions on how to create the file. Click through the rest of the installer. When done, remove the installer by running sudo rm /usr/share/roundcube/installer/ -r
.
Let's also allow Roundcube to do regular cleanups. Run sudo nano /etc/cron.d/roundcube
:
00 01 * * * www-data /usr/share/nginx/mail/bin/cleandb.sh
This script assumes that Roundcube was installed in a different directory. Let's add a symbolic link pointing from the assumed directory to the actual. Run sudo ln -s /usr/share/roundcube /var/www/roundcubemail
.
Verified Email
To tell the world that you are not a spammer is a daunting task. By default, Microsoft tends to think emails from new domains are junk. Google has not done this to me. But to help the rest of the world know your emails are legitimate and authorized, we will set up SPF, DKIM and DMARC.
Install DomainKeys Identified Mail (DKIM)
DKIM verifies that you indeed set up the email server and authorize mail be sent through your domain. We will use OpenDKIM.
Install the package and back up the original configuration files.
sudo apt install opendkim opendkim-tools
sudo cp /etc/opendkim.conf /etc/opendkim.conf.bak
sudo cp /etc/default/opendkim /etc/default/opendkim.bak
Run sudo nano /etc/opendkim.conf
.
[Comment out, should be line 34] #Socket local:/var/run ...
Now add the following to the end of the file.
Domain domain.com
KeyFile /etc/opendkim/dkim.key
Selector dkim
Socket inet:12301@localhost
That's it for the opendkim.conf
file. Now to the other one. Run sudo nano /etc/default/opendkim
.
[Comment out, should be line 12] #SOCKET=local:$RUNDIR ...
[Add at end of file] SOCKET=inet:12301@localhost
We have to tell Postfix about OpenDKIM. Run sudo nano /etc/postfix/main.cf
, and add these lines at the end.
# DKIM entries
milter_protocol = 2
milter_default_action = accept
smtpd_milters = inet:localhost:12301
non_smtpd_milters = inet:localhost:12301
DKIM only works with a key pair. Let's create them, enable OpenDKIM and restart the services.
sudo mkdir /etc/opendkim
cd /etc/opendkim
sudo opendkim-genkey -t -s dkim -d domain.com
sudo cp dkim.private dkim.key
sudo chmod 660 ./*
sudo chmod 770 /etc/opendkim
sudo chown -R root:opendkim /etc/opendkim
sudo systemctl enable opendkim
sudo service opendkim restart
sudo service postfix restart
The only service that needed installation for telling the world that our email server is verified is OpenDKIM. All the other settings are done through our domain registrar.
DNS configuration for SPF, DKIM & DMARC
We have to add DNS entries in our domain registrar. This tells servers receiving our email that we are verified and authorized email senders for our domain. Feel free to read this explanation on SPF, DKIM and DMARC.
SPF
- Host name: @
- Record type: TXT
- Address: v=spf1 a mx -all
For DKIM, the Address
we will use is from the OpenDKIM program. Use cat
or nano
to output the contents of the public dkim key. Run sudo cat /etc/opendkim/dkim.txt
. Copy the output and paste it into a text editor. You need put all the contents in one line of text, and "stitch" together any text separated by closed quotes. Do not include any of the quotation marks. Once you are done testing, be sure to delete t=y
.
The single line of text should be in the format:
v=DKIM1; h=sha256; k=rsa; p=MIIB ... [many characters long] ...
DKIM
- Host name: dkim._domainkey
- Record type: TXT
- Address: Copy-paste the single-line above
Some explanations on the settings for DMARC. v
is the version. p
tells the receiving email server that if anything is wrong, quaratine our email and save it somewhere like Junk or Spam. rua
and ruf
tells the receiving email server where to send reports about our emails. fo
is when to generate a report; 1
means generate report if either SPF or DKIM test fails. adkim
and aspf
set to r
means it is a relaxed test for DKIM and SPF respectively. pct
means test 100% of all emails. sp
tells the receiving server what to do for email sent by @subdomain.domain.com
. Because none of my emails are sent through a subdomain, I set sp
to reject
.
DMARC
- Host name: _dmarc
- Record type: TXT
- Address: v=DMARC1; p=quarantine; rua=mailto:postmaster@domain.com; ruf=mailto:postmaster@domain.com; fo=1; adkim=r; aspf=r; rf=afrf; pct=100; sp=reject
Other measures to mitigate email flagged as spam
It is ineveitable that the new-kid-on-the-block will look suspicious to the larger email servers. Microsoft servers will definitely file your emails (despite the measures above) as junk/spam. If you email somebody with one of Microsoft's free email domains, then just let them know to check their junk folder and unflag it from being junk. They can also add your new email to their addressbook. Microsoft may even block your emails completely (see Microsoft forum post). You can submit a support ticket to Microsoft Outlook, if this happens.
You can also sign up for these email monitoring programs:
- Google Postmaster Tools
- Microsoft Smart Network Data Services (SNDS)
- Microsoft Junk Mail Reporting Program (JMRP)
- Receive DMARC reports via
feedback loops
by signing up with Validity FBL - Register for dnswl.org and follow their instructions.
- Not sure if it helps, but consider registering or looking up your email server on Sender Score
Test your email server
If you haven't yet, go ahead and start sending some email to see what happens. Hopefully, all is well if you followed along. To see if SPF, DKIM and DMARC works, send an email to a Gmail account. When you receive the email, open it and click on the three dots on the right of the From
field. Then click on Show original
. It should say PASS
for SPF, DKIM and DMARC.
You can also use MXToolBox. There are so many tools here not just for mail servers but web servers too. I'd start with their MX Lookup tool.
You now have a fully functioning email server hosting as many virtual emails you want for your domain. Access to your emails is on your SaaS mail web app. When you send email, it will be verified by your domain registrar as valid mail. Be warned, some larger email servers that receive your mail may still mark it junk. Sorry, this is unavoidable. Remind people with @hotmail.com, @outlook.com, @live.com email addresses to add you to their addressbook and remove your email from junk/spam folder.
- Python web app via Flask, uWSGI and Nginx reverse proxy
- PostgreSQL in LEMP Stack and Ubuntu 20.04
- First blog entry — what I hope to accomplish