21.1 Manage SELinux Port Labels
Key Takeaways
- SELinux labels TCP/UDP ports with types such as http_port_t; a service confined to a domain may only bind ports whose type its policy allows.
- List mappings with semanage port -l (filter with grep); add with -a -t TYPE -p protocol port, modify with -m, delete with -d.
- Changing the listen port of httpd (or similar) without updating the SELinux port type is a classic EX200 failure—firewall open is not enough.
- Port label changes via semanage are persistent; verify after change with semanage port -l and by restarting the service under Enforcing.
- When bind fails under Enforcing, check AVC denials with ausearch -m avc -ts recent and sealert; do not habitually disable SELinux to “fix” ports.
21.1 Manage SELinux Port Labels
Quick Answer: SELinux assigns types to TCP/UDP ports. List with
semanage port -l. Addsemanage port -a -t TYPE -p tcp|udp PORT, modify with-m, delete with-d. Common web example: non-default HTTP ports needhttp_port_t. Changes persist. Debug bind denials withausearch/sealertwhile remaining in Enforcing.
Why port labels matter on EX200
Under Manage security, Red Hat expects you to manage SELinux port labels. Exam scenarios often look like:
- “Configure Apache (httpd) to listen on port 8080 and allow it under SELinux.”
- “Add TCP port 8443 as an HTTP-related SELinux port type.”
- “Remove a custom port mapping that is no longer required.”
- “A service fails only when SELinux is Enforcing; fix the port label without disabling SELinux.”
You already know file contexts (ls -Z, restorecon) and modes (getenforce, setenforce, /etc/selinux/config) from earlier security sections. Port types are the network-side twin of file types: the policy decides which process domain may bind/connect which port type.
Firewalld is separate. Opening a port in firewalld allows packets through the host firewall. SELinux still decides whether the confined daemon is allowed to use that port. Graders expect both when a service must be reachable on a non-default port under Enforcing.
Mental model: domains, types, and ports
| Concept | Example | Role |
|---|---|---|
| Process domain | httpd_t | Label of the running service |
| File type | httpd_sys_content_t | Label of content the service may read |
| Port type | http_port_t | Label of ports the service may bind |
Policy rules roughly say: processes in domain X may bind ports of type Y. Default mappings already cover standard ports (for example TCP 80 and 443 map to HTTP-related types). Custom listen ports usually do not inherit those types until you map them with semanage port.
getenforce
sestatus
# Expect Enforcing on exam systems unless a task says otherwise
If you only test in Permissive, you may miss denials that Enforcing would block—practice and verify under Enforcing.
List port labels with semanage port -l
sudo semanage port -l
sudo semanage port -l | grep http
sudo semanage port -l | grep -E 'http_port_t|http_cache_port_t'
sudo semanage port -l | grep 8080
Output columns are essentially: SELinux type, protocol (tcp/udp), port list.
Example lines you will recognize (exact default lists vary slightly by policy package version):
http_port_t tcp 80, 81, 443, 488, 8008, 8009, 8443, 9000
mysql_port_t tcp 1186, 3306, 63132-63164
ssh_port_t tcp 22
Read before you write. If the task’s port is already listed under the correct type, do not blindly re-add it—re-adding an existing definition often errors. If the port is listed under the wrong type, you may need modify or delete then add, depending on how it was defined.
sudo semanage port -l | grep -w 8080
sudo semanage port -lC # when available: show local customizations only
On many RHEL systems, semanage port -l -C (or the policy’s “customized” view) highlights local overrides versus full policy defaults—useful to confirm your exam change stuck.
Add a port mapping: semanage port -a
# General form
sudo semanage port -a -t PORT_TYPE -p PROTOCOL PORT
# Classic EX200: allow httpd-style binding on TCP 8080
sudo semanage port -a -t http_port_t -p tcp 8080
sudo semanage port -l | grep -w 8080
| Flag | Meaning |
|---|---|
-a | Add a new port definition |
-t TYPE | SELinux port type (e.g. http_port_t) |
-p tcp / -p udp | Protocol—must match how the service listens |
PORT | Single port or range (syntax like 10000-10010 when supported) |
httpd on a non-default port (end-to-end pattern)
- Configure httpd to listen (for example
Listen 8080in config under/etc/httpd/). - Open the port in firewalld if remote access is required.
- Map the port for SELinux:
sudo semanage port -a -t http_port_t -p tcp 8080
- Restart and test under Enforcing:
sudo systemctl restart httpd
sudo systemctl status httpd --no-pager
ss -tlnp | grep 8080
curl -I http://127.0.0.1:8080/
If httpd fails to start or bind only under Enforcing, check port labels and file contexts on DocumentRoot/config (file contexts are covered in earlier sections)—both can cause AVC denials.
Other type examples (know the pattern)
| Service scenario | Typical port type | Example |
|---|---|---|
| HTTP/HTTPS alt ports | http_port_t | semanage port -a -t http_port_t -p tcp 8080 |
| SSH on non-22 | ssh_port_t | semanage port -a -t ssh_port_t -p tcp 2222 |
| DNS | dns_port_t | often already 53; custom as required |
| MySQL/MariaDB custom | mysqld_port_t / mysql_port_t | match what semanage port -l | grep mysql shows on the host |
On the exam: discover the correct type with semanage port -l | grep service-ish-name rather than inventing type strings. Policy type names are fixed by the installed SELinux policy modules.
Modify existing mappings: semanage port -m
Use -m (modify) when a port definition already exists and you need to change its type or adjust a definition the policy already owns in a way semanage treats as modify-able:
sudo semanage port -m -t http_port_t -p tcp 8080
| Situation | Prefer |
|---|---|
| Port not listed for the needed type | -a add |
| Port already defined and you must change type | -m modify (or delete then add if modify fails) |
| Port wrongly customized and must go away | -d delete |
# If add fails because the port is already defined:
sudo semanage port -a -t http_port_t -p tcp 8080
# ValueError / already defined → try modify:
sudo semanage port -m -t http_port_t -p tcp 8080
Exam discipline: read the error. “Already defined” is not a reason to set SELinux Permissive; it is a reason to list, then modify or delete/re-add correctly.
Delete a mapping: semanage port -d
sudo semanage port -d -t http_port_t -p tcp 8080
sudo semanage port -l | grep -w 8080 || echo "8080 mapping removed or not present"
Delete requires the same type and protocol that defined the local mapping. You generally cannot delete built-in default ports from base policy the same way you remove local customizations—if delete fails on a default port, you were not supposed to remove it; map additional ports instead.
# Wrong expectation: deleting tcp/80 from policy entirely — not an exam goal
# Right expectation: remove a lab custom you added earlier
sudo semanage port -d -t http_port_t -p tcp 8080
Persistence and package prerequisites
semanage comes from policycoreutils-python-utils (package name on RHEL; install with dnf if missing on a practice VM). Local customizations are stored in the SELinux policy store and survive reboot. You do not put semanage lines in /etc/rc.local for persistence—the policy database is the persistence mechanism.
rpm -q policycoreutils-python-utils
sudo dnf -y install policycoreutils-python-utils # if needed in a lab
After changes:
sudo semanage port -l | grep -w 8080
sudo systemctl restart httpd
getenforce
Troubleshooting: ausearch and sealert
When a service fails under Enforcing after a port change (or before you remembered to map the port):
# Recent AVC denials
sudo ausearch -m avc -ts recent
sudo ausearch -m avc -ts recent | grep -i http
# Human-readable guidance when setroubleshoot is installed
sudo sealert -a /var/log/audit/audit.log
# or follow the sealert command suggested in /var/log/messages
Typical port-related denial language involves name_bind on a port with a type that is not allowed for the domain (for example httpd_t trying to bind a port still typed as something other than an allowed HTTP port type).
| Tool | Use |
|---|---|
ausearch -m avc -ts recent | Raw recent denials from audit |
ausearch -m avc -ts today | Broader time window |
sealert | Explains denials; may suggest semanage port or booleans |
journalctl -u httpd | Service-level bind/start errors |
ss -tlnp / ss -ulnp | What is actually listening |
Do not “fix” port problems by:
- Leaving the system in Permissive permanently, or
- Setting SELINUX=disabled in
/etc/selinux/config, or - Only opening firewalld while ignoring SELinux.
Those hide the objective. EX200 wants the correct label (and related policy tools) under Enforcing.
# Temporary diagnosis only — return to Enforcing
sudo setenforce 0 # Permissive — denials logged, not blocked
# reproduce; then:
sudo setenforce 1
# implement the real fix with semanage port ...
Use Permissive briefly to confirm “this is SELinux,” then apply the port type fix and re-test Enforcing.
Interaction with firewalld (do both)
# Example: public reachability on 8080 plus SELinux port type
sudo firewall-cmd --permanent --add-port=8080/tcp
sudo firewall-cmd --reload
sudo semanage port -a -t http_port_t -p tcp 8080
sudo systemctl restart httpd
| Layer | Tool | Question answered |
|---|---|---|
| Host firewall | firewalld / firewall-cmd | May packets reach the host port? |
| Mandatory access | SELinux port type | May this domain bind/use the port? |
| Service config | httpd Listen, etc. | Does the daemon try that port? |
Fail any one layer and the exam check fails.
Exam workflows
Workflow A — Map TCP 8080 for HTTP and verify
getenforce
sudo semanage port -l | grep -w 8080
sudo semanage port -a -t http_port_t -p tcp 8080
# if already defined for another treatment:
# sudo semanage port -m -t http_port_t -p tcp 8080
sudo semanage port -l | grep -w 8080
sudo systemctl restart httpd
ss -tlnp | grep 8080
Workflow B — SSH on custom port 2222
# After sshd_config Port 2222 (and firewall if needed)
sudo semanage port -a -t ssh_port_t -p tcp 2222
sudo systemctl restart sshd
sudo semanage port -l | grep ssh_port_t
Workflow C — Remove a custom mapping
sudo semanage port -d -t http_port_t -p tcp 8080
sudo semanage port -l | grep -w 8080 || echo gone
Workflow D — Denial-driven discovery
sudo systemctl restart httpd
sudo ausearch -m avc -ts recent
# Identify name_bind / port in denial → choose type from:
sudo semanage port -l | grep http
sudo semanage port -a -t http_port_t -p tcp 8080
sudo systemctl restart httpd
getenforce # still Enforcing
Common traps
- Opening firewalld only and forgetting
semanage port. - Using
semanage port -awhen the port is already defined—need-mor delete first. - Wrong protocol (
tcpvsudp). - Wrong type name—always discover with
semanage port -l | grep .... - Mapping the port but never restarting the service.
- Fixing under Permissive and leaving the host Permissive.
- Editing only runtime
sshd/httpdwithout SELinux when the failure is name_bind. - Expecting file
restoreconalone to fix a port problem. - Deleting default ports like 22 or 80 from policy—not the skill; add custom ports.
- Ignoring
ausearch/sealertwhen the reason is unclear.
Relationship to other sections
| Skill | Where |
|---|---|
| SELinux modes (Enforcing/Permissive) | 20.x core |
| File/process contexts, restorecon | 20.x core |
| Port labels | 21.1 (this) |
Booleans (setsebool) | 21.2 |
| firewalld | Security / networking chapters |
| httpd listen configuration | Service deploy skills |
Section checkpoint
You should list SELinux port mappings with semanage port -l, add non-default ports with -a -t TYPE -p proto PORT (especially http_port_t for alternate HTTP ports), modify with -m, delete local mappings with -d, verify under Enforcing with service restart and ss/curl, and use ausearch/sealert when bind denials appear—without disabling SELinux. That is the EX200 bar for managing SELinux port labels on RHEL 10.
Apache must listen on TCP 8080 under SELinux Enforcing. Which command correctly adds the usual HTTP port type for that socket?
You run semanage port -a -t http_port_t -p tcp 8080 and receive an error that the port is already defined. What is the best next action?
Which command best lists current SELinux port type mappings so you can discover the right type name on the exam host?
httpd fails to bind a custom port only when getenforce shows Enforcing. Which pair is most appropriate for diagnosis and correct remediation?