Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ playbooks/test.yml
roles/test
context/
particle/.vagrant
stig/stig.db
335 changes: 335 additions & 0 deletions stig/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,335 @@
Secure Technical Implementation Guide - Audit and Remediations
===============================================================

Overview
--------

Round about 300 unofficial audit and remediation tasks for different Secure Technical Implementation Guides (STIG).

Compiled STIGs:

.. csv-table::
:header-rows: 1

Type, Name, Version, ``stig_profile_name:``, ``stig_profile_version:``, Tested Platforms
CIS, Rocky Linux 9 Benchmark, v2.0.0, ``CIS Rocky Linux 9``, ``v2.0.0``, Rocky Linux 9

`Drop us a line <https://www.linuxfabrik.ch/ueber-uns/kontakt>`_ if you have successfully used any of the STIG profiles on other platforms.

To audit before and after applying the remediations, you might use tools like `OpenVAS <https://www.openvas.org/>`_, `Lynis <https://cisofy.com/lynis/>`_, `Nessus <https://www.tenable.com>`_ or our Python script `files/audit.py <https://git.linuxfabrik.ch/linuxfabrik-ansible/roles/stig/-/blob/master/files/audit.py>`_. Remediations are done using the `tasks/main.yml <https://git.linuxfabrik.ch/linuxfabrik-ansible/roles/stig/-/blob/master/tasks/main.yml>`_.
Copy link
Copy Markdown
Member

@NavidSassan NavidSassan Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

path to the python script is wrong


.. attention::

Do not attempt to use any of the Ansible tasks without first testing them in a non-operational environment. Linuxfabrik assume no responsibility whatsoever for its use by other parties, and makes no guarantees, expressed or implied, about its quality, reliability, or any other characteristic.

.. note::

* This role cannot be run with the ``--check`` parameter.
* Password-less SSH access is required for the audits and remediations.
* The Rocky Linux 9 profile is intended for Rocky Linux 9 systems.


Before you begin
----------------

While remediating, this role breaks things on productive machines when applied completely.
This role **will make changes to the system that could or will break things**. Please take the time to familarise yourself with the STIG profile of your choice before applying this role to a system.

Compile a list of non-applicable remediations for each server.
For example: If you are running an outbound proxy with Squid and would like to apply the "CIS CentOS Linux 7 v3.1.2" profile, you should exclude "2.2.12 Ensure HTTP Proxy Server is not installed".

This role does not create or change host firewalls.
Because there are far more firewall tools on earth than just firewalld, nftables and iptables, and maybe you create your firewall rules using other techniques or roles. This topic is too complex to be configured automatically.


Installation
------------

Place this Ansible role in an appropriate directory:

.. code-block:: text

stig
├── audit.csv: Audit script definitions
├── audit.py: Python audit script
├── audits: Contains all Audit Snippets (Bash)
├── create-db: Script to create stig.db from CSV files
├── dump-db: Script to dump stig.db to CSV files
├── lib: Home of Linuxfabrik libraries
├── profile.csv: STIG profile definitions
├── remediation.csv: Remediation variable definitions
└── stig.db: SQLite database file

If you want to use ``audit.py``:

* Install Python modules:

.. code-block::

dnf -y install python3-termcolor

* Copy all Python files from https://git.linuxfabrik.ch/linuxfabrik/lib into ``stig/lib``.


Auditing a Machine (audit.py)
-----------------------------

If using our Python script `files/audit.py <https://git.linuxfabrik.ch/linuxfabrik-ansible/roles/stig/-/blob/master/files/audit.py>`_, ensure that you are able to access the machine using SSH with root privileges and password-less authentication. The script checks SSH connectivity and password-less ``sudo`` before running audits.

For example, start an audit only with controls whose name starts with "1", but at the same time exclude all controls whose name starts with "1.3" and "1.4":

.. code-block:: bash

./audit.py --profile-name="CIS Rocky Linux 9" --profile-version="v2.0.0" --hostname=192.0.2.249 --username=root --lengthy --control-name-include='^1' --control-name-exclude='^1\.3|^1\.4'

Example output (parts ommitted)::

Audit Result
============

...

Summary Table
-------------

Control ! Script ! Scoring ! Lvl ! Result
------------------------------------------------------------------------+-------------------+---------+-----+-------
1.1.1.1 Ensure mounting of cramfs filesystems is disabled (Automated) ! cramfs_off.sh ! Scored ! 1 ! Failed
1.1.1.2 Ensure mounting of squashfs filesystems is disabled (Automated) ! squashfs_off.sh ! Scored ! 2 ! Failed
1.1.1.3 Ensure mounting of udf filesystems is disabled (Automated) ! udf_off.sh ! Scored ! 1 ! Failed
1.1.2 Ensure /tmp is configured (Automated) ! tmp_separate_partition.sh ! Scored ! 1 ! Passed
1.1.3 Ensure noexec option set on /tmp partition (Automated) ! tmp_noexec.sh ! Scored ! 1 ! Passed
1.1.4 Ensure nodev option set on /tmp partition (Automated) ! tmp_nodev.sh ! Scored ! 1 ! Passed
1.1.5 Ensure nosuid option set on /tmp partition (Automated) ! tmp_nosuid.sh ! Scored ! 1 ! Passed
...

Profile
-------

* Benchmark: CIS Rocky Linux 9 (v2.0.0)
* Host: ``192.0.2.194``
* Datetime: 2021-09-28 14:22:45
* Score: 128/236 points (54.2%)
* Grade: F

For each control:

* If you get "Passed", the configuration is CIS-compliant for that control.
* If you get "Failed", the CIS requirements are not met.
* If you get "Skipped", the control is not applicable on that system.
* If you get "TODO", no audit script is implemented for that control yet.
* If you get "Review", we cannot detect compliance automatically; check the configuration manually.

The overall grade is calculated as follows:

.. code-block:: python

def get_grade(percentage):
if percentage >= 97:
return 'A+'
if percentage >= 93:
return 'A'
if percentage >= 90:
return 'A-'
if percentage >= 87:
return 'B+'
if percentage >= 83:
return 'B'
if percentage >= 80:
return 'B-'
if percentage >= 77:
return 'C+'
if percentage >= 73:
return 'C'
if percentage >= 70:
return 'C-'
if percentage >= 67:
return 'D+'
if percentage >= 63:
return 'D'
if percentage >= 60:
return 'D-'
return 'F'


Remediating a Machine
---------------------

We have implemented more audits than remediation measures, especially in the area of application servers (for example Apache). The reason: Audits are not only easier to implement, but the configuration of an existing application server is far too specialized and complex to be done by a small, general role. Better, specialized or custom Ansible roles must be used here to deploy and maintain the server.

After applying remediations:

* Reboot. Always reboot a remediated machine to be sure for all settings to take effect.
* Keep an eye on your monitoring software.
* Run a second audit.
* Fix further findings using other roles.

Variables (have a look at `defaults/main.yml <https://git.linuxfabrik.ch/linuxfabrik-ansible/roles/stig/-/blob/master/defaults/main.yml>`_ for a complete list of available variables):

.. code-block:: yml

stig:
- profile_name: 'CIS Rocky Linux 9' # mandatory
profile_version: 'v2.0.0' # default: "latest"
also_use_controls_disabled_by_default: True # default: false
control_name_include: # use regular expressions here
- '^1'
- '^2'
control_name_exclude: # use regular expressions here
- '^2\.1'
- '^2\.3'


Ansible Role Variables
----------------------

Have a look at `defaults/main.yml <https://git.linuxfabrik.ch/linuxfabrik-ansible/roles/stig/-/blob/master/defaults/main.yml>`_ for a complete list of available variables.


STIG "CIS Rocky Linux 9 Benchmark" - Details
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Mandatory:

* You have to set the ``stig__grub2_password`` variable.

Some remediations are disabled by default for various reasons - enable them only if needed:

* | Audit system file permissions (or similar)
| Reason: File permissions can be reset by the package manager or even a reboot at any time, which means that auditing tends to fail. For this reason, our audit task ignores some of the known files in ``var/log``.
* | Ensure audit logs are not automatically deleted (or similar)
| Reason: No customer likes to have his machine stopped simply because the audit partition runs out of space, and the mass of cryptic audit logs cannot be checked anyway.
* | Ensure password expiration is 365 days or less (or similar)
| Reason: May lock you and Ansible out.
* | Ensure rsyslog is configured to send logs to a remote log host (or similar)
| Reason: This is more complex in reality than the CIS mediation suggests.
* | Ensure SSH root login is disabled (or similar)
| Reason: May lock you and Ansible out.
* | Ensure updates, patches, and additional security software are installed (or similar)
| Reason: Skipping this saves quite some time during the run. Also, there are other possible update strategies.


stig.db
-------

This is a SQLite file and can be viewed and edited with *DB Browser for SQLite*, for example.

The ``profile`` table contains STIG control definitions (currently some CIS benchmarks). The meaning of some of the columns:

* ``enabled``: Specifies whether a *remediation* should be applied automatically or not. Set to "0" if this causes problems or is unnecessary.

The ``audit`` table maps controls to audit scripts (Bash). The meaning of some of the columns:

* ``exec_order``: Execution order within the specific STIG profile.
* ``audit_name``: Filename of the audit script in the ``audits/`` directory.

The ``remediation`` table maps controls to Ansible remediation variables.

Use NULL to unset any value.

To get a complete list of disabled remediations, execute this SQL statement on ``stig.db``:

.. code-block:: text

SELECT *
FROM profile
WHERE
enabled = 0

Some audits and remediations in some STIG profiles might not be implemented for various reasons. As an example, to get a list, execute this SQL statement on ``stig.db``:

.. code-block:: text

SELECT p.*
FROM profile p
LEFT JOIN audit a ON p.id = a.id
WHERE
p.profile_name = "CIS Rocky Linux 9"
AND a.id IS NULL


Naming Scheme for Controls
--------------------------

From a remediation action point of view: ``<package or device>[-<section or detail>][-<section or detail>]-<action>``

* package or device: for example "httpd" or "tmp"
* section: for example "vhosts"
* action: a remediation action that should be done. One of

* get: fetch some information - for audit tasks that will never have a remediation counterpart
* compare: compare two or more items - for audit tasks that will never have a remediation counterpart

* off: disabling or configuring something to "off"
* on: enabling or configuring something to "on"
* disable: disabling a service
* enable: enabling a service

* install: install a package
* update: update a package or packages
* remove: uninstalling a package, deleting files and directories

* chmod: changing permissions using chmod
* chown: changing owner using chown

* cron: configuring cronjobs
* timer: configuring systemd timer
* select: set something from a list of choices
* configure: more or less complex configuration tasks
* setup: both installation and configuration


Examples
--------

Auditing a Rocky Linux 9 VM, excluding some controls:

.. code-block:: bash

./audit.py --lengthy --profile-name='CIS Rocky Linux 9' --profile-version='v2.0.0' --hostname=192.0.2.194 --control-name-exclude='^1\.9|^3\.4\.|^4\.1\.2\.2|^4\.2\.1\.5|^5\.2\.10|^5\.3\.1|^5\.3\.2|^5\.3\.3|^5\.4\.2|^5\.5\.1\.1'

Apply the remedies of "CIS Rocky Linux 9" (use this as a starting point):

.. code-block:: text

# hosts.yml
cis_hosts:
vars:
ansible_become: True
hosts:
192.0.2.194:

.. code-block:: text

# host_vars/192.0.2.194.yml
stig__crypto_policy: 'FIPS'
stig__grub2_password: 'BlueLake23'

.. code-block:: text

# group_vars/cis_hosts.yml
stig:
- profile_name: 'CIS Rocky Linux 9'
profile_version: 'v2.0.0' # or "latest"
also_use_controls_disabled_by_default: False
control_name_include: # use regular expressions here
- '^1'
- '^2'
- '^3'
- '^4'
- '^5'
- '^6'
control_name_exclude:
- '^1\.9'

.. code-block:: text

# playbook.yml
- hosts:
- 'cis_hosts'
roles:
- role: 'stig'

.. code-block:: bash

ansible-playbook --inventory=path/to/hosts.yml path/to/playbook.yml --extra-vars="ansible_ssh_user=root"
Loading