The idempotency of actions
Whenever we create a remote management environment with a central repository, we need to consider the impact of running remote management activities on the system. A very common best practice, strongly adopted by all these frameworks, is idempotency.
An idempotent task is a task that will not modify a system if the system’s state is already how it should be. Or, differently put, repeatedly executing a task does not affect the system or the processes that run on it if nothing needs to be changed. As an example, consider loading an SELinux module: if the module is already loaded, then the module should not be reloaded. If it isn’t loaded yet, then we will load the proper module.
While most actions supported by the automation frameworks are idempotent, we will need to create custom actions ourselves if the framework does not support what we want. For instance, if the framework does not support loading SELinux modules, then we need to write our own code to do so.
Most orchestration frameworks will envelope non-idempotent tasks in a definition that is more idempotent. For instance, if a change in a configuration file requires a system reboot, then the enveloped definition would be something like reboot after file change. The engine can check the state of the file (when it changed) and the system (when it rebooted) and deduce whether a reboot is needed or not, even though a system reboot as a task is a non-idempotent task.
Policy and state management
The first set of scenarios that we want to support through the automation frameworks is to ensure that SELinux is active (enforcing) and that the right SELinux policy is loaded, a task usually performed by the machine’s package management system. While allowing the package management system to handle this is convenient, it only offers the ability to use distribution-specific default policies. Sysadmins of systems with different security requirements will be restrained when using default policies and will need to create custom policies and policy handling routines. So, we will examine how to distribute and load custom policies.
The custom policy we will use in the examples is a CIL policy, which is very new and often not directly supported by the automation frameworks. However, it gives us a nice reoccurring situation to create custom rules within the automation framework. We store the policy itself in a file called
test.cil that has the following content:
(auditallow staff_sudo_t sysadm_t (process (transition)))
This simple policy will enable logging any transition from the
staff_sudo_t domain to the
sysadm_t domain and is easy to test out with
sudo. In our example, it serves no other purpose than to quickly allow us to verify that the policy has been correctly loaded.
State-wise, we will ensure that the system is in enforcing mode, but have the
zoneminder_t SELinux domain marked as permissive.
SELinux configuration settings
The second group of actions we want to take up is to configure the system with the various SELinux settings we’ve discussed in different chapters before. Most of these we’ve seen through the
semanage commands, and we will learn how the various automation frameworks support these entries, and to what extent.
We will not go through every setting, but rather focus on the supported configuration sets within each automation framework. If a framework does not support a particular configuration (such as the
semanage ibpkey one, which is fairly new), we will need to create custom actions for this. In that case, we will show how to approach this once, as it is a recurring and similar approach for the others as well.
Setting file contexts
The third and final group of actions we want to see is how the automation frameworks support applying file contexts to resources. This can be applying an
semanage fcontext configuration, after which a restore operation is done (such as with
restorecon), but also validating whether the framework supports applying contexts directly.
Directly applying the context allows administrators to use the frameworks directly without having to twiddle with creating and reapplying file context definitions (which can have some performance overhead). However, this should only be considered if the automation framework is the sole method through which system changes can be made. In any other case, having missing file context definitions might lead to administrators resetting contexts to an incorrect state.
Recovering from mistakes
In this tutorial articles series, we’re diving into the various frameworks that allow managing SELinux across a multitude of systems. It is not the intention of this chapter to explain the frameworks themselves in detail, nor the secure configuration of the frameworks themselves. We don’t recommend immediately applying this to production systems without testing first, and make sure to have backups!
That being said, many settings applied in this chapter are easily corrected if things fail.
Every framework we discuss further has its own approach to infrastructure automation and configuration. It is not the intention of this book to dwell on the details of each framework, but rather to focus on its core support and how it deals with SELinux. We will also abstract away how to handle different Linux distributions and have all examples based on CentOS.
Furthermore, these frameworks are continuously improving and evolving. When we consider these frameworks in this chapter, we only explore how they are commonly used, and not how they can specialize in specific deployments. For instance, if a framework uses an agent-based architecture by default but also supports SSH-based connections, we will only consider the agent-based one in this book, as that is the default setup for these frameworks and we want to focus on the SELinux configuration support features.
But don’t let this stop you from experimenting with the frameworks further and adapting them to your own liking! That said, let’s dive into our first engine, Ansible.