Rocky 9, firewalld, nftables

Rocky 9 has firewalld (frontend) and nftables (backend).

If I add a simple rule with firewall-cmd, does it make a change in nttables behind the scenes? If I add a complicated rule to nftables, would it show up in firewalld in a meaningful way?

What is “IP Forwarding”; when would I want it (or not want it)? What is the Rocky default? As I understand it, it’s actually a kernel parameter, as opposed to a firewall setting, but sometimes the firewall will turn it on?

What is “masquerade”, when would I want it (or not want it)? What is the Rocky default? Is it IPv4 only?

You can do:

cat /proc/sys/net/ipv4/ip_forward

and see what comes back. Mostly it should be set to zero. You usually will only need it if you want to use your machine/VM as a router. There are probably other reasons as well, but it’s mostly for this. It’s not related to whether the firewall is enabled and running or not.

This is when you want to translate and route packets from your internal network to go out to the internet, or vice-versa. For example, if you configure a firewalld zone as external, then you would enable masquerade on that interface. Your internal interface would have masquerade disabled.

For normal usage, you don’t need to worry about either of these settings. If you want to build your own firewall with Rocky Linux which would be your gateway to the internet, then you would need both these options and configure your system accordingly.

There are probably much better and more detailed explanations out on the internet for all of that.

As for the nftables rule thing, I would expect it won’t show up in firewalld since you didn’t create the rule with firewalld as you used nftables directly.

Yes. nft list ruleset shows the rules that kernel has. Run it before and after use of firewall-cmd and compare the changes.

Exactly.

The default is that a packet that did arrive from outside must be destined for this machine, or it will be dropped.
When forwarding is enabled, the packet can be redirected (forwarded, routed) out of the machine. Then it will go through the “forward” filter, if the firewall rules allow.
If you do need traffic (of other machines) to go through this machine, then you need to enable the forwarding (and adjust the ruleset).


Masquerade is a variant of source NAT (sNAT). NAT is network address translation.
Lets have a packet. It is from address A and destined to address B. If we sNAT it with address C,
then it seems to be from address C and destined to address B.
Masquerade uses the address of the outgoing interface in sNAT (rather than C).

1 Like

Thanks for the great answers; much clearer than anything on the internet.

I want to test this:

Computer A runs a web server, listens on port 443
Computer B runs a web server, listens on port 8443
both computers are on the LAN, 10.0.x.x
Computer A also has NAT to receive outside traffic

Can I make selected web requests to Computer A go to computer B instead, and any response goes back to the origin?

rule family="ipv4" source address="52.83.0.0/16" forward-port to-addr="10.20.0.1" to-port="8443" protocol="tcp" port="443"

I’d have to make a similar rule for ipv6.

In addition, could I do the same using just Computer A, simply redirect to a different port

rule family="ipv4" source address="52.83.0.0/16" forward-port to-port="8443" protocol="tcp" port="443"

You can do this without these rules by making what is called a load balancer, or reverse proxy. And this can be done using things like Apache, Nginx, HAProxy. And based on the configuration of one of these, you can choose based on source IP in a location block to forward it. Eg:

User’s Computer (52.83.0.10) → Computer A (Port 443) → Computer B (8443)

return traffic from Computer B will go back to the load balancer on computer A, and the packet gets returned to the User’s Computer. For example, a location config for Nginx:

    location / {
        proxy_set_header Host $http_host;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Server $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass https://computer-b:8443;
    }

that’s without the source IP added, so effectively all HTTPS request to computer A will get redirected to 8443 on computer B. But it’s easy enough to add the appropriate section to restrict the IP. Ideally you’ll also use a specific FQDN for that particular config to ensure it gets routed to the correct machine. Otherwise, 403 forbidden will be given for other hosts that attempt to use it and their source IP isn’t allowed.

So basically just adding:

        allow 52.83.0.0/16;

to the above location block.

Otherwise, simple firewall rules can be used to redirect appropriately as you already mentioned. I’ve used in the past something like this:

firewall-cmd --zone=public --add-forward-port=port=443:proto=tcp:toport=8443 --permanent
firewall-cmd --reload

so any request incoming on port 443 gets rerouted to 8443. Since that was locally on the machine to redirect to something running docker, you can just add a source address to it, so:

firewall-cmd --zone=public --add-source=52.83.0.0/16 --add-forward-port=port=443:proto=tcp:toport=8443 --permanent

should also work. Now whether that will conflict with the fact you have a web server running on port 443 on computer-A or not, you’ll have to see.

Note that the “load balancer/reverse proxy” does not require IP forwarding. An outside client talks to httpd in A:443 and the httpd in A talks to httpd in B:8443. These are two “independent” connections, not packets from client reaching B (replies back). The proxy config “does NAT” in httpd.


The port forwarding is destination NAT (dNAT). Packet enters A, because it has “to A”. The first thing is to modify the “to”. After that, it might no longer be for A, but “to B:8443”. FirewallD will add multiple nftables rules with the -add-forward-port. The dNAT rule, obviously, but also an “allow” rule to the forward chain, for the packet to continue to the new destination.

The port forward rule(s) thus change packet’s “from=client, to=A:443” into “from=client, to=B:8443”. The web server in B:8443 will create a reply that has: “from=B:8443, to=client”. If that reply arrives to A, it will note that it is related traffic and “undo” the dNAT, modify the reply to have: “from=A:443, to=client”. The client will thus believe that it talks with A.

However, this works if B has route to client via A. If B has different route to client, then reply will not pass A and the client will receive packet that has “from=B:8443, to=client”. It never called B, so this cannot be a valid reply to anything.

You would add a sNAT rule to change the outgoing “from=client, to=B:8443” into “from=A, to=B:8443” to force the B to send reply to A.


FirewallD uses a concept of “zones”. A zone is two things:

  • A set of clients
  • A list of rules

If there are two sets of clients: members of 52.83.0.0/16 and everyone else, then there are two logical zones. The zone for 52.83.0.0/16 will have all the rules that that group needs (including the port forward). The other zone will have their own rules (i.e. no port forward).

For real websites, I’d use the http proxy; this is an experiment; I don’t want the packets to get to httpd on Computer A, I want to capture some of them on Computer B (but where the client thinks it’s still talking to A)

Yes, I can imagine this part going wrong.

A and B are just standard computers on the LAN, I have not set up any special routing on B to say to go “via” A.