Skip to main content

Firewall Basics With UFW & Firewalld: A Practical Linux Guide

Media Firewall Basics With UFW and firewalld

 

A Linux server can be fully patched, correctly configured and running only trusted software—and still expose services to networks that should never have been able to reach them.

That is where a host firewall helps.

A firewall decides which network traffic is:

  • allowed
  • rejected
  • dropped
  • limited

based on rules you define.

For many Linux systems, two common management tools are:

UFW — popular on Ubuntu and Debian-based systems.

firewalld — common on Fedora, Rocky Linux, AlmaLinux, RHEL and related distributions.

Both ultimately manage Linux packet-filtering capabilities underneath. The difference is mainly how you express and manage the rules.

The principle is the same:

allow only what the server genuinely needs.

What Does a Firewall Actually Do?

Suppose a Linux server runs:

  • SSH on TCP 22
  • a website on TCP 443
  • a database on TCP 3306

Without sensible firewall controls, all three services may potentially be reachable wherever the network routing allows.

But perhaps your requirements are actually:

HTTPS
Accessible from the internet.

SSH
Accessible only from your management network.

MySQL
Accessible only from the application server.

A firewall lets you enforce those boundaries.

That means instead of thinking:

“Which ports should I block?”

a better approach is:

“Which connections should this machine accept at all?”

Firewalls Are Not a Substitute for Secure Services

A firewall is an important layer.

It does not mean you can ignore:

  • software updates
  • strong authentication
  • MFA where applicable
  • application security
  • SSH hardening
  • least privilege
  • vulnerability management

If you expose SSH to the internet, allowing TCP 22 through the firewall does not make SSH secure by itself.

Likewise, blocking access to an old vulnerable application from most networks reduces exposure, but it does not repair the vulnerability.

Think:

firewall = control who can reach the service

service security = control what happens once they reach it

Before Changing a Remote Firewall: Protect Your SSH Session

This is the single most important operational warning in this article.

If you are connected remotely over SSH and enable a firewall before allowing SSH, you can lock yourself out.

Before enabling UFW or changing firewalld rules, confirm:

  1. Which port SSH actually uses.
  2. Which network should be allowed to reach it.
  3. Whether you have console/out-of-band recovery access.
  4. That the allow rule exists before you apply restrictive policy.

Ubuntu’s UFW documentation specifically warns that enabling UFW can affect existing SSH connections and recommends adding the SSH rule before enabling the firewall.

If possible, keep a second SSH session open while testing important firewall changes.

Part 1: UFW Basics

UFW stands for:

Uncomplicated Firewall

It provides a simpler interface for managing a host-based Linux firewall.

Ubuntu describes UFW as primarily intended to make simple host firewall configuration easier.

Check whether it is installed:

ufw --version

Check status:

sudo ufw status

You may see:

Status: inactive

That is normal on a system where UFW has not yet been enabled.

Set Sensible Default Policies

For a typical server, a useful starting point is:

sudo ufw default deny incoming
sudo ufw default allow outgoing

This means:

unsolicited inbound traffic → denied

outbound traffic → allowed

Ubuntu documents UFW’s installation defaults as deny for incoming and forwarded traffic while allowing outgoing connections.

That is a good general host-firewall model for many ordinary servers.

But do not enable it until you have allowed the services you actually need.

Allow SSH First

If the server uses standard SSH:

sudo ufw allow OpenSSH

or explicitly:

sudo ufw allow 22/tcp

Application profiles are useful because they avoid hard-coding ports when UFW already knows the service definition.

List available application profiles:

sudo ufw app list

Inspect one:

sudo ufw app info OpenSSH

UFW supports application profiles stored under /etc/ufw/applications.d.

Restrict SSH to a Management Network

This is better than opening SSH to everyone where your network design permits it.

For example:

sudo ufw allow proto tcp from 192.168.10.0/24 to any port 22

Ubuntu documents this source-restricted syntax directly.

Now only systems from:

192.168.10.0/24

can initiate SSH connections.

That is much better than:

sudo ufw allow 22/tcp

from everywhere if global access is unnecessary.

Allow a Web Server

For HTTP:

sudo ufw allow 80/tcp

For HTTPS:

sudo ufw allow 443/tcp

Or use application profiles where installed.

For example, Apache or Nginx packages may register profiles visible through:

sudo ufw app list

The principle is:

allow the service, not every possible port.

Preview UFW Changes

One useful UFW feature is:

--dry-run

For example:

sudo ufw --dry-run allow 443/tcp

Ubuntu’s current documentation says dry-run outputs the rules that would be generated without actually applying them.

That is especially useful when writing more specific source/interface rules.

Enable UFW

Once the required management rules exist:

sudo ufw enable

Then check:

sudo ufw status verbose

You may see something like:

Status: active

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW       192.168.10.0/24
80/tcp                     ALLOW       Anywhere
443/tcp                    ALLOW       Anywhere

Now test from another machine.

Do not assume the rule is correct simply because UFW accepted the command.

View Rules With Numbers

Run:

sudo ufw status numbered

Example:

[1] 22/tcp   ALLOW IN   192.168.10.0/24
[2] 80/tcp   ALLOW IN   Anywhere
[3] 443/tcp  ALLOW IN   Anywhere

To delete rule 2:

sudo ufw delete 2

UFW officially supports numbered rule deletion.

Be careful because rule numbers can change after deletions.

Always run:

sudo ufw status numbered

again before deleting another one.

UFW deny vs reject

UFW can:

sudo ufw deny 8080/tcp

or:

sudo ufw reject 8080/tcp

Broadly:

deny
Silently drops the traffic.

reject
Refuses it and sends an appropriate response where supported.

Ubuntu’s UFW manual notes that reject can be useful where you want the sender to know the connection was refused instead of simply timing out.

For most simple server firewall policies, the defaults are usually adequate.

Rate-Limit SSH Attempts

UFW provides:

sudo ufw limit ssh/tcp

The UFW manual documents this as a simple method of limiting repeated SSH connection attempts.

This can help reduce noisy brute-force activity.

But it is not a substitute for:

  • SSH keys
  • disabling unnecessary password authentication
  • MFA where appropriate
  • source restrictions
  • monitoring

Enable UFW Logging

You can enable logging:

sudo ufw logging on

Or specify a level:

sudo ufw logging medium

Check:

sudo ufw status verbose

UFW uses kernel logging, and systems using rsyslog may log to /var/log/ufw.log.

Logging can help answer:

Why can't this client connect?

But avoid turning on excessively verbose firewall logging permanently on busy systems without considering:

  • disk usage
  • log rotation
  • noise

Part 2: firewalld Basics

firewalld takes a somewhat different approach.

Instead of primarily thinking in terms of one long list of rules, it uses zones to represent different trust levels or network contexts.

The firewalld project describes it as a dynamically managed firewall supporting zones associated with interfaces, connections or source networks.

Examples of predefined zones may include:

  • public
  • home
  • work
  • internal
  • trusted
  • dmz

The actual zones available can be listed with:

sudo firewall-cmd --get-zones

Check Whether firewalld Is Running

Run:

sudo firewall-cmd --state

Expected:

running

firewall-cmd is the primary command-line interface for firewalld.

To see active zones:

sudo firewall-cmd --get-active-zones

Example:

public
  interfaces: enp1s0

This means the interface:

enp1s0

is currently associated with the public zone.

Understand firewalld Zones Before Opening Ports

This is critical.

If you run:

sudo firewall-cmd --zone=public --add-service=https

you are allowing HTTPS only within the public zone.

If the relevant network interface is actually bound to:

internal

your rule may have no effect where you expected it.

Check:

sudo firewall-cmd --get-active-zones

and:

sudo firewall-cmd --get-zone-of-interface=enp1s0

firewalld officially supports both commands.

Runtime vs Permanent: The Most Important firewalld Concept

firewalld has two configurations:

runtime

and:

permanent

The runtime configuration is what the kernel is using right now.

The permanent configuration is what firewalld will load after restart/reload.

The firewalld project explicitly says runtime changes are lost when the service stops or reloads unless they are also made permanent.

This catches people constantly.

For example:

sudo firewall-cmd --zone=public --add-service=https

allows HTTPS now.

But after a reboot, that rule may disappear.

Allow HTTPS Permanently With firewalld

One method:

sudo firewall-cmd --permanent --zone=public --add-service=https
sudo firewall-cmd --reload

The reload makes the permanent configuration become active.

firewalld documents exactly this model.

Alternatively, you can first test a rule in runtime:

sudo firewall-cmd --zone=public --add-service=https

Confirm everything works.

Then save the working runtime configuration:

sudo firewall-cmd --runtime-to-permanent

This is one of firewalld's nicest safety features.

The official documentation specifically recommends runtime testing and then migrating the working configuration to permanent.

Services Are Usually Better Than Raw Ports

To allow HTTP:

sudo firewall-cmd --zone=public --add-service=http

HTTPS:

sudo firewall-cmd --zone=public --add-service=https

SSH:

sudo firewall-cmd --zone=public --add-service=ssh

firewalld recommends using predefined service definitions for common protocols instead of manually specifying ports where an appropriate service already exists.

List available services:

sudo firewall-cmd --get-services

Opening a Raw Port With firewalld

Sometimes no predefined service exists.

For TCP 8443:

sudo firewall-cmd --zone=public --add-port=8443/tcp

To make it permanent:

sudo firewall-cmd --permanent --zone=public --add-port=8443/tcp
sudo firewall-cmd --reload

firewalld documents both runtime and permanent port rules.

Again:

use a named service when one exists

and:

use raw ports when necessary.

Check What a Zone Allows

Run:

sudo firewall-cmd --zone=public --list-all

You may see:

public
  interfaces: enp1s0
  services: ssh http https
  ports:

For only services:

sudo firewall-cmd --zone=public --list-services

For raw ports:

sudo firewall-cmd --zone=public --list-ports

Always inspect the active configuration after changes.

Remove a Service

Runtime:

sudo firewall-cmd --zone=public --remove-service=http

Permanent:

sudo firewall-cmd --permanent --zone=public --remove-service=http
sudo firewall-cmd --reload

Do not leave old firewall openings indefinitely after an application is removed.

Firewall rules should evolve with the server.

Runtime Testing Is Safer for Remote Changes

This is where firewalld has a particularly useful operational pattern.

Suppose you want to change SSH access.

Apply the new rule at runtime only.

Test it from another terminal.

If it works:

sudo firewall-cmd --runtime-to-permanent

If it does not work:

sudo firewall-cmd --reload

The reload discards runtime-only experimentation and re-applies the known permanent configuration.

That can be much safer than immediately making an untested remote rule permanent.

UFW or firewalld: Which Should You Use?

In most cases:

use the firewall management system already standard for your distribution.

UFW

A good fit for:

  • Ubuntu
  • simple server rules
  • users wanting straightforward allow/deny syntax

Typical workflow:

set defaults → allow required services → enable → inspect

firewalld

A good fit for:

  • Fedora
  • RHEL
  • Rocky Linux
  • AlmaLinux
  • environments benefiting from zones and dynamic configuration

Typical workflow:

identify zone → test runtime rule → verify → make permanent

Neither is automatically “more secure.”

Security depends on the rules you configure.

Do Not Run UFW and firewalld as Competing Managers

Avoid trying to manage the same host simultaneously with:

  • UFW
  • firewalld
  • hand-written nftables rules
  • old iptables scripts

unless you understand exactly how those components interact.

Having several systems manipulate packet-filtering rules can make troubleshooting extremely confusing.

Choose one authoritative management method for ordinary firewall administration.

Default Deny Is Usually the Right Mental Model

Instead of:

allow everything and block known bad things

prefer:

deny unsolicited inbound traffic and explicitly allow required services.

For example, a web server might need:

22/tcp  management network only
80/tcp  internet
443/tcp internet

It probably does not need random exposure of:

  • database
  • Redis
  • management dashboards
  • internal APIs

Ask of every open service:

Who needs to reach this?

Then restrict accordingly.

Databases Should Rarely Be Open to the Whole Internet

Suppose MySQL uses:

3306/tcp

Do not casually run:

sudo ufw allow 3306/tcp

or:

sudo firewall-cmd --add-port=3306/tcp

from everywhere.

If only application server:

10.10.20.15

needs access, restrict the source.

With UFW:

sudo ufw allow proto tcp from 10.10.20.15 to any port 3306

For firewalld, source-specific policy can be expressed through zones, source bindings or richer rules depending on the environment.

The general principle matters more than the syntax:

internal service → internal access only.

Check Which Services Are Actually Listening

Before opening a port, establish whether anything is even listening on it.

Run:

sudo ss -lntup

For TCP listeners:

sudo ss -lntp

You might discover:

0.0.0.0:22
0.0.0.0:443
127.0.0.1:5432

The address matters.

0.0.0.0:443

Listening on all IPv4 interfaces.

127.0.0.1:5432

Listening only on localhost.

A firewall rule opening PostgreSQL externally is pointless if PostgreSQL itself only listens on localhost.

Conversely, an application accidentally listening on all interfaces may deserve configuration changes even if the firewall currently protects it.

Test From Another Machine

Do not test only from the server itself.

From another system:

nc -vz server.example.com 443

or:

Test-NetConnection server.example.com -Port 443

for a Windows test client.

This proves the actual network path.

A local:

curl localhost

does not tell you whether the firewall permits remote access.

Firewall Troubleshooting: Check the Layers in Order

If an application is unreachable:

  1. Is the service running?
  2. Is it listening on the expected address/port?
  3. Is the host firewall allowing it?
  4. Is upstream routing correct?
  5. Is there another firewall/security group?
  6. Is NAT/port forwarding required?
  7. Is DNS pointing at the right address?

Do not immediately disable the firewall.

A firewall is only one layer.

Never “Fix” Connectivity With ufw disable

or:

systemctl stop firewalld

as the permanent solution.

Temporarily proving that the firewall is involved can sometimes be diagnostically useful in a controlled environment.

But the correct fix is:

add the minimum required rule

not:

remove the whole security boundary.

Cloud Servers May Have Two Firewalls

A cloud VM may have:

Linux host firewall

plus:

cloud network security rules

such as:

  • Azure Network Security Groups
  • AWS Security Groups
  • another provider's network firewall

Both must permit the connection.

For example:

UFW allows 443

but:

cloud security group blocks 443

Result:

HTTPS still fails.

Likewise, opening a cloud security group while UFW blocks the traffic still fails.

Know where all enforcement layers are.

Container Platforms Can Complicate Firewall Behaviour

Docker and other container platforms may add their own packet-filtering or NAT rules.

That means firewall behaviour can be less straightforward than:

UFW says deny → therefore Docker port is definitely unreachable.

Do not assume ordinary host-firewall rules automatically describe every container exposure.

Check:

docker ps

and inspect published ports.

For production container hosts, design firewall policy with the container networking model in mind rather than treating Docker as an ordinary host process.

Keep Firewall Rules Documented

A rule such as:

allow 8443/tcp

becomes mysterious six months later.

Where supported, use comments or maintain documentation explaining:

  • purpose
  • owner
  • source network
  • service
  • expiry/review date

For example, UFW supports rule comments in current syntax.

You want to be able to answer:

Why is this port open?

If nobody knows, it should be investigated.

Review Rules Regularly

Firewalls accumulate history.

An administrator opens a port for a migration.

The migration finishes.

The rule stays forever.

Review:

sudo ufw status numbered

or:

sudo firewall-cmd --list-all

periodically.

Remove openings belonging to:

  • old applications
  • retired servers
  • temporary projects
  • obsolete management networks

Firewall hygiene is an ongoing process.

A Practical UFW Example

A simple Ubuntu web server might use:

sudo ufw default deny incoming
sudo ufw default allow outgoing

sudo ufw allow proto tcp from 192.168.10.0/24 to any port 22
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

sudo ufw enable
sudo ufw status verbose

Result:

SSH → management network only

HTTP/HTTPS → public

everything else unsolicited inbound → denied

That is much better than opening services whenever an application complains.

A Practical firewalld Example

Suppose a Rocky Linux web server uses the public zone.

Check:

sudo firewall-cmd --get-active-zones

Test rules at runtime:

sudo firewall-cmd --zone=public --add-service=http
sudo firewall-cmd --zone=public --add-service=https

Confirm the site works.

Then save:

sudo firewall-cmd --runtime-to-permanent

Verify:

sudo firewall-cmd --zone=public --list-all

That runtime-first approach gives you a clean testing and rollback workflow.

The Firewall Checklist

Before considering a Linux firewall finished:

  1. Identify required inbound services.
  2. Identify who actually needs access to each service.
  3. Deny unsolicited inbound traffic by default where appropriate.
  4. Allow SSH before enabling restrictive rules remotely.
  5. Restrict management services to trusted sources where possible.
  6. Prefer named services over raw ports where supported.
  7. Check the active interface/zone.
  8. Test rules from another host.
  9. Make firewalld rules permanent only after validation where practical.
  10. Check IPv4 and IPv6 behaviour.
  11. Review container/cloud firewall layers.
  12. Enable useful—not excessive—logging.
  13. Remove obsolete rules.
  14. Document why each non-obvious opening exists.
  15. Maintain console/out-of-band recovery for critical remote systems.

The key principle is:

A firewall should expose exactly what the server needs—nothing more.

UFW vs firewalld at a Glance

UFWfirewalld
Straightforward rule syntaxZone-based management
Common on UbuntuCommon on Fedora/RHEL derivatives
ufw allow 443/tcpfirewall-cmd --add-service=https
Persistence built into UFW rulesRuntime and permanent configs are separate
Easy for simple serversExcellent for dynamic/multi-zone environments
Supports dry runsExcellent runtime-testing workflow

Use whichever is appropriate for the system you are administering.

Do not switch firewall managers simply because another distribution uses a different one.

How Hamilton Group Can Help

Linux firewall problems can range from one missing rule to a poorly designed network exposing services that should never have been internet-accessible.

Hamilton Group can help businesses with:

  • Linux servers
  • UFW
  • firewalld
  • host firewalls
  • SSH security
  • cloud servers
  • network segmentation
  • VPN
  • server hardening
  • vulnerability management
  • managed IT support

For business systems, the goal is not simply to make an application reachable.

It is to make it reachable only from the people and systems that genuinely need it.

Visit hgmssp.com or call 0330 043 0069.