If you have been following along with the posts so far, you will know that we are using Vagrant to help automate the provision of VNFs within VirtualBox.

Vagrant uses “Box” files as a template to spin up multiple copies of a Virtual Machine (VM). To re-use this Cisco CSR 1000V VM, we need to convert it into a re-usable Box image.

Box files have a few requirements that should be applied to the VM to operate correctly. These are:

  • The first interface (management) is set to get its address from DHCP.
  • There is a user called vagrant configured with a password of “vagrant”.
  • Vagrant must be able to SSH to the VM.
  • There is a default SSH key applied to the “vagrant” user.

So, let’s configure these settings on our newly created IOS image. We’ll start with DHCP. Enter the commands below in the CLI:

Router>enable
Router#configure terminal
Router(config)#interface gigabitEthernet 1
Router(config-if)#ip address dhcp
Router(config-if)#end
Router#show ip int br
Interface              IP-Address      OK? Method Status                Protocol
GigabitEthernet1       192.168.56.106  YES DHCP   up                    up
Router#write memory
Building configuration...
[OK]

Now that we have DHCP configured on our GigabitEthernet1 interface, let’s set up the “vagrant” user and SSH.

Router#configure terminal
Router(config)#username vagrant privilege 15 secret vagrant
Router(config)#aaa new-model
Router(config)#aaa authentication login default local
Router(config)#aaa authorization exec default local
Router(config)#hostname ios
ios(config)#ip domain name nfv.dev
ios(config)#crypto key generate rsa modulus 2048
% You already have RSA keys defined named ios.nfv.dev.
% They will be replaced.
% The key modulus size is 2048 bits
% Generating 2048 bit RSA keys, keys will be non-exportable...
[OK] (elapsed time was 0 seconds)
ios(config)#ip ssh version 2
ios(config)#ip scp server enable
ios(config)#end
ios#write memory

Open up a WSL2 terminal (either in Windows Terminal, VSCode or by searching “Ubuntu” in the start menu”) and try SSHing to the CSR VM (if asked to trust the host, type “yes”):

nfvdev@ubuntu:/mnt/c/Users/nfvdev$ ssh vagrant@192.168.56.106
The authenticity of host '192.168.56.106 (192.168.56.106)' can't be established.
RSA key fingerprint is SHA256:27mVVH1Tt9UBOM1EXwVbkNGDXXtMFxD30DxINurfZ6Q.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '192.168.56.106' (RSA) to the list of known hosts.
Password:

ios#

Finally, we should add the Vagrant default SSH key into the vagrant user to log in without using a password. The Vagrant default SSH key can be found at https://github.com/hashicorp/vagrant/blob/master/keys/vagrant.pub.

Note: This is very insecure and should not be used in a production environment.


ios#configure terminal
ios(config)#ip ssh pubkey-chain
ios(conf-ssh-pubkey)#username vagrant
ios(conf-ssh-pubkey-user)#key-string
ios(conf-ssh-pubkey-data)#AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9e
ios(conf-ssh-pubkey-data)#WW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS
ios(conf-ssh-pubkey-data)#0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBck
ios(conf-ssh-pubkey-data)#FXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUec
ios(conf-ssh-pubkey-data)#p4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd
ios(conf-ssh-pubkey-data)#0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96
ios(conf-ssh-pubkey-data)#hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ==
ios(conf-ssh-pubkey-data)#exit
ios(conf-ssh-pubkey-user)#end
ios#write memory

Great, that should be it. Let’s test it out.

nfvdev@ubuntu:/mnt/c/Users/nfvdev$ ssh -i .vagrant.d/insecure_private_key vagrant@192.168.56.106

ios#

Awesome, we logged into the Cisco CSR without a password using the Vagrant insure private key.

Before creating our Box file, we need to do one more thing. We statically set the serial port on the VM to be “\\.\pipe\csr”. If we want to create multiple VMs, we need a different name for each VM’s serial port. We could develop a fancy way of doing this; however, we do not need the serial port as we have SSH working now.

Power down the VM. Go into the VM’s Settings and then “Serial Ports”. Then change “Port 1”, “Port Mode” to “Disconnected”.

VirtualBox Serial

Ok, now it’s time to package everything up into a reusable Box file. From within Linux, run “vagrant package –base <name_of_vm> –output <name_of_output_file>.box”:

nfvdev@ubuntu:/mnt/c/Users/nfvdev$ export VAGRANT_HOME="/mnt/c/Users/nfvdev/.vagrant.d"
nfvdev@ubuntu:/mnt/c/Users/nfvdev$ vagrant package --base csr1000v-universalk9.17.03.02-serial --output csr1000v-universalk9.17.03.02-serial.box
==> csr1000v-universalk9.17.03.02-serial: Exporting VM...
==> csr1000v-universalk9.17.03.02-serial: Compressing package to: /mnt/c/Users/nfvdev/csr1000v-universalk9.17.03.02-serial.box
nfvdev@ubuntu:/mnt/c/Users/nfvdev$ export VAGRANT_HOME="~/.vagrant.d"

This creates a file called csr1000v-universalk9.17.03.02-serial.box for us to reuse. We can then test the box image by creating a Vagrantfile with the following contents. The entire file available at: https://github.com/nfvdev/nfvdev-blog/blob/main/05-how-to-create-a-cisco-csr-1000v-box-image/single/Vagrantfile

# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant.configure("2") do |config|
  config.ssh.insert_key = false # By default, Vagrant will try and change the insecure SSH key to a better one. This won't work for us as it does not know how to talk ios.
  config.vm.box = "csr1000v-universalk9.17.03.02-serial.box" # Use the ios box image
  config.vm.synced_folder '.', '/vagrant', disabled: true # Disable shared folders
  config.vm.guest = :linux # Tell Vagrant that it is Linux so it doesn't error
end

And then type “vagrant up” to spin up the VM.

nfvdev@ubuntu:/mnt/c/Users/nfvdev$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Importing base box 'csr1000v-universalk9.17.03.02-serial.box'...
==> default: Matching MAC address for NAT networking...
==> default: Setting the name of the VM: nfvdev_default_1642936280913_64423
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
==> default: Forwarding ports...
    default: 22 (guest) => 2222 (host) (adapter 1)
    default: 22 (guest) => 2222 (host) (adapter 1)
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 172.25.64.1:2222
    default: SSH username: vagrant
    default: SSH auth method: private key
==> default: Machine booted and ready!
==> default: Checking for guest additions in VM...
    default: No guest additions were detected on the base box for this VM! Guest
    default: additions are required for forwarded ports, shared folders, host only
    default: networking, and more. If SSH fails on this machine, please install
    default: the guest additions and repackage the box to continue.
    default:
    default: This is not an error message; everything may continue to work properly,
    default: in which case you may ignore this message.

Finally, we can check that Vagrant can SSH into the newly created IOS VM:

nfvdev@ubuntu:/mnt/c/Users/nfvdev$ vagrant ssh default

ios#exit
Connection to 172.25.64.1 closed.

We can destroy the VM with “vagrant destroy” and remove our template VM.

nfvdev@ubuntu:/mnt/c/Users/nfvdev$ vagrant destroy
    default: Are you sure you want to destroy the 'default' VM? [y/N] y
==> default: Forcing shutdown of VM...
==> default: Destroying VM and associated drives...
VirtualBox Remove VM

IOS Vagrant Mini-Lab

Now that we have our IOS Box file created, we can use it in a mini-lab. This will set up 2 IOS routers connected back to back and do some ping tests between them.

nfvdev@ubuntu:/mnt/c/Users/nfvdev/boxes$ vagrant up
Bringing machine 'ios1' up with 'virtualbox' provider...
Bringing machine 'ios2' up with 'virtualbox' provider...
==> ios1: Box 'csr1000v-universalk9.17.03.02-serial.box' could not be found. Attempting to find and install...
    ios1: Box Provider: virtualbox
    ios1: Box Version: >= 0
==> ios1: Box file was not detected as metadata. Adding it directly...
==> ios1: Adding box 'csr1000v-universalk9.17.03.02-serial.box' (v0) for provider: virtualbox
    ios1: Unpacking necessary files from: file:///mnt/c/Users/nfvdev/boxes/csr1000v-universalk9.17.03.02-serial.box
==> ios1: Successfully added box 'csr1000v-universalk9.17.03.02-serial.box' (v0) for 'virtualbox'!
==> ios1: Importing base box 'csr1000v-universalk9.17.03.02-serial.box'...
==> ios1: Matching MAC address for NAT networking...
==> ios1: Setting the name of the VM: boxes_ios1_1642937334161_8769
==> ios1: Clearing any previously set network interfaces...
==> ios1: Preparing network interfaces based on configuration...
    ios1: Adapter 1: nat
    ios1: Adapter 2: intnet
    ios1: Adapter 3: intnet
==> ios1: Forwarding ports...
    ios1: 22 (guest) => 2222 (host) (adapter 1)
    ios1: 22 (guest) => 2222 (host) (adapter 1)
==> ios1: Booting VM...
==> ios1: Waiting for machine to boot. This may take a few minutes...
    ios1: SSH address: 172.25.64.1:2222
    ios1: SSH username: vagrant
    ios1: SSH auth method: private key
==> ios1: Machine booted and ready!
==> ios1: Checking for guest additions in VM...
    ios1: No guest additions were detected on the base box for this VM! Guest
    ios1: additions are required for forwarded ports, shared folders, host only
    ios1: networking, and more. If SSH fails on this machine, please install
    ios1: the guest additions and repackage the box to continue.
    ios1:
    ios1: This is not an error message; everything may continue to work properly,
    ios1: in which case you may ignore this message.
==> ios2: Box 'csr1000v-universalk9.17.03.02-serial.box' could not be found. Attempting to find and install...
    ios2: Box Provider: virtualbox
    ios2: Box Version: >= 0
==> ios2: Box file was not detected as metadata. Adding it directly...
==> ios2: Adding box 'csr1000v-universalk9.17.03.02-serial.box' (v0) for provider: virtualbox
==> ios2: Importing base box 'csr1000v-universalk9.17.03.02-serial.box'...
==> ios2: Matching MAC address for NAT networking...
==> ios2: Setting the name of the VM: boxes_ios2_1642937512209_62343
==> ios2: Fixed port collision for 22 => 2222. Now on port 2200.
==> ios2: Clearing any previously set network interfaces...
==> ios2: Preparing network interfaces based on configuration...
    ios2: Adapter 1: nat
    ios2: Adapter 2: intnet
    ios2: Adapter 3: intnet
==> ios2: Forwarding ports...
    ios2: 22 (guest) => 2200 (host) (adapter 1)
    ios2: 22 (guest) => 2200 (host) (adapter 1)
==> ios2: Booting VM...
==> ios2: Waiting for machine to boot. This may take a few minutes...
    ios2: SSH address: 172.25.64.1:2200
    ios2: SSH username: vagrant
    ios2: SSH auth method: private key
==> ios2: Machine booted and ready!
==> ios2: Checking for guest additions in VM...
    ios2: No guest additions were detected on the base box for this VM! Guest
    ios2: additions are required for forwarded ports, shared folders, host only
    ios2: networking, and more. If SSH fails on this machine, please install
    ios2: the guest additions and repackage the box to continue.
    ios2:
    ios2: This is not an error message; everything may continue to work properly,
    ios2: in which case you may ignore this message.
nfvdev@ubuntu:/mnt/c/Users/nfvdev/boxes$ vagrant ssh ios1

ios#configure terminal
Enter configuration commands, one per line.  End with CNTL/Z.
ios(config)# hostname ios1
ios1(config)# interface GigabitEthernet 2
ios1(config-if)#  ip address 10.100.12.1 255.255.255.0
ios1(config-if)#  description External
ios1(config-if)#  no shutdown
ios1(config-if)# interface GigabitEthernet 3
ios1(config-if)#  ip address 10.100.1.1 255.255.255.0
ios1(config-if)#  description Internal
ios1(config-if)#  no shutdown
ios1(config-if)# router ospf 1
ios1(config-router)#  router-id 10.100.12.1
ios1(config-router)#  passive-interface GigabitEthernet 3
ios1(config-router)#  network 10.100.12.0 0.0.0.255 area 0.0.0.0
ios1(config-router)#  network 10.100.1.0 0.0.0.255 area 1.1.1.1
ios1(config-router)# end
ios1#write memory
Building configuration...
[OK]
ios1#exit
Connection to 172.25.64.1 closed.
nfvdev@ubuntu:/mnt/c/Users/nfvdev/boxes$ vagrant ssh ios2

ios#configure terminal
Enter configuration commands, one per line.  End with CNTL/Z.
ios(config)# hostname ios2
ios2(config)# interface GigabitEthernet 2
ios2(config-if)#  ip address 10.100.12.2 255.255.255.0
ios2(config-if)#  description External
ios2(config-if)#  no shutdown
ios2(config-if)# interface GigabitEthernet 3
ios2(config-if)#  ip address 10.100.2.2 255.255.255.0
ios2(config-if)#  description Internal
ios2(config-if)#  no shutdown
ios2(config-if)# router ospf 1
ios2(config-router)#  router-id 10.100.12.2
ios2(config-router)#  passive-interface GigabitEthernet 3
ios2(config-router)#  network 10.100.12.0 0.0.0.255 area 0.0.0.0
ios2(config-router)#  network 10.100.2.0 0.0.0.255 area 2.2.2.2
ios2(config-router)# end
ios2#write memory
Building configuration...
[OK]
ios2#show ip ospf neighbor
Neighbor ID     Pri   State           Dead Time   Address         Interface
10.100.12.1       1   FULL/BDR        00:00:35    10.100.12.1     GigabitEthernet2
ios2#ping 10.100.1.1 source 10.100.2.2
Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 10.100.1.1, timeout is 2 seconds:
Packet sent with a source address of 10.100.2.2
!!!!!
Success rate is 100 percent (5/5), round-trip min/avg/max = 1/1/1 ms
ios2#exit
Connection to 172.25.64.1 closed.
nfvdev@ubuntu:/mnt/c/Users/nfvdev/boxes$ vagrant destroy -f
==> ios2: Forcing shutdown of VM...
==> ios2: Destroying VM and associated drives...
==> ios1: Forcing shutdown of VM...
==> ios1: Destroying VM and associated drives...