Book Image

Network Automation Cookbook

By : Karim Okasha
Book Image

Network Automation Cookbook

By: Karim Okasha

Overview of this book

Network Automation Cookbook is designed to help system administrators, network engineers, and infrastructure automation engineers to centrally manage switches, routers, and other devices in their organization's network. This book will help you gain hands-on experience in automating enterprise networks and take you through core network automation techniques using the latest version of Ansible and Python. With the help of practical recipes, you'll learn how to build a network infrastructure that can be easily managed and updated as it scales through a large number of devices. You'll also cover topics related to security automation and get to grips with essential techniques to maintain network robustness. As you make progress, the book will show you how to automate networks on public cloud providers such as AWS, Google Cloud Platform, and Azure. Finally, you will get up and running with Ansible 2.9 and discover troubleshooting techniques and network automation best practices. By the end of this book, you'll be able to use Ansible to automate modern network devices and integrate third-party tools such as NAPALM, NetBox, and Batfish easily to build robust network automation solutions.
Table of Contents (15 chapters)

Connecting to Cisco IOS devices

In this recipe, we will outline how to connect to Cisco IOS devices from Ansible via SSH in order to start managing devices from Ansible.

Getting ready

In order to follow along with this recipe, an Ansible inventory file should be constructed as per the previous recipe. IP reachability between the Ansible control machine and all the devices in the network must be configured.

How to do it...

  1. Inside the ch2_ios directory, create the groups_vars folder.
  2. Inside the group_vars folder, create the network.yml file with the following content:
$cat network.yml
Ansible_network_os: ios
Ansible_connection: network_cli
Ansible_user: lab
Ansible_password: lab123
Ansible_become: yes
Ansible_become_password: admin123
Ansible_become_method: enable
  1. On all IOS devices, ensure that the following is configured to set up SSH access:
!
hostname <device_hostname>
!
ip domain name <domain_name>
!
username lab secret 5 <password_for_lab_user>.
!
enable secret 5 <enable_password>.
!
line vty 0 4
login local
transport input SSH
!
  1. Generate SSH keys on the Cisco IOS devices from the config mode, as shown in the following code snippet:
(config)#crypto key generate rsa
Choose the size of the key modulus in the range of 360 to 4096 for your
General Purpose Keys. Choosing a key modulus greater than 512 may take
a few minutes.
How many bits in the modulus [512]: 2048
% Generating 2048 bit RSA keys, keys will be non-exportable...
[OK] (elapsed time was 0 seconds)
  1. Update the Ansible.cfg file with the following highlighted parameters:
$ cat Ansible.cfg
[defaults]
host_key_checking=False

How it works...

In our sample network, we will use SSH to set up the connection between Ansible and our Cisco devices. In this setup, Ansible will use SSH in order to establish the connection to our Cisco devices with a view to start managing it. We will use username/password authentication in order to authenticate our Ansible control node with our Cisco devices.

On the Cisco devices, we must ensure that SSH keys are present in order to have a functional SSH server on the Cisco devices. The following code snippet outlines the status of the SSH server on the Cisco device prior to generating the SSH keys:

wan01#show ip SSH
SSH Disabled - version 2.0
%Please create RSA keys to enable SSH (and of atleast 768 bits for SSH v2).
Authentication methods:publickey,keyboard-interactive,password
Authentication Publickey Algorithms:x509v3-SSH-rsa,SSH-rsa
Hostkey Algorithms:x509v3-SSH-rsa,SSH-rsa
Encryption Algorithms:aes128-ctr,aes192-ctr,aes256-ctr
MAC Algorithms:hmac-sha2-256,hmac-sha2-512,hmac-sha1,hmac-sha1-96
KEX Algorithms:diffie-hellman-group-exchange-sha1,diffie-hellman-group14-sha1
Authentication timeout: 120 secs; Authentication retries: 3
Minimum expected Diffie Hellman key size : 2048 bits
IOS Keys in SECSH format(SSH-rsa, base64 encoded): NONE

Once we create the SSH keys, the SSH server on the Cisco device is operational, and is ready to accept an SSH connection from the Ansible control node.

On the Ansible machine, we include all the variables required to establish the SSH connection to the managed devices in the network.yml file. As per our inventory file, the network group includes all the devices within our topology, and so all the attributes that we configure in this file will apply to all the devices in our inventory. The following is a breakdown of the attributes that we included in the file:

  • Ansible_connection: This establishes how Ansible connects to the device. In this scenario, we set it to network_cli to indicate that we will use SSH to connect to a network device.
  • Ansible_network_os: When using network_cli as the connection plugin to connect to the network device, we must indicate which network OS Ansible will be connecting to, so as to use the correct SSH parameters with the devices. In this scenario, we will set it to ios, since all the devices in our topology are IOS-based devices.
  • Ansible_user: This parameter specifies the username that Ansible will use to establish the SSH session with the network device.
  • Ansible_password: This parameter specifies the password that Ansible will use to establish the SSH session with the network device.
  • Ansible_become: This instructs Ansible to use the enable command to enter privileged mode when configuring or executing show commands on the managed device. We set this to yes in our context, since we will require privileged mode to configure the devices.
  • Ansible_become_password: This specifies the enable password to use in order to enter privileged mode on the managed IOS device.
  • Ansible_become_method: This option specifies the method to use in order to enter privileged mode. In our scenario, this is the enable command on IOS devices.
In this recipe, I have defined the SSH password and the enable passwords as plain text just for simplicity; however, this is highly discouraged. We should use Ansible-vault to secure the passwords, as outlined in the Ansible Vault recipe in the previous chapter.

On the Cisco devices, we set up the required username and password so that Ansible can open an SSH connection to the managed Cisco IOS devices. We also configure an enable password to be able to enter privileged mode, and to make configuration changes. Once we apply all of these configurations to the devices, we are ready to set up Ansible.

In any SSH connection, when an SSH client (Ansible control node in our case) connects to an SSH server (Cisco devices in our case), the server sends a copy of its public key to the client before the client logs in. This is used to establish the secure channel between the client and the server, and to authenticate the server to the client in order to prevent any man-in-the-middle attacks. So, at the start of a new SSH session involving a new device, we see the following prompt:

$SSH [email protected]
The authenticity of host '172.20.1.18 (172.20.1.18)' can't be established.
RSA key fingerprint is SHA256:KnWOalnENZfPokYYdIG3Ogm9HDnXIwjh/it3cqdiRRQ.
RSA key fingerprint is MD5:af:18:4b:4e:84:19:a6:8d:82:17:51:d5:ee:eb:16:8d.
Are you sure you want to continue connecting (yes/no)?

When the SSH client initiates the SSH connection to the client, the SSH server sends its public key to the client in order to authenticate itself to the client. The client searches for the public key in its local known hosts files (in the ~/.SSH/known_hosts or /etc/SSH/SSH_known_hosts files). In the event that it does not find the public key for this machine in its local known hosts file, it will prompt the user to add this new key to its local database, and this is the prompt that we see when we initiate the SSH connection.

In order to simplify the SSH connection setup between the Ansible control node and its remotely managed hosts, we can disable this host checking. We can do this by telling Ansible to ignore host keys and not to add them to the known hosts files by setting host_key_checking to False in the Ansible.cfg configuration file.

Disabling host key checking is not a best practice, and we are only showing it as it is a lab setup. In the next section, we will outline an alternative method to establish the SSH connection between Ansible and its remote managed devices.

There's more...

If we need to verify the identity of the SSH hosts that we will connect to, and thereby enable host_key_checking, we can automate the addition of the SSH public key of the remote managed hosts to the ~/.SSH/known_hosts file using Ansible. We create a new Ansible playbook that will run on the Ansible control machine to connect to the remote devices using the ssk-keyscan command. We then collect the SSH public keys for the remote machines and add them to the ~/.SSH/known_hosts file. The method is outlined here:

  1. Create a new playbook pb_gather_SSH_keys.yml file and add the following play:
- name: "Play2: Record Keys in Known Hosts file"
hosts: localhost
vars:
- hosts_file: "~/.SSH/known_hosts"
tasks:
- name: create know hosts file
file:
path: "{{ hosts_file }}"
state: file
changed_when: false
  1. Update the playbook and add another play within the same playbook to save and store the SSH public keys for the remote managed nodes:
- name: "Play2: Record Keys in Known Hosts file"
hosts: localhost
vars:
- hosts_file: "~/.SSH/known_hosts"
tasks:
- name: create know hosts file
file:
path: "{{ hosts_file }}"
state: file
changed_when: false
- name: Populate the known_hosts file
blockinfile:
block: |
{% for host in groups['all'] if hostvars[host].SSH_keys.stdout != ''
%}
{{ hostvars[host].SSH_keys.stdout}}
{% endfor %}
path: "{{ hosts_file }}"
create: yes

In our new playbook, we have a play that targets all our managed devices by setting the hosts parameter to all. In this play, we have a single task, which we run on the Ansible control node (using the delegate_to localhost) to issue the SSH-keyscan command, which returns the SSH public key for the remote device, as shown in the following code:

$ SSH-keyscan 172.20.1.22

# 172.20.1.22:22 SSH-2.0-Cisco-1.25
172.20.1.22 SSH-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDTwrH4phzRnW/RsC8eXMh/accIErRfkgffDWBGSdEX0r9EwAa6p2uFMWj8dq6kvrREuhqpgFyMoWmpgdx5Cr+10kEonr8So5yHhOhqG1SJO9RyzAb93H0P0ro5DXFK8A/Ww+m++avyZ9dShuWGxKj9CDM6dxFLg9ZU/9vlzkwtyKF/+mdWNGoSiCbcBg7LrOgZ7Id7oxnhEhkrVIa+IxxGa5Pwc73eR45Uf7QyYZXPC0RTOm6aH2f9+8oj+vQMsAzXmeudpRgAu151qUH3nEG9HIgUxwhvmi4MaTC+psmsGg2x26PKTOeX9eLs4RHquVS3nySwv4arqVzDqWf6aruJ
In this task, we are using delegate_to as being equal to localhost, as Ansible will try to connect to the remote devices and issue the command on the remote device by default. In our case, this is not what we need; we need to issue this command from the Ansible control node. So, we use delegate_to as being equal to localhost in order to enforce this behavior.

We run the second play on the Ansible control host by setting hosts to localhost, and we execute tasks to create the known hosts file (if not already present) and to populate this file with the data that we captured in the first play using the SSH_keys variable. We run this playbook on the Ansible control machine to store the SSH keys from the remotely managed nodes prior to running any of our playbooks.