Connect to Junos Devices Using Junos PyEZ
Connect to a Junos device or an attached console server using different connection methods and protocols in a Junos PyEZ application.
Junos PyEZ is a microframework for Python that enables you to manage Junos devices. Junos
PyEZ models each device as an instance of the jnpr.junos.device.Device class. The Device class enables
you to connect to a Junos device using a serial console connection, Telnet, or a NETCONF
session over SSH. Junos PyEZ also supports connecting to the device through a telnet or
SSH connection to a console server. A console server, also known as a terminal server,
is a specialized device that provides a network connection to a device’s out-of-band
management console port. In addition, you can use Junos PyEZ to connect to a Junos
device through an intermediate device such as a bastion or jump host.
This topic provides an overview of the connection methods supported by Junos PyEZ and explains how to use the different methods to connect to a Junos device. The Junos PyEZ examples use various authentication methods, but for detailed information about authenticating a user, see Authenticate Junos PyEZ Users.
Connection Methods Overview
Junos PyEZ enables you to connect to a Junos device using a serial console connection, telnet, or a NETCONF session over SSH. You must use a serial console connection when you are physically connected to the CONSOLE port on a device. You can use telnet or SSH to connect to the device’s management interface or to a console server that is connected to the device’s CONSOLE port. Junos PyEZ also supports SSH connections to a device through a bastion or jump host. In addition, Junos PyEZ supports outbound SSH connections, in which the Junos device initiates the connection with the client management application.
New or zeroized devices that have factory default configurations require access through a console connection. You can use Junos PyEZ to initially configure a device that is not yet configured for remote access. You can use either a serial console connection when you are directly connected to the device or you can use telnet or SSH through a console server that is connected to the device.
By default, Junos PyEZ uses SSH to connect to a device. To specify a different
connection type, you must include the mode parameter in the
Device argument list. To telnet to a device, include the
mode='telnet' argument. To connect to a device using a serial
console connection, include the mode='serial' argument. Table 1
summarizes the Junos PyEZ connection methods, their default values for certain
parameters, and any required Junos OS configuration.
|
Connection Mode |
Value of |
Default Port |
Required Junos OS Configuration |
|---|---|---|---|
|
NETCONF over SSH (default) |
– |
830 |
[edit system services]
netconf {
ssh;
}
|
|
Serial console connection |
serial |
/dev/ttyUSB0 |
– |
|
Telnet to Junos device |
telnet |
23 |
[edit system services] telnet; |
|
Telnet through a console server |
telnet |
23 |
– |
|
SSH through a console server |
– |
22 |
– |
|
SSH through a jump host |
– |
22 |
[edit system services]
netconf {
ssh;
}
|
|
Outbound SSH |
– |
– |
[edit system services]
outbound-ssh {
...
}
|
Before you can access a device’s management interface using Telnet or NETCONF
over SSH, you must first enable the appropriate service at the [edit
system services] hierarchy level. For more information, see Set Up Junos PyEZ Managed Nodes. Because
Telnet uses clear-text passwords (thereby creating a potential security
vulnerability), we recommend that you use SSH.
Obtain credentials in a secure manner appropriate for your environment. As a best practice, prompt for credentials each time you run the script instead of storing them in an unencrypted format.
Junos PyEZ supports using context managers
(with ... as syntax) for all connection
methods. When you use a context manager, Junos PyEZ automatically calls the
open() and close() methods to connect to and
disconnect from the device. If you do not use a context manager, you must explicitly
call the open() and close() methods in your
application. We recommend that you use a context manager for console connections.
The context manager automatically handles closing the connection, and failure to
close the connection might lead to unpredictable results.
Understanding Junos PyEZ Connection Properties
When you connect to a Junos device, Junos PyEZ stores information about the current
connection as properties of the Device instance. Table 2 outlines the
available connection properties.
|
Property |
Type |
Description |
|---|---|---|
|
|
Boolean |
Current state of the connection. Returns |
|
|
String |
Hostname of the device to which the application is connected. |
|
|
Boolean |
Returns |
|
|
Integer or string |
Port used for the connection. |
|
|
String |
Routing Engine name to which the application is connected. |
|
|
Integer |
RPC timeout value in seconds. |
|
|
Integer |
Number of seconds since the current Routing Engine was booted. |
|
|
String |
User accessing the Junos device. |
For example, after connecting to a device, you can query the
connected property to return the current state of the
connection. A SessionListener monitors the session and responds to
transport errors by raising a TransportError exception and setting
the Device.connected property to False.
The following sample code prints the value of the connected property
after connecting to a Junos device and again after closing the session.
from jnpr.junos import Device dev = Device(host='router.example.net') dev.open() print (dev.connected) dev.close() print (dev.connected)
When you execute the program, the connected property returns
True while the application is connected to the device and
returns False after the connection is closed.
user@host:~$ python connect.py True False
Connect to a Device Using SSH
The Junos PyEZ Device class supports using SSH to connect to a Junos
device.
Junos
PyEZ supports the following connections:
-
Connect directly to the device's management interface
-
Connect through a console server that is directly connected to the device’s CONSOLE port
-
Connect through a bastion or jump host
The SSH server must be able to authenticate the user using standard SSH authentication mechanisms, as described in Authenticate Junos PyEZ Users. To establish a NETCONF session over SSH, you must also satisfy the requirements outlined in Set Up Junos PyEZ Managed Nodes.
When using SSH to connect to a device, Junos PyEZ first attempts SSH public key-based authentication and then tries password-based authentication. When password-based authentication is used, the supplied password is used as the device password. When SSH keys are in use, the supplied password is the passphrase for unlocking the private key. You do not need to supply a password if you load the key into an SSH key agent or if the SSH private key has an empty passphrase. However, we do not recommend using SSH private keys with empty passphrases.
Junos PyEZ automatically queries the default SSH configuration file at
~/.ssh/config, if one exists. Junos PyEZ uses any relevant
settings in the SSH configuration file that are not overridden by the
Device arguments, such as the user or the identity file. To use
a different SSH configuration file, set ssh_config to the file path
when you define Device. For example:
ssh_config_file = "~/.ssh/config_dc"
dev = Device(host='router1.example.com', ssh_config=ssh_config_file)
Starting in Junos PyEZ Release 2.7.2, a Junos PyEZ client can enable SSH host key verification when it connects to a Junos device. The client verifies the identity of the remote device by validating that the device's host key fingerprint matches a trusted key in the client's known_hosts file. If the presented host key does not match a trusted key, the connection is rejected. Use host key verification, where applicable, to prevent man-in-the-middle attacks.
By default, Junos PyEZ disables SSH host key verification and allows the connection
to proceed without requiring the server's public key to be registered in the
client's known_hosts file. To enable host key verification,
include hostkey_verify=True when you define
Device. If you enable host key verification, you must load the
device's host key in the known_hosts file before connecting to
the device. Otherwise, the connection fails.
dev = Device(host='router1.example.com', hostkey_verify=True)
The following sections discuss the different methods for connecting to a device using SSH.
- Connect Directly to a Device
- Connect to a Device through a Console Server
- Connect to a Device through a Bastion or Jump Host
Connect Directly to a Device
Junos PyEZ enables you to connect directly to a device running Junos OS or a
device running Junos OS Evolved. When you instantiate the
Device object, provide the hostname or IP address of the
target device and any required user credentials.
If the Junos PyEZ client must establish the connection using a specific local
source IP address on the configuration management server (CMS), you can include
the bind_addr argument to specify the address. Setting the
local source address for a connection might be necessary, for example, if the
CMS uses multiple network interfaces and IP addresses and only a subset of
addresses are reachable from the network device.
To create a Junos PyEZ application that establishes a NETCONF session over SSH with a Junos device and prints the device facts:
Import the
Deviceclass and any other modules or objects required for your tasks.import sys from getpass import getpass from jnpr.junos import Device from jnpr.junos.exception import ConnectError
Create the device instance, and provide the hostname, any parameters required for authentication, and any optional parameters.
hostname = input("Device hostname: ") junos_username = input("Junos OS username: ") junos_password = getpass("Junos OS or SSH key password: ") dev = Device(host=hostname, user=junos_username, passwd=junos_password)
(Optional) If the Junos device configures NETCONF on a port other than the default (830), include the
portargument and specify the port.dev = Device(host=hostname, user=junos_username, passwd=junos_password, port=50001)
(Optional) If you need to specify the local source address that should be used to establish the connection, include the
bind_addrargument and specify the IP address to use.dev = Device(host=hostname, user=junos_username, passwd=junos_password, bind_addr=10.10.10.1)
Connect to the device by calling the
open()method, for example:try: dev.open() except ConnectError as err: print ("Cannot connect to device: {0}".format(err)) sys.exit(1) except Exception as err: print (err) sys.exit(1)
Print the device facts.
print (dev.facts)
After performing any necessary tasks, close the connection to the device.
dev.close()
The sample program in its entirety (optional parameters omitted) is presented here:
import sys from getpass import getpass from jnpr.junos import Device from jnpr.junos.exception import ConnectError hostname = input("Device hostname: ") junos_username = input("Junos OS username: ") junos_password = getpass("Junos OS or SSH key password: ") dev = Device(host=hostname, user=junos_username, passwd=junos_password) try: dev.open() except ConnectError as err: print ("Cannot connect to device: {0}".format(err)) sys.exit(1) except Exception as err: print (err) sys.exit(1) print (dev.facts) dev.close()
Alternatively, you can use a context manager when connecting to the device. The
context manager automatically calls the open() and
close() methods. For example:
import sys from getpass import getpass from jnpr.junos import Device from jnpr.junos.exception import ConnectError hostname = input("Device hostname: ") junos_username = input("Junos OS username: ") junos_password = getpass("Junos OS or SSH key password: ") try: with Device(host=hostname, user=junos_username, passwd=junos_password) as dev: print (dev.facts) except ConnectError as err: print ("Cannot connect to device: {0}".format(err)) sys.exit(1) except Exception as err: print (err) sys.exit(1)
Connect to a Device through a Console Server
A Junos PyEZ client can connect to a Junos device through an SSH connection to a
console server. In this case, you must specify the login credentials for both
the console server and the Junos device. In the Device argument
list, include the console server credentials in the cs_user and
cs_passwd arguments. When SSH keys are in use, set the
cs_passwd argument to the variable containing the
passphrase for the private key. Additionally, set the port
argument to the TCP port that maps to the TTY device, if applicable.
The console server connects to the Junos device through a serial connection,
which can be slow. Junos PyEZ connections through a console server have a
default connection timeout value of 0.5 seconds. To allow sufficient time for
the client application to establish the connection, you might need to increase
the connection timeout interval by including the
timeout=seconds argument.
The following Junos PyEZ example authenticates with the console server and then the Junos device. The connection timeout is set to six seconds so that the client has sufficient time to establish the connection.
import sys from getpass import getpass from jnpr.junos import Device from jnpr.junos.exception import ConnectError hostname = input("Console server hostname: ") cs_username = input("Console server username: ") cs_password = getpass("Console server or SSH key password: ") junos_username = input("Junos OS username: ") junos_password = getpass("Junos OS password: ") port = input("Port number: ") try: with Device(host=hostname, user=junos_username, passwd=junos_password, cs_user=cs_username, cs_passwd=cs_password, port=port, timeout=6) as dev: print (dev.facts) except ConnectError as err: print ("Cannot connect to device: {0}".format(err)) sys.exit(1) except Exception as err: print (err) sys.exit(1)
Connect to a Device through a Bastion or Jump Host
Junos PyEZ enables you to access a target device through an intermediary host (bastion or jump host). You might need to use a bastion or jump host when you cannot log directly into the target device. For example, the device might be behind a firewall.
To use SSH to connect through an intermediate device, you can use one of the following options:
-
In your SSH configuration file, add an entry for the target device and specify the ProxyCommand option.
-
In your
Deviceargument list, include theproxy_commandparameter.
The Device
proxy_command argument takes precedence over the ProxyCommand
option in the SSH configuration file. If you configure both options, Junos PyEZ
uses only the proxy_command argument. The following sections
provide details about how to use each option.
- Example: Use ProxyCommand in the SSH Configuration File
- Example: Use the
Deviceproxy_commandArgument
Example: Use ProxyCommand in the SSH Configuration File
To configure a bastion or jump host in your SSH configuration file, add an entry for the Junos host and include the ProxyCommand option. The ProxyCommand string defines the connection to the intermediate device. For example, the following configuration connects through an intermediate host that supports netcat.
user1@server:~$ cat ~/.ssh/config Host 198.51.100.1 User user1 ProxyCommand ssh -l user1 192.168.1.1 nc %h 22 2>/dev/null
Similarly, the following configuration connects through the intermediate device to the target host and port in quiet mode, which suppresses warnings.
user1@server:~$ cat ~/.ssh/config Host 198.51.100.1 User user1 ProxyCommand ssh -W %h:%p -q jumphost.example.com
Example: Use the Device proxy_command Argument
When you define Device, you can include the
proxy_command argument to connect through an
intermediate device. The proxy_command string defines the
connection to the bastion or jump host. If required, Junos PyEZ prompts for
the jump host's password or its SSH private key passphrase.
The following example connects to a Junos device through a jump host. In this example, Junos PyEZ authenticates with each device using passphrase-protected SSH keys loaded in the SSH key agent.
from jnpr.junos import Device junos_host = "198.51.100.1" with Device(host=junos_host, proxy_command="ssh -W %h:%p -q user1@192.168.1.1") as dev: print(dev.facts)
If you load the SSH keys for the jump host and network device in your SSH key
agent, you do not need to provide the private key paths or passphrases in
the Junos PyEZ application. However, if you do not load the required keys
into the SSH key agent, you must provide the path and passphrase for any
passphrase-protected keys in non-default locations. The
ssh_private_key_file and password
arguments specify the path and passphrase of the private key used to
authenticate with the Junos device. In the proxy_command
argument, use the -i path option to
specify the path of the private key used to authenticate with the jump host.
When you execute the script, Junos PyEZ prompts for the corresponding
private key passphrase. For example:
from jnpr.junos import Device from getpass import getpass junos_host = "198.51.100.1" passwd = getpass("SSH key passphrase for network device: ") dev = Device( host=junos_host, passwd=passwd, ssh_private_key_file="/home/user1/.ssh/id_rsa_junos", proxy_command="ssh -i /home/user1/.ssh/id_rsa_jump -W %h:%p -q 192.168.1.1" ) dev.open() print(dev.facts) dev.close()
When you execute the script, the script prompts for the passphrases of the private keys.
user1@server:~$ python junos-pyez-proxy-command.py
Network device SSH key password:
Enter passphrase for key '/home/user1/.ssh/keys/id_rsa_jump':
{'2RE': True, 'HOME': '/var/home/user1', 'RE0': {'mastership_state': 'Master', 'status': 'OK', 'model': 'RE VIRTUAL', 'last_reboot_reason': 'Unknown', 'up_time': '29 days, 23 hours, 11 minutes, 42 seconds'}, 'RE1': {'mastership_state': 'Backup', 'status': 'OK', 'model': 'RE VIRTUAL', 'last_reboot_reason': 'Unknown', 'up_time': '29 days, 23 hours, 9 minutes'},
[...output omitted...]Connect to a Device Using Outbound SSH
You can configure a Junos device to initiate a TCP/IP connection with a client
management application that would be blocked if the client attempted to initiate
the connection (for example, if the device is behind a firewall). The
outbound-ssh configuration instructs the device to create a
TCP/IP connection with the client management application and to forward the
identity of the device. Once the connection is established, the management
application acts as the client and initiates the SSH sequence, and the Junos
device acts as the server and authenticates the client.
Once you configure and commit outbound SSH on the Junos device, the device begins to initiate an outbound SSH connection based on the committed configuration. The device repeatedly attempts to create this connection until successful. If the connection between the device and the client management application is dropped, the device again attempts to create a new outbound SSH connection until successful. This connection is maintained until the outbound SSH configuration is deleted or deactivated.
To configure the Junos device for outbound SSH connections, include the
outbound-ssh statement at the [edit system
services] hierarchy level. In the following example, the Junos
device attempts to initiate a connection with the host at 198.51.100.101 on port
2200:
user@router1> show configuration system services outbound-ssh
client nms1 {
device-id router1;
secret "$9abc123"; ## SECRET-DATA
services netconf;
198.51.100.101 port 2200;
}To establish a connection with the Junos device using outbound SSH, the Junos
PyEZ application sets the sock_fd argument in the
Device constructor equal to the file descriptor of an
existing socket and either omits the host argument or sets it
to None.
The following Junos PyEZ example listens on the configured TCP port for incoming
SSH sessions from Junos devices. The application accepts an incoming connection
and retrieves the socket’s file descriptor for that connection, which is used
for the value of the sock_fd argument. The client application
establishes the SSH connection with the device, collects and prints the device
facts, disconnects from the device, and waits for more connections.
import socket from jnpr.junos import Device from jnpr.junos.exception import ConnectError from getpass import getpass from pprint import pprint """ Listen on TCP port 2200 for incoming SSH session from a Junos device. After the device connects, collect and print the devices facts. Then disconnect from that device and wait for more connections. """ def launch_junos_proxy(client, addr): val = { 'MSG-ID': None, 'MSG-VER': None, 'DEVICE-ID': None, 'HOST-KEY': None, 'HMAC': None } msg = '' count = 0 while count < 5: c = client.recv(1) c = c.decode("utf-8") msg += str(c) if c == '\n': count += 1 for line in msg.splitlines(): (key, value) = line.split(': ') val[key] = value print("{}: {}".format(key, val[key])) return client.fileno() def main(): PORT = 2200 junos_username = input('Junos OS username: ') junos_password = getpass('Junos OS password: ') s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind(('', PORT)) s.listen(5) print('\nListening on port %d for incoming sessions ...' % (PORT)) sock_fd = 0 while True: client, addr = s.accept() print('\nGot a connection from %s:%d' % (addr[0], addr[1])) sock_fd = launch_junos_proxy(client, addr) print('Logging in ...') try: with Device(host=None, sock_fd=sock_fd, user=junos_username, passwd=junos_password) as dev: pprint(dev.facts) except ConnectError as err: print ("Cannot connect to device: {0}".format(err)) if __name__ == "__main__": main()
user@nms1:~$ python junos-pyez-outbound-ssh.py
Junos OS username: user
Junos OS password:
Listening on port 2200 for incoming sessions ...
Got a connection from 10.10.0.5:57881
MSG-ID : DEVICE-CONN-INFO
MSG-VER : V1
DEVICE-ID : router1
HOST-KEY : ssh-rsa AAAAB...0aF4Mk=
HMAC : 4e61201ec27a8312104f63bfaf77a4478a892c82
Logging in ...
{'2RE': True,
'HOME': '/var/home/user',
'RE0': {'last_reboot_reason': 'Router rebooted after a normal shutdown.',
'mastership_state': 'master',
'model': 'RE-MX-104',
'status': 'OK',
'up_time': '2 days, 6 hours, 22 minutes, 22 seconds'},
'RE1': {'last_reboot_reason': 'Router rebooted after a normal shutdown.',
'mastership_state': 'backup',
'model': 'RE-MX-104',
'status': 'OK',
'up_time': '2 days, 6 hours, 22 minutes, 12 seconds'},
'RE_hw_mi': False,
'current_re': ['re0', 'master', 'node', 'fwdd', 'member', 'pfem'],
'domain': 'example.com',
'fqdn': 'router1.example.com',
'hostname': 'router1',
...For detailed information about configuring outbound SSH on Junos devices, see Configure Outbound SSH Service.
Connect to a Device Using Telnet
The Junos PyEZ Device class supports connecting to a Junos
device using Telnet, which provides unencrypted access to the network device.
You can telnet to the device’s management interface or to a console server that
is directly connected to the device’s CONSOLE port. You
must configure the telnet statement at the [edit system
services] hierarchy level on all devices that require access to the
management interface. Accessing the device through a console server enables you
to initially configure a new or zeroized device that is not yet configured for
remote access.
To use Junos PyEZ to telnet to a Junos device, you must include
mode='telnet' in the Device argument list,
and optionally include the port parameter to specify a port.
When you specify mode='telnet' but omit the
port parameter, the value for port
defaults to 23. When the application connects through a console server, specify
the port through which the console server connects to the Junos device.
To use Junos PyEZ to telnet to a Junos device and print the device facts:
The sample program in its entirety is presented here:
import sys from getpass import getpass from jnpr.junos import Device hostname = input("Device hostname: ") junos_username = input("Junos OS username: ") junos_password = getpass("Junos OS password: ") dev = Device(host=hostname, user=junos_username, passwd=junos_password, mode='telnet', port=23) try: dev.open() except Exception as err: print (err) sys.exit(1) print (dev.facts) dev.close()
Alternatively, you can use a context manager when connecting to the device, which handles opening and closing the connection. For example:
import sys from getpass import getpass from jnpr.junos import Device hostname = input("Device hostname: ") junos_username = input("Junos OS username: ") junos_password = getpass("Junos OS password: ") try: with Device(host=hostname, user=junos_username, passwd=junos_password, mode='telnet', port=23) as dev: print (dev.facts) except Exception as err: print (err) sys.exit(1)
In some cases, when you connect to a console server that emits a banner message, you might be required to press Enter after the message to reach the login prompt. In this case, the application might fail to receive the login prompt, which can cause the connection to hang.
Starting in Junos PyEZ Release 2.6.2, Junos PyEZ automatically handles the
console server banner. In Junos PyEZ Releases 2.1.0 through 2.6.1, a Junos PyEZ
application can include console_has_banner=True in the
Device argument list to handle a console server that emits
a banner message.
dev = Device(host=hostname, user=username, passwd=password, mode='telnet', port=port, console_has_banner=True)
When you include the console_has_banner=True argument, if the
application does not receive a login prompt upon initial connection, the
application waits for 5 seconds. The application then emits a newline
(\n) character so that the console server issues the login
prompt. If you omit the argument and the connection hangs, the application
instead emits the <close-session/> RPC to terminate the
connection.
Connect to a Device Using a Serial Console Connection
The Junos PyEZ Device class enables you to connect to a Junos
device using a serial console connection. A serial console connection is useful
when you must initially configure a new or zeroized device that is not yet
configured for remote access. To use this connection method, you must be
physically connected to the device through the CONSOLE
port. For detailed instructions about connecting to the
CONSOLE port on your device, see the hardware
documentation for your specific device.
Junos PyEZ supports using context managers for serial console connections. We recommend that you use a context manager for console connections, because the context manager automatically handles opening and closing the connection. Failure to close the connection can lead to unpredictable results.
To use Junos PyEZ to connect to a Junos device through a serial console
connection, you must include mode='serial' in the
Device argument list. You can optionally include the
port parameter to specify a port. When you specify
mode='serial' but omit the port parameter,
the value for port defaults to /dev/ttyUSB0.
To use Junos PyEZ to connect to a Junos device using a serial console connection and load and commit a configuration:
The sample program in its entirety is presented here:
import sys from getpass import getpass from jnpr.junos import Device from jnpr.junos.utils.config import Config junos_username = input("Junos OS username: ") junos_password = getpass("Junos OS password: ") try: with Device(mode='serial', port='port', user=junos_username, passwd=junos_password) as dev: print (dev.facts) cu = Config(dev) cu.lock() cu.load(path='/tmp/config_mx.conf') cu.commit() cu.unlock() except Exception as err: print (err) sys.exit(1)