How to set up a firewall using FirewallD on CentOS 8
Introduction
A Linux firewall used to protect your workstation or server from unwanted traffic. You can set up rules to either block traffic or allow through. CentOS 8 comes with a dynamic, customizable host-based firewall with a D-Bus interface. You can add or delete or update firewall rules without restarting the firewall daemon or service. The firewall-cmd act as a frontend for the nftables. In CentOS 8 nftables replaces iptables as the default Linux network packet filtering framework.
In this guide, we will show you how to set up a firewall for your CentOS 8 and manage with the help of firewall-cmd administrative tool.
Prerequisites
- an active KVM VPS or Linux Server
- root user
Basic concepts of FirewallD
Firewalld simplifies the concepts of network traffic management. You have two main ideas as follows when it comes to firewalld on CentOS 8.
Firewalld 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.
Below are the zones provided by FirewallD ordered according to the trust level of the zone from untrusted to trusted:
- drop: All incoming connections are dropped without any notification. Only outgoing connections are allowed.
- block: All incoming connections are rejected with an
icmp-host-prohibited
message forIPv4
andicmp6-adm-prohibited
for IPv6n. Only outgoing connections are allowed. - public: For use in untrusted public areas. You do not trust other computers on the network, but you can allow selected incoming connections.
- external: For use on external networks with NAT masquerading enabled when your system acts as a gateway or router. Only selected incoming connections are allowed.
- internal: For use on internal networks when your system acts as a gateway or router. Other systems on the network are generally trusted. Only selected incoming connections are allowed.
- dmz: Used for computers located in your demilitarized zone that have limited access to the rest of your network. Only selected incoming connections are allowed.
- work: Used for work machines. Other computers on the network are generally trusted. Only selected incoming connections are allowed.
- home: Used for home machines. Other computers on the network are generally trusted. Only selected incoming connections are allowed.
- trusted: All network connections are accepted. Trust all of the computers in the network.
To use the firewall, we can create rules and alter the properties of our zones and then assign our network interfaces to whichever zones are most appropriate.
Firewalld Runtime and Permanent Settings
Firewalld uses two separated configuration sets, runtime, and permanent configuration.
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 permenent 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.
Installing and Enabling firewalld
On CentOS 8, firewalld is installed and enabled by default. If for some reason it is not installed on your system, you can install and start the daemon by typing:
yum install firewalld
After you install firewalld
, you can enable the service and reboot your server. Keep in mind that enabling firewalld will cause the service to start up at boot. It is best practice to create your firewall rules and take the opportunity to test them before configuring this behavior in order to avoid potential issues.
systemctl enable firewalld systemctl start firewalld
You can check the status of the firewall service with:
firewall-cmd --state
If the firewall is enabled, the command should print running
. Otherwise, you will see not running
Firewalld Zones
If you haven’t changed it, the default zone is set to public
, and all network interfaces are assigned to this zone.
The default zone is the one that is used for everything that is not explicitly assigned to another zone.
We can see which zone is currently selected as the default by typing:
firewall-cmd --get-default-zone
Output Public
To get a list of all available zones, type:
firewall-cmd --get-zones
Output
block dmz drop external home internal public trusted work
To see the active zones and the network interfaces assigned to them:
firewall-cmd --get-active-zones
Output
public
interfaces: eth0
Here, we can see that our 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.
How do we know what rules are associated with the public zone though? We can print out the default zone’s configuration by typing:
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:
We can see the specific configuration associated with a zone by including the --zone=
parameter in our --list-all
command:
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:
If you want to check the configurations of all available zones type:
firewall-cmd --list-all-zones | less
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 create specific sets of rules for different zones and assign different interfaces to them. This is especially useful when you multiple interfaces on your machine.
To assign an interface to a different zone, specify the zone with the --zone
option and the interface with the --change-interface
option.
For instance, we can move our eth0
interface to the home zone by typing this:
firewall-cmd --zone=home --change-interface=eth0
Output
success
Verify the changes by typing:
firewall-cmd --get-active-zones
Outputhome
interfaces: eth0
Changing the Default Zone
If all of your interfaces can be handled well by a single zone, it’s probably easiest to just designate the best zone as default and then use that for your configuration.
You can change the default zone with the --set-default-zone=
parameter. This will immediately change any interface using the default zone:
firewall-cmd --set-default-zone=home
Output success
Creating new Zones
Firewalld also allows you to create your own zones. This is handy when you want to create per-application rules.
In the following example we’ll create a new zone named memcached
, open the port 18787
and allow access only from the 192.168.10.100
IP address:
firewall-cmd --new-zone=memcached --permanent
Add the rules to the zone:
firewall-cmd --zone=memcached --add-port=18787/udp --permanent
firewall-cmd --zone=memcached --add-port=18787/tcp --permanent
firewall-cmd --zone=memcached --add-source=192.168.10.100/32 --permanent
Reload the firewalld daemon to activate the changes:
firewall-cmd --reload
Firewalld Services
With firewalld you can allow traffic for specific ports and/or sources based on predefined rules called services.
To get a list of all default available services type:
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 kube-apiserver 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 get more details about each of these services by looking at their associated .xml
file within the /usr/lib/firewalld/services
directory. For instance, the HTTP service is defined like this:
WWW (HTTP) HTTP is the protocol used to serve Web pages. If you plan to make your Web server publicly available, enable this option. This option is not required for viewing pages locally or developing Web pages.
You can enable a service for a zone using the --add-service=
parameter. The operation will target the default zone or whatever zone is specified by the --zone=
parameter. By default, this will only adjust the current firewall session. 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. To allow incoming HTTP traffic (port 80) for interfaces in the public zone, only for the current session (runtime configuration) type:
firewall-cmd --zone=public --add-service=http
If you are modifying the default zone you can leave out the --zone
option.
To verify that the service was added successfully use the --list-services
option:
firewall-cmd --zone=public --list-services
Output cockpit dhcpv6-client http ssh
Alternately, you could use the --runtime-to-permanent
flag to save the currently running firewall configuration to the permanant config:
firewall-cmd --runtime-to-permanent
Be careful with this, as all changes made to the running firewall will be commited permenantly.
Use the --list-services
along with the --permanent
option to verify your changes:
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 want to add the https
service. We can add that to the current session and the permanent rule-set by typing:
firewall-cmd --zone=public --add-service=https firewall-cmd --zone=public --add-service=https --permanent
Creating a new FirewallD Service
As we have already mentioned, the default services are stored in the /usr/lib/firewalld/services
directory. The easiest way to create a new service is to copy an existing service file to the /etc/firewalld/services
directory, which is the location for user-created services and modify the file settings.
For instance, we could copy the SSH service definition to use for our example service definition like this. The filename minus the .xml
suffix will dictate the name of the service within the firewall services list:
cp /usr/lib/firewalld/services/ssh.xml /etc/firewalld/services/example.xml
Now, you can adjust the definition found in the file you copied. First open it in your favorite text editor. We’ll use vi
here:
vi /etc/firewalld/services/example.xml
To start, the file will contain the SSH definition that you copied:
/etc/firewalld/services/example.xml
SSH
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.
Save the file and reload the FirewallD service:
firewall-cmd --reload
The majority of this definition is actually metadata. You will want to change the short name and description for the service within the and
tags
For our example service, imagine that we need to open up port 5000 for TCP and 7000 for UDP.
We can modify the existing definition with something like this:
/etc/firewalld/services/example.xml
example service
This is just an example service. It probably shouldn't be used on a real system.
Save and close the file.
Reload your firewall to get access to your new service:
firewall-cmd --reload
You can see that it is now among the list of available services:
firewall-cmd --get-services
You can now use this service in your zones same as any other service
What If No Appropriate Service Is Available?
The services that are included with the firewalld installation represent many of the most common applications that you may wish 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 easiest 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 our application runs on port 3600 and uses TCP, we could temporarily add this to the public zone using the --add-port=
parameter. Protocols can be designated as either tcp
or udp
:
firewall-cmd --zone=public --add-port=3600/tcp
Output
success
We can verify that this was successful using the --list-ports
operation:
firewall-cmd --zone=public --list-ports
Output 3600/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 our application uses UDP ports 2000 to 3000, we could open these up on public by typing:
firewall-cmd --zone=public --add-port=2000-3000/udp
To keep the port open after a reboot, add the rule to the permanent settings by running the same command using the --permanent
flag or by executing:
firewall-cmd --zone=public --permanent --add-port=3600/tcp firewall-cmd --zone=public --permanent --add-port=2000-3000/udp firewall-cmd --zone=public --permanent --list-ports
Output 3600/tcp 2000-3000/udp
The syntax for removing a port is the same as when adding a port. Just use --remove-port
instead of the --add-port
option.
firewall-cmd --zone=public --remove-port=3600/tcp
Output success
How to write port forwarding firewalld rule
To forward traffic from one port to another port, first enable masquerading for the desired zone using the --add-masquerade
option.
For example, to enable masquerading for the external
zone, type:
firewall-cmd --zone=external --add-masquerade
In the following example we are forwarding the traffic from port 443 to port 8080
on the same server:
firewall-cmd --zone=external --add-forward-port=port=443:proto=tcp:toport=8080
- To delete above port forwarding, run
firewall-cmd --zone=external --remove-forward-port=port=443:proto=tcp:toport=8080
Forward traffic to another IP address
In the following example we are forwarding the traffic to lxd server/container hosted at 192.168.10.100 port 443:
firewall-cmd --zone=external --add-forward-port=port=80:proto=tcp:toaddr=192.168.10.100
Forward traffic to another server on a different port
In the following example we are forwarding the traffic from port 443 to port 8080
on a server with IP 192.168.10.100
:
firewall-cmd --zone=external --add-forward-port=port=443:proto=tcp:toport=8080:toaddr=192.168.10.100
To make the forward rule persistent, use:
firewall-cmd --runtime-to-permanent
Conclusion
You learned the basic concept of firewalld and some common examples for CentOS 8 server.
Make sure to allow all incoming connections that are necessary for the proper functioning of your system, while limiting all unnecessary connections.