Test Paramiko to configure the switch
-
The topology
Configure the SSH Server, SSH login user, and password on SW1
-
Creating a Key Pair
[SW1]rsa local-key-pair create The key name will be: SW1_Host The range of public key size is (512 ~ 2048). NOTES: If the key modulus is greater than 512, it will take a few minutes. Input the bits inthe modulus[default = 512]:2048 Generating keys... . + + + + + + + + + + + +... + + + + + + + + + + + +. + + + + + + + +... ++++++++Copy the code
Ubuntu 20.04 is a relatively new version and does not support keys shorter than 1023 bits. Therefore, you can directly configure keys of 1024 or 2048 bits
-
The Stelnet server was enabled
stelnet server enable Copy the code
-
Configure SSH
-
Configure the SSH user login page
user-interface vty 0 4 authentication-mode aaa user privilege level 15 protocol inbound ssh # Copy the code
-
Configuring SSH Users
ssh user sht ssh user sht authentication-type password ssh user sht service-type stelnet # Copy the code
If use SSH user password authentication, the authentication – type = “| rsa | password – rsa), then only need to generate local rsa key in SSH server. If SSH users use RSA authentication, local RSA key pairs need to be generated on both the server and client, and the public keys of the server and client need to be configured locally.
-
Configure password authentication for SSH users
aaa local-user sht password cipher Huawei local-user sht privilege level 15 local-user sht service-type ssh # Copy the code
-
-
Configuring management vlans
-
Create manage VLAN100
# vlan 100 management-vlan #Interface Vlanif100 IP address 192.168.50.1 255.255.255.0# Copy the code
-
Configuring Hybrid interfaces (For details on why Hybrid interfaces are configured, see Network Basics review [1])
# interface Ethernet0/0/1 port hybrid pvid vlan 100 port hybrid untagged vlan 100 # Copy the code
-
Ubuntu 20.04 Use SSH to remotely log in to SW1 and check whether the SSH configuration of SW1 takes effect
-
SSH [email protected] on Ubuntu 20.04
$SSH [email protected] **Unable to negotiate with** 192.168.50.1 port 22: **no matching key exchange method found.** **Their offer: diffie-hellman-group1-sha1,diffie-hellman-group-exchange-sha1**Copy the code
-
Problem Description:
If the client and server cannot agree on a common set of parameters, the connection will fail. OpenSSH (7.0 and later) produces an error message similar to this. Ubuntu20.04 uses OpenSSH 8.2. In this case, the client and server cannot agree on the key exchange algorithm, and the server only provides (in this case, SW1) two methods, Diffie-Hellman-group1-SHA1 and Diffie-Hellman-group-exchange-sha1. OpenSSH supports both key exchange methods, but they are not enabled by default because they are older, weak and fall within the theoretical scope of the so-called Logjam attack. So we need to enable weaker methods on the client side.
$SSH -v openssh_8.2p1 Ubuntu-4Ubuntu0.1, OpenSSL 1.1.1f 31 Mar 2020Copy the code
-
Solution:
Source:
OpenSSH: Legacy Options
$SSH - oKexAlgorithms = + diffie-hellman group1 -- sha1 [email protected]Copy the code
+
– Add the switching algorithm to the client default set rather than replace the default algorithm-o
Options –-KexAlgorithms
– Key exchange method – used to generate the key for each connection;
-
-
After using ssh-okexalgorithms =+ Diffie-Hellman-group1-sha1 [email protected], SW1 is connected successfully
$ ssh -oKexAlgorithms=+diffie-hellman-group1-sha1 [email protected] The authenticity of host '192.168.50.1 (192.168.50.1)' can't be established. RSA key fingerprint is SHA256:BqJmFBxfyFuobgWlC7MmLlpjfu1UjsyhnLbdd/GIVc8. Are you sure you want to continue connecting (yes/no/[fingerprint])? yes Warning: Permanently added '192.168.50.1'(RSA) to the list of known hosts. [email protected]'s password: Info: The max number of VTY users is 5, and the number of current VTY users on line is 1. The current login time is 2021-03-02 19:12:58. <SW1>sys Enter system view, return user view with Ctrl+Z. [SW1] Copy the code
configurationparamiko_test.py
Configure SW1
-
Write the script paramiko_test.py
import paramiko import time ip_address = "192.168.50.1" username = "sht" password = "Huawei" ssh_client = paramiko.SSHClient() ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # not recommended for production but fine for labs ssh_client.connect(hostname = ip_address, username = username, password = password) print("Successful connection", ip_address) remote_connection = ssh_client.invoke_shell() remote_connection.send("system\n") # creation of vlans for n inRange (2, 21) :print("Creating VLAN " + str(n)) remote_connection.send("vlan " + str(n) + "\n") remote_connection.send("description VLAN " + str(n) + "\n") time. Sleep (0.5)# wait half of second remote_connection.send("return\n") time.sleep(1) output = remote_connection.recv(65535) print(output.decode('ascii')) ssh_client.close Copy the code
paramiko_test.py
The script to explain-
The first part
ip_address = "192.168.50.1" username = "sht" password = "Huawei" ssh_client = paramiko.SSHClient() ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # not recommended for production but fine for labs ssh_client.connect(hostname = ip_address, username = username, password = password) Copy the code
-
Paramiko official document given Paramiko. Client. SSHClient class description as follows:
High-level representation of an SSH session with an SSH server. This class encapsulates the Transport, Channel, and SFTPClient classes to handle authentication and open channels.
The typical usage of SSHClient is as follows:
client = SSHClient() client.load_system_host_keys() client.connect('ssh.example.com') Exec_command ('ls -l') exec_command('ls -l' Copy the code
Then call load_system_host_keys(filename = None) to read the host key. Filename defaults to None. Attempts to read the key from the user’s local “Known hosts” file; Call connect() to establish a connection with the server whose hostname is ssh.example.com. The server’s host key is checked against the system host key (load_system_host_keys) and any local host key (load_host_keys). If the host name of the server is not found in either set of host keys, the missing host key policy (set_missing_host_KEY_policy) is used. The default policy is to reject the connection and report an error SSHException.
-
set_missing_host_key_policy(paramiko.AutoAddPolicy( ))
set_missing_host_key_policy(policy)
Set the connection toUnknown Host KeyThe policy to be used when using the server;policy
The policy class to be set can be RejectPolicy(default), AutoAddPolicy, or WarningPolicy.If the policy is not configured, the connection to the server is denied by default.- will
paramiko.AutoAddPolicy()
Set to a parameter forAutomatically adds the host name and the new host key to the local HostKeys objectAnd save the pair information. This method is called by SSHClient. - It not only accepts the public key provided by the server with the unknown host key and establishes the connection, but also saves the host name and the corresponding key into the HostKeys object, and then connects to the server for use
load_system_host_keys
orload_host_keys
Methods the
-
-
The second part
remote_connection = ssh_client.invoke_shell() remote_connection.send("system\n") # creation of vlans for n in range (2.21) :print("Creating VLAN " + str(n)) remote_connection.send("vlan " + str(n) + "\n") remote_connection.send("description VLAN " + str(n) + "\n") time.sleep(0.5) # wait half of second remote_connection.send("return\n") Copy the code
remote_connection = ssh_client.invoke_shell()
- Since the SSHClient class encapsulates the Channel class, similar to socket, use
invoke_shell()
After that, the client starts an interactive shell session on the SSH server, using the channel class (ChannelCreate a channel and connect it to a dummy terminal according to the required terminal type and size ssh_client
SSHClient is an object of the SSHClient class that calls methods of SSHClientinvoke_shell()
After creatingremote_connection
The Channel of this Channel class
- Since the SSHClient class encapsulates the Channel class, similar to socket, use
remote_connection.send()
- In the Channel class
send()
Used to transmit data - Here remote_connection is a channel, called
send()
Method to send data to the SSH server
- In the Channel class
-
The third part
time.sleep(1) output = remote_connection.recv(65535) print(output.decode('ascii')) ssh_client.close Copy the code
-
time.sleep(1)
-
Output = remote_connection.recv(65535) the remote_connection channel uses recv(65535) to set the maximum number of Bytes read. If recv() returns a length of 0 bytes, the channel stream is closed;
-
Output.decode (‘ ASCII ‘), recv() returns a byte string, assigns the return value to the output variable, and then uses decode(‘ ASCII ‘) in Python 3 to parse the byte string into an ASCII encoding to help output beautiful content;
If not applicable
decode('ascii')
, the output is as follows: -
Ssh_client. close Closes ssh_client. Close Closes the SSHClient class
-
-
Push the configuration to the device
-
Upload to SW1
-
Check whether the script configuration takes effect on SW1
The script successfully configured the vlans on SW1 remotely.
Configure the MSTP scenario
Topology planning and basic device configuration
-
Topology and Planning
- In MST1, SwitchA acts as the root and blocks the Ethernet0/0/2 interface on SwitchD
- In MST2, SwitchB acts as the root and blocks the Ethernet0/0/2 interface of SwitchC
-
Basic Switch Configuration
rsa local-key-pair create **2048** # stelnet server enable # user-interface vty 0 4 authentication-mode aaa user privilege level 15 protocol inbound ssh # aaa local-user sht password cipher Huawei local-user sht privilege level 15 local-user sht service-type ssh # ssh user sht ssh user sht authentication-type password ssh user sht service-type stelnet # vlan 100 management-vlan #Interface Vlanif100 IP address 192.168.50.x 255.255.255.0Copy the code
-
SwitchA | SwitchB
# interface Ethernet0/0/1 port link-type trunk port trunk allow-pass vlan 100 # interface Ethernet0/0/2 port link-type trunk port trunk allow-pass vlan 100 # interface Ethernet0/0/3 port link-type trunk port trunk allow-pass vlan 100 # Copy the code
-
SwitchC | SwitchD
# interface Ethernet0/0/1 port link-type trunk port trunk allow-pass vlan 100 # interface Ethernet0/0/2 port link-type trunk port trunk allow-pass vlan 100 # Copy the code
-
-
Verify the connectivity between Ubuntu and switches
$ ssh-keygen -f "/home/sht/.ssh/known_hosts" -R "192.168.50.100" # $SSH -keygen -f "/home/ SHT /. SSH /known_hosts" -r "192.168.50.101" # $SSH -keygen -f "/home/ SHT /. SSH /known_hosts" -r "192.168.50.102" # $SSH -keygen -f "/home/ SHT /. SSH /known_hosts" -r "192.168.50.103" Copy the code
If the login is successful, the connection between Ubuntu and the switch is good and remote login can be performed.
Write the script and upload it to the device
Script overview
-
Configuration overview
-
Script overview
#! /usr/bin/env python3 import paramiko import time SwitchA = { 'ip': '192.168.50.100'.'username': 'sht'.'password': 'Huawei'.'is_root': True.'primary_instances': ['1'].'secondary_instances': ['2'].'cost_change_interfaces': [].'trunk_interfaces': ['e0/0/1'.'e0/0/2'].'access_interfaces': [].'root_interfaces': ['e0/0/2'], } SwitchB = { 'ip': '192.168.50.101'.'username': 'sht'.'password': 'Huawei'.'is_root': True.'primary_instances': ['2'].'secondary_instances': ['1'].'cost_change_interfaces': [].'trunk_interfaces': ['e0/0/1'.'e0/0/2'].'access_interfaces': [].'root_interfaces': ['e0/0/2'], } SwitchC = { 'ip': '192.168.50.102'.'username': 'sht'.'password': 'Huawei'.'is_root': False.'primary_instances': ['1'].'secondary_instances': ['2'].'cost_change_interfaces': ['e0/0/2'].'trunk_interfaces': ['e0/0/1'.'e0/0/2'].'access_interfaces': ['e0/0/3'].'root_interfaces':[], } SwitchD = { 'ip': '192.168.50.103'.'username': 'sht'.'password': 'Huawei'.'is_root': False.'primary_instances': ['2'].'secondary_instances': ['1'].'cost_change_interfaces': ['e0/0/2'].'trunk_interfaces': ['e0/0/1'.'e0/0/2'].'access_interfaces': ['e0/0/3'].'root_interfaces':[], } def paramiko_SSH_Command(switch) : ''' Start ''' ssh_client = paramiko.SSHClient() ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # not recommended for production but fine for labs ssh_client.connect(hostname = switch['ip'], username = switch['username'], password = switch['password']) print(""*3) print("= = = = = = = = = = = = = = ="*3) print(""*3) print("Successful connection", switch['ip']) remote_connection = ssh_client.invoke_shell() remote_connection.send("system\n") ''' Some Configuration Functions ''' common_Configuration(remote_connection, switch['cost_change_interfaces'], switch['secondary_instances']) if switch['is_root']: root_PriSec(remote_connection, switch['primary_instances'], switch['secondary_instances']) edged_port_bpdu_protection(remote_connection, switch['access_interfaces']) if switch['root_interfaces']: root_protection(remote_connection, switch['root_interfaces']) conf_trunk_interfaces(remote_connection, switch['trunk_interfaces']) if switch == SwitchC: conf_access_interfaces(remote_connection, switch['access_interfaces'].2) elif switch == SwitchD: conf_access_interfaces(remote_connection, switch['access_interfaces'].11) ''' End ''' remote_connection.send("return\n") remote_connection.send("save\n") remote_connection.send("y\n") remote_connection.send("\n") time.sleep(5) output = remote_connection.recv(65535) print(output.decode('ascii')) ssh_client.close def common_Configuration(remote_connection, cost_change_interfaces, secondary_instances) : # Stp region_configuration print("Configuring stp region_configuration ...") region_configurations = ['stp region-configuration\n'.'region-name RG1\n'.'instance 1 vlan 2 to 10\n'.'instance 2 vlan 11 to 20\n'.'active region-configuration\n'.'quit\n'] for region_configuration in region_configurations: remote_connection.send(region_configuration) # Stp cost computing methods print("Configuring stp pathcost-standard legacy ...") remote_connection.send("stp pathcost-standard legacy\n") # Stp interface cost print("Configuring interface cost ...") if cost_change_interfaces: for secondary_instance in secondary_instances: for cost_change_interface in cost_change_interfaces: remote_connection.send("interface " + cost_change_interface + "\n") remote_connection.send("stp instance " + secondary_instance +" cost 20000\n") remote_connection.send("quit\n") # STP enable print("Creating VLAN and Enabling STP ...") remote_connection.send("vlan batch 2 to 20\n") remote_connection.send("stp enable\n") def conf_trunk_interfaces(remote_connection, trunk_interfaces) : print("Configuring trunk interfaces ...") for trunk_interface in trunk_interfaces: remote_connection.send("interface " + trunk_interface + "\n") remote_connection.send("port link-type trunk\n") remote_connection.send("port trunk allow-pass vlan 2 to 20\n") remote_connection.send("quit\n") def conf_access_interfaces(remote_connection, access_interfaces, access_vlan) : print("Configuring access interfaces ...") for access_interface in access_interfaces: remote_connection.send("interface " + access_interface + "\n") remote_connection.send("port link-type access\n") remote_connection.send("port default vlan " + str(access_vlan) + "\n") remote_connection.send("quit\n") def root_protection(remote_connection, interfaces) : print("Configuring stp protection ...") for interface in interfaces: remote_connection.send("interface " + interface + "\n") remote_connection.send("stp root-protection\n") remote_connection.send("quit\n") def edged_port_bpdu_protection(remote_connection, interfaces) : print("Configuring edged-port and bpdu protection ...") if interfaces: for interface in interfaces: remote_connection.send("interface " + interface + "\n") remote_connection.send("stp edged-port enable\n") remote_connection.send("quit\n") remote_connection.send("stp bpdu-protection\n") def root_PriSec(remote_connection, primary_instances, secondary_instances) : # Stp primary root print("Configuring primary root ...") if primary_instances: for primary_instance in primary_instances: remote_connection.send("stp instance " + primary_instance + " root primary\n") else: print("No primary root configuration.") # Stp secondary root print("Configuring secondary root ...") if secondary_instances: for secondary_instance in secondary_instances: remote_connection.send("stp instance " + secondary_instance + " root secondary\n") else: print("No secondary root configuration.") if __name__=="__main__": paramiko_SSH_Command(SwitchA) paramiko_SSH_Command(SwitchB) paramiko_SSH_Command(SwitchC) paramiko_SSH_Command(SwitchD) Copy the code
Push configuration to the device
Script parsing
- Use dict to regulate switch configuration parameters
- The access layer differs from the aggregation layer in some configurations, and the keys in the dictionary are the same
- For the purpose of calling different methods to configure different configurations, keys that do not have corresponding configurations are set with null values. When the function is called, it checks whether the value is null. If it is empty, no specific function is configured. If the value is not empty, the value is invoked according to the key to configure the corresponding function.
- use
paramiko_SSH_Command(switch)
Call the remaining methods that correspond to various configurations as a general method- The STP region-configuraion configuration of the four switches is the same. The same VLAN needs to be created and STP(the default MSTP mode) needs to be enabled on the four switches. In addition, STP costs are calculated using huawei’s unique method. Use a function
common_Configuration
Encapsulation; - When configuring the Access interface, SwitchA and SwitchB at the aggregation layer do not need to be configured
conf_access_interfaces(remote_connection, access_interfaces, access_vlan)
, check whether the switch object is SwitchC or SwitchD. For example, SwitchC is responsible for hosts on even-numbered vlans, and SwitchD is responsible for hosts on odd-numbered vlans. Configure different Pvids by matching SwitchC or SwitchD. - Similar to the configuration of edge ports
edged_port_bpdu_protection
From some special conditions (such as different Switch configuration PVID), only need to configure the interface is different on each of the switches, can make the edge port command without the participation of other parameters, you can first determine whether need to configure the edge of the port list is empty, empty is not configured, no empty, the for loop calls for configuration interface list, Each interface is configured one by one. Similarly, MSTP root and standby root are configured only on SwitchA and SwitchB. To avoid configuration on SwitchC and SwitchD, the MSTP root and standby root are invoked only on the aggregation layer according to the instance fields in the device dictionaryroot_PriSec
.
- The STP region-configuraion configuration of the four switches is the same. The same VLAN needs to be created and STP(the default MSTP mode) needs to be enabled on the four switches. In addition, STP costs are calculated using huawei’s unique method. Use a function
Verify the configuration
- View the port status and port protection type
-
SwitchA
[SwitchA]display stp brief MSTID Port Role STP State Protection ... 1 Ethernet0/0/1 DESI FORWARDING NONE 1 Ethernet0/0/2 DESI FORWARDING ROOT 2 Ethernet0/0/1 ROOT FORWARDING NONE 2 Ethernet0/0/2 DESI FORWARDING ROOT Copy the code
- In MSTI1, SwitchA is the root bridge, and Ethernet0/0/1 and Ethernet0/0/2 are the specified ports.
- In MSTI2, Ethernet0/0/1 of SwitchA becomes the specified port, and Ethernet0/0/2 becomes the root port.
-
SwitchB
[SwitchB]display stp brief MSTID Port Role STP State Protection ... 1 Ethernet0/0/1 ROOT FORWARDING NONE 1 Ethernet0/0/2 DESI FORWARDING ROOT 2 Ethernet0/0/1 DESI FORWARDING NONE 2 Ethernet0/0/2 DESI FORWARDING ROOT Copy the code
- In MSTI2, SwitchB is the root bridge, and Ethernet0/0/1 and Ethernet0/0/2 are the specified ports.
- In MSTI1, Ethernet0/0/2 of SwitchB becomes the specified port, and Ethernet0/0/1 becomes the root port.
-
SwitchC
<SwitchC>display stp interface Ethernet 0/0/1 brief MSTID Port Role STP State Protection .. 1 Ethernet0/0/1 ROOT FORWARDING NONE 2 Ethernet0/0/1 ROOT FORWARDING NONE <SwitchC>display stp interface Ethernet 0/0/2 brief MSTID Port Role STP State Protection ... 1 Ethernet0/0/2 DESI FORWARDING NONE 2 Ethernet0/0/2 ALTE DISCARDING NONE Copy the code
- In MSTI1 and MSTI2, Ethernet0/0/1 is the root port.
- In MST1, Ethernet0/0/2 is the specified port. In MST2, Ethernet0/0/1 is blocked;
-
SwitchD
<SwitchD>display stp interface e0/0/1 brief MSTID Port Role STP State Protection ... 1 Ethernet0/0/1 ROOT FORWARDING NONE 2 Ethernet0/0/1 ROOT FORWARDING NONE <SwitchD>display stp interface e0/0/2 brief MSTID Port Role STP State Protection ... 1 Ethernet0/0/2 ALTE DISCARDING NONE 2 Ethernet0/0/2 DESI FORWARDING NONE Copy the code
- In MSTI1 and MSTI2, Ethernet0/0/1 is the root port.
- In MST2, Ethernet0/0/2 is the specified port. In MST1, Ethernet0/0/1 is blocked;
-
reference
- Paramiko documentation
- Huawei S3700 configuration documents
Write in the last
- This paper mainly records my learning process of Paramiko, aiming to improve my understanding of paramiko and programming ability. It is just a way of exploration and may not be a reliable reference for learning.
- There are a lot of content in the article is translation and their own thoughts, there may be many mistakes, but also please everyone can point out the mistakes and shortcomings.
Content of the past
[1] Huawei simulator eNSP was used to build network automation scenarios
Network Basics review
[1] Switch data processing process