Skip to content

Cookie settings

Choose which kinds of cookies you allow. You can change your choice at any time via Cookie settings at the bottom of every page. Privacy statement

Necessary

Always on

Needed to log in, keep your cart and remember your choices. These are always on.

Which cookies?
  • weboke_sessie, weboke_klant, weboke_remember: your session and staying logged in
  • weboke_basket: your cart
  • wkBtwIncl: showing prices with or without VAT
  • wok_cookies: your cookie choice, 12 months

Measures which ad leads to a visit or order, so we do not pay for ads that do nothing.

Which cookies?
  • Google Ads via Google Tag Manager (_gcl_au, _gcl_aw), up to 90 days
  • OpenAI Ads, ads in ChatGPT
  • wok_aff: which partner referred you to us (affiliate programme), 60 days

4.6

Country and language

Each country has its own site, with prices and VAT for that country.

View

Module 5 · 5.2

A firewall with ufw and nftables

Module 5: Shielding the network · Reading time: 12 minutes

Learning objective: After this lesson you can set up a firewall that only allows what is needed, for IPv4 and IPv6, without locking yourself out, and you know what a firewall on your server does not protect against.

Key concepts: packet filter, netfilter, nftables, iptables-nft, ufw, default policy, allow, deny, reject, limit, IPv6 in ufw, provider firewall, firewalld, Docker and the firewall

What a firewall does

A firewall on your server is a packet filter: for every incoming network packet, it checks a list of rules and decides whether it may pass. In Linux, that filter lives in the kernel and is called netfilter. You configure it with a program:

  • nftables (command nft) is the current tool. Debian has used it by default since Debian 10.
  • iptables is the old tool. On modern systems, iptables is usually a translation layer to nftables (iptables-nft), so old guides still work.
  • ufw (Uncomplicated Firewall) is a simple layer on top, installed by default on Ubuntu and one command away on Debian. For a single server, ufw is the easiest choice. This lesson uses ufw and then shows you what the same rules look like in nftables.

A firewall is your second line of defence. The first is making sure services only listen on the right address (lesson 5.1). Together they ensure that a mistake in one layer does not immediately leave a door wide open.

First: how do you get back in if things go wrong?

The classic mistake: you switch on the firewall, forget to allow SSH, and your connection drops. You can then no longer get in via SSH. Before you start:

  1. Know how to open your provider's console (lesson 1.2). It works outside the network, so it still works when the firewall blocks everything. Log in there and type ufw disable.
  2. Take a snapshot if you are unsure.
  3. Keep your current SSH session open and test with a second session.

ufw in five steps

Step 1: install and choose the defaults.

anna@web1:~$ sudo apt install ufw
anna@web1:~$ sudo ufw default deny incoming
Default incoming policy changed to 'deny'
anna@web1:~$ sudo ufw default allow outgoing
Default outgoing policy changed to 'allow'

The default policy applies to everything that has no rule. "Deny incoming" means: everything coming in is forbidden, except what you allow next. That is the only safe foundation. You allow outgoing traffic (your server fetching updates).

Step 2: allow SSH, before you switch on the firewall.

anna@web1:~$ sudo ufw allow OpenSSH
Rules updated
Rules updated (v6)

OpenSSH is an application profile that ufw knows (it stands for port 22/tcp). If SSH runs on a different port, use the number: sudo ufw allow 2222/tcp. Note "Rules updated (v6)": ufw also creates the rule for IPv6. It does this because /etc/default/ufw contains the line IPV6=yes, which is the default. Do check this; if it is set to no, your firewall only applies to IPv4.

Step 3: whatever else you need.

anna@web1:~$ sudo ufw allow 80/tcp
anna@web1:~$ sudo ufw allow 443/tcp
anna@web1:~$ sudo ufw allow 443/udp       # only if you use HTTP/3

You can restrict a rule to a single source address, for example a management port that may only be reached from your office:

anna@web1:~$ sudo ufw allow from 198.51.100.20 to any port 9090 proto tcp

Step 4: switch it on.

anna@web1:~$ sudo ufw enable
Command may disrupt existing ssh connections. Proceed with operation (y|n)? y
Firewall is active and enabled on system startup

Step 5: check. Open a second window and log in again. If that works, SSH is set up correctly. Then look at the rules:

anna@web1:~$ sudo ufw status verbose
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), deny (routed)
New profiles: skip

To                         Action      From
--                         ------      ----
22/tcp (OpenSSH)           ALLOW IN    Anywhere
80/tcp                     ALLOW IN    Anywhere
443/tcp                    ALLOW IN    Anywhere
22/tcp (OpenSSH (v6))      ALLOW IN    Anywhere (v6)
80/tcp (v6)                ALLOW IN    Anywhere (v6)
443/tcp (v6)               ALLOW IN    Anywhere (v6)

You remove a rule by number: sudo ufw status numbered and then sudo ufw delete 3.

deny, reject and limit

  • deny silently drops the packet: from outside you see "filtered" (lesson 5.1).
  • reject sends a refusal back: from outside you see "closed". Tidier for your own services, but it gives away that there is a server.
  • limit allows a connection, but blocks an address that tries to connect six or more times within 30 seconds. Handy for SSH: sudo ufw limit OpenSSH instead of allow. It is crude (it counts connections, not failed logins); fail2ban (lesson 5.3) is more precise.

The same rules in nftables

Under the bonnet, ufw writes netfilter rules. If you want to work without ufw, you write an nftables file yourself. On Debian it lives in /etc/nftables.conf and you load it with the nftables service:

#!/usr/sbin/nft -f
flush ruleset

table inet filter {
    chain input {
        type filter hook input priority 0; policy drop;
        ct state established,related accept
        ct state invalid drop
        iif "lo" accept
        meta l4proto { icmp, ipv6-icmp } accept
        tcp dport { 22, 80, 443 } accept
    }
    chain forward {
        type filter hook forward priority 0; policy drop;
    }
    chain output {
        type filter hook output priority 0; policy accept;
    }
}

Read it like this. The table is called filter and belongs to the inet family: one table for IPv4 and IPv6 at the same time, so you cannot forget IPv6. In the input chain the policy is drop. Then: replies to connections that already exist may come back (established,related), everything on loopback is allowed, ICMP is allowed (needed for IPv6 and for error messages about packet size, among other things), and ports 22, 80 and 443 are allowed.

anna@web1:~$ sudo nft -c -f /etc/nftables.conf    # check without loading
anna@web1:~$ sudo systemctl enable --now nftables
anna@web1:~$ sudo nft list ruleset

Use either ufw or your own nftables file, not both: they write into the same netfilter and can get in each other's way.

What your firewall does not protect against

  • Services you allow yourself. Port 443 is open for your website, so a vulnerability in your website is simply reachable. The firewall only protects the ports that are closed.
  • Docker. When you publish a port with Docker (-p 5432:5432), Docker sets its own netfilter rules, which redirect the traffic before the ufw rules apply. The Docker documentation states plainly that Docker and ufw do not work well together. So a container port can be open from outside while ufw status does not show it. You will learn the solution (publishing on 127.0.0.1) in lesson 9.3.
  • Outgoing traffic. With "allow outgoing", a hacked program can connect anywhere. For most small servers that is an acceptable choice; strict environments restrict outgoing traffic too.

Your provider's firewall

Many providers also have a firewall in the network, in front of your server, which you configure in the customer panel (sometimes called a "cloud firewall" or "security group"). It is a fine extra layer, and it still works if you get something wrong on the server itself. But it does not replace the firewall on the server: if you move the server or switch provider, it does not come with you, and not every provider offers one. So always keep a firewall on the server itself.

On AlmaLinux and Rocky Linux

There, firewalld is enabled by default, with zones. The most important commands:

[root@web1 ~]# firewall-cmd --list-all
[root@web1 ~]# firewall-cmd --permanent --add-service=http --add-service=https
[root@web1 ~]# firewall-cmd --reload

Without --permanent, a change only lasts until the next restart; with --permanent, it only takes effect after --reload. SSH is already open in the default zone.

In practice

If you do lock yourself out of a VPS at WebOké, open the console in the VPS screen, log in and switch off the firewall (ufw disable) or undo the last rule. With other providers, this emergency entrance is also called console, VNC or rescue. Know where that button is before you need it.

Summary

  • A firewall is your second layer after "only listen where needed"; nftables is the tool in the kernel, ufw a simple layer on top.
  • Start with default deny incoming, allow SSH before ufw enable, test with a second session, and know your provider's console.
  • Check that ufw also covers IPv6 (IPV6=yes); in nftables, the inet family does that in one go.
  • deny shows "filtered" from outside, reject shows "closed"; limit slows down repeated connections.
  • Docker ports bypass ufw (lesson 9.3); your provider's firewall is an extra layer, not a replacement.

Quiz

1. In what order do you switch on ufw for the first time on a server you are logged into via SSH?



2. After ufw enable, your website no longer works over IPv4, but SSH does. What is the most likely cause?



3. You use Docker to publish port 5432 of a database container. ufw status shows no rule for 5432. Can the database be reached from outside?



4. Why does the nftables example use a table of the inet family?



5. Your provider has a network firewall in the customer panel. Do you still need a firewall on the server?



Exercise

Exercise O5.2: Setting up your firewall. Switch on a firewall on your server with ufw (or nftables, or firewalld on AlmaLinux/Rocky): everything incoming closed by default, SSH, and whatever else you really need. Test with a second session, check that IPv6 is covered, and use nc from outside to see whether a port you have not allowed is closed or filtered. Fill in the checklist and paste your ufw status verbose (or nft list ruleset); for each open port, state what it is for.

Exercise O5.2: Setting up your firewall

You do this exercise yourself; no one checks it. Tick off what you've done and mark the exercise as done.



Your ticks and notes only stay in this browser; we don't send them anywhere. If you're signed in, we only save in My courses that you've done the exercise.

Further reading

One moment…

Set up your domains

For each domain, arrange the nameservers, the link to your hosting and who becomes the owner.

Loading your domains…