Skip to Content

Build a fortress for Debian based distributions in 15 minutes

The first thing to think of once the instance is delivered is, “How can I secure the instance before putting any workloads on the server?”; so we can reduce the risk of illegal accesses, data breaches, and server compromises.

This article covers how to :

  • Update root credentials and the creation of a non-root user
  • Harden the SSH configuration
  • Configure the firewall
  • Have a system up-to-date

Update the root credentials

The first thing to do is to update the _root _’s password. Login to the instance with the credentials provided.

# Connect to the server as root
ssh root@ip_address
# Modify the root's password
passwd root

Harden the SSH configuration

Limit the access to critical information by creating a user. This user replaces the root during login with SSH.

# Create a user with a HOME directory
useradd -m guest
# And then change the password
passwd guest

Then, set up the SSH key pair, on the local machine, run the following command to generate the keys.

ssh-keygen -t ecdsa -b 521 -C "$(whoami)@$(hostname)-$(date -I)"

Copy the public key to the instance.

ssh-copy-id -i ~/.ssh/id_ecdsa.pub guest@ip_address

The public key is uploaded in the file /home/guest/.ssh/authorized_keys. To avoid any unauthorized modification, the file access will only allow the guest user and block all modifications.

mkdir /home/guest/.ssh
# Protect the folder, only guest user can access to the folder
chmod 700 /home/guest/.ssh
# Protect the authorized key file
chmod 400 /home/guest/.ssh/authorized_keys
# Set the folder ownership and its content to the guest user
chown guest:guest /home/guest -R
# Block all permsission modifications on the file
chattr +i ~/.ssh/authorized_keys
# Block all permsission modifications on the folder
chattr +i ~/.ssh

To validate the configuration, try to connect with the SSH key with the following command. Keep a terminal opened with root access, in case you get lockout.

ssh -i ~/.ssh/id_ecdsa deploy@ip_address

If the connection succeeds, disconnect and come back in the root terminal. Otherwise, something is missing in the configuration performed above. Try to fix the issue in the root terminal.

Everything is set up to configure the SSH configuration, open the file with the editor of your choice /etc/ssh/sshd_config and apply the following configuration:

# Block root login by SSH
PermitRootLogin no
# Block login when the user's password is empty
PermitEmptyPasswords no
# Block graphical application to be run X11
Forwarding no
# Block all connexions by password (keys will be used instead)
PasswordAuthentication no
# Use protocol v2
Protocol 2
# Enable RSA keys authentication
RSAAuthentication yes
# Security reason (https://access.redhat.com/site/solutions/336773)
ChallengeResponseAuthentication no
# Limit the number of attempts by password to 5
MaxAuthTries 5
# Limit the number of simultaneous connexion by SSH to 5
MaxStartups 5
# Period allowed to the user to enter the password, otherwise he will be disconnected
LoginGraceTime 1m
# Disconnect the client after 600sec (10min) of inactivity
ClientAliveInterval 600
ClientAliveCountMax 0
# Allow only the guest user to connect by SSH
AllowUsers guest
# Modify the location where the public keys are stored
AuthorizedKeysFile %h/.ssh/authorized_keys

To apply the modification, restart the SSH service with the following command.

systemctl restart ssh

Configure the firewall

Create a folder where the custom scripts will be stored.

mkdir -p /homez/scripts/firewall

Create the script, firewall.sh, that will be executed on each boot run. Then open it with the editor of your choice. The policy applied for the firewall is “everything is blocked by default”.

vim /homez/scripts/firewall/firewall.sh

Copy and paste the below script, modify it according to your needs.

#!/bin/bash
# Flush the tablesiptables -F
iptables -X

# Reject all incoming and outgoing connexions, the policy applied is a "blocked by default"
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT DROP

# Reject invalid package
iptables -A INPUT -m conntrack --ctstate INVALID -j DROP​

# Reject packets incoming from adresses defined in the RFC1918 standard
iptables -A INPUT -i enp1s0 -s 10.0.0.0/8 -j DROP
iptables -A INPUT -i enp1s0 -s 169.254.0.0/16 -j DROP
iptables -A INPUT -i enp1s0 -s 127.0.0.0/8 -j DROP
iptables -A INPUT -i enp1s0 -s 224.0.0.0/4 -j DROP
iptables -A INPUT -i enp1s0 -d 224.0.0.0/4 -j DROP
iptables -A INPUT -i enp1s0 -s 240.0.0.0/5 -j DROP
iptables -A INPUT -i enp1s0 -d 240.0.0.0/5 -j DROP
iptables -A INPUT -i enp1s0 -s 0.0.0.0/8 -j DROP
iptables -A INPUT -i enp1s0 -d 0.0.0.0/8 -j DROP
iptables -A INPUT -i enp1s0 -d 239.255.255.0/24 -j DROP
iptables -A INPUT -i enp1s0 -d 255.255.255.255-j DROP
# Reject SMURF attacks
iptables -A INPUT -p icmp -m icmp --icmp-type address-mask-request -j DROP
iptables -A INPUT -p icmp -m icmp --icmp-type timestamp-request -j DROP

# Reject packets that do not initiate the communication with a SYN packet type
iptables -A INPUT -p tcp ! --syn -m state --state NEW -j DROP

# Reject incoming frament packets
iptables -A INPUT -f -j DROP

# Reject malformed NULL packets
iptables -A INPUT -p tcp --tcp-flags ALL NONE -j DROP

# Reject XMAS packets type
iptables -A INPUT -p tcp --tcp-flags ALL ALL -j DROP

# Allow established/active/in progress connexions
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A OUTPUT -m conntrack --ctstate ESTABLISHED -j ACCEPT

# Allow loopback
iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT

# ICMP (Ping)
iptables -A OUTPUT -p icmp -j ACCEPT
iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT

# SSH
iptables -A OUTPUT -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT

# DNS
iptables -A OUTPUT -p tcp --dport 53 -j ACCEPT
iptables -A OUTPUT -p udp --dport 53 -j ACCEPT


# NTP
iptables -A OUTPUT -p udp --dport 123 -j ACCEP​T

# HTTP
iptables -A OUTPUT -p tcp --dport 80 -j ACCEPT
# HTTPS
iptables -A OUTPUT -p tcp --dport 443 -j ACCEPT

Save the script and modify the script permission to be only visible and executable by the root user.

chmod 700 /homez/scripts/firewall/firewall.sh

The script needs to be executed at each boot; to do so, create a file in the /etc/systemd/system/ folder named firewall.service.

vim /etc/systemd/system/firewall.service

Copy and paste the following content.

[Unit]Description=Add firewall rules to iptables
[Service]Type=oneshotExecStart=/homez/scripts/firewall/firewall.sh
[Install]WantedBy=multi-user.target

And, activate the service

systemctl start firewall.service

To validate that your are not lock out, try to logout and login to the instance. If you can not login, reboot the instance with an hard reboot, and the firewall rules will be restored. And try to find where the issue in the script.

At any moment, it is possible to display the firewall rules with the command.

iptables -L -v -n

Now that the rules are validated enable the service to be run at each boot.

systemctl enable firewall.service

Get the latest software updates

Get the latest vulnerability fixes by upgrading the system, and the packages installed.

# Update the package list and upgrade the packages
apt-get update && apt-get upgrade
# Remove unused and orphan packages 
apt-get autoremove && apt-get autoclean