ansiblessh-keysansible-templatemultiple-usersauthorized-keys

Remove authorized_keys using Ansible for multiple keys and multiple users


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


Solution

  • 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 }}"