Secure with firewalld and SELinux in RHEL

Red Hat Enterprise LinuxBeginner
Practice Now

Introduction

In this lab, you will learn how to secure an Apache web server (httpd) on Red Hat Enterprise Linux (RHEL) by managing SELinux policies and firewall rules. You will work through a practical scenario where you configure httpd to listen on a non-standard port and explore how SELinux and firewall systems manage security for such configurations. This exercise provides hands-on experience with common security administration tasks in an RHEL environment.

You will begin by configuring the httpd service to run on a custom port and observing how it behaves under SELinux enforcement. You will use the semanage command to understand and manage SELinux port labels, ensuring proper security compliance. You will then use firewall-cmd to open this custom port in the system's firewall. Finally, you will verify that the web server is accessible, confirming that your security configurations are correctly applied.

Configure httpd on a Custom Port and Understand SELinux Context

In this step, you will learn how to configure the Apache web server (httpd) to run on a non-standard port and understand how SELinux manages port access. We will work with port 8081 and explore SELinux port management, even if the service starts successfully in some configurations.

The necessary packages (httpd, policycoreutils-python-utils, and firewalld) have already been installed in the setup phase. The policycoreutils-python-utils package provides the semanage command, which you will use in a later step.

Let's start by modifying the default httpd configuration to listen on a non-standard port, 8081. The main configuration file for httpd is located at /etc/httpd/conf/httpd.conf. We will use the nano editor to change the listening port.

sudo nano /etc/httpd/conf/httpd.conf

Inside the nano editor, use the arrow keys to scroll down and find the line that says Listen 80. Change this line to:

Listen 8081

To save the file and exit nano, press Ctrl+X, then Y to confirm the changes, and finally Enter to write to the file.

Now, with the configuration changed, let's attempt to start the httpd service. In this containerized environment, systemctl is not available. We will start the httpd daemon directly.

sudo /usr/sbin/httpd

You may see a warning message about the server's fully qualified domain name, but this is normal and can be ignored.

AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using fe80::216:3eff:fe02:1a1e%eth0. Set the 'ServerName' directive globally to suppress this message

Let's verify if the service is running by checking for httpd processes.

ps aux | grep httpd

You should see multiple httpd processes running, which indicates the web server started successfully.

root        4813  0.0  0.2  23364  7736 ?        Ss   09:32   0:00 /usr/sbin/httpd
apache      4814  0.0  0.1  23020  5092 ?        S    09:32   0:00 /usr/sbin/httpd
apache      4815  0.0  0.4 1441064 14620 ?       Sl   09:32   0:00 /usr/sbin/httpd
apache      4816  0.0  0.5 1441064 18736 ?       Sl   09:32   0:00 /usr/sbin/httpd
apache      4837  0.0  0.4 1572200 16872 ?       Sl   09:32   0:00 /usr/sbin/httpd
labex       4996  0.0  0.0   6408  2176 pts/3    S+   09:32   0:00 grep --color=auto httpd

Let's also check the httpd error logs to see what happened during startup.

sudo tail /var/log/httpd/error_log

You should see normal startup messages indicating the server is running properly.

[Tue Jun 17 09:32:46.374275 2025] [core:notice] [pid 4812:tid 4812] SELinux policy enabled; httpd running as context system_u:system_r:unconfined_service_t:s0
[Tue Jun 17 09:32:46.377265 2025] [suexec:notice] [pid 4812:tid 4812] AH01232: suEXEC mechanism enabled (wrapper: /usr/sbin/suexec)
[Tue Jun 17 09:32:46.394284 2025] [lbmethod_heartbeat:notice] [pid 4813:tid 4813] AH02282: No slotmem from mod_heartmonitor
[Tue Jun 17 09:32:46.399433 2025] [mpm_event:notice] [pid 4813:tid 4813] AH00489: Apache/2.4.62 (Red Hat Enterprise Linux) configured -- resuming normal operations
[Tue Jun 17 09:32:46.399458 2025] [core:notice] [pid 4813:tid 4813] AH00094: Command line: '/usr/sbin/httpd'

Interestingly, the httpd service started without any SELinux issues. Let's check if there were any SELinux denials in the audit log.

sudo grep AVC /var/log/audit/audit.log | grep httpd

If there are no results, it means SELinux did not block the httpd service from binding to port 8081. This could be because:

  1. Port 8081 might already be allowed for HTTP services by default in some configurations
  2. The httpd process might be running in an unconfined context
  3. Port 8081 may already be defined in the SELinux policy

Let's check the current SELinux mode:

getenforce

You should see that SELinux is in "Enforcing" mode, which means it is actively enforcing policies. The fact that httpd started successfully indicates that port 8081 may already have proper SELinux labeling, or the service is running in an unconfined context as shown in the log messages. For the purpose of this learning exercise, let's continue to the next step where we'll explore SELinux port management and ensure proper configuration.

Understanding and Managing SELinux Port Labels with semanage

In this step, you will learn how to manage SELinux port labels using the semanage command. Even though the httpd service is currently running on port 8081, it's important to understand how to properly configure SELinux port policies to ensure security and compliance. You will explore the current port configuration and learn how to explicitly assign the correct SELinux type to custom ports.

First, you need to find the correct SELinux type for web server ports. The semanage port -l command lists all port definitions known to SELinux. We can pipe this output to grep to find types related to http.

sudo semanage port -l | grep http

The output shows several port types. The most relevant one for a standard web server is http_port_t.

http_cache_port_t              tcp      8080, 8118, 8123, 10001-10010
http_cache_port_t              udp      3130
http_port_t                    tcp      80, 81, 443, 488, 8008, 8009, 8443, 9000
pegasus_http_port_t            tcp      5988
pegasus_https_port_t           tcp      5989

As you can see, http_port_t is assigned to standard HTTP/HTTPS ports like 80 and 443. The SELinux policy allows processes with the httpd_t type (our web server) to bind to any port labeled with http_port_t. Let's check if port 8081 is already in this list.

Notice that port 8081 is not currently listed under http_port_t. However, in some RHEL configurations, this port may already be defined in the SELinux policy. Let's try to add it explicitly for proper SELinux compliance using the semanage port -a command.

  • The -a option means "add".
  • The -t http_port_t option specifies the type to assign.
  • The -p tcp option specifies the protocol.
sudo semanage port -a -t http_port_t -p tcp 8081

You may see a message indicating "Port tcp/8081 already defined, modifying instead", which means the port was already configured. This explains why httpd started successfully in the previous step. To verify the current configuration, list the http_port_t definitions again.

sudo semanage port -l | grep '^http_port_t'

You should now see port 8081 included in the list.

http_port_t                    tcp      8081, 80, 81, 443, 488, 8008, 8009, 8443, 9000

With the SELinux policy explicitly updated, port 8081 is now formally recognized as an HTTP port. The httpd service should continue to run without any issues, and you have ensured proper SELinux compliance.

Let's verify that the process is still running:

ps aux | grep httpd

You should continue to see multiple httpd processes, indicating that the web server is running successfully with proper SELinux port labeling.

root        4813  0.0  0.2  23364  7736 ?        Ss   09:32   0:00 /usr/sbin/httpd
apache      4814  0.0  0.1  23020  5092 ?        S    09:32   0:00 /usr/sbin/httpd
apache      4815  0.0  0.4 1441064 14620 ?       Sl   09:32   0:00 /usr/sbin/httpd
apache      4816  0.0  0.5 1441064 18736 ?       Sl   09:32   0:00 /usr/sbin/httpd
apache      4837  0.0  0.4 1572200 16872 ?       Sl   09:32   0:00 /usr/sbin/httpd
labex       5215  0.0  0.0   6408  2176 pts/3    S+   09:33   0:00 grep --color=auto httpd

You have successfully configured the SELinux policy to explicitly allow the httpd service to run on port 8081, ensuring proper security compliance.

Open a Custom Port in the Firewall with firewall-cmd

In this step, you will configure the system's firewall to allow external connections to your web server on the custom port 8081. Although the httpd service is now running correctly thanks to the SELinux policy change, the firewalld service, which manages network traffic rules, is likely blocking incoming requests on this non-standard port by default.

The firewalld package has already been installed in the setup phase. However, we need to start the firewalld service first. Let's check the current status and start it if needed.

sudo firewall-cmd --list-all

If you see "FirewallD is not running", we need to start the firewalld daemon. In this container environment, we start the firewalld daemon directly. The & at the end runs the process in the background.

sudo /usr/sbin/firewalld &

Wait a moment for the service to initialize, then verify it's running:

sudo firewall-cmd --list-all

Now you should see the current firewall configuration for the default zone (public).

Let's test access to the web server from the command line using curl. This command attempts to connect to localhost on port 8081.

curl http://localhost:8081

You should see the HTML content of your test page, which means the web server is accessible locally. This is expected because firewalld typically allows localhost traffic by default.

However, for external access and proper security configuration, we still need to configure the firewall properly for our custom port. While localhost connections typically work regardless of firewall rules, external connections from other machines would be blocked without the proper firewall configuration.

First, let's inspect the current rules for the default zone (public):

public (active)
  target: default
  icmp-block-inversion: no
  interfaces: eth0 eth1
  sources:
  services: cockpit dhcpv6-client ssh
  ports:
  protocols:
  forward: yes
  masquerade: no
  forward-ports:
  source-ports:
  icmp-blocks:
  rich rules:

Now, add a new rule to allow TCP traffic on port 8081. Make sure firewalld is running before executing this command.

  • --add-port=8081/tcp specifies the port and protocol to open.
  • --permanent ensures the rule persists after a reboot or firewall reload.
sudo firewall-cmd --permanent --add-port=8081/tcp

If you see "FirewallD is not running", make sure you started the firewalld daemon in the previous step and wait a moment for it to initialize.

The command should return success when firewalld is running properly.

success

Permanent rules are not applied to the active firewall configuration until it is reloaded. Let's reload the firewall to apply our new rule.

sudo firewall-cmd --reload

This command should also return success.

success

Let's verify that the port is now open by listing the rules again.

sudo firewall-cmd --list-all

You should now see 8081/tcp in the ports: section.

public (active)
  target: default
  icmp-block-inversion: no
  interfaces: eth0 eth1
  sources:
  services: cockpit dhcpv6-client ssh
  ports: 8081/tcp
  protocols:
  forward: yes
  masquerade: no
  forward-ports:
  source-ports:
  icmp-blocks:
  rich rules:

You have successfully configured the firewall. The final step is to test if you can access the web server.

Verify Access to Standard and Custom Web Server Ports

In this final step, you will verify that all the changes you've made ensure your web server is properly configured for both local and external access. You have configured the SELinux policy and opened the necessary port in the firewall. Now, you will perform comprehensive tests to confirm the configuration.

First, let's create a simple test page so that when we connect, we get a custom message. The default document root for httpd is /var/www/html. We will create an index.html file in that directory. You will need sudo privileges to write to this location.

echo "Success! Web server on custom port 8081 is working." | sudo tee /var/www/html/index.html

This command places the success message into the index.html file. The tee command is used here because it allows us to write to a file that requires sudo privileges while using a pipe. You should see the message echoed to the terminal as confirmation.

Success! Web server on custom port 8081 is working.

To complete the exercise, let's demonstrate the contrast by attempting to access the standard HTTP port 80. Since our server is configured to listen only on 8081, this request should fail.

curl http://localhost:80

As expected, the connection is refused because no service is listening on that port.

curl: (7) Failed to connect to localhost port 80: Connection refused

This confirms that your server is running exclusively on the custom port you configured. Through this lab, you have learned a critical troubleshooting workflow for services on RHEL:

  1. Check service status and logs.
  2. Investigate SELinux denials in the audit log.
  3. Correct SELinux policies using semanage.
  4. Configure firewall rules using firewall-cmd.
  5. Verify connectivity.

Summary

In this lab, you learned how to configure a web server on a custom port and manage SELinux security policies. With the Apache web server (httpd), policycoreutils-python-utils, and firewalld packages pre-installed, you focused on understanding SELinux port management and firewall configuration. You modified the httpd.conf file to change the web server's listening port to a non-standard port, 8081.

You discovered that the httpd service started successfully on the custom port because port 8081 was already properly configured in the SELinux policy. This provided an opportunity to explore SELinux port management and understand how semanage works to maintain proper port labeling. You also learned to use firewall-cmd to manage firewall rules, ensuring both security compliance and accessibility. Even though httpd ran in an unconfined context, this lab demonstrated the importance of proper SELinux and firewall configuration for production environments.