community.crypto icon indicating copy to clipboard operation
community.crypto copied to clipboard

openssh_keypair module fails to read private key if mode is set to "0644"

Open noonedeadpunk opened this issue 3 years ago • 5 comments

SUMMARY

In case you chmod ssh private key to mode 644 or define mode to '0644' for the module, it fails to read the private key

ISSUE TYPE
  • Bug Report
COMPONENT NAME

openssh_keypair

ANSIBLE VERSION
root@aio1:/home/ubuntu# ansible --version
ansible [core 2.13.4]
  config file = None
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /opt/ansible-runtime/lib/python3.10/site-packages/ansible
  ansible collection location = /etc/ansible
  executable location = /opt/ansible-runtime/bin/ansible
  python version = 3.10.6 (main, Nov 14 2022, 16:10:14) [GCC 11.3.0]
  jinja version = 3.1.2
  libyaml = True
root@aio1:/home/ubuntu# 
COLLECTION VERSION
root@aio1:/home/ubuntu# ansible-galaxy collection list community.crypto 

# /etc/ansible/ansible_collections
Collection       Version
---------------- -------
community.crypto 2.7.0  
root@aio1:/home/ubuntu#
CONFIGURATION
root@aio1:/home/ubuntu# ansible-config dump --only-changed
COLLECTIONS_PATHS(env: ANSIBLE_COLLECTIONS_PATH) = ['/etc/ansible']
root@aio1:/home/ubuntu# 
OS / ENVIRONMENT

Ubuntu 22.04, Python 3.10

STEPS TO REPRODUCE

Assuming you have a task:

---
- hosts: localhost
  tasks:
    - name: generate keypair
      community.crypto.openssh_keypair:
        comment: "{{ kp.comment | default(omit) }}"
        passphrase: "{{ kp.passphrase | default(omit) }}"
        regenerate: "{{ kp.regenerate | default(omit) }}"
        size: "{{ kp.size | default(omit) }}"
        type: "{{ kp.type | default(omit) }}"
        path: "{{ kp.path }}"
        mode: "{{ kp.mode | default(omit) }}"
      loop:
        - path: /home/ubuntu/Ansible-SSH-Signing-Key
          type: rsa
          mode: '0644'
      loop_control:
        loop_var: kp

Then run the task couple of times

EXPECTED RESULTS

Module passes in idempotent manner, preserving mode 644 for files and being able to re-run without failures.

ACTUAL RESULTS
root@aio1:/home/ubuntu# ansible-playbook test.yml 
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

PLAY [localhost] ***************************************************************************************************************************************************************************************************************************

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

TASK [generate keypair] ********************************************************************************************************************************************************************************************************************
changed: [localhost] => (item={'path': '/home/ubuntu/Ansible-SSH-Signing-Key', 'type': 'rsa', 'mode': '0644'})

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


EXIT NOTICE [Playbook execution success] **************************************
===============================================================================
root@aio1:/home/ubuntu# stat /home/ubuntu/Ansible-SSH-Signing-Key
  File: /home/ubuntu/Ansible-SSH-Signing-Key
  Size: 3357            Blocks: 8          IO Block: 4096   regular file
Device: fc01h/64513d    Inode: 259916      Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2023-01-18 18:10:13.771070714 +0000
Modify: 2023-01-18 18:10:13.735069467 +0000
Change: 2023-01-18 18:10:13.771070714 +0000
 Birth: 2023-01-18 18:10:13.735069467 +0000
root@aio1:/home/ubuntu# ansible-playbook test.yml 
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

PLAY [localhost] ***************************************************************************************************************************************************************************************************************************

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

TASK [generate keypair] ********************************************************************************************************************************************************************************************************************
failed: [localhost] (item={'path': '/home/ubuntu/Ansible-SSH-Signing-Key', 'type': 'rsa', 'mode': '0644'}) => {"ansible_loop_var": "kp", "changed": false, "kp": {"mode": "0644", "path": "/home/ubuntu/Ansible-SSH-Signing-Key", "type": "rsa"}, "msg": "Unable to read the key. The key is protected with a passphrase or broken. Will not proceed. To force regeneration, call the module with `generate` set to `full_idempotence` or `always`, or with `force=true`."}

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


EXIT NOTICE [Playbook execution failure] **************************************
===============================================================================
root@aio1:/home/ubuntu# chmod 600 /home/ubuntu/Ansible-SSH-Signing-Key
root@aio1:/home/ubuntu# stat /home/ubuntu/Ansible-SSH-Signing-Key
  File: /home/ubuntu/Ansible-SSH-Signing-Key
  Size: 3357            Blocks: 8          IO Block: 4096   regular file
Device: fc01h/64513d    Inode: 259916      Links: 1
Access: (0600/-rw-------)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2023-01-18 18:10:29.295608544 +0000
Modify: 2023-01-18 18:10:13.735069467 +0000
Change: 2023-01-18 18:10:49.992325558 +0000
 Birth: 2023-01-18 18:10:13.735069467 +0000
root@aio1:/home/ubuntu# ansible-playbook test.yml 
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

PLAY [localhost] ***************************************************************************************************************************************************************************************************************************

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

TASK [generate keypair] ********************************************************************************************************************************************************************************************************************
changed: [localhost] => (item={'path': '/home/ubuntu/Ansible-SSH-Signing-Key', 'type': 'rsa', 'mode': '0644'})

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


EXIT NOTICE [Playbook execution success] **************************************
===============================================================================
root@aio1:/home/ubuntu# stat /home/ubuntu/Ansible-SSH-Signing-Key
  File: /home/ubuntu/Ansible-SSH-Signing-Key
  Size: 3357            Blocks: 8          IO Block: 4096   regular file
Device: fc01h/64513d    Inode: 259916      Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2023-01-18 18:11:02.860771371 +0000
Modify: 2023-01-18 18:10:13.735069467 +0000
Change: 2023-01-18 18:11:02.904772896 +0000
 Birth: 2023-01-18 18:10:13.735069467 +0000
root@aio1:/home/ubuntu# ansible-playbook test.yml 
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

PLAY [localhost] ***************************************************************************************************************************************************************************************************************************

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

TASK [generate keypair] ********************************************************************************************************************************************************************************************************************
failed: [localhost] (item={'path': '/home/ubuntu/Ansible-SSH-Signing-Key', 'type': 'rsa', 'mode': '0644'}) => {"ansible_loop_var": "kp", "changed": false, "kp": {"mode": "0644", "path": "/home/ubuntu/Ansible-SSH-Signing-Key", "type": "rsa"}, "msg": "Unable to read the key. The key is protected with a passphrase or broken. Will not proceed. To force regeneration, call the module with `generate` set to `full_idempotence` or `always`, or with `force=true`."}

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


EXIT NOTICE [Playbook execution failure] **************************************
===============================================================================
root@aio1:/home/ubuntu#

noonedeadpunk avatar Jan 18 '23 18:01 noonedeadpunk

This is the nature of the ssh-keygen binary. It refuses to load world readable private keys with a message like:

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0644 for 'test' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
Load key "test": bad permissions

using backend: cryptography should permit reading keys with perms like 0644.

Ajpantuso avatar Jan 18 '23 18:01 Ajpantuso

Yes, that helps, thank you.

I wonder if it's worth to fix documentation to mention usage of cryptography backend with mode, or make auto use cryptograpy not only when passphrase is set, but also switch to it when mode defined.

Or well, is there any good reason why cryptography is not picked by default and opensshbin is first choice for auto backend?

As even if mode is not set for module, but attributes for file were somehow changed (ie keypair is stored in git which doesn't preserve attributes) error that key can't be decrypted is very confusing.

noonedeadpunk avatar Jan 18 '23 19:01 noonedeadpunk

cryptography and the concept of backends were added to this module more recently so we default to whatever options will not break older playbooks which have not adapted to using cryptography.

Ajpantuso avatar Jan 18 '23 19:01 Ajpantuso

We could switch the default backend for 3.0.0 (see #559).

felixfontein avatar Jan 18 '23 20:01 felixfontein