Installing and Hardening an Ubuntu Server
This guide will help you install, configure, and secure a golden image for your Ubuntu servers.
Requirements
This guide assumes you know the basics of installing an operating system on a computer.
Installing the Ubuntu Server
This should be a pretty standard answer the questions and follow the directions on screen. If you want a static IP address you can set it here, but I like to set a static address through DHCP instead of on the actual server. I also like to use LVM for the disk set ups.
Update and Configure
After you've finished installing the OS, it's time to connect to the server and begin updates and a base configuration. I connect through SSH, but you can do it the physical box if you want.
Fix Disk Size
I've found that if you don't make any changes to the default disk settings, you fill up the disk very quickly. Let's change that first, we need to resize the logical volume to use all the existing and free space of the volume group.
sudo lvm
lvextend -l +100%FREE /dev/ubuntu-vg/ubuntu-lv
exit
And then, we need to resize the file system to use the new available space in the logical volume. After that's done, we should make sure that it shows the new disk size.
sudo resize2fs /dev/ubuntu-vg/ubuntu-lv
df -h
Update the Server
Next, let's get the latest updates for the server and remove any space wasters.
sudo apt update && sudo apt -y upgrade && sudo apt -y autoremove
Timezone, Charset, Unattended Upgrades
Now we need to configure the timezone and our charset. If you want to enable unattended upgrades, now is a good time to do that as well.
sudo dpkg-reconfigure tzdata && locale-gen en_US.UTF-8 && dpkg-reconfigure locales && dpkg-reconfigure -plow unattended-upgrades
Hostname
Check your hostname to make sure it's set correctly.
hostnamectl
If the previous command returned the wrong hostname, you can run the following command to change it permanently. Change <hostname> to what you want the new hostname to be, without the <>.
sudo hostnamectl set-hostname <hostname>
If you change the hostname, it's a good idea to update the hosts file with the new hostname.
/etc/hosts
127.0.0.1 <FQDN> <hostname> localhost
...
Securing the Server
ICMP Requests
Now we can begin securing the server, first we should block ICMP requests.
/etc/sysctl.d/50-ip-sec.conf
# Disable Source Routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
net.ipv6.conf.default.accept_source_route = 0
# Disable acceptance of all ICMP redirected packets on all interfaces
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv6.conf.default.accept_redirects = 0
# Disable send IPv4 redirect packets
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
# Set Reverse Path Forwarding to strict mode as defined in RFC 3704
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# Ignore ICMP broadcast requests
net.ipv4.icmp_echo_ignore_broadcasts = 1
# Block pings
net.ipv4.icmp_echo_ignore_all = 1
# Syn flood help
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 2048
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syn_retries = 5
# Log suspicious martian packets
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.log_martians=1
net.ipv4.icmp_ignore_bogus_error_responses = 1
# Disable IPv6 auto config
net.ipv6.conf.default.accept_ra=0
net.ipv6.conf.default.autoconf=0
net.ipv6.conf.all.accept_ra=0
net.ipv6.conf.all.autoconf=0
net.ipv6.conf.eth0.accept_ra=0
net.ipv6.conf.eth0.autoconf=0
# Disable TCP Timestamps
net.ipv4.tcp_timestamps=0
Spoofing Attacks
Next we should block spoofing attacks.
/etc/sysctl.conf
...
# Do not accept ICMP redirects (prevent MITM attacks)
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
# _or_
# Accept ICMP redirects only for gateways listed in our default
# gateway list (enabled by default)
net.ipv4.conf.all.secure_redirects = 0
#
# 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
...
Configuring the Firewall
Next we should set up a firewall. Ubuntu comes with UFW, which is what I'll be using. If you're using SSH, make sure to open that port before you enable the firewall.
sudo ufw default deny incoming
sudo ufw allow 22
sudo ufw enable
We can check the status of the firewall, and see what we're allowing through by running the following.
sudo ufw status
Now we'll drop all ICMP connections at the firewall as well.
/etc/ufw/before.rules
...
# ok icmp codes for INPUT
-A ufw-before-input -p icmp --icmp-type destination-unreachable -j DROP
-A ufw-before-input -p icmp --icmp-type time-exceeded -j DROP
-A ufw-before-input -p icmp --icmp-type parameter-problem -j DROP
-A ufw-before-input -p icmp --icmp-type echo-request -j DROP
...
Installing Fail2Ban
We'll want to set up a way to ban IP addresses that attempt to connect to your server but fail repeatedly.
sudo apt install fail2ban
The default settings work for most set ups, but make any changes you feel necessary. Run the following to see who is currently blocked.
sudo fail2ban-client status
sudo fail2ban-client status sshd
Securing SSH
Creating a RSA Key Pair
I use an RSA key pair to connect to my servers through SSH. If you’ve never set up a RSA key pair, continue from here. If you have, skip ahead to copy the RSA key. On the machine you connect from, run the following to generate an RSA key pair.
ssh-keygen -b 4096
Copy the Key to the Server
Next we need to copy the pubic key to the server. I like to use ssh-copy-id if it's available, but that won't be possible through PowerShell if you're using Windows.
Bash/Terminal
ssh-copy-id <username>@<hostname_or_ip_address>
Windows Powershell
cat ~/.ssh/id_rsa.pub | ssh <username>@<hostname_or_ip_address> "mkdir -p ~/.ssh && touch ~/.ssh/authorized_keys && chmod -R go= ~/.ssh && cat >> ~/.ssh/authorized_keys"
You should now be able to connect to your server with your key.
Disable Password Authentication
You should now be able to ssh into your server without being prompted for your password. If you set up a passphrase for your key, you will be prompted for it.
We now should disable password authentication for ssh entirely. Locate the following line and change it.
/etc/ssh/sshd_config
...
PasswordAuthentication no
...
All that's left is to restart the SSH service. Please note that you will be disconnected if you are currently connected to your server over ssh.
sudo systemctl restart ssh
I know that it’s also a good idea to change the default port that ssh runs on, but I don’t since I don’t let that port talk to anything outside my network. You can if you want though. I also like to restart a fresh server after this whole process, but that's not always necessary.