Forge Home
Premium module


Compliance Enforcement Module for Linux




32 latest version

Version information

  • 1.0.0 (latest)
released Sep 28th 2021
This version is compatible with:
  • Puppet Enterprise 2021.3.x, 2021.2.x, 2021.1.x, 2021.0.x, 2019.8.x
  • Puppet >= 6.23.0 < 8.0.0
  • CentOS
  • audit_authselect
  • audit_duplicate_gid
  • audit_duplicate_group_names
  • audit_duplicate_uid
  • audit_duplicate_user_names
  • audit_etcpasswd_groups
  • audit_pw_change_date
  • and 16 more. See all tasks


puppetlabs/cem_linux — version 1.0.0 Sep 28th 2021



Table of Contents


The cem_linux module is one of two Compliance Enforcement Modules (CEM). These are supported Puppet modules developed specifically to bring your Puppet Enterpirse (PE) managed nodes under compliance. CEM currently supports CIS (Center for Internet Security) compliance rules, and other compliance frameworks will be added to CEM in the future.

By default, CEM enforces CIS rules for the level 1 server profile.

This README walks you through installing CEM and customizing the configuration settings to suit your own compliance needs. For a list of available parameters, see the CEM reference.

To manage Windows nodes, navigate to cem_windows.


System requirements

cem_linux supports the following operating systems and CIS benchmarks:

Operating system Framework Level Profile
Red Hat Enterprise Linux 7 CIS benchmarks v3.1.1 1 Server
Red Hat Enterprise Linux 8 CIS benchmarks v1.0.1 1 Server
CentOS Linux 7 CIS benchmarks v3.1.1 1 Server

Install CEM with Code Manager

To install CEM with Code Manager, you need to add the Forge premium content API key to your primary Puppet server.

  1. Add the following Hiera code to your primary Puppet server:
   authorization_token: 'Bearer <API key>'
  1. Run the Puppet agent on your primary Puppet server.
  2. Navigate to the Puppet Enterprise (PE) console, and click Node groups.
  3. Create a new node group.
  4. On the Classes tab — in the Add new class field — select the cem_linux class.
  5. Click Add to node group, and then commit the changes.
  6. Run puppet agent -t on your primary Puppet server.

CEM creates the following directories on posix systems:

  • /opt/puppetlabs/cem/
  • /opt/puppetlabs/cem/scripts/

Install CEM without Code Manager

  1. Install CEM's dependencies from Puppet Forge with the following command:

puppet module install <module-name> --version <module-version>

For example:

puppet module install puppetlabs-stdlib --version 6.6.0

Note that you need to install each CEM dependency separately.

  1. Install CEM by extracting the module archive to your chosen location — each environment you want to run CEM. For example:


Note: If your Puppet Server does not have internet access, you need to manually download each CEM dependency, copy it to Puppet Server, and extract it from each environment’s module directory.


By default, CEM enforces CIS rules for the level 1 server profile — based on default acceptable values for each CIS recommendation. However, sometimes enforcing these values can leave your nodes in an undesirable state. In these situations, you can customize how CEM enforces the rules and tailor them to your own compliance needs.

Important: CEM's default settings are fully CIS compliant — too much customization can result in your configurations being noncompliant.

Finding and setting configuration options

You can find configuration options in the class *.pp file. For example, in the manifests/benchmarks/cis/controls/ensure_at_is_restricted_to_authorized_users.pp file below, you can can see the optional parameters in the comments:

# @api public
# @param [Boolean] enforced
#   If yes, the control will be enforced
# @param [Hash] config
#   Options for the control
# @option config [Boolean] purge_at_deny
# @option config [Boolean] set_at_allow_perms
# @option config [Array[String[1]]] at_allowlist
# @see cem_linux::utils::packages::linux::at

You can set these configuration options in Hiera using the following format:

cem_linux::benchmark: '<benchmark code>'
  <config options>

For example, configuring the options purge_at_deny, set_at_allow_perms and at_allowlist would look like:

# control-repo/data/common.yaml
cem_linux::benchmark: 'cis'
  profile: 'server'
  level: '1'
      purge_at_deny: true
      set_at_allow_perms: true

You can find all the configuration options in the

Top-level configuration options

The configuration options are configured at the top-level of the module. If configuring with Hiera, these options will be prefixed with cem_linux::

  • benchmark - Enum['cis'] - the compliance framework to use. CEM only supports cis. Default: cis.
  • config - Optional[Hash] - where all non-top-level configuration options live. Default: undef.
  • allow_on_kubernetes_node - Boolean - If cem_linux detects that it is running on a Kubernetes cluster node or host, it will not enforce any rules. It will log a warning stating such. This is to prevent accidentally enforcing incorrect compliance settings that can render Kubernetes non-functional. Default: true.

General configuration options

These options are available as key-value pairs within the cem_linux::config: hash.

  • only: - Optional[Array[String]] — takes an array of control class names (manifests/benchmarks/<benchmark>/controls/*.pp) — classes specified here are included in the catalog. Takes precedence over ignore:. Default: undef.
  • ignore: - Optional[Array[String]] — takes an array of control class names (manifests/benchmarks/<benchmark>/controls/*.pp). The classes specified here are not included in the catalog. If only: is specified, this option does nothing. Default: undef.
  • control_configs - Optional[Hash] — where all rule-specific configurations live. Default: undef.

CIS configuration options

These options are available as key-value pairs within the cem_windows::config: hash.

  • profile: - Optional[Enum['server', 'workstation']] — the name of the benchmark profile. CEM only supports server. Default: server.
  • level: - Optional[Enum['1', '2']] — the name of the profile level. CEM only supports 1. Default: 1.
  • firewall_type: - Optional[Enum['iptables', 'firewalld', 'unmanaged']] — the preferred firewall provider. If set to unmanaged, CEM will not enforce any firewall-related rules. Default: firewalld.

Control classes

Control classes (manifests/benchmarks/<benchmark>/controls/*.pp) are the interfaces that configure rule settings. Each control class accepts the following two parameters:

  • The $enforced (Boolean) parameter — this toggles whether the included code in the manifest is executed.
  • The $config parameter — this holds the configuration options for a control class as keys in the hash. You set the $config parameter based on values from the control_configs hash. Each top-level key is a control class name (sans path and .pp suffix) and the value of that key is a hash. The keys of the sub-hash map 1 to 1 with the configuration options available in the specific control class.

For example, the control class would look like:

class cem_linux::benchmarks::cis::controls::super_cool_class (
  Boolean $enforced => true,
  Boolean $config => {},
) {
  if $enforced {
    class { 'cem_linux::utils::super_cool_util':
      param_one => dig($config, 'param_one'),
      param_two => dig($config, 'param_two'),

And the Hiera file would look like:

      param_one: 'Dude'
      param_two: 'Sweet'

For a list of control classes and their configuration options, see the reference.

Configuration examples

To see what CEM looks like in production, see the configuration examples below.

Basic configuration example

When you specify a compliance framework, CEM is configured to provide rule enforcement and configuration for that framework. For example, to enforce the CIS Server Level 1 benchmark for a node, you need to classify the node with the CEM class, set the framework parameter to cis, and run Puppet.

In the following example, CEM enforces the CIS level 1 server recommendations "Ensure AIDE is installed" and "Ensure filesystem integrity is regularly checked" on a CentOS 7 node.

  1. Add the following Hiera data to your control repo:
# control-repo/data/nodes/<node name>.yaml
cem_linux::benchmark: 'cis'
  profile: 'server'
  level: '1'
    - 'ensure_aide_is_installed'
    - 'ensure_filesystem_integrity_is_regularly_checked'
  1. Classify the node with the class cem_linux.
  2. Run Puppet.

Some CIS recommendations require you to run a Bolt task. The output of a Puppet run for the debug logs tell you which task to run.

Advanced configuration example

Building on the basic configuration example, the following example customizes the AIDE configuration file in Hiera.

  1. Add the following code to the node's Hiera file:
# control-repo/data/nodes/<node name>.yaml
cem_linux::benchmark: 'cis'
  profile: 'server'
  level: '1'
    - 'ensure_aide_is_installed'
    - 'ensure_filesystem_integrity_is_regularly_checked'
        - 'PERMS = p+u+g+acl+xattrs'
        - 'CONTENT_EX = sha256+ftype+p+u+g+n+acl+xattrs'
        - '/root/\..* PERMS'
        - '/root/   CONTENT_EX'
  1. Classify the node with the class cem_linux.

  2. Run Puppet.

  3. Run the Bolt task specified in the debug log.

The AIDE configuration file now reflects the changes in Hiera.

Configuring custom logrotate rules

The following example creates custom logrotate rules for the primary Puppet server's puppetserver logs.

# control-repo/data/nodes/<your puppetserver>.yaml
            - '/var/log/puppetlabs/puppetserver/puppetserver.log'
            - '/var/log/puppetlabs/puppetserver/pcp-broker.log'
            - '/var/log/puppetlabs/puppetserver/puppetserver-access.log'
            - '/var/log/puppetlabs/puppetserver/puppetserver-daemon.log'
            - '/var/log/puppetlabs/puppetserver/puppetserver-status.log'
            - '/var/log/puppetlabs/puppetserver/code-manager-access.log'
            - '/var/log/puppetlabs/puppetserver/file-sync-access.log'
            - '/var/log/puppetlabs/puppetserver/masterhttp.log'
          create_owner: 'puppet'
          create_group: 'puppet'

Configuring sudo without a password

The following example configures the admins group to grant sudo access without a password.

cem_linux::benchmark: 'cis'
  profile: 'server'
  level: '1'
      package_ensure: 'installed'
              - 'NOPASSWD:'

Configuring user SSH keys

The following example configures SSH keys.

cem_linux::benchmark: 'cis'
  profile: 'server'
  level: '1'
      permit_root_login: 'yes'
          username: testuser1
          home_dir: /home/testuser1
          ssh_key: ssh-rsa A...ZcTFw== rsa-key-20201022
          username: testuser2
          home_dir: /home/testuser2
          ssh_key: ssh-rsa A...ZcTFw== rsa-key-20201022

Configuring the firewall type

The following examples configure firewall.

firewalld is default:

cem_linux::benchmark: 'cis'
  profile: 'server'
  level: '1'
  firewall_type: 'firewalld'

You can also configure iptables:

cem_linux::benchmark: 'cis'
  profile: 'server'
  level: '1'
  firewall_type: 'iptables'

You can also configure unmanaged. When you set firewall_type to unmanaged, CEM does not enforce state on any firewall resource. Use unmanaged if you do not want CEM to configure your firewalls.

cem_linux::benchmark: 'cis'
  profile: 'server'
  level: '1'
  firewall_type: 'unmanaged'

Rules that rely on specific site information

There are some CIS rules that require site specific information. You can use Bolt tasks to configure these.

Bolt tasks in Puppet Enterprise (PE)

Using PE, you can run Bolt tasks and plans to audit or configure specific parts of a node. These are available under Tasks in the PE console — search the drop down menu for cem_linux tasks.

Run Bolt tasks from the command line

You can also run Bolt tasks from the command line.

  1. Install Puppet Development Kit (PDK) and Bolt.
  2. In the root of the CEM directory, run pdk bundle exec rake 'spec_prep'. This downloads the required dependencies as RSpec fixtures, and then symlinks the module directory into the fixtures directory.
  3. Run the tasks on one or more hosts. For example: bolt task run comply_enforcement_module::audit_unowned_files_and_directories -t $nodefqdn --modulepath spec/fixtures/modules. Note that you need to add the --modulepath spec/fixtures/modules option to Bolt commands, otherwise Bolt is not able to find the tasks and plans.

Known issues

cem_linux has the following known issues:

  • nftables firewall is not supported. Use firewalld or iptables instead.
  • CEM cannot configure a bootloader password. You can set a bootloader password with the following steps:
    • Run grub2-setpassword and enter the bootloader password; or
    • If you are on an old version of grub2:
      • Run the command grub2-mkpasswd-pbkdf2 and enter the password to get an encrypted version
      • Add the following into /etc/grub.d/01_users
        • set superusers="<username>"
        • password_pbkdf2 <username> <encrypted-password>
    • Regenerate the grub configuration by running the included update_bootloader Bolt task or by manually running grub2-mkconfig -o /boot/grub2/grub.cfg on bios systems and grub2-mkconfig -o /boot/efi/EFI/(centos or redhat)/grub.cfg on UEFI systems.
  • CEM cannot set permissions on removable media partitions. To set the required permissions on these partitions, ensure nodev,nosuid,noexec exists in the options portion of /etc/fstab for the partition.
  • XD/NX support is dependent on the host kernel and CEM cannot configure it. Ensure you are using up-to-date kernels.
  • Restricting the root login to a system console requires knowledge of the site. You need to configure this control manually by removing entries in /etc/securetty for consoles that are not in secure locations.
  • CEM does not enforce authselect controls for CIS 1.0.0 5.3.x on RHEL8 or CentOS8. This requires site knowledge and can break network authentication. CIS recomends that you do not enforce this control. CEM includes a Bolt task audit_authselect to audit these controls.
  • You can only configure the ensure_nodev_option_set_on_home_partition class if the /home setting is mounted on it's own partition. Note that Puppet does not create a partition for /home.
  • If you are running on RHEL 8 and CentOS 8:
    • The ensure_nis_server_is_not_enabled class is dependent on ensure__rpc_is_not_enabled. If you enforce ensure_nis_server_is_not_enabled, you must also enforce ensure__rpc_is_not_enabled.
    • The ensure_nfs_is_not_enabled class is dependent on ensure__rpc_is_not_enabled. If you do not enforce ensure_nfs_is_not_enabled, you must also enforce ensure__rpc_is_not_enabled.
  • If you are running on RHEL 7 and CentOS 7:
    • The ensure_rpcbind_is_not_installed_or_the__rpcbind_services_are_masked class is dependant on ensure_nfsutils_is_not_installed_or_the__nfsserver_service_is_masked. If you enforce ensure_rpcbind_is_not_installed_or_the__rpcbind_services_are_masked, you must also enforce ensure_nfsutils_is_not_installed_or_the__nfsserver_service_is_masked.
  • The control disable_wireless_interfaces requires that you install the NetworkManager package and that the service is running.