ansible.posix icon indicating copy to clipboard operation
ansible.posix copied to clipboard

synchronize doesn't parse variables for ansible_user

Open Vanav opened this issue 3 years ago • 9 comments

SUMMARY

When using variable in ansible_user, and the default value set_remote_user=true, then we got error:

group_vars/all/default.yml

deploy_user: ubuntu
ansible_user: '{{ deploy_user }}'

task.yml

- name: ANSISTRANO | RSYNC DIRECT | Rsync application files directly to remote release path
  synchronize:
    src: "{{ ansistrano_deploy_from }}"
    dest: "{{ ansistrano_release_path.stdout }}"
    set_remote_user: yes
    recursive: yes
    delete: yes
    archive: yes
    compress: yes
    use_ssh_args: "{{ ansistrano_rsync_use_ssh_args | default(omit) }}"
    rsync_opts: "{{ ansistrano_rsync_extra_params | default(omit) }}"
    rsync_path: "{{ ansistrano_rsync_path | default(omit) }}"

Output (added line breaks):

fatal: [dev_host_site]: FAILED! => {"changed": false, "cmd": 
"/usr/bin/rsync --delay-updates -F --compress --delete-after --archive 
--rsh='/usr/bin/ssh -S none -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null' 
--no-perms --no-g --chmod=ugo=rwX --omit-dir-times --exclude=/infra/vault_pass.txt 
--quiet --out-format='<<CHANGED>>%i %n%L' 
/srv/Projects/site/infra/.. 
{{ deploy_user }}@84.84.84.84:/srv/site-dev/releases/20210930114615Z", 
"msg": "Warning: Permanently added '84.84.84.84' (ECDSA) to the list of known hosts.\r\n
}}@84.84.84.84: Permission denied (publickey).\r\n
rsync: connection unexpectedly closed (0 bytes received so far) [sender]\n
rsync error: unexplained error (code 255) at io.c(235) [sender=3.1.3]\n", "rc": 255}

As you can see, text {{ deploy_user }} inserted as is, even with spaces, and so SSH login }} is used.

ISSUE TYPE
  • Bug Report
COMPONENT NAME

synchronize

ANSIBLE VERSION
ansible [core 2.11.5] 
  config file = /srv/Projects/Examples/ansistrano-deploy/ansible.cfg
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /opt/pipx/venvs/ansible/lib/python3.9/site-packages/ansible
  ansible collection location = /root/.ansible/collections:/usr/share/ansible/collections
  executable location = /opt/pipx/bin/ansible
  python version = 3.9.5 (default, May 19 2021, 11:32:47) [GCC 9.3.0]
  jinja version = 3.0.1
  libyaml = True
COLLECTION VERSION
Collection    Version
------------- -------
ansible.posix 1.3.0  
CONFIGURATION
ANSIBLE_PIPELINING(/srv/Projects/site/infra/ansible.cfg) = True
DEFAULT_FORKS(/srv/Projects/site/infra/ansible.cfg) = 1
DEFAULT_HOST_LIST(/srv/Projects/site/infra/ansible.cfg) = ['/srv/Projects/site/infra/hosts.yml']
DEFAULT_JINJA2_NATIVE(/srv/Projects/site/infra/ansible.cfg) = True
DEFAULT_MANAGED_STR(/srv/Projects/site/infra/ansible.cfg) = This file is managed by Ansible, changes will be lost. Source: {file}
DEFAULT_VAULT_PASSWORD_FILE(/srv/Projects/site/infra/ansible.cfg) = /srv/Projects/site/infra/vault_pass.txt
DIFF_ALWAYS(/srv/Projects/site/infra/ansible.cfg) = True
INTERPRETER_PYTHON(/srv/Projects/site/infra/ansible.cfg) = auto
OS / ENVIRONMENT

Ubuntu 20.04

Vanav avatar Sep 30 '21 12:09 Vanav

I believe that this is because synchronize uses an action plugin, and that's where the variable is retrieved, and it ends up being an instance of this issue:

  • https://github.com/ansible/ansible/issues/73268

See also:

  • https://github.com/ansible/ansible/pull/58288

So in fact, I don't know that it is possible (or recommended) to solve this within the synchronize action plugin itself.


The way I would recommend fixing this is to have an explicit option in the plugin for setting the user. It can still default to reading the user from ansible_user, but being able to set it as an explicit option offers better control. If it had that option (I will call it remote_user), then you could invoke it this way:

- name: ANSISTRANO | RSYNC DIRECT | Rsync application files directly to remote release path
  synchronize:
    src: "{{ ansistrano_deploy_from }}"
    dest: "{{ ansistrano_release_path.stdout }}"
    set_remote_user: yes
    recursive: yes
    delete: yes
    archive: yes
    compress: yes
    use_ssh_args: "{{ ansistrano_rsync_use_ssh_args | default(omit) }}"
    rsync_opts: "{{ ansistrano_rsync_extra_params | default(omit) }}"
    rsync_path: "{{ ansistrano_rsync_path | default(omit) }}"
    remote_user: '{{ ansible_user }}'

(see last line)

If ansible_user is set to a static value (no Jinja) then this would be redundant, but in the example in the issue report, this will work, because the Jinja templating will be resolved before the value is passed into the action.


To workaround this issue now, with no changes, you can use set_fact:

- name: "Set the value of ansible_user"
  set_fact:
    ansible_user: '{{ ansible_user }}'

- name: ANSISTRANO | RSYNC DIRECT | Rsync application files directly to remote release path
  synchronize:
    src: "{{ ansistrano_deploy_from }}"
    dest: "{{ ansistrano_release_path.stdout }}"
    set_remote_user: yes
    recursive: yes
    delete: yes
    archive: yes
    compress: yes
    use_ssh_args: "{{ ansistrano_rsync_use_ssh_args | default(omit) }}"
    rsync_opts: "{{ ansistrano_rsync_extra_params | default(omit) }}"
    rsync_path: "{{ ansistrano_rsync_path | default(omit) }}"

But this is not desirable in all cases. You will be setting the precedence of that value differently than it was previously, so you may it find it has an unexpected value in other places where you tried to override it without using set_fact.

briantist avatar Oct 23 '21 13:10 briantist

@Vanav Thank you for reporting this. Is there any chance to try the workaround that @briantist provided ?

saito-hideki avatar Nov 08 '21 04:11 saito-hideki

For my use case I was able just to remove variable. But this is definitely unexpected behavior. Maybe it is also a security and data loss risk, because ansible_user contents is passed to rsync unquoted and as is:

/usr/bin/rsync /path/ {{ deploy_user }}@84.84.84.84:/path/

Vanav avatar Nov 08 '21 08:11 Vanav

I just ran into this in a playbook where I was setting ansible_user temporarily using vars: in a block: in my script to force synchronization to go to a specific user on the host.

In my case, the workaround by @briantist wouldn't work (as he notes, "not desirable in all cases"). Although the ability to set the remote_user explicitly as he suggests would solve my problem, by removing the need to set the ansible_user at all in that block.

My workaround right now is to set ansible_user to a static value, but that creates a situation where I need to manually set it in a couple of different places, differently from how I use it in any of the rest of the plays.

Here's an example of what I'm doing:

  - name: synchronize as {{bot_user}}
    vars:
      ansible_user: "{{ bot_user }}"
    block:    
      - name: synchronize test files
        synchronize:
          src: ~/MyData
          dest: "{{ bot_home }}/Development/Test/MapData"
          delete: yes
          owner: no
          group: no
          rsync_opts:
            - --prune-empty-dirs
            - --filter=. /tmp/files_to_copy

gaige avatar Dec 07 '21 11:12 gaige

This worked in ansible==4.3.0 (should be ansible.posix version 1.2.0) and stopped working some time after that.

Not sure if this helps, though.

danielpanteleit avatar Mar 03 '22 07:03 danielpanteleit

+1

juanvalino avatar Mar 08 '22 13:03 juanvalino

ansible.posix==1.2.0 is no viable option because #222. Desperatly need a fix. This and #222 force us to stay at ansible==2.10.7.

helmecke avatar Apr 25 '22 14:04 helmecke

Found it! Thank you @jovial. #344 fixes the issue for me. Testet with ansible==5.5.0.

helmecke avatar Apr 25 '22 15:04 helmecke