Skip to content

Latest commit

 

History

History
2288 lines (1581 loc) · 74 KB

README.md

File metadata and controls

2288 lines (1581 loc) · 74 KB

Raspbian Secure Server Config Tutorial (R.S.S.C.T)

ko-fi

Table of Contents

Description

Let's build a server at home with a RaspberryPI, a minimal optimized Raspbian OS, configure it, secure it, hide it, test it and of course, enjoy it!

This tutorial is for Raspberry Pi Model 1B, 1B+ and 2B, a minimal microSD card of 8GB (i'm using a 95mb/s 64GB) and the standard RPi 1GB of RAM memory

For RPi Model 3 or RPi zero, check the alpha branch v1.1.x of raspbian-ua-netinst, but beware, some stuff from this tutorial will be probably different, and you'll be able to solve things only with a lot of passion and enthusiasm.

Here follow the detailed process of building the server, let's make coffee and get to it!

Install

First of all, we want to start with a minimal version of Raspbian, something similar to the netinst version of Debian, so i searched the web (not googleed, i prefer to use DuckDuckGo because they do not track you, or at least it seems so) and i find a great contribution from debian-pi github user, the raspbian-ua-netinst repo, a Raspbian (minimal) unattended netinstaller!

Amazing!

So, follow the repo instructions, download the last release installer and flash the SD card. Easy. The second step is to put the flashed SD card in your RPi, power on and wait, the installer will boot your RPi, connect to the internet (you need to connect the RPi with an ethernet cable), downloading the latest version of Raspbian, and installing it. Depending on your internet connection speed you will have to go for another coffee, or not.

When this step is finished you will have a minimal Raspbian system with ssh enabled by default, the root account with password: raspbian, and all the necessary basic command line tools. (you can check the details in the raspbian-ua-netinst repo)

If everything went ok, now you can ssh into your RPi with {user : root, password: raspbian}:

ssh root@RPI_ip_number

Let's print some system info, first the running kernel version:

cat /proc/version

my output:

Linux version 4.4.0-1-rpi2 ([email protected]) (gcc version 4.9.2 (Raspbian 4.9.2-10) ) #1 SMP Debian 4.4.6-1+rpi14 (2016-05-05)

The Linux distribution and version:

cat /etc/*release*

my output:

PRETTY_NAME="Raspbian GNU/Linux 8 (jessie)"
NAME="Raspbian GNU/Linux"
VERSION_ID="8"
VERSION="8 (jessie)"
ID=raspbian
ID_LIKE=debian
HOME_URL="http://www.raspbian.org/"
SUPPORT_URL="http://www.raspbian.org/RaspbianForums"
BUG_REPORT_URL="http://www.raspbian.org/RaspbianBugs"

The block devices:

lsblk

my output (with 64GB SD card):

NAME        MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
mmcblk0     179:0    0  59.5G  0 disk
├─mmcblk0p1 179:1    0 122.1M  0 part /boot
└─mmcblk0p2 179:2    0  59.4G  0 part /

or more human readable:

df -h --output=source,fstype,size,pcent,target -x tmpfs -x devtmpfs

This is just the beginning! In the next session we'll make a little post-install configuration, just the suggestions from raspbian-ua-netinst repo, next story, "Post-Install Config".

Post-Install Config

1 - Set new root password:

passwd

2 - Configure your default locale

dpkg-reconfigure locales

3 - Configure your timezone

dpkg-reconfigure tzdata

4 - Improve memory management performance

apt-get install raspi-copies-and-fills

5 - Install and auto-load and use the kernel module for the hardware random number generator. This improves the performance of various server applications needing random numbers significantly.

apt-get install rng-tools

6 - Create a 1GB SWAP file, and enable it on boot modifing fstab file:

dd if=/dev/zero of=/swap bs=1M count=1024 && mkswap /swap && chmod 600 /swap
echo "/swap none swap sw 0 0" | tee -a /etc/fstab

Ok, we have our basic Raspbian server post installation!

I'm curious at this moment about things like Attack Surface, let's print some information:

find / -perm -4000 -print

This command will list potential 'vulnerable' system points, listing all executable files with SUID . My output:

/bin/mount
/bin/umount
/bin/su

basically SUID flag allow a user to run an executable with the permissions of the executable owner, so if someone finds a vulnerability in one of this programs and exploits it, GAME OVER, he/she will have root permissions on the system, goodbye Raspbian Secure Server!!!

But don't worry, we are just getting started, a long journey awaits us, with so much to learn.

Let's install and configure all the essentials for our Raspbian Secure Server, next story, "Configuration".

Configuration

First of all, install some package downloader utils:

apt-get install apt-utils

We will need to get comfortable with edit a lot of text files, maybe some programming too :P, so we begin installing our favorite console text editor, i'm going to use "nano", but there are better options like "vim", choose here whatever suits you:

apt-get install nano

And customize it by adding line numbering:

nano /etc/nanorc

Uncomment # set const to add line numbering:

# set const
set const

Users

First of all, we need to install sudo, a program designed normal users to execute some commands as root, and why is that? Because it's safer than always opening root sessions, nobody will need to know the root password, every execution will be logged and so on with a few more security related reasons.

apt-get install sudo

Create a new user with regular account privileges (change "user" with your username of choice):

adduser user

Follow instructions, fill all the fields you want, and most important, enter a strong password.

Now we need to add the new user to the sudo group, in order to grant sudo capabilities:

adduser user sudo

My output:

Adding user 'user' to group 'sudo'
Adding user user to group sudo
Done.

In order to apply the new group assign log out and log in again.

Next story, SSH

SSH

Create a new SSH Key Pair for securing the server with a public key authentication for the new user:

1 - On your local machine, generate a key pair:

ssh-keygen -t rsa -b 4096 -C "raspbian_rsa"

Choose the name of the key (Ex. myKey) files and set a password This generates a private key "myKey" and a public key "myKey.pub", in the .ssh directory of the localuser's home directory. Remember that the private key should not be shared with anyone who should not have access to your servers!

2 - On your local machine, copy the public key to our server:

ssh-copy-id -i myKey.pub user@RPI_ip_number

That's it, now we may SSH login to our server using the private key as authentication, so the time has come for configuring our SSH daemon for better security

Let's open the SSH configuration file:

nano /etc/ssh/sshd_config

And change:

0 - Disable ipv6

#ListenAddress ::
ListenAddress 0.0.0.0

1 - Disallow SSH access to root account

PermitRootLogin no

2 - Disable X11Forwarding:

X11Forwarding no

3 - Add AllowUsers user, in order to enable access for your new user ONLY:

AllowUsers user

4 - Disable tunneled cleartext password authentication and enable SSH public key only access

PasswordAuthentication no
PubkeyAuthentication yes
AuthorizedKeysFile      %h/.ssh/authorized_keys

5 - Save the file (ctrl o), close it (ctrl x) and restart the ssh service:

/etc/init.d ssh restart

6 - On your local machine, Log out and ssh login to check, now with your generated ssh key, normal cleartext password is now disabled.

ssh -i ~/.ssh/your_rsa_key_name -p 22 username@RPi_ip_number

For the most curious, -i specify the identity_file (your private key from the key pair), and -p specify the port where to connect (22 is the standard ssh port).

7 - For more info about securing services, take a look at the debian manual

Now let's harden our SSH for protection against brute force attacks, installing fail2ban

Fail2ban (Special Section)

fail2ban provides a way to automatically protect virtual servers from malicious behavior. The program works by scanning through log files and reacting to offending actions such as repeated failed login attempts.

apt-get install fail2ban

Now make a local copy of the configuration file:

cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

And go on configuring it (the elegant stuff):

nano /etc/fail2ban/jail.local

We start with the [DEFAULT] section, edit this lines:

ignoreip = 127.0.0.1/8 192.168.1.0/24
bantime  = 3600
maxretry = 3

This will whitelist the local direction (127.0.0.1/8) and the local net (192.168.1.0/24), and ban a malicious ip after 3 wrong login intents, for 1 hour (3600 seconds).

Now to the [JAILS] section, under ssh you'll see:

enabled  = true
port     = ssh
filter   = sshd
logpath  = /var/log/auth.log
maxretry = 6

This are the specific settings for SSH service, we don't need to change it, but in case you change the standard port for ssh (22) to another, you'll need to set it up:

port     = 33000 # for example

Perfect, we have it, save the file, close it and restart fail2ban:

/etc/init.d/fail2ban restart

Ok, user configuration and SSH login secured and tested, if everything is working correctly, next story, "Net".

Net

So, basically, at this stage we want to connect our Raspbian Server at our local network via ethernet cable and ssh into it to continue installing and configuring stuff, and probably we are not going to do that always at the same place, sometimes we will working on that from home, and maybe sometimes we are going to work from a friend house, or a co-working somewhere, or whatever. The point is, we don't want to check every time the IP number of our RPi, we don't want to have DHCP (default) assign a new IP every time we connect our RPi to a new router, so we disable DHCP and assign a static IP. That means, our Raspbian Server, locally, will always have the IP we choose. This is really trivial, so let's do it.

1 - Open the file /etc/network/interfaces

nano /etc/network/interfaces

2 - You'll see a line like this:

iface eth0 inet dhcp

This is the default configuration for eth0, the RPi standard ethernet device, with DHCP enabled. DHCP is a protocol commonly used by the vast majority of routers to dynamically assign a free IP to the connected device. It's really the easy choice, but we don't want that here (not because of the "easy" part), we just want our RPi to have always the same IP number (static).

3 - So we comment the default DHCP config line and we add a static IP:

#iface eth0 inet dhcp
iface eth0 inet static
  address your.static.ip.number # Ex. 192.168.1.59
  gateway your.router.ip.number # Ex. 192.168.1.1
  netmask 255.255.255.0

The address and netmask goes accordingly with your router configuration, but the above example is really common

4 - Save the file and close it

We have it! Try rebooting your RPi and check for the eth0 assigned IP:

ifconfig eth0 | grep inet

If everything is correct, your output will show the IP and netmask you configured in the /etc/network/interfaces file

Ok, so pack your RPi with an ethernet cable, and stuff it in your bag, now we will be able to work at our server at any place with a router; we connect the RPi, start it up, and from another computer connected to the same router we ssh to our server, great!

Let's take a look now at a really useful tool for when we are working with network stuff, netstat

netstat -apt

This will output all(listening and established) active tcp connections, my output:

Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 RPi.static.ip:22        *:*                     LISTEN      294/sshd        
tcp        0      0 RPi.static.ip:22        client.ip.number:22     ESTABLISHED 388/sshd: username

Everything seems ok, we have our SSH daemon listening at port 22 and our active ssh connection established. Port 22 is the standard port for SSH, and what are the standard port numbers for other services? To answer that, the best thing we can do is to take a look at the list of common standard ports in a server.

Super easy, just take a look at the /etc/services file:

cat /etc/services

So services and ports, and netstat, we will use this tool a lot, so check it out and get comfortable with it ;

Ok, we'll hit pause right here, it's the update/dist-upgrade moment, we'll repeat this step a number of times over the entire server configuration:

apt-get update && apt-get dist-upgrade

To obtain all the updates for our current raspbian system, and:

apt-get clean

To clean disk space removing temporary updates installation files.

In one line:

apt-get update && apt-get dist-upgrade -y && apt-get clean

So far so good, we have now a decent secure access door to our Raspbian server, we can now start installing and configure all our services, more or less depending on what we'll need from our server, but this is the next chapter so, see you in a bit.

DNS

Before continuing with our installs and configs, we need to tell the system where to ask for ip <--> domain names associations, and for that we need to use what is called Domain Name System Server (DNS server), basically we need to pass the system the ip of one or varios DNS servers so it will be able to get the ip of a domain or viceversa.

We'll use the standard OpenDNS servers here, but feel free to use whatever you like more.

So, edit the /etv/resolv.conf file:

nano /etc/resolv.conf

And add your DNS servers ip, in case of using OpenDNS's:

nameserver 208.67.222.222
nameserver 208.67.220.220

Save the file, close it and reboot. DONE!

Services

From wikipedia: "Servers can provide various functionalities, often called 'services', such as sharing data or resources among multiple clients, or performing computation for a client."

CERN First WWW Server The first HTTP web server of the history, year 1990 (from CERN, where they actually invented the web!)

Why are we building a server? This is what you need to ask yourself just now! Because depending on what the answer will be, the content of this chapter will eventually change a lot, but anyway, we'll tray to stay cool and cover at least all the basic services, and maybe something more, but enough with the chatting, let's get to it. There is no better or specific order to install services, in general at least, so i will use mine but you feel free to change it, and of course contributions are welcome and really appreciated.

SFTP

So, we'll start with implementing a SFTP service with a Chroot'ed Isolated File Directory, WHAAAAAAAAT?

Well, yes, it's not double click on the icon :P, but we are trying to be pro here, and the tutorial title say "Secure Server Config..." so, but don't worry, we'll crack it step by step.

Step 1, what is SFTP? From digitalocean : "it stands for SSH File Transfer Protocol, or Secure File Transfer Protocol, is a separate protocol packaged with SSH that works in a similar way over a secure connection. The advantage is the ability to leverage a secure connection to transfer files and traverse the filesystem on both the local and remote system."

Step 2, what in the hell means chroot'ed? From wikipedia : "A chroot on Unix operating systems is an operation that changes the apparent root directory for the current running process and its children. A program that is run in such a modified environment cannot name (and therefore normally cannot access) files outside the designated directory tree. The term "chroot" may refer to the chroot(2) system call or the chroot(8) wrapper program. The modified environment is called a chroot jail."

Step 3, in short, let's implement a secure difficult hackable file transfer protocol service for our Rasbian Server, yheaaaaaa! With this service we will be able to safe connect with our server and upload files, including let someone else access to our server to upload/download files, but in a chroot jail environment, like a bubble with no exits, a chroot environment is the only observable universe, so the rest of the system where we don't want anyone peeking or worse (see Directory traversal attack), will not exists.

Step 4, install OpenSSH server software

apt-get install openssh-server

Step 5, create a users group for sftp access and a specific user, this is a good practice for every kind of services, create specific groups for every service in order to limit access, if i will connect via sftp, i will have access to ONLY that.

groupadd sftpgroup
cat /etc/group

Take note here of the id related with the newly created group, in my case is 1001:

sftpgroup:x:1001:

Add now a new user that we will use exclusively for SFTP access (change 1001 with your group id, and choose your user name):

sudo useradd [user name] -d /home/[user name] -g 1001 -N -o -u 1001
sudo passwd [user name]
  • -d is the user home directory which needs to be set to /home/[user name].
  • -g is the user group id to assign which in our example needs to be assigned to sftpgroup.
  • -N useradd by default creates a group with the same name as the new user, this disables that.
  • -u is the user id, which in our case needs to be the same id value as sftpgroup.
  • -o allows duplicate, non-unique user ids.
  • The passwd command sets an encrypted user password.

Now output the system users list to check that everything went fine:

cat /etc/passwd

In the last line we'll see the new added user

sftpuser:x:1001:1001::/home/sftpuser:/bin/sh

Now, before configuring the SSH daemon, we need to create a new keypair for this new user, in my case sftpuser, we did it before for the regular ssh connecting user (NET chapter?), so here a little refresh:

1 - Generate the new keypair on your client machine:

ssh-keygen -t rsa -b 4096 -C "raspbian_sftp_key"

2 - Copy the public key to the server:

ssh-copy-id -i myKey.pub sftpuser@RPI_ip_number

3 - That's it, exit from actual ssh session and try to log in with the new sftpuser just for testing.

Step 6, we need now to edit the SSH daemon configuration file, the same one we edited for SSH connection some while ago, remember? Let's do it:

nano /etc/ssh/sshd_config

Search for the line

Subsystem sftp /usr/lib/openssh/sftp-server

And change it to

#Subsystem sftp /usr/lib/openssh/sftp-server
Subsystem sftp internal-sftp

Now the cool part, go to the end of the document and add the following block:

Match group sftpgroup
ChrootDirectory /var/www
X11Forwarding no
AllowTcpForwarding no
ForceCommand internal-sftp

And the last really important one, add the new user sftpuser to the AllowUsers line:

AllowUsers user sftpuser

And this is the part where we confine the sftpgroup users group to the /var/www directory (they will not be able to escape from there, or at least they will have to sweat it). We use /var/www because is the standard directory for web servers, but feel free to choose another folder if you prefer, like /var/sftp for example. This step is really important, if you forget to configure the ChrootDirectory for the specific users group, a connected user could gain access to the / (the server root) and we do not actually want that!!!

So, save the document and

Step 7, create the /var/www folder, if you don't have it already:

mkdir /var/www

Step 8, create some testing folder, a read-only one, a read/write one and a no-access one:

cd /var/www
mkdir test_readonly
mkdir test_readwrite
mkdir test_noaccess

At this moment the three folders have the same permissions, let's explain a bit:

ls -la

Give me (we are inside /var/www):

drwxr-xr-x  5 root root 4096 Mar 26 05:41 .
drwxr-xr-x 12 root root 4096 Mar 26 05:37 ..
drwxr-xr-x  2 root root 4096 Mar 26 05:41 test_noaccess
drwxr-xr-x  2 root root 4096 Mar 26 05:40 test_readonly
drwxr-xr-x  2 root root 4096 Mar 26 05:41 test_readwrite

We see here a list of the folder content, just the three folders we just created, and on the left we have the permission mode:

drwxr-xr-x

Let's break it in parts:

d

For now we will focus on the last three blocks:

rwx r-x r-x

The first one on the left represent the root permissions, the second one in the center represent the group permissions, and the third one on the right represent permissions for everyone else, and we can read it this way:

r w x --> 2^2 2^1 2^0 --> 4 2 1

And in bits we can read it:

r w x --> 1 1 1

r - x --> 1 0 1

And so on

So we have some possibilities, but not so much in the end:

  • 0 - No permissions
  • 1 - execute permission
  • 2 - write permission
  • 3 - execute+write permissions
  • 4 - read permission
  • 5 - execute+read permissions
  • 6 - read+write permissions
  • 7 - execute+read+write permissions

More about Linux permissions here

So, coming back to our list:

drwxr-xr-x  5 root root 4096 Mar 26 05:41 .
drwxr-xr-x 12 root root 4096 Mar 26 05:37 ..
drwxr-xr-x  2 root root 4096 Mar 26 05:41 test_noaccess
drwxr-xr-x  2 root root 4096 Mar 26 05:40 test_readonly
drwxr-xr-x  2 root root 4096 Mar 26 05:41 test_readwrite

We have now all the folders with 755 permissions (rwx r-x r-x), and this is ok for the test_readonly one, but we need to change the permissions for the other two:

chown root:sftpgroup test_readwrite
chmod 775 test_readwrite

With that we assign root as owner of the folder and sftpgroup as folder group, and with 775 permissions we grant full permission to owner, full permissions to assigned group, and execute+read permission to everyone else.

So for the noaccess folder we set permissions to 711, execute only for group and everyone else:

chmod 711 test_noaccess

And our list again:

drwxr-xr-x  5 root root      4096 Mar 26 05:41 .
drwxr-xr-x 12 root root      4096 Mar 26 05:37 ..
drwx--x--x  2 root root      4096 Mar 26 05:41 test_noaccess
drwxr-xr-x  2 root root      4096 Mar 26 05:40 test_readonly
drwxrwxr-x  2 root sftpgroup 4096 Mar 26 05:41 test_readwrite

Step 9, test it! We are done, restart the SSH server:

/etc/init.d/ssh restart

Step 10, connect to our SFTP server from a client, i'm using FileZilla:

Create a new connection, with the server ip of your Raspbian server, port 22, SFTP protocol, and Key file as access type, set the name of the user, "sftpuser" in my case, and set the path to the private key from the key pair we recently create in step 5.

So, if everything was correct, we are now able to navigate our /var/www server folder from a filezilla client, great!

Next story, Apache web server install...

Apache

A web server, this sounds like troubles! Well, it's not untrue, but here we go, let's take a step back and talk about something indispensable, the firewall, and prepare yourself, we' wi'll come back to this topic a lot, while installing all our server stuff. A server without a good firewall set up is like a safe without a door, right now it will probably not last till the end of the day. So let's learn something about linux firewalls!

The standard debian firewall is called iptables (for IPv4, and ip6tables for IPv6) , so we'll using that, first step install it:

apt-get install iptables iptables-persistent

The package iptables-persistent is used to make our firewall rules persistent over reboots.

Ok, now print the current iptable rules, none right now:

iptables -L

My output:

Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

And a useful command to flush all rules (like a firewall reset):

iptables -F

Now, we don't want to be lock out from our server, and playing with the firewall can lock us out really easy, so first of all we add a rule that assure us to maintain untouched all current connections (basically our actual ssh connection):

iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

In english, we tell to iptables to -A append the rule, INPUT to the input chain, -m conntrack --ctstate ESTABLISHED,RELATED relate this rule with the current connections ONLY, -j ACCEPT JUMP to accept and the connection are still in place.

If we list our rules again:

iptables -L

We'll see something new, the "door" opened for incoming current connections (our SSH connection), amazing!

Now we start to design our firewall, starting with the basics for what we already have now (SSH, SFTP and soon Apache web server), along the way we will come back and add some new rules for all the other stuff we will need on our server. Maybe it's a good idea to make yourself a new cup of coffee, or whatever you like to drink.

Let's start blocking off insecure connections, we actually are using port 22 for SSH and SFTP, and we'll want to have port 80 (http) and port 443 (https) available:

iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT

Now block all the remaining traffic:

iptables -P INPUT DROP

Allow loopback access (-I INPUT 1 place this rule first in the list, IMPORTANT):

iptables -I INPUT 1 -i lo -j ACCEPT

And do not forget to permit outgoing connections (for apt-get, web browsing, sendmail, etc..)

iptables -F OUTPUT  # remove your existing OUTPUT rules if you have some
iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -p tcp --dport 80 -m state --state NEW -j ACCEPT
iptables -A OUTPUT -p tcp --dport 443 -m state --state NEW -j ACCEPT
iptables -A OUTPUT -p tcp --dport 53 -m state --state NEW -j ACCEPT
iptables -A OUTPUT -p udp --dport 53 -m state --state NEW -j ACCEPT
iptables -A OUTPUT -p tcp --dport 25 -m state --state NEW -j ACCEPT
iptables -A OUTPUT -p tcp --sport 25 -m state --state ESTABLISHED -j ACCEPT
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -P OUTPUT DROP
iptables -P FORWARD DROP

List now rules in verbose mode:

iptables -L -v

My output:

Chain INPUT (policy DROP 1 packets, 32 bytes)
 pkts bytes target     prot opt in     out     source               destination
    8  1104 ACCEPT     all  --  lo     any     anywhere             anywhere
 6779 9556K ACCEPT     all  --  any    any     anywhere             anywhere             state RELATED,ESTABLISHED
 1087 75053 ACCEPT     tcp  --  any    any     anywhere             anywhere             tcp dpt:ssh
    0     0 ACCEPT     tcp  --  any    any     anywhere             anywhere             tcp dpt:http
    0     0 ACCEPT     tcp  --  any    any     anywhere             anywhere             tcp dpt:https

Chain FORWARD (policy DROP 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy DROP 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination
 3435  250K ACCEPT     all  --  any    any     anywhere             anywhere             state RELATED,ESTABLISHED
   13   780 ACCEPT     tcp  --  any    any     anywhere             anywhere             tcp dpt:http state NEW
    0     0 ACCEPT     tcp  --  any    any     anywhere             anywhere             tcp dpt:https state NEW
    0     0 ACCEPT     tcp  --  any    any     anywhere             anywhere             tcp dpt:domain state NEW
   21  1415 ACCEPT     udp  --  any    any     anywhere             anywhere             udp dpt:domain state NEW

We have now our basic firewall! Let's save it (do not change the saving files path /etc/iptables/rules.vX):

iptables-save > /etc/iptables/rules.v4 && ip6tables-save > /etc/iptables/rules.v6

Restart your Raspbian server and check if everything is ok, and if the rules are automatically loaded.

We are ready now to start with the Apache installation/configuration, let's do it:

apt-get install apache2

Now, from a client browser, let's check if it's working, copy in the url the ip of your Raspberry server and hit enter

Apache web server

That's it, Apache installed, and up&running! Now the configuration:

  • 1 - Hide Apache version:
nano /etc/apache2/conf-enabled/security.conf

And add/edit this lines:

ServerSignature Off
ServerTokens Prod

Save and restart apache2:

/etc/init.d/apache2 restart
  • 2 - Turn Off Directory Browsing, Disable Symbolic Links, Limit request size (to 600 Kb) and Turn Off Server Side Includes and CGI Execution
nano /etc/apache2/apache2.conf

Then edit the following lines:

<Directory /var/www/>
        LimitRequestBody 614400
        Options -FollowSymLinks -Includes -ExecCGI
        AllowOverride None
        Require all granted
</Directory>

Save and restart again.

  • 3 - Disable unnecessary modules and restart again
a2dismod autoindex
a2dismod status
/etc/init.d/apache2 restart
  • 4 - Install additional modules
apt-get install libapache2-mod-security2

ModSecurity need to be enabled:

mv /etc/modsecurity/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf
nano /etc/modsecurity/modsecurity.conf

And edit this line:

#SecRuleEngine DetectionOnly
SecRuleEngine On

Restart apache service and install the next module:

apt-get install libapache2-mod-evasive

Then append this an the end of /etc/apache2/apache2.conf:

<IfModule evasive_module>
    #optional directive (default value equals to 1024)
    DOSHashTableSize    1024

    #obligatory directives (if even one of them is not set, malfunctioning is possible)
    DOSPageCount        10
    DOSSiteCount        150
    DOSPageInterval     1
    DOSSiteInterval     1
    DOSBlockingPeriod   10
</IfModule>

Restart apache again, we got it! Now it's time for the next component, the MySQL Server!

MySQL Server

First step, install it, easy (always use strong passwords):

apt-get install mysql-server

And, to secure the install:

mysql_secure_installation

Let's test it:

mysql -u root -p

And you will enter the mysql console, perfect! Now install PHP!

PHP

Now, we have a small issue here, the latest Raspbian is based on debian Jessie, that still comes with PHP 5.6 by default (from the stable branch), but we don't want an older almost unsupported (and most insecure) PHP release, we want to install PHP 7, the last release. In order to do that we'll need to tweak a little bit our apt system, let's get to it:

nano /etc/apt/sources.list

And add at the end:

# TWEAK - Stretch (testing) branch for PHP7 install on Jessie
deb http://mirrordirector.raspbian.org/raspbian/ stretch main contrib non-free rpi

Now what we don't want is that every package is updated or installed from the stretch (testing) branch. To do this we can set some preferences that we want all packages to be selected from Jessie by default. Open up the following file /etc/apt/preferences, and add the following:

Package: *
Pin: release n=jessie
Pin-Priority: 600

Save the file and update:

apt-get update

We have it, every time we want to install something from the testing branch, we'll do it like that (this will update the apache2 package, when asked, maintain the current config files):

apt-get install -t stretch php7.0-cli php7.0-dev php-pear libapache2-mod-php7.0 php7.0-mysql php7.0-mcrypt php7.0-sqlite3 php7.0-bcmath php7.0-bz2 php7.0-curl php7.0-gd php7.0-imap php7.0-mbstring php7.0-odbc php7.0-pgsql php7.0-soap php7.0-xml php7.0-xmlrpc php7.0-zip

And, to fix some issues due to this change of repo:

apt-get install -t stretch mailutils maildir-utils sendmail-bin
apt-get install sensible-mda

This one we'll need to wait a little longer, so we have some time to clarify something here, the moment we use the testing branch (from debian stretch), we are mixing "not yet marked stable" packages in our system, this is not a good policy for a security oriented server, but an older release of php is surely a worst case scenario, so buckle up, we just passed to the next level, little bit more challenging, feels scary but don't lie, you're liking it!

Now the last module, a specific one for GnuPG for encryption:

apt-get install -t stretch gnupg libgpg-error-dev libassuan-dev

Go to a temp folder and download gpgme library:

wget https://www.gnupg.org/ftp/gcrypt/gpgme/gpgme-1.8.0.tar.bz2

Extract, configure, make && make install:

tar xvfj gpgme-1.8.0.tar.bz2 && cd gpgme-1.8.0 && ./configure

Then

make && make install

and

pecl install gnupg

last one, open /etc/php/7.0/apache2/conf.d/20-gnupg.ini

nano /etc/php/7.0/apache2/conf.d/20-gnupg.ini

and add the following line:

extension=gnupg.so

Save & close the file, and to fix a little library loading issue, open this new file:

nano /etc/ld.so.conf.d/userlib.conf

then add this line:

/usr/local/lib

Save/close the file, and rerun ldconfig to rebuild the cache:

ldconfig

Finally restart apache, and create a new file for print php info:

nano /var/www/html/info.php

Then add the following typical php code:

<?php phpinfo(); ?>

Now open from your client browser the following url: http://your_raspbian_server_ip/info.php, if everything went fine you will see the common php information page.

PHP Install

We are done with PHP installation, now we remove the info file for security reasons:

rm -i /var/www/html/info.php

This is starting to look nice!

Ok, let's hit pause for a moment, and take a better look at what we have at the moment:

service --status-all

This command will give us the complete list of services available on our server, where [ + ] means service started, [ - ] service stopped, and [ ? ] state unknown.

But let's take a deep look with another program:

apt-get install chkconfig

and

chkconfig --list

This will show us the availability of our services at all different runlevels. No room here for a runlevel class, so here some more info.

And another really powerful tool for discovering services is the systemd init system:

systemctl list-units -t service

And as we did before with netstat, let's check all active tcp connections:

netstat -atp

My output:

Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 localhost:smtp          *:*                     LISTEN      1151/exim4
tcp        0      0 192.168.1.104:22        *:*                     LISTEN      310/sshd
tcp        0      0 localhost:mysql         *:*                     LISTEN      781/mysqld
tcp        0     92 raspbian.ip.number:22   client.ip.number:port   ESTABLISHED 1188/sshd: username
tcp6       0      0 localhost:smtp          [::]:*                  LISTEN      1151/exim4
tcp6       0      0 [::]:http               [::]:*                  LISTEN      736/apache2

As you can see, we have our newly installed apache2 and mysql services listening, our active ssh connection established, and a new one, the exim4 service listening too, but hey, we do not install this exim4, what is that? Well, when we installed php7, one of his dependencies is the exim4 service for sending system information to internal users, so the system installed it automatically.

Next story, a special bonus, Ruby on Rails!

Ruby on Rails with rbenv (EXTRA BONUS - INTERMEDIATE LEVEL!!!)

Ruby on Rails is a rapid development web framework that allows web designers and developers to implement dynamic fully featured web applications. Personally, i believe that RoR is really a better option than PHP, and i'm not the only one, but anyway, search the internet and learn about it, you'll choose to have it or not on your personal server.

Let's get to work! We start as always with the dependencies:

apt-get install autoconf bison build-essential curl libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev libffi-dev libgdbm3 libgdbm-dev

and

apt-get install git-core

Right, now install, from github, rbenv:

git clone https://github.com/rbenv/rbenv.git ~/.rbenv

Adding his PATH for using the command line utility:

echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(rbenv init -)"' >> ~/.bashrc

source ~/.bashrc

Time to test it:

type rbenv

You should see this, if everything is fine:

rbenv is a function
rbenv ()
{
    local command;
    command="$1";
    if [ "$#" -gt 0 ]; then
        shift;
    fi;
    case "$command" in
        rehash | shell)
            eval "$(rbenv "sh-$command" "$@")"
        ;;
        *)
            command rbenv "$command" "$@"
        ;;
    esac
}

And remember, from time to time, to update rbenv, as it is installed from Git, we'll need to do it manually:

cd ~/.rbenv
git pull

Perfect! Now install a rbenv plugin to make life easier, ruby-build:

git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build

At this point, we have all the tools to start installing ruby and configuring it properly, so let's install Ruby!

So, here it comes our first dilemma, which version of Ruby? Well, rbenv help us with that, as it manage and organize, behind the curtain, one or multiple installed version of Ruby, cool!

We can then list all versions available at the moment:

rbenv install -l

and choose one, i picked 2.3.3 this time:

rbenv install 2.3.3

BE ADVISED, this step can take a lot, maybe a lot as in go to lunch or whatever you prefer, but a coffee will not be enough! See you in a bit then :P

OK, we are back and Ruby is installed, my output:

Installed ruby-2.3.3 to /home/your_username/.rbenv/versions/2.3.3

Last things, setting this version as default:

rbenv global 2.3.3

And test it, obviously:

ruby -v

My output:

ruby 2.3.3p222 (2016-11-21 revision 56859)

Great, now we need to configure Gems (packages that extend the functionality of Ruby), we turn off local documentation in order to have more speed, and we install a dependencies manager called bundler :

echo "gem: --no-document" > ~/.gemrc
gem install bundler

My output:

Fetching: bundler-1.15.1.gem (100%)
Successfully installed bundler-1.15.1
1 gem installed

Let's check if the Gems path is correct:

gem env home

My output:

/home/your_username/.rbenv/versions/2.3.3/lib/ruby/gems/2.3.0

Fine, Gems is correctly set up, now we install Rails:

gem install rails

This one, could take a while... When finished, as always, we check the installed version:

rails -v

And my output is:

Rails 5.1.2

SUPER! Now we need to install JavaScript Runtime, because some Rails features depends on it:

cd /tmp
\curl -sSL https://deb.nodesource.com/setup_6.x -o nodejs.sh

And we take a look at the script file we just downloaded (just in case):

less nodejs.sh

If we are satisfied and everything is correct, we quit by typing q

Ok, let's install the NodeSource Node.js v6.x repo:

cat /tmp/nodejs.sh | sudo -E bash -

Where the -E flag used here will preserve the user's existing environment variables.

Almost done, we now can simply install nodejs via apt:

apt-get install nodejs

And that's it! We can start now testing our Ruby on Rails installation! There are different options to deploy a Ruby on Rails App, we will try here to use our already installed Apache web server, so, this is what we need, the Passenger Apache Module. Debian repository comes with an older version of the libapache2-mod-passenger, so we install the correct version through gem:

# First install some dependencies
apt-get install libcurl4-openssl-dev apache2-threaded-dev

# Install Passeneger module
gem install passenger

# Install Passenger + Apache module
passenger-install-apache2-module

Now follow the instruction and the module will compile (damn fine cup of coffee time).

We need now to properly configure apache in order to have Passenger working correctly:

nano /etc/apache2/mods-available/passenger.load

And copy the line suggested from previous Passenger install instruction:

LoadModule passenger_module /home/user/.rbenv/versions/2.3.3/lib/ruby/gems/2.3.0/gems/passenger-5.1.5/buildout/apache2/mod_passenger.so

Then:

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

And copy:

<IfModule mod_passenger.c>
        PassengerRoot /home/your_user_name/.rbenv/versions/2.3.3/lib/ruby/gems/2.3.0/gems/passenger-5.1.5
        PassengerDefaultRuby /home/your_user_name/.rbenv/versions/2.3.3/bin/ruby
</IfModule>

And the last step, enable the module and restart apache:

a2enmod passenger
service apache2 restart

Fine, now let's understand how to deploy our Rails applications, but in order to do that, we first need one! As always, we will use a testing app, and with the help the previously installed tools, it'll be super easy!

We'll start creating a new directory for storing rails apps:

cd /home/your_username/ &&  mkdir your_rails_dev_folder_name

And we create a testing app:

rails new testapp --skip-bundle

Perfect, enter the directory and modify the Gemfile to install a JavaScript execution environment:

cd testapp && nano Gemfile

Now search for this line:

# gem 'therubyracer',  platforms: :ruby

uncomment it, save the file and close it.

All right, now launch the automatic install (thanks (bundler)[https://bundler.io/]):

bundle install

WOW, we are almost finished, apache is running fine with Passenger module configured, Ruby on Rails is fine tuned with rbenv, now we'll check if everything is sound, and finally, we'll create a virtual host file for testing our rails app.

So, the testing:

passenger-memory-stats

And my output:

--------- Apache processes ---------
PID  PPID  VMSize    Resident  Name
------------------------------------
855  1     12.8 MB   6.3 MB    /usr/sbin/apache2 -k start
899  855   232.1 MB  5.9 MB    /usr/sbin/apache2 -k start
900  855   230.0 MB  4.2 MB    /usr/sbin/apache2 -k start


-------- Nginx processes ---------



---- Passenger processes -----
PID   VMSize   Resident  Name
------------------------------
861   30.7 MB  8.1 MB    Passenger watchdog
866   93.9 MB  11.4 MB   Passenger core
877   39.1 MB  8.9 MB    Passenger ust-router

You can see the apache processes and the Passenger processes up and running, GREAT!

Finally, we create a new virtual host file for our testapp rails app:

cp /etc/apache2/sites-available/000-default.conf /etc/apache2/sites-available/testapp.conf
nano /etc/apache2/sites-available/testapp.conf

The file need to look like this:

<VirtualHost *:80>

        ServerAdmin webmaster@localhost
        DocumentRoot /home/your_username/rails_folder/testapp/public
        RailsEnv development

        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined

        <Directory "/home/your_username/rails_folder/testapp/public">
                Allow from all
                Options -FollowSymLinks -MultiViews
                Require all granted
        </Directory>

</VirtualHost>

Then, disable the default apache virtual host, enable the new testapp rails, and restart apache:

a2dissite 000-default
a2ensite testapp
service apache2 restart

AND THAT'S IT, PEOPLE! Open in your browser the ip of your raspbian server and check it out:

Raspbian RoR

So here we are, our server is starting to get all the pieces in place. Next story? Hide our SSH service!

Hide

This is the last last layer of security we are going to add to our SSH and SFTP services, it is some kind of advanced obfuscation technique, so not everyone will agree that it is really useful, but hey, in my opinion it can add some trouble for an attacker trying to own our server, so here we go, let's install a port knocker! And what is a port knocker? Is a special type of disguised service that listen for a specific sequence of "knocks" on a predefined list of ports, when this list of port is correctly "knocked" this service opens temporarily a specified port (our enter door to the server, the SSH port 22) in order to obtain access, and close it again after we log in. It's the same as knocking at your house door with a predefined knocking code, then someone open the door, and close it again after you are inside. So, in terms of visibility (port scanning for example), our server will be invisible, because at the question: is the SSH port listening?, the answer will be NO.

But BEWARE! Port Knocking techniques are an open debate about actual efficiency and if we can really gain some more security with it, so search the internet and read about it, then choose if you want that on your server, or not.

Port Knock

Let's get to it, install the debian standard port knocker:

apt-get install knockd

Then edit his main config file, /etc/knockd.conf, you will have something like this:

[options]
        UseSyslog

[openSSH]
       sequence    = 7000,8000,9000
       seq_timeout = 5
       command     = /sbin/iptables -A INPUT -s %IP% -p tcp --dport 22 -j ACCEPT
       tcpflags    = syn

[closeSSH]
       sequence    = 9000,8000,7000
       seq_timeout = 5
       command     = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 22 -j ACCEPT
       tcpflags    = syn

And change it to something like this:

[options]
        UseSyslog

#[openSSH]
#       sequence    = 7000,8000,9000
#       seq_timeout = 10
#       command     = /sbin/iptables -A INPUT -s %IP% -p tcp --dport 22 -j ACCEPT
#       tcpflags    = syn

#[closeSSH]
#       sequence    = 9000,8000,7000
#       seq_timeout = 10
#       command     = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 22 -j ACCEPT
#       tcpflags    = syn

[SSH]
        sequence        = 5004,1233,8732,1112,6
        seq_timeout     = 10
        cmd_timeout     = 15
        start_command   = /sbin/iptables -I INPUT 1 -s %IP% -p tcp --dport 22 -j ACCEPT
        stop_command    = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 22 -j ACCEPT
        tcpflags        = syn

Let's see, we commented out the [openSSH] and [closeSSH] blocks, and added a new block called [SSH], this is because we want to automatically close the port 22 some seconds after we opened it, we do not want to have different knocking sequences for first open and then later close the port. In our new [SSH] block we configured the port knocking sequence with a random port sequence (i've used 5004,1233,8732,1112,6, you choose yours), the time for receiving the knocks (seq_timeout), the time the system wait to close the port after the opening (cmd_timeout), then the command for open the port (start_command, an iptables rule that momentarily give us access to the port), and finally the closing command (stop_command).

Ok, so now edit another file, /etc/default/knockd and make it look like this:

################################################
#
# knockd's default file, for generic sys config
#
################################################

# control if we start knockd at init or not
# 1 = start
# anything else = don't start
#
# PLEASE EDIT /etc/knockd.conf BEFORE ENABLING
START_KNOCKD=1

# command line options
KNOCKD_OPTS="-i eth0"

That's it, restart the knockd service and test it:

/etc/init.d/knockd restart

Now, before we finish to configure the firewall and hide our SSH service, we have to make sure this is working, because is we do not configure it properly, or something go wrong, we will be closed out by our server!!! The firewall will close the port 22, and we will need to access directly to the server to fix the issue (not a problem if your server is in your room, a little worst if the server is elsewhere...) So, let's test it!

From a client machine, create the following script:

#!/bin/bash

for x in 5004,1233,8732,1112,6;
do nmap -Pn --host_timeout 201 --max-retries 0 -p $x your.rpi.server.number;
done

Change the port sequence accordingly and the server IP, and save it the script as knock_rpi.sh

If you don't have nmap installed in your client, install it, nmap

The moment of truth, run from a terminal in your client:

sh knock_rpi.sh

Then in your server print the iptables rules:

iptables -L -v

If everything went right, you will see something similar to this line at the beginning of the INPUT chain:

27  1872 ACCEPT     tcp  --  any    any     your.client.ip.number        anywhere             tcp dpt:ssh

That's it, this is the line tha knockd temporarily add to the firewall in order to let us in via port 22.

If we print the iptables rules again, this rule had disappeared because knockd command will timeout, so server hidden again.

Ok, last step, we need to delete from our firewall the rule we configured before about listening at port 22, remember? :

iptables -A INPUT -p tcp --dport 22 -j ACCEPT

In order to do that we open the file /etc/iptables/rules.v4:

nano /etc/iptables/rules.v4

And remove this line:

-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT

This is it! Restart our server and try it!

shutdown -r now

Now, if we try to connect via SSH like always, the server will not respond, because the port 22 is actually closed! In order to SSH into our server we need to "knock" before, and then ask for a ssh connection.

So:

sh knock_rpi.sh

Then later (like always):

ssh -i ~/.ssh/your_rsa_key_name -p 22 username@RPi_ip_number

And that's it, SSH service well hidden!

This will be the same for SFTP connection, "knock" before ask for connection!

Well, we have done most of the job, we almost have our secure server, we just need to secure something more, then configure our domain DNS, configure our local router, and finally start using it. But don't rush, one step at the time, next story, "Fingerprint Your Files"

Security

Let's say it, this was not so difficult in the end, we are now reaching the end of our journey, and this doesn't mean we looked at everything nor that we are now good sysadmin security experts, nothing more far from the truth actually, this field is an incredibly complex one, especially for online servers, it needs dedication, continuos update (on personal side as on machine side), imagination and a lot of hours of practice, really a lot! So, we are not experts yet, but maybe some of us will be one day, who knows. In the meantime, let's finish our basic security configuration for our Raspbian personal server, intrusion detection systems, here we go.

RKHunter

RKHunter is a rootkit protection system. Rootkits are an extremely dangerous problems for online servers, once secretly installed on servers, they permit intruders to repeatedly enter the server without been detected. In short, if a server have a unresolved vulnerability, some attacker could use it to install a rootkit, then imagine that the sysadmin of the server fix the vulnerability, the server is now secure! but the invisible rootkit is already there, so the attacker can come back whenever he/she want, through the rootkit that was installed.

So, it's a good idea to install RKhunter, it will help us to protect our system from this kinds of problems, let's do it:

apt-get install -t stretch libwww-perl

We need to install it from the testing repo because some dependencies of rkhunter was previously installed by the php installation.

apt-get install -t stretch rkhunter

This will install the rkhunter 1.4.2, let's check it:

rkhunter --versioncheck

Ok, now we perform an update of our data files, some kind of "base" information about our filesystem that rkhunter will use for checks:

rkhunter --update

Now we confir to rkhunter that this is the baseline from which to do the checks:

rkhunter --propupd

Perfect, we are ready to perform our initial run, this will probably produce some warnings, but don't worry, this is expected:

rkhunter -c --enable all --disable none

This will take his time, and will ask to press enter for execute different checks.

Ok, log saved, open it and review:

nano /var/log/rkhunter.log

Then search for "Warning", i have the following:

...
Warning: Found preloaded shared library: /usr/lib/arm-linux-gnueabihf/libarmmem.so
...
Warning: The command '/sbin/chkconfig' has been replaced by a script: /sbin/chkconfig: Perl script, ASCII text executable
...
Warning: The following processes are using deleted files:
Process: /usr/sbin/apache2    PID: 673    File: /tmp/.ZendSem.BwxJJJ
Process: /usr/sbin/mysqld    PID: 794    File: /tmp/ibI3FUpC
Process: /usr/sbin/apache2    PID: 3078    File: /tmp/.ZendSem.BwxJJJ
Process: /usr/sbin/apache2    PID: 3079    File: /tmp/.ZendSem.BwxJJJ
Process: /usr/sbin/apache2    PID: 3080    File: /tmp/.ZendSem.BwxJJJ
Process: /usr/sbin/apache2    PID: 3081    File: /tmp/.ZendSem.BwxJJJ
Process: /usr/sbin/apache2    PID: 3082    File: /tmp/.ZendSem.BwxJJJ
...
Warning: Process '/usr/sbin/knockd' (PID 366) is listening on the network.
...

Another way to perform a complete check printing warning only is:

rkhunter -c --enable all --disable none --rwo

We have now a simple example of rkhunter warning, let's configure it a little:

nano /etc/rkhunter.conf

First, we set up local mail for receiving notification when rkhunter hits a warning:

MAIL-ON-WARNING=root@localhost
MAIL_CMD=mail -s "[rkhunter] Warnings found for ${HOST_NAME}"

Then we'll fix the warnings that told us that some binary packages have been replaced by scripts:

SCRIPTWHITELIST=/sbin/chkconfig

Next, allow apache2 and mysqld process to use deleted files, this is not ALWAYS the better thing to do, but in our case, we have a clean box, no one has touched our server (at least in my case) apart from me, and we haven't open it to the internet yet, so, it is not crazy to consider this a false positive, in which case i decided to whitelist it:

ALLOWPROCDELFILE=/usr/sbin/apache2
ALLOWPROCDELFILE=/usr/sbin/mysqld

Next, we whitelist a specific rpi arm preloaded shared library that is giving us another false positive:

SHARED_LIB_WHITELIST=/usr/lib/arm-linux-gnueabihf/libarmmem.so

Next, we disable the warning about Protocol 1 which is no longer supported on OpenSSH: we do this by using the option 2 for the allow_ssh_prot (as per instructions If the 'Protocol' option has not been set in the SSH configuration file, then a value of '2' may be set here in order to suppress a warning message)

ALLOW_SSH_PROT_V1=2

And finally, the last one (in my case), we have knockd installed and listening to the network interface (our port knocker), so we need to whitelist it:

ALLOWPROCLISTEN=/usr/sbin/knockd

Ok, we check the configuration for errors:

rkhunter -C

If no errors, then we re-run a check again:

rkhunter -c --enable all --disable none --rwo

RKHunter will tell us here that the rkhunter.conf file properties has changed, fine, so we update his db (set a new baseline):

rkhunter --propupd

That's it, we are ready for the last step, enable automated checks with a CRON job. Edit file /etc/default/rkhunter

CRON_DAILY_RUN="true"
CRON_DB_UPDATE="true"
REPORT_EMAIL="root"

This will activate the rkhunter job defined in /etc/cron.daily which sources its configuration from /etc/default/rkhunter

# source our config
. /etc/default/rkhunter

We have it! RKHunter ready and running checks every day, amazing! But remember, you'll need to check regularly for messages about warnings, at least once a week, in order to keep everything in order, every new change in the system could be recognized as a warning by rkhunter, so we'll need to always take a look a keep it clean from false positives, if we want to be able in the future to recognize real bad files.

Perfect, we'll now install and configure a network intrusion detection, next story PSAD!

psad Network Intrusion Detection System

psad stands for port scan attack detection, and is a software that monitors firewall logs to determine is a scan/attack is in progress. It can alert system administrators, like rkhunter via mail, or it can take active steps to deter the threat.

As always, let's install it:

apt-get install -t stretch psad

Now the firewall config, let's add the necessary rules to our firewall (iptables) to let psad do the work:

iptables -A INPUT -j LOG && iptables -A FORWARD -j LOG

That's it, this was super easy!

Now it's the config time, open the psad config file:

nano /etc/psad/psad.conf

And start by configuring the scans detection, search and change the following:

HOSTNAME    pi; # or whatever hostname you set on your raspbian server, if you don't know it, use the "hostname" command
IPT_SYSLOG_FILE         /var/log/syslog;
IGNORE_PORTS            your_port_knocking_ports;
ENABLE_PERSISTENCE          N;
MAX_SCAN_IP_PAIRS           50000;
MIN_DANGER_LEVEL            3;
EMAIL_ALERT_DANGER_LEVEL    4;

Now implement intrusion detection, but first update psad signature definitions and restart the service:

psad --sig-update && /etc/init.d/psad restart

But before implement the intrusion detection, let's play a little, we are going to do a port scan!!! From a client run this on a terminal:

sudo nmap -PN -sS your_rpi_server_ip

Then wait for finish or stop it after a while, then run on server:

psad -S

AHHHHHHHHHH! Don't worry, it was you with your port scan doing all that. This is the current status of psad service, cool eh? A lot of info about our server network! Very good, now it's time to edit some more config:

nano /etc/psad/auto_dl

Then add:

127.0.0.1       0;
your.local.machine.ip   0; # local machine

This will exempt those ip numbers from psad intrusion detection system, good so we don't ever end locked out from our server.

Now go back to the psad main config file /etc/psad/psad.conf end edit the following:

ENABLE_AUTO_IDS         Y;
AUTO_IDS_DANGER_LEVEL       4;
AUTO_BLOCK_TIMEOUT          3600;

This will enable the auto firewall configuration, banning a specific ip for 60 minutes if detected a danger level 4 (a normal SYN scan for example), we got it!

It's testing time, from another client connected to your local network, not from the one where you have the current SSH connection open, run this command:

sudo nmap -PN -sS your_rpi_server_ip

In the meantime, close your ssh connection and reconnect, then on your server show the actual iptables rules:

iptables -S

My output:

...
N PSAD_BLOCK_FORWARD
-N PSAD_BLOCK_INPUT
-N PSAD_BLOCK_OUTPUT
...
-A PSAD_BLOCK_FORWARD -d the.scanning.client.ip/32 -j DROP
-A PSAD_BLOCK_FORWARD -s the.scanning.client.ip/32 -j DROP
-A PSAD_BLOCK_INPUT -s the.scanning.client.ip/32 -j DROP
-A PSAD_BLOCK_OUTPUT -d the.scanning.client.ip/32 -j DROP
...

As you can see psad added a new chain with new rules to our firewall, and the scanning ip number is banned now!!! YHEAAAAAAAA! It's working!

Now we'll couple psad with tripwire, and our Intrusion Detection System will become fairly good, but this is the next story.

Tripwire Intrusion Detection System

Tripwire is host-based intrusion detection system (HIDS), it collects details about our filesystem and configurations.

First, install:

apt-get install tripwire

Answer yes to everything and set the passwords it asks.

Then, similar as rkhunter, initialize the tripwire database:

tripwire --init

And we run a check saving the result into a file:

cd /etc/tripwire
sh -c 'tripwire --check | grep Filename > test_results'

We have now a starting list of tripwire complains, let's configure it good to match our system:

nano /etc/tripwire/twpol.txt

In the "Boot Scripts" section we comment the /etc/rc.boot line, since this isn't present in our raspbian system:

#        /etc/rc.boot            -> $(SEC_BIN) ;

And the same for the "Root config files" section, comment all the lines from your test_results file. In my case:

/root                           -> $(SEC_CRIT) ; # Catch all additions to /root
        /root/mail                      -> $(SEC_CONFIG) ;
        #/root/Mail                     -> $(SEC_CONFIG) ;
        #/root/.xsession-errors         -> $(SEC_CONFIG) ;
        #/root/.xauth                   -> $(SEC_CONFIG) ;
        #/root/.tcshrc                  -> $(SEC_CONFIG) ;
        #/root/.sawfish                 -> $(SEC_CONFIG) ;
        #/root/.pinerc                  -> $(SEC_CONFIG) ;
        #/root/.mc                      -> $(SEC_CONFIG) ;
        #/root/.gnome_private           -> $(SEC_CONFIG) ;
        #/root/.gnome-desktop           -> $(SEC_CONFIG) ;
        #/root/.gnome                   -> $(SEC_CONFIG) ;
        #/root/.esd_auth                        -> $(SEC_CONFIG) ;
        #/root/.elm                     -> $(SEC_CONFIG) ;
        #/root/.cshrc                   -> $(SEC_CONFIG) ;
        /root/.bashrc                   -> $(SEC_CONFIG) ;
        /root/.bash_profile            -> $(SEC_CONFIG) ;
        /root/.bash_logout             -> $(SEC_CONFIG) ;
        /root/.bash_history             -> $(SEC_CONFIG) ;
        #/root/.amandahosts             -> $(SEC_CONFIG) ;
        #/root/.addressbook.lu          -> $(SEC_CONFIG) ;
        #/root/.addressbook             -> $(SEC_CONFIG) ;
        #/root/.Xresources              -> $(SEC_CONFIG) ;
        #/root/.Xauthority              -> $(SEC_CONFIG) -i ; # Changes Inode number on login
        #/root/.ICEauthority                -> $(SEC_CONFIG) ;

Almost done, we had some complains about some files descriptors inside /proc filesystem, and this files changes all the time, so in order to avoid regular false positives, we'll remove the specific check over the general /proc folder and we'll add all directories under /proc that we want to check. Go to the "Devices & Kernel information" section and make it look like this:

        /dev            -> $(Device) ;
        /dev/pts        -> $(Device) ;
        #/proc          -> $(Device) ;
        /proc/devices           -> $(Device) ;
        /proc/net               -> $(Device) ;
        /proc/tty               -> $(Device) ;
        /proc/sys               -> $(Device) ;
        /proc/cpuinfo           -> $(Device) ;
        /proc/modules           -> $(Device) ;
        /proc/mounts            -> $(Device) ;
        /proc/filesystems       -> $(Device) ;
        /proc/interrupts        -> $(Device) ;
        /proc/ioports           -> $(Device) ;
        /proc/self              -> $(Device) ;
        /proc/kmsg              -> $(Device) ;
        /proc/stat              -> $(Device) ;
        /proc/loadavg           -> $(Device) ;
        /proc/uptime            -> $(Device) ;
        /proc/locks             -> $(Device) ;
        /proc/meminfo           -> $(Device) ;
        /proc/misc              -> $(Device) ;

And the last one, we need to comment out the /var/run and /var/lock lines so that our system does not flag normal filesystem changes by services:

        #/var/lock              -> $(SEC_CONFIG) ;
        #/var/run               -> $(SEC_CONFIG) ; # daemon PIDs
        /var/log                -> $(SEC_CONFIG) ;

DONE! With tripwire configured, first we recreate his encrypted policy:

twadmin -m P /etc/tripwire/twpol.txt

and reinitialize the database:

tripwire --init

If everything went right, we we'll have no warnings, so run a check:

tripwire --check

There we go, this will be a typical tripwire report.

Let's clean the system from sensitive information:

rm /etc/tripwire/test_results
rm /etc/tripwire/twpol.txt

Just in case we someday need to edit again the tripwire config, we'll need to temporarily recreate the plaintext file we just edited:

sh -c 'twadmin --print-polfile > /etc/tripwire/twpol.txt'

This is how we do it!

Right, we are near the end of the story, we only need to set up tripwire email notification and automate checks with CRON, like we did it for rkhunter, let's get to it:

tripwire --check | mail -s "Tripwire report for `uname -n`" your@email

This will generate a tripwire report and send it to the specified mail. Just like that!

What next then, well we add a new cron job to our cron table:

crontab -e

and we add this line:

30 03 * * * /usr/sbin/tripwire --check | mail -s "Tripwire report for `uname -n`" your@email

So, every day we will receive a report from our tripwire system, and another one from rkhunter in case it find some warnings.

But that is a lot of logs and reports, so we will install a very powerful log parser to help us organize and recover information about our system, so next story, a short one, Logwatch Log Analyzer!

Logwatch Log Analyzer

Usually, system log files are really, really long files with eventually a lot of information repeated, so in order to help us maintaining our beautiful raspbian server, we'll install here a very useful application that take all the system logs and create a nice and clean digest about our system activities (the good ones and the not so good ones), so let's install logwatch:

apt-get install logwatch

Done! Now the configuration. we'll need to, as always, edit his config file:

nano /usr/share/logwatch/default.conf/logwatch.conf

And edit the MailTo line, entering the mail where you want logwatch to send the reports:

MailTo = email@address

And that's all! So we now have a perfectly readable daily report automatically generated by logwatch, great!

If you want to test it from terminal to get a look at the reports, just type:

logwatch --detail High --mailto email@address

Wait a little bit and check your mail, there it is!

We are set and decently secured, we are at the last steps of our journey, we'll just need to secure apache with a TLS/SSL certificate from Let's Encrypt, then set up our hostnames we are going to host, and finally, correctly and securely configure our home router to have our amazing Raspbian Server available on the internet!!!!

Next story, TLS/SSL certificates.

TLS/SSL

SSL certificates are used within web servers to encrypt the traffic between the server and client, providing extra security for users accessing your application. Let's Encrypt provides an easy way to obtain and install trusted certificates for free.

Remember that in order to complete this step, you need to have already configured some domain (www.yourdomain.com) with DNS pointing to your home server (your home IP address).

So we are going to install and configure our apache server with a TLS/SSL certificate from Let's Encrypt, let's do it:

apt-get install augeas-lenses libaugeas0
apt-get install -t stretch python-certbot-apache

Now set up the SSL certificate:

certbot --apache

This will be really straightforward, the certbot mechanism will do all the work, answer his questions and you will have it!

Now try to connect to https://www.yourdomain.whatever and that's it, SSL certificate up and running.

Now, let's encrypt certificates needs to be renewed every 90 days, so the best is to automatize the check for renewal with a cronjob, open your crontab:

crontab -e

and add this line (customize the time at your wish):

00 4 * * 1 /usr/bin/certbot renew >> /var/log/le-renewal.log

That line means check, every monday at 04:00h if we need to renew our let's encrypt certificates, and if that's the case, renew them. Easy!

We have it, our server is almost complete, next story, HARDENING!

HARDENING (BONUS)

Kernel hardening and IPv6 disable, edit file /etc/sysctl.conf and add/edit:

# Turn on execshield
kernel.exec-shield=1
kernel.randomize_va_space=1
...
# Disable tcp timestamps
net.ipv4.tcp_timestamps = 0
...
#Uncomment the next two lines to enable Spoof protection (reverse-path filter)
# Turn on Source Address Verification in all interfaces to
# prevent some spoofing attacks
net.ipv4.conf.default.rp_filter=1
net.ipv4.conf.all.rp_filter=1
...
# Do not send ICMP redirects (we are not a router)
net.ipv4.conf.all.send_redirects = 0
#
# Do not accept IP source route packets (we are not a router)
net.ipv4.conf.all.accept_source_route = 0
#net.ipv6.conf.all.accept_source_route = 0
#
# Log Martian Packets
net.ipv4.conf.all.log_martians = 1
...
# Disable IPv6
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1
net.ipv6.conf.eth0.disable_ipv6 = 1

Now open file /etc/default/ntp and make it look like this:

NTPD_OPTS='-4 -g'

Reboot and enjoy!

HOME ROUTER SETTINGS

In order to be available on the internet, we'll need to open the specific ports for the specific services we want to offer to users, so, at home we have our fantastic Raspbian Server connected to the internet BEHIND our router, and even if the server is perfectly configured with his firewall, the router is, by default, completely closed for input connections, with the consequence of not letting no one reach our webpage on port 80 (http) or port 443 (https) because when we'll ask for it, our router will deny the access. This is actually good, if our router was completely open, probably our internet connection will be flooded in no time!

So, remember here that, for every port you'll open on your router, that means availability but exposure too, and it's because of it that we are trying to build a pretty secure server.

Enough chat, in short we'll need to access our router settings, in general at ip 192.168.1.1 (but now always, check your router user manual), and in the firewall section, or in the port forwarding section (it depends on the router model), we'll need to open the port for our specific server, for example, if we want to run a web server, we'll need to forward port 80 to port 80 of our Raspbian (pointing to the internal LAN server ip), and the same for port 443. Or, if we want to access via SSH from internet, we'll need to forward input port 22 to port 22 of the server internal LAN ip.

The same for every other service you'll need.

So do it, and then test your services, if everything was correct, your server is actually AVAILABLE!!! Congratulation!

Your 80€ dedicated server (80DS)

This is the finish line, we've done it, but before we say goodbye, let's just take the final step, a step that a good sysadmin at least repeat once per week:

1 - Check for updates and apply

apt-get update && apt-get dist-upgrade

2 - Update rkhunter

rkhunter --propupd

3 - Update tripwire

tripwire --init

4 - Time to go, our Raspbian Server is rock n rollin'.

Finally, it's time for the conclusions, we can say that this journey was not so easy, neither always comfortable, but damn, it was adventurous, it was not? So, like the simplest comparative between tourists and travelers, this one was indeed one of travelers, don't you think?