Here is the list of the different steps to configure a web server with Ubuntu 16.04. The host of our VPS is Linode. This tutorial covers the basic configuration of the server.
1. Create a Linode
Choose “Deploy an image” (or “Rebuild” if the server was already existing), then these options:
- Image: Ubuntu 16.04 TLS
- Deployment Disk Size: 24320 MB
- Swap Disk: 256 MB
Choose a root password, click on “Deploy” and boot the server.
2. Connect via SSH to the Server
In the command line, type:
When prompted, indicate the password chosen for root during the creation of the linode.
3. Update the Server
Run this command:
apt-get update && apt-get upgrade -y
4. Set the Hostname
Run this command to set the hostname (replacing the last “hostname” by the actual hostname for the server):
hostnamectl set-hostname hostname
5. Configure the Domain DNS Zone
- Click on the DNS Manager tab
- Click on “Add a domain zone”
- Select “Yes, insert a few records to get me started”, choosing the Linode previously created.
- Click on “Add a Master Zone”
6. Configure the FQDN (Fully Qualified Domain Name)
In order to get a functional FQDN (Fully Qualified Domain Name), add the hostname as a A / AAAA record in the DNS Manager zone:
hostname.domain.com (pointing to the IPv4)
hostname.domain.com (pointing to the IPv6)
7. Update /etc/hosts
Under the 2 IP addresses already there, add the IPv4 and the IPv6:
[IP ADDRESS IPv4] hostname.domain.com hostname [IP ADDRESS IPv6] hostname.domain.com hostname
126.96.36.199 hostname.domain.com hostname 2600:3c01::a123:b456:c789:d012 hostname.domain.com hostname
Separate each part of the IP – FQDN – Hostname by a Tab spacing for better formatting.
Reminder (cf. point n°6): The value you assign as your system’s FQDN should have an “A” record in DNS pointing to your Linode’s IPv4 address. For Linodes with IPv6 enabled, you should also set up a “AAAA” record in DNS pointing to your Linode’s IPv6 address.
8. Setting the Timezone
To set up the timezone, execute:
9. Set the Reverse DNS
- On the Linode dashboard, click on “Remote Access”
- Click on “Reverse DNS”
- Look up the hostname and add it as reverse DNS for both IPv4 and IPv6
10. Install Unattended-Upgrades
Install this package, which will take care of security updates automatically:
apt install unattended-upgrades
To make a more customize install and get emails with summaries of the available updates & upgrades available, check this page.
11. Add A Limited User Account
To avoid mistakes, hacking, etc. it’s preferable to create a limited user who will be able to sudo to accomplish administrative tasks.
To create a user, run this command:
To give him admin privileges:
adduser example_user sudo
Exit the terminal.
Log in as the limited user to follow the next steps.
12. Harden SSH Access
Once logged-in as the limited user, upload your RSA key to the server. The process described below is for a user who already has a private key, and is using Mac OS.
On your Linode (while signed in as your limited user):
mkdir -p ~/.ssh && sudo chmod -R 700 ~/.ssh/
From your local computer, you’ll send the SSH public key to the server.
scp ~/.ssh/id_rsa.pub email@example.com:~/.ssh/authorized_keys
Finally, back on your linode, you’ll want to set permissions for the public key directory and the key file itself:
sudo chmod 700 -R ~/.ssh && chmod 600 ~/.ssh/authorized_keys
Log out and try reconnecting with the limited user. You should get connected without entering your password.
13. Disallow Root Login Over SSH
This requires all SSH connections be by non-root users. Once a limited user account is connected, administrative privileges are accessible either by using
sudo or changing to a root shell using
14. Disable SSH Password Authentication
This requires all users connecting via SSH to use key authentication. Depending on the Linux distribution, the line
PasswordAuthentication may need to be added, or uncommented.
15. Listen On Only One Internet Protocol
The SSH daemon listens for incoming connections over both IPv4 and IPv6 by default. Unless you need to SSH into your Linode using both protocols, disable whichever you do not need.
Use the option:
AddressFamily inetto listen only on IPv4.
AddressFamily inet6to listen only on IPv6.
AddressFamily option is usually not in the
sshd_config file by default. Add it to the end of the file:
echo 'AddressFamily inet' | sudo tee -a /etc/ssh/sshd_config
16. Restart The SSH Service
To load the new configuration, type:
sudo systemctl restart sshd
17. Install Fail2Ban
Install this package, which is designed to limit brute-force attacks:
apt-get install fail2ban
Fail2ban reads its configuration files so that all .conf files are read first and .local files override any settings. Because of this, all changes to the configuration are generally done in .local files, leaving the .conf files untouched.
Navigate to the directory with all the configuration files:
Copy fail2ban.conf to fail2ban.local, commenting out all variables, and then uncomment only the options you want to modify:
sed 's/\(^[[:alpha:]]\)/# \1/' fail2ban.conf | sudo tee fail2ban.local 1&> /dev/null
Do the same with the jail configuration file:
sed 's/\(^[[:alpha:]]\)/# \1/' jail.conf | sudo tee jail.local 1&> /dev/null
This will create a copy of jail.conf with all the directives commented out. To overwrite a setting, uncomment and modify it in jail.local.
For more details and advanced customization of fail2ban, check this page.
18. Configure A Firewall
A best practice is to allow only the traffic you need, and deny everything else.
Iptables is the controller for netfilter, the Linux kernel’s packet filtering framework. FirewallD for the Fedora distribution family and UFW for the Debian family are two common iptables controllers, but this section will focus exclusively on iptables.
To view you current iptables rules, run the following command lines:
sudo iptables -L -nv
sudo ip6tables -L -nv
Iptables has no rules by default for both IPv4 and IPv6. This means that all incoming, forwarded and outgoing traffic is allowed. Here is how to set appropriate rules for a web server.
Add the rules for IPv4:
Copy, paste and save the following rules:
*filter # Allow all loopback (lo0) traffic and reject traffic # to localhost that does not originate from lo0. -A INPUT -i lo -j ACCEPT -A INPUT ! -i lo -s 127.0.0.0/8 -j REJECT # Allow ping. -A INPUT -p icmp -m state --state NEW --icmp-type 8 -j ACCEPT # Allow SSH connections. -A INPUT -p tcp --dport 22 -m state --state NEW -j ACCEPT # Allow HTTP and HTTPS connections from anywhere # (the normal ports for web servers). -A INPUT -p tcp --dport 80 -m state --state NEW -j ACCEPT -A INPUT -p tcp --dport 443 -m state --state NEW -j ACCEPT # Allow inbound traffic from established connections. # This includes ICMP error returns. -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT # Log what was incoming but denied (optional but useful). -A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables_INPUT_denied: " --log-level 7 # Reject all other inbound. -A INPUT -j REJECT # Log any traffic that was sent to you # for forwarding (optional but useful). -A FORWARD -m limit --limit 5/min -j LOG --log-prefix "iptables_FORWARD_denied: " --log-level 7 # Reject all traffic forwarding. -A FORWARD -j REJECT COMMIT
Add the rules for IPv6 :
Copy, paste and save:
*filter # Allow all loopback (lo0) traffic and reject traffic # to localhost that does not originate from lo0. -A INPUT -i lo -j ACCEPT -A INPUT ! -i lo -s ::1/128 -j REJECT # Allow ICMP -A INPUT -p icmpv6 -j ACCEPT # Allow HTTP and HTTPS connections from anywhere # (the normal ports for web servers). -A INPUT -p tcp --dport 80 -m state --state NEW -j ACCEPT -A INPUT -p tcp --dport 443 -m state --state NEW -j ACCEPT # Allow inbound traffic from established connections. -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT # Log what was incoming but denied (optional but useful). -A INPUT -m limit --limit 5/min -j LOG --log-prefix "ip6tables_INPUT_denied: " --log-level 7 # Reject all other inbound. -A INPUT -j REJECT # Log any traffic that was sent to you # for forwarding (optional but useful). -A FORWARD -m limit --limit 5/min -j LOG --log-prefix "ip6tables_FORWARD_denied: " --log-level 7 # Reject all traffic forwarding. -A FORWARD -j REJECT COMMIT
Import the rulesets into immediate use:
sudo iptables-restore < /tmp/v4 sudo ip6tables-restore < /tmp/v6
Check your Linode’s firewall rules with the v option for a verbose output:
sudo iptables -vL sudo ip6tables -vL
To continue with a LEMP web stack (Ubuntu 16.04, Nginx, MySQL, PHP), check this guide: Installing nginx, MySQL and PHP on an Ubuntu 16.04 Web Server.