ansible-collections / community.crypto

The community.crypto collection for Ansible.
https://galaxy.ansible.com/ui/repo/published/community/crypto/
Other
96 stars 85 forks source link

openssh_keypair module always calls chattr after creating a file, causing exception on fs not supporting file attributes #416

Open klanip opened 2 years ago

klanip commented 2 years ago
SUMMARY

openssh_keypair module always calls chattr on file creation causing exception on filesystems not supporting attributes

ISSUE TYPE
COMPONENT NAME

openssh_keypair

ANSIBLE VERSION
 ansible [core 2.12.2]
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/home/klanip/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3.9/site-packages/ansible
  ansible collection location = /home/klanip/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/bin/ansible
  python version = 3.9.9 (main, Nov 19 2021, 00:00:00) [GCC 11.2.1 20210728 (Red Hat 11.2.1-1)]
  jinja version = 2.11.3
  libyaml = True
COLLECTION VERSION
# /home/klanip/.ansible/collections/ansible_collections
Collection       Version
---------------- -------
community.crypto 2.2.3  
CONFIGURATION
OS / ENVIRONMENT

Fedora 34

STEPS TO REPRODUCE

1) make sure /tmp is tmpfs filesystem type (findmnt /tmp -o fstype) 2) make sure /tmp/id_rsa_test and /tmp/id_rsa_test.pub don't exist (rm /tmp/id_rsa_test*) 3) use openssh_keypair module to generate a ssh key with path "/tmp/id_rsa_test" (see example playbook below)

- hosts: localhost
  connection: local
  gather_facts: no
  vars:
    ssh_key_path: "/tmp/id_rsa_test" #must be tmpfs
  tasks:
    - name: Generate temporary ssh keys
      become: false
      openssh_keypair:
        path: "{{ ssh_key_path }}"
      delegate_to: localhost
EXPECTED RESULTS

Playbook generates public and private ssh key and exits normally, without attempting to modify file attributes when not asked to do so by attributes parameter.

ACTUAL RESULTS

task "Generate temporary ssh keys" attemtps to call chattr (note that the parameter attributes is not specified in the task) and fails, because tmpfs filesystem doesn't support attributes.

This only occurs if the files didn't exist previously. It doesn't happen on files that already exist.

Apparently the error occurs after a file is created, so on 1st playbook run, /tmp/id_rsa_test is created, then task fails on chattr. On second subsequent run, /tmp/id_rsa_test.pub is created and task again fails on chattr. On third and all subsequent runs, the playbook behaves as expected (until the keyfiles are deleted again by a reboot for example)

The full traceback is:
Traceback (most recent call last):
  File "/tmp/ansible_openssh_keypair_payload_bdrhitkj/ansible_openssh_keypair_payload.zip/ansible/module_utils/basic.py", line 1008, in set_attributes_if_different
    raise Exception("Error while setting attributes: %s" % (out + err))
Exception: Error while setting attributes: /usr/bin/chattr: Inappropriate ioctl for device while reading flags on /tmp/id_rsa_test

fatal: [localhost]: FAILED! => {
    "changed": false,
    "details": "Error while setting attributes: /usr/bin/chattr: Inappropriate ioctl for device while reading flags on /tmp/id_rsa_test\n",
    "gid": 1000,
    "group": "klanip",
    "invocation": {
        "module_args": {
            "attributes": null,
            "backend": "auto",
            "comment": null,
            "force": false,
            "group": null,
            "mode": null,
            "owner": null,
            "passphrase": null,
            "path": "/tmp/id_rsa_test",
            "private_key_format": "auto",
            "regenerate": "partial_idempotence",
            "selevel": null,
            "serole": null,
            "setype": null,
            "seuser": null,
            "size": null,
            "state": "present",
            "type": "rsa",
            "unsafe_writes": false
        }
    },
    "mode": "0600",
    "msg": "chattr failed",
    "owner": "klanip",
    "path": "/tmp/id_rsa_test",
    "secontext": "unconfined_u:object_r:user_home_t:s0",
    "size": 3357,
    "state": "file",
    "uid": 1000
}
WORKAROUND

ensure files exist by using ansible.builtin.file module with state=touch parameter

Ajpantuso commented 2 years ago

I cannot reproduce this using a tmpfs mount except when explicitly setting an attribute.

Basically the exception returned should not be possible with the module_args that task was provided. The code attempting to set these attributes comes from ansible-core and returns early if attributes was unset. (See here)

But, the following message makes me think you are not using the community.crypto version of openssh_keypair:

 File "/tmp/ansible_openssh_keypair_payload_bdrhitkj/ansible_openssh_keypair_payload.zip/ansible/module_utils/basic.py"

Can you run again with -vvvvv and inspect which module is being invoked for this task?

klanip commented 2 years ago

I've attached -vvvvv output as a file below: playbook_out.txt

Looking at the file it seemed like I was using the community.crypto version. To check further I tried deleting my $HOME/.ansible folder, ran playbook (failed, no openssh_keypair module), then ansible-galaxy collection install community.crypto and after running playbook, task failed at chattr as before.

My colleague with Arch says he encounters the same issue. However, I was unable to reproduce this bug on my personal computer that runs Fedora35 (as opposed to Fedora34 on my work machine where I do encounter the bug)

Given this bug seems to be so hard to pin down and the fact a very simple workaround exists, I wouldn't mind if you decided this isn't worth your time and closed this issue with could-not-reproduce or similar.

felixfontein commented 2 years ago

I think this is rather a bug in ansible-core than a bug in this collection. This is probably triggered by a module.preserved_copy() call (https://github.com/ansible-collections/community.crypto/blob/main/plugins/module_utils/openssh/backends/common.py#L145). I can also reproduce this issue (I'm using Arch as well), I'll try to debug it a bit more.

felixfontein commented 2 years ago

I created https://github.com/ansible/ansible/issues/77217 for this.

felixfontein commented 2 years ago

BTW, one workaround for this is to set Ansible's temp directory to /tmp as in https://devops.stackexchange.com/a/11699.

felixfontein commented 2 years ago

(This might have some security implications though, so be sure what you are doing. Don't sue me if something does wrong :) )

Ajpantuso commented 2 years ago

Ah, thanks @felixfontein! I will update the base class to give proper context to these types of errors for better traceability.

felixfontein commented 2 years ago

If anyone has an idea how to fix preserved_copy(), please comment in https://github.com/ansible/ansible/issues/77217.

felixfontein commented 2 years ago

This finally broke CI due to a change on how ansible-test from ansible-core devel runs the CI containers (with a proper tmpfs): https://github.com/ansible/ansible/commit/4187707f035a5dde9d02e99e5dec40d71b06d5d1

arodier commented 3 months ago

The work around mentioned above are all assuming the playbooks is run with root access, which is not always the case. For instance, I am generating SSH certificates on a system, to access a server, and I don't need to do this as root. chattr cannot be run by normal users, afaik.