Firewalls

Listen to this lesson

Episode 20 · 44:57

This episode is a study companion for CompTIA Linux+ XK0-006 and is not produced by or endorsed by CompTIA.

Objective 3.2 · Security · 18% of the exam

Why this matters

The firewall is the first control you reach for once a machine is reachable, and the one most likely to lock you out of it. Every Linux firewall is a front end to the same kernel machinery, so the concepts transfer even when the commands do not.

Two ideas do most of the work: runtime versus permanent, which is how you avoid locking yourself out, and stateful versus stateless, which is why a sane ruleset is four lines rather than four hundred.

The lesson

One engine, several front ends

Underneath everything is Netfilter, the kernel's packet-filtering framework. Rules are expressed to it by one of:

  • iptables — the long-standing interface. Still everywhere, often now a compatibility shim translating to nftables underneath.
  • nftables — the modern replacement. One tool for IPv4, IPv6, ARP and bridging, better performance, cleaner syntax.
  • firewalld — a higher-level daemon with zones and services, default on RHEL/Fedora.
  • ufw — the Uncomplicated Firewall, a simple front end, default on Ubuntu.

You configure one of these; they all end up as Netfilter rules. Running two at once produces conflicting rules and unpredictable behaviour, so pick the one your distribution ships and disable the others.

Stateful versus stateless

A stateless filter judges each packet alone. To allow outbound web browsing you must also write a rule permitting the replies — and since replies come from arbitrary ports, that rule is uncomfortably broad.

A stateful filter tracks connections. It remembers that you sent a SYN to example.com:443 and automatically permits the packets that belong to that conversation. One rule covers every reply to every connection you initiated:

iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

That single line is why a sane ruleset is short. Allow established and related traffic, allow the handful of ports you serve, drop the rest. Everything your machine initiates works, and nothing unsolicited gets in. All the tools above are stateful by default; understanding why is what the exam is after.

firewalld

firewalld organises rules into zones — named policies you attach interfaces or sources to. public is restrictive, trusted allows everything, internal and dmz sit between. The zone decides what its interfaces may do.

firewall-cmd --state
firewall-cmd --get-active-zones
firewall-cmd --list-all                    # the default zone in full
firewall-cmd --get-default-zone
firewall-cmd --set-default-zone=public
firewall-cmd --zone=internal --change-interface=eth1

Ports versus services. You can open a raw port, or a named service that bundles the right ports and any helper modules:

firewall-cmd --add-service=https           # by name — preferred
firewall-cmd --add-port=8080/tcp           # by number
firewall-cmd --list-services
firewall-cmd --get-services                # everything predefined

Prefer services: --add-service=samba opens several ports correctly, and the name documents intent in a way --add-port=445/tcp does not.

Runtime versus permanent — the important one:

firewall-cmd --add-service=http                        # RUNTIME only, lost on reload
firewall-cmd --permanent --add-service=http            # PERMANENT, not active yet
firewall-cmd --reload                                  # load permanent into runtime
firewall-cmd --runtime-to-permanent                    # save what is running

This split is a safety feature, not an annoyance. Test a rule in runtime first. If it locks you out, firewall-cmd --reload — or a reboot — restores the permanent configuration and you are back in. Adding --permanent first and getting it wrong means the lockout survives the reboot too.

The reverse trap is equally common: add a rule without --permanent, confirm it works, and lose it at the next reload. --runtime-to-permanent promotes the tested rules in one step.

Rich rules express what simple ones cannot — source addresses, logging, rate limits, rejection with a specific message:

firewall-cmd --permanent --add-rich-rule='rule family="ipv4" \
  source address="10.0.0.0/8" service name="ssh" accept'

firewall-cmd --permanent --add-rich-rule='rule family="ipv4" \
  source address="203.0.113.5" drop'

firewall-cmd --permanent --add-rich-rule='rule service name="ssh" \
  log prefix="SSH " level="info" limit value="3/m" accept'

"SSH only from the internal network" is the canonical rich rule and a very common real requirement.

ufw

Ubuntu's front end, built for the common cases.

ufw status verbose
ufw enable
ufw disable
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp
ufw allow ssh                      # by service name from /etc/services
ufw allow from 10.0.0.0/8 to any port 22
ufw limit ssh                      # rate-limit: 6 attempts in 30s
ufw delete allow 22/tcp
ufw status numbered
ufw reset

ufw limit is worth knowing — it throttles repeated connections from one address, which blunts SSH brute-forcing without any extra software.

Enabling ufw with no allow rule for SSH will disconnect you. ufw allow ssh first, ufw enable second. In that order, always.

nftables and iptables

# nftables
nft list ruleset
nft add table inet filter
nft add chain inet filter input '{ type filter hook input priority 0; policy drop; }'
nft add rule inet filter input ct state established,related accept
nft add rule inet filter input tcp dport 22 accept

# iptables
iptables -L -n -v                  # list with counters
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -P INPUT DROP             # default policy
iptables -D INPUT 3                # delete rule 3
iptables-save > /etc/iptables/rules.v4

Order matters and the first match wins. A rule after a blanket DROP is never reached, which is the most common reason a rule "does not work" — it is present and unreachable. iptables -L -n -v shows packet counters per rule, and a rule with zero packets is one nothing is hitting.

iptables rules are not persistent by default. They vanish on reboot unless saved (iptables-save, netfilter-persistent, or the iptables-services package). That is a real trap and, like ip, occasionally a useful escape hatch.

ipset holds large collections of addresses that rules can reference as a single object:

ipset create blacklist hash:net
ipset add blacklist 203.0.113.0/24
iptables -A INPUT -m set --match-set blacklist src -j DROP

Matching one set is dramatically faster than walking ten thousand individual rules, and updating the set needs no rule changes at all — which is how automated blocklists are implemented.

Forwarding and NAT

By default Linux does not forward packets between interfaces. To make it a router or a container host you must enable it:

sysctl net.ipv4.ip_forward                     # read
sysctl -w net.ipv4.ip_forward=1                # set, until reboot
echo "net.ipv4.ip_forward = 1" > /etc/sysctl.d/99-forward.conf
sysctl --system                                # permanent

Network address translation rewrites addresses as packets pass through.

SNATsource NAT — rewrites the source address of outbound packets, so machines on a private network appear to come from the router's public address. MASQUERADE is SNAT for a dynamic public IP.

DNATdestination NAT — rewrites the destination of inbound packets, sending traffic arriving at the public address to an internal host. This is port forwarding.

PATport address translation — is SNAT that also rewrites source ports, so many internal hosts share one public address, distinguished by port. This is what almost every home and office router does, and it is what people usually mean by "NAT".

# SNAT / masquerade for outbound
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

# DNAT: public :80 to an internal web server
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 \
  -j DNAT --to-destination 10.0.0.10:80

# firewalld equivalents
firewall-cmd --permanent --add-masquerade
firewall-cmd --permanent --add-forward-port=port=80:proto=tcp:toport=80:toaddr=10.0.0.10

The direction is the thing to fix in memory: SNAT is applied on the way out (POSTROUTING), DNAT on the way in (PREROUTING). Forwarding must also be enabled, or the rules are correct and nothing passes.

Locking yourself out, and not

The realistic hazard of this whole lesson. Before changing firewall rules on a remote machine:

  • Make the change in runtime only, confirm it works, then promote it.
  • Or schedule an escape: echo "firewall-cmd --reload" | at now + 5 minutes, so a lockout heals itself.
  • Have console access — a hypervisor console, IPMI, or a colleague — before you need it.

On the exam

  • Runtime versus permanent in firewalld: without --permanent a rule is lost on reload; with it, it is not active until --reload. --runtime-to-permanent saves tested rules.
  • firewalld zones attach policy to interfaces or sources; services are named bundles of ports.
  • Stateful filtering with ESTABLISHED,RELATED is why replies need no rule.
  • iptables is first-match-wins, and rules after a DROP are unreachable.
  • iptables rules are not persistent unless saved.
  • ufw allow ssh before ufw enable.
  • SNAT/MASQUERADE rewrites the source on the way out (POSTROUTING); DNAT rewrites the destination on the way in (PREROUTING); PAT shares one address across many hosts by port.
  • net.ipv4.ip_forward=1 is required for any routing, and sysctl -w alone does not survive a reboot.
  • nftables is the modern engine; ipset makes large address lists fast.

Practise what you just read

1. An administrator runs "firewall-cmd --add-service=https" and the rule works. After a reload it is gone. What was omitted?

Select one

  1. --reload, which activates the rule
  2. --permanent, which writes the rule to the saved configuration
  3. firewall-cmd --runtime-to-permanent, which is the only way to add rules
  4. --zone=public, which selects the correct zone
Show answer

B. firewalld keeps a runtime configuration and a permanent one. Without --permanent a rule applies now and is lost at the next reload or reboot; with --permanent it is saved but not active until --reload. The safe pattern is to test in runtime, then run firewall-cmd --runtime-to-permanent once it works.

2. In iptables, a permissive ACCEPT rule is placed above a restrictive DROP rule for the same traffic. What is the effect?

Select one

  1. Both rules apply and the more restrictive one takes precedence
  2. The chain is rejected as contradictory when loaded
  3. The rules alternate on successive packets
  4. The DROP rule never applies, because the first match wins
Show answer

D. iptables and nftables evaluate rules in order and stop at the first match, so a rule below one that already matched the traffic is dead code. This is why -I, which inserts at the top, and -A, which appends at the bottom, have such different consequences, and why iptables -L --line-numbers is worth reading before adding anything.

3. A new Ubuntu server is configured over SSH. Which order of commands avoids being locked out?

Select one

  1. ufw allow ssh, then ufw enable
  2. ufw enable, then ufw allow ssh
  3. ufw reset, then ufw enable
  4. ufw enable, then ufw default allow incoming
Show answer

A. ufw enable applies a default-deny policy for incoming traffic immediately, which drops your own SSH session if no rule permits it. Adding the rule first means the policy takes effect with SSH already allowed. The same reasoning applies to firewalld, where --timeout adds a rule that reverts itself if you cannot confirm it.

6 more questions on this objective are part of the full course.

Practise the full question bank in the exam simulator

Hands-on labs

All hands-on labs