Generating the remediation Ansible playbook

Before we continue, I must first draw your attention to the fact the report gives the following warning:

Do not attempt to implement any of the settings in this guide without first testing them in a non-operational environment. The creators of this guidance 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.

While we are only targeting a test host here, if you like what you see and decide to look at implementing OpenSCAP against other workloads, please ensure that you take it slowly and test thoroughly before running against anything that is in use, even if it is just by developers—the changes made during the remediation we are about to carry out could have serious consequences on the running of your target host.

Now that we have that warning out of the way, we can continue to look at securing our host using an automatically generated Ansible playbook:

$ ansible-galaxy init roles/fix-ansible

With this role, we need a few defaults that define whereabouts our generated playbook will be sorted, and again we need to define the command that needs to run. These values can be found in roles/fix-ansible/defaults/main.yml.

The first block deals with where the files we are going to be generating are stored on both the target host and locally:

playbook_file:
remote: "/tmp/{{ inventory_hostname }}_ansible.yml"
local: "generated/{{ inventory_hostname }}_ansible.yml"
log: "generated/{{ inventory_hostname }}_ansible.log"

Next up, we have the command that needs to be executed to generate the playbook file:

ansible_fix_command: >
oscap xccdf generate fix
--profile {{ oscap.profile }}
--template urn:xccdf:fix:script:ansible
--output {{ playbook_file.remote }}
{{ report.results }}

Then, we have the locations of some folders and files that need to be in place before the playbook runs; otherwise, it will result in an error and a fail:

missing_folders:
- "/etc/dconf/db/local.d/locks/"

missing_files:
- "/etc/dconf/db/local.d/locks/00-security-settings-lock"
- "/etc/sysconfig/prelink"

Now that we have the default variables in place, we can start adding tasks to roles/fix-ansible/tasks/main.yml, starting with one that uses the file module to put the missing folders and files in place:

- name: fix missing folders
file:
path: "{{ item }}"
state: "directory"
with_items: "{{ missing_folders }}"

- name: fix missing files
file:
path: "{{ item }}"
state: "touch"
with_items: "{{ missing_files }}"

Next, we are going to add a check to see whether the playbook file already exists on the target machine:

- name: do we already have the playbook?
stat:
path: "{{ playbook_file.remote }}"
register: playbook_check

We are doing this so that we have a way of skipping running the playbook that has been generated. Next up, we run the command to generate the playbook:

- name: generate the ansible playbook with the fixes
command: "{{ ansible_fix_command }}"
args:
creates: "{{ playbook_file.remote }}"
ignore_errors: yes

As you can see, we are passing arguments that tell Ansible the command that creates the playbook file; if the file is there, then the command will not execute again. Now that we have the playbook on the machine, we need to copy it to our Ansible controller. Here, we are using the fetch module again:

- name: download the ansible playbook
fetch:
src: "{{ playbook_file.remote }}"
dest: "{{ playbook_file.local }}"
flat: yes
when: playbook_check.stat.exists == False

As you can see, we are using when so that the task only runs if the playbook file did not exist at the start of the role being run. Now that we have a copy of the playbook locally, we can run it. To do that, we are going be using the local_action module in combination with the command module to run Ansible within Ansible:

- name: run the ansible playbook locally
local_action:
module: "command ansible-playbook -i production --become --become-method sudo {{ playbook_file.local }}"
become: no
register: playbook_run
when: playbook_check.stat.exists == False

There are a few different things happening here, so let's break it down a little more, starting with the command we are running, which translates to:

$ ansible-playbook -i production --become --become-method sudo generated/box1_ansible.yml

As you can see, we are having to pass the instructions for using become with the method of sudo as part of the command. This is because the Ansible playbook that is being generated does not account for you connecting externally using a user other than root.

The final task in this role writes the results of the previous task to a file on our Ansible controller:

- name: write the results to a log file
local_action:
module: "copy content={{ playbook_run.stdout }} dest={{ playbook_file.log }}"
become: no
when: playbook_check.stat.exists == False

That completes the role. We can run the playbook again to apply the fixes and remediation, then run another scan so that we can update the site.yml file so it reads:

---

- hosts: scap
gather_facts: true
become: yes
become_method: sudo

vars_files:
- group_vars/common.yml

roles:
- { role: install, tags: [ "scan" ] }
- { role: scan, tags: [ "scan" ], report_name: "01-initial-scan" }
- { role: fix-ansible, report_name: "01-initial-scan" }
- { role: scan, report_name: "02-post-ansible-fix" }

As you can see, we have removed the tags for the fix-ansible role and we have also updated the report name for the second scan. We can start the playbook by running this:

$ ansible-playbook -i production site.yml

This gives us the following output:

PLAY [scap] *************************************************************************************

TASK [Gathering Facts] **************************************************************************
ok: [box1]

TASK [install : update all of the installed packages] *******************************************
ok: [box1]

TASK [install : install the packages needed] ****************************************************
ok: [box1] => (item=openscap-scanner)
ok: [box1] => (item=scap-security-guide)

TASK [scan : run the openscap scan] *************************************************************
ok: [box1]

TASK [scan : download the html report] **********************************************************
ok: [box1]

TASK [fix-ansible : fix missing folders] ********************************************************
changed: [box1] => (item=/etc/dconf/db/local.d/locks/)

TASK [fix-ansible : fix missing files] **********************************************************
changed: [box1] => (item=/etc/dconf/db/local.d/locks/00-security-settings-lock)
changed: [box1] => (item=/etc/sysconfig/prelink)

TASK [fix-ansible : do we already have the playbook?] *******************************************
ok: [box1]

TASK [fix-ansible : generate the ansible playbook with the fixes] *******************************
changed: [box1]

TASK [fix-ansible : download the ansible playbook] **********************************************
changed: [box1]

TASK [fix-ansible : run the ansible playbook locally] *******************************************
changed: [box1 -> localhost]

TASK [fix-ansible : write the results to a log file] ********************************************
changed: [box1 -> localhost]

TASK [scan : run the openscap scan] *************************************************************
fatal: [box1]: FAILED! =>
...ignoring

TASK [scan : download the html report] **********************************************************
changed: [box1]

PLAY RECAP **************************************************************************************
box1 : ok=14 changed=8 unreachable=0 failed=0

Let's take a look at the report and see what difference running the Ansible playbook has made:

$ open generated/box1_report_02-post-ansible-fix.html

The output can be seen as follows:

That's a little better than before; however, we are still failing 25 rules—why is that? Well, as already mentioned, work is still ongoing with porting all of the remediation rules over to Ansible; for example, if you open up the original scan results and scroll to the bottom, you should see that the Set SSH Idle Timeout Interval check failed.

Clicking on it will show you information on what OpenSCAP is checking, why they are checking it, and also why it should be fixed. And finally, at the bottom, you will notice that there are options to show both the shell and Ansible remediation solutions:

Now, click on one of the remaining failures from the second report. You should notice that there is only an option for remediation using a shell script. We will be generating this in the next role, but before we move on, let's quickly look at the playbook that was generated.

The playbook I generated at the time of writing contained over 3,200 lines of code, so I am not going to cover them all here, but as we have already mentioned the Set SSH Idle Timeout Interval check, let's take a look at the task in the playbook that applies the fix:

    - name: Set SSH Idle Timeout Interval
lineinfile:
create: yes
dest: /etc/ssh/sshd_config
regexp: ^ClientAliveInterval
line: "ClientAliveInterval {{ sshd_idle_timeout_value }}"
validate: sshd -t -f %s
#notify: restart sshd
tags:
- sshd_set_idle_timeout
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27433-2
- NIST-800-53-AC-2(5)
- NIST-800-53-SA-8(i)
- NIST-800-53-AC-12
- NIST-800-171-3.1.11
- PCI-DSS-Req-8.1.8
- CJIS-5.5.6
- DISA-STIG-RHEL-07-040320

As you can see, it uses the lineinfile module to apply a variable that is defined at the very top of the playbook. Also, each of the tasks is tagged with quite a lot of information about which areas of the standard the fix comes under, and also the severity. This means we can get quite granular on which parts of the playbook run; for example, you could only run the low disruption changes by using the following command:

$ ansible-playbook -i production --become --become-method sudo --tags "low_disruption" generated/box1_ansible.yml

Finally, at the bottom of the box1_ansible.log file, we can see that the playbook run made the following changes:

PLAY RECAP **************************************************************************************
box1 : ok=151 changed=85 unreachable=0 failed=0
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.136.97.64