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.