Remove authorized_keys using Ansible for multiple keys and multiple users

1.6k views Asked by At

I am fairly new to Ansible and has been assigned a task. I have a YAML file in which I have the following keys for multiple users.

client:
 - key: ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAklOUpkDHrfHY17SbrmTIpNLTGK9Tjom/BWDSU
GPl+nafzlHDTYW7hdI4yZ5ew18JH4JW9jbhUFrviQzM7xlELEVf4h9lFX5QVkbPppSwg0cda3
Pbv7kOdJ/MTyBlWXFCR+HAo3FXRitBqxiX1nKhXpHAZsMciLq8V6RjsNAQwdsdMFvSlVK/7XA
t3FaoJoAsncM1Q9x5+3V0Ww68/eIFmb1zuUFljQJKprrX88XypNDvjYNby6vw/Pb0rwert/En
mZ+AW4OZPnTPI89ZPmVMLuayrD2cE86Z/il8b+gw3r3+1nKatmIkjn2so1d01QraTlMqVSsbx
NrRFi9wrf+M7Q==
  name: user1
 - key: ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAklOUpkDHrfHY17SbrmTIpNLTGK9Tjom/BWDSU
GPl+nafzlHDTYW7hdI4yZ5ew18JH4JW9jbhUFrviQzM7xlELEVf4h9lFX5QVkbPppSwg0cda3
Pbv7kOdJ/MTyBlWXFCR+HAo3FXRitBqxiX1nKhXpHAZsMciLq8V6RjsNAQwdsdMFvSlVK/7XA
t3FaoJoAsncM1Q9x5+3V0Ww68/eIFmb1zuUFljQJKprrX88XypNDvjYNby6vw/Pb0rwert/En
mZ+AW4OZPnTPI89ZPmVMLuayrD2cE86Z/il8b+gw3r3+1nKatmIkjn2so1d01QraTlMqVSsbx
NrRFiefwwefew4w223e3e==
  name: user1
 - key: ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAklOUpkDHrfHY17SbrmTIpNLTGK9Tjom/BWDSU
GPl+nafzlHDTYW7hdI4yZ5ew18JH4JW9jbhUFrviQzM7xlELEVf4h9lFX5QVkbPppSwg0cda3
Pbv7kOdJ/MTyBlWXFCR+HAo3FXRitBqxiX1nKhXpHAZsMciLq8V6RjsNAQwdsdMFvSlVK/7XA
t3FaoJoAsncM1Q9x5+3V0Ww68/eIFmb1zuUFljQJKprrX88XypNDvjYNby6vw/Pb0rwert/En
mZ+AW4OZPnTPI89ZPmVMLuayrD2cE86Z/il8b+gw3r3+1nKatmIku3hrbfwejw4ur4hfjewf4wkjr3434==  
  name: user2
 - key: ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAklOUpkDHrfHY17SbrmTIpNLTGK9Tjom/BWDSU
GPl+nafzlHDTYW7hdI4yZ5ew18JH4JW9jbhUFrviQzM7xlELEVf4h9lFX5QVkbPppSwg0cda3
Pbv7kOdJ/MTyBlWXFCR+HAo3FXRitBqxiX1nKhXpHAZsMciLq8V6RjsNAQwdsdMFvSlVK/7XA
t3FaoJoAsncM1Q9x5+3V0Ww68/eIFmb1zuUFljQJKprrX88XypNDvjYNby6vw/Pb0rwert/En
mZ+AW4OZPnTPI89ZPmVMLuayrD2cE86Z/il8b+gw3r3+1nKatmIkjn2so1d01Qrao3rj32hirbk2jewf239r232e3==
  name: user2

My goal is to remove the keys from the server for the users whenever the key is removed from the YAML file. I tried exclusive but it is not working as it is not loop aware. Here is the snippet of my code:

   - name: Set authorized key for user ubuntu copying it from current user
     become: yes
     authorized_key:
       user: "{{ item.name }}"
       state: present
       key: "{{ item.key }}"
       exclusive: True
     with_items:
       - "{{ clients }}"

I tried the following approach but it is only working for single user and not for multiple user because it is just concatenating both keys and adding and removing it for both user. I want that it should add and remove the keys uniquely for both the user.

  - name: lookup ssh pubkeys from keyfiles and create ssh_pubkeys_list
    set_fact:
      ssh_keys: "{{ item.key }}"
    register: ssh_pubkeys_results_list
    with_items:
      - "{{ clients }}"
  
  - name: iterate over ssh_pubkeys_list and join into a string
    set_fact:
      ssh_pubkeys_string: "{{ ssh_pubkeys_results_list.results | map(attribute='ansible_facts.ssh_keys') | join('\n')}}"

  - name: lookup ssh pubkeys from name and create ssh_pubkeys_list
    set_fact:
      ssh_keys: "{{ item.name }}"
    register: ssh_pubkeys_results
    with_items:
      - "{{ clients }}"

  - name: Set authorized key for user ubuntu copying it from current user
    become: yes
    authorized_key:
      user: "{{ item.name }}"
      state: present
      key: "{{ ssh_pubkeys_string }}"
      exclusive: True
    with_items:
      - "{{ clients }}"

Any help would be greatly appreciated. Thanks

1

There are 1 answers

0
Vladimir Botka On BEST ANSWER

1) Manage users in the list only

Group the keys by names. Declare the variable

client_groups: "{{ client|groupby('name') }}"

Iterate the list

    - authorized_key:
        user: "{{ item.0 }}"
        key: "{{ item.1|map(attribute='key')|join('\n') }}"
        exclusive: true
      loop: "{{ client_groups }}"

Example of a complete playbook for testing

Given the authorized keys

shell> ssh admin@test_11 sudo cat /home/alice/.ssh/authorized_keys
ssh-rsa key1 
ssh-rsa key2 
ssh-rsa key3 
shell> ssh admin@test_11 sudo cat /home/bob/.ssh/authorized_keys
ssh-rsa key4 
ssh-rsa key5 
ssh-rsa key6

The playbook

shell> cat pb.yml
- hosts: test_11

  vars:

    client:
      - {name: alice, key: ssh-rsa key1}
      - {name: alice, key: ssh-rsa key2}
      - {name: bob, key: ssh-rsa key4}
      - {name: bob, key: ssh-rsa key5}
    client_groups: "{{ client|groupby('name') }}"

  tasks:

    - block:
        - debug:
            var: client_groups
        - debug:
            msg: |
              user: "{{ item.0 }}"
              key: "{{ item.1|map(attribute='key')|join('\n') }}"
          loop: "{{ client_groups }}"
      when: debug|d(false)|bool

    - authorized_key:
        user: "{{ item.0 }}"
        key: "{{ item.1|map(attribute='key')|join('\n') }}"
        exclusive: true
      loop: "{{ client_groups }}"

gives

shell> ansible-playbook pb.yml -e debug=true -CD

PLAY [test_11] *******************************************************************************

TASK [debug] *********************************************************************************
ok: [test_11] => 
  client_groups:
  - - alice
    - - key: ssh-rsa key1
        name: alice
      - key: ssh-rsa key2
        name: alice
  - - bob
    - - key: ssh-rsa key4
        name: bob
      - key: ssh-rsa key5
        name: bob

TASK [debug] *********************************************************************************
ok: [test_11] => (item=['alice', [{'name': 'alice', 'key': 'ssh-rsa key1'}, {'name': 'alice', 'key': 'ssh-rsa key2'}]]) => 
  msg: |-
    user: "alice"
    key: "ssh-rsa key1\nssh-rsa key2"
ok: [test_11] => (item=['bob', [{'name': 'bob', 'key': 'ssh-rsa key4'}, {'name': 'bob', 'key': 'ssh-rsa key5'}]]) => 
  msg: |-
    user: "bob"
    key: "ssh-rsa key4\nssh-rsa key5"

TASK [authorized_key] ************************************************************************
--- before: /home/alice/.ssh/authorized_keys
+++ after: /home/alice/.ssh/authorized_keys
@@ -1,3 +1,2 @@
 ssh-rsa key1 
 ssh-rsa key2 
-ssh-rsa key3 

changed: [test_11] => (item=['alice', [{'name': 'alice', 'key': 'ssh-rsa key1'}, {'name': 'alice', 'key': 'ssh-rsa key2'}]])
--- before: /home/bob/.ssh/authorized_keys
+++ after: /home/bob/.ssh/authorized_keys
@@ -1,3 +1,2 @@
 ssh-rsa key4 
 ssh-rsa key5 
-ssh-rsa key6 

changed: [test_11] => (item=['bob', [{'name': 'bob', 'key': 'ssh-rsa key4'}, {'name': 'bob', 'key': 'ssh-rsa key5'}]])

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

2) Manage all users

Get the database

    - getent:
        database: passwd

Select the users you want to manage. For example by the login shell. Declare the variables

shells: [/bin/csh, /bin/sh, /bin/bash]
users: "{{ getent_passwd|dict2items|
                         selectattr('value.5', 'in', shells)|
                         map(attribute='key')|list }}"

Create a dictionary of the keys. Declare the variables

client_groups: "{{ client|groupby('name') }}"
client_nmes: "{{ client_groups|map('first')|list }}"
client_keys: "{{ client_groups|map('last')|
                               map('map', attribute='key')|list }}"
client_dict: "{{ dict(client_nmes|zip(client_keys)) }}"

Iterate the list of the users

    - authorized_key:
        user: "{{ item }}"
        key: "{{ client_dict[item]|d([])|join('\n') }}"
        exclusive: true
      loop: "{{ users }}"

Example of a complete playbook for testing

- hosts: test_11

  vars:

    client:
      - {name: alice, key: ssh-rsa key1}
      - {name: alice, key: ssh-rsa key2}
      - {name: bob, key: ssh-rsa key4}
      - {name: bob, key: ssh-rsa key5}

    client_groups: "{{ client|groupby('name') }}"
    client_nmes: "{{ client_groups|map('first')|list }}"
    client_keys: "{{ client_groups|map('last')|
                                   map('map', attribute='key')|list }}"
    client_dict: "{{ dict(client_nmes|zip(client_keys)) }}"

    shells: [/bin/csh, /bin/sh, /bin/bash]
    users: "{{ getent_passwd|dict2items|
                             selectattr('value.5', 'in', shells)|
                             map(attribute='key')|list }}"

  tasks:

    - getent:
        database: passwd

    - block:
        - debug:
            var: client_dict
        - debug:
            var: users
        - debug:
            msg: |
              user: "{{ item }}"
              key: "{{ client_dict[item]|d([])|join('\n') }}"
          loop: "{{ users }}"
      when: debug|d(false)|bool

    - authorized_key:
        user: "{{ item }}"
        key: "{{ client_dict[item]|d([])|join('\n') }}"
        exclusive: true
      loop: "{{ users }}"