Categories
Ansible Networking Projects

Creating passwordless logins with Ansible

What kind of users? Well, a special user called Ansible, which will use SSH keys to login into remote devices, allowing for full automation on playbooks.

Creating a new key

If you have been following the series, maybe you remember that we already created keys on the Juniper Junos SSH Keys post.

To create a new key, let’s issue the ssh-keygen command as follows. The -f flag tells the output path, and the -C flags specifies a comment.

$ ssh-keygen -f ansible.key -C ansible-login-passwordless

This should output two files, ansible.key and ansible.key.pub.

The public key should look something like this.

$ cat ansible.key.pub 
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCtJUPfzJY6vKqLUssPPQe+LD7qRmIPbVhb/1i4Qab7T0Vf3x+ItfJyV4Ej4FsnRSU8iMU8J5eIdcetGQfsmwIZAm8glB0T6En5F9lvq2Yd+3RKIvxM3UlrIH6EaRedhsRUyV96CHfIO2nVqS9dmFfgrOJMIOwfTWIiRDNczUPw7aqw0FExslw9ZC0FO/1A6hYgofkGLrdIu9gK/WkNg5BE1EUCYPqbDBEHnnhv3C33LqiSJZnXJyqu53qz+jlv+1LZxerNHuovMGZMkjQsBo2f3r9Gk/9HqBmT0rcLr5prm4CqqryJ3S9VyVVlF599BlqYMuMjj+fCj277R8kSnLxl ansible-login-passwordless

Of course we need an inventory to use, which has the following content.

$ cat inventory.yml 
---
all:
  hosts:
    vars:
      ansible_ssh_user: ansible
      ansible_ssh_private_key_file: ansible.key
      ansible_python_interpreter: auto_silent
    hosts:
      localhost:

This inventory only has one host, localhost, and uses three main variables:

  • ansible_ssh_user, which tell Ansible to use the user ansible
  • ansible_ssh_private_key_file, which indicates the key for this user
  • ansible_python_interpreter, just to avoid non needed logs

The playbook will looks like this. Notice we don’t need to gather_facts here, and we will instruct ansible to use become to gain privileges on the destination host.

---
- hosts: all
  become: yes

  tasks:

    - name: Make sure we have a "wheel" group
      group:
        name: wheel
        state: present

    - name: Allow 'wheel' group to have passwordless sudo
      lineinfile:
        dest: /etc/sudoers
        state: present
        regexp: '^%wheel'
        line: '%wheel ALL=(ALL) NOPASSWD: ALL'
        validate: 'visudo -cf %s'
        
    - name: Create "ansible" user
      user:
        name: ansible
        comment: Ansible Automation User
        groups: wheel

    - name: Add ssh key
      authorized_key:
        user: ansible
        state: present
        key: "{{ lookup('file', './ansible.key.pub') }}"

First, we want to make sure there is a group called wheel which will group users with administrative privileges.

Then, the /etc/sudoers file will be edited by allowing the wheel group to gain privileges, with a failsafe using a visudo validation.

Once the group has been created, the new user will be created, and a SSH key will be added to it.

It seems allright, but, how should we run the playbook, if the default user is ansible and this user does not exists yet? Let’s give it a try.

$ ansible-playbook create-user.yml -i inventory.yml 

PLAY [all] ************************************************************************

TASK [Gathering Facts] ************************************************************
fatal: [localhost]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: ansible@localhost: Permission denied (publickey,password).", "unreachable": true}

PLAY RECAP ************************************************************************
localhost                  : ok=0    changed=0    unreachable=1    failed=0    skipped=0    rescued=0    ignored=0   

It fails, as expected, because the ansible user does not exists in the host.

Well, there is a way to provide a one-time password by connecting a as different user. You will need to install sshpass with your favourite package manager, like apt install sshpass.

One installed, run the playbook once again with the following arguments:

  • -e “ansible_ssh_user=xxxxx”, where xxxxx is a valid user on the remote host
  • -kK, which tell Ansible to ask for a login and a sudo password
$ ansible-playbook create-user.yml -i inventory.yml -e "ansible_ssh_user=arturo" -kK
SSH password: 
BECOME password[defaults to SSH password]: 

PLAY [all] ************************************************************************

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

TASK [Make sure we have a "wheel" group] ******************************************
changed: [localhost]

TASK [Allow 'wheel' group to have passwordless sudo] ******************************
changed: [localhost]

TASK [Create "ansible" user] ******************************************************
changed: [localhost]

TASK [Add ssh key] ****************************************************************
changed: [localhost]

PLAY RECAP ************************************************************************
localhost                  : ok=5    changed=4    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

Awesome, we have sucessfully created a new user!

Let’s try to connect using the ansible user with its key, as defined in the playbook.

$ ansible -m ping -i inventory.yml all
localhost | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}

Stay tuned for more automation using Ansible.