Setup a Digital Ocean Server with ISPConfig on Ubuntu 16.04

ISPConfig 3.1 on Ubuntu 16.04 on a Digital Ocean Server incl. Apache, Postfix, Pure FTPD, Let’s Encrypt, MariaDB & Fail2Ban

So, this is the text version of the YouTube Video, which is still uploading at the time of writing.

I know there are plenty tutorials out there to setup ISPConfig on almost any OS – but most of them are kind of general tutorials which don’t dive deeper into things like fine tuning fail2ban, postfix, enabling apache mods for caching and stuffs like that. So my approach is to extend the Perfect Server tutorials on Howtoforge. I will try to keep this tutorial as beginner friendly as possible. So in case you have in mind to have your own server where you can host as many domains and mails your server can handle, this tutorial should be just perfect for you. In case you are a more advanced user, I hope you will not find it that boring.

You will learn how to use the Digital Ocean Nameserver and Firewall. Apart from that, I will dive a bit deeper into the configuration of Postfix and Fail2Ban. I will show you how to configure Postfix to limit the amount of Spam to a very minimum. With digging deeper into Fail2Ban I will show you how you can protect your server against Bruteforce attacks, but also how you can protect your WordPress sites with Fail2Ban. Further you will learn how to install and configure Mod_Pagespeed for super fast web site performance and also how to enable and install caching mods for the apache web server. I will do the whole thing form my Ubuntu Desktop and I will be using the nano editor to edit files. In case you are on Windows, just search for Putty in YouTube. There are plenty of videos for that.

In case you are new to Digital Ocean, I’d appreciate when you could use the ref-link’s from this tutorial. It will get you $10 in hosting credit immediately after unlocking your account by adding a valid payment method. Of course I will have a benefit from it as well and I just hope that providing this tutorial free of charge in return, will make us even. The $10 will definitely allow you to play around for a while and get familiar with the server.

I also want to let you know, that I am not being paid by anyone for doing this. NameCheap, Digital Ocean, and any other TMs or Brands I am mentioning, are just recommendations, because I am convinced of their products or services. Otherwise, I’d not be naming or showing them at all.

This Video will show you how you (actually how I) install ISPConfig on a Digital Ocean droplet. In detail, this Video will show you:

  • Registering & Configuring a new Domain on NameCheap
  • Creating DNS Records on Digtal Ocean (incl. SPF, DKIM, A, MX)
  • Creating a Droplet (VPS) on Digital Ocean
  • ISPConfig 3.1 (incl. valid SSL Certifcate for the Userinterface)
  • Apache Webserver (with Mod_Pagespeed, headers, expires & geoip)
  • Postfix MTA (incl. tight anti spam configuration, using a valid SSL Certificate)
  • Tight Fail2Ban config (incl. WordPress Protection for all WordPress Sites on your server
  • Roundcube Webmail
  • phpMyAdmin (incl. custom URL to prevent bruteforce attacks)
  • PureFTPD (incl. valid SSL Certficate)
  • Policy SPF
  • Creating a Firewall for your Server on Digital Ocean
  • …… and very much more

As soon as the Video is published on YouTube, it should also be available in this blog post. My intention with this post is to describe things a bit more detailed and also give you the files / configurations / links / etc you have seen in the Video.

So first lets start with the links.

Register a Domain at NameCheap

Open NameCheap.com in your browser, click on DOMAINS on top of the page and enter your preferred domain name. You can enter your desired domain with the .tld you prefer, or just leave it blank. For this video I registered the domain name ispconfig-server.space and that is what I will be using the whole time. After your payment has been processed, you will return to the Purchase Summary, click on MANAGE next to your new domain. Find your new domain in the overview, and click on manage again. In the Domain tab, roughly in the middle of the site, you will find “NAMESERVERS”. Click on the drop down button, click on custom DNS, and enter:

ns1.digitalocean.com
ns2.digitalocean.com
ns3.digitalocean.com

and click on the check mark to save your changes.

Sign Up with Digital Ocean

Sign up at Digital Ocean, confirm your E-Mail address and either add a valid payment method or top up your account with PayPal. Make sure you are using my Digital Ocean Link to get a $10 credit. Afterwards click that blue icon at the top right hand corner, click on Settings and then on Billing. Add a payment method, either a credit card or top up your Digital Ocean account using paypal account.

Create a Digital Ocean droplet

Now lets fire up a machine. On your top right hand corner, click on “Create Droplet”. We want to have it running under Ubuntu 16.04, so we choose that. Choosing a size is critical. Choose wisely at this point. Because we are about to setup a Mail Server in this tutorial, we choose the $40 server with 4GB of RAM. 4GB of RAM will make sure that CLAMAV is not running out of memory for the beginning. CLAMAV needs some recourses because it checks emails for malware and viruses.

Now we need to choose a datacenter region. Generally I would suggest you choose a datacenter which is the closest to your audience, not to you. Your audience needs to have quick loading sites and services. Not you. One thing I want to make you aware of, is to choose rather a datacenter which has block storage enabled. Which is at the moment FRA1 (Frankfurt in Germany) NYC1 (New York City in the US), SFO2 (San Francisco in the US) SGP1 (Singapore) and TOR1 (Toronto). Block storage allows you to expand the disc space of your droplet dynamically in case you run out of space – but this I will describe in another tutorial. So I will just pick NYC1 for this tutorial.

At “Select additional options” you want to Monitoring. We don’t enable IPv6 because Fail2Ban is not ready yet for IPv6 and therefore the server is not protected by Fail2Ban against bruteforce attacks from IPv6 networks. At the bottom of the page you want to set your server’s hostname. Here you should rather choose a sub-domain. Something like your.host-name.com. This is kind of important. Your hostname will also represent your mailserver and webserver. After you have finished this tutorial and have created some mail accounts. You will need to enter your.host-name.com as incoming and outgoing mail server to prevent certificate warnings in mail clients. Also it will show up when someone sends a ping to the domains you are hosting on that server. So this should be something easy to remember and decent. I will use host.ispconfig-server.space.

Click on create to fire up your machine. Meanwhile we can create some DNS records for your domain

Setup the DNS Records

Click on Networking and then click on Domains. Enter your domain and click on add.

Make sure A is selected. Enter

@

in the Hostname field and choose your droplet in the next field. Click add to add the record. Then we want to create a MX (Mail Exchange) record for your domain. Make sure MX is selected on top, enter

@

and your droplet hostname in the field next to it. So for me it is:

host.ispconfig-server.space

, enter 10 as priority and click on add. Next we want to add the SPF record for the domain. Click on TXT on top, enter

@

in the Hostname field, and enter

v=spf1 mx a -all

in the text field. Save it. Then we also want to create the record for the subdomain

host.

. So make sure you select A on top, enter

host

as hostname, choose your droplet and save it. If you like to host a www site for your domain, add a www A record for your domain as well. Keep in mind, in case you have enabled IPv6, you should do the same for AAAA records as well.

Logging in to your server

By now your server should be ready. Check your E-Mails, you should find your login details there.

  • Just a short notice about passwords. Use strong ones. Usually I go to xkpasswd.net, have it generate 10 passwords for me, and then I just pick one randomly. So you should maybe do the same. That’s up to you.

To login to your server, enter

ssh root@your-ip

in the terminal and hit enter. The first time you login to a server via SSH, you will be asked this one:

The authenticity of host 'xxx.xxx.xxx.xxx (xxx.xxx.xxx.xxx)' can't be established.
ECDSA key fingerprint is SHA256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.
Are you sure you want to continue connecting (yes/no)?

Type in “yes” and hit the enter key. This adds a ssh fingerprint to your local pc. In case someone is trying to intercept your communication next time you log in, it will give you again a warning. In case all things are fine, you will not see an error message again once you’ve entered yes.

No you are forced to create a new password. First enter the current one, send to you via E-Mail. Then pick one from the list and store it somewhere safely. Enter it twice and you are ready to go.

Lets get started with the server

So now that you are logged in into your machine, you first want to check if there are updates available and install them. So what you do first is to run

apt update && apt upgrade -y

command. It will check if there are any updates available and will install them. apt update checks for updates and apt upgrade installs them. The -y flag will just tell the apt upgrade command that it should install the updates without asking you whether you want it or not. And the && are just combining the commands and making them run in one step.

The next thing we need to do, is to install unzip, so that we can unzip .zip files. install it with:

apt install unzip

and hit the enter key

You will probably notice an error massage appearing about some locales missing for perl. This message will appear almost every time you do something on the server, so we will fix it now. Just enter:

dpkg-reconfigure locales

this command will the show up some options, as it will reconfigure the locales settings on your machine. At first you can simply choose all locales. Use this keys to navigate:

TAB = Navigate between options
SPACEBAR = Choose an option
ARROW KEYS = Navigating up and down

So here you actually just hit the space-bar at the first option (All locales), hit TAB to get to the OK button and hit Enter on your keyboard. Next you will find en_US.UTF-8, just hit the Tab key and then Enter. This process will take a while to finish. Just just get a cup of coffee and wait for it to finish.

Meanwhile, we can check if our DNS Records have been applied

Just open digwebinterface.com, and enter your details in Hostnames & IP Addresses, check: Colorize output to have it look a bit better, check: Sort alphabetically to have a little structure in it, and select: Authoritative unter Nameservers. It should be looking like in my video right now.

Lets install ISPConfig now

We will install ISPConfig 3 using a script which actually does lots of work for us and saves us lots of time.

So we change to the /tmp/ (temporary) directory of the server with:

cd /tmp/

next we need to download the script with:

wget --no-check-certificate -O installer.tgz "https://github.com/servisys/ispconfig_setup/tarball/master"

then we un-tar it using:

tar zxvf installer.tgz

we change in the install directory

cd *ispconfig*

and we start the setup with:

bash install.sh

You will be asked if the script has correctly detected your OS. It should show you:

The detected Linux Distribution is: Ubuntu 16.04.2 LTS

In case it does, just type “y”. At

Select SQL Server type

I would recommend using MariaDB. So just use the arrow down button, hit the spacebar, hit tab and hit enter. Now you have to define a root password for the MySQL root user which should not be the same as the login password you have chosen for the root user on the server. So pick another one from your passwords list for the MySQL root user. Next choose apache as webserver.

The next question is a bit difficult to answer. In case you want or need to use Ioncube you should answer this with NO. In case you have no clue what Ioncube is, let me explain it shortly. Ioncube is a framework which allows php developers to decrypt their php code. In case you just want to host some WordPress Sites on your machine, or Joomla or so, you will not need Ioncube. Therefore, check the things you want to run on your machine now, so that you know if you need Ioncube or not. In this tutorial, I will not use Ioncube, so I just continue with the default.

I’m very sure you want to install phpmyadmin on your machine, so that you can change databases and look into tables or so from your browser instead of using mysql in the cli. I will continue here also with the default.

For mailserver type you can leave dovecot, for webmail type we choose Squirrelmail. You also want the Antivirus database to be updated during the setup, in case you want to host things for your friends, but give them a limited amount of server space, you want to setup quota. In the next step we want to choose the expert mode, since we don’t need all services, we can simply uncheck them in expert mode.

Hit Enter to confirm having Jailkit installed. For the SSL Configuration you can simply hit enter without entering something. We will correct this later.

SSL Country => Enter
SSL State => Enter
SSL Locality => Enter
SSL Organization => Enter
SSL Organization Unit => Enter

Now the script starts running and doing its job. Just let it run and don’t break it. When you find the message:

Attention: When asked 'Configure database for phpmyadmin with dbconfig-common?' select 'NO' Due to a bug in dbconfig-common, this can't be automated.

Press Enter on your keyboard and wait for another popup. In this popup

The phpmyadmin package must have a database installed and configured before it can be used. This can be optionally handled with dbconfig-common…...

you have to choose NO because ISPConfig will take care of the phpmyadmin database. Once the script is done, it will fetch ISPConfig and start the installation of it. Answer the following questions like:

Select language (en,de) [en]: => Hit Enter
Installation mode (standard,expert) [standard]: expert
Full qualified hostname (FQDN) of the server, eg server1.domain.tld [your.host-name.com]: => Hit Enter (when the hostname is OK)
MySQL server hostname [localhost]: => Hit Enter
MySQL server port [3306]: => Hit Enter
MySQL root username [root]: => Hit Enter
MySQL root password []: Enter your MySQL Root Password
MySQL database to create [dbispconfig]: => Hit Enter
MySQL charset [utf8]: => Hit Enter
ISPConfig mysql database username [ispconfig]: => Hit Enter
ISPConfig mysql database password [xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx]: => Hit Enter
Shall this server join an existing ISPConfig multiserver setup (y,n) [n]: => Hit Enter
Configure Mail (y,n) [y]: => Hit Enter
[WARN] autodetect for Postgrey failed
Force configure Postgrey (y,n) [n]: => Hit Enter

Configuring Postfix
Generating a 4096 bit RSA private key
..........................................................................................................................++
..................................................................................................................................................................................................++
writing new private key to 'smtpd.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----

Country Name (2 letter code) [AU]: => Hit Enter
State or Province Name (full name) [Some-State]: => Hit Enter
Locality Name (eg, city) []: => Hit Enter
Organization Name (eg, company) [Internet Widgits Pty Ltd]: => Hit Enter
Organizational Unit Name (eg, section) []: => Hit Enter
Common Name (e.g. server FQDN or YOUR name) []: => Hit Enter
Email Address []: => Hit Enter

[WARN] autodetect for Mailman failed
Force configure Mailman (y,n) [n]: => Hit Enter

Choose n here, since we will not setup a DNS Server..

Configure DNS Server (y,n) [y]: n
[WARN] autodetect for OpenVZ failed
Force configure OpenVZ (y,n) [n]: => Hit Enter
Configure Firewall Server (y,n) [y]: => Hit Enter
[WARN] autodetect for Metronome XMPP Server failed
Force configure Metronome XMPP Server (y,n) [n]: => Hit Enter
Install ISPConfig Web Interface (y,n) [y]: => Hit Enter

You can leave here the default port or you can change it. Just make sure you don’t use a port that needs to be used by another application. When you are unsure, just leave it to 8080.

ISPConfig Port [8080]: => Hit Enter

Pick here again one of the passwords you have created earlier.

Admin password [admin]: xxxxxxxxxxxxxxxxxx
Re-enter admin password []: xxxxxxxxxxxxxxxxxx

Do you want a secure (SSL) connection to the ISPConfig web interface (y,n) [y] => Hit Enter

Generating RSA private key, 4096 bit long modulus
.........................................................................................++
....++
e is 65537 (0x10001)
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]: => Hit Enter
State or Province Name (full name) [Some-State]: => Hit Enter
Locality Name (eg, city) []: => Hit Enter
Organization Name (eg, company) [Internet Widgits Pty Ltd]: => Hit Enter
Organizational Unit Name (eg, section) []: => Hit Enter
Common Name (e.g. server FQDN or YOUR name) []: => Hit Enter
Email Address []:=> Hit Enter
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []: => Hit Enter
An optional company name []: => Hit Enter

Thats all - ISPConfig will now be installed. Afterwards you can access the dashboard using:
https://your.host-name.com:8080 or https://IP_ADDRESS:8080

Installing Apache Mod Pagespeed, GeoIP, Headers, Expires & Cache

But we are not done in the Terminal for now. And we will continue straight. Next thing we need to do, is to enable some apache mods and install mod pagespeed. So we continue with:

cd /tmp/

to get to the temp directory and download the pagespeed deb file with:

wget https://dl-ssl.google.com/dl/linux/direct/mod-pagespeed-stable_current_amd64.deb

To install Mod Pagespeed we run:

dpkg -i mod-pagespeed-stable_current_amd64.deb

Then we want to deactivate Mod Pagespeed server wide. Don’t worry, you can later on enable it per domain and configure it to your needs. Some tools like PIWIK don’t work well with pagespeed.

nano /etc/apache2/mods-available/pagespeed.conf

Around the 4th line you change it to:

ModPagespeed off

Then we install Mod GEOIP to allow the server and certain scripts to use IP Based Geo-Location. Run:

apt install libapache2-mod-geoip -y

Next we need to enable these apache modules with:

a2enmod headers
a2enmod expires
a2enmod cache
a2enmod geoip

Next we want to configure the GEO IP Configuration with first making a backup of the original file:

mv /etc/apache2/mods-enabled/geoip.conf /etc/apache2/mods-enabled/geoip.conf.back

Then enter:

nano /etc/apache2/mods-enabled/geoip.conf

And paste this content into it.

<IfModule mod_geoip.c>
GeoIPEnable On
GeoIPDBFile /usr/share/GeoIP/GeoLiteCity.dat
GeoIPDBFile /usr/share/GeoIP/GeoIP.dat

# SetEnvIf GEOIP_COUNTRY_CODE AE BlockCountry
# SetEnvIf GEOIP_COUNTRY_CODE AF BlockCountry
# SetEnvIf GEOIP_COUNTRY_CODE AL BlockCountry
# SetEnvIf GEOIP_COUNTRY_CODE AM BlockCountry
# SetEnvIf GEOIP_COUNTRY_CODE AZ BlockCountry
# SetEnvIf GEOIP_COUNTRY_CODE BA BlockCountry
# SetEnvIf GEOIP_COUNTRY_CODE BD BlockCountry
# SetEnvIf GEOIP_COUNTRY_CODE BG BlockCountry
# SetEnvIf GEOIP_COUNTRY_CODE BY BlockCountry
# SetEnvIf GEOIP_COUNTRY_CODE CD BlockCountry
# SetEnvIf GEOIP_COUNTRY_CODE CF BlockCountry
# SetEnvIf GEOIP_COUNTRY_CODE CN BlockCountry
# SetEnvIf GEOIP_COUNTRY_CODE GR BlockCountry
# SetEnvIf GEOIP_COUNTRY_CODE HK BlockCountry
# SetEnvIf GEOIP_COUNTRY_CODE IL BlockCountry
# SetEnvIf GEOIP_COUNTRY_CODE IQ BlockCountry
# SetEnvIf GEOIP_COUNTRY_CODE IR BlockCountry
# SetEnvIf GEOIP_COUNTRY_CODE JO BlockCountry
# SetEnvIf GEOIP_COUNTRY_CODE KE BlockCountry
# SetEnvIf GEOIP_COUNTRY_CODE KG BlockCountry
# SetEnvIf GEOIP_COUNTRY_CODE KR BlockCountry
# SetEnvIf GEOIP_COUNTRY_CODE KZ BlockCountry
# SetEnvIf GEOIP_COUNTRY_CODE LB BlockCountry
# SetEnvIf GEOIP_COUNTRY_CODE LY BlockCountry
# SetEnvIf GEOIP_COUNTRY_CODE MA BlockCountry
# SetEnvIf GEOIP_COUNTRY_CODE MD BlockCountry
# SetEnvIf GEOIP_COUNTRY_CODE ME BlockCountry
# SetEnvIf GEOIP_COUNTRY_CODE MN BlockCountry
# SetEnvIf GEOIP_COUNTRY_CODE OM BlockCountry
# SetEnvIf GEOIP_COUNTRY_CODE PK BlockCountry
# SetEnvIf GEOIP_COUNTRY_CODE RU BlockCountry
# SetEnvIf GEOIP_COUNTRY_CODE SA BlockCountry
# SetEnvIf GEOIP_COUNTRY_CODE SD BlockCountry
# SetEnvIf GEOIP_COUNTRY_CODE SN BlockCountry
# SetEnvIf GEOIP_COUNTRY_CODE SY BlockCountry
# SetEnvIf GEOIP_COUNTRY_CODE TJ BlockCountry
# SetEnvIf GEOIP_COUNTRY_CODE TM BlockCountry
# SetEnvIf GEOIP_COUNTRY_CODE TN BlockCountry
# SetEnvIf GEOIP_COUNTRY_CODE TW BlockCountry
# SetEnvIf GEOIP_COUNTRY_CODE UA BlockCountry
# SetEnvIf GEOIP_COUNTRY_CODE UZ BlockCountry
# SetEnvIf GEOIP_COUNTRY_CODE VN BlockCountry
</Ifmodule>

Run a

ln -s /usr/share/GeoIP /usr/local/share/

and then a

service apache2 restart

to restart apache.

Tight Fail2Ban configuration

Next we have to harden Fail2Ban so that brute-force attacks are limited to very low possibility. For this we have to modify the Fail2ban config file:

nano /etc/fail2ban/jail.local

Mine looks like this one:

[DEFAULT]
# "ignoreip" can be an IP address, a CIDR mask or a DNS host
ignoreip = 127.0.0.1/8
bantime  = 21000
maxretry = 3
destemail = your-email-address-here
action = %(action_mwl)s

[sshd]
enabled  = true
port     = ssh
filter   = sshd
logpath  = /var/log/auth.log
maxretry = 3
bantime  = 21000
findtime = 3600

[pureftpd]
enabled  = true
port     = ftp
filter   = pureftpd
logpath  = /var/log/syslog
maxretry = 3
bantime  = 21000
findtime = 3600

[dovecot-pop3imap]
enabled = true
filter = dovecot-pop3imap
action = iptables-multiport[name=dovecot-pop3imap, port="pop3,pop3s,imap,imaps", protocol=tcp]
logpath = /var/log/mail.log
maxretry = 5
bantime  = 21000
findtime = 3600

[postfix-sasl]
enabled  = true
port     = smtp,ssmtp
filter   = postfix-sasl
logpath  = /var/log/mail.log
maxretry = 3
bantime  = 21000
findtime = 3600

[wordpress-hard]
enabled = true
filter = wordpress-hard
logpath = /var/log/auth.log
maxretry = 3
port = http,https
bantime  = 600
findtime = 600

[framework-ddos]
enabled = true
port = 80,443
protocol = tcp
filter = framework-ddos
logpath = /var/log/apache2/other_vhosts_access.log
maxretry = 50
# findtime: 10 mins
findtime = 600
# bantime: 1 week
bantime  = 604800

The main values, or the important values here are:

  • maxretry (how often do you want to allow an IP to enter a wrong password)
  • bantime (for how long do you want to block the IP – in sec.)
  • findtime (In which timeframe – in sec.)

With values like:

maxretry = 5
bantime = 21000
findtime = 3600

You are actually saying: Allow 4 wrong password attempts (the fifth time the IP will be blocked) within 3600 seconds (which is an hour) and ban that IP for 21000 seconds.

Next you would have to configure the filter for wordpress-hard and framework-ddos with:

nano /etc/fail2ban/filter.d/wordpress-hard.conf

and put in there:

# Fail2Ban filter for WordPress hard failures
#

[INCLUDES]

before = common.conf

[Definition]

_daemon = (?:wordpress|wp)

failregex = ^%(__prefix_line)sAuthentication attempt for unknown user .* from $
            ^%(__prefix_line)sBlocked user enumeration attempt from $
            ^%(__prefix_line)sBlocked authentication attempt for .* from $
            ^%(__prefix_line)sPingback error .* generated from $
            ^%(__prefix_line)sSpam comment \d+ from $
            ^%(__prefix_line)sXML-RPC authentication attempt for unknown user .* from $
            ^%(__prefix_line)sXML-RPC multicall authentication failure from $

ignoreregex =

# DEV Notes:
# Requires the 'WP fail2ban' plugin:
# https://wordpress.org/plugins/wp-fail2ban/
#
# Author: Charles Lecklider

Next:

nano /etc/fail2ban/filter.d/framework-ddos.conf

and make sure it looks like this:

[Definition]
failregex = .*:(80|443)  .*(GET|POST) .*/xmlrpc.php
            .*:(80|443)  .*(GET|POST) .*/wp-login.php
            .*:(80|443)  .*(GET|POST) /administrator/index.php HTTP

The next thing you want to do is restart fail2ban with

service fail2ban restart

Thats it. Fail2ban is now in action and allows only very few login attempts per IP.

Configuring Postfix

The next thing we will take care of is Postfix. I am sure you want postfix to be setup to block as much spam as possible and therefore I will show you how you make a very tight postfix configuration. The first thing is to look into the postfix config file. Which is /etc/postfix/main.cf. Mine looks like the one below. I am sure it looks very different from yours, so instead of overwriting it, you should make a backup of the old one with:

mv /etc/postfix/main.cf /etc/postfix/main.cf-back

then you run a

nano /etc/postfix/main.cf

and paste the content from here into it. Please make sure you check the details in this file and you replace your.host-name.com with your real hostname.

# 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 $mail_name
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 = /usr/share/doc/postfix

# TLS parameters
smtpd_tls_cert_file = /etc/postfix/smtpd.cert
smtpd_tls_key_file = /etc/postfix/smtpd.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

# See /usr/share/doc/postfix/TLS_README.gz in the postfix-doc package for
# information on enabling SSL in the smtp client.

smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination
alias_maps = hash:/etc/aliases, hash:/var/lib/mailman/data/aliases
alias_database = hash:/etc/aliases, hash:/var/lib/mailman/data/aliases

myhostname = YOUR HOSTNAME
myorigin = /etc/mailname
mydestination = YOUR HOSTNAME, localhost, localhost.localdomain
mynetworks = 127.0.0.0/8 [::1]/128
inet_interfaces = all
inet_protocols = all
html_directory = /usr/share/doc/postfix/html

relayhost =
mailbox_size_limit = 0
recipient_delimiter = +

virtual_alias_domains =
virtual_alias_maps =
  hash:/var/lib/mailman/data/virtual-mailman,
  proxy:mysql:/etc/postfix/mysql-virtual_forwardings.cf,
  proxy:mysql:/etc/postfix/mysql-virtual_email2email.cf

virtual_mailbox_domains = proxy:mysql:/etc/postfix/mysql-virtual_domains.cf
virtual_mailbox_maps = proxy:mysql:/etc/postfix/mysql-virtual_mailboxes.cf
virtual_mailbox_base = /var/vmail
virtual_uid_maps = mysql:/etc/postfix/mysql-virtual_uids.cf
virtual_gid_maps = mysql:/etc/postfix/mysql-virtual_gids.cf

sender_bcc_maps = proxy:mysql:/etc/postfix/mysql-virtual_outgoing_bcc.cf
smtpd_sasl_auth_enable = yes
broken_sasl_auth_clients = yes
smtpd_sasl_authenticated_header = yes
smtpd_restriction_classes = greylisting
greylisting = check_policy_service inet:127.0.0.1:10023

smtpd_tls_security_level = may
transport_maps = hash:/var/lib/mailman/data/transport-mailman, proxy:mysql:/etc/postfix/mysql-virtual_transports.cf
relay_domains = mysql:/etc/postfix/mysql-virtual_relaydomains.cf
relay_recipient_maps = mysql:/etc/postfix/mysql-virtual_relayrecipientmaps.cf
smtpd_sender_login_maps = proxy:mysql:/etc/postfix/mysql-virtual_sender_login_maps.cf
proxy_read_maps = $local_recipient_maps $mydestination $virtual_alias_maps $virtual_alias_domains $sender_bcc_maps $virtual_mailbox_maps $virtual_mailbox_domains $relay_recipient_maps $relay_domains $canonical_maps $sender_canonical_maps $recipient_canonical_maps $relocated_maps $transport_maps $mynetworks $smtpd_sender_login_maps

smtpd_helo_required = yes
strict_rfc821_envelopes = yes
smtpd_client_message_rate_limit = 100

smtpd_helo_restrictions =
  permit_mynetworks,
  permit_sasl_authenticated,
  reject_invalid_helo_hostname,
  reject_non_fqdn_helo_hostname,
  reject_unknown_helo_hostname,
  check_helo_access regexp:/etc/postfix/helo_access,
  check_helo_access regexp:/etc/postfix/blacklist_helo

smtpd_sender_restrictions =
  permit_mynetworks,
  permit_sasl_authenticated,
  check_sender_access mysql:/etc/postfix/mysql-virtual_sender.cf,
  check_sender_access regexp:/etc/postfix/tag_as_foreign.re,
  check_sender_access regexp:/etc/postfix/tag_as_originating.re,
  reject_unlisted_sender,
  reject_non_fqdn_sender,
  reject_unknown_sender_domain,
  reject_sender_login_mismatch,
  reject_unauth_pipelining,
  reject_non_fqdn_sender

smtpd_client_restrictions =
  permit_mynetworks,
  permit_sasl_authenticated,
  check_client_access mysql:/etc/postfix/mysql-virtual_client.cf

smtpd_recipient_restrictions =
  permit_sasl_authenticated,
  permit_mynetworks,
  reject_unauth_destination,
  reject_invalid_hostname,
  reject_non_fqdn_hostname,
  reject_non_fqdn_sender,
  reject_non_fqdn_recipient,
  reject_unknown_sender_domain,
  reject_unknown_client_hostname,
  reject_unauth_pipelining,
  reject_unknown_recipient_domain,
  check_policy_service unix:private/policy-spf,
  check_recipient_access mysql:/etc/postfix/mysql-virtual_recipient.cf,
  reject_rbl_client zen.spamhaus.org,
  check_recipient_access mysql:/etc/postfix/mysql-virtual_policy_greylist.cf

smtpd_data_restrictions = reject_unauth_pipelining
smtpd_delay_reject = yes
maildrop_destination_concurrency_limit = 1
maildrop_destination_recipient_limit = 1
virtual_transport = dovecot
header_checks = regexp:/etc/postfix/header_checks
mime_header_checks = regexp:/etc/postfix/mime_header_checks
nested_header_checks = regexp:/etc/postfix/nested_header_checks
body_checks = regexp:/etc/postfix/body_checks
owner_request_special = no
smtp_tls_security_level = may
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3
smtpd_tls_protocols = !SSLv2,!SSLv3
smtp_tls_protocols = !SSLv2,!SSLv3
smtpd_tls_exclude_ciphers = RC4, aNULL
smtp_tls_exclude_ciphers = RC4, aNULL
dovecot_destination_recipient_limit = 1
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
content_filter = amavis:[127.0.0.1]:10024
receive_override_options = no_address_mappings
message_size_limit = 0
policy-spf_time_limit = 3600s
compatibility_level = 2

This configuration file does tell postfix to verify some important things, and when some mandatory things are not in place, then postfix should reject the email. Some very important parts are:

smtpd_helo_required = yes
strict_rfc821_envelopes = yes

smtpd_helo_restrictions =
[...]
  reject_invalid_helo_hostname,
  reject_non_fqdn_helo_hostname,
  reject_unknown_helo_hostname,

smtpd_sender_restrictions =
[...]
  reject_unlisted_sender,
  reject_non_fqdn_sender,
  reject_unknown_sender_domain,
  reject_sender_login_mismatch,
  reject_unauth_pipelining,
  reject_non_fqdn_sender

smtpd_recipient_restrictions =
[...]
  reject_unauth_destination,
  reject_invalid_hostname,
  reject_non_fqdn_hostname,
  reject_non_fqdn_sender,
  reject_non_fqdn_recipient,
  reject_unknown_sender_domain,
  reject_unknown_client_hostname,
  reject_unauth_pipelining,
  reject_unknown_recipient_domain,
  check_policy_service unix:private/policy-spf,
  check_recipient_access mysql:/etc/postfix/mysql-virtual_recipient.cf,
  reject_rbl_client zen.spamhaus.org,
  check_recipient_access mysql:/etc/postfix/mysql-virtual_policy_greylist.cf

In case you want to know the details of every single restriction, you can search for them at http://www.postfix.org/postconf.5.html it is very well documented and describes what each single one of them does.

Now we want to make a backup of the master.cf file in postfix with:

mv /etc/postfix/master.cf /etc/postfix/master.cf-back

And then we create a new one with:

nano /etc/postfix/master.cf

and you can past in there this content:

#
# 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_client_restrictions=permit_sasl_authenticated,reject
#  -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
smtps     inet  n       -       y       -       -       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 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 vmail ${extension} ${recipient} ${user} ${nexthop} ${sender}
#
# ====================================================================
#
# 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}

dovecot   unix  -       n       n       -       -       pipe
  flags=DRhu user=vmail:vmail argv=/usr/lib/dovecot/deliver -f ${sender} -d ${user}@${nexthop}

amavis unix - - - - 2 smtp
        -o smtp_data_done_timeout=1200
        -o smtp_send_xforward_command=yes


127.0.0.1:10025 inet n - n - - smtpd
        -o content_filter=
        -o local_recipient_maps=
        -o relay_recipient_maps=
        -o smtpd_restriction_classes=
        -o smtpd_client_restrictions=
        -o smtpd_helo_restrictions=
        -o smtpd_sender_restrictions=
        -o smtpd_recipient_restrictions=permit_mynetworks,reject
        -o mynetworks=127.0.0.0/8
        -o strict_rfc821_envelopes=yes
        -o receive_override_options=no_unknown_recipient_checks,no_header_body_checks
        -o smtp_send_xforward_command=yes
        -o disable_dns_lookups=no


127.0.0.1:10027 inet n - n - - smtpd
        -o content_filter=
        -o local_recipient_maps=
        -o relay_recipient_maps=
        -o smtpd_restriction_classes=
        -o smtpd_client_restrictions=
        -o smtpd_helo_restrictions=
        -o smtpd_sender_restrictions=
        -o smtpd_recipient_restrictions=permit_mynetworks,reject
        -o mynetworks=127.0.0.0/8
        -o strict_rfc821_envelopes=yes
        -o receive_override_options=no_unknown_recipient_checks,no_header_body_checks
        -o smtp_send_xforward_command=yes
      	-o milter_default_action=accept
        -o milter_macro_daemon_name=ORIGINATING
        -o disable_dns_lookups=no

policy-spf  unix  -       n       n       -       -       spawn
     user=nobody argv=/usr/bin/policyd-spf

The next thing you want to do, is to configure how strict you want to be dealing with SPF. SPF (Sender Policy Framework) is a framework which protects your domain against being abused, but also protects you against getting spam emails quite good. You should have a look at this WikiPedia Article https://en.wikipedia.org/wiki/Sender_Policy_Framework about SPF. The world would have very less spam when every admin would take care of this and implement it properly. With SPF you declare with a DNS TXT record, which IP’s, Servers, or domains are supposed to send emails from your domain.

Now we need to install postfix-policyd-spf-python with

apt install postfix-policyd-spf-python

With the postfix settings above, Postfix is being told to do a SPF check and accept emails, based on your SPF configuration. You find the configuration file with:

nano /etc/postfix-policyd-spf-python/policyd-spf.conf

Make sure it looks like:

# For a fully commented sample config file see policyd-spf.conf.commented

debugLevel = 1
defaultSeedOnly = 1

HELO_reject = SPF_Not_Pass
Mail_From_reject = Softfail

PermError_reject = False
TempError_Defer = False

skip_addresses = 127.0.0.0/8,::ffff:127.0.0.0/104,::1

Restart Postfix with:

service postfix restart

The setting: Mail_From_reject = Softfail will make sure, that a softfail will already force postfix to deny the email.

A softfail is actually the case when there is an issue in your SPF DNS record. For instance, when you made a typo while typing in your servers IP. Most big mail servers like GMail, Hotmail, Live, Yahoo, etc, tolerate a Softfail. With setting “Mail_From_reject = Softfail” to “Mail_From_reject = Fail” When this is set to fail, then it is possible, that a poorly maintained mail server will pass over spam to you from a domain the server is not allowed to handle. Why would one want that. So that is why I have set my mail server to not tolerate a softfail.

Install & Configure Roundcube Webmailer

Install Roundcube with:

apt-get install roundcube roundcube-core roundcube-mysql roundcube-plugins roundcube-plugins-extra javascript-common libjs-jquery-mousewheel php-net-sieve tinymce

Select YES when being asked if Roundcube should be installed with dbconf-common. And just hit enter, when you being asked to create a MySQL password for roundcube.

Now we want to make sure you can access roundcube from any domain / subdomain hostet on your server from /webmail, just open this file:

nano /etc/apache2/conf-enabled/roundcube.conf

and make sure it looks like:

# Those aliases do not work properly with several hosts on your apache server
# Uncomment them to use it or adapt them to your configuration
    Alias /roundcube /var/lib/roundcube
    Alias /webmail /var/lib/roundcube

<Directory /var/lib/roundcube/>
  AddType application/x-httpd-php .php
  Options +FollowSymLinks
  # This is needed to parse /var/lib/roundcube/.htaccess. See its
  # content before setting AllowOverride to None.
  AllowOverride All
  <IfVersion >= 2.3>
    Require all granted
  </IfVersion> 
  <IfVersion < 2.3>
    Order allow,deny
    Allow from all
  </IfVersion>
</Directory>

# Protecting basic directories:
<Directory /var/lib/roundcube/config>
        Options -FollowSymLinks
        AllowOverride None
</Directory>

<Directory /var/lib/roundcube/temp>
        Options -FollowSymLinks
        AllowOverride None
        <IfVersion >= 2.3>
          Require all denied
        </IfVersion> 
        <IfVersion < 2.3>
          Order allow,deny
          Deny from all
        </IfVersion>
</Directory>

<Directory /var/lib/roundcube/logs>
        Options -FollowSymLinks
        AllowOverride None
        <IfVersion >= 2.3>
          Require all denied
        </IfVersion> 
        <IfVersion < 2.3>
          Order allow,deny
          Deny from all
        </IfVersion>
</Directory>

Next we need to edit the Roundcube Configuration file and add there one single value.

nano /etc/roundcube/config.inc.php

and change

$config['default_host'] = '';

to

$config['default_host'] = 'localhost';

To make sure all changes are loaded in apache, run a:

service apache2 restart

and Roundcube should bow be available via your-domain.com/webmail.

Change phpMyAdmin URL to prevent Bruteforce attacks

Unfortunately, phpMyAdmin is logging about nothing. No where. So in this case we cannot use Fail2Ban to protect phpMyAdmin. But what we can do, is change the default URL to something unique. You can even use a password generator and make it available only via /your-password. To do that, we need to edit one line:

nano /etc/phpmyadmin/apache.conf

Change the value after Alias to something really special, save the file, and restart apache with:

service apache2 restart

Let’s Encrypt SSL certificate for ISPConfig Interface, Postfix & PureFTPD

To establis that, we first want to create the hostname of our server, as a website in ISPConfig. So log in to your ISPCOnfig interface, click on sites, add new website, enter your servers hostname in domain, disable auto-subdomain, enable Lets Encrypt SSL and save it. Wait for changes to apply.

Meanwhile we can adjust some settings in ISPConfig. First head over to System Settings. Then click on Server IP Addresses and remove the first one. That is the internal IP of your droplet on Digital Ocean. In case you have enabled IPv6, enter your droplets IPv6 there. Next you click on Main Config, and change the values for your phpMyAdmin URL and for your Roundcube Webmail URL

Now we make a quick check if the SSL certificate has been applied successfully. Just run a:

ls -lah /etc/letsencrypt/live/

and you should see the hostname of your server there. Nice. First we will now create backups for the Certificate that has been created by the ISPConfig installer with:

mv /usr/local/ispconfig/interface/ssl/ispserver.bundle /usr/local/ispconfig/interface/ssl/ispserver.bundle-back
mv /usr/local/ispconfig/interface/ssl/ispserver.crt /usr/local/ispconfig/interface/ssl/ispserver.crt-back
mv /usr/local/ispconfig/interface/ssl/ispserver.key /usr/local/ispconfig/interface/ssl/ispserver.key-back

Then we will link the certificate files to the ISPConfig Interface SSL directory with:

ln -s /etc/letsencrypt/live/your-server-hostname/chain.pem /usr/local/ispconfig/interface/ssl/ispserver.bundle
ln -s /etc/letsencrypt/live/your-server-hostname/cert.pem /usr/local/ispconfig/interface/ssl/ispserver.crt
ln -s /etc/letsencrypt/live/your-server-hostname/privkey.pem /usr/local/ispconfig/interface/ssl/ispserver.key

And restart apache with:

service apache2 restart

Then we need to make backups of the certificate files that were created for Postfix with:

mv /etc/postfix/smtpd.cert /etc/postfix/smtpd.cert-bak
mv /etc/postfix/smtpd.key /etc/postfix/smtpd.key-bak

And we will link those as well with:

ln -s /etc/letsencrypt/live/your-server-hostname/privkey.pem /etc/postfix/smtpd.key
ln -s /etc/letsencrypt/live/your-server-hostname/fullchain.pem /etc/postfix/smtpd.cert

Restart Postfix and Dovecot with:

service postfix reload
service dovecot reload

and last but not least, we want to create a valid SSL certificate for PureFTPD. And here as well, we first make a backup with:

mv /etc/ssl/private/pure-ftpd.pem /etc/ssl/private/pure-ftpd.pem-back

and then we put together the certificate for PureFTPD with:

cat /etc/letsencrypt/live/host.ispconfig-server.space/privkey.pem /etc/letsencrypt/live/host.ispconfig-server.space/fullchain.pem > /etc/ssl/private/pure-ftpd.pem

And you can restart PureFTPD with a:

service pure-ftpd-mysql restart

And because you would need to do that every time your certificate was renewed by Let’s Encrypt, we will create a cronjob which runs once a day with:

crontab -e

and add the following line at the end of the file:

@daily cat /etc/letsencrypt/live/host.ispconfig-server.space/privkey.pem /etc/letsencrypt/live/host.ispconfig-server.space/fullchain.pem > /etc/ssl/private/pure-ftpd.pem && service pure-ftpd-mysql restart > /dev/null 2>&1

Create a DKIM Key using ISPConfig & the correct DNS record in Digital Ocean

DKIM technology allows you sign your Emails with a private key, and allows other Mail Servers to check that one with your public key, which is published with public DNS TXT record.

So in ISPConfig, click on E-Mail, and create a E-Mail Domain. While creating it, enable DKIM and also click on Generate Private Key. In the lower field, you will find something like:

default._domainkey.your-domain.com. 3600 TXT v=DKIM1; t=s; p=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/vqtVW33tSfdc1Tfv47Fu7sogiKLZ1o89gxDMgRTqTkJLk0i8QaGSDP4z9c+2VRP1SuE+KTa+qQyse315gFCWK0EuWYtstbR2WxnI444VtP9F909fOgGX2WAFHFC78VouTgiztHxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxB

So what you want to copy first, is:

default._domainkey

Head over to Digital Ocean => Network => Domains => Your Domain, and click on TXT on top. Paste that content in the Hostname field. Then go back to ISPConfig, and copy this part of the lower field:

v=DKIM1; t=s; p=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/vqtVW33tSfdc1Tfv47Fu7sogiKLZ1o89gxDMgRTqTkJLk0i8QaGSDP4z9c+2VRP1SuE+KTa+qQyse315gFCWK0EuWYtstbR2WxnI444VtP9F909fOgGX2WAFHFC78VouTgiztHxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxB

Head over to Digital Ocean, and paste it in the Text content field. Click on save, go to your ISPConfig Interface, click on save there as well.

You can now go ahead and run some checks with Thunderbird, or your Mail Client, but I will not dig into this in this post and will continue with:

Create your Firewall Rules in Digital Ocean

Just a few weeks ago Digital Ocean has implemented a Firewall which is free to use for every droplet you have created there. Since then, I am definitely getting less E-Mails about banned IP addresses, since I only allow my IP to login via SSH on my machines.

So in Digital Ocean you click on Network => Firewall and create a new one. You will only need to have this ports opened in “Inbound” rules for the setup described above. Means, incoming traffic:

20
21
22
25
80
110
143
443
465
587
993
995
3306
8080
8081
10000
29799-29899

So you can just add this Ports in the firewall, regarding Port 22 (SSH), remove all IPv4, check your public IP and enter you public IP in there, in order to have a similar setup to mine. In case your public IP became a different one, you first need to login to Digital Ocean and change it in the firewall to be able to login to your machine via SSH.

Configure PureFTPD Passive Port Range

One last thing we need to do now, is to tell PureFTPD on which ports it should allow inbound and outbound data traffic. In my Setup I have opened port 29799-29899 for FTP data transfer. So we just need to create the file:

nano /etc/pure-ftpd/conf/PassivePortRange

and enter there this line:

29799 29899

save the file and restart PureFTPD with:

service pure-ftpd-mysql restart

and now head over to Digital Ocean and apply the firewall and assign it to your Droplet. That’s it.

Configuring a site with Mod Pagespeed and Chaching

I have just recently blogged about Mod Pagespeed and Caching. You will find a working configuration file in my post: Get a great Website Performance with Mod_Pagespeed & Caching

36 Replies to “Setup a Digital Ocean Server with ISPConfig on Ubuntu 16.04”

  • hi there, thanks for this. Im in the process of going through it. But after following this direction:

    “The phpmyadmin package must have a database installed and configured before it can be used. This can be optionally handled with dbconfig-common……”

    and hitting “no”…i do not get asked all the questions in the next section. Is there something missing?

    • more info…im at the point in the guide where i need to configure Encrypt SSL by adding a hostname of the server as a website. But i think because of the lack of the questions in the above post, i do not have a login/pass to log into ispconfig. Any ideas?

      • Nevermind got it working. Was my fault for not selecting “expert”. Also, didnt change my hostname in a few places after that step.

        Everything is working now, thank you!

      • The ISPConfig username is admin by default and you should be able to specify the password during the installation of ISPConfig on your server.

    • In case you don’t get the phpmyadmin questions, you can simply run a

      dpkg-reconfigure phpmyadmin

      and it should start over again.

  • Hi Dimitri,

    Thank you for this great tutorial.

    Btw, I failed to send email. I was getting message that this file smtpd.cert doesn’t exists after I ran the following command:
    mv /etc/postfix/smtpd.cert /etc/postfix/smtpd.cert-bak

    Do you know the problem?

    • Hey Ari.
      Sorry for the late response.
      In this case, you might want to run the ISPConfig Installer first. As the certificate is not yet created and therefore can’t be moved. When you run the ISPConfig Installer, ISPConfig will create the self signed certificate.

  • There are another problem,

    I get this error message:
    `Job for fail2ban.service failed because the control process exited with error code. See “systemctl status fail2ban.service” and “journalctl -xe” for details.`

    after running this command: service fail2ban restart

  • hello after running this command and come out work good, no error at all when i reboot my server it don’t work at all why ???

      • i’m talking about how to setting up ispconfig ok when i done it work good fine ok so i reboot my vps server and is gone don’t work

        • So you mean that you install ISPConfig on your VPS and after a reboot everything is gone?
          How is it with other data? Apache? php? Does it remain on the VPS?
          That’s really strange man. That should not happen.
          There is maybe something wrong with your VPS.

  • Hi Dimitri Enns
    Really wonderful article over here . can you help me with these issue .
    Got these error while do these part
    ” Let’s Encrypt SSL certificate for ISPConfig Interface, Postfix & PureFTPD ”
    I moved and linked the letsencript files but after ” service apache2 restart ” got the below error

    Job for apache2.service failed because the control process exited with error code. See “systemctl status apache2.service” and “journalctl -xe” for details.

    • After Running the top command to check

      got these in log

      apache2.service – LSB: Apache2 web server
      Loaded: loaded (/etc/init.d/apache2; bad; vendor preset: enabled)
      Drop-In: /lib/systemd/system/apache2.service.d
      └─apache2-systemd.conf
      Active: failed (Result: exit-code) since Sat 2018-01-20 05:33:30 UTC; 2min 17s ago
      Docs: man:systemd-sysv-generator(8)
      Process: 28597 ExecStop=/etc/init.d/apache2 stop (code=exited, status=0/SUCCESS)
      Process: 28810 ExecStart=/etc/init.d/apache2 start (code=exited, status=1/FAILURE)

      Jan 20 05:33:30 mediacapsule-london apache2[28810]: Output of config test was:
      Jan 20 05:33:30 mediacapsule-london apache2[28810]: AH00548: NameVirtualHost has no effect and wil
      Jan 20 05:33:30 mediacapsule-london apache2[28810]: AH00526: Syntax error on line 63 of /etc/apach
      Jan 20 05:33:30 mediacapsule-london apache2[28810]: SSLCertificateFile: file ‘/usr/local/ispconfig
      Jan 20 05:33:30 mediacapsule-london apache2[28810]: Action ‘configtest’ failed.
      Jan 20 05:33:30 mediacapsule-london apache2[28810]: The Apache error log may have more information
      Jan 20 05:33:30 mediacapsule-london systemd[1]: apache2.service: Control process exited, code=exit
      Jan 20 05:33:30 mediacapsule-london systemd[1]: Failed to start LSB: Apache2 web server.
      Jan 20 05:33:30 mediacapsule-london systemd[1]: apache2.service: Unit entered failed state.
      Jan 20 05:33:30 mediacapsule-london systemd[1]: apache2.service: Failed with result ‘exit-code’.

  • apache2.service – LSB: Apache2 web server
    Loaded: loaded (/etc/init.d/apache2; bad; vendor preset: enabled)
    Drop-In: /lib/systemd/system/apache2.service.d
    └─apache2-systemd.conf
    Active: failed (Result: exit-code) since Sat 2018-01-20 05:33:30 UTC; 2min 17s ago
    Docs: man:systemd-sysv-generator(8)
    Process: 28597 ExecStop=/etc/init.d/apache2 stop (code=exited, status=0/SUCCESS)
    Process: 28810 ExecStart=/etc/init.d/apache2 start (code=exited, status=1/FAILURE)

    Jan 20 05:33:30 mediacapsule-london apache2[28810]: Output of config test was:
    Jan 20 05:33:30 mediacapsule-london apache2[28810]: AH00548: NameVirtualHost has no effect and wil
    Jan 20 05:33:30 mediacapsule-london apache2[28810]: AH00526: Syntax error on line 63 of /etc/apach
    Jan 20 05:33:30 mediacapsule-london apache2[28810]: SSLCertificateFile: file ‘/usr/local/ispconfig
    Jan 20 05:33:30 mediacapsule-london apache2[28810]: Action ‘configtest’ failed.
    Jan 20 05:33:30 mediacapsule-london apache2[28810]: The Apache error log may have more information
    Jan 20 05:33:30 mediacapsule-london systemd[1]: apache2.service: Control process exited, code=exit
    Jan 20 05:33:30 mediacapsule-london systemd[1]: Failed to start LSB: Apache2 web server.
    Jan 20 05:33:30 mediacapsule-london systemd[1]: apache2.service: Unit entered failed state.
    Jan 20 05:33:30 mediacapsule-london systemd[1]: apache2.service: Failed with result ‘exit-code’.

  • After running these command fould out the issue but dont know how to fix it apache2ctl configtest
    AH00548: NameVirtualHost has no effect and will be removed in the next release /etc/apache2/sites-enabled/000-ispconfig.conf:73
    AH00526: Syntax error on line 63 of /etc/apache2/sites-enabled/000-ispconfig.vhost:
    SSLCertificateFile: file ‘/usr/local/ispconfig/interface/ssl/ispserver.crt’ does not exist or is empty
    Action ‘configtest’ failed.

    • HI These has been fixed it was my mistake i didnt replace ” your.host-name.com ” to mine .. kindly remove those comments . Thanks

  • TL;DR = I do not have an ispserver.bundle file to backup.

    Hi, really wanted to thank you for this. It is not your “average run-of-the-mill” tutorial.
    That being said; I’m at this step:

    mv /usr/local/ispconfig/interface/ssl/ispserver.bundle /usr/local/ispconfig/interface/ssl/ispserver.bundle-back
    mv /usr/local/ispconfig/interface/ssl/ispserver.crt /usr/local/ispconfig/interface/ssl/ispserver.crt-back
    mv /usr/local/ispconfig/interface/ssl/ispserver.key /usr/local/ispconfig/interface/ssl/ispserver.key-back

    I do not have a ispserver.bundle

    Contents of that folder:
    ls -la
    total 28
    drwxr-x— 2 root root 4096 Feb 1 11:53 .
    drwxr-x— 9 ispconfig ispconfig 4096 Feb 1 11:53 ..
    -rwxr-x— 1 root root 45 Feb 1 11:53 empty.dir
    -rwxr-x— 1 root root 1919 Feb 1 11:53 ispserver.crt
    -rwxr-x— 1 root root 1651 Feb 1 11:53 ispserver.csr
    -rwxr-x— 1 root root 3243 Feb 1 11:53 ispserver.key
    -rwxr-x— 1 root root 3311 Feb 1 11:53 ispserver.key.secure

    I’m going to move forward now skipping just the “.bundle” file in hopes that is not needed, after all if something breaks I can simply rebuild or deploy a new droplet but I wanted to know if you had ever seen this issue before or whether or not it is actually an issue. Seems pretty isolated as there are no comments here about it and googling gives lacking results. I’m human, I could have missed a step or made a mistake but I redeployed and started from scratch twice (no better way to learn than to rinse and repeat) now with the same result.

    Thank you again for this, it is obvious that a lot of time and effort went into this and I really appreciate it!

  • hello.. thanks for such a wonderful tutorial..
    i have an issue i have been trying to fix.. i followed this tutorial to the last letter with out encountering any error, but my roundcube mail is sending mails but cannot receive mails. i actually dont know what to do about it.. any help will be very much appropriated

    • Hey Osas,
      you may want to check your DNS MX entry. Additionally you should have a look at the mail.log file to see what happens when a mail comes in and what the mail server is doing with it.

  • Hi,

    I did not succeed in sending email.
    How much time and at what rate would you document the procedure and implement it on my Digital Ocean Droplet ?

    • Hi Anika,
      since I work as for a Hosting Company, I cannot offer any commercial support for this one. But you should have a look at the logfiles. You will find them in /var/log/.
      Usually you find the reason for things not working right in there.

  • Hello. I used this tutorial to set up my server on digital ocean. First time around i did something wrong and it didn’t work. I destroyed the droplet and started over. Unfortunately now I cannot go past ssl steps. Do you know any solution for this?

    Thank you in advance!

  • Hello there. Thanks for all of this. I know this might been awhile but I wanted to ask you something about mail receiving. It always show ***UNCHECKED*** same line with subject. Was there something that I did wrong ? There’s no error or else. Your response would be highly appreciated.

    • I think this is being added to the Subject in case clamav was not able to scan the E-Mail. Either because the service is not available or overwhelmed. You might want to look in to your logs and see whether the service is running well or maybe not able to start up at all or even postfix is not able to communicate with clamav.

Leave a Reply

Your email address will not be published. Required fields are marked *

*