STIG is an acronym for Security Technical Implementation Guide, which is a cyber security protocol that sets the standards for the security of networks, computers, servers, etc. In this 16th article in the DevOps series, we will learn how to build Ansible playbooks to test and set up CentOS 6 as per STIG on RHEL6, version 1, release 19.
The Security Technical Implementation Guide (STIG) has been developed jointly by Red Hat, the National Security Agency (NSA) and the Defence Information Systems Agency (DISA) for the US Department of Defense (DoD). The security vulnerabilities are classified into three Category Codes (CAT for short), based on the severity.
CAT I type is an exploit that “…directly and immediately results in loss of confidentiality, availability or integrity.”
CAT II type vulnerablity “…has a potential to result in the loss of confidentiality, availability or integrity.”
The existence of a CAT III type vulnerability “…degrades measures to protect against loss of confidentiality, availability or integrity.”
On October 16, 2009, the chief information officer of the Department of Defense (USA) released a memorandum with guidance on using free and open source software (FOSS). The memo can be obtained from http://dodcio.defense.gov/Portals/0/Documents/FOSS/2009OSS.pdf.
Setting things up
A CentOS 6.8 virtual machine (VM) running on KVM is used for the setup. Please ensure that the VM has access to the Internet. The Ansible version used on the host (Parabola GNU/Linux-libre x86_64) is 2.5.0.
$ ansible --version ansible 2.5.0 config file = /etc/ansible/ansible.cfg configured module search path = [u’/home/guest/.ansible/plugins/modules’, u’/usr/share/ansible/plugins/modules’] ansible python module location = /usr/lib/python2.7/site-packages/ansible executable location = /usr/bin/ansible python version = 2.7.14 (default, Jan 5 2018, 10:41:29) [GCC 7.2.1 20171224]
The ansible/ folder contains the following files:
ansible/inventory/kvm/inventory ansible/playbooks/configuration/stig.yml ansible/playbooks/configuration/fix-stig.yml
The IP address of the guest CentOS 6.8 VM is added to the inventory file as shown below:
centos ansible_host=192.168.122.16 ansible_connection=ssh ansible_user=root ansible_password=password
Also, add an entry for the centos guest in /etc/hosts file as indicated below:
192.168.122.16 centos
The libselinux-python package needs to be installed on the CentOS guest VM as follows, in order to verify SELinux configuration using Ansible:
# yum update && yum install libselinux-python
We shall now go through a few CAT vulnerabilities and look at how to use Ansible to protect against them.
RHEL-06-000005
The rule version (STIG-ID) RHEL-06-000005 states that the audit system must send an email to the designated staff members when the storage volume reaches its capacity. The ‘space_left_action’ variable in /etc/audit/auditd.conf should be set to an email address of an administrator. The following Ansible code snippet checks the same, and asserts if an ‘@’ symbol for the email address exists.
--- - name: STIG hosts: centos become: true gather_facts: true tags: [STIG] tasks: - name: “RHEL-06-000005 | CAT II | The audit system must alert designated staff members” shell: “grep ^space_left_action /etc/audit/auditd.conf” register: space_left_action_result - assert: that: - “’@’ in space_left_action_result.stdout”
RHEL-06-000008
This rule states that the Red Hat GPG keys need to be installed in order to cryptographically verify that the packages are actually from Red Hat. The ‘gpg-pubkey’ software package should be installed in the system.
- name: “RHEL-06-000008 | CAT I | Vendor-provided cryptographic certificates must be installed” shell: “rpm -q gpg-pubkey” register: gpg_pubkey_result - assert: that: - “gpg_pubkey_result.stdout != ‘’”
RHEL-06-000011
The system should always be up to date with the latest software. This can be checked from the output of ‘yum check-update’. The remedy is to run ‘yum update’ to pull the latest software packages from the repositories, as follows:
- name: “RHEL-06-000011 | CAT II | System security patches and updates must be installed” shell: “yum check-update” register: yum_check_update_result - assert: that: - “yum_check_update_result.rc == 0”
RHEL-06-000013
This rule states that the downloaded packages need to be cryptographically verified before proceeding with the installation. The ‘gpgcheck=1’ verification should exist in the /etc/yum.conf file.
- name: “RHEL-06-000013 | CAT II | System package management tool must be cryptographically verified” shell: “grep gpgcheck /etc/yum.conf” register: gpgcheck_result - assert: that: - “’=1’ in gpgcheck_result.stdout”
RHEL-06-000016
The system should have a file integrity tool installed. Advanced Intrusion Detection Environment (AIDE) is a file and directory integrity checker, released under the GPL. It can be installed on CentOS, as follows:
- name: “RHEL-06-000016 | CAT II | A file integrity tool must be installed” shell: “rpm -q aide” register: aide_result - assert: that: - “aide_result.stdout != ‘’”
RHEL-06-000018
The AIDE file integrity tool must be initialised before using it. An initial database needs to be created for its use. The database consists of regular expression rules obtained from the configuration files. A number of digest algorithms such as md5, sha1, sha256, sha512, crc32, etc, are used to check the integrity of files.
- name: “RHEL-06-000018 | CAT II | A file integrity baseline must be created” stat: path: /var/lib/aide/aide.db.gz register: aide_db_result - assert: that: - “aide_db_result.stat.exists == true”
RHEL-06-000019
The .rhosts and /etc/hosts.equiv files used by the rsh daemon can allow unauthorised access to the system. Hence, these should not exist in the file system. The Ansible Stat module is used to check the presence of these files.
- name: “RHEL-06-000019 | CAT I | There must be no /etc/hosts.equiv files” stat: path: /etc/hosts.equiv register: etc_hosts_result - assert: that: - “etc_hosts_result.stat.exists == false” - name: “RHEL-06-000019 | CAT I | There must be no .rhosts file” stat: path: ~/.rhosts register: rhosts_result - assert: that: - “rhosts_result.stat.exists == false”
RHEL-06-000020
The SELinux policy needs to be set to ‘Enforcing’ in the /etc/selinux/config file. This setting needs to take effect from the boot time, and it enforces limits on system services.
- name: “RHEL-06-000020 | CAT II | The system must use a Linux Security Module to enforce limits” shell: “grep ^SELINUX= /etc/selinux/config” register: selinux_result - assert: that: - “’enforcing’ in selinux_result.stdout”
RHEL-06-000023
The SELinux targeted policy ensures that processes that are targeted run in a confined domain, and those that are not, run in an unconfined domain. The SELINUXTYPE variable needs to be set to ‘targeted’ in /etc/selinux/config for this setting to take effect.
- name: “RHEL-06-000023 | CAT III | The system must use a Linux Security Module to limit the privileges” shell: “grep ^SELINUXTYPE= /etc/selinux/config” register: selinux_type_result - assert: that: - “’targeted’ in selinux_type_result.stdout”
RHEL-06-000032
We need to ensure that there is only one root user in the system. The /etc/passwd file can be checked to see that there is only one entry with UID 0.
- name: “RHEL-06-000032 | CAT II | The root account must be the only account having UID 0” shell: “awk -F: ‘($3 == 0) {print}’ /etc/passwd” register: root_account_result - assert: that: - “root_account_result.stdout_lines | length == 1”
The entire playbook invocation and run to check the above STIG-IDs is shown below:
$ ansible-playbook -i inventory/kvm/inventory playbooks/configuration/stig.yml PLAY [STIG]************************************************* TASK [Gathering Facts] ************************************** ok: [centos] TASK [RHEL-06-000005 | CAT II | The audit system must alert designated staff members] *********************************** changed: [centos] TASK [assert] ***********************************************ok: [centos] => { “changed”: false, “msg”: “All assertions passed” } TASK [RHEL-06-000008 | CAT I | Vendor-provided cryptographic certificates must be installed]****************************** changed: [centos] TASK [assert] ***********************************************ok: [centos] => { “changed”: false, “msg”: “All assertions passed” } TASK [RHEL-06-000011 | CAT II | System security patches and updates must be installed] ********************************** changed: [centos] TASK [assert] ***********************************************ok: [centos] => { “changed”: false, “msg”: “All assertions passed” } TASK [RHEL-06-000013 | CAT II | System package management tool must be cryptographically verified] ********************* changed: [centos] TASK [assert] *********************************************** ok: [centos] => { “changed”: false, “msg”: “All assertions passed” } TASK [RHEL-06-000016 | CAT II | A file integrity tool must be installed] ************************************************* changed: [centos] TASK [assert] *********************************************** ok: [centos] => { “changed”: false, “msg”: “All assertions passed” } TASK [RHEL-06-000018 | CAT II | A file integrity baseline must be created] ************************************************ok: [centos] TASK [assert]************************************************ok: [centos] => { “changed”: false, “msg”: “All assertions passed” } TASK [RHEL-06-000019 | CAT I | There must be no /etc/hosts.equiv files] ************************************************ok: [centos] TASK [assert] *********************************************** ok: [centos] => { “changed”: false, “msg”: “All assertions passed” } TASK [RHEL-06-000019 | CAT I | There must be no .rhosts file] ************************************************************ ok: [centos] TASK [assert] ***********************************************ok: [centos] => { “changed”: false, “msg”: “All assertions passed” } TASK [RHEL-06-000020 | CAT II | The system must use a Linux Security Module to enforce limits] ************************** changed: [centos] TASK [assert] *********************************************** ok: [centos] => { “changed”: false, “msg”: “All assertions passed” } TASK [RHEL-06-000023 | CAT III | The system must use a Linux Security Module to limit the privileges] ******************** changed: [centos] TASK [assert] ***********************************************ok: [centos] => { “changed”: false, “msg”: “All assertions passed” } TASK [RHEL-06-000032 | CAT II | The root account must be the only account having UID 0] ********************************** changed: [centos] TASK [assert] *********************************************** ok: [centos] => { “changed”: false, “msg”: “All assertions passed” } PLAY RECAP ************************************************* centos : ok=23 changed=8 unreachable=0 failed=0
Fixing STIG
We will also need to create a fix-stig.yml Ansible playbook, to set up the system to meet the required STIG specification. The following playbook provides the necessary steps to set up the system for the above mentioned STIG-IDs.
# ansible-playbook -i inventory/kvm/inventory playbooks/configuration/fix-stig.yml -vv --- - name: STIG hosts: centos become: true gather_facts: true tags: [FIX] tasks: - name: “RHEL-06-000011 | CAT II | System security patches and updates must be installed” # Also RHEL-06-000008 | CAT I | Vendor-provided cryptographic certificates must be installed yum: name: ‘*’ state: latest - name: “RHEL-06-000005 | CAT II | The audit system must alert designated staff members” lineinfile: dest: “/etc/audit/auditd.conf” regexp: ‘^space_left_action’ line: ‘space_left_action = author@shakthimaan.com’ - name: “RHEL-06-000016 | CAT II | A file integrity tool must be installed” yum: name: ‘aide’ state: latest - name: “RHEL-06-000018 | CAT II | A file integrity baseline must be created” shell: “aide --init && cp /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz” - name: “RHEL-06-000019 | CAT I | There must be no /etc/hosts.equiv or .rhosts file” file: dest: ‘{{ item }}’ state: absent with_items: - { “/etc/hosts.equiv” } - { “~/.rhosts” } - name: “RHEL-06-000020 | CAT II | The system must use a Linux Security Module to enforce limits” lineinfile: dest: “/etc/selinux/config” regexp: ‘^SELINUX=’ line: ‘SELINUX=enforcing’ - name: “RHEL-06-000023 | CAT III | The system must use a Linux Security Module to limit the privileges” lineinfile: dest: “/etc/selinux/config” regexp: ‘^SELINUXTYPE=’ line: ‘SELINUXTYPE=targeted’
You can obtain the entire STIG specification and documents from https://iase.disa.mil/stigs/os/unix-linux/Pages/index.aspx.