Systems Hardening Using the CIS Benchmarks & Ansible

In my previous post, we discussed the CIS Benchmarks and system hardening. If you missed it, please check it out here so you can follow along. If you are familiar with the Benchmarks and would love to learn how you can automate implementation with Ansible, please keep reading.

With that said, there are numerous ways in which you can automate system hardening using the Benchmarks as a guideline. I am choosing Ansible because it is pretty simple to learn, unlike other languages that have a steep learning curve. So what is Ansible? Ansible is an open-source, agentless, automation engine, that uses playbooks written in YAML (Yet Another Markup Language) which are a combination of tasks that push out Ansible modules to automate well, tasks.  So let’s start by installing Ansible so that we can create a playbook.

Installing Ansible  

I am going to be using an Ubuntu 18.04 LTS Amazon Machine Image (AMI) as I did in the previous blog.  Once you are logged into your Ubuntu instance, type the command ‘sudo apt install software-properties-common’ on the command line as shown in the example below:

Installing software-properties-common via apt

Installing software-properties-common via apt

And then install ansible by typing, sudo apt install ansible:

Installing Ansible via apt

Installing Ansible via apt

Once the install is complete, make sure that you upgrade Ansible by typing the command sudo apt upgrade ansible to ensure the latest version is installed:

Upgrade Ansible via apt

Upgrade Ansible via apt

Now that ansible is installed, we will need to navigate to the directory that it is installed in. We can do this by typing cd /etc/ansible:

Navigating to the Ansible directory

Navigating to the Ansible directory

As you can see in the image above, there are currently two files, ansible.cfg and a hosts file. We aren’t going to address these at this time since we are going to be running our playbook locally.  

The next step is to create a directory where we will store our playbooks.  Create the directory by typing sudo mkdir playbooks:

Create the ansible playbook directory

Create the ansible playbook directory

I am going to use the same CIS Benchmark configuration from the last blog which was, 2.3.4 Ensure telnet client is not installed (Scored). Since this specific configuration item is part of a larger section, 2.3 Service Clients, we should create the playbook as such so we can add multiple plays for a specific section in a single playbook. 

To do this, make sure you are in the ansible directory and type sudo touch 2.3_service_clients.yaml  This will create a .yaml file called 2.3_service_clients.yaml:

Create an ansible playbook in the new directory

Create an ansible playbook in the new directory

Creating an Ansible playbook

Before we continue creating the playbook, let’s talk about the playbook structure and its various components. A playbook can be a single play or a group of plays that are contained within a playbook. A playbook is essentially a block of instructions that are carried out on a single host or group of hosts. 

Below is an example of a playbook that we are going to create and run on our local Ubuntu 18.04 machine. We will walk through each of the items so that you have a clear understanding of the different variables and structure of the playbook:

Ansible playbook example

Ansible playbook example

Let’s start with the — at the beginning of the playbook. Every playbook starts with 3 dashes to indicate the beginning of a .yaml file. 

The next item, – hosts: 127.0.0.1 defines the hosts that a playbook runs against.  These hosts are defined as variables in the hosts file and are used to group hosts together.  In this case, we will be running our playbook on our local machine and will not be modifying the hosts file. To ensure that the playbook runs locally, we will set the hosts to 127.0.0.1 and the connection to local in the playbook.  This ensures the task is carried out and only executed on the local machine using the loopback address. 

The next item, tasks: is where we start defining a task or list of tasks that we want to run in the playbook.  The – name: 2.3.4 Ensure telnet client is not installed (Scored) is a logical name of a task that is going to run. I like to keep this pretty descriptive for troubleshooting purposes

Our next step is to define the ansible module that we will be using  If you are not that familiar with Ubuntu there is a utility called, apt which we used above to install ansible. CentOS and RHEL use the package manager, yum. Ansible has a modules for both:  apt – Manages apt-packages and yum – Manages packages with the yum package manager which defines various parameters. 

We will be using the parameter “state” and we will define the desired package state as “absent”. We also have an ansible conditional statement so the playbook will know which play to run, apt or yum depending on the OS. This will ensure that the telnet client is absent, or uninstalled on our local machine:

 apt:

       name: telnet

       state: absent

 yum:

       name: telnet

       state: absent

AND adding a conditional statement so you can run the playbook across multiple OSs

when: ansible_distribution == ‘Debian’ or ansible_distribution == ‘Ubuntu’ 

when: ansible_distribution == ‘CentOS’ or ansible_distribution == ‘Red Hat Enterprise Linux’

Ansible apt – Manages apt-packages parameters

Ansible apt – Manages apt-packages parameters

Running the playbook

Before we run our first playbook, we can run the commands from the previous blog to ensure the telnet client is installed so that you can see the changes all the way through and make sure your playbook is working. On the command line, type dpkg -s telnet and verify the output:

Verify the telnet client is installed

Verify the telnet client is installed

We have verified that the telnet client is installed so now let’s run our playbook.  Make sure you are in the /etc/ansible/playbooks directory and type the command sudo ansible-playbook 2.3_service_clients.yaml:

Ansible 2.3_service_clients.yaml playbook output

Ansible 2.3_service_clients.yaml playbook output

You will see in the output above that the first task ran successfully and a change was made to the 2.3.4 Ubuntu & Debian Ensure telnet client is not installed (Scored) play and that the 2.3.4 CentOS & Rhel Ensure telnet client is not installed (Scored) play was skipped. To verify the change was successful, we can run the command dpkg -s telnet again and verify the output:

Verify the telnet client is not installed

Verify the telnet client is not installed

Alternatively, you can rerun the ansible playbook and you will see that no changes were made.  This is because Ansible will skip over a defined task if no changes are required. We can demonstrate this by adding a second play within the playbook to uninstall the talk client.

Ansible playbook to uninstall the talk client

Ansible playbook to uninstall the talk client

Below is the output of running the playbook after adding the task to remove the talk client.

Ansible playbook output removing talk

Ansible playbook output removing talk

You can see from the output above that the playbook ran successfully and one item was changed, this was the task of uninstalling the talk client for the 2.3.3 Ubuntu & Debian Ensure talk client is not installed (Scored) play.  

Congratulations! You have run an Ansible playbook and uninstalled two services. It is important to note that this is a very simple playbook.  Many playbooks get very complex depending on their purpose like implemnting all Level 1 CIS Benchmarks. This example was just to provide an overview of how to write and run a simple playbook on your local machine to leverage Ansible to enforce CIS Benchmark compliance.

Previous

Next