understanding fetch module's security implications

Hi,

I consider the ansible host as trusted, the target server that is
managed with ansible is considered less trusted (it might start attacks
against the ansible host).

Does ansible's security design match that threat model in general?

Given the following fetch module example:

- fetch: src={{ DataDir }}/{{ item[0] }}/{{ item[1] }}
    dest={{ foo_dir }}/{{ item[0] }}/{{ item[1] }}.untrustedremotefile
    flat=yes
  with_nested:
   - "{{ ansible_all_ipv4_addresses }}"
   - [ 'a', 'b' ]
become: yes

I assume the target host trying to attack the ansible hosts can provide
arbitrary strings (i.e "../../../../")
as one of its IP addresses to trick the ansible host to write to
arbitrary locations (flat=yes) , but is it at least safe to assume that
the static strings defined in the playbook/role ('a', 'b' and file
suffix '.untrustedremotefile') can not be altered by the target server
or is the entire dest= path composed/generated remotely and transmitted
back to the ansible host and therefore considered untrusted entirely?

If static strings can not be altered by the remote (attacking) server
the remote server would only be able to override files named
a/b.untrustedremotefile in arbitrary locations but not for example
~/.ssh/authorized_keys.

Another question that came up while writing this email:
Is 'become: yes' in the fetch context limited to the target server or
will ansible try to escalate its privileges on the ansible host as well
(localhost)?

thanks!

- All templating (variable substitution) is done on the 'controller',
it makes no sense to send that data, plus the template to the targets
and then have to install jinja2 to run it, get it back and then update
the task, copy the updated module and run it remotely ...

- the fact variables (what ansible_all_ipv4_addresses is) are
sanitized against template injection but not verified against
directories, most variables can legitimately return such structures.
The attacker would have to know your exact playbook to do this, as you
already state, since they cannot update the ['a', 'b'] list, it
minimizes the issue.

- become executes privilege escalation on the target, the current
ansible process does not elevate privilege when the target is another
server, even when targeting localhost, only the forked task will get
the changed privilege, as intended.

Ansible mostly works using by ssh and sudo like you would in the
command line, you can gather data from other machines and use it, but
only send the minimal data to achieve a task.

thanks for your answer!

- the fact variables (what ansible_all_ipv4_addresses is) are
sanitized against template injection but not verified against
directories, most variables can legitimately return such structures.
The attacker would have to know your exact playbook to do this, as you
already state, since they cannot update the ['a', 'b'] list, it
minimizes the issue.

Would it make sense to add a check to ansible that verifies elements of
ansible_all_ipv4_addresses against an IP regex? (If I understood you
correctly that is not the case yet.)

It doesn't help in my specific case since an attacker will still be able
to override files it should not but maybe as a general precaution it
doesn't hurt I guess (I'll have to remove 'flat=yes' in my case.)

I tried to address this security problem (which turned out to be more
severe then I initially thought, because it probably allowed a relay to
steal keys) by prefixing the fact with a var that can not be manipulated
by the target server ( inventory_hostname ).

https://github.com/nusenu/ansible-relayor/commit/d0a969fabe731e8f20dad074ec772ec12faaab7b#diff-0ab2a3945984f21ca36ce37d625d716cL147

Prefixing with trusted vars is still open to directory traversal. In
most cases you can setup your systems so the variable data is set by
the 'master' always and not derived from the target, which avoids this
issue altogether.

This is only a problem if you rely on untrusted data to access
secrets, there is not much Ansible can do in that case. I would advise
creating assert/fail tasks to validate the data before using it.

Brian Coca:

Prefixing with trusted vars is still open to directory traversal.

oh that is true, thanks for pointing that out!
(example: 1.1.1.1/../foo-2.2.2.2)

In
most cases you can setup your systems so the variable data is set by
the 'master' always and not derived from the target, which avoids this
issue altogether.

I agree, but in my case I use the IP address as unique identifier and
only the target knows its IPs.

This is only a problem if you rely on untrusted data to access
secrets, there is not much Ansible can do in that case. I would advise
creating assert/fail tasks to validate the data before using it.

I opened a github issue to validate all elements of
ansible_all_ipv4_addresses
by default against an IP address regex:

https://github.com/ansible/ansible/issues/14350

Brian Coca:

Prefixing with trusted vars is still open to directory traversal.

I hope the ipv4('address') filter method is safe?

https://github.com/nusenu/ansible-relayor/commit/d2c2108a8850241369aa8867faf4ac53246171d5