Managing System Users Using Ansible

By Anish, Alibaba Cloud Tech Share Author. Tech Share is Alibaba Cloud’s incentive program to encourage the sharing of technical knowledge and best practices within the cloud community.

In this article, we will learn how to manage users using Ansible in an Alibaba Cloud environment. We will start by defining the architecture, as shown in the diagram below.

Image for post
Image for post

The diagram shows an Ansible controller node managing various nodes using SSH protocol. Extending this diagram, we are going to create playbooks that will manage different users with their sudoers privilege in the target node.

To follow this tutorial, you will need to have Alibaba Cloud Elastic Compute Service (ECS) instances. If you are not sure how to launch an ECS instance in Alibaba Cloud, refer to this documentation.

Setting Up Ansible User

Here is a quick guide of creating and setting up ansible user in controller and target nodes.

Ansible User Setup in Controller Node

Create ansible remote user to manage the installation from Ansible Controller node. This user should have appropriate sudo privileges. An example sudoers entry is given below.

Add user ansible

[root@controller-node] adduser ansible

Switch to ansible user

[root@controller-node] su - ansible

Generate the SSH-keyPair for ansible user

[ansible@controller-node]ssh-keygen -t rsa -b 4096 -C "ansible"

Copy the id_rsa.pub file, to the target node Ansible /home/ansible.ssh/home directory

[ansible@controller-node]cd /home/ansible/.ssh/
[ansible@controller-node .ssh]$ ls
id_rsa id_rsa.pub

Note down the public key and copy it over onto other machines

[ansible@controller-node .ssh]$ cat id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAgEApeDUYGwaMfHd7/Zo0nzHA69uF/f99BYktwp82qA8+osph1LdJ/NpDIxcx3yMzWJHK0eg2yapHyeMpKuRlzxHHnmc99lO4tHrgpoSoyFF0ZGzDqgtj8IHS8/6bz4t5qcs0aphyBJK5qUYPhUqAL2Sojn+jLnLlLvLFwnv5CwSYeHYzLPHU7VWJgkoahyAlkdQm2XsFpa+ZpWEWTiSL5nrJh5aA3bgGHGJU2iVDxj2vfgPHQWQTiNrxbaSfZxdfYQx/VxIERZvc5vkfycBHVwanFD4vMn728ht8cE4VjVrGyTVznzrM7XC2iMsQkvmeYTIO2q2u/8x4dS/hBkBdVG/fjgqb78EpEUP11eKYM4JFCK7B0/zNaS56KFUPksZaSofokonFeGilr8wxLmpT2X1Ub9VwbZV/ppb2LoCkgG6RnDZCf7pUA+CjOYYV4RWXO6SaV12lSxrg7kvVYXHOMHQuAp8ZHjejh2/4Q4jNnweciuG3EkLOTiECBB0HgMSeKO4RMzFioMwavlyn5q7z4S82d/yRzsOS39qwkaEPRHTCg9F6pbZAAVCvGXP4nlyrqk26KG7S3Eoz3LZjcyt9xqGLzt2frvd+jLMpgvnlXTFgGA1ITExOHRb+FirmQZgnoiFbvpeIFj65d0vRIuY6VneIJ6EFcLGPpzeus0yLoDN1v8= ansible
[ansible@controller-node $ exit
[root@controller-node $ exit

Update the /etc/sudoers.d and add the ansible user to manage controller node itself

[root@controller-node $ visudo 
#includedir /etc/sudoers.d
ansible ALL=(ALL) NOPASSWD: ALL

Ansible User Setup on Target Node

Create user Ansible and create a file named authorized_keys in the .ssh directory and change its file permissions to 600 (only the owner can read or write to the file).

[root@nod1]sudo adduser ansible
[root@node1]sudo su - ansible
[ansible ~]$ cd /home/ansible
[ansible ~]$ mkdir .ssh
[ansible ~]$ touch authorized_keys
[ansible ~]$ chmod 600 authorized_keys

Copy the SSH public key from the ansible_controller node and add it to all the VM which is

[ansible ~]$ cat authorized_keys
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAgEApeDUYGwaMfHd7/Zo0nzHA69uF/f99BYktwp82qA8+osph1LdJ/NpDIxcx3yMzWJHK0eg2yapHyeMpKuRlzxHHnmc99lO4tHrgpoSoyFF0ZGzDqgtj8IHS8/6bz4t5qcs0aphyBJK5qUYPhUqAL2Sojn+jLnLlLvLFwnv5CwSYeHYzLPHU7VWJgkoahyAlkdQm2XsFpa+ZpWEWTiSL5nrJh5aA3bgGHGJU2iVDxj2vfgPHQWQTiNrxbaSfZxdfYQx/VxIERZvc5vkfycBHVwanFD4vMn728ht8cE4VjVrGyTVznzrM7XC2iMsQkvmeYTIO2q2u/8x4dS/hBkBdVG/fjgqb78EpEUP11eKYM4JFCK7B0/zNaS56KFUPksZaSofokonFeGilr8wxLmpT2X1Ub9VwbZV/ppb2LoCkgG6RnDZCf7pUA+CjOYYV4RWXO6SaV12lSxrg7kvVYXHOMHQuAp8ZHjejh2/4Q4jNnweciuG3EkLOTiECBB0HgMSeKO4RMzFioMwavlyn5q7z4S82d/yRzsOS39qwkaEPRHTCg9F6pbZAAVCvGXP4nlyrqk26KG7S3Eoz3LZjcyt9xqGLzt2frvd+jLMpgvnlXTFgGA1ITExOHRb+FirmQZgnoiFbvpeIFj65d0vRIuY6VneIJ6EFcLGPpzeus0yLoDN1v8= ansible

Alternatively from the Ansible Controller run the following command for each target node

[ansible@localhost ~]$ ssh-copy-id ansible@node3
/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/bin/ssh-copy-id: WARNING: All keys were skipped because they already exist on the remote system.[ansible@localhost ~]$ ssh-copy-id ansible@node2
/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/bin/ssh-copy-id: WARNING: All keys were skipped because they already exist on the remote system.[ansible@localhost ~]$ ssh-copy-id ansible@node1
/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
ansible@node1's password:
Number of key(s) added: 1Now try logging into the machine, with: "ssh 'ansible@node1'"
and check to make sure that only the key(s) you wanted were added.

Update the /etc/sudoers.d and add the ansible user

[root@controller-node $ visudo 
#includedir /etc/sudoers.d
ansible ALL=(ALL) NOPASSWD: ALL

Operations Perspective

Managing users in the cloud environment such as Alibaba Cloud is often a security and infrastructure requirement. In general, we deal with three sets of users:

This lab will utilize and illustrates this concept.

sudo accessnon sudo accessnologinuser1User4user7user2User5user3user6

For security hardening, servers running on Alibaba Cloud should only accept password-less logins. Using change management process such as (git), all users will be deployed in their respective nodes through Ansible controller. The users must submit their public key to the controller. For example, user1 has submitted the keys to be rolled out in the listed servers.

Image for post
Image for post

Managing System User Using Ansible

In this section, we will discuss how to add and delete Ansible users.

Adding System Users Using Ansible

  • [ansible@controller]$ tree . └── ssh ├── files ├── tasks │ └── main.yml └── vars
  • [ansible@controller]$ tree . └── ssh ├── files ├── tasks │ └── main.yml └── vars └── users.yml
  • [ansible@controller]$ cat ssh/vars/users.yml --- users: - username: user2 use_sudo: yes - username: user4 use_sudo: no - username: user6 use_sudo: no
  • [ansible@controller]$ tree . └── ssh ├── files │ ├── user2.pub │ ├── user4.pub │ └── user6.pub ├── tasks │ └── main.yml └── vars └── users.yml 4 directories, 5 files
  • [ansible@ ~]$ cat ssh/tasks/main.yml --- - include_vars: users.yml - name: Create users with home directory user: name={{ item.username }} shell=/bin/bash createhome=yes comment='Created by Ansible' with_items: '{{users}}' - name: Setup | authorized key upload authorized_key: user={{ item.username }} key="{{ lookup('file', 'files/{{ item.username }}.pub') }}" when: '{{ item.use_sudo }} == True' with_items: '{{users}}' - name: Sudoers | update sudoers file and validate lineinfile: "dest=/etc/sudoers insertafter=EOF line='{{ item.username }} ALL=(ALL) NOPASSWD: ALL' regexp='^{{ item.username }} .*' state=present" when: '{{ item.use_sudo }} == True' with_items: '{{users}}'

Let’s break down the code:

To run this playbook make sure we have the ansible inventory file set up correctly. Ansible inventory file is group of servers. For this example, I have created an inventory file named hosts and added all the nodes to it, which I need to manage.

[ansible@ ~]$ tree
.
├── hosts
└── ssh
├── files
│ ├── user2.pub
│ ├── user4.pub
│ └── user6.pub
├── tasks
│ └── main.yml
└── vars
└── users.yml
4 directories, 6 files

The inventory file looks like this:

[ansible@~]$ cat hosts
[all]
node1
node2
node3
[ansible@localhost ansible]$

Next we will create ssh.yml to tell ansible-playbook to use role ssh.

[ansible@~]$ cat ssh.yml 
# To Run this Playbook Issue the command
#Author Anish Nath
# ansible-playbook ssh.yml
---
- hosts: all
become: yes
gather_facts: yes
roles:
- { role: ssh }

Finally, run the playbook using the inventory hosts.

[ansible@controller]$ ansible-playbook ssh.yml -i hosts

The ansible-playbook output shows in all the managed nodes respective users with configured sudoers access is pushed.

PLAY [all] *************************************************************************************************************************************TASK [Gathering Facts] *************************************************************************************************************************
ok: [node1]
ok: [node3]
ok: [node2]
TASK [ssh : include_vars] **********************************************************************************************************************
ok: [node1]
ok: [node2]
ok: [node3]
TASK [ssh : Create users with home directory] **************************************************************************************************
changed: [node2] => (item={u'username': u'user2', u'use_sudo': True})
changed: [node1] => (item={u'username': u'user2', u'use_sudo': True})
changed: [node3] => (item={u'username': u'user2', u'use_sudo': True})
changed: [node2] => (item={u'username': u'user4', u'use_sudo': False})
changed: [node1] => (item={u'username': u'user4', u'use_sudo': False})
changed: [node3] => (item={u'username': u'user4', u'use_sudo': False})
changed: [node2] => (item={u'username': u'user6', u'use_sudo': False})
changed: [node1] => (item={u'username': u'user6', u'use_sudo': False})
changed: [node3] => (item={u'username': u'user6', u'use_sudo': False})
TASK [ssh : Setup | authorized key upload] *****************************************************************************************************
[WARNING]: when statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found: {{ item.use_sudo }} == True
[WARNING]: when statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found: {{ item.use_sudo }} == True [WARNING]: when statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found: {{ item.use_sudo }} == Trueok: [node1] => (item={u'username': u'user2', u'use_sudo': True})
skipping: [node1] => (item={u'username': u'user4', u'use_sudo': False})
skipping: [node1] => (item={u'username': u'user6', u'use_sudo': False})
ok: [node3] => (item={u'username': u'user2', u'use_sudo': True})
skipping: [node3] => (item={u'username': u'user4', u'use_sudo': False})
ok: [node2] => (item={u'username': u'user2', u'use_sudo': True})
skipping: [node2] => (item={u'username': u'user4', u'use_sudo': False})
skipping: [node3] => (item={u'username': u'user6', u'use_sudo': False})
skipping: [node2] => (item={u'username': u'user6', u'use_sudo': False})
TASK [ssh : Sudoers | update sudoers file and validate] ****************************************************************************************
[WARNING]: when statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found: {{ item.use_sudo }} == True
[WARNING]: when statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found: {{ item.use_sudo }} == True [WARNING]: when statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found: {{ item.use_sudo }} == Trueok: [node3] => (item={u'username': u'user2', u'use_sudo': True})
skipping: [node3] => (item={u'username': u'user4', u'use_sudo': False})
skipping: [node3] => (item={u'username': u'user6', u'use_sudo': False})
ok: [node1] => (item={u'username': u'user2', u'use_sudo': True})
skipping: [node1] => (item={u'username': u'user4', u'use_sudo': False})
skipping: [node1] => (item={u'username': u'user6', u'use_sudo': False})
ok: [node2] => (item={u'username': u'user2', u'use_sudo': True})
skipping: [node2] => (item={u'username': u'user4', u'use_sudo': False})
skipping: [node2] => (item={u'username': u'user6', u'use_sudo': False})
PLAY RECAP *************************************************************************************************************************************
node1 : ok=5 changed=1 unreachable=0 failed=0
node2 : ok=5 changed=1 unreachable=0 failed=0
node3 : ok=5 changed=1 unreachable=0 failed=0

Removing System Users Using Ansible

In the Alibaba Cloud environment, a user has a specific lifecycle. If the user is no longer required to be present in the system, the user must be deleted, and this should happen proactively. For an example, if user2 needs to deleted, then from the change management process, users.yml files needs to be edited by removing the entry of user2.

Before deleting user2:

[ansible@controller]$ cat ssh/vars/users.yml 
---
users:
- username: user2
use_sudo: yes
- username: user4
use_sudo: no
- username: user6
use_sudo: no

After deleting user2:

[ansible@controller]$ cat ssh/vars/users.yml 
---
users:
- username: user4
use_sudo: no
- username: user6
use_sudo: no

Now this user needs to be deleted across the Alibaba Cloud environment, which is managed by theAnsible controller. To do this, create a file deleteusers.yml in the vars directory and maintain a set of users that needs to be removed from the target node.

[ansible@controller ~]$ cat ssh/vars/deleteusers.yml 
---
users:
- username: user2
- username: user3
- username: user5

Next, update the main.yml file, which is present in tasks and add the delete instructions using:

- include_vars: deleteusers.yml  - name: Deleting The users
user: name={{ item.username }} state=absent remove=yes
with_items: '{{users}}'

Finally, run the playbook using the inventory hosts.

[ansible@controller ~]$ ansible-playbook ssh.yml -i hosts

The “delete” task has been executed by this playbook. User2 has been removed from all ansible managed nodes.

TASK [ssh : Deleting The users] ****************************************************************************************************************
changed: [node2] => (item={u'username': u'user2'})
changed: [node1] => (item={u'username': u'user2'})
changed: [node3] => (item={u'username': u'user2'})

Conclusion

That’s the end of this post. In this article, we discussed how to manage users using Ansible, including adding and deleting users. Ansible is great way of automatically securing Alibaba Cloud environment. For more references around the Ansible User module refer to the Ansible Website.

Reference:

https://www.alibabacloud.com/blog/managing-system-users-using-ansible_593861?spm=a2c41.11840284.0.0

Written by

Follow me to keep abreast with the latest technology news, industry insights, and developer trends.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store