Connect to Network Devices Through a Jumphost with Ansible
Posted on
I have some network devices (routers) and I want to run playbooks on them. Due to good practices all connections to the routers are limited to certain IP addresses, so I'm required to jump the ssh connection via an intermediate server; a jumphost.
In theory this is should be pretty easy but I spent so much time getting this to work. There's plenty of information about this subject out there but for some reason I couldn't get it to work. Hopefully my findings can help someone else, because this was not a fun experience.
This is my playbook:
---
- name: Jumphost testing
hosts: all
gather_facts: false
tasks:
- name: Gather facts
cisco.iosxr.iosxr_facts:
register: device_output
- name: Print facts
ansible.builtin.debug:
msg:
- "{{ device_output }}"
I execute it with this command:
ansible-playbook -i inventory/hosts.yml -u user -k playbooks/jumphost_test.yml
After I enter my password for the routers the playbook will fail with a connection refused error message. This is because Ansible is trying to connect directly, without jumping through a jumphost.
Ideally I want Ansible to use the settings in my ~/.ssh/config file automatically (like described here) but no matter what I couldn't get that to work.
I suspect using the ansible.netcommon.network_cli connection setting messes a bit with how the ssh settings are read, but I have no source for that. In the end I had to combine the two methods.
In ansible.cfg I had to make the following modification:
[ssh_connection]
ssh_common_args="-o ProxyCommand='ssh -W %h:%p -q jumphost'"
And in ~/.ssh/config:
Host * !jumphost
HostKeyAlgorithms=+ssh-rsa
KexAlgorithms=+diffie-hellman-group1-sha1,diffie-hellman-group-exchange-sha1
Ciphers=+aes128-cbc
Host jumphost
Hostname jumphost.example.net
User user
IdentityFile ~/.ssh/id_ed25519
Some caveats I found along the way:
- I couldn't specify another ssh_config using
-F. - I couldn't specify a jumphost using
-J, instead of usingProxyCommand. - Specifying a
UserunderHost *doesn't work, not sure what's happning but I have to use-utoansible-playbook. - Can't set
ProxyJumpunderHost *, nothing seems to happen when I try to connect.
With these changes I'm now able to execute the playbook, and it will happily use the jumphost specified.
Update 2025-03-06
I did another test after committing this post.
My ansible.cfg looks like this:
[defaults]
transport=ssh
From what I can tell this means that Ansible should use ansible-pylibssh (which is backed by libssh) for ssh connections and if that Python package isn't available Ansible will fallback to Paramiko.
When I set transport=paramiko in the config Ansible didn't connect via my jumphost, so make sure ansible-pylibssh is installed.