Bookmark this page

Chapter 2. Automating Configuration and Remediation with Ansible

Abstract

Goal Remediate configuration and security issues automatically with Ansible Playbooks.
Objectives
  • Describe the benefits of automation tools for managing security, install and configure an Ansible control node, and configure systems so that they can be managed by Ansible.

  • Read and interpret an existing Ansible Playbook, and run it in order to apply its plays to hosts as specified by the plays and the current Ansible inventory.

  • Run playbooks and manage access to authentication credentials using Red Hat Ansible Tower

Sections
  • Configuring Ansible for Security Automation (and Guided Exercise)

  • Remediating Issues with Ansible Playbooks (and Guided Exercise)

  • Managing Playbooks with Red Hat Ansible Tower (and Guided Exercise)

Lab

Automating Configuration and Remediation with Ansible

Configuring Ansible for Security Automation

Objectives

After completing this section, students should be able to describe the benefits of automation tools for managing security, install and configure an Ansible control node, and configure systems so that they can be managed by Ansible.

Security Automation

Managing security for large numbers of systems can be challenging. You need to ensure that all machines are correctly and consistently configured and patched. When new threats or issues are discovered, you may need to rapidly ensure that all systems are configured or provisioned correctly. You need to be able to detect incorrect changes that have been applied manually to individual systems and return them to the correct configuration. Trying to do this manually, updating each machine individually, does not scale.

Administrators have used various techniques to ensure that work is applied consistently and correctly to all machines under their care. One of the best approaches to manage this is through automation. Effective automation can help speed up the correct deployment of a security remediation.

A good automation solution should be able to deploy and provision systems from scratch. They should also be able to run on a system that is already configured, and either confirm that it is already correctly configured and do nothing, or make appropriate changes to that system to ensure that it is correctly configured.

There are a number of useful tools that can be used to do this. In this course, you will learn a little about how to use Red Hat Ansible Automation for security remediation. There are many reasons to use Ansible in a security context, but a few include:

  • Ansible is simple to use. Managed systems have minimal requirements and do not need to have a special agent installed. The Ansible software itself is simple to install and use on your control systems.

  • Ansible provides human-readable automation. The automation instructions (Ansible Playbooks) are written as structured text files, which are easy to read and help ensure that everyone can understand what they will do.

  • A number of security tools provide Ansible Playbooks to help remediate issues. You don't have to write your own Playbooks. A number of tools that can identify and detect security-related issues provide Ansible Playbooks which can be used to help remediate them. This course reviews two of them in detail: the SCAP Security Guide for OpenSCAP and Red Hat Insights.

When your security policy is defined in Ansible, scanning and remediation of site-wide security policies can be integrated into other automated processes. Instead of being an afterthought, it is an integral part of everything that is deployed.

Note

In this course, you will

  • Focus on how to install Red Hat Ansible Engine on a control node and configure systems for Ansible automation

  • Interpret an existing Ansible Playbook, and learn how to run it

  • Use an existing Ansible Playbook to ensure machines are correctly configured or updated

In some later chapters, you may have the opportunity to use preexisting Ansible Playbooks to apply provided remediations to managed systems.

In this course, you will not learn how to write your own Ansible Playbooks from scratch, although you may learn enough to start to do so. However, the goal of this course is for you to understand the value of a tool like Ansible for security automation, gain a basic understanding of how to use it, and help you determine whether you want to learn more from other courses and resources.

Ansible Concepts and Architecture

Ansible is an open source automation platform that uses a simple automation language to describe the desired configuration of servers and applications.

Instead of writing complex scripts, Ansible users write high-level plays that specify the desired state of particular managed hosts. A file containing one or more plays is called an Ansible Playbook. The plays are expressed as human-readable text in YAML format in the playbook file.

Each play specifies which hosts should be managed and a list of tasks to perform on them. Tasks are executed in the order in which they are listed in the play. Each task runs a small piece of code called a module with specific arguments. The task and its arguments check to see whether the host is in a particular state, and if it is not, changes the host to put it in that state. Each module is a small piece of code that is essentially a tool in your toolkit. Ansible ships with hundreds of useful modules that can perform a wide variety of automation tasks. They can act on system files, install software, or make API calls.

When used in a task, a module generally ensures that some particular thing about the machine is in a particular state. For example, a task using one module may ensure that a file exists and has particular permissions and contents, while a task using a different module may make certain that a particular file system is mounted. If the system is not in that state, the task should put it in that state. If the system is already in that state, it should do nothing. If a task fails, Ansible's default behavior is to abort the rest of the playbook for the hosts that had a failure.

Tasks, plays, and playbooks should be idempotent. This means that you should be able to run a playbook on the same hosts multiple times safely, and when your systems are in the correct state the playbook should make no changes when run.

There are two types of machines in the Ansible architecture: control nodes and managed hosts. Ansible is installed and run from a control node. A control node could be an administrator's laptop, a system shared by a number of administrators, or a server running Ansible Tower. Managed hosts are the systems that Ansible controls. Managed hosts are listed in an inventory, which can organize those systems into host groups for easier collective management. The inventory can be defined in a static text file, or dynamically determined by scripts that get information from external sources.

Each play in a playbook specifies the hosts or groups of hosts on which it should run. Those hosts or host groups must be defined in the current inventory.

Installing and configuring Ansible

Installing Ansible

In order to use Ansible, you must install the Ansible software on a host (the control node) from which you will run Ansible Playbooks. Then you need to make sure that your managed hosts are set up so that you can use Ansible to connect to them, become root (probably using sudo, but other methods are possible, depending on the type of managed host), and run Ansible plays.

Installing Ansible on the Control Node

Information on how to install the ansible software package on a Red Hat Enterprise Linux system is available in the Knowledgebase article How Do I Download and Install Red Hat Ansible Engine?.

Ansible is under rapid upstream development, and therefore Red Hat Ansible Engine has a rapid life cycle. More information on the current life cycle is available at https://access.redhat.com/support/policy/updates/ansible-engine.

At the time of writing, Red Hat provided several channels for Red Hat Enterprise Linux 7 Server, as listed in the following table.

Channel Name Description
rhel-7-server-ansible-2-rpms

The latest update of the Red Hat Ansible Engine 2 major release for RHEL 7. The minor release can change in this channel.

rhel-7-server-ansible-2.6-rpms

Red Hat Ansible Engine 2.6 for RHEL 7.

rhel-7-server-ansible-2.5-rpms

Red Hat Ansible Engine 2.5 for RHEL 7.

If you have standard Red Hat Enterprise Linux subscriptions, you can use these channels to install Ansible with limited support. If you need more comprehensive Ansible support, you can purchase full Red Hat Ansible Engine subscriptions and associate them with your systems before enabling the channels, as discussed in the Knowledgebase article.

If you have a Red Hat Ansible Engine Subscription, the installation procedure for Red Hat Ansible Engine 2 is:

  1. Register your system to Red Hat Subscription Manager.

    [root@host ~]# subscription-manager register
  2. Attach your Red Hat Ansible Engine subscription. This command helps you find your Red Hat Ansible Engine subscription:

    [root@host ~]# subscription-manager list --available
  3. Use the pool ID of the subscription to attach the pool to the system.

    [root@host ~]# subscription-manager attach --pool=<engine-subscription-pool>
  4. Enable the Red Hat Ansible Engine repository.

    [root@host ~]# subscription-manager repos \
    > --enable rhel-7-server-ansible-2-rpms
  5. Install Red Hat Ansible Engine.

    [root@host ~]# yum install ansible

If you are using the version with limited support provided with your Red Hat Enterprise Linux subscription:

  1. Enable the Red Hat Ansible Engine repository.

    [root@host ~]# subscription-manager refresh
    [root@host ~]# subscription-manager repos \
    > --enable rhel-7-server-ansible-2-rpms
  2. Install Red Hat Ansible Engine.

    [root@host ~]# yum install ansible

Important

If you are already using Ansible but you installed it from the Red Hat Enterprise Linux 7 Extras channel, be aware that Ansible and its dependencies will no longer be updated through the Extras channel. The official channels discussed in this section replace that distribution method.

For more information, see the Knowledgebase article "Ansible deprecated in the Extras channel".

Preparing Managed Hosts for Ansible Automation

Ansible is a cross-platform solution that can manage Linux hosts, Microsoft Windows systems, and even managed networking devices. For the purposes of this discussion, the focus is on configuration for Linux managed hosts.

Most modules used on Linux managed hosts are written in Python. Ansible 2.5 requires that the managed hosts are installed with Python 2.6 or later (Python 3.5 or later if the system has Python 3 installed). The python package in Red Hat Enterprise Linux 7 provides the correct version of Python.

Some modules have additional requirements. For example, you also need to install libselinux-python before using the Ansible modules that are related to copy, file, or template functions if SELinux is enabled on the managed host. The package module does not have this requirement, and can be used in a play to make sure libselinux-python is installed.

The control node needs to be able to connect to the managed host in order to run tasks. Normally, this means the managed host must be running the sshd service. You can specify the user that the control node uses to connect to the managed hosts, and optionally you can use privilege escalation methods like sudo to switch from a non-privileged account to root.

There are a number of ways you can configure authentication and privilege escalation depending on your policies and requirements. One of the most common is to use SSH public key authentication to connect to each managed host as a non-privileged user, and then to have Ansible use sudo as needed to escalate privileges to root.

There are two ways to configure the Ansible user with sudo access. First, you may configure sudo to require the Ansible user to enter their password to escalate privileges. The default configuration of /etc/sudoers permits this for any member of group wheel, so you might add the Ansible user to that group.

Alternatively, you could add a rule to explicitly allow that access for a user. For example, by creating a file named /etc/sudoers.d/ansible-testuser which includes the following line, you permit the ansible-testuser user to use sudo to run any command as any user after entering their password to authenticate.

user  ALL=(ALL) ALL

Another option is to permit the Ansible user to run sudo without entering a password. This is less secure, but may be more convenient than the other option. For example, you could set up /etc/sudoers.d/user like this in order to permit ansible-testuser user to use sudo to run any command as any user without further authentication:

user  ALL=(ALL) NOPASSWD:ALL

Note

There are various security trade-offs here. If you require the Ansible user to provide their password to escalate privileges, the administrators running Ansible may need to know that user's password and it must be the same everywhere. Tools like Red Hat Ansible Tower can be used to help manage this, by protecting the sudo password from administrators running Ansible plays while still providing it for privilege escalation.

If you don't require the Ansible user to provide their password for sudo, then if that account is compromised the attacker may more easily gain root access. On the other hand, then you can set up the Ansible user with no valid password at all, to help ensure that the only way to access that account is through SSH public key authentication or the local root account.

Managing a Host Inventory

An inventory defines a collection of hosts that Ansible will manage. These hosts can also be assigned to groups, which can be managed collectively. Groups can contain child groups, and hosts can be members of multiple groups. The inventory can also set variables that apply to the hosts and groups that it defines.

An inventory can be of two types, static and dynamic. A static host inventory can be defined by a INI-like text file. A dynamic host inventory can be generated by a script or other program as needed, using external information providers.

In its simplest form, a static inventory is a list of host names or IP addresses of managed hosts, each on a single line:

server1.example.com
server2.example.com
server3.example.com

You can organize managed hosts into host groups. Host groups allow you to more effectively run Ansible against a collection of systems. In this example, each section starts with a host group name enclosed in square brackets ([]). This is followed by the host name or an IP address for each managed host in the group, each on a single line. Hosts can and often are members of multiple host groups for easier manageability.

Host groups can also be nested. The suffix :children on a name in a square-bracketed section sets up a nested group, and each line is the name of another group. All hosts in those groups are also members of the parent group.

This example inventory file creates a webservers group containing the server1.example.com and server2.example.com hosts. It also creates another group named fileservers, containing server3.example.com and server4.example.com. Finally, it creates a group named myservers that includes the webservers and fileservers groups as nested groups inside it. (The inventory could also have included a [myservers] section to add individual hosts to that group.)

[webservers]
server1.example.com
server2.example.com

[fileservers]
server3.example.com
server4.example.com

[myservers:children]
webservers
fileservers

Note

Two host groups always exist:

  • The all host group includes every host listed in the inventory.

  • The ungrouped host group contains every host listed in the inventory that is not a member of another group.

Configuring Ansible Operation

The behavior of an Ansible installation can be customized by modifying settings in the Ansible configuration file. Ansible chooses its configuration file from one of several possible locations on the control node.

The ansible package provides a default configuration file in /etc/ansible/ansible.cfg. But the recommended practice is to create an ansible.cfg file in a directory from which you run Ansible commands. If that configuration file exists, it is used instead of the system-wide file. This directory would also contain any files used by your Ansible project, such as an inventory and a playbook.

Note

The ansible --version command will show you the location of the configuration file being used.

The Ansible configuration file consists of several sections, with each section containing settings defined as key-value pairs. Section titles are enclosed in square brackets. For basic operation, two sections matter:

  • [defaults] sets defaults for Ansible operation

  • [privilege_escalation] configures how Ansible performs privilege escalation on managed hosts.

This is an example of a typical ansible.cfg file:

[defaults]
inventory = ./inventory
remote_user = user
ask_pass = false

[privilege_escalation]
become = true
become_method = sudo
become_user = root
become_ask_pass = false

The directives in this file are explained in the following table:

Directive Description
inventory

Specifies the path to the inventory file.

remote_user

The name of the user to log in as on the managed hosts. If not specified, the current user's name is used.

ask_pass

Whether or not to prompt for an SSH password. Can be false if using SSH public key authentication.

become

Whether to automatically switch user on the managed host (typically to root) after connecting. This can also be specified by a play.

become_method

How to switch user (typically sudo, which is the default, but su is an option).

become_user

The user to switch to on the managed host (typically root, which is the default).

become_ask_pass

Whether to prompt for a password for your become_method. Defaults to false.

Testing Ansible with Ad Hoc Commands

You can test your Ansible configuration and inventory before running a complete playbook by using the ansible command. Assuming you are in a directory containing your Ansible configuration file, you can use the ansible command to validate that configuration and the inventory file at which it points. The following command would attempt to list all the hosts in the myservers host group:

[user@demo project1]$ ansible myservers --list-hosts
SSH password: redhat
SUDO password[defaults to SSH password]: redhat
  hosts (4):
    server1.example.com
    server2.example.com
    server3.example.com
    server4.example.com

An ad hoc command is a simple one-task command that you can run using the ansible command without writing a playbook. The simplest ad hoc command uses the ping module. The ping module takes no arguments, and connects to the specified managed hosts to check if any Ansible module can be run successfully. It does not perform a network ping, it actually connects to the host and runs a task.

The following example ad hoc command connects to each host in the webservers host group and runs the ping module on them successfully, confirming that Ansible is working:

[user@demo project1]$ ansible webservers -m ping
server1.example.com | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}
server2.example.com | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}

Instead of specifying webservers, you could have specified any host or host group in the inventory.

A more complex example takes advantage of ad hoc commands to make one quick change to a group of systems. The firewalld module can adjust the configuration of a server firewall managed by the firewalld daemon. The module takes a list of arguments (specified after -a for the ansible command) that tell it what to check and correct. You can use the command ansible-doc firewalld to see its documentation, but the next section covers this in greater detail.

In the following example, the ansible ad hoc command uses the firewalld module to ensure the correct configuration of the firewall settings of systems in the webservers group. It ensures that the ports specified by the firewalld predefined service group http are immediately and permanently enabled for the default zone.

[user@demo project1]$ ansible webservers \
> -m firewalld -a "service=http permanent=true state=enabled immediate=true"

If a system is already correctly configured, nothing changes and the command tries the next system. If a system is not correctly configured, the ansible ad hoc command changes the system's configuration to comply.

References

ansible(1), ansible-inventory(1), and ansible-config(1) man pages

For more information, refer to the GET STARTED video in the ansible.com website at https://www.ansible.com/resources/get-started

The HOW ANSIBLE WORKS overview page is available at https://www.ansible.com/overview/how-ansible-works

Documentation for the latest upstream version of Ansible is available at https://docs.ansible.com

Revision: rh415-7.5-813735c