openssh_keypair module fails to read private key if mode is set to "0644"
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#
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.
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.
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.
We could switch the default backend for 3.0.0 (see #559).