Intro

In this post I will install a Juniper vSRX for use with the vagrant-libvirt provider. Prior to installing the vSRX there are some required steps to get the KVM host prepped which I will not cover. These steps are covered in the Juniper documentation here.

Note
I originally covered this topic in December 2017. I have learned alot of lessons since then and there have been some nice additions to Vagrant to make things a bit more seamless. This post is a revamp of that previous post.

For reference the following software will be used in this post.

  • Juniper vSRX - 18.1R1.9
  • Vagrant - Vagrant 2.1.0
  • vagrant-libvirt - 0.0.43
  • Ubuntu - 1604

Download

Juniper does not provide a Vagrant Libvirt box, but they do provide a qcow2 image. You will need to download the qcow2 from the Juniper software download section.

Important
You will need either a valid support contract or rights to download this image. Prior to working in my current role I was able to raise a case with Juniper support requesting access to the vSRX image for the purpose of preparing for my new role.

Install

Create and change into directory for vSRX files. I like to keep my custom vagrant boxes under ~/vagrant/boxes/ .

cmd
mkdir -p ~/vagrant/boxes/juniper
cd ~/vagrant/boxes/juniper

Copy the qcow2 file downloaded earlier to the ~/vagrant/boxes/juniper directory.

cmd
cp ~/Downloads/media-vsrx-vmdisk-15.1X49-D120.3.qcow2 .

The maintainers of the vagrant-libvirt plugin have a script that can be used to convert qcow2 images to a vagrant box. Download the libvirt conversion script.

cmd
curl -O https://raw.githubusercontent.com/vagrant-libvirt/vagrant-libvirt/master/tools/create_box.sh

Create a bootstrap configuration file named juniper.conf with following contents that will be applied on boot to the vSRX.

file
system {
    host-name vsrx;
    root-authentication {
        encrypted-password "$1$nq.N1UsY$JxA/ESAj3KuXseXE597gg0"; ## SECRET-DATA
        ssh-rsa "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant insecure public key"; ## SECRET-DATA
    }
    login {
        user vagrant {
            uid 2000;
            class super-user;
            authentication {
                ssh-rsa "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant insecure public key"; ## SECRET-DATA
            }
        }
    }
    services {
        ssh {
            root-login allow;
        }
        netconf {
            ssh;
        }
    }
    syslog {
        user * {
            any emergency;
        }
        file messages {
            any any;
            authorization info;
        }
        file interactive-commands {
            interactive-commands any;
        }
    }
    license {
        autoupdate {
            url https://ae1.juniper.net/junos/key_retrieval;
        }
    }
}
interfaces {
    fxp0 {
        unit 0 {
            family inet {
                dhcp;
            }
        }
    }
}
security {
    forwarding-options {
        family {
            inet6 {
                mode packet-based;
            }
            mpls {
                mode packet-based;
            }
        }
    }
}
Note
I am creating this VM in packet-mode. This essentially makes the vSRX a router. To use the vSRX as a firewall omit the security > forwarding-options stanza.

Place the juniper.conf file in a directory named iso_dir .

cmd
ls -l iso_dir
# output


total 4
-rw-rw-r-- 1 bradmin bradmin 2050 Nov 29 23:13 juniper.conf

Create an iso image named bootstrap.iso .

cmd
mkisofs -l -o bootstrap.iso iso_dir

# output

I: -input-charset not specified, using utf-8 (detected in locale settings)
Total translation table size: 0
Total rockridge attributes bytes: 0
Total directory bytes: 0
Path table size(bytes): 10
Max brk space used 0
176 extents written (0 MB)

Now lets build the vSRX VM.

cmd
virt-install \
    --name vsrx \
    --os-type linux \
    --os-variant rhel7 \
    --cpu host \
    --vcpus=2 \
    --hvm \
    --arch=x86_64 \
    --ram 4096 \
    --disk path=junos-media-vsrx-x86-64-vmdisk-18.1R1.9.qcow2,size=16,device=disk,bus=ide,format=qcow2 \
    --disk path=bootstrap.iso,device=cdrom,bus=ide \
    --boot hd \
    --network=network:vagrant-libvirt,model=virtio \
    --graphics none \
    --import

A virtual console will be connected to the VM and you will be able to see the VM boot up. Once the VM is booted login with the username root and password Juniper and check the IP address assigned to the fxp0.0 interface.

cmd
vsrx (ttyd0)

login: root
Password:

--- JUNOS 18.1R1.9 Kernel 64-bit  JNPR-11.0-20180308.0604c57_buil
root@vsrx%
root@vsrx% cli
root@vsrx> show interfaces terse | match fxp0.0

# output

fxp0.0                  up    up   inet     192.168.121.99/24

From another host terminal confirm that it is possible to SSH to the VM with the Vagrant insecure_private_key .

cmd
# from host shell

ssh vagrant@192.168.121.99 -i ~/.vagrant.d/insecure_private_key

# now logged into guest vsrx

--- JUNOS 18.1R1.9 Kernel 64-bit  JNPR-11.0-20180308.0604c57_buil
vagrant@vsrx>

If you can successfully SSH to the vSRX with the Vagrant insecure_private_key its time to package the VM into a Vagrant box. First shutdown the VM.

cmd
request system power-off

# output

Power Off the system ? [yes,no] (no) yes


*** FINAL System shutdown message from vagrant@vsrx ***

System going down IMMEDIATELY

To exit the console use one of these key combinations (Assuming English keyboard).

  • CTRL + ]
  • CTRL + 5
  • Press and hold CTRL and SHIFT while pressing 6 then ]

Create a metadata.json file with the following contents.

file
{"provider":"libvirt","format":"qcow2","virtual_size":16}

Use the previously downloaded create_box.sh script to make a Vagrant box from the qcow2 image.

cmd
bash create_box.sh junos-media-vsrx-x86-64-vmdisk-18.1R1.9.qcow2

# output

{16}
==> Creating box, tarring and gzipping
./metadata.json
./Vagrantfile
./box.img
Total bytes written: 4768798720 (4.5GiB, 24MiB/s)
==> junos-media-vsrx-x86-64-vmdisk-18.1R1.9.box created
==> You can now add the box:
==>   'vagrant box add junos-media-vsrx-x86-64-vmdisk-18.1R1.9.box --name junos-media-vsrx-x86-64-vmdisk-18.1R1.9'

Create a metadata file called vsrx.json so that the box is added with the correct version number.

file
{
  "name": "juniper/vsrx",
  "description": "Juniper vSRX",
  "versions": [
    {
      "version": "18.1R1.9-packetmode",
      "providers": [
        {
          "name": "libvirt",
          "url": "file:///home/bradmin/vagrant/boxes/juniper/junos-media-vsrx-x86-64-vmdisk-18.1R1.9.box"
        }
      ]
    }
  ]
}

Add the box to Vagrant using the vsrx.json file.

cmd
vagrant box add vsrx.json

# output

==> box: Loading metadata for box 'vsrx.json'
    box: URL: file:///home/bradmin/vagrant/boxes/juniper/vsrx.json
==> box: Adding box 'juniper/vsrx' (v18.1R1.9-packetmode) for provider: libvirt
    box: Unpacking necessary files from: file:///home/bradmin/vagrant/boxes/juniper/junos-media-vsrx-x86-64-vmdisk-18.1R1.9.box
==> box: Successfully added box 'juniper/vsrx' (v18.1R1.9-packetmode) for 'libvirt'!

Confirm the vsrx box was added successfully.

cmd
vagrant box list

# output

CumulusCommunity/cumulus-vx   (libvirt, 3.6.0)
.
. <snip>
.
juniper/vsrx                  (libvirt, 12.1X47-D15.4-packetmode)
juniper/vsrx                  (libvirt, 12.1X47-D15.4)
juniper/vsrx                  (libvirt, 18.1R1.9-packetmode)

Vagrantfile

Use this Vagrantfile to test out the new vSRX Vagrant box.

file
# -*- mode: ruby -*-
# vi: set ft=ruby :

cwd = Dir.pwd.split("/").last

Vagrant.configure("2") do |config|

  config.vm.define "rt01" do |node|
    hostname = "rt01"
    node.vm.box = "juniper/vsrx"
    node.vm.box_version = "18.1R1.9-packetmode"
    node.vm.synced_folder ".", "/vagrant", id: "vagrant-root", disabled: true

    node.ssh.insert_key = false

    # Limit CPU once Box has finished booting.
    node.trigger.after :up do |trigger|
      trigger.name = "Finished Message"
      trigger.info = "Machine is up! : #{cwd}_#{hostname}"
      trigger.run = {inline: "virsh schedinfo #{cwd}_#{hostname} --set vcpu_quota=33000"}
    end

    node.vm.provider :libvirt do |domain|
      domain.nic_adapter_count = 1
      domain.disk_bus = "ide"
      domain.cpus = 2
      domain.memory = 4096
    end

  end
end
Note
You may get 100% CPU on the host machine, the only way I could get this down without drastically affecting the box performance was to reduce the CPU scheduler access to 33% using the virsh schedinfo command. I am using a new feature in Vagrant 2.1.0 called triggers to execute the required command automatically once the box has booted up.

Testing

Lets vagrant up and make sure we can connect to the vSRX.

cmd
vagrant up

# output

Bringing machine 'rt01' up with 'libvirt' provider...
==> rt01: Checking if box 'juniper/vsrx' is up to date...
==> rt01: Uploading base box image as volume into libvirt storage...
==> rt01: Creating image (snapshot of base box volume).
==> rt01: Creating domain with the following settings...
==> rt01:  -- Name:              vsrx_rt01
==> rt01:  -- Domain type:       kvm
==> rt01:  -- Cpus:              2
==> rt01:
==> rt01:  -- Feature:           acpi
==> rt01:  -- Feature:           apic
==> rt01:  -- Feature:           pae
==> rt01:  -- Memory:            4096M
==> rt01:  -- Management MAC:
==> rt01:  -- Loader:
==> rt01:  -- Base box:          juniper/vsrx
==> rt01:  -- Storage pool:      disk1
==> rt01:  -- Image:             /var/lib/libvirt/images/vsrx_rt01.img (16G)
==> rt01:  -- Volume Cache:      default
==> rt01:  -- Kernel:
==> rt01:  -- Initrd:
==> rt01:  -- Graphics Type:     vnc
==> rt01:  -- Graphics Port:     -1
==> rt01:  -- Graphics IP:       127.0.0.1
==> rt01:  -- Graphics Password: Not defined
==> rt01:  -- Video Type:        cirrus
==> rt01:  -- Video VRAM:        9216
==> rt01:  -- Sound Type:
==> rt01:  -- Keymap:            en-us
==> rt01:  -- TPM Path:
==> rt01:  -- INPUT:             type=mouse, bus=ps2
==> rt01: Creating shared folders metadata...
==> rt01: Starting domain.
==> rt01: Waiting for domain to get an IP address...
==> rt01: Waiting for SSH to become available...
==> rt01: Configuring and enabling network interfaces...
    rt01: SSH address: 192.168.121.175:22
    rt01: SSH username: vagrant
    rt01: SSH auth method: private key
==> rt01: Running triggers after up ...
==> rt01: Running trigger: Finished Message...
==> rt01: Machine is up! : vsrx_rt01
    rt01: Running local: Inline script
    rt01: virsh schedinfo vsrx_rt01 --set vcpu_quota=33000
    rt01: Scheduler      : posix
    rt01: cpu_shares     : 1024
    rt01: vcpu_period    : 100000
    rt01: vcpu_quota     : 33000
    rt01: emulator_period: 100000
    rt01: emulator_quota : -1

Now SSH into vSRX.

cmd
# from host shell

vagrant ssh

# now in vSRX shell

Last login: Sat May 12 04:39:58 2018
--- JUNOS 18.1R1.9 Kernel 64-bit  JNPR-11.0-20180308.0604c57_buil
vagrant@vsrx> show version
Hostname: vsrx
Model: vsrx
Junos: 18.1R1.9

Now that we are able to connect now lets clean up the box.

cmd
# from host shell

vagrant destroy -f

# output

==> rt01: Removing domain...

Remove the original KVM image.

cmd
rm -f junos-media-vsrx-x86-64-vmdisk-18.1R1.9.qcow2
virsh undefine vsrx

# output

Domain vsrx has been undefined

Outro

The vSRX is a staple platform in the Juniper product line. If you made it this far you will have a modern Juniper vSRX Vagrant box configured for use with the vagrant-libvirt provider. Now it's time to go out and build some Juniper labs.