Newsletter
Subscribe to my newsletter for the latest updates. 👇
Setting up a firewall for a new server is crucial, as a firewall plays a key role in server security.
Think of it as your server’s bouncer – it filters incoming and outgoing traffic, allowing only authorized access to specific ports.
UFW simplifies the setup and management of firewall rules, making it user-friendly.
In this guide, I’ll teach you how to set up a firewall using UFW along with some best practices for effective firewall management.
To make the most of this guide, ensure you have a properly set up Ubuntu server.
If you don’t have one, consider getting a free VPS server to follow along.
Following along on your own server will enhance your understanding and practical experience.
On Debian-based distributions, like Ubuntu, it often comes pre-packaged.
You can check and install it using this command:
sudo apt install ufw
Many server providers configure the UFW firewall upon deploying the server to allow only SSH connections, enabling you to connect to the server.
If you have a server from Vultr, UFW is likely to be enabled by default.
In my case with Hetzner, UFW is not enabled.
If you’re interested in trying out Hetzner and you’re a new customer, use my referral link to receive 20€ in free credits to get started.
You can check the status of UFW and your current ruleset using this command:
sudo ufw status
The command’s output will either indicate that UFW is inactive, or that it is active with your current rule set.
If UFW is currently inactive, that’s fine, as we’ll proceed to configure it properly and enable it.
However, if UFW is already active, disable and reset it using the following commands:
sudo ufw disable
sudo ufw reset
You can re-enable it once you have added all the rules and finished configuring it.
If you wish to review the rules you added and your firewall is disabled, the sudo ufw status
command won’t display your ruleset.
Instead, you can use the sudo ufw show added
command to view the rules you added, even when the firewall is disabled.
Now, let’s examine UFW’s default policy.
By default, UFW takes a secure approach by blocking all incoming traffic while allowing outgoing traffic from our server.
This means our server can communicate externally, but it remains inaccessible to others.
Since there is no issue with our server reaching the outside world, there is no need to make any changes to that aspect.
However, to enable incoming traffic, it’s essential to selectively open only the required ports and authorize traffic through them.
If you wish to review the default settings for UFW, you can examine the /etc/default/ufw
file.
Open the file using your preferred editor.
...
# Set the default input policy to ACCEPT, DROP, or REJECT. Please note that if
# you change this you will most likely want to adjust your rules.
DEFAULT_INPUT_POLICY="DROP"
# Set the default output policy to ACCEPT, DROP, or REJECT. Please note that if
# you change this you will most likely want to adjust your rules.
DEFAULT_OUTPUT_POLICY="ACCEPT"
...
As you can see, the default policy for incoming traffic is set to DROP
, while the default policy for outgoing traffic is set to ACCEPT
.
You can review the default policy using the following command too:
sudo ufw status verbose
You can modify this default behavior of UFW either by directly editing the file or by using these two commands:
sudo ufw default <policy> incoming
sudo ufw default <policy> outgoing
Replace policy
with either deny
, allow
or reject
.
deny
corresponds to DROP, allow
corresponds to ACCEPT, and reject
corresponds to REJECT.
Both DROP and REJECT policies prevent traffic from passing through the firewall, but they differ in their response messages.
With DROP, the traffic is silently discarded without any acknowledgment sent to the source. It neither forwards the packet nor responds to it.
On the other hand, REJECT sends an error message back to the source, signaling a connection failure.
Before adding any firewall rules, you need to identify which ports are open on your server.
This information can be obtained using the nmap
utility.
Install the nmap
package using the following command:
sudo apt install nmap
Once nmap
is installed, use the following command to scan for open ports:
sudo nmap <server_ip>
Output
...
PORT STATE SERVICE
22/tcp open ssh
25/tcp open smtp
80/tcp open http
110/tcp open pop3
143/tcp open imap
587/tcp open submission
993/tcp open imaps
995/tcp open pop3s
This is the output I got from one of my servers.
If your server is new, you might only see the SSH port open, as SSH is the only service installed by default.
By default, nmap
only scans TCP ports.
Use the following command to scan UDP ports:
sudo nmap -sU <server_ip>
The open
state, as indicated, isn’t solely related to the firewall.
In other words, it doesn’t necessarily imply that the port is accessible to the public.
Even if I have a rule to deny traffic to port 80, nmap
may still show the port as open.
Essentially, this signifies that a service, such as the HTTP service, is installed on the server and is using port 80.
We utilize nmap
to identify the services installed on our server along with their associated ports, helping us plan the necessary rules to add.
Now that we’ve identified the open ports on our server using nmap
and understand that UFW defaults to blocking incoming traffic, the next step is to add rules that allow traffic to these ports.
In the following, I’ll guide you through the various options that UFW offers for rule configuration, sharing my approach to firewall configuration through helpful tips as well.
The first step before enabling a firewall is to allow traffic on port 22 (SSH) to ensure access to the server.
If you enable the firewall before adding this rule, you risk losing access to your server.
Use the following command to allow SSH traffic:
sudo ufw allow 22/tcp
This is a simple and swift solution.
Essentially, we’ve permitted any IP address to access port 22, meaning our SSH port is open to everyone.
This is something I never do on a production server.
Even if you have generated an SSH key pair, implemented key authentication, and added a non-root user, hackers could still attempt unauthorized access.
What if we could proactively prevent these attempts? This would enhance overall security.
We can restrict the SSH port to a specific IP, allowing only this designated IP to access it.
This ensures that SSH access is limited to a single, trusted IP address, such as your IP at your home network.
However, this approach comes with a drawback.
Most home networks use DHCP, causing your IP to change periodically.
When your IP changes, you lose access to the server, making it less practical.
I typically opt for restricting the SSH port to a specific IP only when I have a dedicated static IP.
A method to acquire a dedicated static IP is by using a VPN service.
Numerous VPN providers provide the option of a dedicated static IP for an additional fee.
If you have a dedicated static IP, use the following command:
sudo ufw allow from <ip> proto tcp to any port 22
Now, the IP specified in the command is the only one that can access the server.
Note: When you restrict SSH access to only one IP, Fail2ban becomes irrelevant as there are no IPs to block. I mention this to avoid confusion. However, I still recommend keeping Fail2ban installed and enabled even when restricting SSH access.
If I can’t restrict SSH access and don’t want to make my SSH port accessible to all, there’s still something I can do.
Use the limit
rule instead of the allow
rule for SSH on a production server:
sudo ufw limit 22/tcp
It allows only 6 connections from the same IP address within a 30-second window, protecting the server from potential brute-force attacks.
If you are hosting a website, you need to allow traffic on ports 80 (HTTP) and 443 (HTTPS) for your visitors to reach your website.
In this case, there should be no restrictions, as we want everyone to be able to access our website.
You can allow traffic by using the allow
rule, similar to what we did for SSH, as follows:
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
This should be fine, but there are some considerations I would like to share with you.
First, if you are using an SSL certificate, which you should, and redirecting traffic from HTTP to HTTPS, as recommended, there is no need to allow traffic on port 80.
Although allowing traffic on both ports is not a problem and I’ve never faced any issues with it, I wanted to inform you.
Second, if you are using a proxy service, such as Cloudflare or Sucuri, traffic will only come from their IPs.
You can enhance security by restricting HTTP and HTTPS traffic to only their IPs.
For example, for Cloudflare, you would use these commands:
sudo ufw allow from <cf_ip> to any port 80 proto tcp
sudo ufw allow from <cf_ip> to any port 443 proto tcp
You should repeat these two commands for all Cloudflare IPs, or you can use a bash script to automate this.
Now that you know how to allow traffic, restrict access, and utilize the limit
rule, it’s time to add rules for other ports.
Plan your ruleset based on the services installed and those you intend to use.
If you have a control panel installed on your server or a service that you exclusively use, consider restricting access to your IP or using the limit
rule.
As I mentioned earlier, UFW is configured to deny all incoming traffic by default.
However, there may be situations where you want to block traffic based on the source IP address or subnet, especially if you’re aware of attacks coming from that source.
Consider this simple scenario: I have a WordPress website hosted on my server, and I’ve set rules to allow traffic on ports 80 and 443. However, I noticed that a specific IP is continuously attacking the login page, consuming my server resources. To address this, I want to block traffic from that IP, preventing access to the website.
I can achieve this using the following commands:
sudo ufw deny from <ip> to any port 80 proto tcp
sudo ufw deny from <ip> to any port 443 proto tcp
This way, we have blocked HTTP and HTTPS traffic to our website from that IP address.
If you want to block traffic from an IP address on all ports, you can use the following command:
sudo ufw deny from <ip>
If an entire subnet of IPs is causing issues, we can extend this approach to block traffic from the entire subnet using this command:
sudo ufw deny from <ip>/24
These are some basic examples of how to deny traffic from a specific source.
These can be quite handy in certain situations when you need to quickly stop an ongoing attack.
Applications (software or services installed) can register their profiles with UFW upon installation, enabling UFW to manage them by name.
To view the available profiles, you can use the following command:
sudo ufw app list
Output
Available applications:
Apache
Apache Full
Apache Secure
Dovecot IMAP
Dovecot POP3
Dovecot Secure IMAP
Dovecot Secure POP3
Nginx Full
Nginx HTTP
Nginx HTTPS
OpenSSH
Postfix
Postfix SMTPS
Postfix Submission
This is the output I got from one of my servers.
If your server is new, you are more likely to see only the OpenSSH profile, which is the service behind SSH.
When using Application Profiles, there’s no need to memorize specific ports.
Instead, you utilize the profile name to allow, deny, or reject traffic.
For instance, to allow traffic on port 443 (HTTPS), you would use the following command:
sudo ufw allow "NGINX HTTPS"
Or:
sudo ufw allow "Apache Secure"
If you’re curious about the origins of these profiles, check the /etc/ufw/applications.d/
directory.
If, for some reason, you want to delete a rule you have added, you can use the sudo ufw delete
command followed by the rule itself like this:
sudo ufw delete deny from 111.111.111.111 to any port 80 proto tcp
Or:
sudo ufw delete allow 80
There is another easier way to delete rules, but it requires the firewall to be enabled.
This method involves using the rule number.
Once the firewall is enabled, you can use the sudo ufw status numbered
command to obtain a list of your rules and their corresponding numbers, like this:
Output
Status: active
To Action From
-- ------ ----
[ 1] 22/tcp ALLOW IN Anywhere
[ 2] 22/tcp (v6) ALLOW IN Anywhere (v6)
Now, to delete a rule, I can simply use the rule number:
sudo ufw delete 1
This is a much simpler method.
Before activating our firewall, it’s crucial to review the rules we’ve added so far to prevent any unexpected behavior.
Since the firewall is currently disabled, we can’t use the sudo ufw status
command to get a list of our rules.
Instead, we could use this command:
sudo ufw show added
This command will list all the rules you have added.
Always add the rules, review them, and then proceed to activate the firewall.
For our final step, let’s enable the firewall.
Simply use this command:
sudo ufw enable
With this, our firewall is enabled.
If you experience any issues, review your rules again.
Read more: Explore my blog post on effectively blocking INVALID packets using UFW.
Great job reaching the end!
In this guide, you’ve learned how to set up a firewall using UFW.
However, there’s more to securing Linux servers — advanced measures that I’ve covered comprehensively in my complete server security and hardening guide.
It’s a collection of all security and hardening measures with links to detailed guides for each.
By following these guides one by one, your server will be thoroughly secured.
If you found value in this guide or have any questions or feedback, please don’t hesitate to share your thoughts in the comments section below.
Your input is greatly appreciated, and you can also contact me directly if you prefer.