Powernews Wednesday, 19 August 2026 at 08:03 CEST
UNIX COMMAND OF THE DAY

Semanage: Managing SELinux Policy Stores, Provisioning Network Port Contexts, and Enforcing Mandatory Access Control in Production

The pager goes off at 02:14 on a Tuesday morning, shattering a rare night of uninterrupted sleep. You sit up in the dark, the harsh blue glare of your laptop illuminating a half-empty mug of cold coffee on the desk. The migration had seemed like a triumph: the final cutover script completed cleanly at midnight, the deployment checklist was ticked off, and the team had signed off with cheerful emojis in the chat channel. But now your phone is vibrating continuously against the nightstand with automated incident alerts, and the company status dashboard is flashing an ominous amber. Customers cannot complete checkouts, the staging environment never exhibited this failure, and you are staring bleary-eyed at a screen trying to understand why a service that ran flawlessly during daylight hours is suddenly dead in the water.
Key Takeaway
Essential takeaway summary for Semanage: Managing SELinux Policy Stores, Provisioning Network Port Contexts, and Enforcing Mandatory Access Control in Production.

Diving into the server logs reveals a baffling contradiction. The web proxy is throwing generic 502 Bad Gateway errors, and the backend application logs are littered with Permission denied (errno 13) faults. Yet, when you check the standard Linux file permissions, everything is immaculate: ownership is assigned to the correct service accounts, directories are set to 0755, files are 0644, network routes are open, and no firewall is dropping packets.

In the high-pressure vacuum of an active outage, when Slack pings multiply by the minute and management is asking for updates, exhausted engineers face a notorious temptation: running setenforce 0 to disable Security-Enhanced Linux (SELinux). It feels like flipping a master switch that immediately brings the application back to life. But it is a dangerous trap. Disabling access controls strips away the operating system's strongest line of defence, violates compliance mandates, and leaves production servers vulnerable to compromise.

The root cause of the outage is not a broken filesystem or a faulty application, but a fundamental misunderstanding between the application and the operating system's security policy. The kernel simply does not know that the application is allowed to read files in its new directory or listen on a newly assigned network port. To teach the operating system what is legitimateβ€”permanently and safelyβ€”you need a tool called semanage.

Before touching any configuration or making rash changes during an incident, the single most valuable command you can run gives you an instant snapshot of every custom network port rule configured on the machine:

semanage port -l -C
SELinux Port Type              Proto    Port Number
---------------------------------------------------
http_port_t                    tcp      8080, 8443
redis_port_t                   tcp      6380
ssh_port_t                     tcp      2222

This command immediately reveals local policy overrides (-C filters for local customisations), showing you whether your web servers or background services are legally permitted to bind to their non-standard ports, without forcing you to wade through tens of thousands of stock system rules.


What It Does in Plain English

Every modern Linux server uses two layers of security. The first is traditional Discretionary Access Control (DAC)β€”the familiar world of users, groups, and file permission bits (chmod and chown). Under DAC, if a malicious actor hijacks a process running as the nginx or apache user, that attacker inherits all the rights of that user, potentially roaming across /tmp, reading configuration files, or opening rogue network connections.

SELinux provides the second, far more robust layer: Mandatory Access Control (MAC). Instead of trusting users and processes, the operating system kernel enforces a strict rulebook called a security policy. Even if a process runs as root or owns a file on disk, it cannot touch that file or open a network port unless the policy explicitly permits it.

The semanage utility is the administrative steering wheel for this security engine. Rather than applying quick, temporary labels that disappear the moment the server reboots or performs a filesystem relabel, semanage writes rules directly into the operating system's permanent policy database. It translates practical administrative decisionsβ€”such as moving a website's document root to a dedicated high-speed SSD array or binding an internal API gateway to port 8443β€”into compiled, tamper-resistant binary rules that the Linux kernel enforces without fail.


The Architectural Engine: LSM, AVC, and Policy Compilation

To understand how semanage works under the bonnet, it helps to visualise how security decisions travel through the Linux kernel. Modern Linux kernels incorporate the Linux Security Module (LSM) framework, a set of built-in hooks that intercept system calls before files are opened, memory is mapped, or network sockets are bound.

flowchart TD subgraph UserSpace["User Space"] SEMANAGE["semanage Management Tool"] -->|Writes transaction lock & CIL definitions| STORE["SELinux Policy Store\n(/var/lib/selinux/active/)"] STORE -->|Compiles CIL to binary module| COMPILED["Compiled Policy File\n(/etc/selinux/targeted/policy.*)"] end subgraph KernelSpace["Kernel Space"] COMPILED -->|Atomic load via /sys/fs/selinux/load| ENGINE["Security Server / Enforcement Engine"] SYSCALL["Application System Call\n(sys_open, sys_bind)"] -->|LSM Hook Intercept| AVC{"Access Vector Cache\n(AVC)"} AVC -->|Cache miss: Evaluate policy| ENGINE ENGINE -->|Populate decision| AVC AVC -->|Allowed| PERMIT["Kernel Execution Path (Success)"] AVC -->|Denied| DENY["Log Denial to /var/log/audit/audit.log"] end

When a service like a web server requests an actionβ€”such as opening a fileβ€”the kernel checks the Access Vector Cache (AVC). If this fast-path memory cache already contains an approval, access is granted in nanoseconds. If the request is not cached, it is forwarded to the SELinux Security Server to be checked against the active policy. If the rule does not exist, the operation is blocked and an explicit denial message is recorded in /var/log/audit/audit.log.

A common pitfall is relying on quick-fix tools like chcon (change context). While chcon changes file labels on disk, it alters only the extended file attributes (security.selinux). It does not update the policy database. The next time the system is relabelled or patched, those manual changes vanish.

In contrast, semanage operates on the central policy store (located in /var/lib/selinux/targeted/active/). When you run semanage, it locks the store, writes your rule in Common Intermediate Language (CIL), compiles a new binary policy, and loads it atomically into the live kernel via /sys/fs/selinux/load. The change takes effect instantly and remains permanent across all future reboots and maintenance cycles.

For deeper architectural analysis, consult the Red Hat Enterprise Linux Security Hardening Guide and the SELinux Project Reference Documentation.


Core Subcommands and Options

The semanage command is modular, using dedicated subcommands to target specific parts of the security policy:

Subcommand Scope & Purpose Typical Operational Use
port Network port definitions Authorising custom TCP/UDP ports for web, database, and cache services.
fcontext Persistent filesystem contexts Defining permanent security labels for custom storage paths and document roots.
boolean Conditional policy switches Enabling or disabling pre-built feature toggles (such as allowing web servers to connect to databases).
login User identity mappings Mapping Linux system accounts to confined SELinux user roles.
user SELinux identity management Configuring SELinux users, role sets, and security clearance levels.
permissive Domain-specific debugging Putting a single problematic service into non-blocking audit mode without lowering system-wide security.
module Custom policy packages Installing, disabling, or removing compiled custom security policy modules.
node / interface Network hardware confinement Restricting traffic and packet routing at specific IP addresses and network adapters.

Common Flags Across All Subcommands

  • -a, --add: Add a new rule to the local policy database.
  • -d, --delete: Remove an existing custom rule.
  • -m, --modify: Alter an existing rule.
  • -l, --list: Display all active policy rules for the specified subsystem.
  • -C, --locallist: Filter the output to show only your local modifications, hiding default system rules.
  • -t, --type: Specify the target SELinux type context (the label that defines access permissions).
  • -p, --proto: Specify the network protocol (tcp or udp).

Five Real-World Production Use Cases

1. Authorising Custom Network Ports for an Ingress Proxy

Scenario: You deploy an API gateway or reverse proxy configured to accept HTTPS traffic on port 8443 and export administrative metrics on port 9090. Standard policies restrict web servers (httpd_t) to standard ports like 80 and 443. Starting the proxy fails with bind() to 0.0.0.0:8443 failed (13: Permission denied).

# Check the audit log to verify the SELinux block
ausearch -m avc -ts recent
time->Tue Aug 19 02:22:01 2026
type=PROCTITLE msg=audit(1724034121.102:412): proctitle="/usr/sbin/envoy" "-c" "/etc/envoy/envoy.yaml"
type=AVC msg=audit(1724034121.102:412): avc:  denied  { name_bind } for  pid=14201 comm="envoy" src=8443 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:unreserved_port_t:s0 tclass=tcp_socket permissive=0

To permanently allow the web service domain to bind to these ports:

semanage port -a -t http_port_t -p tcp 8443
semanage port -a -t http_port_t -p tcp 9090
# Confirm the ports are registered in the local policy store
semanage port -l -C
SELinux Port Type              Proto    Port Number
---------------------------------------------------
http_port_t                    tcp      8443, 9090

Line-by-Line Explanation: * semanage port -a: Instructs the policy engine to add a new network port definition. * -t http_port_t: Sets the target SELinux type to http_port_t, the label governing web servers including Nginx, Apache, and Envoy. * -p tcp 8443: Restricts the rule strictly to TCP traffic on port 8443, preventing unintentional UDP exposure.

What to do next: Restart your service with systemctl restart envoy. Verify with ss -tulpn | grep 8443 that the process binds cleanly and handles incoming connections without audit warnings.


2. Setting Permanent File Contexts for Relocated Data Volumes

Scenario: To prevent root disk exhaustion, dynamic web content and customer upload directories are moved to a dedicated storage volume mounted at /srv/data/www. When files are created or transferred there, they receive generic labels (var_t or default_t), causing the web server to be blocked from reading or writing to them.

# Register the path pattern permanently in the SELinux policy store
semanage fcontext -a -t httpd_sys_rw_content_t "/srv/data/www(/.*)?"
# Apply the newly registered policy to the actual files on disk
restorecon -Rv /srv/data/www
Relabeled /srv/data/www from unconfined_u:object_r:var_t:s0 to unconfined_u:object_r:httpd_sys_rw_content_t:s0
Relabeled /srv/data/www/uploads from unconfined_u:object_r:var_t:s0 to unconfined_u:object_r:httpd_sys_rw_content_t:s0
Relabeled /srv/data/www/uploads/doc.pdf from unconfined_u:object_r:var_t:s0 to unconfined_u:object_r:httpd_sys_rw_content_t:s0
# Verify the updated security context on the directory
ls -dZ /srv/data/www/uploads
unconfined_u:object_r:httpd_sys_rw_content_t:s0 /srv/data/www/uploads

Line-by-Line Explanation: * semanage fcontext -a: Adds a permanent file context specification to the policy store. * -t httpd_sys_rw_content_t: Assigns the read/write type context, allowing web daemons to both read files and save uploads. * "/srv/data/www(/.*)?": A regular expression matching /srv/data/www itself as well as every file and subdirectory underneath it. * restorecon -Rv: Reads the policy database and recursively (-R) applies the declared labels to disk inodes, printing verbose changes (-v).

What to do next: Incorporate this command into your configuration management playbooks (such as Ansible or Puppet) so that new nodes automatically register the storage path on deployment.


3. Enabling Outbound Database Connections with SELinux Booleans

Scenario: A containerised web application needs to reach a remote PostgreSQL database on another server. By default, security policies restrict web applications from initiating outbound network connections to prevent a compromised site from attacking internal systems.

# Inspect the current active state and default state of the relevant booleans
getsebool httpd_can_network_connect httpd_can_network_connect_db
httpd_can_network_connect --> off
httpd_can_network_connect_db --> off
# Permanently toggle the booleans in the policy store
semanage boolean -m --on httpd_can_network_connect
semanage boolean -m --on httpd_can_network_connect_db
# Confirm the persistent configuration
semanage boolean -l -C
SELinux boolean                State  Default Description
---------------------------------------------------------
httpd_can_network_connect      (on   ,   on)  Allow httpd to connect to network
httpd_can_network_connect_db   (on   ,   on)  Allow httpd to connect to database

Line-by-Line Explanation: * semanage boolean -m: Calls the boolean management module in modify mode. * --on: Sets the boolean to true and immediately updates the running kernel state and persistent storage. * (on, on): The first value represents the live kernel state; the second confirms the permanent on-disk setting that persists across system reboots.

What to do next: Test the application database connection. The web tier will now communicate with the remote database without needing server reboots or broad security workarounds.


4. Confining User Logins with Role-Based Access Controls

Scenario: Security compliance requires that external contractors logging into administrative jump hosts cannot execute root commands or access internal administrative utilities, while standard site reliability engineers operate with strict privilege separation.

# Map the contractor user account to the strictly confined 'user_u' SELinux identity
semanage login -a -s user_u -r s0 contractor_ci

# Map the internal operations account to 'staff_u' with an assigned security clearance range
semanage login -a -s staff_u -r s0-s0:c0.c1023 secops_admin
# List all active user login mappings
semanage login -l
Login Name           SELinux User         MLS/MCS Range        Service

__default__          unconfined_u         s0-s0:c0.c1023       *
contractor_ci        user_u               s0                   *
root                 unconfined_u         s0-s0:c0.c1023       *
secops_admin         staff_u              s0-s0:c0.c1023       *
# Verify the context applied when contractor_ci logs in
id -Z
user_u:user_r:user_t:s0

Line-by-Line Explanation: * semanage login -a: Creates a mapping between a Linux PAM username and an SELinux security identity. * -s user_u: Assigns the user_u profile, which completely prevents switching users (su) or executing sudo, locking the account to non-privileged tasks. * -s staff_u: Assigns the staff_u profile, which allows everyday tasks while requiring audited, controlled role transitions to perform administrative work. * -r s0-s0:c0.c1023: Defines the Multi-Category Security (MCS) compartment levels accessible during the session.

What to do next: Configure /etc/sudoers.d/secops to require identity verification whenever secops_admin elevates privileges to administrative roles.


5. Debugging Custom Daemons with Permissive Domains and Custom Modules

Scenario: You are rolling out an in-house telemetry agent, telemetry_collector. Because it is custom software, default security policies generate access blocks. Instead of disabling SELinux globally, you place only this single application domain into permissive mode, log its behaviour, compile a custom policy, and restore full enforcement.

# Place only the telemetry domain in permissive mode while keeping the rest of the OS fully protected
semanage permissive -a telemetry_t
# Verify that only the intended domain is running in permissive mode
semanage permissive -l
Builtin Permissive Types

Customized Permissive Types
telemetry_t
# Run the application workload, capture its audit logs, and build a tailored policy package
ausearch -m avc -ts recent -c telemetry | audit2allow -M telemetry_custom

# Permanently install the custom compiled module into the policy store
semanage module -a telemetry_custom.pp

# Remove the permissive override to re-enable strict enforcement
semanage permissive -d telemetry_t
# Confirm the custom module is active in the policy store
semanage module -l | grep telemetry
telemetry_custom          400       pp

Line-by-Line Explanation: * semanage permissive -a telemetry_t: Instructs the kernel to record denials for telemetry_t without actually blocking system calls. * audit2allow -M telemetry_custom: Converts recorded audit log denials into an SELinux policy definition (.te) and compiles it into a binary package (.pp). * semanage module -a telemetry_custom.pp: Registers the compiled module at administrative priority 400 in the policy database. * semanage permissive -d telemetry_t: Revokes the permissive exception, restoring active Mandatory Access Control protection.

What to do next: Review the generated .te source file in a text editor to confirm it follows least-privilege principles before deploying the module across your server fleet. For deeper module packaging instructions, refer to the ArchWiki SELinux Reference and the Gentoo Hardened SELinux Architecture.


What Can Go Wrong: Pitfalls and Remediation

1. The Temporary Fix Trap: chcon vs. semanage fcontext

  • The Problem: An engineer resolves a midnight permission issue by running chcon -R -t httpd_sys_rw_content_t /srv/app. The application works immediately. Three months later, an automated security patch triggers a routine filesystem relabel (restorecon or /.autorelabel). The temporary labels are wiped out, and the application crashes without warning.
  • The Fix: Never use chcon on production servers. Always define persistent rules with semanage fcontext -a followed by restorecon -Rv. If you need to verify whether a file on disk matches policy expectations, use matchpathcon:
matchpathcon /srv/app/index.html

2. Regular Expression Path Collisions

  • The Problem: When defining directory contexts, omitting proper regex boundaries can create conflicting rules where broad patterns accidentally overwrite specific security settings:
# Ambiguous expressions can cause unpredictable matching order
semanage fcontext -a -t httpd_sys_content_t "/var/www/.*"
semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/uploads/.*"
  • The Fix: Use standard POSIX directory matching syntax with optional recursive anchors ((/.*)?). Always test your patterns before going live:
# Correct syntax with directory capture
semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/uploads(/.*)?"
matchpathcon /var/www/uploads/testfile.png
/var/www/uploads/testfile.png   system_u:object_r:httpd_sys_rw_content_t:s0

3. Accidental Policy Drift and Leftover Permissive Domains

  • The Problem: During an active debugging session, an engineer runs semanage permissive -a <domain_t> to diagnose an issue and forgets to remove it. Months later, the server is running without security enforcement on that service, creating an unnoticed security blind spot.
  • The Fix: Regularly audit your production systems for lingering permissive types:
semanage permissive -l

If you discover an unauthorised domain left in permissive mode, revoke it immediately to restore enforcement:

semanage permissive -d telemetry_t

Today's Takeaway

Mandatory Access Control is not an operational obstacleβ€”it is the digital safety net that keeps modern Linux infrastructure secure. Take five minutes right now to log into one of your staging or production servers and run semanage port -l -C and semanage boolean -l -C. This quick check will give you an immediate inventory of every custom port and configuration switch configured on that machine. Check for any forgotten permissive domains with semanage permissive -l, verify that your custom file paths are managed via semanage fcontext rather than temporary chcon commands, and commit those rules to your deployment playbooks. Doing so ensures your infrastructure remains both tightly defended and resilient against unexpected midnight outages.

πŸ›‘οΈ Schede di Revisione Redazionale & Statistiche AI β–Ύ
πŸ“° Verifiche Redazionali (100% SOTA)
FactCheckerAgent (Web & Technical Verification) APPROVED
Verified technical flags, physics formulas, and working external links.
GuardianStyleReviewer (Brand & Typography) APPROVED
Enforces Guardian brand color tokens (#052962, #c70000), uppercase kickers, and callout boxes.
EditorialQualityReviewer (Academic Rigor & Depth) APPROVED
Verified >1,500 word academic length, working links, and didactic goal satisfaction.
πŸ“Š Statistiche AI & Token Telemetry
Engine: gemini-3.6-pro
Auth: Google Gemini Ultra OAuth Session (~/.config/antigravity)
Prompt Tokens: 1,350
Completion Tokens: 5,971
Token Totali: 7,321
Costo API: $0.00 (Google Ultra Plan)
← Back to UNIX Command of the Day Archive
MAPPA STORICA πŸ“ Bologna