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:
- Port 8081 might already be allowed for HTTP services by default in some configurations
- The httpd process might be running in an unconfined context
- 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
-aoption means "add". - The
-t http_port_toption specifies the type to assign. - The
-p tcpoption 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/tcpspecifies the port and protocol to open.--permanentensures 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:
- Check service status and logs.
- Investigate SELinux denials in the audit log.
- Correct SELinux policies using
semanage. - Configure firewall rules using
firewall-cmd. - 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.



