// Tutorial //

How To Set Up a Firewall Using firewalld on Rocky Linux 8

Published on July 4, 2022
Default avatar
By Alex Garnett
Senior DevOps Technical Writer
How To Set Up a Firewall Using firewalld on Rocky Linux 8
Not using Rocky Linux 8?Choose a different version or distribution.
Rocky Linux 8

Introduction

firewalld is firewall management software available for many Linux distributions, which acts as a frontend for Linux’s in-kernel nftables or iptables packet filtering systems.

In this guide, you will review how to set up a firewalld firewall for your Rocky Linux 8 server, and cover the fundamentals of managing the firewall with the firewall-cmd administrative tool.

Prerequisites

To complete this tutorial, you will need a server running Rocky Linux 8. You should be logged into this server as a non-root, sudo-enabled user. To set this up, see our Initial Server Setup for Rocky Linux 8 guide.

Step 1 — Reviewing Core Concepts in firewalld

Before reviewing how to actually use the firewall-cmd utility to manage your firewall configuration, you should become familiar with a few concepts that the tool introduces.

Zones

The firewalld daemon manages groups of rules using entities called zones. Zones are sets of rules that dictate what traffic should be allowed depending on the level of trust you have in the network. Network interfaces are assigned to a zone to dictate the behavior that the firewall should allow.

For computers that might move between networks frequently (like laptops), this kind of flexibility provides a good method of changing your rules depending on your environment. You may have strict rules in place prohibiting most traffic when operating on a public WiFi network, while allowing more relaxed restrictions when connected to your home network. For a server, these zones are often not as important because the network environment rarely, if ever, changes.

Regardless of how dynamic your network environment may be, it is still useful to be familiar with the general idea behind each of the predefined zones for firewalld. The predefined zones within firewalld are, in order from least trusted to most trusted:

  • drop: The lowest level of trust. All incoming connections are dropped without reply and only outgoing connections are possible.
  • block: Similar to the above, but instead of dropping connections, incoming requests are rejected with an icmp-host-prohibited or icmp6-adm-prohibited message.
  • public: Represents public, untrusted networks. You don’t trust other computers but may allow selected incoming connections on a case-by-case basis.
  • external: External networks in the event that you are using the firewall as your gateway. It is configured for NAT masquerading so that your internal network remains private but reachable.
  • internal: The other side of the external zone, used for the internal portion of a gateway. The computers are fairly trustworthy and some additional services are available.
  • dmz: Used for computers located in a DMZ (isolated computers that will not have access to the rest of your network). Only certain incoming connections are allowed.
  • work: Used for work machines. Trust most of the computers in the network. A few more services might be allowed.
  • home: A home environment. It generally implies that you trust most of the other computers and that a few more services will be accepted.
  • trusted: Trust all of the machines in the network. The most open of the available options and should be used sparingly.

To use the firewall, you can create rules and alter the properties of your zones and then assign your network interfaces to whichever zones are most appropriate.

Rule Permanence

In firewalld, rules can be applied to the current runtime ruleset, or be made permanent. When a rule is added or modified, by default, only the currently running firewall is modified. After the next reboot – or reload of the firewalld service – only the permanent rules will remain.

Most firewall-cmd operations can take a --permanent flag to indicate that the changes should be applied to the permanent configuration. Additionally, the currently running firewall can be saved to the permanent configuration with the firewall-cmd --runtime-to-permanent command.

This separation of runtime vs permanent configuration means that you can safely test rules in your active firewall, then reload to start over if there are problems.

Step 2 — Installing and Enabling firewalld

firewalld is installed by default on some Linux distributions, including many deployments of Rocky Linux. However, it may be necessary for you to install firewalld yourself. You can do this using Rocky’s dnf package manager:

  1. sudo dnf install firewalld -y

After you install firewalld, you’ll need to enable the service using systemctl. Keep in mind that enabling firewalld will cause the service to start up at boot. It is best to create your firewall rules and take the opportunity to test them while still logged in over SSH to avoid potential issues.

  1. sudo systemctl enable firewalld
  2. sudo systemctl start firewalld

You can verify that the service is running and reachable by typing:

  1. sudo firewall-cmd --state
Output
running

This indicates that your firewall is up and running with the default configuration. Before you make further modifications, you should familiarize yourself with the default environment and rules provided by firewalld.

Exploring the Defaults

You can see which zone is currently selected as the default by running firewall-cmd --get-default-zone:

  1. firewall-cmd --get-default-zone
Output
public

Since you haven’t given firewalld any commands to deviate from the default zone, and none of your interfaces are configured to bind to another zone, that zone will also be the only active zone (the zone that is controlling the traffic for our interfaces). You can verify that by running firewall-cmd --get-active-zones:

  1. firewall-cmd --get-active-zones
Output
public interfaces: eth0 eth1

Here, you can see that your example server has two network interfaces being controlled by the firewall (eth0 and eth1). They are both currently being managed according to the rules defined for the public zone.

You can print out the rules tied to the default zone’s configuration by using firewall-cmd --list-all:

  1. sudo firewall-cmd --list-all
Output
public (active) target: default icmp-block-inversion: no interfaces: eth0 eth1 sources: services: cockpit dhcpv6-client ssh ports: protocols: masquerade: no forward-ports: source-ports: icmp-blocks: rich rules:

You can tell from the output that this zone is both the default and active, and that the eth0 and eth1 interfaces are associated with this zone. From the services: line, you can also see that this zone allows traffic for a DHCP client (for IP address assignment), SSH (for remote administration), and Cockpit (a web-based console).

Exploring Alternative Zones

You can find out information about other zones as well.

To get a list of the available zones, run firewall-cmd --get-zones:

  1. firewall-cmd --get-zones
Output
block dmz drop external home internal public trusted work

You can see the specific configuration associated with a zone by including the --zone= parameter in your --list-all command:

  1. sudo firewall-cmd --zone=home --list-all
Output
home target: default icmp-block-inversion: no interfaces: sources: services: cockpit dhcpv6-client mdns samba-client ssh ports: protocols: masquerade: no forward-ports: source-ports: icmp-blocks: rich rules:

You can output all of the zone definitions by using the --list-all-zones option. Next, you will learn about assigning zones to network interfaces.

Step 3 — Selecting Zones for your Interfaces

Unless you have configured your network interfaces otherwise, each interface will be put in the default zone when the firewall is started.

Changing the Zone of an Interface

You can move an interface between zones during a session by using the --zone= parameter in combination with the --change-interface= parameter. As with all commands that modify the firewall, you will need to use sudo.

For instance, you can move your eth0 interface to the home zone:

  1. sudo firewall-cmd --zone=home --change-interface=eth0
Output
success

Note: Whenever you are moving an interface to a new zone, be aware that you are modifying which services will be operational. In this case, you are moving to the home zone, which has SSH available. This means that your connection shouldn’t drop. Some other zones do not have SSH enabled by default, and switching to one of these zones could cause your connection to drop, preventing you from logging back into your server.

You can verify that this was successful by checking the active zones again:

  1. firewall-cmd --get-active-zones
Output
home interfaces: eth0 public interfaces: eth1

Adjusting the Default Zone

If all of your interfaces can be handled well by a single predefined zone, you should designate that zone as the default. You can change the default zone with the --set-default-zone= parameter. This will immediately change any interface using the default zone:

  1. sudo firewall-cmd --set-default-zone=home
Output
success

Step 4 — Setting Rules for your Applications

Let’s run through the ways of defining firewall exceptions.

Adding a Service to your Zones

The most straightforward method is to add the services or ports you need to the zones you are using. You can get a list of the available service definitions by using the --get-services option of firewall-cmd:

  1. firewall-cmd --get-services
Output
RH-Satellite-6 amanda-client amanda-k5-client amqp amqps apcupsd audit bacula bacula-client bb bgp bitcoin bitcoin-rpc bitcoin-testnet bitcoin-testnet-rpc bittorrent-lsd ceph ceph-mon cfengine cockpit condor-collector ctdb dhcp dhcpv6 dhcpv6-client distcc dns dns-over-tls docker-registry docker-swarm dropbox-lansync elasticsearch etcd-client etcd-server finger freeipa-4 freeipa-ldap freeipa-ldaps freeipa-replication freeipa-trust ftp ganglia-client ganglia-master git grafana gre high-availability http https imap imaps ipp ipp-client ipsec irc ircs iscsi-target isns jenkins kadmin kdeconnect kerberos kibana klogin kpasswd kprop kshell ldap ldaps libvirt libvirt-tls lightning-network llmnr managesieve matrix mdns memcache minidlna mongodb mosh mountd mqtt mqtt-tls ms-wbt mssql murmur mysql nfs nfs3 nmea-0183 nrpe ntp nut openvpn ovirt-imageio ovirt-storageconsole ovirt-vmconsole plex pmcd pmproxy pmwebapi pmwebapis pop3 pop3s postgresql privoxy prometheus proxy-dhcp ptp pulseaudio puppetmaster quassel radius rdp redis redis-sentinel rpc-bind rsh rsyncd rtsp salt-master samba samba-client samba-dc sane sip sips slp smtp smtp-submission smtps snmp snmptrap spideroak-lansync spotify-sync squid ssdp ssh steam-streaming svdrp svn syncthing syncthing-gui synergy syslog syslog-tls telnet tentacle tftp tftp-client tile38 tinc tor-socks transmission-client upnp-client vdsm vnc-server wbem-http wbem-https wsman wsmans xdmcp xmpp-bosh xmpp-client xmpp-local xmpp-server zabbix-agent zabbix-server

Note: You can find more details about each of these services by looking at their associated .xml file within the /usr/lib/firewalld/services directory. For instance, the SSH service is defined like this:

/usr/lib/firewalld/services/ssh.xml
<?xml version="1.0" encoding="utf-8"?>
<service>
  <short>SSH</short>
  <description>Secure Shell (SSH) is a protocol for logging into and executing commands on remote machines. It provides secure encrypted communications. If you plan on accessing your machine remotely via SSH over a firewalled interface, enable this option. You need the openssh-server package installed for this option to be useful.</description>
  <port protocol="tcp" port="22"/>
</service>

You can enable a service for a zone using the --add-service= parameter. The operation will target the default zone, or whichever zone is specified by the --zone= parameter. By default, this will only adjust the current firewall session, and will not persist beyond service restarts or reboots. You can adjust the permanent firewall configuration by including the --permanent flag.

For instance, if you are running a web server serving conventional HTTP traffic, you can temporarily allow this traffic for interfaces in your public zone:

  1. sudo firewall-cmd --zone=public --add-service=http

You can omit the --zone= flag to modify the default zone. You can verify the operation was successful by using the --list-all or --list-services operations:

  1. sudo firewall-cmd --zone=public --list-services
Output
cockpit dhcpv6-client http ssh

Once you have tested that everything is working as it should, you can modify the permanent firewall rules so that your service will still be available after a reboot. You can make the previous command permanent by adding the --permanent flag:

  1. sudo firewall-cmd --zone=public --add-service=http --permanent
Output
success

Alternately, you could use the --runtime-to-permanent flag to save the currently running firewall configuration to the permanent config:

  1. sudo firewall-cmd --runtime-to-permanent

Be careful with this option, as all changes made to the running firewall will be committed permanently.

Whichever method you chose, you can verify that it was successful by adding the --permanent flag to the --list-services operation. You need to use sudo for any --permanent operations:

  1. sudo firewall-cmd --zone=public --list-services --permanent
Output
cockpit dhcpv6-client http ssh

Your public zone will now allow HTTP web traffic on port 80. If your web server is configured to use SSL/TLS, you’ll also need to add the https service. You can add that to the current session and the permanent rule-set by typing:

  1. sudo firewall-cmd --zone=public --add-service=https
  2. sudo firewall-cmd --zone=public --add-service=https --permanent

The services that are included with the firewalld installation represent many of the most common applications that you may need to allow access to. However, there will likely be scenarios where these services do not fit your requirements.

In this situation, you have two options.

Opening a Port for your Zones

The most straightforward way to add support for your specific application is to open up the ports that it uses in the appropriate zone(s). This is done by specifying the port or port range, and the associated protocol (TCP or UDP) for the ports.

For instance, if your application runs on port 5000 and uses TCP, you could temporarily add this to the public zone using the --add-port= parameter. Protocols can be designated as either tcp or udp:

  1. sudo firewall-cmd --zone=public --add-port=5000/tcp
Output
success

You can verify that this was successful using the --list-ports operation:

  1. sudo firewall-cmd --zone=public --list-ports
Output
5000/tcp

It is also possible to specify a sequential range of ports by separating the beginning and ending port in the range with a dash. For instance, if your application uses UDP ports 4990 to 4999, you could open these up on public by typing:

  1. sudo firewall-cmd --zone=public --add-port=4990-4999/udp

After testing, you can add these to the permanent firewall. Use sudo firewall-cmd --runtime-to-permanent, or rerun the commands with the --permanent flag:

  1. sudo firewall-cmd --zone=public --permanent --add-port=5000/tcp
  2. sudo firewall-cmd --zone=public --permanent --add-port=4990-4999/udp
  3. sudo firewall-cmd --zone=public --permanent --list-ports
Output
success success 5000/tcp 4990-4999/udp

Defining a Service

Opening ports for your zones is a straightforward solution, but it can be difficult to keep track of what each of them is used for. If you ever decommission a service on your server, you may struggle to catalog which ports that have been opened are still required. To avoid this situation, you can define a new service.

Services are collections of ports with an associated name and description. Managing your firewall using services is generally more maintainable than mapping ports, but requires some initial configuration. You can start by copying an existing script in /usr/lib/firewalld/services to the /etc/firewalld/services directory where the firewall looks for non-standard definitions.

For instance, you could copy the SSH service definition to use for your example service definition like this. The name of the service within the firewall services list will be the name of this file, minus the .xml suffix:

  1. sudo cp /usr/lib/firewalld/services/ssh.xml /etc/firewalld/services/example.xml

Open the file, using vi or your favorite text editor:

  1. sudo vi /etc/firewalld/services/example.xml

To start, the file will contain the SSH definition that you copied:

/etc/firewalld/services/example.xml
<?xml version="1.0" encoding="utf-8"?>
<service>
  <short>SSH</short>
  <description>Secure Shell (SSH) is a protocol for logging into and executing commands on remote machines. It provides secure encrypted communications. If you plan on accessing your machine remotely via SSH over a firewalled interface, enable this option. You need the openssh-server package installed for this option to be useful.</description>
  <port protocol="tcp" port="22"/>
</service>

The majority of this definition is actually metadata. You should change the short name for the service within the <short> tags. This is a human-readable name for your service. You should also add a description so that you have more information if you ever need to audit the service. The only configuration you need to make that actually affects the functionality of the service will likely be the port definition where you identify the port number and protocol to open. Multiple <port/> tags can be specified.

For your example service, imagine that you need to open up port 7777 for TCP and 8888 for UDP. You can modify the existing definition with something like this:

/etc/firewalld/services/example.xml
<?xml version="1.0" encoding="utf-8"?>
<service>
  <short>Example Service</short>
  <description>This is just an example service. It probably shouldn't be used on a real system.</description>
  <port protocol="tcp" port="7777"/>
  <port protocol="udp" port="8888"/>
</service>

Save and close the file.

Reload your firewall to get access to your new service:

  1. sudo firewall-cmd --reload

You can see that it is now among the list of available services:

  1. firewall-cmd --get-services
Output
RH-Satellite-6 amanda-client amanda-k5-client amqp amqps apcupsd audit bacula bacula-client bb bgp bitcoin bitcoin-rpc bitcoin-testnet bitcoin-testnet-rpc bittorrent-lsd ceph ceph-mon cfengine cockpit condor-collector ctdb dhcp dhcpv6 dhcpv6-client distcc dns dns-over-tls docker-registry docker-swarm dropbox-lansync elasticsearch etcd-client etcd-server example finger freeipa-4 freeipa-ldap freeipa-ldaps freeipa-replication freeipa-trust ftp ganglia-client ganglia-master git grafana gre high-availability http https imap imaps ipp ipp-client ipsec irc ircs iscsi-target isns jenkins kadmin kdeconnect kerberos kibana klogin kpasswd kprop kshell ldap ldaps libvirt libvirt-tls lightning-network llmnr managesieve matrix mdns memcache minidlna mongodb mosh mountd mqtt mqtt-tls ms-wbt mssql murmur mysql nfs nfs3 nmea-0183 nrpe ntp nut openvpn ovirt-imageio ovirt-storageconsole ovirt-vmconsole plex pmcd pmproxy pmwebapi pmwebapis pop3 pop3s postgresql privoxy prometheus proxy-dhcp ptp pulseaudio puppetmaster quassel radius rdp redis redis-sentinel rpc-bind rsh rsyncd rtsp salt-master samba samba-client samba-dc sane sip sips slp smtp smtp-submission smtps snmp snmptrap spideroak-lansync spotify-sync squid ssdp ssh steam-streaming svdrp svn syncthing syncthing-gui synergy syslog syslog-tls telnet tentacle tftp tftp-client tile38 tinc tor-socks transmission-client upnp-client vdsm vnc-server wbem-http wbem-https wsman wsmans xdmcp xmpp-bosh xmpp-client xmpp-local xmpp-server zabbix-agent zabbix-server

You can now use this service in your zones as you normally would.

Step 5 — Creating Your Own Zones

While the predefined zones should work for most users, it can be helpful to define your own zones that are more descriptive of their function.

For instance, you might need to create a zone for your web server, called publicweb. However, you may need to have another zone configured for the DNS service you provide on your private network. You could make another zone called privateDNS for that.

When adding a zone, you must add it to the permanent firewall configuration. You can then reload to bring the configuration into your running session. For instance, you could create these two zones using firewall-cmd –new-zone:

  1. sudo firewall-cmd --permanent --new-zone=publicweb
  2. sudo firewall-cmd --permanent --new-zone=privateDNS

You can verify that these are present in your permanent configuration by typing:

  1. sudo firewall-cmd --permanent --get-zones
Output
block dmz drop external home internal privateDNS public publicweb trusted work

Reload the firewall to bring these new zones into the active runtime configuration:

  1. sudo firewall-cmd --reload
  2. firewall-cmd --get-zones
Output
block dmz drop external home internal privateDNS public publicweb trusted work

Now, you can begin assigning the appropriate services and ports to your zones. It’s usually a good idea to adjust the runtime firewall and then save those changes to the permanent configuration after testing. For instance, for the publicweb zone, you could add the SSH, HTTP, and HTTPS services:

  1. sudo firewall-cmd --zone=publicweb --add-service=ssh
  2. sudo firewall-cmd --zone=publicweb --add-service=http
  3. sudo firewall-cmd --zone=publicweb --add-service=https
  4. sudo firewall-cmd --zone=publicweb --list-all
Output
publicweb target: default icmp-block-inversion: no interfaces: sources: services: http https ssh ports: protocols: masquerade: no forward-ports: source-ports: icmp-blocks: rich rules:

Then you can add the DNS service to your privateDNS zone:

  1. sudo firewall-cmd --zone=privateDNS --add-service=dns
  2. sudo firewall-cmd --zone=privateDNS --list-all
Output
privateDNS target: default icmp-block-inversion: no interfaces: sources: services: dns ports: protocols: masquerade: no forward-ports: source-ports: icmp-blocks: rich rules:

You could then change our interfaces over to these new zones to test them out:

  1. sudo firewall-cmd --zone=publicweb --change-interface=eth0
  2. sudo firewall-cmd --zone=privateDNS --change-interface=eth1

At this point, you have the opportunity to test your configuration. If these values work for you, you should add these rules to the permanent configuration. You could do that by running all the commands again with the --permanent flag appended, but in this case you’ll use the --runtime-to-permanent flag to save your entire runtime configuration permanently:

  1. sudo firewall-cmd --runtime-to-permanent

After permanently applying these rules, reload the firewall to test that the changes remain:

  1. sudo firewall-cmd --reload

Validate that the correct zones were assigned:

  1. firewall-cmd --get-active-zones
Output
privateDNS interfaces: eth1 publicweb interfaces: eth0

And validate that the appropriate services are available for both of the zones:

  1. sudo firewall-cmd --zone=publicweb --list-services
Output
http https ssh
  1. sudo firewall-cmd --zone=privateDNS --list-services
Output
dns

You have successfully set up your own zones! To make one of these zones the default for other interfaces, remember to configure that behavior with the --set-default-zone= parameter:

  1. sudo firewall-cmd --set-default-zone=publicweb

Conclusion

You should now have a fairly thorough understanding of how to administer the firewalld service on your Rocky Linux system for day-to-day use.

The firewalld service allows you to configure maintainable rules and rulesets that take your network environment into consideration. It allows you to seamlessly transition between different firewall policies through the use of zones. Acquiring a working knowledge of this system will allow you to take advantage of the flexibility and power that this tool provides.

For more information on firewalld, please see the official firewalld documentation.

If you’ve enjoyed this tutorial and our broader community, consider checking out our DigitalOcean products which can also help you achieve your development goals.

Learn more here


About the authors
Default avatar
Senior DevOps Technical Writer

Still looking for an answer?

Was this helpful?
Leave a comment

This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!