Using a firewall on your Linux server

ufw and firewalld side by side for default deny, opening SSH and a game port, and how a host firewall relates to Clouduxe Prism.

Linux5 min read

On this page
  1. Find out what is already running
  2. The common cases, side by side
  3. ufw, on Debian and Ubuntu
  4. Listing and removing
  5. firewalld, on RHEL, Alma, and Rocky
  6. Listing and removing
  7. Testing a rule that might cut you off
  8. Two things that catch people out
  9. How this relates to Clouduxe protection
  10. They act at different points
  11. They cover different ground
  12. Keep the two lists in agreement
  13. Related

A firewall on the server decides which of its ports answer at all. Debian and Ubuntu use ufw, the RHEL family uses firewalld, and both are front ends for the same packet filter underneath, so run one of them and not both.

Find out what is already running

Shell
systemctl is-active ufw firewalld

Debian and Ubuntu images ship ufw installed but inactive. RHEL, Alma, and Rocky images usually ship firewalld installed and running. Install the missing one only if you have neither:

Shell
apt install -y ufw
Shell
dnf install -y firewalld
systemctl enable --now firewalld

The common cases, side by side

What you wantufwfirewalld
Default deny inboundufw default deny incomingAlready the behaviour of the public zone
Allow SSHufw allow 22/tcpfirewall-cmd --permanent --add-service=ssh
Allow a game portufw allow 30120/udpfirewall-cmd --permanent --add-port=30120/udp
Allow a port rangeufw allow 27015:27020/udpfirewall-cmd --permanent --add-port=27015-27020/udp
Allow one source onlyufw allow from 203.0.113.10 to any port 22 proto tcpA rich rule, below
List the rulesufw status numberedfirewall-cmd --list-all
Remove a ruleufw delete 3firewall-cmd --permanent --remove-port=30120/udp
Apply changesufw reloadfirewall-cmd --reload

Note the range separator differs: ufw wants a colon, firewalld wants a hyphen.

ufw, on Debian and Ubuntu

Set the policy and the SSH rule before you enable anything:

Shell
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp
ufw enable

ufw enable warns that the command may disrupt existing SSH connections and asks for confirmation. That warning is the reason the SSH rule goes first.

A game server usually needs the same port on both protocols, and each protocol is a separate rule:

Shell
ufw allow 30120/tcp
ufw allow 30120/udp

Restrict a management port to one address rather than opening it to everyone:

Shell
ufw allow from 203.0.113.10 to any port 22 proto tcp

To slow down repeated connection attempts on a port, ufw limit 22/tcp denies an address that opens too many connections in a short window. Run man ufw for the exact thresholds your version uses.

Listing and removing

Shell
ufw status numbered

Delete by the number shown, or by repeating the rule:

Shell
ufw delete 3
ufw delete allow 30120/udp

Deleting by number renumbers everything below it, so re-run ufw status numbered between deletions rather than working down a list you printed earlier.

firewalld, on RHEL, Alma, and Rocky

firewalld sorts interfaces into zones, and the default zone for a server is normally public. Check what you are working on:

Shell
firewall-cmd --get-default-zone
firewall-cmd --list-all

The public zone already refuses inbound traffic that is not explicitly allowed, so there is no default-deny command to run. What you add is the list of exceptions:

Shell
firewall-cmd --permanent --add-service=ssh
firewall-cmd --permanent --add-port=30120/tcp
firewall-cmd --permanent --add-port=30120/udp
firewall-cmd --reload

--permanent writes the rule to disk but does not apply it until the reload. Without --permanent the rule applies immediately and is lost at the next reload or reboot. Run a change one way, confirm it, then commit what is live:

Shell
firewall-cmd --runtime-to-permanent

Allowing a single source needs a rich rule:

Shell
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.10" port port="22" protocol="tcp" accept'
firewall-cmd --reload

Listing and removing

Shell
firewall-cmd --list-all
firewall-cmd --list-ports
firewall-cmd --list-services
firewall-cmd --permanent --remove-port=30120/udp
firewall-cmd --reload

Testing a rule that might cut you off

firewalld can add a rule that expires on its own, which is the safest way to try a change to a port you depend on:

Shell
firewall-cmd --add-port=2222/tcp --timeout=5m

--timeout cannot be combined with --permanent: the rule is runtime only, and after five minutes it disappears whether or not you are still connected. ufw has no equivalent, so there the discipline is a second session and the console.

Two things that catch people out

Docker publishes ports past ufw. A container started with -p 30120:30120 is reachable from the internet even when ufw denies that port, because Docker inserts its own forwarding rules ahead of ufw's. If a port should only be reachable from the host, publish it to loopback instead, with -p 127.0.0.1:30120:30120. If it should not be reachable at all, close it somewhere Docker does not rewrite, which includes the panel.

Published on every addressdocker run -p 30120:30120 ...A packet from the internetTo port 30120, which ufw is set to denyDocker's forwarding rulesInserted ahead of ufw's, so they decide firstReaches the containerufw's deny for 30120 never stops itPublished to loopbackdocker run -p 127.0.0.1:30120:30120 ...A packet from the internetTo port 30120Nothing published on the public addressOnly 127.0.0.1:30120, which software on theserver itself can reachTo close the port entirely, close it where Docker does not rewrite, such as the panel's rules beforethe server.Published on every addressdocker run -p 30120:30120 ...A packet from the internetTo port 30120, which ufw is set to denyDocker's forwarding rulesInserted ahead of ufw's, so they decide firstReaches the containerufw's deny for 30120 never stops itPublished to loopbackdocker run -p 127.0.0.1:30120:30120 ...A packet from the internetTo port 30120Nothing published on the public addressOnly 127.0.0.1:30120, which software on theserver itself can reachTo close the port entirely, close it whereDocker does not rewrite, such as thepanel's rules before the server.
The same container published two ways. On every address, Docker's own forwarding rules decide before ufw's, so a ufw deny for the port does not stop it; on 127.0.0.1, nothing is published on the public address at all. Illustration.

Check what is listening before you decide what to allow. A service bound to 0.0.0.0 is reachable the moment a rule opens its port, including ones you forgot were installed:

Shell
ss -tulnp

How this relates to Clouduxe protection

Your host firewall and the rules on the Firewall tab in the panel are not two versions of the same thing. They sit at different points in the path, and they know different things.

They act at different points

A packet aimed at your server meets the panel's rules first. If a rule there drops it, ufw and firewalld never see it, and neither does the service. If your host firewall drops it, the packet has already arrived: it crossed the network, reached the machine, and cost it the work of looking at it and deciding.

A packet aimed at your serverUDP, to port 30120 on 203.0.113.10The panel's rules: Clouduxe PrismBefore your server's operating systemRate ceilings per portProtocol checksSource allow and denyDropped hereufw and firewalld never see it,and neither does the serviceThe host firewall: ufw or firewalldOn the server, which also seesLoopbackInterfacesOutboundContainer bridgesDropped hereIt already arrived, and cost themachine the work of decidingYour service on UDP 30120Reachable only when both lists allow the portA packet aimed at your serverUDP, to port 30120 on 203.0.113.10The panel's rules: Clouduxe PrismBefore your server's operating systemRate ceilings per portProtocol checksSource allow and denyufw and firewalld never see it, andneither does the serviceThe host firewall: ufw or firewalldOn the server, which also seesLoopbackInterfacesOutboundContainer bridgesIt already arrived, and cost the machinethe work of decidingYour service on UDP 30120Reachable only when both lists allow the port
One path, two lists. The panel's rules come first, so what they drop never reaches ufw, firewalld or the service; what the host firewall drops has already arrived. Each list also knows things the other does not, and a port works only when both allow it. Illustration.

For one packet that difference does not matter. For a flood it is the entire difference, which is why a source you want gone is better blocked in the panel than with a ufw deny rule. See Blocking and allowing IPs.

They cover different ground

The host firewall sees things the panel does not: loopback, individual interfaces, outbound traffic, container bridges, and every local service that is listening whether you meant it to be or not. It is also where you express rules about traffic that never leaves the machine.

The panel's rules carry what a host firewall has no notion of: packet-rate ceilings per port, whether traffic arriving on a port speaks the protocol the service actually speaks, and per-source allow and deny lists that apply before the traffic lands. How protection works sets out what runs where.

Neither replaces the other. A host firewall does not defend against a flood: it decides what is reachable, it does not reduce what arrives. Protection in the panel is not a reason to leave a database or a management port listening on a public address, because a rate ceiling does not make an open service safe.

Keep the two lists in agreement

For a port to be usable it has to be allowed in both places. When players cannot connect, that is the first thing to check: the rule exists in the panel, and the host firewall is not denying the same port. The number of rules the panel accepts is finite, so plan them per service rather than per player; see Port rules.

Need a server, or a hand with one?

Pick a plan to deploy a server, or open a ticket in the panel. Support runs 24/7/365.