Utilizing SaltStack to Configure SELinux

Posted on the 20 June 2021 by Satish Kumar @satish_kumar86

The second orchestration and automation framework we’ll consider is SaltStack, which has commercial backing by the SaltStack company. SaltStack uses a declarative language similar to Ansible and is also written in Python. In this chapter, we will use the open source SaltStack framework, but an enterprise version of SaltStack is available as well, which adds more features on top of the open source one.

How SaltStack works

SaltStack, oftenalso described as just Salt, is an automation frameworkthat uses an agent/server model for its integrations. Unlike Ansible, SaltStack generally requires agent installations on the target nodes (calledminions) andactivation of the minion daemons to enable communications to the master. This communication is encrypted, and the minion authentication uses public-key validation, which needs to be approved on the master to ensure no rogue minions participate in a SaltStack environment.

While agent-less installations are possible with SaltStack as well, we will focus on agent-based deployments. In such a configuration, the minions regularly check with the master to see whetherany updates need to be applied. But administrators do not need to wait until the minion pulls the latest updates: you can also trigger updates from the master, effectively pushing changes to the nodes.

The target state that aminion should be in is written down in aSalt State file, which uses the.slssuffix. These Salt State files can refer to other state files, to allow a modular design and reusability across multiple machines.

If we needmore elaborate coding, SaltStack supports the creation and distribution of modules, calledSalt execution modules. However, unlike Ansible’s Galaxy, no community repositories currently exist to find more execution modules.

Installing and configuring SaltStack

The installation of SaltStack is similar across the different Linux distributions. Let’s see how the installation is done on a CentOS machine:

  • We first need to enable the SaltStack repository that contains its software. The project maintains the repository definitions through RPM files that can be installed immediately:
# yum install
  • Once we have enabled the repository on all systems, install salt-master on the master, and salt-minion on the remote systems:
master ~# yum install salt-master
remote ~# yum install salt-minion
  • Before we start the daemons on the systems, we first update the minion configuration to point to the master. By default, the minions will attempt to connect to a host with the hostname salt, but this can be easily changed by editing /etc/salt/minion and setting the right hostname:
remote ~# vim /etc/salt/minion
master: ppubssa3ed
  • With the minion configured, we can now launch the SaltStack master (salt-master) and minion (salt-minion) daemons:
master ~# systemctl start salt-master
remote ~# systemctl start salt-minion
  • The minion will connect to the master and present its public key. To list the agents currently connected, use salt-key -L:
master ~# salt-key -L
Accepted Keys:
Denied Keys:
Unaccepted Keys:
Rejected Keys:

We need to accept the keys for the remote machines:

master ~# salt-key -a rem1.internal.genfic.local
The following keys are going to be accepted:
Unaccepted Keys:
Proceed? [n/Y] y
Key for minion rem1.internal.genfic.local accepted.
  • Once we have accepted the key, the master will know and control the minion. Let’s see whether we can properly interact with the remote system:
master ~# salt '*' service.get_all

This command will list all system services on the minion.

The salt command is the main command used to query and interact with the remote minions from the master. If the last command is successfully returning all system services, then SaltStack is correctly configured and ready to manage the remote systems.

Creating and testing our SELinux state with SaltStack

Let’s create our SELinux state called packt_selinux, and have it applied to the remote minion:

  • We first need to create the top file. This file is the master file for SaltStack, from which the entire environment is configured:
master ~# mkdir /srv/salt
master ~# vim /srv/salt/top.sls
    - packt_selinux
  • Next, we create the state definition for packt_selinux:
master ~# mkdir /srv/salt/packt_selinux
master ~# vim /srv/salt/packt_selinux/init.sls
    - source: salt://packt_selinux/test.cil
    - mode: 644
    - user: root
    - group: root
    - makedirs: True

The init.sls file is the main state file for this packt_selinux state. So, when SaltStack reads the top.sls file, it sees a reference to the packt_selinux state and then searches for the init.sls file inside this state.

  • Place the SELinux test.cil module, as defined earlier on in this chapter, inside /srv/salt/packt_selinux as we refer to it in the state definition. Once placed, we can apply this state to the environment:
master ~# salt '*' state.apply

The state.apply subcommand of the salt command is used to apply the state across the environment. Each time we modify our state definition, this command can be used to force an update to the minions. Without this, the minions will (by default) update their state every 60 minutes. These scheduled state updates are called mine updates and are configured on the agents inside /etc/salt/minion

Assigning SELinux contexts to filesystem resources with SaltStack

Atthe time of writing, support for addressing SELinux types in resources has not yet reached the stable versions of SaltStack. SaltStack, however, supports running commands but only if a certain test has succeeded (or failed).

Update theinit.slsfile and add the following code to it:

{%- set path = '/usr/share/selinux/custom/test.cil' %}
{%- set context = 'system_u:object_r:usr_t:s0' %}
set {{ path }} context:
    - name: chcon {{ context}} {{ path }}
    - unless: test $(stat -c %C {{ path }}) == {{ context }}

In this code snippet, we declare two variables (pathandcontext) so that we do not need to iteratethe path and context multiple times, and then use these variables in acmd.runcall.

Thecmd.runapproach allows us to easily create custom SELinux support using the commands we’ve seen earlier on in this book. Theunlesscheck contains the test to see whether we need to execute the command or not, allowing us to create idempotent state definitions.

Loading custom SELinux policies with SaltStack

Let’s load our custom SELinux module on the remote systems. SaltStack has support for loading SELinux modules through the selinux.module state:

load test.cil:
    - name: test
    - source: /usr/share/selinux/custom/test.cil
    - install: True
    - unless: "semodule -l | grep -q ^test$"

As in the previous section, we need to add an unless statement, as otherwise, SaltStack will attempt to load the SELinux module repeatedly every time the state is applied.

Using SaltStack’s out-of-the-box SELinux support

SaltStack’s native SELinux support is gradually expanding but still has much room for improvement:

  • With selinux.boolean, the SELinux boolean values can be set on the target machines:
    - value: True
  • The file contexts, as managed with semanage fcontext, can be defined using the selinux.fcontext_policy_present state:
    - sel_type: httpd_sys_content_t
  • To remove the definition, use theselinux.fcontext_policy_absentdefinition.
  • Withselinux.mode, we can put the system in enforcing or permissive mode:
  • Port mappings are handled using the selinux.port_policy_present state:
    - sel_type: ssh_port_t

With the approach mentioned earlier, we can apply SELinux configuration updates to systems in a repeatable fashion for unsupported settings.

Back to Featured Articles on Logo Paperblog