Of course. Here is a comprehensive guide to libselinux and its Python bindings, covering what it is, why you'd use it, and how to use it with practical examples.

What is libselinux?
libselinux is the C library that acts as the primary interface between user-space applications (like web servers, daemons, or your Python script) and the Linux kernel's Security-Enhanced Linux (SELinux) subsystem.
Think of it as the translator or the API for SELinux. When an application needs to:
- Check its own security context.
- Change its security context.
- Check if it has permission to perform an action on a file.
- Find the security context of a file.
...it uses libselinux to communicate with the kernel's SELinux security server.
Why Use the Python Bindings?
While you can manage SELinux with command-line tools like getsebool, setsebool, chcon, semanage, etc., using the Python bindings (libselinux-python) is powerful for:

- Automation: Embedding SELinux logic directly into your Python scripts, deployment tools (Ansible, Puppet), or applications.
- Dynamic Security: Writing scripts that make decisions based on the current SELinux state.
- Fine-Grained Control: Performing checks and actions that are not easily done with standard CLI tools.
- Understanding SELinux: Interacting directly with the SELinux API is one of the best ways to understand how it works under the hood.
Installation
The Python bindings are typically provided in a package named libselinux-python or python3-libselinux.
On RHEL / CentOS / Fedora / Rocky Linux:
sudo dnf install libselinux-python3 # Or for older systems: sudo yum install libselinux-python
On Debian / Ubuntu:
sudo apt-get install python3-libselinux
Verification: After installation, you can verify it's working by importing it in a Python shell:

python3 >>> import selinux >>> print(selinux.is_selinux_enabled()) 1 # 1 means enabled, 0 means disabled >>> exit()
Core Concepts and Common Python Functions
Let's go through the most common tasks you'll perform with the selinux Python module.
A. Checking SELinux Status
import selinux
# Check if SELinux is enabled, disabled, or in permissive mode.
# Returns: 1 (enabled), 0 (disabled), -1 (error)
status = selinux.is_selinux_enabled()
if status == 1:
print("SELinux is enabled.")
elif status == 0:
print("SELinux is disabled.")
else:
print("Could not determine SELinux status.")
# Check the current mode: enforcing or permissive
# Returns: 1 (enforcing), 0 (permissive), -1 (error)
mode = selinux.security_getenforce()
if mode == 1:
print("SELinux is in enforcing mode.")
else:
print("SELinux is in permissive mode.")
B. Working with File Contexts (Labels)
This is one of the most common use cases. Every file, directory, and process on an SELinux-enabled system has a security context (or label).
import selinux
import os
# Path to the file/directory we want to inspect
path_to_file = "/var/www/html/index.html"
# Get the security context of a file
# Returns the context as a string (e.g., "system_u:object_r:httpd_sys_content_t:s0")
context = selinux.getfilecon(path_to_file)
print(f"Current context of {path_to_file}: {context[1]}")
# Set the security context of a file
# This is equivalent to the 'chcon' command.
new_context = "system_u:object_r:httpd_sys_content_t:s0"
try:
# selinux.setfilecon requires the path and the context string
selinux.setfilecon(path_to_file, new_context)
print(f"Successfully set context to {new_context}")
except OSError as e:
print(f"Error setting context: {e}")
# Restore the default (native) security context for a file
# This is equivalent to the 'restorecon' command.
try:
selinux.restorecon(path_to_file)
print(f"Successfully restored default context for {path_to_file}")
except OSError as e:
print(f"Error restoring context: {e}")
C. Working with Process Context
A process also has a security context, which determines its access rights.
import selinux
import os
# Get the security context of the current process
# Returns the context as a string
current_context = selinux.getcon()
print(f"Current process context: {current_context[1]}")
# Get the security context of another process (if you have permission)
# Takes a PID as an argument
other_pid = 1 # Example: PID 1 (init/systemd)
try:
other_context = selinux.getpidcon(other_pid)
print(f"Context of PID {other_pid}: {other_context[1]}")
except OSError as e:
print(f"Could not get context for PID {other_pid}: {e}")
D. Checking File Permissions (Access Vector Cache - AVC)
This is a more advanced and powerful feature. You can check if the current process is allowed to perform a specific action on a file without actually doing it. This is how the kernel makes decisions.
import selinux
import os
# The file we want to check access to
file_path = "/etc/passwd"
# The action we want to perform (e.g., read, write, execute)
access = os.R_OK # Or os.W_OK, os.X_OK
# Check if the current process has the required permission
# Returns: 1 (allowed), 0 (denied), -1 (error)
allowed = selinux.access_check(file_path, access)
if allowed == 1:
print(f"Current process is allowed to {access} on {file_path}")
elif allowed == 0:
print(f"Current process is DENIED to {access} on {file_path}")
else:
print(f"Error checking access for {file_path}")
E. Working with SELinux Booleans
SELinux booleans allow you to change certain aspects of the policy on the fly without reloading the entire policy.
import selinux
boolean_name = "httpd_can_network_connect"
# Check the current value of a boolean
# Returns: 1 (on), 0 (off), -1 (error)
current_value = selinux.security_get_boolean_active(boolean_name)
if current_value == 1:
print(f"Boolean '{boolean_name}' is ON.")
else:
print(f"Boolean '{boolean_name}' is OFF.")
# Set the value of a boolean (requires root privileges)
# 1 for on, 0 for off
try:
selinux.security_set_boolean(boolean_name, 1)
print(f"Successfully set '{boolean_name}' to ON.")
except PermissionError:
print("Permission denied. Are you root?")
except OSError as e:
print(f"Error setting boolean: {e}")
Practical Example: A Custom restorecon Script
Let's create a Python script that finds all .html files in a directory and ensures they have the correct SELinux context. This is a common task for web server administration.
#!/usr/bin/env python3
import os
import selinux
import argparse
def ensure_html_context(directory, target_context="system_u:object_r:httpd_sys_content_t:s0"):
"""
Recursively finds all .html files in a directory and restores their
default SELinux context.
"""
print(f"Scanning directory: {directory}")
print(f"Target context: {target_context}\n")
for root, _, files in os.walk(directory):
for file in files:
if file.endswith(".html"):
file_path = os.path.join(root, file)
# Get the current context
try:
current_context = selinux.getfilecon(file_path)[1]
except OSError as e:
print(f"Could not get context for {file_path}: {e}")
continue
# Check if it already has the correct context
if current_context == target_context:
print(f"[OK] {file_path} already has correct context: {current_context}")
else:
print(f"[FIX] {file_path} has incorrect context: {current_context}")
print(f" -> Restoring to: {target_context}")
try:
# Restore the default context
selinux.restorecon(file_path)
print(" -> Successfully restored.")
except OSError as e:
print(f" -> FAILED to restore: {e}")
print("-" * 40)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Ensure .html files have correct SELinux context.")
parser.add_argument("directory", help="The directory to scan.")
args = parser.parse_args()
if not os.path.isdir(args.directory):
print(f"Error: Directory '{args.directory}' not found.")
exit(1)
ensure_html_context(args.directory)
How to use the script:
- Save it as
fix_selinux_html.py. - Make it executable:
chmod +x fix_selinux_html.py. - Run it on your web root directory (requires
sudoifrestoreconneeds it):sudo ./fix_selinux_html.py /var/www/html.
Important Considerations
- Permissions: Many
libselinuxoperations, especially those that modify contexts or booleans, require root privileges. Your Python script may need to be run withsudo. - Error Handling: Always wrap
libselinuxcalls intry...except OSErrorblocks. SELinux operations can fail for many reasons (permission denied, invalid path, invalid context string, etc.). - Idempotency: When writing automation scripts, it's good practice to check the current state before making a change, as shown in the practical example. This makes your scripts idempotent (running them multiple times has the same effect as running them once).
