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

  1. 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
  2. List and query port labels — Use semanage port -l to display all port-to-type mappings and find the correct label for a specific service or port number
  3. 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
  4. Modify, delete, and verify port labels — Use semanage port -m and -d to 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_t processes to bind to ports labeled http_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_t label using semanage port
Port labels work alongside firewall rules — both are needed

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_t22/tcpsshd_tOpenSSH server
http_port_t80/tcp, 443/tcphttpd_tApache web server
http_cache_port_t8080/tcphttpd_tHTTP proxy / alternate web
ftp_port_t21/tcpftpd_tFTP server
dns_port_t53/tcp, 53/udpnamed_tBIND DNS server
mysqld_port_t3306/tcpmysqld_tMariaDB/MySQL
postgresql_port_t5432/tcppostgresql_tPostgreSQL
smtp_port_t25/tcp, 465/tcp, 587/tcppostfix_master_tMail server
ntp_port_t123/udpchronyd_tNTP / chrony
The most common exam scenario: SSH on a non-standard port

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
Port already defined error: use -m instead of -a

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 labelssudo semanage port -l
Find ports for a specific typesudo semanage port -l | grep ssh_port_t
Find the type for a specific portsudo semanage port -l | grep " 2222 "
List only custom (administrator-added) labelssudo semanage port -l -C
Add a TCP port labelsudo semanage port -a -t TYPE -p tcp PORT
Add a UDP port labelsudo semanage port -a -t TYPE -p udp PORT
Add a port rangesudo semanage port -a -t TYPE -p tcp PORT1-PORT2
Modify an existing port labelsudo semanage port -m -t TYPE -p tcp PORT
Delete a custom port labelsudo semanage port -d -t TYPE -p tcp PORT
View recent port bind denialssudo ausearch -m AVC -ts recent | grep name_bind
Verify service is listening on new portss -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 rulessemanage fcontext -lsemanage port -l
List custom rules onlysemanage fcontext -l -Csemanage port -l -C
Add a rulesemanage fcontext -a -t TYPE "PATH"semanage port -a -t TYPE -p PROTO PORT
Modify a rulesemanage fcontext -m -t TYPE "PATH"semanage port -m -t TYPE -p PROTO PORT
Delete a custom rulesemanage fcontext -d "PATH"semanage port -d -t TYPE -p PROTO PORT
Apply rule to existing objectsrestorecon -Rv /path/No apply step — port labels take effect immediately
Verifymatchpathcon -V FILEsemanage 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.

  1. Why does SELinux block a service from binding to a non-standard port, even if the firewall allows the connection?
  2. Write the command to list all port numbers currently assigned to the http_port_t type.
  3. Write the command to configure SELinux to allow Apache to listen on port 9001/tcp.
  4. You run sudo semanage port -a -t ssh_port_t -p tcp 2222 and get the error "Port tcp/2222 already defined". What does this mean, and what command should you use instead?
  5. An AVC denial shows { name_bind } denied for process sshd_t on port 2222. What is the complete fix — including all steps needed to make the service reachable?
  6. What is the difference between semanage port -a and semanage port -m?

Knowledge Check — Answers

  1. 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.
  2. sudo semanage port -l | grep http_port_t
  3. sudo semanage port -a -t http_port_t -p tcp 9001
  4. Port 2222 is already assigned to some type in the policy (it may already be labeled ssh_port_t, or some other type). The -a flag can only add a port that is not yet in the policy. Use semanage port -m -t ssh_port_t -p tcp 2222 to modify the existing label, or verify with semanage port -l | grep 2222 — if it already shows ssh_port_t, no change is needed.
  5. 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: set Port 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
  6. semanage port -a adds a new port-to-type mapping — it fails if the port is already assigned to any type. semanage port -m modifies an existing port-to-type mapping — it changes the type assigned to a port that is already in the policy. Use -a for unlabeled ports; use -m for ports that already have a label.

Key Takeaways

  1. SELinux labels network ports with type labels — services can only bind to matching types. Port 22 is labeled ssh_port_t; sshd_t can bind to it. A non-standard port is unlabeled (unreserved_port_t) — service bind is denied. Check with semanage port -l | grep PORT before adding a label.
  2. Add a port label with semanage port -a -t TYPE -p PROTO PORT. Use -m instead of -a if the port is already labeled. List custom labels with semanage port -l -C. Changes take effect immediately — no apply step like restorecon is needed.
  3. Diagnose bind failures with ausearch -m AVC -ts recent. Look for { name_bind } — the action that identifies port binding denials. src=PORT shows the blocked port; scontext=SERVICE_t shows the process type. Map the service type to its port type to find the correct label.
  4. 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 with ss -tlnp | grep PORT and semanage port -l | grep PORT.

Graded Lab

  • Run sudo semanage port -l | grep ssh to see the current SSH port label. Confirm port 22 is labeled ssh_port_t. Check whether port 2222 is labeled with semanage 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 with semanage port -l | grep ssh_port_t — confirm both 22 and 2222 appear. Check semanage port -l -C to confirm it is listed as a custom addition.
  • Add Port 2222 to /etc/ssh/sshd_config. Open port 2222 in the firewall with firewall-cmd --permanent --add-port=2222/tcp and reload. Restart sshd and confirm it listens with ss -tlnp | grep sshd.
  • Try to add port 80 to ssh_port_t with semanage port -a. Observe the "Port already defined" error (port 80 is already labeled http_port_t). Then use -m to change port 80 to ssh_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 with semanage port -l | grep 2222. Restart sshd — observe that it fails or falls back to port 22 only. Confirm with ss -tlnp.
  • Run sudo semanage port -l | grep http. Identify all ports assigned to http_port_t and http_cache_port_t. Add port 9090 to http_port_t and verify. Then check the audit log with sudo ausearch -m AVC -ts recent to view any recent port bind denials from the lab.
RHCSA Objective

"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.