Linux firewalls: What you need to know about iptables and firewalld

Here's how to use the iptables and firewalld tools to manage Linux firewall connectivity rules.
410 readers like this.
open network

Opensource.com

This article is excerpted from my book, Linux in Action, and a second Manning project that’s yet to be released.

The firewall

A firewall is a set of rules. When a data packet moves into or out of a protected network space, its contents (in particular, information about its origin, target, and the protocol it plans to use) are tested against the firewall rules to see if it should be allowed through. Here’s a simple example:

firewall filtering request

A firewall can filter requests based on protocol or target-based rules.

On the one hand, iptables is a tool for managing firewall rules on a Linux machine.

On the other hand, firewalld is also a tool for managing firewall rules on a Linux machine.

You got a problem with that? And would it spoil your day if I told you that there was another tool out there, called nftables?

OK, I’ll admit that the whole thing does smell a bit funny, so let me explain. It all starts with Netfilter, which controls access to and from the network stack at the Linux kernel module level. For decades, the primary command-line tool for managing Netfilter hooks was the iptables ruleset.

Because the syntax needed to invoke those rules could come across as a bit arcane, various user-friendly implementations like ufw and firewalld were introduced as higher-level Netfilter interpreters. Ufw and firewalld are, however, primarily designed to solve the kinds of problems faced by stand-alone computers. Building full-sized network solutions will often require the extra muscle of iptables or, since 2014, its replacement, nftables (through the nft command line tool).

iptables hasn’t gone anywhere and is still widely used. In fact, you should expect to run into iptables-protected networks in your work as an admin for many years to come. But nftables, by adding on to the classic Netfilter toolset, has brought some important new functionality.

From here on, I’ll show by example how firewalld and iptables solve simple connectivity problems.

Configure HTTP access using firewalld

As you might have guessed from its name, firewalld is part of the systemd family. Firewalld can be installed on Debian/Ubuntu machines, but it’s there by default on Red Hat and CentOS. If you’ve got a web server like Apache running on your machine, you can confirm that the firewall is working by browsing to your server’s web root. If the site is unreachable, then firewalld is doing its job.

You’ll use the firewall-cmd tool to manage firewalld settings from the command line. Adding the –state argument returns the current firewall status:

# firewall-cmd --state
running

By default, firewalld will be active and will reject all incoming traffic with a couple of exceptions, like SSH. That means your website won’t be getting too many visitors, which will certainly save you a lot of data transfer costs. As that’s probably not what you had in mind for your web server, though, you’ll want to open the HTTP and HTTPS ports that by convention are designated as 80 and 443, respectively. firewalld offers two ways to do that. One is through the –add-port argument that references the port number directly along with the network protocol it’ll use (TCP in this case). The –permanent argument tells firewalld to load this rule each time the server boots:

# firewall-cmd --permanent --add-port=80/tcp
# firewall-cmd --permanent --add-port=443/tcp

The –reload argument will apply those rules to the current session:

# firewall-cmd --reload

Curious as to the current settings on your firewall? Run –list-services:

# firewall-cmd --list-services
dhcpv6-client http https ssh

Assuming you’ve added browser access as described earlier, the HTTP, HTTPS, and SSH ports should now all be open—along with dhcpv6-client, which allows Linux to request an IPv6 IP address from a local DHCP server.

Configure a locked-down customer kiosk using iptables

I’m sure you’ve seen kiosks—they’re the tablets, touchscreens, and ATM-like PCs in a box that airports, libraries, and business leave lying around, inviting customers and passersby to browse content. The thing about most kiosks is that you don’t usually want users to make themselves at home and treat them like their own devices. They’re not generally meant for browsing, viewing YouTube videos, or launching denial-of-service attacks against the Pentagon. So to make sure they’re not misused, you need to lock them down.

One way is to apply some kind of kiosk mode, whether it’s through clever use of a Linux display manager or at the browser level. But to make sure you’ve got all the holes plugged, you’ll probably also want to add some hard network controls through a firewall. In the following section, I'll describe how I would do it using iptables.

There are two important things to remember about using iptables: The order you give your rules is critical, and by themselves, iptables rules won’t survive a reboot. I’ll address those here one at a time.

The kiosk project

To illustrate all this, let’s imagine we work for a store that’s part of a larger chain called BigMart. They’ve been around for decades; in fact, our imaginary grandparents probably grew up shopping there. But these days, the guys at BigMart corporate headquarters are probably just counting the hours before Amazon drives them under for good.

Nevertheless, BigMart’s IT department is doing its best, and they’ve just sent you some WiFi-ready kiosk devices that you’re expected to install at strategic locations throughout your store. The idea is that they’ll display a web browser logged into the BigMart.com products pages, allowing them to look up merchandise features, aisle location, and stock levels. The kiosks will also need access to bigmart-data.com, where many of the images and video media are stored.

Besides those, you’ll want to permit updates and, whenever necessary, package downloads. Finally, you’ll want to permit inbound SSH access only from your local workstation, and block everyone else. The figure below illustrates how it will all work:

kiosk traffic flow ip tables

The kiosk traffic flow being controlled by iptables.

The script

Here’s how that will all fit into a Bash script:

#!/bin/bash
iptables -A OUTPUT -p tcp -d bigmart.com -j ACCEPT
iptables -A OUTPUT -p tcp -d bigmart-data.com -j ACCEPT
iptables -A OUTPUT -p tcp -d ubuntu.com -j ACCEPT
iptables -A OUTPUT -p tcp -d ca.archive.ubuntu.com -j ACCEPT
iptables -A OUTPUT -p tcp --dport 80 -j DROP
iptables -A OUTPUT -p tcp --dport 443 -j DROP
iptables -A INPUT -p tcp -s 10.0.3.1 --dport 22 -j ACCEPT
iptables -A INPUT -p tcp -s 0.0.0.0/0 --dport 22 -j DROP

The basic anatomy of our rules starts with -A, telling iptables that we want to add the following rule. OUTPUT means that this rule should become part of the OUTPUT chain. -p indicates that this rule will apply only to packets using the TCP protocol, where, as -d tells us, the destination is bigmart.com. The -j flag points to ACCEPT as the action to take when a packet matches the rule. In this first rule, that action is to permit, or accept, the request. But further down, you can see requests that will be dropped, or denied.

Remember that order matters. And that’s because iptables will run a request past each of its rules, but only until it gets a match. So an outgoing browser request for, say, youtube.com will pass the first four rules, but when it gets to either the –dport 80 or –dport 443 rule—depending on whether it’s an HTTP or HTTPS request—it’ll be dropped. iptables won’t bother checking any further because that was a match.

On the other hand, a system request to ubuntu.com for a software upgrade will get through when it hits its appropriate rule. What we’re doing here, obviously, is permitting outgoing HTTP or HTTPS requests to only our BigMart or Ubuntu destinations and no others.

The final two rules will deal with incoming SSH requests. They won’t already have been denied by the two previous drop rules since they don’t use ports 80 or 443, but 22. In this case, login requests from my workstation will be accepted but requests for anywhere else will be dropped. This is important: Make sure the IP address you use for your port 22 rule matches the address of the machine you’re using to log in—if you don’t do that, you’ll be instantly locked out. It's no big deal, of course, because the way it’s currently configured, you could simply reboot the server and the iptables rules will all be dropped. If you’re using an LXC container as your server and logging on from your LXC host, then use the IP address your host uses to connect to the container, not its public address.

You’ll need to remember to update this rule if my machine’s IP ever changes; otherwise, you’ll be locked out.

Playing along at home (hopefully on a throwaway VM of some sort)? Great. Create your own script. Now I can save the script, use chmod to make it executable, and run it as sudo. Don’t worry about that bigmart-data.com not found error—of course it’s not found; it doesn’t exist.

chmod +X scriptname.sh
sudo ./scriptname.sh

You can test your firewall from the command line using cURL. Requesting ubuntu.com works, but manning.com fails.

curl ubuntu.com
curl manning.com

Configuring iptables to load on system boot

Now, how do I get these rules to automatically load each time the kiosk boots? The first step is to save the current rules to a .rules file using the iptables-save tool. That’ll create a file in the root directory containing a list of the rules. The pipe, followed by the tee command, is necessary to apply my sudo authority to the second part of the string: the actual saving of a file to the otherwise restricted root directory.

I can then tell the system to run a related tool called iptables-restore every time it boots. A regular cron job of the kind we saw in the previous module won’t help because they’re run at set times, but we have no idea when our computer might decide to crash and reboot.

There are lots of ways to handle this problem. Here’s one:

On my Linux machine, I’ll install a program called anacron that will give us a file in the /etc/ directory called anacrontab. I’ll edit the file and add this iptables-restore command, telling it to load the current values of that .rules file into iptables each day (when necessary) one minute after a boot. I’ll give the job an identifier (iptables-restore) and then add the command itself. Since you’re playing along with me at home, you should test all this out by rebooting your system.

sudo iptables-save | sudo tee /root/my.active.firewall.rules
sudo apt install anacron
sudo nano /etc/anacrontab
1 1 iptables-restore iptables-restore < /root/my.active.firewall.rules

I hope these practical examples have illustrated how to use iptables and firewalld for managing connectivity issues on Linux-based firewalls.

David Clinton
DAVID CLINTON is a system administrator, teacher, and writer. He has administered, written about, and created training material for many important technology subjects including Linux systems, cloud computing (AWS in particular), and container technologies like Docker.

10 Comments

You might also look into the iptables-persistent package as a way to automatically restore iptables at bootup. Easier than using anacron.

Thanks for explaining this. Up until now, every time I've played with iptables I have just been blindly following someone's instructions without understanding what I'm doing :p

Hi David,

In the following lines:

iptables -A OUTPUT -p tcp -d bigmart.com -j ACCEPT
iptables -A OUTPUT -p tcp -d bigmart-data.com -j ACCEPT
iptables -A OUTPUT -p tcp -d ubuntu.com -j ACCEPT
iptables -A OUTPUT -p tcp -d ca.archive.ubuntu.com -j ACCEPT

it appears that *all* TCP traffic, not just TCP 80 and 443, will be allowed to these four hosts. That's because the "--dport" (or "destination port") switch was not used. Was that your intention?

Lines 5 and 6 illustrate this well. Here, you *will* block TCP ports 80 and 443, and only those two. Everything else will be allowed out, including, say, proxies that run on TCP 8080, TCP 8443, and so on.

The SSH-specific block is OK. However, it only blocks SSH from other hosts. Sounds like a kiosk machine would want to block more inbound than that.

My approach for locking down traffic would be to allow specifically only TCP 80 and 443 to those hosts, along with DNS traffic to only my DNS server, outbound, like so.

#!/bin/bash
# Let's turn on connection tracking, a.k.a. stateful tracking, to let
# our permitted outbound traffic come back in
iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

# Allow loopback to talk to us, otherwise that can break some things
iptables -A INPUT -i lo -j ACCEPT

# First, allow DNS outbound to only my DNS server, both UDP and TCP
iptables -A OUTPUT -p udp -d --dport 53 -j ACCEPT
iptables -A OUTPUT -p tcp -d --dport 53 -j ACCEPT

# Then, let the Web traffic that we want to let out, out
iptables -A OUTPUT -p tcp -d bigmart.com --dport 80 -j ACCEPT
iptables -A OUTPUT -p tcp -d bigmart.com --dport 443 -j ACCEPT
iptables -A OUTPUT -p tcp -d bigmart-data.com --dport 80 -j ACCEPT
iptables -A OUTPUT -p tcp -d bigmart-data.com --dport 443 -j ACCEPT
iptables -A OUTPUT -p tcp -d ubuntu.com --dport 80 -j ACCEPT
iptables -A OUTPUT -p tcp -d ubuntu.com --dport 443 -j ACCEPT
iptables -A OUTPUT -p tcp -d ca.archive.ubuntu.com --dport 80 -j ACCEPT
iptables -A OUTPUT -p tcp -d ca.archive.ubuntu.com --dport 443 -j ACCEPT

# Allow SSH traffic only from that one workstation
iptables -A INPUT -p tcp -s 10.0.3.1 --dport 22 -j ACCEPT

# Uncomment the next two lines to allow DHCP if need be,
# i. e. if this box gets its IP address via DHCP (not statically assigned)
#iptables -A INPUT -p udp --dport 68
#iptables -A OUTPUT -p udp --dport 67

# Deny everything else!
iptables -A INPUT -s 0.0.0.0/0 -j DROP
iptables -A OUTPUT -d 0.0.0.0/0 -j DROP

Hi,
You're correct that my rules did allow all outbound traffic. I did this because I can't be 100% sure that software and data upgrades won't require access through a different (non-HTTP) port. The idea, though, is that I fully trust those four sites, so allowing full access isn't a major risk.
Thanks!

In reply to by Sum Yung Gai (not verified)

Great information, and well written.

I'm not very good with networking, but this all seems to make sense (so far).

Except nearly everything he wrote about iptables is incorrect.

He didn't even reference /etc/sysconfig/iptables which is the file that gets read by default when iptables starts up.

In reply to by dragonbite

Good article. I notice the article doesn't mention system-config-firewall
or lokkit. I still use both on el6 servers.

And I just discovered that lokkit is available on el7. Is anyone using that?

"iptables rules won’t survive a reboot" false statement.

iptables-save > /etc/sysconfig/iptables

Done.

Hello, interesting article!

Two minor things, though:

1- cron can actually run commands upon a reboot, you'd use a line like this:

@reboot /sbin/iptables-restore < /root/my.active.firewall.rules

Keep in mind that cron's PATH does not contain /sbin, /usr/sbin or /usr/local/sbin, so make sure to use the full path to iptables-restore.

2- I usually favor REJECT instead of DROP for iptables rules. The latter will drop packets, meaning you'll have to wait for a (looong) timeout, the former will give you an instant deny.

Cheers!

Creative Commons LicenseThis work is licensed under a Creative Commons Attribution-Share Alike 4.0 International License.