ansible / awx

AWX provides a web-based user interface, REST API, and task engine built on top of Ansible. It is one of the upstream projects for Red Hat Ansible Automation Platform.
Other
13.91k stars 3.4k forks source link

vault passwords are exposed to all commands run by the playbook #13970

Open azrdev opened 1 year ago

azrdev commented 1 year ago

Please confirm the following

Bug Summary

We're looking again at jinja-controlled vault decryption (instead of relying on the yaml parser invoking decrypt):

We noticed that even though the unvault filter cannot use them, vault passwords are in fact exposed to processes spawned by ansible, i.e. command and shell modules and presumably every command called by any other module.

I understand this is a different attack surface than exposing vault pw as ansible variable to python plugins, I'm still wondering if that's intentional and consistent.

AWX version

22.1.0

Select the relevant components

Installation method

kubernetes

Modifications

no

Ansible version

No response

Operating system

No response

Web browser

No response

Steps to reproduce

Put a vaulted file (echo "foo: bar">vaulted.yml && ansible-vault encrypt vaulted.yml) and the following playbook in a project:

# playbook
---
- hosts: localhost
  gather_facts: false
  tasks:
    - command:
        cmd: ansible-vault view vaulted.yml

Run without vault credential:

No config file found; using defaults
PLAY [localhost] ***************************************************************
TASK [command] *****************************************************************
Vault password: 
fatal: [localhost]: FAILED! => {"changed": true, "cmd": ["ansible-vault", "view", "vaulted.yml"], "delta": "0:00:00.549177", "end": "2023-05-09 12:40:58.076811", "msg": "non-zero return code", "rc": 1, "start": "2023-05-09 12:40:57.527634", "stderr": "\u001b[1;35m[WARNING]: Error in vault password prompt (default): Invalid vault password was\u001b[0m\n\u001b[1;35mprovided\u001b[0m\n\u001b[0;31mERROR! Invalid vault password was provided\u001b[0m", "stderr_lines": ["\u001b[1;35m[WARNING]: Error in vault password prompt (default): Invalid vault password was\u001b[0m", "\u001b[1;35mprovided\u001b[0m", "\u001b[0;31mERROR! Invalid vault password was provided\u001b[0m"], "stdout": "", "stdout_lines": []}
PLAY RECAP *********************************************************************
localhost                  : ok=0    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   

Run with vault credential:

Expected results

the same as without credential

Actual results

No config file found; using defaults
Vault password: 
PLAY [localhost] ***************************************************************
TASK [command] *****************************************************************
Vault password: 
changed: [localhost] => {"changed": true, "cmd": ["ansible-vault", "view", "vaulted.yml"], "delta": "0:00:00.581329", "end": "2023-05-09 12:41:45.513627", "msg": "", "rc": 0, "start": "2023-05-09 12:41:44.932298", "stderr": "", "stderr_lines": [], "stdout": "foo: bar", "stdout_lines": ["foo: bar"]}
PLAY RECAP *********************************************************************
localhost                  : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

Additional information

No response

shanemcd commented 1 year ago

I'll do my best to describe what AWX does.

We prepare a dictionary of "prompts" here:

https://github.com/ansible/awx/blob/92dce854684276ff2459e0ae972c0c637814f322/awx/main/tasks/jobs.py#L1055-L1060

These are the passed in to ansible-runner:

https://github.com/ansible/awx/blob/92dce854684276ff2459e0ae972c0c637814f322/awx/main/tasks/jobs.py#L541-L552

Which uses pexpect to watch for these patterns and simulates a user typing:

https://github.com/ansible/ansible-runner/blob/a8eb404af71588091234df59f0a8e0a98ccb7441/src/ansible_runner/runner.py#L317

What isn't clear to us is how your cmd: ansible-vault view vaulted.yml is obtaining the password. That task should just hang forever since pexpect won't see the output until the task completes. Are you using vault_password_file or some other way of getting that password into the environment?

azrdev commented 1 year ago

This is a fresh install of AWX without customization, and the Project repo contains nothing but the two files. So no, I'm not doing any configuration and you should be able to reproduce the example.

shanemcd commented 1 year ago

@azrdev I will give it a shot, but in the future... if you think something is a security-related issue, please do not open an issue that is visible to the public. Most projects will have a security policy listed somewhere in the repo and/or in GitHub like you see below. We also opened https://github.com/ansible/awx/pull/13971 yesterday, which was in response to this issue being created.

awx 2023-05-10 06-44-52
azrdev commented 1 year ago

@shanemcd thank you for thinking about it! Indeed I pondered reporting it as a security issue, but given it needs access to the playbook & module code I deemed it more a bug than a vulnerability.

BartOpitz commented 2 months ago

Hmm, but is it really a bug? I mean, having vault-encrypted variables in inventory also does not prevent anybody to use ansible.builtin.debug to show their plaintext contant. If someone runs ansible-vault view command, and such code is commited (and hopefully reviewed) isn't it what it should actually do - render unencrypted contant of the file?

There might also be a slight risk, that someone emulates the vault prompt and gets the vault secret pasted in. But again - someone does such code review, right? I assume, if code goes to a branch that a job is running against, then it is meant to be executed and has been approved/reviewed. And therefore it works as expected from my point of view.