A firewall is a piece of software or hardware which filters network traffic coming in or out of a system. It's important to setup at least a software firewall on a server, so that you can control which applications can be publicly accessed, which can only be accessed from certain IP ranges, as well as blocking IPs / IP ranges which are causing problems for you.
For additional security tips, we recommend checking out our other article about securing your Linux server after you've finished setting up your firewall.
We have virtual servers starting from just US$0.99/mo, and dedicated servers starting from as low as US$50/mo
Unlike other hosts, we don't ask for any personal details - only a name (can be an alias / username), and an e-mail address so we can send you your server details and renewal invoices.
We also accept several different cryptocurrencies with our own in-house payment processor - no third parties involved in processing your payments.
At the time of writing, we currently accept: Bitcoin (BTC), Litecoin (LTC), Monero (XMR), Dogecoin (DOGE), HIVE, and HBD
Managing UFW or IPTables involves a lot of commands which must be ran as root.
To avoid having to constantly prepend sudo
to your commands, we strongly advise that you login as the root
user, which you can generally do
from the standard user that you login as (e.g. ubuntu
/ debian
user) by running this command:
sudo su -
Your shell prompt should now show that you are root:
root@myserver:~ #
You can now run commands that require root, without needing to prepend sudo
If for some reason, you don't want to become the root user, or your specific setup does not allow you to have a root shell and you need to run everything via sudo, then you'll need to remember to prepend sudo
to most commands listed in this section
UFW is one of the most user friendly options for firewalling your server, but may hold back advanced system administrators who want to setup complex firewall rules.
If you require advanced / complex firewall rules, you may prefer using IPTables directly, rather than UFW.
For most standard server usecases however, UFW should be perfectly fine.
UFW may or may not be already installed on your system, it doesn't hurt to run the install commands to check though, and this will also update UFW if it's out of date.
Ubuntu / Debian based distros:
sudo apt update
sudo apt install ufw
Fedora / CentOS / RHEL based distros:
sudo dnf install ufw
# On older versions of Fedora/CentOS/RHEL
# you may need to use yum instead of dnf
sudo yum install ufw
If you've changed your SSH port, you'll want to update the OpenSSH UFW profile with your new port number.
If you haven't changed your SSH port, you can skip this.
(Please login as root via sudo su -
as mentioned earlier in this guide before running commands)
First you'll need to open the profile in a text editor such as nano:
nano /etc/ufw/applications.d/openssh-server
Inside this file, you should see something like this:
[OpenSSH]
title=Secure shell server, an rshd replacement
description=OpenSSH is a free implementation of the Secure Shell protocol.
ports=22/tcp
You'll need to change the ports=22/tcp
line, replacing 22 with the port you changed SSH to use.
For example, if your SSH port is 2222, you'd change the ports line to ports=2222/tcp
- so the final file may look like:
[OpenSSH]
title=Secure shell server, an rshd replacement
description=OpenSSH is a free implementation of the Secure Shell protocol.
ports=2222/tcp
Assuming you're using nano
to edit it, press CTRL-X
to exit, press Y
when it asks if you'd like to save the file, and then hit enter
to accept the default save path.
To confirm the file was properly updated, we can use cat
to view the contents of the file:
cat /etc/ufw/applications.d/openssh-server
Now we need to run the following command to reload the UFW profile:
ufw app update OpenSSH
Then we can check to make sure it's updated correctly by running the following command to view the live profile:
ufw app info OpenSSH
It should show the updated ports at the bottom:
Profile: OpenSSH
Title: Secure shell server, an rshd replacement
Description: OpenSSH is a free implementation of the Secure Shell protocol.
Port:
2222/tcp
Now you've successfully updated your OpenSSH UFW profile, and can continue with the guide :)
Now that UFW is installed, and you've adjusted the SSH UFW profile (if needed), we can start using it!
(Please login as root via sudo su -
as mentioned earlier in this guide before running commands)
First, we want to make sure SSH is whitelisted so that you don't lose SSH access when it's turned on:
ufw allow OpenSSH
It should display Rules updated
if it worked correctly. (If it shows an error, please look up the error on a search engine to check what you should do)
If there are any other important services that you need to ensure have their port open and don't get disrupted, you can use the following command to allow their ports:
# Assuming the service is on port 5555, you'd run the following command:
ufw allow 5555
Now let's enable the firewall:
ufw enable
It will warn you that enabling the firewall may disrupt SSH - assuming you've correctly allowed OpenSSH in the previous steps, your SSH should be unaffected, so you can hit Y
to continue:
root@ufw-test:~# ufw enable
Command may disrupt existing ssh connections. Proceed with operation (y|n)? y
Firewall is active and enabled on system startup
To check the firewall is enabled and which rules are in the firewall, you can use ufw status
like so:
root@ufw-test:~# ufw status
Status: active
To Action From
-- ------ ----
OpenSSH ALLOW Anywhere
5555 ALLOW Anywhere
OpenSSH (v6) ALLOW Anywhere (v6)
5555 (v6) ALLOW Anywhere (v6)
You should now have a basic firewall setup, which will allow traffic to your SSH port, and any other applications which you have allowed their port.
You can get a more detailed report from UFW using ufw status verbose
which will show you the default input/output policy for traffic without rules, whether logging is enabled, and will show ports for application profiles, rather than just their name:
root@ufw-test:~# ufw status verbose
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip
To Action From
-- ------ ----
2222/tcp (OpenSSH) ALLOW IN Anywhere
5555 ALLOW IN Anywhere
2222/tcp (OpenSSH (v6)) ALLOW IN Anywhere (v6)
5555 (v6) ALLOW IN Anywhere (v6)
To assist in removing/ordering firewall rules, you can request a numbered list of rules, so that you can see the number of each rule:
root@ufw-test:~# ufw status numbered
Status: active
To Action From
-- ------ ----
[ 1] OpenSSH ALLOW IN Anywhere
[ 2] 5555 ALLOW IN Anywhere
[ 3] OpenSSH (v6) ALLOW IN Anywhere (v6)
[ 4] 5555 (v6) ALLOW IN Anywhere (v6)
There are two common ways you can remove a firewall rule from UFW:
Generally removing via rule number is going to be the most accurate and easiest.
First you need to find the rule number, which you can do using ufw status numbered
root@ufw-test:~# ufw status numbered
Status: active
To Action From
-- ------ ----
[ 1] OpenSSH ALLOW IN Anywhere
[ 2] 5555 ALLOW IN Anywhere
[ 3] OpenSSH (v6) ALLOW IN Anywhere (v6)
[ 4] 5555 (v6) ALLOW IN Anywhere (v6)
Assuming this is the rule we want to remove:
[ 4] 5555 (v6) ALLOW IN Anywhere (v6)
We can see that it has the rule number 4, so we can remove it using ufw delete 4
:
root@ufw-test:~# ufw delete 4
Deleting:
allow 5555
Proceed with operation (y|n)? y
Rule deleted (v6)
If we run ufw status numbered
again - we can see that rule #4 has been deleted, meaning we have removed the rule that allowed port 5555 for IPv6, without touching rule #2 which allows 5555 for IPv4:
root@ufw-test:~# ufw status numbered
Status: active
To Action From
-- ------ ----
[ 1] OpenSSH ALLOW IN Anywhere
[ 2] 5555 ALLOW IN Anywhere
[ 3] OpenSSH (v6) ALLOW IN Anywhere (v6)
You can remove a rule based on it's policy, which can be useful for deleting multiple rules.
First we'll check what rules we currently have using ufw status verbose
:
root@ufw-test:~# ufw status verbose
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip
To Action From
-- ------ ----
2222/tcp (OpenSSH) ALLOW IN Anywhere
5555 ALLOW IN Anywhere
2222/tcp (OpenSSH (v6)) ALLOW IN Anywhere (v6)
5555 (v6) ALLOW IN Anywhere (v6)
Assuming we want to get rid of the rules which allow traffic to port 5555, we can delete them similar to how we originally made them, using ufw delete allow 5555
:
root@ufw-test:~# ufw delete allow 5555
Rule deleted
Rule deleted (v6)
If we run ufw status verbose
again - you'll see that BOTH the IPv4 and IPv6 rules that allowed port 5555 are now gone:
root@ufw-test:~# ufw status verbose
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip
To Action From
-- ------ ----
2222/tcp (OpenSSH) ALLOW IN Anywhere
2222/tcp (OpenSSH (v6)) ALLOW IN Anywhere (v6)
To allow all traffic from the IP address 10.1.2.3
(i.e. to any port) - run the following command:
ufw allow from 10.1.2.3
We can do the same for IPv6 addresses, such as 2a07:e00::333
:
ufw allow from 2a07:e00::333
If you need to allow an entire subnet, it works the same way, just add the subnet mask:
# Allow all traffic from 10.1.0.0 to 10.1.0.255
ufw allow from 10.1.0.0/24
If you want to allow an IP address to only be able to access a certain port:
# Allows the IP address 10.1.2.3 to access port 4444
ufw allow from 10.1.2.3 to any port 4444
You can block traffic from certain IP addresses or subnets in much the same way as you allow them.
To block all traffic from the IP address 10.3.2.5
:
ufw deny from 10.3.2.5
To block all traffic from the IPv6 address 2a07:e02:ab:cd:463::8af
ufw deny from 2a07:e02:ab:cd:463::8af
To block all traffic from IPs in the range 10.3.0.0 to 10.3.255.255:
ufw deny from 10.3.0.0/16
To block traffic from a certain IP only for a certain port:
# Block traffic from 10.3.2.4 only if it's going to port 3333
ufw deny from 10.3.2.4 to any port 3333
In some cases, you may want to block traffic that's outgoing from your server, rather than traffic coming into your server, such as to prevent an employee/customer's traffic going through the server accessing things you don't want them to.
For example, to block traffic going to any IP on port 25 (SMTP), to help prevent applications from sending spam:
ufw deny out 25
To block traffic going to a specific IP address:
# Block any traffic going to 10.4.4.4
ufw deny out to 10.4.4.4
To block traffic only to a specific port on a specific IPv6 subnet:
# Block traffic going to port 1234 in the subnet 2a07:e02:123:123::/64
ufw deny out to 2a07:e02:123:123::/64 port 1234
For some advanced system adminstrators, UFW might not support the advanced firewall rules they'd like to configure.
It's possible to use a mixture of UFW and manual IPTables rules, however some administrators may prefer having full control over their firewall rules.
In situations where you need to immediately apply a firewall rule, without it being persisted, you can use the commands iptables
and ip6tables
to set IPTables rules in memory, which you can later copy to your persistent IPTables rule file if desired.
Allowing established connections is a very important rule and should be set before changing your global policy to DROP/REJECT - as it tells the firewall not to filter connections that are already established (such as your SSH connection that you're managing it from).
You'll also want to whitelist traffic to the interface lo
(loopback), which is internal network traffic such as to 127.0.0.1
/ ::1
You may also want to whitelist ICMP, so that you can still ping / traceroute your server
The following rules do all of the above, for both IPv4 and IPv6:
iptables -I INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -I INPUT -p icmp -j ACCEPT
iptables -I INPUT -i lo -j ACCEPT
ip6tables -I INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
ip6tables -I INPUT -p ipv6-icmp -j ACCEPT
ip6tables -I INPUT -i lo -j ACCEPT
To allow all TCP traffic to port 22 (SSH) using iptables:
# Use iptables for IPv4 rules
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# Use ip6tables for IPv6 rules
ip6tables -A INPUT -p tcp --dport 22 -j ACCEPT
To block all TCP and UDP traffic to port 3333 using iptables:
iptables -A INPUT -p tcp --dport 3333 -j DROP
iptables -A INPUT -p udp --dport 3333 -j DROP
ip6tables -A INPUT -p tcp --dport 3333 -j DROP
ip6tables -A INPUT -p udp --dport 3333 -j DROP
To block all traffic coming from the IPv4 address 10.4.5.6
:
iptables -A INPUT -s 10.4.5.6 -j DROP
To block all traffic coming from the IPv6 subnet 2a07:e02:abc:dff::/64
:
ip6tables -A INPUT -s 2a07:e02:abc:dff::/64 -j DROP
To allow all traffic from the IP address 10.1.2.3
you use the following command - note that you should use -I
instead of -A
to make sure the rule is inserted at the start of the rules table, so that it's not ignored due to a DROP/REJECT rule earlier on:
iptables -I INPUT -s 10.1.2.3 -j ACCEPT
To allow traffic from the subnet 10.4.0.0/24
but only to the TCP ports 2222,3333,4444:
iptables -I INPUT -s 10.4.0.0/24 -p tcp -m multiport --dports 2222,3333,4444 -j ACCEPT
To allow all traffic from the IPv6 subnet 2a07:e00::/32
we need to use ip6tables
instead of iptables
:
ip6tables -I INPUT -s 2a07:e00::/32 -j ACCEPT
To view all your rules in memory, you can use iptables-save
and ip6tables-save
:
# Prints out all current IPv4 rules
iptables-save
# Prints out all current IPv6 rules
ip6tables-save
Once you have your most important rules setup, such as allowing established connections and SSH traffic, you can change the global policy so that traffic that doesn't match any rules is automatically rejected.
To drop any incoming traffic that doesn't match your rules:
iptables -P INPUT DROP
ip6tables -P INPUT DROP
You may wish to also DROP any FORWARD traffic, however be aware that if you use this server for virtual machines or network routing (such as VPNs, or have machines pointed to the server as their internet gateway), you'll need to setup appropriate FORWARD rules to avoid breaking their connectivity:
iptables -P FORWARD DROP
ip6tables -P FORWARD DROP
To persist your IPTables rules, you'll need to install a service which handles persistent IPTables rules such as netfilter-persistent
(formerly called iptables-persistent
)
On Ubuntu/Debian based systems, this package is available as netfilter-persistent
:
apt update
apt install netfilter-persistent
Once it's installed, you can generate the IPTables persistent configuration files by simply dumping your existing IPTables rules from memory:
# Dump your IPv4 iptables rules
iptables-save > /etc/iptables/rules.v4
# Dump your IPv6 iptables rules
ip6tables-save > /etc/iptables/rules.v6
You can now either edit the rules files directly as needed at /etc/iptables/rules.v4
for IPv4 rules, and /etc/iptables/rules.v6
for IPv6 rules - or you can simply dump your in-memory rules whenever you want to update the persistent rules.
Make sure the service is enabled (so it starts on boot) and running:
systemctl enable netfilter-persistent
systemctl restart netfilter-persistent
The persistent rule files work similar to creating rules in memory, you'll see that each section of the rule file contains lines which look like the command arguments for iptables
/ ip6tables
For example, it may look like this:
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
:f2b-sshd - [0:0]
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -p tcp --dport 22 -j ACCEPT
COMMIT
As you can see, outside of the table configuration blocks (the area with :INPUT DROP [0:0]
etc.), the file simply contains IPTables command line arguments, and you can enter rules in here as you would via the iptables
command - just without iptables
at the start.
To make sure netfilter-persistent
is working correctly, after you've setup your rules files with some IPTables rules, we recommend rebooting your server to make sure that it's correctly loading your rules on boot.
To reboot a Linux server, simply run the following command:
sudo reboot
Wait for the server to come back up, and then run:
sudo iptables-save
sudo ip6tables-save
The output should look like your rules.v4
/ rules.v6
configuration, possibly plus a few extra rules automatically added by applications such as fail2ban.
If so, you've now got a working IPTables that persists between reboots! Congratulations!
Please remember that when you run iptables
/ ip6table
to add rules in memory, that they will not automatically persist. You'll need to remember to either edit the rules files yourself to add the rules you'd like to persist, or use iptables-save
/ ip6tables-save
to dump your rules into the rules files as previously shown.
For additional security tips, we recommend checking out our other article about securing your Linux server after you've finished setting up your firewall.
We have virtual servers starting from just US$0.99/mo, and dedicated servers starting from as low as US$50/mo
Unlike other hosts, we don't ask for any personal details - only a name (can be an alias / username), and an e-mail address so we can send you your server details and renewal invoices.
We also accept several different cryptocurrencies with our own in-house payment processor - no third parties involved in processing your payments.
At the time of writing, we currently accept: Bitcoin (BTC), Litecoin (LTC), Monero (XMR), Dogecoin (DOGE), HIVE, and HBD