Secure your Linux network with firewall-cmd
Firewalls are a vital part of network security, so it’s important for a sysadmin to be familiar with how they work. If you understand firewalls, you can keep your network secure by making intelligent choices about the traffic you allow in and out.
Because "firewall" is such an exciting name, people often imagine an intricate Tron-style neon battle happening on the outskirts of a network, with packets of rogue data being set alight to protect your users’ the techno fortress. In reality, a firewall is just a piece of software controlling incoming and outgoing network traffic.
Ports
A firewall is able to manage this traffic by monitoring network ports. In the world of firewalls, the term port doesn’t refer to a physical connection like a USB, VGA, or HDMI port. For the purpose of firewalls, a port is an artificial construct created by the operating system to represent a pathway for a specific type of data. This system could have been called anything, like "contacts," "connections," or even "penguins," but it the creators used "ports," and that’s the name that we still use today. The point is, there’s nothing special about any port; they are just a way to designate an address where data transference happens.
There are a number of ports that are well-known, but even these are only conventions. For instance, you may know that HTTP traffic occurs on port 80, HTTPS traffic uses port 443, FTP uses port 21, and SSH uses port 22. When your computer transmits data to another computer, it adds a prefix to the data to indicate which port it wants to access. If the port on the receiving end is accepting data of the same protocol as the data you are sending, then the data is successfully exchanged.
You can see this process in action by going to any website. Open a web browser and navigate to example.com:80
, which causes your computer to send an HTTP request to port 80 of the computer serving the example.com website. You receive a webpage in return. Web browsers don’t require you to enter the port you want to access every time you navigate to a URL, however, because it’s assumed that HTTP traffic accesses port 80 or 443.
You can test this process using a terminal-based web browser:
$ curl --connect-timeout 3 "http://example.com:80" | head -n4
<!doctype html>
<html>
<head>
<title>Example Domain</title>
Using the same notation, you can force rejection by navigating to a website using a nonstandard port. Navigate to an arbitrary port, example.com:79
for instance. Your request for a webpage is declined:
$ curl --connect-timeout 3 "http://example.com:79"
curl: (7) Failed to connect: Network is unreachable
The correlation between ports and protocols are merely conventions mutually agreed upon by a standards group, and a user base. These settings can be changed on individual computers. In fact, back in the pioneer days of computing, many people felt that just changing the port number of popular services would allay an attack. Today, attacks are a lot more sophisticated. There’s little value in surprising an automated port scanner by changing which port a service listens on.
Instead, a firewall governs what activity is permitted on any given port.
[ Free download: Advanced Linux commands cheat sheet. ]
The firewall-cmd interface
Your infrastructure may have a server in a rack with the sole purpose of running a firewall, or you may have a firewall embedded in the router—or modem—acting as your primary gateway to the internet. You probably also have a firewall running on your personal workstation or laptop. All of these firewalls have their own configuration interface. This article covers the firewall-cmd
terminal command found on most Linux distributions.
Firewall-cmd is a front-end tool for managing the firewalld
daemon, which interfaces with the Linux kernel’s netfilter framework. This stack probably isn’t present on the embedded modems common in small- to medium-sized businesses, but it’s on or available for any Linux distribution that uses systemd
.
Without an active firewall, firewall-cmd
has nothing to control, so the first step is to ensure that firewalld
is running:
$ sudo systemctl enable --now firewalld
This command starts the firewall daemon and sets it to auto-load upon reboot.
Block (almost) everything
Common advice when configuring a firewall is to first block everything, and then open the ports you know you actually need. That means you have to know what you need, though, and sometimes figuring that out is an afternoon’s job all its own.
If your organization runs its own DNS or DNS caching service, for instance, then you must remember to unblock the port (usually 53) handling DNS communication. If you rely on SSH to configure your servers remotely, then you must not block that port. You must account for every service running on your infrastructure, and you must understand whether that service is internal-only or whether it needs to interact with the outside world.
In the case of proprietary software, there may be calls made to the outside world that you’re not even aware of. If some applications react poorly to a strict firewall recently put in place, you may have to reverse engineer (or talk to the application’s support line) to discover what kind of traffic it’s trying to create, and why. In the open source world, this issue is less common, but it’s not outside the realm of possibility, especially in the case of complex software stacks (for example, today even media players make calls out to the internet, if only to fetch album art or a track listing).
Firewall-cmd uses zones as presets, giving you sane defaults to choose from. Doing this saves you from having to build a firewall from scratch. Zones apply to a network interface, so on a server with two ethernet interfaces, you may have one zone governing one ethernet interface, and a different zone governing the other.
It’s worth taking time to get familiar with the zones provided on your system. To see all available zones, use:
$ sudo firewall-cmd --get-zones
block dmz drop external home internal public trusted work
To see what’s unblocked in a specific zone:
$ sudo firewall-cmd --zone work --list-all
work
target: default
icmp-block-inversion: no
interfaces: ens3
sources:
services: cockpit dhcpv6-client ssh
ports:
protocols:
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:
Use one of the existing zones as a starting point for your own firewall rules, or just create your own.
Create a zone
To create a new zone, use the --new-zone
option.
All firewall-cmd
actions persist only until the firewall or the computer running it restarts. Anything you want to be permanent must be accompanied by the --permanent
flag.
For an example, create a new permanent zone called corp
, and then reload the firewall rules so that your new zone activates:
$ sudo firewall-cmd --new-zone corp --permanent
success
$ sudo firewall-cmd --reload
Before assigning any network interface to this new zone, add the ssh
service so you can access it remotely. Use the --permanent
option to make this addition persist across reboots:
$ sudo firewall-cmd --zone corp --add-service ssh --permanent
Your new zone, called corp
, is now active, rejects all but SSH traffic, and is assigned to no specific network interface. To make corp
the active and default zone for the network interface you want to protect (ens3
in this example), use the --change-interface
option:
$ firewall-cmd --change-interface ens3 \
--zone corp --permanent
The interface is under control of NetworkManager, setting zone to 'corp'.
success
By making corp
the default zone, all future commands are applied to corp
unless the --zone
option specifies a different zone. Whether you want to set corp
as the default depends on whether you plan to this zone as your new primary zone. If so, the following does the job:
$ sudo firewall-cmd --set-default corp
To view the zones currently assigned to each interface, use the --get-active-zones
option:
$ sudo firewall-cmd --get-active-zones``
corp
interfaces: ens3
work
interfaces: ens4
Add and remove services
Now that you’ve blocked everything but SSH, you can open the ports your network relies upon. The quick and easy way to permit traffic through your firewall is to add a predefined service.
The list of available predefined services is extensive. To view it, use:
$ sudo firewall-cmd --get-services
RH-Satellite-6 amanda-client amqp
amqps apcupsd audit bacula bacula-client
bgp bitcoin bitcoin-rpc bitcoin-testnet ceph
cockpit dhcp dhcpv6 dhcpv6-client distcc dns
[...]
Assume you need to run a webserver. First, you would install the webserver you want to use (the httpd
package on RHEL or Fedora, apache2
on Ubuntu and Debian). For this example, we’ll use httpd
:
$ sudo dnf install httpd
$ sudo systemctl --enable --now httpd
Then, test your webserver locally:
$ curl --silent localhost:80 | grep title
<title>Test Page for the Apache HTTP Server on Red Hat Enterprise Linux</title>
Next, attempt to connect to your webserver from an external browser. The connection fails, demonstrating that the firewall is effective:
$ curl --connect-timeout 3 192.168.122.206
curl: (28) Connection timed out after 3001 milliseconds
Unblock a service
To permit HTTP traffic through your firewall, add the http
service:
$ sudo firewall-cmd --add-service http --permanent
$ sudo firewall-cmd --reload
Then, test from an outside source:
$ curl --silent 192.168.122.206 | grep title
<title>Test Page for the Apache HTTP Server on Red Hat Enterprise Linux</title>
Now that you know how to add a service, removing one is fairly intuitive:
$ sudo firewall-cmd --remove-service http --permanent
$ sudo firewall-cmd --reload
Add and remove ports
Sometimes, a predefined service doesn’t exist, or it assumes defaults that don’t match your network. Instead of adding a service, you can add a port number and protocol type directly with --add-port
.
For instance, if you need to add the non-standard port 1622 for SSH to your custom zone (if your custom zone isn’t the default zone for your commands, add the --zone
option):
$ sudo firewall-cmd --add-port 1622/tcp --permanent
success
$ sudo firewall-cmd --reload
To remove that port, use --remove-port
:
$ sudo firewall-cmd --remove-port 1622/tcp --permanent
success
$ sudo firewall-cmd --reload
Walls of fire
There’s a lot more you can do with firewall-cmd
, such as defining your own services, ICMP blocking, and defining sources of permissible incoming traffic. The best way to learn is to experiment, so install Red Hat Enterprise Linux or Fedora in GNOME Boxes, and experiment with shaping traffic through all of the options firewall-cmd
provides.
Seth Kenlon
Seth Kenlon is a UNIX geek and free software enthusiast. More about me