SELinux hardening with Ansible

Updated -

In the article SELinux as a security pillar of an operating system - Real-world benefits and examples, we demonstrated real-world examples of SELinux protecting the systems. By default, RHEL provides SELinux in a configuration that does not require further adjustments in the most common scenarios. But, usability comes at the expense of security. On one hand, SELinux has a lot of security features and protections you can use to harden security, without limitations. On the other hand, configuring SELinux can be a complex and time-consuming task.

This article demonstrates how to quickly and easily harden your system with SELinux using an Ansible playbook. On top of that, with the Ansible automation tool, you can manage multiple hosts at once.

selinux System Role and selinux-playbooks

On RHEL-compatible systems, you can configure SELinux using System Roles, which is a collection of Ansible roles and modules providing a consistent configuration interface to remotely manage multiple RHEL systems. With the selinux System Role, you can:

  • set enforcing or permissive mode
  • restoring file contexts on specified files or directories
  • set and get booleans
  • set and get file contexts
  • manage logins
  • manage ports
  • manage SELinux modules
  • save or clean local policy modifications
  • lock down a system

The selinux-playbooks Github repository contains preconfigured SELinux booleans, which prevent processes from memory execution, sharing files, and connecting to ports. You can disable unconfined and permissive domains by applying the playbook from this repository. Moreover, an option for user mapping simplifies confining users. You can also use two additional playbooks for reverting SELinux configuration and locking down a system with SELinux.

Installation

You must install the Ansible tool to configure and apply playbooks. For the SELinux configuration, install also the following packages providing system roles.

RHEL 9:

# dnf install rhel-system-roles

Fedora:

# dnf install linux-system-roles

Download the configurable Ansible playbooks from the fedora-selinux Github project:

$ git clone https://github.com/fedora-selinux/selinux-playbooks.git

Move to the SELinux hardening directory:

$ cd selinux-playbooks/selinux-hardening/

Configuration

You can manage the main SELinux configuration through the selinux-playbook.yml file. The playbook includes variables for defining where SELinux is targeted and enforced, the file with hosts, tasks, SELinux roles, files for configuring SELinux booleans, and the file where users are mapped into SELinux users.

Hosts

By default, the configuration is targeted on localhost. Through the hosts.yaml file in the inventory/ directory , you can define the hosts you want to configure with the SELinux playbook. For example:

[webserver]
192.168.122.164

When you manage multiple hosts, you must define them in the main configuration file, selinux-playbook.yml. Make sure to use the -i inventory/hosts option when you run the playbook.

Users

Linux users, who are not mapped into SELinux users, are by default assigned to the SELinux user unconfined_u. This is the least restrictive user role. By mapping users to the SELinux policy, the configuration is more strict. SELinux controls user access through the defined SELinux users:

SELinux user Description
unconfined unconfined user role
sysadm general system administration role
staff administrator’s unprivileged user
user generic unprivileged user
guest least privileged terminal user
xguest least privileged X user

Linux users are mapped to the SELinux policy in the users/user_mapping.yml file.

  • The login parameter contains a user name of a user that is confined as an SELinux user.
  • The SELinux user is defined in the seuser parameter.
  • The state parameter can contain one of the following values:
    ** present - the user is mapped to SELinux users
    ** absent - the user is removed from the policy

Usage:

user_mapping:
  - { login: '<username>', seuser: '<selinux_user>', state: '<absent/present>'}

For example:

user_mapping:
  - { login: '__default__', seuser: 'unconfined_u', state: 'present' }
  - { login: 'niki', seuser: 'staff_u', state: 'present'}

SELinux booleans

SELinux booleans provide a quick way to configure parts of the SELinux policy. The following YAML files use booleans to prevent or mitigate various attacks:

  • files/execmem.yml - booleans to prevent execution of memory, stack, and heap and process tracing.
  • files/cant_connect.yml - booleans to prevent processes from connecting to port.
  • files/deny_export.yml - booleans to prevent Samba, GlusterFS, or NFS from sharing files.

You can customize the SELinux policy by enabling or disabling them, by changing its state to on or off. For example:

# Allow cluster administrative domains to connect to the network using TCP.
    - { name: 'cluster_can_network_connect', state: 'off', persistent: 'yes' }

Running the playbook

After you customize the configuration files, you can run selinux-playbook by using the following command:

# ansible-playbook selinux-playbook.yml -i inventory/hosts

If you run the playbook without any customization, SELinux booleans block domains from connecting to the port, exporting files, or executing in memory, heap, and stack. Default Linux users are assigned to staff_u. Permissive and unconfined domains are deactivated.

Reverting the configuration changes

To restore the SELinux configuration to the state before applying selinux-playbook, use the revert/revert-playbook.yml file. You can specify hosts through the inventory/hosts.yaml file if different than localhost.

Use the following command to revert the configuration changes by selinux-playbook.yml:

# ansible-playbook revert/revert-playbook.yml -i inventory/hosts

System lockdown

The system-lockdown/selinux-lockdown-playbook.yml playbook utilizes three powerful SELinux booleans, which prevent processes from transitioning to privileged user domains, restrict confined users from inserting Linux kernel modules (for example, an SELinux policy module), and manage the SELinux policy:

Boolean Description State
secure_mode Do not allow transition to sysadm_t, sudo and su on
secure_mode_insmod Do not allow any processes to load kernel modules on
secure_mode_policyload Do not allow any processes to modify kernel SELinux policy on

If you enable the secure_mode_policyload boolean, the system restricts any management of SELinux policy, modules, booleans, and the SELinux state. You can change this configuration only by rebooting the system and adjusting the SELinux state during the boot time by using the enforcing=0 kernel parameter. This switches SELinux to permissive mode.

IMPORTANT: Use this playbook only if you have a clear understanding of the changes it makes. If you run this playbook with all secure booleans enabled, keep in mind that users won't be able to revert the system lockdown using the revert playbook. Additionally, users will lose the ability to perform any operations related to SELinux.

Run the system-lockdown playbook:

# ansible-playbook system-lockdown/selinux-lockdown-playbook.yml -i inventory/hosts

Conclusion

Ansible playbooks provide an efficient way to configure SELinux on one or multiple systems. The selinux-playbooks project contains a set of playbooks you can use for additional security hardening of your systems depending on your scenario.

You can configure SELinux to block processes from sharing files, connecting to ports, and accessing memory. You can also confine Linux users by mapping them to SELinux users. You can also revert changes to the SELinux configuration back to its original state and lock down the SELinux system to prevent any further modifications.

Additional resources

For details on managing SELinux with Ansible and more information about the SELinux role, see the linux-system-roles/selinux Github repository and the Using the selinux System Role section in the Using SELinux document. The Automating system administration by using RHEL System Roles document provides comprehensive guidance for installing, configuring, and using RHEL System Roles.

Comments