Code Done Right!

Basic security

Every server accessible from the internet is going to be a target of an attack at some point, this is a fact and there is no exception. You will have proof of that in your logs. Fortunately, unless your server gains popularity and attracts a real hacker, majority of those attacks are going to be either bots or script kiddies trying out the combination of known password pairs like admin/admin. Still, you need to protect yourself from them.

The most newbie friendly solution is to install packages that will

  • Block access to ports that should not be accessed
  • Ban bots that are using known password pairs to gain access (brute force attacks)
  • Run an antivirus scan
  • Run automated update notification script

Even though I will instruct you how to seetup all this, I still advise to pay attention to system logs. You need to know what is happening to your server. What one man wrote, the other can hack. From time to time you hear about password leaks or break-ins to major players like Dropbox or iCloud, a home server will never be more secure than companies using first rate security solutions. Security measures I will talk about in a moment should be enough for your home server.

UFW – security 101

UFW stands for Uncomplicated Firewall. This is the first security measure that you need to take. Its purpose is to block access to ports that you do not need to have open.

Every connection uses one port from the pool of 65535 ports. And UFW is going to block those in case you run a service listening for a connection on one of those ports and you do not want it to be accessible from outside. On the other hand, you need to have certain ports open in order for your services to be accessible from the outside.

For example, you will not be able to access a website on your server without opening port 80 for http:// There is a list of standard ports for services – 22 for SSH connection, 80 for websites, 993 for email etc., and in theory we could change some of those in order to secure access to our server, but in practice ports can be mapped and the attacker can discover which ports are open on our server and that, in turn, nullifies the whole point of changing ports. So let us just block everything that does not need to be open and leave defaults as they are.

I encourage you to read more about the topic of ports and why some require an encrypted connection and some are freely open but for now just follow me.

UFW installation

In order to install UFW run the following command

sudo apt install ufw

Before you proceed!

CAUTION do not enable the firewall before opening port 22. If you block port 22, you will not be able to connect to your server and you will have to boot your pi with external monitor to open said por

CAUTION! delete the default user pi before opening your server to the internet. Logging in with valid credentials is not an attack, it is system administration malpractice. Leaving the pi user with sudo privileges allows anyone to remotely log in and do as he pleases, your data will get stolen, your server will get broken and your time setting everything up will be wasted. Find out how to delete user pi in the previous part of the tutorial here.

UFW configuration

UFW is not enabled after installation and we need to manually do so, but before we do that we need to open at least port 22 for SSH connection, and while at it, we might as well enable all ports that we are going to use.

First of all we need to set up a few exceptions to open some of ports to our server. Run the following commands

sudo ufw allow 20
sudo ufw allow 21
sudo ufw allow 22
sudo ufw allow 25
sudo ufw allow 80
sudo ufw allow 143
sudo ufw allow 443
sudo ufw allow 465
sudo ufw allow 587
sudo ufw allow 993

Check out why we opened those specific ports on Wikipedia

You will get a prompt after adding any rule, should a rule be already in place you will get skipping rule prompt. Rules will be applied to both IPv4 and IPv6.

FTP ports

On top of that we need to have a few ports for FTP passive access. You can open only one, but should you install a package listening on that specific port, your FTP access will be blocked. It is just common sense to open a small range. UFW allows for specifying a range instead of just one port in a rule and we need to decide what ports we are going to use for our FTP access. Ports go from 0 to 65535. Pick ten, any ten ports. Now run the following command

sudo ufw allow $PASSIVE_MIN:$PASSIVE_MAX/tcp

Substitute $PASSIVE_MIN and $PASSIVE_MAX with the range you have chosen for FTP. It does not have to be ten ports, it can be two, five or thirty. Just do not make it one, and ten is a such a nice round number. If you have chosen range from 667 to 700 the command would look as follows

sudo ufw allow 667:700/tcp

When adding a range you have to specify if this should open TCP or UDP ports. We want TCP.

Enabling UFW

Now that we have everything in place, we can finally enable UFW by running the following command

sudo ufw enable

Idiot-proofing measure asks you if you are sure that you want to enable UFW as you may lose access, but since we opened port 22 we are ok with starting the UFW service. Answer yes

Checking rules in place and removing them

If you want to check what rules are in place run the following command

sudo ufw status numbered

Parameter numbered shows us an additional column with numbers next to rules that makes removing them easy. We can do that by running the following command

sudo ufw delete x

Substitute x with the number of a rule you want to remove. Bear in mind that the list shifts after deleting one of them. Meaning that if you want to delete three consecutive rules then you have to delete the same rule three times, as for example rule 7 becomes rule 6 after you delete the initial rule 6. Just allow a few random ports, print out the rule list, delete one of them and print out the rule list again. You will see for yourself.

You might not realize, but with Raspbian you get a firewall security with the system by default in the form of IPTABLES. Using it without UFW is a different story. UFW is actually a front-end for IPTABLES. Think of it as hiring a crew to build your house instead of having to set up a tent every time you want to get some sleep. That is it for UFW installation and configuration. Once enabled is will boot up with the system.

Restoring default rules

In case you made a big mess of rules you can simply revert to defaults by running the following command

sudo ufw reset

Before you do that it is advised to deactivate UFW as your SSH connection might get dropped. Run the following to disable UFW

sudo ufw disable

After you reset all rules simly add those that you actually need and enable UFW again.

Running IPv4 server only

If you have only IPv4 address you can skip adding IPv6 rules by modifying UFW config located here


and changing the following rule to no


SSHGuard – security by banning offenders

SSHGuard security
SSHGuard blocks brute-force attacks

This security package monitors your logs for an unauthorized access, validates the threat level and when the level reaches certain threshold it jails the attacker for a period of time. In layman terms – put incorrect login credentials too many times and you are banned for a period of time, any further login tries within the jail period will only extend it exponentially. Simple and effective.

CAUTION you are not exempt. If you forget the user name or your password and you will try logging in with incorrect credentials you will get banned as well. Mind the login!

Installing SSHGuard

Keeping the above in mind let us install SSHGuard with the following command

sudo apt install sshguard

Contrary to UFW, security is enabled right after installation and daemon is loaded after every reboot out of the box. Default setting jails an IP for 3 minutes after 3 consecutive incorrect logins within a short period of time. More information about SSHGuard can be found here.

SSHGuard, just like UFW, uses IPTABLES firewall and is just a front-end for the service. We could get by without both UFW and SSHGuard but that is a difference between running a few simple commands and writing a complicated scripts that need to be ran after every reboot. The hassle is just not worth it and you should just use those two wonderful tools.

In case you are facing problems with SSHGuard you can try the fail2ban alternative. This is a more popular solution, but I found that it conflicted with my mail setup, so I am using SSHGuard. The functionality is the same. If you run into any trouble with one then try using the other.

Whitelisting your local IP

If you do not want to lock yourself out of your server, you might want to whitelist your local IP address of the client you will use to tinker with the server.

Run the following

sudo nano /etc/sshguard/whitelist

And at the bottom of the file add your local IP address. One IP per line

ClamAV – security with antivirus

ClamAV® is an open source antivirus engine for detecting trojans, viruses, malware & other malicious threats

It is very unlikely that you will catch a virus on your server but it may happen. Better safe than sorry. A good and a free choice is ClamAV which we will configure to run daily at a certain time. It will scan your system and get rid of infected files should it find any.

CAUTION from my experience with Pi Zero, is just too weak to handle ClamAV and it simply gives up at some point during the scan. If you think you know what is going on or managed to do it with Pi Zero then leave a comment.

Install ClamAV with the following command

sudo apt install clamav

Out of the box ClamAV updates its database automatically and you do not have to worry about it. Scanning on the other hand has to be taken care of manually. Real-time protection is in form of warnings. You will not be stopped from accessing an infected file. You can read more about ClamAV and its configuration here.

CAUTION clamav will perform an update and a scan after installation. This will consume resources for quite a while, depending on the number of files you have on SD and on the Pi you are running. Use the command htop to confirm this, you should see a screen similar to below image

clamav security antivirus scan
ClamAV consuming Raspberry Pi Zero resources

It took about 20 minutes on a freshly installed Raspbian on a Pi Zero W with SanDisk Ultra UHS-I SD card. The time needed for scan will vary depending on the Pi you are running, the speed of your SD card and the number of files on it.

Give it time to complete before proceeding further.

Setting up a daily scan

In order to set up a daily scan we need to make a bash script. Since we will have the script running by itself via Cron, an automatic task schedule package, it is a good idea to set it up somewhere else than our home directory so it does not get in the way. And while we are at it we can set up a directory for custom log files as well.

Choose a place for scripts and logs now. Is is a good idea to put our ClamAV logs in a subfolder as well. A good choice is the /var/ folder, as its name suggests, it is for various files. Again, this can be any place you want, but make it easily accessible so you do not have to dig through folders to find them.

Creating custom folders for logs and scripts

In order to create those three custom folders run the following commands

sudo mkdir /var/zzz_log
sudo mkdir /var/zzz_log/clamav
sudo mkdir /var/zzz_script

Adding a zzz_ prefix will send them to the bottom of the folder list and make them easier to find. If you want, you can call them fluffybunny1 and iloverainbow if you wish.

Now lets change the owners of said folders, run the following code to make those folders assigned to your administrative user, that you have chosen in the installation part of the tutorial

sudo chown $USER:$USER -R /var/zzz_log
sudo chown $USER:$USER -R /var/zzz_log/clamav
sudo chown $USER:$USER -R /var/zzz_script

Folders should have the correct permissions assigned to them, but in case they do not, we need to set them up with the following code

sudo chmod 755 -R /var/zzz_log
sudo chmod 755 -R /var/zzz_log/clamav
sudo chmod 755 -R /var/zzz_script

This will allow the $USER to read, write and execute anything inside those folders while $USER‘s group and other accounts will be able to read an execute files inside without the write ability. You can read about file permissions here. Do get yourself acquainted with managing rights as this is a very important part of working with Linux. In the infancy of your dealings with system administration you will often run into hurdles if you do not know how to manage permissions.

Creating the daily script itself

Now we need to make the script itself. The below script is almost a direct copy of Habilis’ script here. It is a nice and useful peace of code and I am not the one to reinvent the wheel.

In order to create a file and edit it run the following command

nano /var/zzz_script/

Inside of it paste the following code


FIND="$(which find)"
DATE="$(which date)"
GREP="$(which grep)"

TIMESTAMP=`$DATE '+%Y-%m-%d %H:%M'`

if [[ ! -e /tmp/virus ]]; then
  mkdir /tmp/virus

check_scan () {
if [ `tail -n 12 ${LOG} | $GREP Infected | $GREP -v 0 | wc -l` != 0 ]
1>> $MYLOG
echo "`tail -n 50 ${LOG}`" 1>> $MYLOG
echo "#####################################"
1>> $MYLOG
echo "$TIMESTAMP - scan has been performed as expected" 1>> $MYLOG

clamscan -r / --move=/tmp/virus --max-filesize=600M --max-scansize=600M --exclude-dir=/sys/ --quiet --infected --log=${LOG}

Make sure the script is executable with the following command

sudo chmod a+x /var/zzz_script/

Read more about scripting in bash if you want to fully understand the script. With time you will have to anyways, might as well start now.

CAUTION if you are running a NAS, or you have large folders with files like video etc. then you need to include path to such folders by adding additional arguments


Just put it after the first one in the script. Otherwise your antivirus scan will scan those folders as well and will unnecessarily throttle your system!

You can do that at any point in time, just remember about it after adding a 1TB NAS with video files.

What does the script even mean?

The script works out of the box, but if you are curious what those lines do here is the breakdown

  • #!/bin/bash – this is how any script starts, with a shebang and path to shell it is going to be executed in
  • LOG and MYLOG lines define places where we will store ClamAV logs
  • FIND, DATE, GREP and TIMESTAMP set up some variables for the script to use later, they are pretty self explanatory, if not brush up on basic commands we are dealing with linux so there is no hand-holding here
  • First if module checks if there is a /temp/virus folder present, if not – it creates it
  • check_scan () is a custom procedure for checking ClamAV $LOG and if a virus has been detected it writes to $MYLOG saying so. You should check the main log for more information in such a case. It is set up this way so you do not have to scrub many logs and you can just take a look at $MYLOG to see if everything is OK, we will also add this information later to our SSH welcome screen. You will know if you need to check out the log right after logging to your server
  • clamscan command is what actually performs the scan itself and takes care of any virae it finds. Type clamscan –help in CLI for more in-depth information regarding attributes used, there is no point in retyping the MAN page here

Make sure the script executable with the ls -l command. If not, see above part about chmod and chown

Scheduling the task

Since we want to automate the scan we need to use cron, a tool for scheduling tasks. It is built into the system so we do not have to install it, we just need to set it up

Run the follownig code

sudo crontab -e

At the bottom of the file add the following line

0 5 * * * /var/zzz_script/

This makes the script, and by extension the scan, run at 5am every day. You can set up a different time of day or make it run every 7 hours for example, but once a day should be enough for our peace of mind. Take a look at the crontab file where you added the task, it tells you how to schedule tasks for different hours and/or time intervals.

CAUTION do not set up many tasks for the same time as it will slow down your server to a crawl. Your Pi might start to overheat and when it reaches 85 degrees Celsius it will throttle the CPU to cool itself and in turn all operations will slow down. Set up tasks for different full hours and you will be ok.


Security is paramount! With UFW and SSH Guard in place we are done with basic security, server should be protected from most common threats by now and we can move on to installing actual services that our server will provide. This is the absolute and bare minimum that we need. Not using UFW and SSH Guard is, as stated before, plain malpractice. Did you remove the user pi as well? If not go HERE right now and do it! With the addition of anti-virus scan we should have a peace of mind. Up next – setting up an FTP server!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.