RED HAT ENTERPRISE LINUX
Manage SELinux
Port Labels
Manage SELinux port labels
CIS126RH | RHEL System Administration 1
Mesa Community College
SELinux applies security labels not only to files and processes, but also to
network ports. When a service attempts to bind to a port, SELinux
checks whether the service's domain type is permitted to use that port's label.
Configuring a service on a non-standard port — moving SSH to port 2222, running
Apache on port 8080, or binding a database to a custom port — requires adding that
port to the correct SELinux port label using semanage port. This is
tested on the RHCSA exam.
Learning Objectives
- Explain SELinux port labeling — Describe how SELinux assigns type labels to network ports and why services must bind only to ports with the matching type label
-
List and query port labels —
Use
semanage port -lto display all port-to-type mappings and find the correct label for a specific service or port number - Add port labels with semanage port -a — Add a port number to an existing SELinux port type to allow a service to bind to a non-standard port
-
Modify, delete, and verify port labels —
Use
semanage port -mand-dto update or remove port labels, and confirm bindings work after changes
How SELinux Port Labeling Works
SELinux assigns a type label to every network port number and protocol. When a service tries to bind to a port, SELinux checks whether the service's domain is allowed to use that port's type.
- Every port is labeled with a type — e.g., port 80 is labeled
http_port_t - The SELinux policy permits
httpd_tprocesses to bind to ports labeledhttp_port_t - If Apache is configured to listen on port 8080, SELinux checks: "is port 8080 labeled
http_port_t?" - If not, SELinux blocks the bind — the service fails to start with an AVC denial
- Fix: add port 8080 to the
http_port_tlabel usingsemanage port
When moving a service to a non-standard port you need two changes:
(1) add the port to the SELinux port label with semanage port, and
(2) open the port in the firewall with firewall-cmd --add-port.
SELinux and the firewall are independent — fixing one does not fix the other.
Listing Port Labels: semanage port -l
# List all SELinux port type assignments
$ sudo semanage port -l
SELinux Port Type Proto Port Number
...
dns_port_t tcp 53
dns_port_t udp 53
ftp_port_t tcp 21, 989, 990
http_port_t tcp 80, 443, 488, 8008, 8009, 8443
http_port_t udp 80
mysqld_port_t tcp 1186, 3306, 63132-63164
ssh_port_t tcp 22
...
# Filter to find a specific service's ports
$ sudo semanage port -l | grep http
http_cache_port_t tcp 8080, 8118, 8123, 10001-10010
http_port_t tcp 80, 443, 488, 8008, 8009, 8443
# Find what type a specific port is labeled as
$ sudo semanage port -l | grep " 22 "
ssh_port_t tcp 22
# Check if a specific port is already labeled
$ sudo semanage port -l | grep 2222
# No output → port 2222 is not yet assigned a type
Common SELinux Port Types
| SELinux port type | Default ports | Service domain | Service |
|---|---|---|---|
ssh_port_t | 22/tcp | sshd_t | OpenSSH server |
http_port_t | 80/tcp, 443/tcp | httpd_t | Apache web server |
http_cache_port_t | 8080/tcp | httpd_t | HTTP proxy / alternate web |
ftp_port_t | 21/tcp | ftpd_t | FTP server |
dns_port_t | 53/tcp, 53/udp | named_t | BIND DNS server |
mysqld_port_t | 3306/tcp | mysqld_t | MariaDB/MySQL |
postgresql_port_t | 5432/tcp | postgresql_t | PostgreSQL |
smtp_port_t | 25/tcp, 465/tcp, 587/tcp | postfix_master_t | Mail server |
ntp_port_t | 123/udp | chronyd_t | NTP / chrony |
The RHCSA frequently asks to configure SSH on port 2222. The SELinux fix is:
sudo semanage port -a -t ssh_port_t -p tcp 2222
Adding a Port Label: semanage port -a
# Add port 2222/tcp to the ssh_port_t type
$ sudo semanage port -a -t ssh_port_t -p tcp 2222
# Syntax: semanage port -a -t TYPE -p PROTOCOL PORT
# -a = add
# -t = type (the SELinux label to assign)
# -p = protocol (tcp or udp)
# PORT = the port number to add
# Add port 8080 to http_port_t (for Apache on port 8080)
$ sudo semanage port -a -t http_port_t -p tcp 8080
ValueError: Port tcp/8080 already defined
# 8080 is already labeled as http_cache_port_t — use -m to modify
# Add port 9000 to http_port_t (not already labeled)
$ sudo semanage port -a -t http_port_t -p tcp 9000
# Verify the label was added
$ sudo semanage port -l | grep ssh_port_t
ssh_port_t tcp 22, 2222
If semanage port -a returns "Port already defined", the port is already
assigned to a different type. Use semanage port -m (modify) to change
it to the new type, or choose a port number that is not yet assigned.
Modifying and Deleting Port Labels
# MODIFY: change a port that is already labeled to a different type
# Use -m when the port already exists in the policy
$ sudo semanage port -m -t http_port_t -p tcp 8080
# Changes 8080 from http_cache_port_t to http_port_t
# Verify
$ sudo semanage port -l | grep 8080
http_port_t tcp 80, 443, 488, 8008, 8009, 8443, 8080
# DELETE: remove a custom port label (only custom labels can be deleted)
$ sudo semanage port -d -t ssh_port_t -p tcp 2222
# Returns port 2222 to unlabeled — SSH can no longer bind to it
# Attempting to delete a built-in label fails
$ sudo semanage port -d -t ssh_port_t -p tcp 22
ValueError: Port tcp/22 is not in the customized policy
# Built-in port labels cannot be deleted — only custom ones added with -a
# List only custom port labels (those added by administrators)
$ sudo semanage port -l -C
SELinux Port Type Proto Port Number
ssh_port_t tcp 2222
http_port_t tcp 9000
Port Label AVC Denial: What to Look For
When a service tries to bind to a port not labeled for its type, SELinux logs an AVC denial. Reading it reveals exactly which port and type are needed.
# Scenario: sshd configured for port 2222, port label not yet added
$ sudo systemctl restart sshd
Job for sshd.service failed. See 'journalctl -xe' for details.
# Check the SELinux audit log
$ sudo ausearch -m AVC -ts recent
type=AVC msg=audit(...):
avc: denied { name_bind } for
pid=1234 comm="sshd"
src=2222
scontext=system_u:system_r:sshd_t:s0
tcontext=system_u:object_r:unreserved_port_t:s0
tclass=tcp_socket permissive=0
# Diagnosis from the AVC:
# sshd (sshd_t) denied name_bind on port 2222
# Port 2222 is labeled unreserved_port_t — not ssh_port_t
# Fix: semanage port -a -t ssh_port_t -p tcp 2222
Complete Workflow: SSH on Port 2222
Exam scenario: configure SSH to listen on port 2222 instead of (or in addition to) port 22, with SELinux and firewall both updated.
# Step 1: Update sshd_config
$ sudo vim /etc/ssh/sshd_config
# Add or change: Port 2222
# Step 2: Add SELinux port label
$ sudo semanage port -a -t ssh_port_t -p tcp 2222
# Step 3: Open the port in the firewall
$ sudo firewall-cmd --permanent --add-port=2222/tcp
$ sudo firewall-cmd --reload
# Step 4: Restart sshd
$ sudo systemctl restart sshd
# Step 5: Verify the port is listening
$ ss -tlnp | grep sshd
LISTEN 0 128 0.0.0.0:2222 0.0.0.0:* users:(("sshd",...))
# Step 6: Verify SELinux label
$ sudo semanage port -l | grep ssh
ssh_port_t tcp 22, 2222
Complete Workflow: Apache on Port 9000
# Step 1: Check if port 9000 is already labeled
$ sudo semanage port -l | grep 9000
# No output — port 9000 is not yet in the policy
# Step 2: Add port 9000 to http_port_t
$ sudo semanage port -a -t http_port_t -p tcp 9000
# Step 3: Verify
$ sudo semanage port -l | grep http_port_t
http_port_t tcp 80, 443, 488, 8008, 8009, 8443, 9000
# Step 4: Update httpd to listen on port 9000
$ sudo vim /etc/httpd/conf/httpd.conf
# Change or add: Listen 9000
# Step 5: Open firewall port
$ sudo firewall-cmd --permanent --add-port=9000/tcp
$ sudo firewall-cmd --reload
# Step 6: Restart and verify
$ sudo systemctl restart httpd
$ ss -tlnp | grep 9000
LISTEN 0 128 *:9000 *:* users:(("httpd",...))
Port Ranges and UDP Labels
# Add a range of ports to a type
$ sudo semanage port -a -t http_port_t -p tcp 9000-9005
# Adds ports 9000, 9001, 9002, 9003, 9004, and 9005
# Add a UDP port label
$ sudo semanage port -a -t dns_port_t -p udp 5353
# Adds UDP port 5353 to dns_port_t (for mDNS on a custom DNS server)
# List UDP port labels
$ sudo semanage port -l | grep udp
dns_port_t udp 53
ntp_port_t udp 123
...
# Example: NTP server on non-standard UDP port
$ sudo semanage port -a -t ntp_port_t -p udp 1234
# Verify both TCP and UDP labels for a service
$ sudo semanage port -l | grep dns_port
dns_port_t tcp 53
dns_port_t udp 53, 5353
Diagnosing Port Label Problems
# Symptom: service fails to start or restart
$ sudo systemctl restart httpd
Job for httpd.service failed.
# Diagnosis Step 1: Check the SELinux audit log
$ sudo ausearch -m AVC -ts recent -c httpd
avc: denied { name_bind } for pid=... comm="httpd" src=9000
scontext=system_u:system_r:httpd_t:s0
tcontext=system_u:object_r:unreserved_port_t:s0
# { name_bind } = port bind denied
# src=9000 = the port that was blocked
# httpd_t needs to bind to a port labeled http_port_t
# port 9000 is labeled unreserved_port_t
# Diagnosis Step 2: Confirm what type port 9000 currently has
$ sudo semanage port -l | grep 9000
# No output → not yet labeled
# Fix: add the label
$ sudo semanage port -a -t http_port_t -p tcp 9000
$ sudo systemctl restart httpd
← restarts successfully
semanage port Quick Reference
| Task | Command |
|---|---|
| List all port labels | sudo semanage port -l |
| Find ports for a specific type | sudo semanage port -l | grep ssh_port_t |
| Find the type for a specific port | sudo semanage port -l | grep " 2222 " |
| List only custom (administrator-added) labels | sudo semanage port -l -C |
| Add a TCP port label | sudo semanage port -a -t TYPE -p tcp PORT |
| Add a UDP port label | sudo semanage port -a -t TYPE -p udp PORT |
| Add a port range | sudo semanage port -a -t TYPE -p tcp PORT1-PORT2 |
| Modify an existing port label | sudo semanage port -m -t TYPE -p tcp PORT |
| Delete a custom port label | sudo semanage port -d -t TYPE -p tcp PORT |
| View recent port bind denials | sudo ausearch -m AVC -ts recent | grep name_bind |
| Verify service is listening on new port | ss -tlnp | grep SERVICE |
Common Mistakes
| Mistake | What goes wrong | Correct approach |
|---|---|---|
Using -a on a port that is already labeled |
"Port tcp/PORT already defined" error — command fails | Check with semanage port -l | grep PORT first; use -m if already labeled |
| Forgetting to open the firewall port after adding the SELinux label | Service starts successfully but is unreachable from remote clients | Always do both: semanage port -a AND firewall-cmd --add-port |
| Adding the label but forgetting to restart the service | Label is set but the service still listens on the old port | Run sudo systemctl restart SERVICE after all changes |
| Specifying the wrong protocol (tcp vs udp) | Label applied to TCP but service uses UDP — bind still fails | Check the service documentation or AVC denial message to confirm protocol |
Trying to delete a built-in port label with -d |
"Port tcp/22 is not in the customized policy" error | Only custom labels (added with -a) can be deleted; built-in labels are read-only |
| Not checking the AVC denial before guessing the type | Wrong type chosen — service still blocked | Always read the AVC denial first: scontext type (httpd_t) → look up matching port type (http_port_t) |
Port Labels vs File Contexts: The Parallel
Port label management follows the same pattern as file context management — understanding one makes the other immediately clear.
| Operation | File contexts | Port labels |
|---|---|---|
| List all rules | semanage fcontext -l | semanage port -l |
| List custom rules only | semanage fcontext -l -C | semanage port -l -C |
| Add a rule | semanage fcontext -a -t TYPE "PATH" | semanage port -a -t TYPE -p PROTO PORT |
| Modify a rule | semanage fcontext -m -t TYPE "PATH" | semanage port -m -t TYPE -p PROTO PORT |
| Delete a custom rule | semanage fcontext -d "PATH" | semanage port -d -t TYPE -p PROTO PORT |
| Apply rule to existing objects | restorecon -Rv /path/ | No apply step — port labels take effect immediately |
| Verify | matchpathcon -V FILE | semanage port -l | grep PORT |
Complete Exam Workflow Summary
The two exam patterns for port label tasks, side by side.
Pattern A: Add label before configuring service
# 1. Add the SELinux port label
$ sudo semanage port -a -t ssh_port_t -p tcp 2222
# 2. Update the service config
# 3. Open the firewall port
$ sudo firewall-cmd --permanent --add-port=2222/tcp
$ sudo firewall-cmd --reload
# 4. Restart the service and verify
$ sudo systemctl restart sshd
$ ss -tlnp | grep 2222
Pattern B: Diagnose and fix a failing service
# 1. Read the AVC denial
$ sudo ausearch -m AVC -ts recent
# Find: name_bind denied, src=PORT, scontext=SERVICE_t
# 2. Add the label for that port
$ sudo semanage port -a -t SERVICE_port_t -p tcp PORT
# 3. Restart the service
$ sudo systemctl restart SERVICE
# 4. Verify the port is now labeled correctly
$ sudo semanage port -l | grep PORT
Knowledge Check
Answer these before moving to the next slide.
- Why does SELinux block a service from binding to a non-standard port, even if the firewall allows the connection?
- Write the command to list all port numbers currently assigned to the
http_port_ttype. - Write the command to configure SELinux to allow Apache to listen on
port
9001/tcp. - You run
sudo semanage port -a -t ssh_port_t -p tcp 2222and get the error "Port tcp/2222 already defined". What does this mean, and what command should you use instead? - An AVC denial shows
{ name_bind }denied for processsshd_ton port 2222. What is the complete fix — including all steps needed to make the service reachable? - What is the difference between
semanage port -aandsemanage port -m?
Knowledge Check — Answers
- SELinux assigns a type label to every network port. When a service tries to bind, SELinux checks that the service's domain type is permitted to use that port's label. A non-standard port has either no label or the wrong label — the bind is denied before any network connection occurs. The firewall controls external access; SELinux controls what the process can do internally.
sudo semanage port -l | grep http_port_tsudo semanage port -a -t http_port_t -p tcp 9001- Port 2222 is already assigned to some type in the policy (it may already be labeled
ssh_port_t, or some other type). The-aflag can only add a port that is not yet in the policy. Usesemanage port -m -t ssh_port_t -p tcp 2222to modify the existing label, or verify withsemanage port -l | grep 2222— if it already showsssh_port_t, no change is needed. - Complete fix for SSH on port 2222:
(1)sudo semanage port -a -t ssh_port_t -p tcp 2222— add SELinux label
(2) Edit/etc/ssh/sshd_config: setPort 2222
(3)sudo firewall-cmd --permanent --add-port=2222/tcp && sudo firewall-cmd --reload
(4)sudo systemctl restart sshd
(5) Verify:ss -tlnp | grep 2222 semanage port -aadds a new port-to-type mapping — it fails if the port is already assigned to any type.semanage port -mmodifies an existing port-to-type mapping — it changes the type assigned to a port that is already in the policy. Use-afor unlabeled ports; use-mfor ports that already have a label.
Key Takeaways
-
SELinux labels network ports with type labels — services can only bind to matching types.
Port 22 is labeled
ssh_port_t;sshd_tcan bind to it. A non-standard port is unlabeled (unreserved_port_t) — service bind is denied. Check withsemanage port -l | grep PORTbefore adding a label. -
Add a port label with
semanage port -a -t TYPE -p PROTO PORT. Use-minstead of-aif the port is already labeled. List custom labels withsemanage port -l -C. Changes take effect immediately — no apply step like restorecon is needed. -
Diagnose bind failures with
ausearch -m AVC -ts recent. Look for{ name_bind }— the action that identifies port binding denials.src=PORTshows the blocked port;scontext=SERVICE_tshows the process type. Map the service type to its port type to find the correct label. -
Non-standard ports need three changes: SELinux + firewall + service config.
semanage port -a(SELinux),firewall-cmd --add-port(firewall), and update the service configuration file. Restart the service. Verify withss -tlnp | grep PORTandsemanage port -l | grep PORT.
Graded Lab
- Run
sudo semanage port -l | grep sshto see the current SSH port label. Confirm port 22 is labeledssh_port_t. Check whether port 2222 is labeled withsemanage port -l | grep 2222— it should show no output. - Add port 2222 to
ssh_port_t:sudo semanage port -a -t ssh_port_t -p tcp 2222. Verify withsemanage port -l | grep ssh_port_t— confirm both 22 and 2222 appear. Checksemanage port -l -Cto confirm it is listed as a custom addition. - Add
Port 2222to/etc/ssh/sshd_config. Open port 2222 in the firewall withfirewall-cmd --permanent --add-port=2222/tcpand reload. Restart sshd and confirm it listens withss -tlnp | grep sshd. - Try to add port 80 to
ssh_port_twithsemanage port -a. Observe the "Port already defined" error (port 80 is already labeledhttp_port_t). Then use-mto change port 80 tossh_port_t— observe that it succeeds. Restore it:semanage port -m -t http_port_t -p tcp 80. - Delete the port 2222 custom label with
sudo semanage port -d -t ssh_port_t -p tcp 2222. Confirm it is gone withsemanage port -l | grep 2222. Restart sshd — observe that it fails or falls back to port 22 only. Confirm withss -tlnp. - Run
sudo semanage port -l | grep http. Identify all ports assigned tohttp_port_tandhttp_cache_port_t. Add port 9090 tohttp_port_tand verify. Then check the audit log withsudo ausearch -m AVC -ts recentto view any recent port bind denials from the lab.
"Manage SELinux port labels."
Check: semanage port -l | grep TYPE.
Add: semanage port -a -t TYPE -p tcp PORT.
Modify: semanage port -m -t TYPE -p tcp PORT.
Non-standard port also needs: firewall-cmd + service config + systemctl restart.