Securing the Ubuntu server is crucial for protecting every service that works over your server – especially when those services are exposed to the Internet. While steps in this article are most applicable for fresh installations of Ubuntu, they can also be applied to production servers (with appropriate maintenance windows and backups).
This article shows you how to secure the latest stable version of Ubuntu (version 16.04 LTS, also known as Xenial Xerus). After completing the steps in this article you will have a secure platform to build on. In addition to securing the server itself, we also recommend implementing a WAF and DDoS mitigation system to prevent threats from getting through in the first place. (StackPath has a 15-day free trial you can try out.)
Before taking steps to harden your Ubuntu server, you should have a basic understanding of the following:
- Common file editors like: vi, vim, and nano
- Basic directory moving (cd)
- Using SSH for connections
- File structures on Linux
- apt package manager
Steps for Hardening Ubuntu
If you already have Ubuntu installed, skip to the section titled Base System Installation. If you are installing Ubuntu for the first time, proceed to the next step.
BIOS Security Password
When hardening your operating system you have to take all aspects of security into account. That includes protecting physical access to BIOS with a strong password. Enter the BIOS settings on your server and activate this option.
Home Directory Encrypting
With Ubuntu you can protect the /home directory with encryption from the beginning of the installation process by selecting Yes in this window.
It’s essential to have a secure partitioning scheme for the operating file systems storage. Usually this means segregating partitions. (Typically you first create the partitions when you are installing the operating system for the first time.)
You can partition disks from this window. The disks are defined as follows:
- /boot contains necessary files for booting the system that contains the kernel
- /swap is the swap memory used when the RAM is full
- /home is for user files
- /usr is typically used for application software, binary files and documentation
- /var is for logs, Pid files, queues, temporary files, and content pages served by the web server
- / is for key system operation and configuration files
For security reasons it’s recommend to isolate data files from operating system files. If a file system has problems with space, it could affect another file system and server operation.
You should never automatically update Ubuntu. Updates should be done manually within proper maintenance windows. To disable automatic updates, select the No automatic updates option in this window.
Base System Installation
To harden any operating system you should start by having the minimal amount of software required for a working server. Fortunately, Ubuntu makes this easy. Instead of being pre-installed, all software, including SSH, is installed and configured separately.
Only choose what you absolutely need from this list, including standard system utilities.
Use Sudo Instead of Root
Sudo is a great security feature. With Ubuntu you don’t have to use the root user (and you don’t need it). Instead you can grant privileged roles to yourself and other users. Digital Ocean shows you how to do this.
Configure Open SSH Management (Client and Server)
When using SSH to manage servers with Ubuntu, you can perform authentication three different ways: 1) username and password authentication which is the default option, 2) private and public key authentication which is the method we’ll use, and 3) token authentication.
First install the openssh-server locally:
luise@ubuntuServerBlog:~$ sudo apt install openssh-server
Then generate authorization keys for RSA:
sudo ssh-keygen -t rsa [sudo] password for luise: Generating public/private rsa key pair. Enter file in which to save the key (/root/.ssh/id_rsa): Created directory '/root/.ssh'. Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /root/.ssh/id_rsa. Your public key has been saved in /root/.ssh/id_rsa.pub. The key fingerprint is: SHA256:tsrronalb0Qr9qCofKvQCjDPBbNw9fQJh2b51WA7al0 root@ubuntuServerBlog The key's randomart image is: +---[RSA 2048]----+ | . oo. oo | | . o=+ o... | |. + o..o.o E | | o + . .o o | |o . .. .S . | |.= .+ +o . | |o.+o B . | |=.o.+.o. | |++++.=*. | +----[SHA256]-----+
Now we’ll check the server’s public key fingerprint. Put this information in a trusted place where your users can see it. You can do this for both MD5 and SHA256 fingerprints.
For SHA256 hash:
sudo ssh-keygen -l -f /etc/ssh/ssh_host_rsa_key.pub -E SHA256 2048 SHA256:S9DwGt5nASjltxil0ReXmzSYUEewKXu881m8CKeTU+I root@ubuntuServerBlog (RSA)
For MD5 hash:
sudo ssh-keygen -l -f /etc/ssh/ssh_host_rsa_key.pub -E md5 2048 MD5:04:1a:cb:48:8e:6c:44:85:94:fd:bc:e8:65:75:3f:20 root@ubuntuServerBlog (RSA)
For connecting to the server, possible fingerprints for SSH are:
Again, put this information in a trusted place. The ECDSA algorithm comes by default with Ubuntu, but if you are going to use RSA, you need to change a line in your sshd_config file.
Edit the server file /etc/ssh/sshd_config:
sudo nano /etc/ssh/sshd_config
Comment out the following line (adding #):
HostKey /etc/ssh/ssh_host_ecdsa_key #HostKey /etc/ssh/ssh_host_ecdsa_key
Save the file, then restart the SSHD service:
sudo service ssh restart
Now connect to the Ubuntu server from the client.
On User Client
The user client could be a Windows, Linux, or Mac PC. It does not matter. In this case I will use a Linux Debian client with username
Let’s connect to the server for the first time under the server account.
From here you should check that the server fingerprint (red characters) matches the previously-created fingerprint. By default it’s an MD5 hash function:
clientluis@debianleb:~$ ssh firstname.lastname@example.org The authenticity of host '10.10.10.101 (10.10.10.101)' can't be established. RSA key fingerprint is 04:1a:cb:48:8e:6c:44:85:94:fd:bc:e8:65:75:3f:20. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added '10.10.10.101' (RSA) to the list of known hosts. email@example.com's password: Welcome to Ubuntu 16.04.1 LTS (GNU/Linux 4.4.0-31-generic i686) * Documentation: https://help.ubuntu.com * Management: https://landscape.canonical.com * Support: https://ubuntu.com/advantage 28 packages can be updated. 0 updates are security updates. *** System restart required *** Last login: Sat Sep 10 01:43:28 2016 from 10.10.10.100 luise@ubuntuServerBlog:~$
You successfully connected to the server. Now quit the server and return to your client again.
When using SSH to manage servers, you can authenticate by “user/password” or by the user client private/public key pair. The latter is more secure as it requires some to have your physical computer to access the server remotely (in addition to a password).
The following steps are for creating the keys (private/public) for the user client (“clientluis” in this case). Once the keys are created, the public key has to be copied to the server so the server knows that you can connect to it.
You should have your SSH Key pair from the previous steps. If you don’t, go ahead and create it:
ssh-keygen -t rsa Generating public/private rsa key pair. Enter file in which to save the key (/home/clientluis/.ssh/id_rsa): Created directory '/home/clientluis/.ssh'. Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /home/clientluis/.ssh/id_rsa. Your public key has been saved in /home/clientluis/.ssh/id_rsa.pub. The key fingerprint is: 23:74:8e:fd:c7:a5:99:bf:a3:22:d2:1d:47:42:20:d9 clientluis@debianleb The key's randomart image is: +--[ RSA 2048]----+ | .o.. | | ..E . | | . .. | | . = . . | | o S o . | | . o...= | | . ..o* | | . o o. .. | | . . ...oo | +-----------------+
RSA public keys will be located at /home/clientluis/.ssh/id_rsa.pub. RSA is an algorithm that uses asymmetric keys for encrypting and signing. You can share the public key with anyone you wish to communicate with, but the private key is the one that needs to be protected. In this case, the public key is used to authorize the user “clientluis” from the server.
Issue the command
ssh-copy-id to copy your public key to the server:
ssh-copy-id -i .ssh/id_rsa.pub firstname.lastname@example.org The authenticity of host '10.10.10.101 (10.10.10.101)' can't be established. RSA key fingerprint is 04:1a:cb:48:8e:6c:44:85:94:fd:bc:e8:65:75:3f:20. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added '10.10.10.101' (RSA) to the list of known hosts. email@example.com's password: Now try logging into the machine, with "ssh 'firstname.lastname@example.org'", and check in: ~/.ssh/authorized_keys to make sure we haven't added extra keys that you weren't expecting.
Next time you connect to the server you will be prompted for your passphrase (access to your private key) that was created before.
Connect to the server:
ssh email@example.com Enter passphrase for key '/home/clientluis/.ssh/id_rsa':
Now you are on the server and can configure it from a management device (desktop, laptop, etc). Check file permissions for the ssh authorized key you copied (with
ssh-copy-id). It must be owned by your user and group:
luise@ubuntuServerBlog:~$ ls -l .ssh/authorized_keys -rw------- 1 luise luise 402 Sep 4 12:22 .ssh/authorized_keys
Now you need to edit the file /etc/ssh/sshd_config:
We’ll add a line so the sshd daemon only listens on the authorized IP on the server interface. Find the line in the file that starts with
#ListenAddress, then add the following line below it with your server IP:
#ListenAddress 0.0.0.0 ListenAddress 10.10.10.101
It’s always good to have a second interface just for management. In this case you should put the Management IP address there.
Next we’ll restrict password authentication. This way your server will authenticate more securely using your authorized public key pair and, of course, a client passphrase to access your private key. You can also consider a multifactor approach with OATH-TOTP.
Find the line that starts with
#PasswordAuthentication and add the following line below it:
#PasswordAuthentication yes PasswordAuthentication no
The next three line changes will improve security further. Still working with the same files using Nano, do the following:
In the file /etc/sshd/sshd_config, include this line at the end of the file to deny X11 forwarding for SSH:
Replace the line
Loglevel INFO with the following line to record more information about SSH login attempts:
LogLevel INFO // Erase this one LogLevel VERBOSE
Replace the line
LoginGraceTime 120 with the following line to reduce the grace time for creating an SSH connection:
LoginGraceTime 120 // Erase this one LoginGraceTime 10
That was the last change we’ll make in the sshd_config file. Before moving on, save the file and exit the Nano editor.
Now restart the server SSH daemon:
sudo service ssh reload
Edit the file /etc/hosts.allow:
This file contains a list of IPs permitted to access the server via SSH.
Include the following line, which indicates the authorized client IP for management, and then save the file:
Edit the file /etc/hosts.deny:
This file contains a list of IPs that are denied SSH access.
Include the following line, then save the file.
This is an implicit deny which is a good security practice.
Add Security Login Banner
You should warn users about consequences of connecting to the server. To do this, you’ll need to edit the file /etc/issue.net:
sudo nano /etc/issue.net
Add a warning message similar to this:
*********************************************************************************************************** AUTHORIZED USERS ONLY ALL ACTIVITIES ON THE SERVER WILL BE MONITORED AND LOGGED FEDERAL ISSUES APPLY BE RESPONSIBLE FOR YOUR ACTIONS ***********************************************************************************************************
Now let’s disable the banner message from motd.
Edit the file /etc/pam.d/sshd:
sudo nano /etc/pam.d/sshd
Comment out the following two lines to avoid banner messages from motd:
session optional pam_motd.so motd=/run/motd.dynamic session optional pam_motd.so noupdate #session optional pam_motd.so motd=/run/motd.dynamic #session optional pam_motd.so noupdate
Now edit the file /etc/ssh/sshd_config:
sudo nano /etc/ssh/sshd
Uncomment the following line to show the banner message you created:
#Banner /etc/issue.net Banner /etc/issue.net
Save the file and restart the SSHD Service:
sudo service ssh restart
Next time you connect from the client machine you should see something like this:
clientluis@debianleb:~$ ssh firstname.lastname@example.org *********************************************************************************************************** AUTHORIZED USERS ONLY ALL ACTIVITIES ON THE SERVER WILL BE MONITORED AND LOGGED FEDERAL ISSUES APPLY BE RESPONSIBLE FOR YOUR ACTIONS *********************************************************************************************************** Enter passphrase for key '/home/clientluis/.ssh/id_rsa': Last login: Sat Sep 10 02:30:07 2016 luise@ubuntuServerBlog:~$
Remove Unneeded Services (Using rcconf)
By default, the base install of Ubuntu comes with a few services. You can check the services currently installed and remove what you don’t need.
List the installed network services:
luise@ubuntuServerBlog:~$ netstat -lp (Not all processes could be identified, non-owned process info will not be shown, you would have to be root to see it all.) Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 *:ssh *:* LISTEN - tcp 0 0 *:telnet *:* LISTEN - tcp 0 0 *:sunrpc *:* LISTEN - tcp6 0 0 [::]:ssh [::]:* LISTEN - tcp6 0 0 [::]:sunrpc [::]:* LISTEN - udp 0 0 *:sunrpc *:* - udp 0 0 *:kerberos4 *:* - udp 0 0 *:bootpc *:* - udp6 0 0 [::]:sunrpc [::]:* - udp6 0 0 [::]:kerberos4 [::]:* -
Uninstall Telnet (telnetd server):
sudo apt-get remove telnetd
Disable rpcbind (portmapper):
/etc/init.d$ sudo update-rc.d -f rpcbind remove perl: warning: Setting locale failed. perl: warning: Please check that your locale settings: LANGUAGE = "en_US:en", LC_ALL = (unset), LC_CTYPE = "UTF-8", LANG = "en_US.UTF-8" are supported and installed on your system. perl: warning: Falling back to a fallback locale ("en_US.UTF-8"). luise@ubuntuServerBlog:/etc/init.d$
Also remove software like the Dhcp client and ftp:
sudo apt-get remove isc-dhcp-client ftp info eject
Install the utility rcconf to help you disable unnecessary services:
sudo apt-get install rcconf
Uncheck all of the services in this window that appears. Then click Ok and exit.
Run rcconf again to check that your changes were saved. The window that appears should look like this.
Disable IPv6 (if not being used)
Check for IPv4 and IPv6:
sudo ifconfig enp0s3 Link encap:Ethernet HWaddr 08:00:27:a6:d1:95 inet addr:10.10.10.101 Bcast:10.10.10.255 Mask:255.255.255.0 inet6 addr: fd58:20a0:5afd:0:a00:27ff:fea6:d195/64 Scope:Global inet6 addr: fe80::a00:27ff:fea6:d195/64 Scope:Link
The green lines show you have an IPv6 address.
Edit the file /etc/sysctl.conf:
sudo nano /etc/sysctl.conf
Add the following line, save the file, and exit:
net.ipv6.conf.all.disable_ipv6 = 1
sysctl to load the previous line as a kernel parameter:
/etc$ sudo sysctl –p
Confirm that you no longer have IPv6 on your server – just IPv4:
/etc$ sudo ifconfig enp0s3 Link encap:Ethernet HWaddr 08:00:27:a6:d1:95 inet addr:10.10.10.101 Bcast:10.10.10.255 Mask:255.255.255.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:3035 errors:0 dropped:0 overruns:0 frame:0 TX packets:1397 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:220187 (220.1 KB) TX bytes:228137 (228.1 KB)
Add Some Network Protection
Edit the file /etc/sysctl.conf:
/etc$ sudo nano /etc/sysctl.conf
Uncomment the following lines and save the file. This is to prevent the server (at the kernel level) from receiving syn attacks, ICMP redirects, and source routed packets:
#net.ipv4.conf.default.rp_filter=1 #net.ipv4.conf.all.rp_filter=1 #net.ipv4.conf.all.accept_source_route = 0 #net.ipv6.conf.all.accept_source_route = 0 #net.ipv4.conf.all.accept_redirects = 0 #net.ipv6.conf.all.accept_redirects = 0 #net.ipv4.conf.all.send_redirects = 0 #net.ipv4.tcp_syncookies=1 net.ipv4.conf.default.rp_filter=1 net.ipv4.conf.all.rp_filter=1 net.ipv4.conf.all.accept_source_route = 0 net.ipv6.conf.all.accept_source_route = 0 net.ipv4.conf.all.accept_redirects = 0 net.ipv6.conf.all.accept_redirects = 0 net.ipv4.conf.all.send_redirects = 0 net.ipv4.tcp_syncookies=1
sysctl to load the previous line:
/etc$ sudo sysctl –p
It’s assumed that version 2 of Grub is being used.
Generate the hash for the grub password:
grub-mkpasswd-pbkdf2 Enter password: Reenter password: PBKDF2 hash of your password is grub.pbkdf2.sha512.10000.25E25776978D46E948077793B1529EB3407D85C9071C5B0C046B15361677AB61C955148693DFDCCB92ACFEBD5BE7652BAF53571AEC6E143BDD1B1845AB5A0C65.4FEC54088721BB158B3BFF87FEAAF9C9FC2FF252B35202C6927EC852BC883791E1309B43629C902167668D80479C03E4E9789900B24C07D2C546FE4838E4A4E3
Edit the file /etc/grub.d/40_custom:
/etc$ sudo nano /etc/grub.d/40_custom
At the end of the file, include the following lines to add a user and the corresponding hash password created before (the red part must be the same as the one created with
set superusers="luise" password_pbkdf2 luise
Save the file and quit.
sudo update-grub2 Generating grub configuration file ... Found linux image: /boot/vmlinuz-4.4.0-36-generic Found initrd image: /boot/initrd.img-4.4.0-36-generic Found linux image: /boot/vmlinuz-4.4.0-31-generic Found initrd image: /boot/initrd.img-4.4.0-31-generic done
Check the file /boot/grub/grub.cfg to confirm that the lines were added correctly (near the end of the file):
sudo cat /boot/grub/grub.cfg . . . # menu entries you want to add after this comment. Be careful not to change set superuser="luise" password_pbkdf2 luise
grub.pbkdf2.sha512.10000.25E25776978D46E948077793B1529EB3407D85C9071C5B0C046B15361677AB61C955148693DFDCCB92ACFEBD5BE7652BAF53571AEC6E143BDD1B1845AB5A0C65.4FEC54088721BB158B3BFF87FEAAF9C9FC2FF252B35202C6927EC852BC883791E1309B43629C902167668D80479C03E4E9789900B24C07D2C546FE4838E4A4E3 ### END /etc/grub.d/40_custom ### ### BEGIN /etc/grub.d/41_custom ### ### END /etc/grub.d/41_custom ###
Now reboot the system:
Next time you boot the system you’ll be asked for the username and password to access the Grub menu.
Harden User Accounts
Disable shell for user accounts that are not supposed to interactively connect to the server.
Edit the file /etc/passwd:
/etc$ sudo nano /etc/passwd
Add the word nologin at the end of lines for desired users:
luis2:x:1001:1001:Luis prueba,,,:/home/luis2:/bin/bash joe:x:1002:1002:Joe,3,123,456:/home/joe:/bin/bash luis2:x:1001:1001:Luis prueba,,,:/home/luis2:/bin/nologin joe:x:1002:1002:Joe,3,123,456:/home/joe:/bin/nologin
For User Monitoring
Install the acct package to track user activity on the server:
/etc$ sudo apt-get install accton
Enable logging and reporting:
/etc$ accton on
For Security Profiles
To enforce profile parameters for all users, edit the file /etc/profile:
/etc$ nano /etc/profile
Then add the following lines and save the file:
# To enforce to all users profiles HISTFILE=~/.bash_history HISTSIZE=10000 HISTFILESIZE=999999 HISTIGNORE="" HISTCONTROL="" readonly HISTFILE readonly HISTSIZE readonly HISTFILESIZE readonly HISTIGNORE readonly HISTCONTROL export HISTFILE HISTSIZE HISTFILESIZE HISTIGNORE HISTCONTROL
By default, the sudo software is already installed on Ubuntu. All users who want administrative privileges must use sudo, but you can allow specific commands to specific users. For example, you can add the following line to the file /etc/sudoers to permit the user “luis” to execute the command
ifconfig (he must enter his password):
luis ALL=(ALL) PASSWD: /sbin/ifconfig
In this case, the user “luis” can only execute the
ifconfig command and nothing else with sudo/root privileges.
Network Firewall for the Server
Ubuntu comes with a firewall manager for Iptables called ufw. Check the configuration (with Iptables) to see all traffic that is currently permitted to the server:
sudo iptables -L [sudo] password for luise: 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
You can check the status of the ufw:
sudo ufw status Status: inactive
Now we'll enable the firewall. Doing this will apply default rules, denying all incoming traffic and allowing all outgoing traffic. It’s recommended to do this directly on the server console, although the SSH connection you are using stays up as long as you do not close it:
sudo ufw enable Command may disrupt existing ssh connections. Proceed with operation (y|n)? y Firewall is active and enabled on system startup luise@ubuntuServerBlog:~$
You can check with the command
iptables –L to see how the rules were changed.
Now, permit your SSH connection. First, allow SSH only from your client host (in this case, client host is
sudo ufw allow from 10.10.10.143 to 10.10.10.101 port 22 Rules updated
By default, incoming ICMP traffic (e.g. Ping) is allowed. It is normally a good practice to block all ICMP and use it on a per-demand basis or for doing specific troubleshooting. To disable ICMP, change
DENY in the file /before.rules for the
Edit file /etc/ufw/before.rules:
sudo nano /etc/ufw/before.rules
Find the following lines and substitute the
ACCEPT string for
# ok icmp codes for INPUT -A ufw-before-input -p icmp --icmp-type destination-unreachable -j ACCEPT -A ufw-before-input -p icmp --icmp-type source-quench -j ACCEPT -A ufw-before-input -p icmp --icmp-type time-exceeded -j ACCEPT -A ufw-before-input -p icmp --icmp-type parameter-problem -j ACCEPT -A ufw-before-input -p icmp --icmp-type echo-request -j ACCEPT # 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 source-quench -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
Reset the firewall for the ICMP rules to take effect:
sudo ufw reload Firewall reloaded
Now, create the rules to permit web traffic for HTTP and HTTPS services:
sudo ufw allow from any to 10.10.10.101 port http [sudo] password for luise: Rule added luise@ubuntuServerBlog:~$ sudo ufw allow from any to 10.10.10.101 port https Rule added
Now create the rules to permit incoming web traffic to your server for HTTP and HTTPS services:
sudo ufw status Status: active To Action From -- ------ ---- 10.10.10.101 22 ALLOW 10.10.10.143 10.10.10.101 80 ALLOW Anywhere 10.10.10.101 443 ALLOW Anywhere
Activate firewall logs:
sudo ufw logging on Logging enabled
Rsyslog, a log processing system, is already installed with the base installation of Ubuntu. For sending syslog to a remote syslog server, edit the file /etc/rsyslog.conf:
sudo nano /etc/rsyslog.conf
Add the line with the hostname or IP of the remote syslog server and port UDP 514:
Remember that the syslog server (another server) must be up so that it can receive the logs from this server. In another post we show you how to redirect logs to a central syslog server.
Restart the rsyslog service:
/etc/init.d$ service rsyslog restart ==== AUTHENTICATING FOR org.freedesktop.systemd1.manage-units === Authentication is required to restart 'rsyslog.service'. Authenticating as: Luis Enrique,,, (luise) Password: ==== AUTHENTICATION COMPLETE ===
Check Security with Tiger GPL Application
sudo apt-get install tiger
Run tiger to check your system security. This process could take a few minutes:
Look, analyze, and follow recommendations indicated in the output. The results location is in the last line of the output:
09:54> Performing check of embedded pathnames... 09:54> Security report completed for ubuntuServerBlog. Security report is in `/var/log/tiger/security.report.ubuntuServerBlog.160911-09:50'.
Check the file (marked in red) and be aware of the “Alerts” outputs. Those must be treated immediately.
After following the steps above and directing the alerts generated by tiger, you'll have a secure operating system to build your web application on. As you add other software such as web servers like Apache and Nginx, and database management systems like MySQL, make sure to harden them as well.
In another tutorial we show you how to harden Apache on Ubuntu. If you have any other methods for hardening the Ubuntu server, let us know in the comment section below.