Nando @ Aria Media

( learning Clojure ColdFusion Lucee Javascript jQuery Angular CSS Linux Apache HTML5 & etc )

Exploring CentOS 7 Firewalld

| Comments

New with CentOS 7 is firewalld, a replacement for iptables to manage the firewall. As with anything new, at first glance it seems confusing, but I’m finding I prefer it over iptables.

The first thing to understand about firewalld is that it is has multiple layers. It comes with a predefined set of zones, namely block, dmz, drop, external, home, internal, public, trusted, and work. Each of those zones can be associated with a network device or one or more ip addresses. Essentially, zones define and demarcate the level of trust an admin has decided to place on the devices and traffic within a network.

firewalld also pre-defines a set of services that can be added or removed from a zone. Effectively, when a service is added to a zone, it opens a port and sets any other necessary parameters. Services are defined with XML. Here’s what the http service looks like:

1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<service>
  <short>WWW (HTTP)</short>
  <description>HTTP is the protocol used to serve Web pages. If you plan to make your Web server publicly available, enable this option. ... </description>
  <port protocol="tcp" port="80"/>
</service>

So to open port 80 via the tcp protocol to serve http requests, as an example, first a zone must be associated with the network device that will handle the traffic, and then the http service added to the zone. As an admin, you can define your own custom services, or customize existing services. Other techniques allow you to open a port directly on a zone, or define more complex rules for access.

To configure the firewall and check its status, a command line client is provided, firewall-cmd. It can be used to make both permanent and temporary config changes. The configuration for firewalld is stored in XML files in /usr/lib/firewalld/ ( the default settings, not to be modified! ) and /etc/firewalld/ ( for user configured settings, which have preference over those in the default location ). These files can be edited, backed up, or used as templates for other server installations.

Now that we have an overview, we can get to work. To check if firewalld is running:

1
systemctl status firewalld

If you see from the output that firewalld is not running, or you see that the loaded service is disabled, here are the commands needed:

1
2
systemctl enable firewalld
systemctl start firewalld

If a service is enabled, it will start on system reboot. Hence it’s particularly important to ensure firewalld is enabled on a production server.

Here’s how to disable firewalld so it will not start at boot time, and shut it down:

1
2
systemctl disable firewalld
systemctl stop firewalld

Now I want to configure the firewall. First, I check for the name of the ethernet interface so that I can refer to it to associate it with a zone:

1
nmcli dev status

Then I check which zone eno16777736 is currently assigned to:

1
firewall-cmd --get-zone-of-interface=eno16777736

The result is “no zone”, so the next step is to add the ethernet interface to the public zone, which is the zone I’ve decided to use for http access to the server. It’s important to add the –permanent flag to the command so it is retained permanently, across reboots:

1
firewall-cmd --zone=public --add-interface=eno16777736 --permanent

Now I have to reload the firewall configuration for the changes to take effect:

1
firewall-cmd --reload

And then we can double check just to make sure the ethernet interface is now added to the public zone …

1
firewall-cmd --get-zone-of-interface=eno16777736

and the result is “public”, so that’s now set up correctly.

Let’s now check how the public zone is currently set up:

1
firewall-cmd --zone=public --list-all

Here we see again that the ethernet interface is added to the public zone, and that it is both active and the default zone. By default after installing CentOS 7, we have the services dhcpv6-client and ssh added to this zone. Taking a quick look at the description for this service to see what it does by opening /usr/lib/firewalld/services/dhcpv6-client.xml, we see, “This option allows a DHCP for IPv6 (DHCPv6) client to obtain addresses and other IPv6 settings from DHCPv6 server.” We won’t be using IPv6 addresses within our local network to access this machine, so I think it’s safe to remove this service, although we may want to leave it in place on a production server:

1
firewall-cmd --zone=public --remove-service=dhcpv6-client --permanent

Reminder - remember to always add the permanent flag to these commands if you want changes to be persisted!

Now we can add the services for http access to our public zone:

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

… reload the firewall …

1
firewall-cmd --reload

… and recheck the configuration, using list-services instead of list-all just to try it out:

1
firewall-cmd --zone=public --list-services

and I see that we now have services http https ssh configured. Excellent. Let’s test that in a web browser.

I’ve installed nginx web server, but see using systemctl status nginx that it’s not yet running or enabled, so first we run

1
2
systemctl start nginx
systemctl enable nginx

And then I go to 192.168.1.16 in a web browser and see Welcome to nginx! Good.

As a double check, let’s remove the http service and see what happens.

1
2
firewall-cmd --zone=public --remove-service=http --permanent
firewall-cmd --reload

Reloading 192.168.1.16, I get a No data received message, so that’s exactly what we should expect.

1
2
firewall-cmd --zone=public --add-service=http --permanent
firewall-cmd --reload

And adding the http service back to the public zone again allows the Welcome to nginx! page to be loaded in my browser. Perfect.

However, I still don’t have access to the CF Admin panel at http://192.168.1.16:8500/CFIDE/administrator/index.cfm, because that’s over port 8500. On a production machine, I absolutely would not open port 8500 for this purpose. But since this server is on our local office network, let’s see how we can do this.

The first option that comes to mind is to create a custom firewalld service specifically for this purpose. Documentation I’ve read recommends using existing services as a template. Custom services go in /etc/firewalld/services/. First let’s make a copy of the http service, calling it http8500, and place it in /etc/firewalld/services/:

1
cp /usr/lib/firewalld/services/http.xml /etc/firewalld/services/http8500.xml

Then we edit /etc/firewalld/services/http8500.xml to use port 8500 instead of port 80. Here’s what the modified file looks like:

1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<service>
  <short>CF Admin Access</short>
  <description>For CF Admin access via port 8500.</description>
  <port protocol="tcp" port="8500"/>
</service>

Then we add this service to the public zone and reload the firewall:

1
2
firewall-cmd --zone=public --add-service=http8500 --permanent
firewall-cmd --reload

And now http://192.168.1.16:8500/CFIDE/administrator/index.cfm works! Again, this is not how I’d set up access to the CF administrator on a production machine, but it was an opportunity to experiment with creating custom services. What I like about this option is that I can enable or disable it, independently of the other services enabled. So if I decide I want to lock this server down, I can quickly remove the http8500 service and access the CF Administrator via SSH Tunnelling.

What I usually do is move ssh access to an obscure port. I think we can easily accomplish this using a custom service, but before I do that, I want to take a look at how the localhost interface is set up within the firewall. Again, we use nmcli dev status to get the name of the localhost or loopback interface

1
nmcli dev status

It’s “lo”, so let’s see if it’s set to zone by default:

1
firewall-cmd --get-zone-of-interface=lo

Nope, the result I get is “no zone”. Let’s also see if there are any services added to the trusted zone, which would be the most appropriate for localhost

1
firewall-cmd --zone=trusted --list-all

At this point, nothing is added to this zone, no interfaces, services, sources or ports, etc. And the network interface “lo” isn’t associated with any zone.

Now what I want to see is how the server responds to localhost access with the firewall enabled. This might be important on a production server because I will use ssh tunneling to access any areas I will restrict from public access. So let’s logout from the server and login again with the -D flag so that I can tunnel into the test server and test if I have access via localhost with the firewall setup as it is now:

1
2
3
exit
ssh -D 6100 root@192.168.1.16
root@192.168.1.16's password:

I keep Firefox on my dev machine reserved and set up for ssh tunneling on port 6100, so I simply open Firefox and browse to http://localhost:8500/CFIDE/administrator/index.cfm, and find I can access the CF11 admin page and login. Browsing to http://localhost/, I see the Welcome to nginx! page. So at this point via localhost, I have access. ( Note for anyone without experience using ssh tunnelling, when I use localhost on Firefox set up for ssh tunneling, logged to the CentOS server using the -D flag, I am browsing the CentOS server next to me, not my dev machine. See SSH Tunnelling for details how to do this. )

Now what happens if I add the “lo” network interface to the trusted zone, where no access is currently set up?

1
2
firewall-cmd --zone=trusted --add-interface=lo --permanent
firewall-cmd --reload

Adding the “lo” interface to the trusted zone with no services had no affect at all on tunnelled access to localhost. So it looks like the firewall doesn’t interfere there. So to clean up, I will remove the “lo” interface from the trusted zone and call it a day.

1
2
firewall-cmd --zone=trusted --remove-interface=lo --permanent
firewall-cmd --reload

PS - For some reason, “lo” was not removed from the trusted zone according to firewall-cmd –zone=trusted –list-all unless and until I rebooted the server. The strange thing was that the config file was correctly altered, but somehow, firewalld didn’t seem to pick up the change. Perhaps this is the intended behavior, to prevent lockout during a current session, but I’ll look into filing a bug report later this evening … ( which I have now filed here ).

References:

  1. https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/Security_Guide/sec-Using_Firewalls.html
  2. http://www.certdepot.net/rhel7-get-started-firewalld/
  3. http://fedoraproject.org/wiki/FirewallD

Comments