Fail2ban: Setup, Configure, Customize, Test

Hello world! There are a number of tools that I find absolutely indispensable when working on system administration tasks. Some of these tools assist with securing/hardening web servers to prevent DDoS or other types of hacks. I’m going to be delving deeper into Fail2ban and how it can be setup and configured to help with just about any type of issue you and your server may be facing.

Firstly, I’m a huge proponent of Ubuntu and their server operating systems, as you’ve probably seen from my other blogs, so I’ll be assuming you’re operating on the same. Those of you operating on different versions of Linux, you’ll likely recognize when to use another command. To those of you operating on Windows servers, you have my condolences (just kidding ;)).

The Basics

This part of the process is simple enough. Update and upgrade your installation of Ubuntu, for sanity’s sake. Install Fail2ban.

sudo apt update && sudo apt upgrade -y
sudo apt install fail2ban

Configuring Fail2ban

We’ll be operating in the /etc/fail2ban/ folder for the most part. Though, later on, we’ll be referring to log files to give Fail2ban more information.

If you open the jail.conf file, you’ll notice pretty early on that they recommend you not touch that file. It’ll be overwritten quite commonly, so you should enter all of your configurations in a new jail.local file.

sudo vim jail.local

Enter the following informaton into this file to override defaults set in the jail.conf file.

ignoreip = ::1
bantime = 3600
findtime = 600
maxretry = 5
enabled = true

This will set the default ban time to 3600 seconds (how long they’re locked out), the find time to 600 seconds (how long of a period to search for instances to ban), and maxretry to 5 (the number of times before triggering the rule. You may choose to increase or decrease these rules based on your own preferences. Be careful when using -1 bantime, as it is a permanent ban which can lock you out of your server. If you’re a responsible adult and are using ssh keys to log in, you should be fine. Always have a backup entryway in case of failures.

Setting [sshd] enabled = true enables the basic sshd rule to ban anyone failing to access ssh five times within 600 seconds. This will ban the offending individual for 3600 seconds. This is often more than enough time to prevent your server from being overloaded with false requests.

Creating Custom Rules

As you may have noticed from my other blogs, I’m often running open-source web applications on my servers. This means that I’m often open to common attacks and DDoS techniques used by script kiddies across the globe.

Looking through your web-server logs, you may notice many different types of attacks. You can create rules for these attacks using regular expressions in combination with Fail2ban rules.

I’ve previously written about XML-RPC attacks against WordPress and how to prevent them, but I’ll be outlining another option here using fail2ban, which may actually be a more elegant solution, though potentially not as secure.

Firstly, new rules should be placed in the /etc/fail2ban/filter.d/ folder, which you’ll notice has a number of already configured rules you may want to enable in your /etc/fail2ban/jail.local file.

Create a new file called wordpress.conf in the filter.d folder:

vim /etc/fail2ban/filter.d/wordpress.conf

I run Apache2 as my web-server of choice, so my log files are located in /var/log/apache2/ and specifically the newest access logs would be in access.log, though you can see an archive of older logs in the numbered files. Two specific lines that cause me grief are the following:

XX.XXX.XXX.XXX - - [13/Jun/2019:16:07:58 -0400] "POST /wp-login.php HTTP/1.1" 200 5382 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0"
XX.XXX.XXX.XXX - - [13/Jun/2019:16:07:58 -0400] "POST /xmlrpc.php HTTP/1.1" 200 3588 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0"

The first bit of the line item is the IP address (redacted), then we have a couple dashes and the date. Following, we have the type of request, the URI, the type, and the HTTP code type response. In the case 200 – OK. That’s all we really need to create our regular expressions, as this is all the common information we need. You’ll see numerous requests like this one coming in on a daily basis.

Add the following configuration to the newly created wordpress.conf file:

failregex = <HOST>.*POST.*(wp-login\.php|xmlrpc\.php).* 200

As you can see, we specify where we would find the host in the regular expression. Then we specify that it is followed by any characters leading up to POST. Then we are looking for any characters leading up to wp-login.php or xmlrpc.php. Following that, any characters leading up to the 200 code.

This regex calls out two different attacks. Firstly, POST calls to wp-login.php with a 200 error are failures, as a successful login will return a redirect. Secondly, and similarly, POST calls to xmlrpc.php are treated the same way.

Once we’ve saved this configuration file, we’ll need to enable it and set some configuration parameters in our jail.local file. This is what our file should look like after the addition:

ignoreip = ::1
bantime = 3600
findtime = 600
maxretry = 5
enabled = true
enabled = true
port = http,https
filter = wordpress
logpath = /var/log/apache2/access.log
maxretry = 5
bantime = 3600

As you can see, we specify that our requests may be coming over the http or https ports. They should be filtered based on the wordpress.conf filter (excluding the .conf). The rule should find and filter bad actors based on our log file which would be /var/log/apache2/access.log or /var/log/nginx/access.log/ or something similar based on your own configurations.

Testing Your New Configuration

Fail2ban gives us a number of commands that we can use to test our setup.

You can check if your server is running with: sudo fail2ban-client status
You can reload your fail2ban configuration with: sudo fail2ban-client reload
You can even see all commands just by running: sudo fail2ban-client

However, we’re looking to see how effective our fail2ban configuration is at spotting issues in our existing logs. We’ll run fail2ban-regex specifying our access.log input file, and our filter.d/wordpress.conf custom configuration.

sudo fail2ban-regex /var/log/apache2/access.log /etc/fail2ban/filter.d/wordpress.conf

For me, it takes a moment and then spits out the following:

Running tests

Use   failregex filter file : wordpress, basedir: /etc/fail2ban
Use         log file : /var/log/apache2/access.log
Use         encoding : UTF-8


Failregex: 4584 total
|-  #) [# of hits] regular expression
|   1) [4584] <HOST>.*POST.*(wp-login\.php|xmlrpc\.php).* 200

Ignoreregex: 0 total

Date template hits:
|- [# of hits] date format
|  [13957] Day(?P<_sep>[-/])MON(?P=_sep)Year[ :]?24hour:Minute:Second(?:\.Microseconds)?(?: Zone offset)?

Lines: 13957 lines, 0 ignored, 4584 matched, 9373 missed [processed in 4.45 sec]
Missed line(s): too many to print.  Use --print-all-missed to print all 9373 lines

As you can see, we successfully run our configuration failregex against the log retrieving 4584 offending lines. It then lets me know that using the “Date template hint” I could have found 13957 lines. Since this is a log file where every single line includes the date, you can see that it coincides with the number of lines at the bottom, and is effectively useless data. At the very bottom, we see that out of 13957 lines 0 were ignored and 4584 were matched. We don’t need to print or process the other lines, as they should have been legal access to our web-server.

At this point, we can look through additional lines in our access files for patterns in access, and see what we can limit through Fail2ban custom rules. You could even spend some time looking through the additional premade rules to see if any apply to you and enable them in your base configuration file – after all, script kiddies use some pretty common attack vectors.

Happy banning!

Oops! Password Reset Ubuntu: Windows Subsystem for Linux (WSL)

I typically like to write a blog whenever I encounter a problem that I have to research a solution. This particular issue was forgetting the password to my administrative account in my WSL Ubuntu installation. If you ever get into this same predicament, there are a couple really simple steps to get you back up and running.

Firstly, start by opening your WSL Ubuntu installation and figuring out what your user is. If you kept it the default ubuntu feel free to skip this part.

Next, you’ll want to log out (important) using exit.

Once you’re out, you can change the default user with which your computer is logging into your WSL environment.

ubuntu config --default-user root

Now that you’ve elevated your permissions, you can go ahead and run passwd ubuntu to change your password. exit a few times to leave the WSL once complete.

To get back to logging in as ubuntu or your default user, run ubuntu config --default-user ubuntu and you’ll be good to go!

All commands in sequence (from Windows command prompt) skipping input and output:

whoami [assuming it's ubuntu]
ubuntu config --default-user root
passwd ubuntu [enter password in prompts when asked]
ubuntu config --default-user ubuntu

Hope this helps someone out there!

Updating Or Upgrading When /boot Is 100% Full

I recently ran into an issue on a test server with a particularly small /boot partition. I attempted to run the typical sudo apt update && sudo apt upgrade and it failed. This was part of the error:

Preparing to unpack .../linux-image-4.4.0-112-generic_4.4.0-112.135_amd64.deb ...
Unpacking linux-image-4.4.0-112-generic (4.4.0-112.135) ...
dpkg: error processing archive /var/cache/apt/archives/linux-image-4.4.0-112-generic_4.4.0-112.135_amd64.deb (--unp ack):
 cannot copy extracted data for './boot/vmlinuz-4.4.0-112-generic' to '/boot/vmlinuz-4.4.0-112-generic.dpkg-new': failed to write (No space left on device)
No apport report written because the error message indicates a disk full error

I tried running sudo apt autoremove as suggested to clear out the old Linux files. This ran into an error which looked like so:

cole@ubuntu-docker:~$ sudo apt autoremove
Reading package lists... Done
Building dependency tree
Reading state information... Done
You might want to run 'apt-get -f install' to correct these.
The following packages have unmet dependencies:
 libstdc++-5-dev : Depends: libstdc++6 (>= 5.4.0-6ubuntu1~16.04.6) but 5.4.0-6ubuntu1~16.04.5 is installed
 libstdc++6 : Depends: gcc-5-base (= 5.4.0-6ubuntu1~16.04.5) but 5.4.0-6ubuntu1~16.04.6 is installed
 linux-image-extra-4.4.0-112-generic : Depends: linux-image-4.4.0-112-generic but it is not installed
 linux-image-generic : Depends: linux-image-4.4.0-112-generic but it is not installed
 Recommends: thermald but it is not installed
E: Unmet dependencies. Try using -f.

Running sudo apt -f install as suggested left me with:

cole@ubuntu-docker:~$ sudo apt -f install
Reading package lists... Done
Building dependency tree
Reading state information... Done
Correcting dependencies... Done
The following packages were automatically installed and are no longer required:
 libzip4 linux-headers-4.4.0-101 linux-headers-4.4.0-101-generic linux-headers-4.4.0-87
 linux-headers-4.4.0-87-generic linux-headers-4.4.0-91 linux-headers-4.4.0-91-generic linux-headers-4.4.0-92
 linux-headers-4.4.0-92-generic linux-headers-4.4.0-93 linux-headers-4.4.0-93-generic linux-headers-4.4.0-96
 linux-headers-4.4.0-96-generic linux-headers-4.4.0-97 linux-headers-4.4.0-97-generic linux-headers-4.4.0-98
 linux-headers-4.4.0-98-generic linux-image-4.4.0-101-generic linux-image-4.4.0-87-generic
 linux-image-4.4.0-91-generic linux-image-4.4.0-92-generic linux-image-4.4.0-93-generic
 linux-image-4.4.0-96-generic linux-image-4.4.0-97-generic linux-image-4.4.0-98-generic
 linux-image-extra-4.4.0-101-generic linux-image-extra-4.4.0-87-generic linux-image-extra-4.4.0-91-generic
 linux-image-extra-4.4.0-92-generic linux-image-extra-4.4.0-93-generic linux-image-extra-4.4.0-96-generic
 linux-image-extra-4.4.0-97-generic linux-image-extra-4.4.0-98-generic
Use 'sudo apt autoremove' to remove them.
The following additional packages will be installed:
 libstdc++6 linux-image-4.4.0-112-generic
Suggested packages:
 fdutils linux-doc-4.4.0 | linux-source-4.4.0 linux-tools
The following NEW packages will be installed:
The following packages will be upgraded:
1 upgraded, 1 newly installed, 0 to remove and 61 not upgraded.
33 not fully installed or removed.
Need to get 0 B/22.3 MB of archives.
After this operation, 66.9 MB of additional disk space will be used.
Do you want to continue? [Y/n] y
Setting up gcc-5-base:amd64 (5.4.0-6ubuntu1~16.04.6) ...
(Reading database ... 444689 files and directories currently installed.)
Preparing to unpack .../libstdc++6_5.4.0-6ubuntu1~16.04.6_amd64.deb ...
Unpacking libstdc++6:amd64 (5.4.0-6ubuntu1~16.04.6) over (5.4.0-6ubuntu1~16.04.5) ...
Processing triggers for libc-bin (2.23-0ubuntu10) ...
Setting up libstdc++6:amd64 (5.4.0-6ubuntu1~16.04.6) ...
Processing triggers for libc-bin (2.23-0ubuntu10) ...
(Reading database ... 444689 files and directories currently installed.)
Preparing to unpack .../linux-image-4.4.0-112-generic_4.4.0-112.135_amd64.deb ...
Unpacking linux-image-4.4.0-112-generic (4.4.0-112.135) ...
dpkg: error processing archive /var/cache/apt/archives/linux-image-4.4.0-112-generic_4.4.0-112.135_amd64.deb (--unp ack):
 cannot copy extracted data for './boot/vmlinuz-4.4.0-112-generic' to '/boot/vmlinuz-4.4.0-112-generic.dpkg-new': f ailed to write (No space left on device)
No apport report written because the error message indicates a disk full error
 dpkg-deb: error: subprocess paste was killed by signal (Broken pipe)
Examining /etc/kernel/postrm.d .
run-parts: executing /etc/kernel/postrm.d/initramfs-tools 4.4.0-112-generic /boot/vmlinuz-4.4.0-112-generic
run-parts: executing /etc/kernel/postrm.d/zz-update-grub 4.4.0-112-generic /boot/vmlinuz-4.4.0-112-generic
Errors were encountered while processing:
E: Sub-process /usr/bin/dpkg returned an error code (1)

I’ve clearly run into a loop that I’ll not be able to solve without removing some files from the /boot partition. I ran a simple ls -la to find the versions, and removed the oldest files. I ran the following scripts:

sudo rm *4.4.0-91-generic
sudo rm *4.4.0-92-generic

until I only had the most recent 6 versions. I checked df to see where I was at with disk space, and lo-and-behold I had it down to ~50%. Finally I ran the commands that were originally suggested:

sudo apt -f install
sudo apt autoremove
sudo apt update && sudo apt upgrade

Now my installation is all up to date and ready to rock and roll!

The moral of the story is: make sure to keep your Ubuntu installations up to date! Automating this process is ideal!

🙂 Cole

Install Redis Server on Ubuntu 16.04

Why would I use redis?

Redis is wonderful for many reasons. I primarily use it for caching web applications like Magento 2 and WordPress. Some other great ways to use it are as a database or a message broker. You should check it out here.

How do I install it?

The official suggestion is to build the package, but I’d much rather install applications from an official package repository (PPA). This simplifies the install, upgrade, and uninstall methods greatly.

First, you’ll need to add the PPA repository to your OS.

sudo add-apt-repository ppa:chris-lea/redis-server

Next, you’ll want to update your repositories.

sudo apt update

After that, run the installation script.

sudo apt install redis-server

Check that everything installed correctly by checking the version, starting the server, and checking the status of your newly installed server.

redis-server --version
sudo service redis-server start
sudo service redis-server status

You can run a few commands to test out your new server by jumping into the redis-cli application that was installed with your server. If not, just install redis-cli and boot it up.

$ redis-cli> ping

When you get that PONG back, it’s quite satisfying. Let’s test saving and retrieving some values.> set bears-eat "beets"
OK> get bears-eat

Final Notes on Web Aplications

Magento 2

You can configure Redis with Magento 2 quite easily for some really great performance benefits. There are a lot of reasons to use Redis over a few other technologies for many reasons, which are outlined quite nicely over in the Magento DevDocs.


WordPress can easily cache with Redis with the installation of a single plugin. I love that it’s a single click away from being enabled or disabled. It’s stupid simple installation abstracts all the heavy lifting Redis does and hides the awesome power in the back-end. You’ll really love the performance boost though, and so will your visitors!


Installing Send Only Postfix To Ubuntu 16.04 LTS (Replace Sendmail)


There is nothing more upsetting than email. It’s an old technology, and no one should use it. You know it, I know it, everyone knows it.

It’s just too bad the entire world practically revolves around email. So I’m going to help you with an issue I ran into recently. Hopefully this saves some hair-pulling.

Today, I’ll be explaining how to install the Sendmail like application Postfix in conjunction with Mailutils to allow you to send email from your web application. This particular guide will also solve the issue of your web application server not sending to an external mail server on the same domain.

The example I’ll be using will relate to an Amazon Web Service Elastic Cloud Compute (AWS EC2) instance sending mail to GSuite. I know that there are other (likely better) ways of sending mail like AWS SES (Simple Email Service), GSuite SMTP-Relay, Mailgun, or Mandrill. However, we’re going to go purist on this first try and have the Application Server do the entire job.

Server Setup and Installing Mailutils & Postfix

I’ll be removing Sendmail as Postfix will be replacing it. You can run sudo apt purge sendmail if you would like to remove sendmail with it’s configuration. If you only want to remove sendmail but keep its configuration run: sudo apt remove sendmail.

Next, you’ll want to specify the server hostname and change your hosts file if you haven’t yet.

sudo vim /etc/hostname

Remove the current hostname and replace with your domain. (eg.

sudo vim /etc/hosts

Change the first line in your host file to reflect the following: localhost

Once that is complete, give your instance a restart to have it reflect the changes.

Next we’ll need to make sure we have all the utilities and packages we’ll be using installed. I’m told that installing Mailutils will also install Postfix and prompt the setup steps, but in my case, it did not.

sudo apt install mailutils
sudo apt install postfix

Either mailutils will prompt you to set up postfix (a pink background fullscreen prompt) or installing postfix itself will.

You’ll want to specify the the configuration type as Internet Site. You’ll then want to set the System mail name: to

The next step will be editing your Postfix main configuration file.

sudo vim /etc/postfix/

You’ll want to change these lines:

mydestination = $myhostname,, localhost.yourdomain.internal, localhost
inet_interfaces = all


mydestination = 
inet_interfaces = loopback-only

Once you’re done, save your file and restart Postfix.

sudo service postfix restart

Make sure your postfix is running without errors using sudo systemctl status postfix. If you run into any errors, give your server a reboot. This is an important note. I was running into issues with port 25 having already been bound.

You can now send a test email by running:

echo "This is some email body text" | mail -s "This is a test email subject"

Make sure to test it on your own domain, and you should be good to go!

Notes About Spam

You’ll want to make sure of a few things when sending email from multiple sources. One of the most important, and easiest spam-securing technologies available is the humble SPF record.

By adding a TXT record to your Domain Records, you can specify which servers are allowed to send mail, and what to do when something is not validated.

A simple example of this would be:

v=spf1 a mx ip4:999.88.777.66 -all

This SPF record effectively validates A records, MX records, includes, and IP addresses to be able to send mail as an authorized domain sender. If your web application sits at your A record, you’re done. If not, add it with an ip4: tag and call it a day!

Hope you enjoyed!

Ubuntu 16.04: Changing PHP Versions

I was making some changes to a web server recently that I figured I would make a super short blog tutorial about!

Changing From PHP 5.6-7.0

The same instructions will work for pretty much any version of PHP you would like to install or change from. Just change the version of PHP in the commands and you’re good to go.

Firstly, you’ll need to add the public PHP repository. Then you’ll need to update your packages. Then you need to install PHP. I’ve included both versions in case you’re switching one way or the other. It won’t matter if you have both installed, we’ll only be referencing one by the end.

sudo apt-get-repository ppa:ondrej/php
sudo apt-get update
sudo apt-get install php7.0 php5.6 php5.6-mysql php7.0-mysql php-gettext php5.6-mbstring php-mbstring php7.0-mbstring php-xdebug libapache2-mod-php5.6 libapache2-mod-php7.0

Once everything has run its course, you can start disabling and enabling which module is being used in Apache2.

sudo a2dismod php5.6; sudo a2enmod php7.0; sudo service apache2 restart

As we all know, there are two versions of PHP that run: Apache2 and CLI. To change the CLI version fo PHP, you’ll have to run the following command.

sudo update-alternatives --set php /usr/bin/php7.0

This means when you run php -v you’ll see PHP 7.0 as your PHP CLI version.

That’s it! Super simple stuff!

Let’s Encrypt The Internet! Installing Free TLS Certificates On Ubuntu 16.04

Why should we install security certificates?

I’m a big proponent of protecting data and encrypting communication on the internet regardless of the source or destination. There are just some things that shouldn’t be shared, and it’s the same reason why all envelopes aren’t made entirely of transparent plastic (recycling aside).

This doesn’t just protect your “data” but the transfer of secure information like your credit card numbers and passwords. You should always be cognoscente of the sites you’re sending your data to, and whether you would be okay sharing that information with a perfect stranger.

Note: most people still like to refer to these certificates as SSL certificates. This is referring to an old and obsolete protocol that used to be used for encryption. The new version, and widely accepted standard today is the TLS encryption protocol. At the time of writing this blog, I’ve successfully installed a TLS 1.2 certificate using Certbot with a strong key exchange (ECDHE_RS with P-256), and a strong cipher (AES_128_GCM) according to the security audit in Google Chrome. (Ctrl+Shift+I > Security tab).

Things you’ll need before moving forward.

I’m going to make the assumption that you’ve taken the time to set up your Ubuntu server and install Apache on it. Specifically that you’ve properly configured your (one or more) domains in separate Virtual host files that specify the ServerName.

Let’s get encrypting!

Firstly, you’ll want to add the Certbot repository to your list of software repositories. To do this, run the following command.

sudo add-apt-repository ppa:certbot/certbot

Once that’s finished running its course, you’ll want to pull the latest version of all of your software repositories.

sudo apt-get update

Finally, you can install the Certbot client using the following command.

sudo apt-get install python-certbot-apache

Ubuntu will then take a few moments to install the required software and the client on your system. You’re finally ready to install your TLS certificate on your server.

TLS how to install it! (hah, encryption pun)

This step is actually exceedingly easy. I was very pleased at how simple it was to install, and I think you’ll really enjoy it too.

To run the Certbot installation script and install your certificate into your Virtual Host in one shot, run the following command.

sudo certbot --apache -d

You can add more domains to this single certificate by specifying multiple domains. Just flag on another -d and you’re flying!

The installation script will walk you through a few questions regarding an administrative email address, accepting their terms of service, and whether or not all traffic will be going through HTTPS or not. It’ll look something like this:

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Enter email address (used for urgent renewal and security notices) (Enter 'c' to cancel):support@[redacted]

Please read the Terms of Service at You must agree in order to register with the ACME server at
(A)gree/(C)ancel: a

Would you be willing to share your email address with the Electronic Frontier Foundation, a founding partner of the Let's Encrypt project and the non-profit organization that develops Certbot? We'd like to send you email about EFF and our work to encrypt the web, protect its users and defend digital rights.
(Y)es/(N)o: y
Obtaining a new certificate
Performing the following challenges:
tls-sni-01 challenge for [redacted]
Enabled Apache socache_shmcb module
Enabled Apache ssl module
Waiting for verification...
Cleaning up challenges
Created an SSL vhost at /etc/apache2/sites-available/000-default-le-ssl.conf
Enabled Apache socache_shmcb module
Enabled Apache ssl module
Deploying Certificate for [redacted] to VirtualHost /etc/apache2/sites-available/000-default-le-ssl.conf
Enabling available site: /etc/apache2/sites-available/000-default-le-ssl.conf

Please choose whether HTTPS access is required or optional.
1: Easy - Allow both HTTP and HTTPS access to these sites
2: Secure - Make all requests redirect to secure HTTPS access
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 1

Congratulations! You have successfully enabled https://[redacted]

You should test your configuration at:[redacted]

 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/[redacted]/fullchain.pem. Your cert will
   expire on [redacted]. To obtain a new or tweaked version of this
   certificate in the future, simply run certbot again with the
   "certonly" option. To non-interactively renew *all* of your
   certificates, run "certbot renew"
 - Your account credentials have been saved in your Certbot
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Certbot so
   making regular backups of this folder is ideal.
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:
   Donating to EFF:          

They even offer up an easy way to renew your certificate when it eventually expires! Fantastic!

I have to renew this thing every few months?!

Yep! These free certificates will expire every 90 days. There are technical reasons why they would want you to renew your certificate, like renewing your encryption keys to reduce the risk of decryption. Other than that, it’s nice to automate auto renewal, so you never have to think about this again (almost).

We’ll be using our handy-dandy cron to manage our renewal.

sudo crontab -e

This will prompt you to select a text editor. Vim is my preferred, so I select 3. Add the following line at the bottom of your file to schedule your task to run every day at 4:15am.

15 4 * * * /usr/bin/certbot renew --quiet

:wq to write/quit in Vim and you’ll see a friendly little installation message.

You can opt to run the renewal script less frequently, but since it checks and only updates certificates set to expire in less than thirty days, you won’t be wasting very many precious CPU cycles.

Lastly, since we installed the certificate with the --apache flag, it’ll automatically reconfigure the Apache server every time it renews. This means less server maintenance and downtime!

Final Thoughts

Encryption used to be a very painful process, and even though cPanel and WHM made it easier than before, it was never a simple or pleasant process. I would spend at best 20 minutes to an hour waiting for email verifications, installations, and whatever else these Certificate Authorities required to get everything up and running. With Certbot and Let’s Encrypt, I was encrypting and serving an existing Apache site in less than five minutes.

Overall, I’d have to say I’m very happy with the process. I’m even happier about the cost, time, and effort savings. Now… if they’d only release a logo or badge of some sort for sites that want to promote the use of their services… How am I supposed to let other people know how awesome you are without a sticker on the back of my laptop and the bottom of my website!? 😉

– Cole

Prevent XML-RPC Brute Force Attacks – WordPress on Ubuntu 16.04


In my foray into cloud hosting, I’ve noticed that a few of our servers started to peak in their CPU usage more than what normal web traffic would cause(2-5% and 10-30% on our t2.medium and t2.small AWS EC2 servers respectively). After looking at the Apache2 logs, I found that there was a significant number of hits trying to look for combinations of phmyadmin, db, sql, and many other url keywords. This is obviously bothersome, but we’re all secure, so it didn’t cause much of an issue.

What did cause worry was the sheer number post requests to the /xmlrpc.php file. The IP addresses appear to be located in russia, and there were a handful different IP addresses that all had very similar origins. I’ve obscured the starts, but the IP range was ***.***.204.7-12. There is obviously enough traffic to blip our CPU usage on the medium server and enough to task the small server in a big way. To be more exact, there were ~78,000 requests in the log originating from those IP addresses over the past few days.

Why Is This Happening To Me?

There are many reasons hackers would like to gain access to your hosting server. One of the major reasons may be to steal resources to mine or farm bitcoin or some other cryptocurrency.

XML-RPC allows for very efficient brute force hacking, as it allows for hackers to check many username/password combinations in one single request. This is all at the cost of giving developers access to remote procedure calls.

XML-RPC Solution: Apache2 Deny From All

As I don’t use WordPress mobile, or other applications/plugins that require external access, I’ll be disabling access from external addresses.

The simplest solution to blocking this type of traffic on an Apache2 server is to simply deny access to the /xmlrpc.php file itself. To do this, you just have to add a few lines to your site configuration files in Apache2.

sudo vim /etc/apache2/sites-available/*yoursitehere*.conf

Your VirtualHost configuration should look something like the example below after adding the bolded lines.

#Some stuff
    <files xmlrpc.php>
      order deny,allow
      deny from all
      # you can allow from your own IPs using the following line
      # allow from ###.###.###.###

Finally restart Apache2 and you’re on your way to preventing hackers from eating up your server availability.

sudo service apache2 restart

XML-RPC Solution: Security Plugins

Note: This may be a better solution for those of you who wish to use applications that require XML-RPC to function properly.

There are some security plugins that will either deny access to the file, blog IPs that are abusing the service, or will disable XML-RPC altogether. Jetpack is one of those security plugins that will aid in blocking brute force attacks. WordFence and it’s firewall are really great for blocking IP addresses that are abusing your servers with irrelevant traffic.


If you do decide to utilize a security plugin, keep in mind that their scans and extra filtering procedures may have an adverse effect on your servers speed and responsiveness as well. This is sometimes a necessary evil, but you should plan ahead accordingly.

Blocking access to certain system files may prevent you from accessing certain features. However, you may find your site security more important than whatever feature that may be.

After having denied access to the file, we’re back down to our low utilization on the t2.medium server of <1%. I’m glad to see my CPU Credit Balance stabilize!

Hardening Apache2 on Ubuntu 16.04 LTS with Vim and Vigour!

There really isn’t much that’s more important than securing your web server when launching a website. Most of your development tasks are completed (hopefully), your designs are wonderful, and your designers are excited to finally get this project off their plate. So how do we go about securing our web server after launch?

Today, I’ll be talking specifically about the Apache2 web server. The particular flavour of Linux OS that I’ll be addressing is Ubuntu 16.04 (Debian also) considering it seems to be one of the most frequently used web servers today. There are some minor file location differences with RHEL/CentOS/Fedora, though not major. There are other tutorials addressing the particulars floating around. So I won’t include them here.

I’ll be using my favourite command line text editor Vim, but feel free to replace any vim command with nano if you’re more familiar with. I’ll include some basic information to help you through using Vim if you’re unfamiliar.

Shut-out Server Specification: Hide Your Server Version and OS Details

When hitting a server display page like a directory listing or a 404, you may notice that there exists a small colophon reading your servers version, IP, and Port. You can get rid of this tidbit of revealing data by changing some code in your Apache2 configuration files.

Type sudo vim /etc/apache2/apache2.conf to edit the file.

You may quickly notice a nice little message describing that your configuration file has been split for simplicity at the top. Always read documentation!

# This is the main Apache server configuration file. It contains the
# configuration directives that give the server its instructions.
# See for detailed information about
# the directives and /usr/share/doc/apache2/README.Debian about Debian specific
# hints.
# Summary of how the Apache 2 configuration works in Debian:
# The Apache 2 web server configuration in Debian is quite different to
# upstream's suggested way to configure the web server. This is because Debian's
# default Apache2 installation attempts to make adding and removing modules,
# virtual hosts, and extra configuration directives as flexible as possible, in
# order to make automating the changes and administering the server as easy as
# possible.

# It is split into several files forming the configuration hierarchy outlined
# below, all located in the /etc/apache2/ directory:
# /etc/apache2/
# |-- apache2.conf
# | `-- ports.conf
# |-- mods-enabled
# | |-- *.load
# | `-- *.conf
# |-- conf-enabled
# | `-- *.conf
# `-- sites-enabled
# `-- *.conf
# * apache2.conf is the main configuration file (this file). It puts the pieces
# together by including all remaining configuration files when starting up the
# web server.
# * ports.conf is always included from the main configuration file. It is
# supposed to determine listening ports for incoming connections which can be
# customized anytime.
# * Configuration files in the mods-enabled/, conf-enabled/ and sites-enabled/
# directories contain particular configuration snippets which manage modules,
# global configuration fragments, or virtual host configurations,
# respectively.
# They are activated by symlinking available configuration files from their
# respective *-available/ counterparts. These should be managed by using our
# helpers a2enmod/a2dismod, a2ensite/a2dissite and a2enconf/a2disconf. See
# their respective man pages for detailed information.
# * The binary is called apache2. Due to the use of environment variables, in
# the default configuration, apache2 needs to be started/stopped with
# /etc/init.d/apache2 or apache2ctl. Calling /usr/bin/apache2 directly will not
# work with the default configuration.

The actual file will be located in a separate configuration folder. Press esc to enter command mode and type :q or :q! if you accidentally changed the file to exit the file in Vim.

Type sudo vim /etc/apache2/conf-enabled/security.conf to edit the file containing security features.

Look for the string ServerSignature by typing /ServerSignature in Vim command mode (press esc at any time to enter command mode). You can use the arrow keys or h,j,k, and l to move your cursor left, down, up, and right respectively in command mode.

press i to enter text edit mode. This is the mode that you will be most familiar with when typing using a keyboard. Press esc to go back into command mode.

Change the following variables as follows:

ServerSignature Off
ServerTokens Prod

Once you’re done editing the file, enter command mode (esc) and type :wq to write the file and quit. If you’ve messed up the file, feel free to :q! to forcefully quit the file and ignore all changes as :q may not do the job alone. Typing u in command mode will undo any changes you’ve recently made as well, if that suits you better.

Once you’re back in the Ubuntu command line, restart the server by typing sudo service apache2 restart

Now when you visit the same 404 or directory listing page, you won’t be seeing that server signature! Congrats on completing step one!

Disable Detailed Directories: Hide Directory Listing and Files

Your Apache2 server will want to list out all the directories and files if you don’t have a base index.html or index.php (or other if specified in Apache2) in your directory. You can hide this functionality by adding a simple line of code to your apache2.conf file.

Type sudo vim /etc/apache2/apache2.conf to edit the base configuration file and hide directories from all sites located in your web folder.

Type /Directory /var/www/html to find the code you need to edit. It should be a block that looks like this:

<Directory /var/www/html>
 AllowOverride All

Just below AllowOverride All you’ll want to add Options -Indexes. You should end up with this.

<Directory /var/www/html>
 AllowOverride All
 Options -Indexes

Once you’ve changed your code, :wq out of the file and restart your server with sudo service apache2 restart. Once you hit a directory, you’ll now find a message forbidding you from accessing that folder. Congrats on completing step 2!

Write Where We’re Willed: Web Server File Permissions

Web servers are left open to hackers when using open file permissions (777 or -rwxrwxrwx / drwxrwxrwx). It’s important to make sure that your web server is given proper permissions to access and write directories, without opening them to hackers and visitors.

One simple way to do this is to disable write and execution tags where applicable in the permissions for folders and files. Permissions use binary triplets to turn on and off permissions. First, the base ten digit is converted to binary, and those positions turn on and off file and folder features.

To change all directories within your web folder to 755 (rwxr-xr-x):

find /var/www/html -type d -exec chmod 755 {} \;

To change all files within your web folder to 644 (rw-r--r--):

find /var/ww/html -type f -exec chmod 644 {} \;

These permissions not only work well for statically built websites, but also for content management systems like Magento and WordPress.

Updating Ubuntu: Specifically Apache2

Updating your server, and specifically updating Apache2 is very important. You’ll want to make sure you’re updating regularly to make sure the most important security patches have been applied.

Firstly you’ll want to update your package information by using sudo apt-get update

If you’d simply like to install updates for Apache2, just type sudo apt-get install apache2. You should be returned a message that looks something like the following.

Reading package lists... Done
Building dependency tree
Reading state information... Done
apache2 is already the newest version (2.4.18-2ubuntu3.3).
0 upgraded, 0 newly installed, 0 to remove and 21 not upgraded.

As you can see, I have 21 packages that are not upgraded on my server. If you’d like to update all of these packages, you can type sudo apt-get upgrade.

If you’d rather view the packages that need updating and install them one by one (using a command similar to the one for apache2), you can do so by typing sudo apt-get upgrade --dry-run or /usr/lib/update-notifier/apt-check -p for a simpler return.


These are only a few of the many ways you can harden your Apache2 web server. I’ll be adding to and maintaining this list as time passes, but there are a few extra things you’ll want to be sure to check out.

HTTPS and SSL Certificates

You’ll want to make sure that you’re installing SSL Certificates on all of your sites. Whether they’re extended validation or self signed, this can help keep traffic encrypted, and your users feeling safe. There are many other reasons to install SSL, a big reason is that Google promotes sites that use it more than ones that don’t (for obvious reasons). With tools like Lets Encrypt there really is no reason not to install a cert on all of your servers.

Firewalls Firewalls Firewalls

Personally, I like to host on AWS where their console and security features allow for very strict access to your cloud network infrastructure. If you don’t have access to such strong security measures on your own personal server, you’ll want to ensure that you take advantage of the firewall tools available in Ubuntu.

If you have any questions, make sure you comment below!

Cole Speelman

Installing, Configuring, and Maintaining MySQL on Ubuntu 16.04 LTS

This will be a relatively short informative blog regarding how to setup and secure an installation of MySQL on Ubuntu.

MySQL Server Installation

Firstly, update your package library and install your MySQL server.

sudo apt-get update
sudo apt-get install mysql-server

You’ll be prompted to create a root password during the installation. Make sure it’s a complicated password that you’ll remember, because you’ll be needing it.

MySQL Server Configuration

You’ll want to be sure to harden security on your MySQL installation by running the security script.

sudo mysql_secure_installation

This will prompt you to enter your root user password that you created during installation.

Firstly, the setup will ask if you would like to install a VALIDATE PASSWORD PLUGIN that can test passwords and improve security. If you’re the only one administering databases and are diligent about using great passwords, you opt-out.

Second, it will ask if you’d like to change the root password. If you’re having second thoughts about your password strength, you can change it now.

Third, it will ask if you would like to remove anonymous users. I typically use applications like WordPress and Magento, so I always have database users created out of the gate. I opted to remove anonymous users, but you may decide to run this script again before launching your site and remove them at a later date.

Fourth, it will ask if you would like to disallow root user remote access. I strongly suggest enabling this, especially considering that we’ll have phpMyAdmin installed shortly, removing any need for this.

The last two questions are to remove the test database, and to reload the privilege table, both of which I answered yes, as I won’t be needing the test database, and the privileges are important.

Server Status

To check the status of MySQL server you can run the sudo service mysql status command. If the server is not running, you can start it with sudo service mysql restart or sudo service mysql start.

Reset MySQL Root Password

You may find yourself in the situation that you have forgotten your root password. This recently happened to me after I jumped into a server that I had not maintained in quite some time (it was development, don’t worry).

The first step to resetting your password is to gain access to the terminal (ssh is typically what I use). Once you’re in, you’ll want to stop the MySQL service.

sudo service mysql stop

Once you’ve stopped running your server, you’ll need to prep the next command by creating a folder for it to access.

sudo mkdir /var/run/mysqld
sudo chown mysql: /var/run/mysqld

You can start the server with a few options that I’ll explain.

sudo mysqld_safe --skip-grant-tables --skip-networking &

The --skip-grant-tables flag turns off the need for authentication, and the --skip-networking flag turns off the ability to access the database remotely (important when authentication is disabled).

Once you’ve run the server, enter the mysql command line tool, and change the password.

sudo mysql

For MySQL 5.7.6 and later:

ALTER USER 'root'@'localhost' IDENTIFIED BY 'YourNewPassword';

For MySQL 5.7.5 and earlier:

SET PASSWORD FOR 'root'@'localhost' = PASSWORD('YourNewPassword');

Then you’ll need to reboot your MySQL server.

#Shut down MySQL
sudo mysqladmin -S /var/run/mysqld/mysqld.sock shutdown

#Start the MySQL service normally.
sudo service mysql start

From there on out, you can use whatever you set as YourNewPassword to access root functionality.

Notes for PHPMyAdmin

If you’ve decided to do your database administration through PHPMyAdmin, I would strongly suggest disallowing any unauthorized access to it. I’ve seen people block access with an Apache password option, but this doesn’t really help as much as you would think.

Blocking all access except your own is quite easy if you have a static IP address. If you don’t have a static IP address, you can contact your ISP and let them know that you would like one. If a static IP address isn’t in the books for you for one reason or another, you could always check your IP address and change the IP address in the configuration file each time you need access. Which shouldn’t be terribly often.

Either way, here’s how you do it:

sudo vim /etc/apache2/conf-enabled/phpmyadmin.conf

and add these lines to your code:

# phpMyAdmin default Apache configuration

Alias /phpmyadmin /usr/share/phpmyadmin

<Directory /usr/share/phpmyadmin>
 Options SymLinksIfOwnerMatch
 DirectoryIndex index.php

 # Add Your IP instead of ##.##.##.##
 Order Allow,Deny
 Allow from ##.##.##.## 

This will effectively block all access except for your IP address. It doesn’t get much more secure than this for PHPMyAdmin.


MySQL is fun. Don’t get bogged down with the basics! With this basic installation information, you’ll be well on your way to working with databases and enjoying all that relational databases have to offer!

Cole Speelman