This post is going to talk about the ways you could use to access the end devices through the jump server or a proxy server. I am going to use Netmiko which is my all-time favorite tool whenever I need to programmatically interact with network devices. Let’s see how we can leverage Netmiko SSH proxy support to access remote network devices via a jumpserver.
https://pynet.twb-tech.com/blog/automation/netmiko-proxy.html
Above is the URL where it all started.
Step1:- Setup SSH key-based authentication between the source machine and the intermediate jumpserver. How do you do that? There are a bunch of websites talking about this but one below is what came in my search results.
Step2:- Once that is done, test the authentication using the SSH keys instead of the normal username password.
ssh username@remote_host ( if your key file is at the default location with default name )
or
ssh -i <path to key file> username@remote_host ( in case the above doesn’t work or you kept your key at a non default location or non default name )
Step3:- Include this as a part of your script. I am doing to write a barebones netmiko script to ssh to the server just for illustration purposes.
from netmiko import ConnectHandler
jumpserver = {
"device_type": "terminal_server",
"ip": "peplap00121",
"username": "netsvc",
"global_delay_factor": 5,
"use_keys": True,
"key_file": "/home/devnet/.ssh/openssh_private_key"
}
net_connect = ConnectHandler(**jumpserver)
print(net_connect.find_prompt()
At this point, you should be able to login into the jumpserver using netmiko script.
Step4:- This is where it gets tricky. You could either use a more interactive, raw method to access the device from the terminal server. Basically replicating what you would do manually via netmiko but ofcourse that is not a scalable method and just re-inventing the wheel that Kirk Byers has already done.
This is what I did initially, I will just put it here for reference ofcourse this is customized to the login prompt that I see on my jumpserver but it should give a fair idea of what to expect in case you want to go this rudimentary path.
def netmiko_with_jumpserver(device):
try:
print(f"Trying to connect Term Server {jumpserver_netmiko['ip']} with user {jumpserver_netmiko['username']}")
net_connect = ConnectHandler(**jumpserver_netmiko)
print(f"[green]Connected to jump server {net_connect.host} prompt {net_connect.find_prompt()}")
try:
pre_connect_prompt = net_connect.find_prompt()
net_connect.write_channel(f"ssh {config['CREDENTIALS']['USERNAME']}@{device}\r\n")
time.sleep(5)
output = net_connect.read_channel()
if "password" in output.lower():
net_connect.write_channel(f"{config['CREDENTIALS']['PASSWORD']}\r\n")
net_connect.secret = {config['CREDENTIALS']['PASSWORD']}
time.sleep(1)
post_connect_prompt = net_connect.find_prompt()
if (pre_connect_prompt != post_connect_prompt and "password" not in post_connect_prompt.lower()):
redispatch(net_connect, device_type="cisco_ios")
print(f"Connected successfully to device {net_connect.find_prompt()[:-1]}")
except Exception as e:
print(f"[red]Failed to connect to device {device}")
net_connect = None
except Exception as e:
print(f"Failed to connect to JumpServer: {e}")
net_connect = None
return net_connect
Step5:-
In the above step, we are first logging into the jumpserver and then manually passing the ssh command to login to the device and interact with the end device using dispatch to handover the Netmiko connection class to cisco_ios module or whatever is in your case. But instead of having to handle this manually, we could leverage inbuilt SSH feature of Proxycommand in your SSH config file like below.
host jumphost
IdentityFile /home/devnet/.ssh/openssh_private_key
IdentitiesOnly yes
user netsvc
hostname myjumpserver
host * !jumphost
# ProxyCommand ssh jumphost nc %h %p
# ProxyCommand ssh -W %h:%p jumphost
KexAlgorithms +diffie-hellman-group-exchange-sha1
Ciphers aes128-ctr,aes192-ctr,aes256-ctr,aes128-cbc,3des-cbc,aes128-cbc,3des-cbc,aes192-cbc,aes256-cbc
ProxyCommand ssh -F /home/devnet/.ssh/config -W %h:%p jumphost
The top section of the above ssh config file shows the details you are using to connect to the jumphost. The second half shows the parameters netmiko would take to connect to anything other than jumphost.
The first 2 ProxyCommand that are commented out are just alternate ways. KexAlgorithms and Ciphers are optional here which you may not need. I had to use them so I put them here, yours can work without these as well. ProxyCommand basically tells SSH to connect to anything other than jumphost, first, connect to the jumphost and proxy the connection through it.
from netmiko import ConnectHandler
device= {
"device_type": "cisco_ios",
"ip": "<remote_router_ip>",
"username": username,
"password": yourpassword,
"global_delay_factor": 5,
"ssh_config_file": "/home/devnet/.ssh/config"
}
net_connect = ConnectHandler(**device)
x = net_connect.send_command("show version")
print(x)
The above will allow netmiko to directly login to the end cisco router with the correct connection handler class for cisco ios. While this step gets the job done, the next step is to use SSH feature called SSH Control Master that allows the same connection from source machine to jumpserver to be reused for every new connection that you open for multiple remote devices. So if you want to connect to 10 routers in parallel, there will be only 1 connection from the source machine to jumpserver which saves a lot of time because now you don’t have to establish the connection to jumpserver everytime.
Step6:- Setting up SSH Control Master ( very simple )
host jumphost
IdentityFile /home/devnet/.ssh/openssh_private_key
IdentitiesOnly yes
user netsvc
hostname myjumpserver
ControlMaster auto
ControlPath ~/.ssh/%r@%h:%p
host * !jumphost
KexAlgorithms +diffie-hellman-group-exchange-sha1
Ciphers aes128-ctr,aes192-ctr,aes256-ctr,aes128-cbc,3des-cbc,aes128-cbc,3des-cbc,aes192-cbc,aes256-cbc
ProxyCommand ssh -F /home/devnet/.ssh/config -W %h:%p jumphost
That’s it, just add the above 2 commands for Control Master and you are ready to go with a faster and much-optimized connection method to connect to remote devices that are behind the proxy server. Nothing changes from within your netmiko code.
This has been possible with the great help provided by https://twitter.com/kirkbyers over Twitter, over slack, and of course Netmiko GitHub.
You can find more about Netmiko here
UPDATE:-
Control Persist feature doesn’t work with Paramiko yet so it doesn’t work with Netmiko too. I was incorrect about mentioning that above. Scrapli with Openssh should be able to address this issue. I will test this with scrapli and let you all know about my findings.
Did you have an update of using a jumphost with scrapli?