firewalld 101

20 November 2015

firewalld provides a dynamically managed firewall with support for network/firewall zones to define the trust level of network connections or interfaces. It has support for IPv4, IPv6 firewall settings and for ethernet bridges and has a separation of runtime and permanent configuration options. It also supports an interface for services or applications to add firewall rules directly.

The two important concepts of firewalld is zones and services.

A zone defines the trust level of the interface used for a connection. There are several pre-defined zones provided by firewalld.zone.

A service can be a list of local ports and destinations and additionally also a list of firewall helper modules automatically loaded if a service is enabled. Service configuration options and generic information about services are described in firewalld.service. The use of predefined services makes it easier for the user to enable and disable access to a service.

$ firewall-cmd --get-zones
block dmz drop external home internal public trusted work

In order from least trusted to most trusted, the pre-defined zones within firewalld are:

Zone Definition
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 simply 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 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

Changing the zone of your interface permanently

Interfaces will always revert to the default zone if they do not have an alternative zone defined within their configuration. On CentOS, these configurations are defined within the /etc/sysconfig/network-scripts directory with files of the format ifcfg-interface.

$ firewall-cmd --list-all
public (default, active)
  interfaces: enp0s3
  sources:
  services: dhcpv6-client ssh
  ports:
  masquerade: no
  forward-ports:
  icmp-blocks:
  rich rules:

You will see from the output that I have a public zone applied to enp0s3. I will then open /etc/sysconfig/network-scripts/ifcfg-enp0s3 to add zone at the bottom.

TYPE="Ethernet"
BOOTPROTO="dhcp"
DEFROUTE="yes"
PEERDNS="yes"
PEERROUTES="yes"
IPV4_FAILURE_FATAL="no"
IPV6INIT="yes"
IPV6_AUTOCONF="yes"
IPV6_DEFROUTE="yes"
IPV6_PEERDNS="yes"
IPV6_PEERROUTES="yes"
IPV6_FAILURE_FATAL="no"
NAME="enp0s3"
UUID="d51651b7-8f3a-4ee0-bee4-3b58f9e356b7"
DEVICE="enp0s3"
ONBOOT="yes"
ZONE=home

To implement your changes, you'll have to restart the network service, followed by the firewall service:

$ sudo systemctl restart network.service
$ sudo systemctl restart firewalld.service

Adjusting the Default Zone

You can change the default zone with the --set-default-zone= parameter. This will immediately change any interface that had fallen back on the default to the new zone.

$ sudo firewall-cmd --set-default-zone=home
success
$ firewall-cmd --list-all
home (default, active)
  interfaces: enp0s3
  sources:
  services: dhcpv6-client ipp-client mdns samba-client ssh
  ports:
  masquerade: no
  forward-ports:
  icmp-blocks:
  rich rules:

Adding a Service to your Zones

The easiest method is to add the services or ports you need to the zones you are using. Again, you can get a list of the available services with the --get-services option.

$ firewall-cmd --get-services
RH-Satellite-6 amanda-client bacula bacula-client dhcp dhcpv6 dhcpv6-client dns ftp high-availability
http https imaps ipp ipp-client ipsec kerberos kpasswd ldap ldaps libvirt libvirt-tls mdns mountd
ms-wbt mysql nfs ntp openvpn pmcd pmproxy pmwebapi pmwebapis pop3s postgresql proxy-dhcp radius
rpc-bind samba samba-client smtp ssh telnet tftp tftp-client transmission-client vnc-server wbem-https

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.

$ sudo firewall-cmd --zone=home --add-service=http
success
$ firewall-cmd --zone=home --list-services
dhcpv6-client http ipp-client mdns samba-client ssh

Once you have tested that everything is working as it should, you will probably want to modify the permanent firewall rules so that your service will still be available after a reboot.

$ sudo firewall-cmd --zone=home --permanent --add-service=http
success
$ sudo firewall-cmd --zone=home --permanent --list-services
dhcpv6-client http ipp-client mdns samba-client ssh

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 as easy as specifying the port or port range, and the associated protocol for the ports you need to open.

For instance, if our application runs on port 4000 and uses TCP, we could add this to the "home" zone for this session using the --add-port= parameter. Protocols can be either tcp or udp.

$ sudo firewall-cmd --zone=home --add-port=4000/tcp

After testing, you would likely want to add these to the permanent firewall.

$ sudo firewall-cmd --zone=home --permanent --add-port=4000/tcp
success
$ sudo firewall-cmd --zone=home --permanent --list-ports
4000/tcp

Defining a Service

Opening ports for your zones is easy, but it can be difficult to keep track of what each one is for. If you ever decommission a service on your server, you may have a hard time remembering which ports that have been opened are still required. To avoid this situation, it is possible to define a service.

Services are simply collections of ports with an associated name and description. Using services is easier to administer than ports, but requires a bit of upfront work. The easiest way to start is to copy an existing script /usr/lib/firewalld/services to the /etc/firewalld/services directory
where the firewall looks for non-standard definitions.

$ sudo cp /usr/lib/firewalld/services/http.xml /etc/firewalld/services/webrick.xml
$ sudo vi /etc/firewalld/services/webrick.xml
<?xml version="1.0" encoding="utf-8"?>
<service>
  <short>webrick</short>
  <description>Ruby's default HTTP server.This is single threaded which means can only process single request at a time.</description>
  <port protocol="tcp" port="4000"/>
</service>
$ sudo firewall-cmd --reload
success
$ firewall-cmd --get-services
RH-Satellite-6 amanda-client bacula bacula-client dhcp dhcpv6 dhcpv6-client dns ftp high-availability
http https imaps ipp ipp-client ipsec kerberos kpasswd ldap ldaps libvirt libvirt-tls mdns mountd ms-wbt
mysql nfs ntp openvpn pmcd pmproxy pmwebapi pmwebapis pop3s postgresql proxy-dhcp radius rpc-bind samba
samba-client smtp ssh telnet tftp tftp-client transmission-client vnc-server wbem-https webrick

Creating Your Own Zones

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

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, I could create a zone by typing

$ sudo firewall-cmd --permanent --new-zone=
success
$ sudo firewall-cmd --permanent --get-zones
block dmz drop external home internal public publicweb trusted work