Forge Home
Premium module


Compliance Enforcement Module for Linux


10 latest version

Version information

  • 1.3.0 (latest)
  • 1.2.0
  • 1.1.4
  • 1.1.3
  • 1.1.2
  • 1.1.1
  • 1.1.0
  • 1.0.0
released Aug 3rd 2022
This version is compatible with:
  • Puppet Enterprise 2021.6.x, 2021.5.x, 2021.4.x, 2021.3.x, 2021.2.x, 2021.1.x, 2021.0.x, 2019.8.x
  • Puppet >= 6.23.0 < 8.0.0
  • ,
  • audit_authselect
  • audit_check_ipv6
  • audit_duplicate_gid
  • audit_duplicate_group_names
  • audit_duplicate_uid
  • audit_duplicate_user_names
  • audit_etcpasswd_groups
  • and 20 more. See all tasks


puppetlabs/cem_linux — version 1.3.0 Aug 3rd 2022


Table of contents

Introducing the Compliance Enforcement Modules

The cem_linux module is one of two Compliance Enforcement Modules (CEM). These supported Puppet modules were developed specifically to bring your Puppet Enterprise (PE) managed nodes into compliance. CEM currently supports Center for Internet Security (CIS) compliance rules.

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

This readme file provides instructions for installing CEM and customizing the configuration settings to meet your organization’s compliance requirements. For a list of available parameters, see the CEM reference.

After you have installed and configured CEM, PE will run on any classified nodes without user intervention to scan for compliance.

To manage Microsoft Windows nodes, navigate to cem_windows.


Before you install CEM, review the System requirements to ensure that CEM can be run on the operating systems in your environment. Then, contact a Puppet sales representative to purchase CEM.

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, 2 Server
Red Hat Enterprise Linux 8 CIS Benchmarks v2.0.0 1, 2 Server
CentOS Linux 7 CIS Benchmarks v3.1.2 1, 2 Server

Install CEM with Code Manager

You can install CEM by using Code Manager, a code management tool. For installation instructions, see Puppet Forge Premium Content.

Upgrade CEM

To upgrade CEM, update the CEM declaration in the Puppetfile. Specify the version number to which you are upgrading CEM.

For instructions about modifying the Puppetfile, see Declare Forge modules in the Puppetfile.

For example, to upgrade the cem_linux module to version 1.3.0, you would specify the CEM declaration as shown:

mod 'puppetlabs/cem_linux', '1.3.0'


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 default values can leave your nodes in an undesirable state. In these situations, you can customize how CEM enforces compliance to meet your organization's requirements.

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

Find and set configuration options

Configuration options include top-level configuration options, benchmark configuration options, and CIS-specific configuration options.

You can find the configuration options for a specific control in the CEM Linux Reference. The reference is divided into sections, with each section representing a benchmark. In those benchmarks, you will see each control listed with several subsections:

  • Parameters:
    • Configuration options for a control, along with the data type and default value.
  • Config Example:
    • Snippet of Hiera that can be used to configure a control.
  • Supported Levels:
    • The supported levels for a CIS control.
  • Supported Profiles:
    • The supported profiles for a CIS control.
  • Alternate Config IDs:
    • The alternate config IDs for a control. Any of these config IDs, along with the full control name, can be used as a key in the control_config hash.
  • Resource:
    • The name of the Puppet resource that enforces the control.

Alternate config IDs

You can specify controls in the control_config hash by referencing the full control name, the control number, the normalized control name, or the normalized control number. You cannot mix and match these forms and must pick a single control ID form to use for your config. Full control names and control numbers are copied verbatim from the benchmarks and are case-sensitive. Normalized control names have lowercase letters and contain only alphanumeric characters and underscores. Normalized control numbers are always prefixed with a c and contain only numeric characters separated by underscores.

Example of alternate config IDs:

  • Full control name: (L1) Ensure 'Enforce password history' is set to '24 or more password(s)'
  • Control number: 1.1.1
  • Normalized control name: ensure_enforce_password_history_is_set_to_24_or_more_passwords
  • Normalized number: c1_1_1

Resource data

The data that drives CEM Linux is located in directories and files with the following structure:


These Hiera files contain definitions for each Puppet resource that enforces a control.

Caution: Do not modify the resource definitions that drive CEM Linux. If you must change the behavior of a control, configure the control by using the control_config hash.

Top-level configuration options

These configuration options are set at the top level of the module. In Hiera, these options are prefixed with cem_linux:

  • benchmark - Enum['cis'] - the compliance framework to use. CEM supports only cis. Default: cis.
  • config - Optional[Hash] - the location for all non-top-level configuration options. Default: undef.
  • allow_on_kubernetes_node - Boolean - If cem_linux detects that it is running on a Kubernetes cluster node or host, CEM does not enforce controls and logs a warning to inform the user. In this way, CEM helps to prevent the accidental enforcement of incorrect compliance settings that can render Kubernetes non-functional. Default: false.
  • regenerate_grub2_config - Boolean - Some configurations in CEM for Linux modify the Grub2 bootloader configuration. To regenerate the Grub2 configuration after applying a change, set this parameter to true. If you do not set this to true, you must manually regenerate the Grub2 configuration. Default: false.
  • set_grub2_password - Boolean - Set the password for the Grub2 bootloader. If you set this to true, you must also set the grub2_superuser and grub2_superuser_password parameters, or configure the specific bootloader password control by using the control_configs option. Default: false.
  • grub2_superuser - Optional[String[1]] - The superuser for the Grub2 bootloader if you set set_grub2_password to true. Default: Undef.
  • grub2_superuser_password - Optional[Sensitive[String]] - The superuser password for the Grub2 bootloader if you set set_grub2_password to true. This value is sensitive in terms of security, and should be stored in a Sensitive data type. Default: Undef.

Hiera example

The following example configures CEM for Linux to regenerate the Grub2 bootloader config on a node using the CIS benchmark:

cem_linux::benchmark: 'cis'
cem_linux::allow_on_kubernetes_node: false
cem_linux::regenerate_grub2_config: true

Benchmark configuration options

The benchmark configuration 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-specific configuration options

The CIS-specific configuration options are available as key-value pairs within the cem_linux::config: hash.

  • profile: - Optional[Enum['server', 'workstation']] — the name of the benchmark profile. The only value supported by CEM is server. Default: server.
  • level: - Optional[Enum['1', '2']] — the name of the profile level. The only value supported by CEM is 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.
  • enable_systemd_journal - Optional[Boolean] - Whether to enable systemd-journal. The default value is false. If this option is enabled, several configuration parameters are required.
    cem_linux::utils::services::systemd::journal::url: 'ip address or DNS resolvable hostname to receiving host'
    cem_linux::utils::services::systemd::journal::serverkeyfile: 'ServerKey file location'
    cem_linux::utils::services::systemd::journal::servercertificatefile: 'Server Certificate file location'
    cem_linux::utils::services::systemd::journal::trustedcertificatefile: 'Trusted Certificate file location'

Red Hat Enterprise Linux 8-specific CIS configuration options

If you installed CEM on a Red Hat Enterprise Linux 8 operating system, the authselect option is available, but the option should be avoided in almost all cases. The authselect option disrupts authentication and requires extensive configuration.

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 parameter determines whether the included code in the manifest is executed.
  • The $config parameter — You can set this parameter to maintain 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 (without a path and the .pp suffix) and the value of that key is a hash. The keys of the sub-hash are identical to the configuration options that are 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 following configuration examples.

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 repository, 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. To determine which task to run, review the output of the Puppet debug logs.

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 that is specified in the debug log.

The AIDE configuration file now reflects the changes in Hiera.

Enforce bootloader configurations

In rare cases, it might be useful to enable automatic regeneration of the bootloader configuration, and you might want to set a bootloader password.

Caution: The only bootloader supported by CEM for Linux is grub2.

CEM for Linux enforces various bootloader configurations as required by the selected compliance framework and benchmark. However, because changes to bootloader configurations can be potentially dangerous, a minimalistic approach to configuration changes is used by CEM for Linux.

For CIS, there are several recommendations that modify the bootloader config. If you run CEM for Linux with the full range of default settings, these changes will be applied, but the bootloader config will not be regenerated. While changes are pending on the node, bootloader operations remain the same until the configurations are regenerated. The exception to this is the bootloader password, which is not set by default. The following examples show how you can configure CEM for Linux to automatically regenerate the bootloader config and how you can set a bootloader password.

Regenerate bootloader configs automatically

To regenerate bootloader configurations automatically, locate the following file, where <node name> specifies the name of the affected node.

Edit the file to specify the following setting:

# control-repo/data/nodes/<node name>.yaml
cem_linux::regenerate_grub2_config: true
Set a bootloader password

You can set a bootloader password as shown in the following example:

# control-repo/data/nodes/<node name>.yaml
cem_linux::regenerate_grub2_config: true
cem_linux::set_grub2_password: true
cem_linux::grub2_superuser: 'root'
cem_linux::grub2_superuser_password: 'password'
    convert_to: 'Sensitive'

Restriction: The cem_linux::grub2_superuser_password key must be of type Sensitive[String]. Setting a lookup option for that key to convert it to Sensitive is the best way to ensure that the value is a Sensitive[String].

Caution: Do not store plain-text passwords in Hiera. Using something like hiera-eyaml is a better way to store secrets.

Guidelines for enabling the authselect option

The authselect option is disabled by default because enablement of authselect can disrupt authentication methods, and use of the option requires extensive configuration.

Caution: If a node is joined to an Active Directory domain or to Red Hat Identity Management (idM), do not enable the authselect option. Enabling authselect on these nodes will break your authentication configurations.


  • The authselect option is supported only on Red Hat Enterprise Linux 8.
  • You cannot enable the authselect option if you are using pluggable authentication modules (PAM) for application management.

By default, cem_linux uses standard PAM rules to configure the authentication controls specified by CIS. However, if you are enforcing CIS compliance on Red Hat Enterprise Linux 8, CIS guidelines call for authselect to be used. The following example configuration shows how to enable authselect on a node by using the minimal system default profile:

# control-repo/data/nodes/<node name>.yaml
  use_authselect: true
  authselect_profile: 'minimal'

To enable the authselect option:

  1. Set the config option use_authselect to true.
  2. Specify an authselect profile with the config option authselect_profile.

Both of the options must be set directly in the cem_linux::config hash for the authselect option to work properly.

Custom authselect profiles

If you are enforcing CIS compliance on a Red Hat Enterprise Linux 8 system and you want to enable additional features for your authselect profile, you can create a custom profile.

To create and use a custom authselect profile in cem_linux, prefix the profile name in authselect_profile with custom/. If the custom profile does not exist on the node, the profile will be created automatically. The following example shows how to create and use a custom profile, my_custom_profile, which is based on the system profile minimal with additional features enabled:

# control-repo/data/nodes/<node name>.yaml
  use_authselect: true
  authselect_profile: 'custom/my_custom_profile'
      custom_profile_base: 'minimal'
        - with-faillock
        - with-mkhomedir
Configure authselect

All authselect configurations are managed via the control class ensure_custom_authselect_profile_is_used, regardless of whether you use a custom profile. See the reference for all configuration options.

Configure custom logrotate rules

To help ensure that logs are pruned on a regular basis to conserve system space, you can specify 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'

Configure sudo without a password

You can give users and user groups the ability to run some or all commands as root 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:'

Configure user SSH keys

To use the Secure Shell (SSH) protocol for communication between computers, you must configure SSH keys. You can also configure SSH keys for individual users. In the following example, keys are configured for testuser1 and testuser2:

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

Configure SSH permissions for users and groups

You can configure SSH at a granular level to specify users and groups that are granted or denied permissions. The following example configures SSH to grant permissions to some users and groups and deny permissions to other users and groups:

cem_linux::benchmark: 'cis'
        - testuser1
        - the_dude
        - testgroup1
        - goonies
        - testuser2
        - the_emperor
        - testgroup2
        - legion_of_doom

Configure the firewall type

The following examples configure the firewall.

firewalld is the default setting:

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

You can also specify a value of iptables:

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

You can also specify a value of unmanaged. When you set the firewall_type parameter to unmanaged, CEM does not enforce a 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 site-specific information

Some CIS rules require information that is specific to a customer site. You can use Bolt tasks to configure these rules.

Bolt tasks in Puppet Enterprise (PE)

Using PE, you can run Bolt tasks and plans to audit or configure specific parts of a node. To run Bolt tasks, open the PE console and select the Tasks menu. Then, select cem_linux.

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 the pdk bundle exec rake 'spec_prep' command. This command downloads the required dependencies as RSpec fixtures, and then creates a symbolic link from the module directory to 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. You must add the --modulepath spec/fixtures/modules option to Bolt commands. Otherwise, Bolt is not able to find the tasks and plans.

Known issues

The current release includes known issues and restrictions. In most cases, workarounds are provided.

Comply scan issues

During a Comply scan, you might see errors about CIS recommended guidelines that are not enforced. These error messages are triggered by bugs in the CIS-CAT Pro Assessor that is bundled with Comply. CEM does correctly enforce these settings.

  • Red Hat Enterprise Linux Benchmark v2.0.0:
    • 1.4.2 - Ensure permissions on bootloader are configured
      • On EFI systems, the script that was run by the CIS-CAT Pro Assessor did not locate the correct grub file path. Permissions are set correctly by CEM.
    • 1.4.1 - Ensure bootloader password is set
      • On EFI systems, the script that was run by the CIS-CAT Pro Assessor did not locate the correct grub file path. The bootloader password can be set by CEM.
    • Ensure system is disabled when audit logs are full
      • This is set to halt by CEM. The CIS-CAT Pro Assessor shows this incorrectly as a scan failure.
    • 5.2.18 Ensure SSH MaxSessions is set to 10 or less
      • This is set to 10 by default. The CIS-CAT Pro Assessor shows this incorrectly as a scan failure. The scanner is incorrectly looking for <=4 instead of <=10.

General issues and limitations

  • You cannot use the iolog_dir option to specify a directory for sudo log files. If you attempt to use the iolog_dir option in the sudoers file to specify a log directory other than the default, errors are reported by the Augeas program. Augeas is a tool used for configuration editing in CEM.
  • Firewalls that are based on the nftables framework are not supported. Use the firewalld or iptables setting instead.
  • CEM cannot create file system partitions. This limitation can cause certain scanner checks to fail.
  • CEM cannot set permissions on removable media partitions. To set the required permissions on these partitions, ensure that 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 customer site. You must 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 2.0.0 5.4.x on Red Hat Enterprise Linux 8. Enforcement requires site knowledge and can break network authentication. CIS recommends that you do not enforce this control. CEM includes a Bolt task, audit_authselect, to audit these controls.
  • You can configure the ensure_nodev_option_set_on_home_partition class only if the /home setting is mounted on its own partition. Puppet does not create a partition for /home.
  • If your system is running on Red Hat Enterprise Linux 8:
    • The ensure_nis_server_is_not_installed class is dependent on ensure_rpcbind_is_not_installed_or_the__rpcbind_services_are_masked. If you enforce ensure_nis_server_is_not_installed, you must also enforce ensure_rpcbind_is_not_installed_or_the__rpcbind_services_are_masked.
    • The ensure_nfs_utils_is_not_installed_or_the__nfs_server_service_is_masked class is dependent on ensure_rpcbind_is_not_installed_or_the__rpcbind_services_are_masked. If you do not enforce ensure_nfs_utils_is_not_installed_or_the__nfs_server_service_is_masked, you must also not enforce ensure_nfs_utils_is_not_installed_or_the__nfs_server_service_is_masked.
    • The ensure_the_running_and_on_disk_configuration_is_the_same control is always enforced if auditd is managed by CEM.
    • The ensure_inactive_password_lock_is_30_days_or_less control is cover by another control ensure_password_expiration_is_365_days_or_less.
  • The ensure_users_must_provide_password_for_escalation class is disabled by default. It is possible removing NOPASSWD: from sudoers files could invalidate those file's syntax and break system authentication. To enable this control set top level config enable_nopasswd_sudo_prune to true.
  • If your system is running on Red Hat Enterprise Linux 7 or CentOS 7:
    • The ensure_rpcbind_is_not_installed_or_the__rpcbind_services_are_masked class is dependent 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 disable_wireless_interfaces control requires that you install the NetworkManager package and that the service is running.