Understanding Ansible Privilege Escalation

When using Ansible, different commands can be run. While some tasks can be performed with no admin privileges needed, most of the time you will need to escalate privileges to run successfully the commands that need to change the state of your managed hosts, like installing packages or restarting services.

We will see in this post how Ansible can use existing privilege escalation systems to allow a user to execute tasks as another.

 

Privilege Escalation


 

Let’s take a basic command to get the date on the localhost

[user@server1 ~]$ ansible localhost -m command -a "date"
localhost | SUCCESS | rc=0 >>
Fri Jul 13 23:11:03 +04 2018

 

Running these commands like pinging or fetching the date on remote servers works fine, but how about running tasks that need admin privileges.

 

In this example:

  • There are two command arguments. The -m (module) argument to specify the type of module to use, which in this case is the command module, and the -a argument which is the actual command to run.
  • We are performing a task against the local server, which is actually not the main purpose of Ansible. In fact, to start managing remote servers, some prerequisites need to be run. The main thing here is only to understand basic privilege escalation in Ansible.

We will see in more details what are Ansible modules and how to prepare managed hosts in future posts.

 

Let’s play with the shadow file by trying to display the root user information in the shadow file using grep.

[user@server1 ~]$ ansible localhost -m command -a "grep ^root: /etc/shadow"
 [WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

localhost | FAILED | rc=2 >>
grep: /etc/shadow: Permission deniednon-zero return code

 

Permission is denied. The point is, that only user root has the rights to do this.

[user@server1 ~]$ su -
Password:
Last login: Thu Jul 12 16:10:22 +04 2018 on pts/0
[root@server1 ~]#
[root@server1 ~]#
[root@server1 ~]# ansible localhost -a "grep ^root: /etc/shadow"
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

localhost | SUCCESS | rc=0 >>
root:$6$dzwtCzZWYgGbsHuH$98n/jzi19CNH5iilBYw1dMCr8A9LYxQs4D32hcHTfZarOnsFIr9NgBO67zRRTobLeKjdPgVqG4dhArO84Q9Rv1::0:99999:7:::

[root@server1 ~]#

 

Permission is denied to perform any administrative task because the Ansible user does not have admin privileges. However, instead of using the root account, it is considered a best practice to use a regular user to run all Ansible tasks. For this, we’ll make sure to get our Ansible admin privileges by adding it to the wheel group.

[user@server2 ~]$ su -
Password:
Last login: Wed Jan 24 12:39:02 +04 2018 on :0
[root@server2 ~]#
[root@server2 ~]# id user
uid=1000(user) gid=1000(user) groups=1000(user)
[root@server2 ~]#
[root@server2 ~]# usermod -aG wheel user
[root@server2 ~]#
[root@server2 ~]# id user
uid=1000(user) gid=1000(user) groups=1000(user),10(wheel)
[root@server2 ~]#

 

Let’s try the same command after logging out then loggin in again to update the security token. To pass the command with sudo privileges in Ansible, the -b or –become argument is used.

[user@server1 ~]$ ansible localhost -b -a "grep ^root: /etc/shadow"
 [WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

localhost | FAILED! => {
  "changed": false,
  "module_stderr": "sudo: a password is required\n",
  "module_stdout": "",
  "msg": "MODULE FAILURE",
  "rc": 1
}

 

Ansible is now complaining that the sudo password is required to run this admin command. The -K or –ask-become-pass argument is used in this case to tell Ansible to ask for the sudo password before running commands that need privileges.

[user@server1 ~]$ ansible localhost -b -K -m command -a "grep ^root: /etc/shadow"
SUDO password:
 [WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

localhost | SUCCESS | rc=0 >>
root:$6$dzwtCzZWYgGbsHuH$98n/jzi19CNH5iilBYw1dMCr8A9LYxQs4D32hcHTfZarOnsFIr9NgBO67zRRTobLeKjdPgVqG4dhArO84Q9Rv1::0:99999:7:::

 

To avoid having to provide a sudo password, you can simply uncomment this option in the sudoers file, but you need to consider the security implications by doing such configuration.

 

[user@server1 ~]$ sudo visudo

    ## Same thing without a password
    {a30ec6af4236f4179fdacbfa14163c342445d6f2c9798eae9caee1ef22a84454}wheel  ALL=(ALL)       NOPASSWD: ALL

 

Let’s try the same command withou the -K argument

[user@server1 ~]$ ansible localhost -b -m command -a "grep ^root: /etc/shadow"
 [WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

localhost | SUCCESS | rc=0 >>
root:$6$dzwtCzZWYgGbsHuH$98n/jzi19CNH5iilBYw1dMCr8A9LYxQs4D32hcHTfZarOnsFIr9NgBO67zRRTobLeKjdPgVqG4dhArO84Q9Rv1::0:99999:7:::

 

Bear in mind that the Ansible user needs to be created in all the servers that have to be managed by the Ansible controller. So for ease of management, stick to the same user / same password in your environment.

The arguments related to privilege escalation can be pre-configured in the Ansible configuration file, making it possible to run Ansible commands without providing any privilege directive. We will see how to to do it in the upcoming posts.

2 Comments

  1. Pingback: Ansible for the Impatient Beginners – vAdmin-Land

  2. Pingback: Ansible for the Impatient Beginners – vAdmin-Land

Leave a Comment

Your email address will not be published. Required fields are marked *