Ansible – How to login to Cisco devices using ssh key

Hi, my name is Meru, and it has been a very long time since my last post.

I have just upgraded my GNS3 to version 2.2.3. And the Ansible controller that I used throughout my previous posts, somehow, after many moons of abandonment, decided to broke, and I have to rebuild a new one. But I try to keep the topology similar to the previous one I use.

The Ansible machine is still running on CENTOS 7, but I am using Ansible 2.9.1 (the latest version at the time of this writing). The two routers are still on IOSv15.5(3)M, and the switch in the middle is still using GNS3’s built-in switch, all interface are vlan 1.

ANSIBLE VM eth1 is 192.168.172.10 /24

R1 g0/0 is 192.168.172.11 /24

R2 g0/0 is 192.168.172.12 /24

Alright,

So, on my previous post, we hard-coded the credentials use to login to the router in ‘routers.yml’ file inside the ansible groups_vars directory.

[root@localhost ~]# cd /etc/ansible/group_vars
[root@localhost ~]# vi routers.yml
---

ansible_ssh_user: meru
ansible_ssh_pass: meru

To avoid hard-coding any username and password, lets now create an ssh key, and use it to login to the router

We will first generate the private and public key in the Ansible controller:

[root@localhost ~]# ssh-keygen 
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa): 
/root/.ssh/id_rsa already exists.
Overwrite (y/n)? y
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:MV564yizP9etqRqPCWs2GRjn8WDJgQhX4ZINexAxyBA root@localhost.localdomain
The key's randomart image is:
+---[RSA 2048]----+
|E+B=oo           |
|.o.O. .          |
|  + +. oo .      |
|   o. B. =       |
|     * +S o      |
|    . o .+ .     |
|      +oo .. .   |
|      =*.=. ...  |
|     ooo=+o.o.   |
+----[SHA256]-----+
[root@localhost ~]#

The output above shows that we run the command ssh-keygen in Ansible controller. Then, it says that there is an existing key already in the directory, and it asks we we want to overwrite.

Before I write this blog, I have already created the rsa key as a test, but lets overwrite the existing anyway.

It then asks for a passphrase, which we will leave it as blank (just press enter twice). The key has been generated. Lets take a look at the content of the id_rsa directory:

[root@localhost ~]# cd /root/.ssh/ 
[root@localhost .ssh]# ll
total 12
-rw-------. 1 root root 1679 Dec 17 23:23 id_rsa
-rw-r--r--. 1 root root  408 Dec 17 23:23 id_rsa.pub
-rw-r--r--. 1 root root  229 Dec  9 10:18 known_hosts
[root@localhost .ssh]#

The file called id_rsa is our private key. This key needs to be present in our Ansible machine, and shall not be shared anywhere. Lets take a look at the content of the private key:

[root@localhost ~]# cat /root/.ssh/id_rsa
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAqRIVL3pl3wstRL9J+/RC0syeFqN/84KPiTM9RXrUXTJThih9
TX6saLugggMpa6l3O4ybvRcI/zdIdi0J88jwHgyVd2pdMyfk5TLYvjPei/FGkWPe
tHlcKlRdTo12Kd++35VG2xz/Ql1NG6jybdLiQXbdxaFP0n9zLfxNFldrMvedE9c/
hqk1tPsuxdrc2fymEaf8rsfa6bBkCTYsJCxjBvZeYuxjSx9nTtIenQ8I6Bd9Djxj
hlvKo6hVXMGK8xv52YM7CLTXuNrk1UKHcT1GVxMxrTKxfSq1UkxURXAFVFSZkPKc
igD9JMxUw3a6VnhRL38CnJiAh9FYOzNzbj4SEQIDAQABAoIBAQCjtwvmtqoQqj0C
lgzpOiS/BT6MA3Sx8xpUq9ZIAmHDgSkZ/vke4mvG2vDZFIC2bRo1AroIB1dB82Fq
dcBuXYQORPcy9D8deyMNwgfZXlbAwjkoLkIIFoBlyN21ZAwpDi1BScacBF80/y3c
e+OM7ykCZTzo04R8+8cnn2lyGeKHLWTWMFYwYmBBp2qBRMFRJl9Te0AGA6tfL4Kv
0fSi87PadgN76awqktJOolkDIaFl425+39QpZsbi4Yz7X42ld9apgsIBhDkKv5rx
mZh6JN2OiKFsGb4MmLPj1kwlwUugflAPutDC/GKZIkoHvIa9urK8X/Jxr/+q0wTQ
AB0ApCH1AoGBAOAxifVaSsM+gUd/c0/9v/+nMlDmdYuonwpFaZa8PTejhmvzpbLE
W57xgWZVXiHMiz6Y9xl/+k80h9J+89YkVV+enlK4gRwd2HozSqCQZVFFPI7ClpPq
rD9Q/JgNpMwVzws1HMEk7fmw86XsD1UBEtDOrIbJLjZMFI23ghq4GTHnAoGBAMEO
irOQ58byOUMrks/u/fr4+kUNYV7yaOiXIu3Rp7+vSNuntfZdmR6ugBu9YS5l3tWB
RTp/SpSDwk5t2b1Sxw3B8y12ajzVftNVadll1plcFkS6cIH12QSF8qCMeB04l42u
TGGZDSp4l0BSkiqHgTaHKIh6VMeGdsyPQBnMc41HAoGBAJ1d6IqSMHxP+YroSVbS
tNyMzeK/ga7gU5JwGqe1xfqyC/7mbV7IOc+dkcj1DzgGTY9I69m4XUkPhG8asamU
o2CVBGjoHahQws00B6Qg9x1Ozi0fJXb6eKGRBVzt5sliXYxuYZqpg2mK7dt/kVuH
f1+WUr/ho1B1os+/CdIbe4PtAoGAP0ct7Ud8wPfaDws0NtWGCAIdvg5xsPZRrpMD
TuIeUrT4c47YFKV/L9BQi0camGOpk1+oulDIuD8rrBtTeDuupPLCa09Z6RCtXkWr
yScZHPFTFzno8KANfu8MpNUF9cX73uOXg5Hv/9DA+sNVx3zcvGu2vG0kZrXLMKdv
gkVCRrcCgYEAuHqA8IqCuIqSaD5ecsgS+0C9Gu481Daf8A1Dxb2GTx5Px5hoj/m5
pSY2tUvr5Pfe4SRpppRKvSXOBEfY4VF0S3z6nAn1Du8EojR44T0kEkOws4Flyqbw
PqU020I+ZZHm4AFN5sMxULVnRz83PnKn/sjFOoXWDErf1v/E1XBYstg=
-----END RSA PRIVATE KEY-----
[root@localhost ~]#

The id_rsa.pub is the public key. This is the key that we need to enter in our router. Lets take a look at the content of the public key:

[root@localhost .ssh]# cat /root/.ssh/id_rsa.pub 
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCpEhUvemXfCy1Ev0n79ELSzJ4Wo3/zgo+JMz1FetRdMlOGKH1Nfqxou6CCAylrqXc7jJu9Fwj/N0h2LQnzyPAeDJV3al0zJ+TlMti+M96L8UaRY960eVwqVF1OjXYp377flUbbHP9CXU0bqPJt0uJBdt3FoU/Sf3Mt/E0WV2sy950T1z+GqTW0+y7F2tzZ/KYRp/yux9rpsGQJNiwkLGMG9l5i7GNLH2dO0h6dDwjoF30OPGOGW8qjqFVcwYrzG/nZgzsItNe42uTVQodxPUZXEzGtMrF9KrVSTFRFcAVUVJmQ8pyKAP0kzFTDdrpWeFEvfwKcmICH0Vg7M3NuPhIR root@localhost.localdomain
[root@localhost .ssh]#

Now lets jump to router R1. We will configure the router to accept username ‘meru’ to login using certificate. If the certificate is not present, then the router will fallback to use local password.

First, we need to set the domain name on the router, otherwise we cannot generate an rsa key on the router:

R1(config)#ip domain name meru.com

Next, we configure a local username:

R1(config)#username meru privilege 15 password meru

Next, we configure the line vty 0 4 to accept ssh, and use login local:

R1(config)#line vty 0 4
R1(config-line)#transport input ssh
R1(config-line)#login local

Next, we create an rsa key on the router. When asked for how many bits in the modulus, lets put in 1024:

R1(config)#crypto key generate rsa
The name for the keys will be: R1.meru.com
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]: 1024
% Generating 1024 bit RSA keys, keys will be non-exportable...
[OK] (elapsed time was 1 seconds)

R1(config)#
*Dec 18 23:57:01.249: %SSH-5-ENABLED: SSH 1.99 has been enabled
R1(config)#

Next, just to be sure, lets set the ssh version to version 2:

R1(config)#ip ssh ver 2

We are now going to enter the public key into the router. The public key, however, is generated as one very long string. But Cisco IOS only supports 254 characters on a single line at a time. This means we cannot just highlight the public key and paste it all in to the router (if you do this, the router will stop paste-ing at the 254th character).

The trick is, as I found out from networklessons.com, is to use this command:

[root@localhost ansible]# fold -b -w 72 /root/.ssh/id_rsa.pub       
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCpEhUvemXfCy1Ev0n79ELSzJ4Wo3/zgo+J
Mz1FetRdMlOGKH1Nfqxou6CCAylrqXc7jJu9Fwj/N0h2LQnzyPAeDJV3al0zJ+TlMti+M96L
8UaRY960eVwqVF1OjXYp377flUbbHP9CXU0bqPJt0uJBdt3FoU/Sf3Mt/E0WV2sy950T1z+G
qTW0+y7F2tzZ/KYRp/yux9rpsGQJNiwkLGMG9l5i7GNLH2dO0h6dDwjoF30OPGOGW8qjqFVc
wYrzG/nZgzsItNe42uTVQodxPUZXEzGtMrF9KrVSTFRFcAVUVJmQ8pyKAP0kzFTDdrpWeFEv
fwKcmICH0Vg7M3NuPhIR root@localhost.localdomain
[root@localhost ansible]#

The fold command lets you wrap the lines into shorter lines. The option -w sets the line’s width. In this case, we set the width as 72 characters per line.

Now that we get that one sorted out, lets copy the public key into the router, one line at a time:

R1(config)#ip ssh pubkey-chain 
R1(conf-ssh-pubkey)#username meru
R1(conf-ssh-pubkey-user)#key-string 
R1(conf-ssh-pubkey-data)#$2EAAAADAQABAAABAQCpEhUvemXfCy1Ev0n79ELSzJ4Wo3/zgo+J
R1(conf-ssh-pubkey-data)#$6CCAylrqXc7jJu9Fwj/N0h2LQnzyPAeDJV3al0zJ+TlMti+M96L
R1(conf-ssh-pubkey-data)#$77flUbbHP9CXU0bqPJt0uJBdt3FoU/Sf3Mt/E0WV2sy950T1z+G
R1(conf-ssh-pubkey-data)#$9rpsGQJNiwkLGMG9l5i7GNLH2dO0h6dDwjoF30OPGOGW8qjqFVc
R1(conf-ssh-pubkey-data)#$odxPUZXEzGtMrF9KrVSTFRFcAVUVJmQ8pyKAP0kzFTDdrpWeFEv
R1(conf-ssh-pubkey-data)#fwKcmICH0Vg7M3NuPhIR root@localhost.localdomain      
R1(conf-ssh-pubkey-data)#exit
R1(conf-ssh-pubkey-user)#exit
R1(conf-ssh-pubkey)#exit
R1(config)#end
R1#

OK. Now, lets check if we can login from the Ansible machine to the router without having to type in our local password:

[root@localhost ansible]# ssh meru@192.168.172.11
The authenticity of host '192.168.172.11 (192.168.172.11)' can't be established.
RSA key fingerprint is SHA256:13DbaAF5Sb13rBvkHQQjhW80GnltZxW89X6sFEKVoCc.
RSA key fingerprint is MD5:6f:de:da:fc:48:b1:48:3c:46:bd:02:b7:c2:f4:e8:62.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.172.11' (RSA) to the list of known hosts.

**************************************************************************
* IOSv is strictly limited to use for evaluation, demonstration and IOS  *
* education. IOSv is provided as-is and is not supported by Cisco's      *
* Technical Advisory Center. Any use or disclosure, in whole or in part, *
* of the IOSv Software or Documentation to any third party for any       *
* purposes is expressly prohibited except as otherwise authorized by     *
* Cisco in writing.                                                      *
**************************************************************************
**************************************************************************
* IOSv is strictly limited to use for evaluation, demonstration and IOS  *
* education. IOSv is provided as-is and is not supported by Cisco's      *
* Technical Advisory Center. Any use or disclosure, in whole or in part, *
* of the IOSv Software or Documentation to any third party for any       *
* purposes is expressly prohibited except as otherwise authorized by     *
* Cisco in writing.                                                      *
**************************************************************************
R1#

Fantastic, it works! We went straight into privileged mode (because we set privilege 15 for username meru), without being prompted for password.

Because this is the first time we login from the Ansible machine to R1, it will display a warning message that the authenticiy of R1 cannot be established. When asked id we want to continue, just hit enter.

The second time you login to R1, you will not see that warning message again, because we have accepted R1 into our known_hosts.

Now that Ansible can login to the router without password, we can remove the hard-coded password on routers.yml group_vars 🙂

See you on the next post!

Note: Special thanks to networklessons.com for the information about the fold command. Very useful little trick. If you want to see the detail, please visit their page here:

https://networklessons.com/uncategorized/ssh-public-key-authentication-cisco-ios

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.