Kahibaro
Discord Login Register

4.5.5 Firewalls in depth

Understanding Linux Firewalls in Depth

The Role of Firewalls on Linux

A firewall on Linux controls which network traffic is allowed in or out of the system, based on a set of rules. At an advanced level, a firewall is not only a simple barrier. It is a policy engine that enforces how packets flow between interfaces, IP addresses, ports, and applications.

Linux firewalls are mostly implemented in the kernel. User space tools like iptables, nft, firewalld, and ufw only manage rules and send them to the kernel. Once the rules are loaded, the kernel evaluates each packet according to those rules without involving user space again, except when a rule explicitly triggers user space, such as logging or helper daemons.

A server administrator must understand both the low level packet filtering layer in the kernel and the higher level management tools that sit on top. In particular, you should understand that iptables and nftables are interfaces to kernel hooks, while firewalld and ufw are policy managers that generate rules for those interfaces.

A firewall is effective only if you have a clear policy. The default policy should be to deny unsolicited inbound traffic and allow only what is explicitly needed.

Netfilter, Tables, and Chains

Linux firewalling is built around the Netfilter framework in the kernel. You do not manipulate Netfilter directly. Instead you use tools that operate on tables and chains.

A table is a collection of chains, and a chain is an ordered list of rules that packets traverse. A rule has matching conditions, such as protocol, IP, port, interface, connection state, and a target action, such as ACCEPT, DROP, or jump to another chain.

Different tables are responsible for different kinds of processing. In the traditional iptables world, you encounter several standard tables, but the most common are filter, nat, and mangle.

The filter table is the default and is responsible for deciding whether packets are allowed. It contains built in chains such as INPUT, OUTPUT, and FORWARD. The nat table is used for network address translation like SNAT and DNAT. It is consulted at the beginning and very end of packet processing to decide if and how addresses and ports should be rewritten. The mangle table is used to alter packet headers and set marks that can be used for routing decisions or quality of service.

Chains correspond to specific points in the packet processing path. In the classic model, PREROUTING happens before routing decisions, INPUT is for packets destined to the local machine, FORWARD is for packets being routed through the machine, OUTPUT is for locally generated packets, and POSTROUTING is for packets just before they leave an interface.

For host firewalls, the INPUT chain in the filter table is the primary place to control inbound connections to local services. The OUTPUT chain is used to restrict what the host itself can reach.

iptables and nftables

On many Linux systems you will still see iptables or compatibility mode in use. iptables is the traditional user space tool that programs Netfilter. It works with separate tools for different protocol families, such as iptables for IPv4 and ip6tables for IPv6.

A basic iptables rule consists of matches followed by a jump target. For example, the following rule allows SSH connections from anywhere to the local machine:

iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -p tcp --sport 22 -m conntrack --ctstate ESTABLISHED -j ACCEPT

Here, the rule matches TCP packets destined to port 22 and uses the connection tracking module to allow new and established connections. Another rule allows responses out from the server. The order of rules is important because chains are processed sequentially until a match is found or the chain ends.

nftables is the more modern replacement framework and tool. It uses a single utility nft for both IPv4 and IPv6, and uses a different configuration language based on sets and rules within named chains. Instead of separate per table commands, you define a table and then chains and rules inside it.

An example of a simple nftables configuration for filtering inbound traffic on a single host could be:

table inet filter {
  chain input {
    type filter hook input priority 0;
    policy drop;
    ct state established,related accept
    iif "lo" accept
    tcp dport 22 accept
  }
}

Here, table inet filter indicates a table that applies to both IPv4 and IPv6. The input chain is hooked into the input point of packet processing. The default policy is set to drop, so anything not explicitly accepted is dropped. Rules then allow established and related connections, traffic on the loopback interface, and new connections to SSH.

Do not mix native iptables rules and native nftables rules on the same system without understanding how your distribution integrates them. Many distributions now implement iptables commands by translating them to nftables under the hood.

Packet Flow and Rule Evaluation

To design robust firewall rules, you must understand how a packet moves through Netfilter hooks. For traffic destined to the local host, the path is conceptually:

$$PREROUTING \rightarrow INPUT \rightarrow local\ process \rightarrow OUTPUT \rightarrow POSTROUTING$$

For traffic routed through the host, the path is:

$$PREROUTING \rightarrow FORWARD \rightarrow POSTROUTING$$

Each of these hooks can have tables and chains attached. The evaluation order affects how NAT and filtering interact.

For example, a packet arriving on an external interface is first considered by the nat table in the PREROUTING chain. If you have DNAT rules, the destination address and port may be rewritten here. After routing decides whether the packet is for the local host or should be forwarded, the packet continues into the INPUT or FORWARD chain in the filter table. This means the filter rules usually see the translated destination address for DNAT. Understanding that sequence is essential when debugging port forwarding issues.

Connection tracking is a kernel feature that records the state of flows between endpoints. The firewall rules can inspect the state, such as NEW, ESTABLISHED, RELATED, or INVALID. A common pattern is to drop everything by default, then allow established and related traffic. That way, return traffic for outbound connections can pass without having to account for every response port.

A typical host firewall design in either iptables or nftables will therefore:

  1. Set the default INPUT policy to DROP.
  2. Allow loopback traffic.
  3. Allow established and related connections.
  4. Allow inbound connections to specific services, for example, SSH or web servers.
  5. Optionally log dropped packets for auditing.

Whenever you change firewall rules over SSH, always have a way to undo changes automatically such as a scheduled rollback command, to avoid locking yourself out of the server.

NAT, Port Forwarding, and Masquerading

Firewalls on routers and gateways often handle address translation in addition to simple filtering. Network Address Translation, or NAT, modifies packet headers so that many private internal hosts can share a smaller number of public addresses, or so that a service behind a firewall is reachable via a specific external port.

In Linux, outbound NAT is commonly done with masquerading or SNAT. Masquerading is a dynamic form of SNAT where the firewall uses the address of the outbound interface. A typical iptables rule for outbound NAT on an internet gateway might be:

iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

This instructs the kernel to rewrite the source address of packets leaving on eth0 to the address assigned to eth0, and to track the mapping so replies can be demasqueraded. In nftables, a similar configuration might be:

table ip nat {
  chain postrouting {
    type nat hook postrouting priority 100;
    oif "eth0" masquerade
  }
}

Port forwarding is achieved using DNAT. This allows connections to a public IP and port to be forwarded to a private internal address and port. With iptables, a simple example is:

iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 \
  -j DNAT --to-destination 192.168.1.100:80

In nftables, the equivalent could be:

table ip nat {
  chain prerouting {
    type nat hook prerouting priority -100;
    iif "eth0" tcp dport 80 dnat to 192.168.1.100:80
  }
}

When using NAT, especially on multi interface firewalls, the combination of PREROUTING, FORWARD, and POSTROUTING rules must be consistent. The NAT table handles the address changes, while the filter table must allow the forwarded traffic. Forgetting to add corresponding rules in FORWARD is a frequent source of problems.

NAT rules do not replace filter rules. Always ensure that forwarded traffic is permitted in the FORWARD chain of the filter table, otherwise packets will still be dropped even if NAT works.

firewalld, Zones, and Rich Rules

Some Linux distributions, especially Fedora, RHEL, and related systems, use firewalld as the primary firewall management daemon. firewalld operates as a high level abstraction over iptables or nftables, depending on configuration. It manages rules dynamically without needing to reload the entire firewall, and it introduces concepts like zones and services.

A firewalld zone is a named trust level that groups rules for one or more network interfaces. Common zones include public, home, work, and trusted. Each zone has a default behavior and a list of allowed services, ports, and other settings. For example, a laptop might mark Wi Fi networks as public, allowing only SSH and DNS, while marking a home network as home, allowing additional services like file sharing.

Interfaces and source addresses can be assigned to zones. When a packet arrives, firewalld determines which zone applies and then evaluates that zone's configuration.

Services in firewalld are predefined rule sets that open one or more ports and may include additional helper settings. The service definitions are stored in XML files that map service names like ssh or http to the underlying ports and protocols.

Rich rules in firewalld resemble more detailed firewall rules that you might write by hand in iptables. They allow matching on source and destination addresses, ports, and protocols, along with actions like accept, reject, or log. This provides more flexibility than simple service definitions, while still using firewalld as the manager.

For example, a rich rule might be defined to allow SSH only from a specific network:

firewall-cmd --permanent --zone=public \
  --add-rich-rule='rule family="ipv4" source address="203.0.113.0/24" service name="ssh" accept'

Here, the rule applies only to IPv4, matches a specific source network, and then allows SSH. Using --permanent writes the configuration to disk. Without it, the rule applies only until the next reload or restart.

firewalld also distinguishes between runtime configuration and permanent configuration. Runtime configuration applies immediately and is lost on reboot unless saved. Permanent configuration is loaded at boot. This split allows testing rules before committing them.

When using firewalld, do not edit iptables or nftables rules manually at the same time. Let firewalld own the firewall configuration to avoid conflicts.

UFW and Other Simplified Frontends

On Debian and Ubuntu based systems, Uncomplicated Firewall, or UFW, is a common frontend to iptables or nftables. It aims to make typical host firewall tasks easy without requiring knowledge of tables and chains.

UFW uses a simple syntax focused on applications, ports, and addresses. For example, allowing SSH can be as simple as:

ufw allow ssh

or using explicit port and protocol:

ufw allow 22/tcp

Rules can be restricted to specific subnets:

ufw allow from 192.168.1.0/24 to any port 22 proto tcp

UFW distinguishes between rules that apply to incoming and outgoing traffic. By default, UFW often allows all outgoing and denies most incoming. As with firewalld, UFW should be the only tool configuring the firewall on the system that uses it.

Although UFW hides the details, it still generates underlying rules using the lower level firewall framework. For advanced cases, such as sophisticated NAT or routing firewalls, you may need to bypass UFW and configure the firewall directly. For common host security use cases however, UFW provides a straightforward interface.

Enabling UFW on a remote server without first allowing SSH will likely block your current connection. Always add the necessary allow rules before enabling or tightening a firewall frontend.

Stateful vs Stateless Filtering

Modern Linux firewalls are stateful by default, using the connection tracking system. A stateful firewall keeps track of active connections and can automatically allow replies and related traffic, which simplifies rules and improves security. For example, you can allow outbound HTTPS connections and then automatically allow inbound packets that are part of those connections, even though they might come from dynamic high ports.

In contrast, a stateless firewall treats each packet in isolation. With IPv4 and TCP this is sometimes workable, but it becomes difficult or impossible with more complex protocols and fragmentation. Linux still supports stateless rules, but they are rarely enough for full host or gateway protection.

The connection state categories exposed to firewall rules include:

NEW, for packets that start a new connection.
ESTABLISHED, for packets that are part of an existing, fully established connection.
RELATED, for packets that start a new connection that is associated with an existing one, such as an FTP data connection associated with a control connection.
INVALID, for packets that do not fit any valid tracked connection.

An effective policy for inbound traffic usually allows ESTABLISHED and RELATED while dropping INVALID and restricting NEW to specific services. For outbound traffic, you can choose to restrict NEW connections to a set of ports, for example allowing only HTTP, HTTPS, and DNS.

Using stateful filtering also makes certain denial of service mitigations more effective, since the firewall can drop unexpected packets that do not match known flows without bothering higher level applications.

Stateful firewalls rely on connection tracking tables in the kernel. High traffic environments must monitor and tune these tables, or connection tracking exhaustion can itself become a denial of service vector.

Logging, Auditing, and Troubleshooting

Firewall logging is a crucial part of security monitoring and troubleshooting. Logging rules can be inserted to record matched packets before they are dropped or accepted. This allows you to see if a particular kind of traffic is being blocked, if unexpected traffic is reaching your server, or if someone is scanning your ports.

In classic iptables, you can use the LOG target. For example:

iptables -A INPUT -p tcp --dport 22 -j LOG --log-prefix "SSH attempt: "

After logging, you might then drop the packet with another rule. The logs are typically written to system logs, which can be viewed with tools like journalctl or by examining files under /var/log/ that contain kernel messages.

In nftables, logging uses the log expression. A rule inside a chain may include:

log prefix "nft-drop: " group 0

which logs packets with a prefix. Additional parameters can control logging details.

firewalld and UFW also provide options to enable logging at their abstraction level. For example, UFW has different logging levels, including low, medium, and high, that increase the amount of logged information.

When debugging firewall problems, useful techniques include:

Temporarily allowing more logging to see what is dropped.
Checking the effective ruleset with tools like iptables-save, nft list ruleset, or firewall-cmd --list-all.
Using packet capture tools such as tcpdump to see what actually arrives and leaves interfaces.
Verifying routing and local service bindings to ensure the firewall is not blamed for non firewall issues.

Do not enable very verbose firewall logging on busy production systems without planning. Excessive logging can fill disks quickly and degrade performance.

Designing Secure and Maintainable Firewall Policies

Going in depth with firewalls means thinking in terms of policy rather than individual rules. A firewall policy describes which traffic is allowed and why, and how that policy is expressed in the chosen tool.

A solid design starts with a default deny stance for inbound connections and a clear list of necessary services. For each service, you specify allowed source networks, protocols, and ports. You also define how the host should behave when acting as a router, if that is needed, and what outbound traffic is permitted.

Grouping related rules into chains or using sets in nftables improves maintainability. For instance, you can define a set of trusted admin IP addresses and reference that set in multiple rules. When a new admin address is added, you update the set once rather than multiple rules.

When using higher level tools like firewalld or UFW, maintain documentation that maps abstract services and zones to real services and networks in your environment. This documentation helps ensure that changes are intentional and consistent.

Finally, consider automation. In complex environments, it is useful to manage firewall rules as code, for example through configuration management tools. This approach helps ensure that all servers share a consistent policy and that changes are applied and tracked in a controlled way.

Always test firewall changes in a non production environment first, and implement changes incrementally with clear rollback plans. A misconfigured firewall can cause outages just as effectively as an external attack.

Views: 8

Comments

Please login to add a comment.

Don't have an account? Register now!