SSH Known Hosts not working with ansible.posix.synchronize?

I need to sync files from a remote source to a remote destination server. I’ve chosen synchronize, but it is not happy, even though passwordless rsync is working correctly for this user. Should I be going a different direction?


    - name: Synchronization using rsync protocol (pull)
      ansible.posix.synchronize:
        mode: pull
        src: rsync://user@sourceserver/home/user/folder
        rsync_opts: "-e ssh" 
        dest: /home/user

Output:

TASK [Synchronization using rsync protocol (pull)] *****************************
task path: /runner/project/ansible/playbooks/demos/rsync.yml:8
[DEPRECATION WARNING]: The connection's stdin object is deprecated. Call 
display.prompt_until(msg) instead. This feature will be removed in version 
2.19. Deprecation warnings can be disabled by setting 
deprecation_warnings=False in ansible.cfg.
<targetserver> ESTABLISH LOCAL CONNECTION FOR USER: 1000840000
<targetserver> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /runner/.ansible/tmp/ansible-local-17rc8e5bm8 `"&& mkdir "` echo /runner/.ansible/tmp/ansible-local-17rc8e5bm8/ansible-tmp-1709053366.0132787-22-63571286671008 `" && echo ansible-tmp-1709053366.0132787-22-63571286671008="` echo /runner/.ansible/tmp/ansible-local-17rc8e5bm8/ansible-tmp-1709053366.0132787-22-63571286671008 `" ) && sleep 0'
Using module file /usr/share/ansible/collections/ansible_collections/ansible/posix/plugins/modules/synchronize.py
<targetserver> PUT /runner/.ansible/tmp/ansible-local-17rc8e5bm8/tmp9boasrlt TO /runner/.ansible/tmp/ansible-local-17rc8e5bm8/ansible-tmp-1709053366.0132787-22-63571286671008/AnsiballZ_synchronize.py
<targetserver> EXEC /bin/sh -c 'chmod u+x /runner/.ansible/tmp/ansible-local-17rc8e5bm8/ansible-tmp-1709053366.0132787-22-63571286671008/ /runner/.ansible/tmp/ansible-local-17rc8e5bm8/ansible-tmp-1709053366.0132787-22-63571286671008/AnsiballZ_synchronize.py && sleep 0'
<targetserver> EXEC /bin/sh -c '/usr/bin/python3 /runner/.ansible/tmp/ansible-local-17rc8e5bm8/ansible-tmp-1709053366.0132787-22-63571286671008/AnsiballZ_synchronize.py && sleep 0'
The authenticity of host 'sourceserver (10.xx.xx.xx)' can't be established.
ED25519 key fingerprint is SHA256:WmAVPxxxxxxxxxxxxxxxxxxxxxxxxxxxeLY.
This key is not known by any other names

In Linux, with ssh, you have to add the server to the known/safe hosts. From the server you are copying from, when you run an ssh command to the server you want to copy to manually, it should prompt you something about adding to safe hosts. then you type yes, and it won’t prompt again.

I would try that, then attempt the ansbile playbook again. If that works, then I am sure there is a way to add that host automatically, I just don’t happen to know it LOL

1 Like

Precisely why I am confused. I am able to successfully rsync from the remote server and have successfully added the server to my known hosts file. All the trusts are in place.

Well, for better or worse the command works perfectly with ansible.builtin.shell, so that’s the plan until I hear better. :slight_smile:

The /runner/… path makes me think you’re running this in an execution environment, so it’s running as … not you? That wouldn’t have access to your known_hosts file. When you run rsync under ansible.builtin.shell, are you delegating to a host, or setting the user somehow? How are you invoking this play?

1 Like

Thank you for the clarifying questions.

I’m running as “user” in all 3 cases, and in both AWX cases I’m delegating to the target server. If I log on to the target server as user via ssh, I can do the rsync with no issue. All trusts are there. When I run the playbook for rsync with from shell, I’m running with the “user” credential (in an execution engine, yes) and connecting to the target server as “user”, and the rsync runs at the target server, pulling from source. That works just fine.

When I try to run ansible.posix.synchronize, I assume (incorrectly?) Ansible is connecting to the target server, and from the target server it’s doing an rsync pull from the source server. If I’m wrong, and the AWX EE is attempting to fetch and cache from the source server and then copy from cache to the target server, then that fully explains my confusion, but creates new confusions for me.

What I want is for the target server to do a pull from the source server, and I don’t want the data to ever have to live on the AWX EE. Using the shell method, that’s what’s happening. I just thought that’s what synchronize should also do.

That explains it then. With ansible.posix.synchronize, one end is always the Ansible controller. As you’re designating mode: pull, it’s trying to pull from user@sourceserver:/home/user/folder to /home/user on the Ansible controller (which, as it’s an execution environment, probably doesn’t exist anyway).

The terminology is often a bit loose. In this case, the synopsis says, “It is run and originates on the local host where Ansible is being run.” That describes the Ansible controller, but it doesn’t actually say “Ansible controller”. Efforts are underway to clarify in a consistent way such statements. And, to be fair, you have to really want to read it another way to get it wrong in this case. But, yeah.

That same synopsis also says, “You still may need to call rsync directly via command or shell depending on your use case.” So don’t feel bad about going old school if ..synchronize doesn’t solve your particular problem.

Cheers.

2 Likes

Thanks a ton, Todd. This really helps. 2 things:

  1. Yes, I 100% really wanted to read the documentation another way. I mean, 100%. I have gigabytes of data to send, so I was searching for a solution to avoid waiting on it twice. Synchronize seemed like the right word and it was doing rsync which was the right tool. I mean, that’s what synchronize means, right? A thing that makes 2 data sets identical? I did not want 3 data sets, so, yeah, I read it with prejudice. To be fair to my reading, I did implement ansible.builtin.shell because of the recommendation in the documentation you quote.

  2. Correct me if I’m wrong. I’m a middling user, not in any way an expert. I believe the Ansible pattern is “all tasks run on the inventory host unless ‘delegate_to’ is explicitly set to ‘localhost’”. I jumped straight to the examples in the documentation, and they seemed to show the target server/inventory host pushing or pulling at will at the inventory host.

It seems one way to clarify this communication problem could be to force conformance to the Ansible pattern. Make the module actively require “delegate_to: localhost” in all cases. You might also require “rsync://localhost/path” for the local file system. That’s 100% clear. You have “delegate_to: delegate.host” on a couple of examples, but I sadly have no idea what delegate.host means and it’s not explained in the document. A quick Google of ‘ansible “delegate.host”’ is not exactly helpful.

Another way to clarify the communication problem could be to pair the examples. Right now, each example is a stand-alone. It looks for the world as if each example individually does a sync to/from ServerA from/to ServerB. If the examples were visibly shown as pairs required to work together to make a full synchronization happen, someone like me might see the problem and abandon the tool much earlier.

I believe that delegate_to: delegate.host is just showing that you can run it from a different server. So if you had a task where it was delegate_to: SERVERA, it would run the synchronize between SERVERA and the targeted host, instead of the Ansible Controller and the targeted host.

At least that is how I read the docs, I haven’t tested it. Currently I only use ansible.posix.synchronize to sync between directories on the same server.

Maybe test with delegate_to one server, target the other, and you may need to play with become_user if @utoddl is correct on the whole known_hosts file being user-specific, as I am not sure about that…

1 Like

I have just spent the last hour testing, and you read that right. delegate_to: does allow you to change the “local” end of the sync from the Ansible controller to another host.

Because of where my ansible run user’s keys are (or aren’t), I have not been able to make synchronize work when delegated to another host. I suspect it’s just a matter of working out users, keys, and how to tweak connection variables, but I ran out of brains and time and patience.

3 Likes

but I ran out of brains and time and patience.

Yeah that happens lol. I have a few tasks that are just using shell because it works, and ansible doesn’t have a module or a module that works quite the way I need it to…

Might be worth writing up a feature request for the module out on GitHub. I have had varying success - always get a response, but sometimes my requests just aren’t in scope or possible.

2 Likes

Kind of, mostly, but there are enough exceptions to warrant being wary. Perhaps there should be a standard way to document such exceptions, something that would stand out and not be easily missed. Ones that leap to mind are ansible.builtin.debug, ansible.builtin.set_fact, and all lookups.

[Something in the back of my head is shouting, “Bring up the difference between facts and host variables!”, but I can’t think of (a) a good segue or (b) what exactly distinguishes them that’s relevant in this context. If somebody knows what I’m thinking, please let me know!]

“Ran out of brains and time”. … Amen :slight_smile:
“Bring up the difference between facts and host variables!” … Yeah, I need every lesson. :slight_smile:

I have learned many things here, so thank you so much for your time.

Here’s my current state. This works at the cli on target pulling from source:

rsync -e ssh -avz user@sourceserver:/home/user/kevintest /appl/tmp/

This should theoretically work, if I force synchronize to run on target:

---
- hosts: all
  gather_facts: false

  tasks:

    - name: Synchronization using rsync protocol (pull)
      ansible.posix.synchronize:
        mode: pull
        src: rsync://user@sourceserver/home/user/kevintest
        rsync_opts: "-e ssh -avz" 
        dest: /appl/tmp
      delegate_to: "{{ ansible_host }}"

It fails with:

rsync: did not see server greeting
rsync error: error starting client-server protocol (code 5) at main.c(1656) [Receiver=3.1.2]

I believe the difference is because the synchronize method is looking for an rsync server, but the rsync method at the command line just connecting via ssh. I tried to force the ssh connection, but no dice. So, if I start the rsync service at the source server, the synchronize module should move my code as I would have expected on day 1. Now I have to decide whether that’s the way the company wants me to proceed.

This goes down as a huge win on the learning front, and maybe even one on the process front.

Thank you, again!

Try this instead:

   rsync_opts:
     - '--rsh=ssh'
     - '-avz'
1 Like

Thank you. I gave that syntax a try, and received the exact same error. The problem is that the rsync daemon is disabled at the source server, and the powers that be assure me it will stay disabled. Security had a good chuckle at me. :smiley:

I appreciate the improved syntax, and will retain it, but it’s rsync from the command line for me going forward.

Kevin

What about this instead?

src: user@sourceserver:/home/user/kevintest

Edit:
Looking at the code, having ‘rsync://’ in either the source or destination changes some of the behavior, so it is expecting the rsync server to exist regardless of any other options as a result when used in the path. ( I think…)

ansible.posix/plugins/modules/synchronize.py at main · ansible-collections/ansible.posix (github.com) - L397
and
ansible.posix/plugins/modules/synchronize.py at main · ansible-collections/ansible.posix (github.com) - L545

1 Like

Tried:

    - name: Synchronization using rsync protocol (pull)
      ansible.posix.synchronize:
        mode: pull
        src: user@sourceserver:/home/user/kevintest
        rsync_opts: 
          - '--rsh=ssh'
          - '-avz'
        dest: /appl/tmp
      delegate_to: "{{ ansible_host }}"

and

    - name: Synchronization using rsync protocol (pull)
      ansible.posix.synchronize:
        mode: pull
        src: user@sourceserver:/home/user/kevintest
        rsync_opts: 
         #  - '--rsh=ssh'
          - '-avz'
        dest: /appl/tmp
      delegate_to: "{{ ansible_host }}"

Both hang. The rsync starts on the server, but just hangs. They leave the target server with this weird situation where the target and source servers are both in the path of the source server???

[user@targetserver ~]$ ps -ef | grep -v grep | grep rsync
user    9905  9904  1 16:37 pts/1    00:00:00 /usr/bin/rsync --delay-updates -F --compress --archive --rsh=ssh -avz --out-format=<<CHANGED>>%i %n%L user@targetserver:user@sourceserver:/home/user/kevintest /appl/tmp
user    9908  9905  0 16:37 pts/1    00:00:00 ssh -l user targetserver rsync --server --sender -vlogDtpre.LsfxC --new-compress . user@sourceserver:/home/user/kevintest

Lol. Looks like you’re getting closer. It might need the username specified elsewhere or left out if you’re using the same user on both ends.

I’m trying to test locally now, and I can’t even delegate_to: "{{ ansible_host }}" because it’s undefined in that context. What version of Ansible are you running? I’m on 2.14.9.

Edit:
I’m using delegate_to: "{{ inventory_hostname }}" and have made a small directory with an empty file to test for quick results.

ansible-playbook ./rsync_test.yml --check -i ./Inventory/ansible_inventory -l host1* --diff -u root --private-key ~/.ssh/id_rsa

---
- hosts: host1.domain.local,host2.domain.local
  gather_facts: false
  tasks:
    - ansible.posix.synchronize:
        src: "remote_user@host2.domain.local:/home/remote_user/foo"
        dest: "/tmp/remote_user"
        mode: pull
      delegate_to: "{{ inventory_hostname }}"
PLAY [host1.domain.local,host2.domain.local] **************************************************************************************************************************************

TASK [ansible.posix.synchronize] *********************************************************************************************************************************************************************
cd+++++++++ foo/
>f+++++++++ foo/bar
changed: [host1.domain.local]

PLAY RECAP *******************************************************************************************************************************************************************************************
host1.domain.local : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

I got it to work successfully, and with differing users.

root@host1 is copying from remote_user@host2, but I also have authorized ssh keys configured both ways. So, I don’t know if you have additional steps to take to get your permissions to work.

You got farther than I did. I’m sure I could get there eventually, but my only “need” was to help answer this question/topic, and you’ve got that handled.

It does bring up another point, though, that I feel compelled to comment on. I get in general that you should use modules when they are available in favor of shell scripts to keep your playbooks declarative. But here we have this inadequately documented synchronize module which, to use in any slightly corner-casey way requires not only fully understanding rsync but a whole course of experimentation to divine how synchronize’s various options map into the underlying rsync invocation. I’m of the opinion that you’re better of going straight to rsync via the command or shell module; that as an abstraction layer, the synchronize module falls short.

2 Likes