How to Secure your Server
Update Your System–Frequently
Keeping your software up to date is the single biggest security precaution you can take for any operating system–be it desktop, mobile or server. Software updates range from critical vulnerability patches to minor bug fixes, and many software vulnerabilities are actually patched by the time they become public.
Automatic Security Updates
There are opposing arguments for and against automatic updates on servers. Nonetheless, CentOS, Debian, Fedora and Ubuntu can be automatically updated to various extents. Fedora’s Wiki has a good breakdown of the pros and cons, but the risk of automatic updates will be minimal if you limit them to security updates.
The practicality of automatic updates must be something which you judge for yourself because it comes down to what you do with your RentVPS. Bear in mind that automatic updates apply only to packages sourced from repositories, not self-compiled applications. You may find it worthwhile to have a test environment which replicates your production server. Updates can be applied there and reviewed for issues before being applied to the live environment.
CentOS uses yum-cron for automatic updates.
Debian and Ubuntu use unattended upgrades.
Fedora uses dnf-automatic.
Add a Limited User Account
Up to this point, you have accessed your server as the
root
user. The concern here is thatroot
has unlimited privileges and can execute any command–even one that could accidentally break your server. For this reason and others, we recommend creating a limited user account and using that at all times. Administrative tasks will be done usingsudo
to temporarily elevate your limited user’s privileges so you can administer your server without logging in as root.To add a new user, log in to your server via SSH.
CentOS / Fedora
Create the user, replacing
example_user
with your desired username, and assign a password:
1 useradd example_user && passwd example_userAdd the user to the
wheel
group for sudo privileges:
1 usermod -aG wheel example_userDebian / Ubuntu
Create the user, replacing
example_user
with your desired username. You’ll then be asked to assign the user a password.
1 adduser example_userAdd the user to the
sudo
group so you’ll have administrative privileges:
1 adduser example_user sudoWith your new user assigned, disconnect from your server as
root
:
1 exitLog back in as your new user. Replace
example_user
with your username, and the example IP address with your server’s IP address:
1 ssh example_user@203.0.113.10Now you can administer your server from your new user account instead of
root
. Nearly all superuser commands can be executed withsudo
(example:sudo iptables -L
) and those commands will be logged to/var/log/auth.log
.Harden SSH Access
By default, password authentication is used to connect to your server via SSH. A cryptographic key pair is more secure because a private key takes the place of a password, which is generally much more difficult to brute-force. In this section we’ll create a key pair and configure the server to not accept passwords for SSH logins.
Create an Authentication Key-pair
This is done on your local computer, not your server, and will create a 4096-bit RSA key pair. During creation, you will be given the option to encrypt the private key with a passphrase. This means that the it cannot be used without entering the passphrase, unless you save it to your local desktop’s keychain manager. We suggest you do use the key pair with a passphrase, but if unwanted, leave the field blank when prompted.
Linux / OS X
If you’ve already created an RSA keypair, this command will overwrite it, potentially locking you out of other systems. If you’ve already created a key pair, skip this step. To check for existing keys, run
ls ~/.ssh/id_rsa*
.
1 ssh-keygen -b 4096Press Enter to use the default names
id_rsa
andid_rsa.pub
in/home/your_username/.ssh
before entering your passphrase.Windows
This can be done using PuTTY as outlined in our guide: Use Public Key Authentication with SSH (coming soon).
Upload the public key to your server. Replace
example_user
with the name of the user you plan to administer the server as, and 63.223.99.2 with your Sentris’ IP address.Linux
From your local computer:
1 ssh-copy-id example_user@63.223.99.2OS X
On your server (while signed in as your limited user):
1 mkdir -p ~/.ssh && sudo chmod -R 700 ~/.ssh/From your local computer:
1 scp ~/.ssh/id_rsa.pub example_user@63.223.99.2:~/.ssh/authorized_keys
ssh-copy-id
is available in Homebrew if you prefer it over SCP. Install withbrew install ssh-copy-id
.Windows
Option 1: This can be done using WinSCP. In the login window, enter your server’s public IP address as the hostname, and your non-root username and password. ClickLogin to connect.
Once WinSCP has connected, you’ll see two main sections. The section on the left shows files on your local computer and the section on the right shows files on your server. Using the file explorer on the left, navigate to the file where you’ve saved your public key, select the public key file, and click Upload in the toolbar above.
You’ll be prompted to enter a path where you’d like to place the file on your server. Upload the file to
/home/example_user/.ssh/authorized_keys
, replacingexample_user
with your username.Option 2: Copy the public key directly from the PuTTY key generator into the terminal emulator connected to your server (as a non-root user):
1 mkdir ~/.ssh; nano ~/.ssh/authorized_keysThe above command will open a blank file called
authorized_keys
in a text editor. Copy the public key into the text file, making sure it is copied as a single line exactly as it was generated by PuTTY. Press CTRL+X, then Y, then Enter to save the file.Finally, you’ll want to set permissions for the public key directory and the key file itself:
1 sudo chmod 700 -R ~/.ssh && chmod 600 ~/.ssh/authorized_keysThese commands provide an extra layer of security by preventing other users from accessing the public key directory as well as the file itself. For more information on how this works, see our guide on how to modify file permissions (coming soon).
Now exit and log back into your server. If you specified a passphrase for your private key, you’ll need to enter it.
SSH Daemon Options
Disallow root logins 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 usingsu -
.
- /etc/ssh/sshd_config
1 2 3 # Authentication: ... PermitRootLogin noDisable 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 by removing the leading#
.
- /etc/ssh/sshd_config
1 2 # Change to no to disable tunnelled clear text passwords PasswordAuthentication noYou may want to leave password authentication enabled if you connect to your server from many different computers. This will allow you to authenticate with a password instead of generating and uploading a key pair for every device.
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 server using both protocols, disable whichever you do not need. This does not disable the protocol system-wide, it is only for the SSH daemon.
Use the option:
AddressFamily inet
to listen only on IPv4.AddressFamily inet6
to listen only on IPv6.The
AddressFamily
option is usually not in thesshd_config
file by default. Add it to the end of the file:
1 echo 'AddressFamily inet' | sudo tee -a /etc/ssh/sshd_configRestart the SSH service to load the new configuration.
If you’re using a Linux distribution which uses systemd (CentOS 7, Debian 8, Fedora, Ubuntu 15.10+)
1 sudo systemctl restart sshdIf your init system is SystemV or Upstart (CentOS 6, Debian 7, Ubuntu 14.04):
1 sudo service ssh restartUse Fail2Ban for SSH Login Protection
Fail2Ban is an application which bans IP addresses from logging into your server after too many failed login attempts. Since legitimate logins usually take no more than three tries to happen (and with SSH keys, no more than one), a server being spammed with unsuccessful logins indicates attempted malicious access.
Fail2Ban can monitor a variety of protocols including SSH, HTTP, and SMTP. By default, Fail2Ban monitors SSH only, and is a helpful security deterrent for any server since the SSH daemon is usually configured to run constantly and listen for connections from any remote IP address.
For complete instructions on installing and configuring Fail2Ban, see our guide: Securing Your Server with Fail2ban (coming soon).
Remove Unused Network-Facing Services
Most Linux distributions install with running network services which listen for incoming connections from the internet, the loopback interface, or a combination of both. Network-facing services which are not needed should be removed from the system to reduce the attack surface of both running process and installed packages.
Determine Running Services
To see your server's running network services:
1 sudo netstat -tulpnIf netstat isn’t included in your Linux distribution by default, install the package
net-tools
.Using Debian 8 as an example, the output should look similar to this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:111 0.0.0.0:* LISTEN 7315/rpcbind tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 3277/sshd tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 3179/exim4 tcp 0 0 0.0.0.0:42526 0.0.0.0:* LISTEN 2845/rpc.statd tcp6 0 0 :::48745 :::* LISTEN 2845/rpc.statd tcp6 0 0 :::111 :::* LISTEN 7315/rpcbind tcp6 0 0 :::22 :::* LISTEN 3277/sshd tcp6 0 0 ::1:25 :::* LISTEN 3179/exim4 udp 0 0 127.0.0.1:901 0.0.0.0:* 2845/rpc.statd udp 0 0 0.0.0.0:47663 0.0.0.0:* 2845/rpc.statd udp 0 0 0.0.0.0:111 0.0.0.0:* 7315/rpcbind udp 0 0 192.0.2.1:123 0.0.0.0:* 3327/ntpd udp 0 0 127.0.0.1:123 0.0.0.0:* 3327/ntpd udp 0 0 0.0.0.0:123 0.0.0.0:* 3327/ntpd udp 0 0 0.0.0.0:705 0.0.0.0:* 7315/rpcbind udp6 0 0 :::111 :::* 7315/rpcbind udp6 0 0 fe80::f03c:91ff:fec:123 :::* 3327/ntpd udp6 0 0 2001:DB8::123 :::* 3327/ntpd udp6 0 0 ::1:123 :::* 3327/ntpd udp6 0 0 :::123 :::* 3327/ntpd udp6 0 0 :::705 :::* 7315/rpcbind udp6 0 0 :::60671 :::* 2845/rpc.statdNetstat tells us that services are running for Remote Procedure Call (rpc.statd and rpcbind), SSH (sshd), NTPdate (ntpd) and Exim (exim4).
TCP
See the Local Address column of the netstat readout. The process
rpcbind
is listening on0.0.0.0:111
and:::111
for a foreign address of0.0.0.0:*
or:::*
. This means that it’s accepting incoming TCP connections from other RPC clients on any external address, both IPv4 and IPv6, from any port and over any network interface. We see similar for SSH, and that Exim is listening locally for traffic from the loopback interface, as shown by the127.0.0.1
address.UDP
UDP sockets are stateless, meaning they are either open or closed and every process’s connection is independent of those which occurred before and after. This is in contrast to TCP connection states such as LISTEN, ESTABLISHED and CLOSE_WAIT.
Our netstat output shows that NTPdate is: 1) accepting incoming connections on the server’s public IP address; 2) communicates over localhost; and 3) accepts connections from external sources. These are over port 123, and both IPv4 and IPv6. We also see more sockets open for RPC.
Determine Which Services to Remove
If you were to do a basic TCP and UDP nmap scan of your server without a firewall enabled, SSH, RPC and NTPdate would be present in the result with ports open. By configuring a firewall you can filter those ports, with the exception of SSH because it must allow your incoming connections. Ideally, however, the unused services should be disabled.
You will likely be administering your server primarily through an SSH connection, so that service needs to stay. As mentioned above, RSA keys and Fail2Ban can help protect SSH.
NTP is necessary for your server’s timekeeping but there are alternatives to NTPdate. If you prefer a time synchronization method which does not hold open network ports, and you do not need nanosecond accuracy, then you may be interested in replacing NTPdate with OpenNTPD.
Exim and RPC, however, are unnecessary unless you have a specific use for them, and should be removed.
This section focused on Debian 8. Different Linux distributions have different services enabled by default. If you are unsure of what a service does, do an internet search to understand what it is before attempting to remove or disable it.
Uninstall the Listening Services
How to remove the offending packages will differ depending on your distribution’s package manager.
Arch
1 sudo pacman -Rs package_nameCentOS
1 sudo yum remove package_nameDebian / Ubuntu
1 sudo apt-get purge package_nameFedora
1 sudo dnf remove package_nameRun
sudo netstat -tulpn
again. You should now only see listening services for SSH (sshd) and NTP (ntpdate, network time protocol).Configure a Firewall
Using a firewall to block unwanted inbound traffic to your server is a highly effective security layer. By being very specific about the traffic you allow in, you can prevent intrusions and network mapping from outside your LAN. 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. Iptables is included in most Linux distros by default but is considered an advanced method of firewall control. Consequently, several projects exist to control iptables in a more user-friendly way.
FirewallD for the Fedora distribution family and UFW for the Debian family are the two common iptables controllers.
View Your Current iptables Rules
IPv4:
1 sudo iptables -LIPv6:
1 sudo ip6tables -LIptables has no rules by default for both IPv4 and IPv6. As a result, on a newly created server you will see what is shown below–three empty chains without any firewall rules. This means that all incoming, forwarded and outgoing traffic is allowed. It’s important to limit inbound and forwarded traffic to only what’s necessary.
1 2 3 4 5 6 7 8 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 destinationBasic iptables Rulesets for IPv4 and IPv6
Appropriate firewall rules depend almost entirely on the services being run. Below are iptables rulesets to secure your server if you’re running a web server.
These are given as an example!
A real production web server may require more or less configuration and these rules would not be appropriate for a file or database server, Minecraft or VPN server, etc. Iptables rules can always be modified or reset later, but these basic rulesets serve only as a beginning demonstration.
IPv4
- /tmp/v4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 *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 which 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 COMMITIPv6
If you would like to supplement your web server’s IPv4 rules with IPv6 too, this ruleset will allow HTTP/S access and all ICMP functions.
- /tmp/v6
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 *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 which 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 COMMITAPT attempts to resolve mirror domains to IPv6 as a result of
apt-get update
. If you choose to entirely disable and deny IPv6, this will slow down the update process for Debian and Ubuntu because APT waits for each resolution to time out before moving on.To remedy this, uncomment the line
precedence ::ffff:0:0/96 100
in/etc/gai.conf
.How these IPv4 and IPv6 rules are deployed differs among the various Linux distros.
Arch Linux
Create the files
/etc/iptables/iptables.rules
and/etc/iptables/ip6tables.rules
. Paste the rulesets above into their respective files.Import the rulesets into immediate use:
1 2 sudo iptables-restore < /etc/iptables/iptables.rules sudo ip6tables-restore < /etc/iptables/ip6tables.rulesIptables does not run by default in Arch. Enable and start the systemd units:
1 2 sudo systemctl start iptables && sudo systemctl start ip6tables sudo systemctl enable iptables && sudo systemctl enable ip6tablesApply the
pre-network.conf
fix from the ArchWiki, so iptables starts before the network is up.For more info on using iptables in Arch, see its Wiki entries for iptables and a Simple Stateful Firewall.
CentOS / Fedora
CentOS 6 or Fedora 19 and below
Create the files
/tmp/v4
and/tmp/v6
. Paste the rulesets above into their respective files.Import the rules from the temporary files:
1 2 sudo iptables-restore < /tmp/v4 sudo ip6tables-restore < /tmp/v6Save the rules:
1 2 sudo service iptables save sudo service ip6tables saveFirewall rules are saved to
/etc/sysconfig/iptables
and/etc/sysconfig/ip6tables
.Remove the temporary rule files:
1 sudo rm /tmp/{v4,v6}CentOS 7 or Fedora 20 and above
In these distros, FirewallD is used to implement firewall rules instead of controlling iptables directly.
If you would prefer to use iptables, FirewallD must first be stopped and disabled.
1 sudo systemctl stop firewalld.service && sudo systemctl disable firewalld.serviceInstall
iptables-services
and enable iptables:
1 2 3 sudo yum install iptables-services sudo systemctl enable iptables && sudo systemctl enable ip6tables sudo systemctl start iptables && sudo systemctl start ip6tablesCreate the files
/tmp/v4
and/tmp/v6
. Paste the rulesets above into their respective files.Import the rulesets into immediate use:
1 2 sudo iptables-restore < /tmp/v4 sudo ip6tables-restore < /tmp/v6Save each ruleset:
1 2 sudo service iptables save sudo service ip6tables saveRemove the temporary rule files:
1 sudo rm /tmp/{v4,v6}For more information on using iptables and FirewallD in CentOS and Fedora, see these pages:
CentOS Wiki: iptables
Fedora Project Wiki: FirewallD
Fedora Project Wiki: How to Edit iptables Rules
Red Hat Security Guide: Using Firewalls
Debian / Ubuntu
UFW is the iptables controller included with Ubuntu but is also available in Debian’s repositories.
Create the files
/tmp/v4
and/tmp/v6
. Paste the above rulesets into their respective files.Import the rulesets into immediate use:
1 2 sudo iptables-restore < /tmp/v4 sudo ip6tables-restore < /tmp/v6Iptables-persistent automates loading iptables rules on boot for Debian and Ubuntu. Install it from the distro repositories:
1 sudo apt-get install iptables-persistentYou’ll be asked if you want to save the current IPv4 and IPv6 rules. Answer
yes
to each prompt.Remove the temporary rule files:
1 sudo rm /tmp/{v4,v6}Verify iptables Rulesets
Recheck your server’s firewall rules with the
v
option for a verbose output:
1 2 sudo iptables -vL sudo ip6tables -vLThe output for IPv4 rules should show:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 Chain INPUT (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination 0 0 ACCEPT all -- lo any anywhere anywhere 0 0 REJECT all -- !lo any loopback/8 anywhere reject-with icmp-port-unreachable 0 0 ACCEPT icmp -- any any anywhere anywhere icmp destination-unreachable 0 0 ACCEPT icmp -- any any anywhere anywhere icmp echo-request 0 0 ACCEPT icmp -- any any anywhere anywhere icmp time-exceeded 0 0 ACCEPT tcp -- any any anywhere anywhere tcp dpt:ssh state NEW 0 0 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 all -- any any anywhere anywhere state RELATED,ESTABLISHED 0 0 LOG all -- any any anywhere anywhere limit: avg 5/min burst 5 LOG level debug prefix "iptables_INPUT_denied: " 0 0 REJECT all -- any any anywhere anywhere reject-with icmp-port-unreachable Chain FORWARD (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination 0 0 LOG all -- any any anywhere anywhere limit: avg 5/min burst 5 LOG level debug prefix "iptables_FORWARD_denied: " 0 0 REJECT all -- any any anywhere anywhere reject-with icmp-port-unreachable Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destinationOutput for IPv6 rules will look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Chain INPUT (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination 0 0 ACCEPT all lo any anywhere anywhere 0 0 REJECT all !lo any localhost anywhere reject-with icmp6-port-unreachable 0 0 ACCEPT ipv6-icmp any any anywhere anywhere 0 0 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 all any any anywhere anywhere state RELATED,ESTABLISHED 0 0 LOG all any any anywhere anywhere limit: avg 5/min burst 5 LOG level debug prefix "ip6tables_INPUT_denied: " 0 0 REJECT all any any anywhere anywhere reject-with icmp6-port-unreachable Chain FORWARD (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination 0 0 LOG all any any anywhere anywhere limit: avg 5/min burst 5 LOG level debug prefix "ip6tables_FORWARD_denied: " 0 0 REJECT all any any anywhere anywhere reject-with icmp6-port-unreachable Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destinationYour firewall rules are now in place and protecting your server. Remember, you may need to edit these rules later if you install other packages which require network access.
Insert, Replace or Delete iptables Rules
Iptables rules are enforced in a top-down fashion, so the first rule in the ruleset is applied to traffic in the chain, then the second, third and so on. This means that rules can not necessarily be added to a ruleset with
iptables -A
orip6tables -A
. Instead, rules must be inserted withiptables -I
orip6tables -I
.Insert
Inserted rules need to be placed in the correct order with respect other rules in the chain. To get a numerical list of your iptables rules:
1 sudo iptables -L --line-numbersFor example, let’s say we want to insert a rule to the ruleset above which accepts incoming port 8080 connections. We’ll add it as rule 9 to the INPUT chain, following the web traffic rules.
1 sudo iptables -I INPUT 9 -p tcp --dport 8080 -j ACCEPTIf you now run
sudo iptables -L
again, you’ll see the new rule in the output.Replace
Replacing a rule is similar to inserting but instead uses
iptables -R
. For example, let’s say you want to reduce the logging of denied entires to only 3 per minute, down from 5 in the original ruleset. The LOG rule is the 11th in the INPUT chain:
1 sudo iptables -R INPUT 11 -m limit --limit 3/min -j LOG --log-prefix "iptables_INPUT_denied: " --log-level 7Delete
Deleting a rule is also done using the rule number. For example, to delete the rule we just inserted for server Longview:
1 sudo iptables -D INPUT 9Editing rules does not automatically save them! To accomplish this, revisit the area above for your distro to save your iptables edits so they’re loaded on reboots.