CentOS 7's new firewalld service

Oliver Pelz

December 2015

 In this article by Oliver Pelz, author of the book, CentOS 7 Linux Server Cookbook, Second Edition, we will learn the fundamentals of CentOS7’s new default firewall service firewalld. A firewall is a program that monitors and controls your system's network interfaces' incoming and outgoing network traffic, and can restrict the transmission to only useful and non-harmful data in and out of a computer system or network.

By default, CentOS is made available with an extremely powerful firewall called netfilter, which is built right into the kernel. While in older versions of CentOS we used the famous iptables application to control it, in version 7, the new standard netfilter management program has changed to a service called firewalld, which is already installed and enabled on every CentOS 7 server upon installation. It is a very powerful service to take full control over your server's firewall security, and is much easier to work with than iptables. Its main advantages are that it features a better structured and more logical approach to managing and configuring every aspect of a modern firewall solution. It hides away the creation of sophisticated networking rules and has a very easy syntax that is less error-prone. It can dynamically reload netfilter settings at runtime without having to restart the complete service, and we can have more than one firewall configuration set per system, which makes it great for working in changing network environments which is especially useful for mobile devices such as laptops.

Here in this article, we will give you an introduction to the two fundamental building blocks of firewalld: the zone and the service.

Please note that you can still use the older iptables program instead of firewalld in CentOS 7. In order to do that, you need to stop and disable firewalld using the systemctl command before you install iptables using yum and then start and enable it. You must be aware that you cannot use both firewall management programs in parallel, as they are incompatible with each other.

(For more resources related to this topic, see here.)

Firewalld zones

Firewalld introduces the new concept of network or firewall zones that assign different levels of trust to your server's network interfaces and their associated connections. In CentOS 7, there already exist a number of predefined firewalld zones, and all of these (with the exception of the trusted zone) will block any form of incoming network connection to the server unless it is explicitly allowed using special rules attached to the zone (which are called firewalld services; we will see later).

Simply put, firewalld zones are all about controlling incoming connections to the server only. Limiting outgoing connections with firewalld is also possible but is outside of the scope of this article.

Get started with zones

To get started on learning how to work with zones, log in to your CentOS 7 server as root and type the following commands showing all available firewalld zone names, the current default zone, and finally, a detailed output of all zones:

firewall-cmd --get-zones | tr " " "\n"

firewall-cmd --get-default-zone

firewall-cmd --list-all-zones

As you have seen in the preceding commands output, you can use the firewall-cmd command line client to query our underlying firewalld service. In the first command's output, using the --get-zones parameter, you will notice that there are already some predefined standard zones defined on a firewalld enabled system in CentOS 7, such as a home, public, or dmz (demilitarized) zone. Each of these zones act as a complete and full firewall that you can use depending on your system's environment and location. For example, as the name implies, the home zone is used if your computer is located in home areas. If this is selected, you mostly trust all other computers and services on the networks to not harm your computer, whereas the public zone is more for use in public areas such as public access points and so on. Here, you do not trust the other computers and services on the network to not harm you. On CentOS 7, the standard zone configuration set after installation is the public zone, which we displayed using the command's --get-default-zone parameter. To get detailed information about all the standard zones available on your system, and when to use which, you can read the man pages using the following line:

man firewalld.zones

Also, to get more technical information about all the currently available zones, we used the firewall client's --list-all-zones parameter. In this command's output, you will notice that a zone can have some associated networking interfaces and a list of services belonging to it, which are special firewall rules applied to incoming network connections (the output is truncated):

public (default, active)

  interfaces: enp0s3

  services: dhcpv6-client ssh

Also, another very important concept can be seen in the previous command's output. Our public zone is marked as default and active. While the active zone is the one that is directly associated to a network interface, the default zone really can get important if you have multiple network adapters available on your system. Firewalld is listening in on every network interface in your system and waiting for new network packets to arrive. If there is a new packet coming in to a specific interface, the next thing firewalld has to do is find out which zone is the correct one associated to our network interface, and after finding it, it will apply all the service rules against the network packets belonging to it. To choose the right zone, firewalld first checks if there has been any zone labeled as active for the network interface.

For example if you have multiple dedicated network interfaces, you can assign each of them to different active zones. For example, you could put one network interface connected to the public Internet (for example, a web server) into the dmz zone, and another interface that you use mainly to access the system within a safe corporate environment (such as SSH) into the internal zone. To get a list of all active zones and their associated network interfaces, you can use this:

firewall-cmd --get-active-zones

If a network interface does not have any active zone assigned to it at all, the default zone will be used instead. This mechanism can be very useful for having one standard minimum firewall protection and fallback strategy in case you missed assigning some active zone for every interface, or when adding a new network interface and connection to your system. For systems with only one network interface setting, the default zone will set the active zone automatically as well.

If you want to make a network interface active for a specific zone, you can use the following command (in our example, we will use a network interface named enp0s10, and add it to the internal zone):

firewall-cmd --zone=internal --add-interface=enp0s10

Please note that the preceding command will only work temporarily until firewalld gets restarted or reloaded the next time (follow this article to learn how to make it permanent). Also note that you can only bind the same network interface to one zone, so if it is already bound to another zone, you will get an error message. In this case, you can use the --change-interface instead of --add-interface, which will release the network interface from the original zone and add it to the zone specified by the --zone parameter:

firewall-cmd --zone=internal --change-interface=enp0s10

By setting a new interface to an existing zone, you have already learned that the firewall-cmd client is not only used for querying information from the underlying firewall service, but also to manage and modify its configuration.

Firewalld is designed to have a separated runtime and permanent configuration. While any change to the runtime configuration has immediate effect but will be gone, the permanent configuration will survive reload or restart of the firewalld service. Some commands such as switching the default zone are writing the changes into both configurations which mean they are immediately applied at runtime and are persistent over service restart. Other configuration settings such as adding an interface to a zone are only writing to the runtime configuration. If you restart firewalld, reload its configuration, or reboot your computer, these temporary changes will be lost. To make those temporary changes permanent, we can use the --permanent flag with the firewall-cmd program call to write it to the permanent configuration file as well. Other than with the runtime options, here the changes are not effective immediately, but only after a service restart/reload or system reboot. Therefore, the most common approach to apply permanent settings for such runtime-only commands is to first apply the setting with the --permanent parameter, and afterwards reload the firewall's configuration file to actually activate them. For example, to set a new interface to a zone permanently, use the following two commands:

firewall-cmd --zone=public --add-interface=enp0s11 --permanent

firewall-cmd --reload

As said before, other commands have an immediate effect and are permanent by default (write into both the runtime and permanent configuration) such as changing to a different default firewall zone:

firewall-cmd --set-default-zone=work

To validate that we have switched to the work zone we can use the following command to show details about the current default zone:

firewall-cmd --list-all

Please remember that restarting firewalld is not the same as reloading it. While the former means stopping the firewall process for real and starting a new one afterwards, which is completely independent from the original process, reloading firewalld will keep the underlying service process running, while its configuration will be refreshed and reloaded instead.

Please choose your default zone wisely because choosing the wrong zone in the wrong environment can be a severe security threat!

If you need, you can build your own firewalld zones as well. For example, you might need a completely new level of trust for a new working environment or surrounding. If I would attend a factious hacking conference, I could come up with the following zone definition to protect me from getting hacked. Please create and open a new file at the following location:

vi /etc/firewalld/zones/hackercon.xml

All default zone files are located at /usr/lib/firewalld/zones, whereas you should put all user created or customized configuration files in /etc/firewalld/zones/. Now, put in the following content, then save and close the file:

<?xml version="1.0" encoding="utf-8"?>

<zone>

  <short>hackercon</short>

  <description>For use at the hacking congress.</description>

  <service name="ssh"/>

</zone>

This will create a new zone with only the ssh service enabled by default. After we create this new zone file, we need to reload the firewalld configuration for the new file to be available in firewalld (you also have to do this every time you have changed something in an already existing zone file):

firewall-cmd --reload

Now it should be available, re-check firewalld can see it using:

firewall-cmd –get-zones | tr " " "\n"

If you can see the new zone, hackercon, you should now be able to set it as our new active zone permanently:

firewall-cmd --set-default-zone=hackercon

Finally, re-check that the new zone is our new default:

firewall-cmd --list-all

Firewalld services

Simply put, firewalld services are rules that open and allow a certain connection within our firewall to our server. It is defined as a configuration file that describes the used network protocols and a list of ports that the firewall should allow access to. Using such service file definitions allows reusability of the containing rules because they can be added or removed to any zone. Also, using the predefined firewalld services already available in your system, as opposed to manually finding out and opening protocols, ports, or port ranges using a complicated iptables syntax for your system service of interest, can make your administration life much easier.

There is already a decent number of standard services shipped with the firewalld service on CentOS 7, which cover most of the basic rules for many system services. To get a full list of every service currently included in your system, use this:

firewall-cmd --get-services | tr " " "\n"

Don't confuse system services with firewalld services. While the former are the programs running as daemons on you server, such as FTP, HTTP, and NFS, the latter are the firewall rules that allow specific access by a system service.

As you can see, in the output there already exists firewall service rules for a good number of commonly used server applications, such as SSH, FTP, MySQL and so on, which is good because we don't need the extra work of specifying everything on our own if we need to enable some system service later. To display all services activated in the default zone, use the following command:

firewall-cmd --list-services

Similarly, you can add a --zone parameter to show the services of another zone:

firewall-cmd --zone=internal --list-services

Now, if you want to add an existing service to your default zone temporarily, use this:

firewall-cmd --add-service=ftp

Similarly, if you want to add a service to a zone that is not your current default one, you can use this:

firewall-cmd --zone=work --add-service=ftp

To add the service permanently, use the following syntax instead:

firewall-cmd --permanent --zone=work --add-service=ftp
firewall-cmd --reload

You can then use the following command to test if adding the service permanently has been successful:

firewall-cmd --list-all

To finish this section, we will revert to the permanent changes we made to the work zone and reload firewalld to reset all non-permanent changes that we applied:

firewall-cmd --permanent --zone=work --remove-service=ftp

firewall-cmd --reload

Customizing existing firewalld service definition files

Almost all Linux system services are highly customizable, and one parameter that is often changed after its installation is the standard port that the application is listening to. The reasons for this could be that the default port is already reserved by another service, or more commonly, to increase security and protect you against brute force attacks on their standard ports. However, you have to know that this is no silver bullet against hackers, since some of their tools will scan the complete network, looking for certain services on every open port.

Now, every time we change a system's service standard listening port, we need to make the firewall aware that it should allow network traffic to flow through the changed port instead. For example, if you have already changed the default SSH listening port from 22 to, lets say, 2223 in the main SSH configuration file, sshd_conf, you must change this port in your firewalld's ssh service definition as well, in order to get incoming communication to the service back to work, otherwise, it stays blocked. You have to remember that all predefined firewalld zones (without the trusted zone) block all network traffic not defined in the service files attached to the zone.

Now, as with the zone configuration files before, there are also two distinct directories where service files are located: /usr/lib/firewalld/services/ is for all service files that come with firewalld out-of-the-box after installation, and /etc/firewalld/services/ is used for all customized or new service files.

If a service definition file has the same filename in both directories, the file in /etc/firewalld/services/ will overload the definition from the default location /usr/lib/firewalld/services/.

For this reason, let’s create the following working copy of the ssh service in the right place; then, open the file for editing:

cp /usr/lib/firewalld/services/ssh.xml /etc/firewalld/services

vi /etc/firewalld/services/ssh.xml

In this file, change the port from 22 to 2223, and save the file and close it:

<port protocol="tcp" port="2223"/>

Finally, reload the firewall to make firewalld aware of your changes to the service files:

firewall-cmd --reload

If the ssh configuration has been reloaded to use the new port, you should be able to test SSH login from another computer in the same network using the following command, substituting XXX.XXX.XXX with the IP address of your server:

ssh root@XXX.XXX.XXX -P 2223

How to create your own firewalld service definitions

As we have learned, firewalld already includes a good amount of standard service files, and sometimes, if you install new software packages on CentOS 7 from the standard repositories, corresponding firewalld service definitions get shipped together with the rest of the application and are installed as well. However, the time will come when there is just no service definition file available for your network-enabled application that requires incoming connections, so we will have to create a new service definition for ourselves.

Before you can create your own firewalld service file, you will need to find out the network protocol (TCP and/or UDP) and the required port or range of ports for your target service. In our example, we want to run and serve a simple Python test web application on our server, listening for incoming connections to the server on port 8080. As we have already learned that all standard firewalld zones will block all connections to the server if not explicitly enabled by a firewall service rule, we need to add such a rule to our active zone. First, let us check if there isn't already some service available because we don't want to reinvent the wheel. The following command will search for port 8080 in all the currently available firewalld service rules:

grep \"8080\"  /etc/firewalld/services /usr/lib/firewalld/services/ -R

The output of this command should be empty, so we need to create our own service now. Let's create a new service file in /etc/firewalld/services; the directory where all services from installed programs and custom user-services will live in your favorite text editor:

vi /etc/firewalld/services/python-webserver.xml

Now, put in the following content, then save and close the file:

<?xml version="1.0" encoding="utf-8"?>

<service>

  <short>A Python web server</short>

  <description>A firewall service for our Python web server</description>

  <port port="8080" protocol="tcp"/>

</service>

As you can see, service files are simple XML text files with some mandatory and some optional tags and attributes. Here, this example is showing you how easily you can create a full working service definition with just a few lines of code. It will work for most of all the basic system services, however, some of the standard services included in a firewalld enabled system also contain a module tag. For example, if you open the samba service file at /usr/lib/firewalld/services/samba.xml, you will see the XML tag: <module name="nf_conntrack_netbios_ns"/>. These are special kernel netfilter helper modules that can be dynamically loaded into the underlying kernel-based firewall, and which are needed for some system services, such as Samba or FTP, which create dynamic connections on temporary TCP or UDP ports instead of using static ports. But, you can also open multiple static ports for a firewalld service by adding multiple port tags within the service tag. To read more about the service XML file syntax and some advanced examples, open the Linux manual with the following command:

man firewalld.service

Now, after the file has been created, we need to make the firewalld daemon aware of this new service. Therefore, let’s reload the configuration:

firewall-cmd --reload

Afterward, check if the new service is available:

firewall-cmd --get-services | tr " " "\n" | grep python-webserver

Before we enable the new service, let’s first start our new Python test web server. To do this, just run the following command-line:

python -m SimpleHTTPServer 8080

This will start a simple HTTP server on port 8080 in the foreground; just leave it open and don’t stop it.

The next thing we want to make sure is that the current state of the firewall will block our web application. While you truly can only test your incoming firewall from an external source, you need another machine in the same network that can ping and communicate with our server. On this second computer, open a web browser and type the following URL, substituting XXX.XXX.XXX with the IP address of your server that is running the Python web server:

http://XXX.XXX.XXX:8080, for example http://192.168.1.7:8080

If you only have access to a Linux based computer with a command-line shell in the same network, you can use the following command instead:

curl http://XXX.XXX.XXX:8080

If you try to fetch this URL, you will get the error message, The connection was reset, or Connection refused after waiting some time, which shows us that access to the web server is currently blocked.

Now, back on the server, open a new shell leaving the Python web server running in another session. Finally, activate the Python web server temporarily in the firewall:

firewall-cmd --add-service=python-webserver

Congratulations! Your new Python web server sitting at port 8080 should now be accessible from other computers in your network using the following URL:

http://XXX.XXX.XXX:8080

If everything is working, the Python web server will show a simple directory listing of your home directory of the server called "Directory listing for /".

To end and clean up our little test, just stop the Python webserver using the Ctrl + C keys in the correct session running it. To discard the new firewall service rule in the firewall, just reload the service, as we have only added it temporarily, it will be gone if we reload:

firewall-cmd --reload

Troubleshooting and debugging firewalld

Oftentimes, you just want to quickly open a specific port for testing out things before writing your own custom-made service definition. In order to do this, you can use the following command line, which will open port 2888, using the tcp protocol, temporarily on the default zone:

firewall-cmd --add-port=2888/tcp

For troubleshooting blocking services, you can also switch the zone to trusted, which will open all ports in the firewall temporarily:

firewall-cmd --set-default-zone=trusted

This method is superior to the old iptables approach, where you needed to shutdown the complete firewall in order to try and find out if the firewall is to blame for connection problems, because with firewalld, you can do this at runtime.

Once you have finished your tests, just reload the firewall configuration:

firewall-cmd --reload

Summary

Thus we learned the fundamentals of CentOS7’s new default firewall service firewalld, customizing existing firewalld service definition files, and how to create, troubleshoot, and debug firewalld service.

Resources for Article:

 


Further resources on this subject:


You've been reading an excerpt of:

CentOS 7 Linux Server Cookbook - Second Edition

Explore Title