PARTE 1.png
Lucas Cavalcante

Lucas Cavalcante

10 Aug 2021 8 min read

Emulate remote servers for web applications with VirtualBox - Part 1

In this guide, we will show how to configure VirtualBox to emulate remote servers for web applications.

Using VirtualBox locally may be a safe solution to test your web server. In this context, safe means your local file won’t be exposed. It also means to isolate yourself from external links. This is a suitable environment to test mutiple server configurations for a web application, without consuming the resources of a deploy.

This alternative does not directly antagonize a container ecosystem (with Docker or Vagrant). In this case, we want to create a persistent scenario, which is not destroyed at the end of the process, and which give us access to the resources of an isolated Operating System.

The following instructions have been tested on an Ubuntu 20.04.1 LTS x86_64.

Create a virtual machine

To install the latest version of VirtualBox, enable the Multiverse repository on your system:

$ sudo add-apt-repository multiverse && sudo apt-get update
$ sudo apt install virtualbox

When the installation is complete, check if everything went well:

$ vboxmanage --version
6.1.18r142142

Attention: If you are unable to start vBoxManage, and receive the warning:

WARNING: The vboxdrv kernel module is not loaded. Either there is no module
         available for the current kernel (4.4.0-47-generic) or it failed to
         load. Please recompile the kernel module and install it by

           sudo /sbin/vboxconfig

         You will not be able to start VMs until this problem is fixed.

Restart the machine and go to the advanced BOOT options (F7). Look for the "secure boot" option and change it from "Windows EUFI mode" to "other OS".

Go to the directory you want to work in, and while downloading a light Ubuntu image, create a virtual machine with the vBoxManage’s createvm command [VB20]:

$vboxmanage createvm --name ubuntu1 --ostype "Ubuntu_64" --register --basefolder `pwd`

The --register flag is used to add this machine to the VMs registry that can be accessed with the list command:

$vboxmanage list vms
"ubuntu1" {465992bf-68c4-47e3-a544-ecf88051a3b5}

With the --basefolder flag we can select the directory where this machine will be stored.

Adjust the memory setting:

$vboxmanage modifyvm ubuntu1 --ioapic on
$vboxmanage modifyvm ubuntu1 --memory 1024 --vram 128

Allocate memory to be used as a storage device:

$vboxmanage createhd --filename `pwd`/ubuntu1/ubuntu1_DISK.vdi --size 80000 --format VDI

And associate virtual devices (HD, CD-ROM) with the virtual machine. Remember to set the path for the downloaded Ubuntu image, or move it to the current working directory.

$vboxmanage storagectl ubuntu1 --name "SATA Controller" --add sata --controller IntelAhci
$vboxmanage storageattach ubuntu1 --storagectl "SATA Controller" --port 0 --device 0 --type hdd --medium `pwd`/ubuntu1/ubuntu1_DISK.vdi
$vboxmanage storagectl ubuntu1 --name "IDE Controller" --add ide --controller PIIX4
$vboxmanage storageattach ubuntu1 --storagectl "IDE Controller" --port 1 --device 0 --type dvddrive --medium `pwd`/ubuntu-20.04.1-live-server-amd64.iso

Configure the boot order using those devices:

$vboxmanage modifyvm ubuntu1 --boot1 dvd --boot2 disk --boot3 none --boot4 none

In order to verify that all the information is correct use the command:

$vboxmanage showinfo ubuntu1

If you need to undo the procedure use the command:

$vboxmanage unregistervm ubuntu1 --delete

If everything is OK, start the virtual machine:

 $ vboxmanage startvm ubuntu0

After the boot, install the OS. And once the procedure is complete, send the shutdown command to the virtual machine:

$ vboxmanage controlvm ubuntu0 acpipowerbutton

Check that the machine does not appear in the list of active VirtualBox processes:

$ vboxmanage list runningvms

And, if you need to force the machine to shut down, use:

$ vboxmanage controlvm ubuntu0 poweroff

Now we no longer need the installation media:

$ vboxmanage modifyvm ubuntu0 --boot1 none

Attention: By default, VirtualBox enables focus capture from input devices (keyboard and mouse), along with a key to change that focus between Host and Guest machines. On my notebook, however, the defined key simply does not exist (RIGHT CTRL). It is possible to easily change this key though.

With the getextradata option of the vboxmanage command it is possible to obtain information regarding the context of the VirtualBox display emulator.

$vboxmanage getextradata global | grep HostKeyCombination
Key: GUI/Input/HostKeyCombination, Value: 65027

Using this table as a reference, we can change this key to RIGHT ALT (or ALT GR) for instance:

$vboxmanage setextradata global GUI/Input/HostKeyCombination 65514

Enable remote access to the virtual server

Now, let's adjust the network settings to allow the Host (our physical machine) to access the Guest (our virtual machine).

We will need two virtual network adapters. The first one will allow the Host to access the Guest via SSH. A suggested solution is to use port forwarding.

With the virtual machine turned off, create a network adapter with the NAT (network address translation) configuration:

$ vboxmanage modifyvm ubuntu0 --nic1 nat

And establish a tunnel between the Host and the Guest:

$ vboxmanage modifyvm ubuntu0 --natpf1 "guestssh,tcp,,5679,,22"

This means that all TCP traffic (including requests made by SSH) that passes through the Host via port 5679 will be forwarded to the Guest via port 22.

The second virtual network adapter will allow the Guest's localhost to be accessed by the Host. A recommended solution is to use a bridge mode adapter. To do this, we then need to find out two things: 1) how the physical network adapter is named on the Host; and 2) which subnet this adapter belongs to. We can use the ifconfig command to obtain this information:

$ ifconfig
lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 4183364  bytes 313420263 (313.4 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 4183364  bytes 313420263 (313.4 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

wlp0s20f3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.11.4  netmask 255.255.255.0  broadcast 192.168.11.255
        inet6 fe80::e3e:7167:aca3:f217  prefixlen 64  scopeid 0x20<link>
        ether 5c:cd:5b:49:f9:3f  txqueuelen 1000  (Ethernet)
        RX packets 9644624  bytes 9927969903 (9.9 GB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 4301466  bytes 736022594 (736.0 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

In this case, we can see that 1) the adapter is called wlp0s20f3; and 2) that it is associated with the 192.168.11.x subnet. Okay, now we can create a network adapter with the bridged configuration for the Guest. Here, use the name of the physical network adapter (don't forget the quotes):

$ vboxmanage modifyvm ubuntu0 --nic2 bridged --bridgeadapter1 "wlp0s20f3"

Restart the virtual machine. This time, if you prefer, it is possible not to allocate dedicated visual resources, with the command:

$ vboxmanage startvm ubuntu0 --type headless

Make sure the SSH service is available on Guest:

$ sudo service ssh status

And if not, install it with the command:

$ sudo apt-get install openssh-server

Then, from the Host, try to access the Guest with the created user:

$ ssh myGuestUser@127.0.0.1 -p 5679

Attention: If you are unable to access, getting the warning:

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@            
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
...
Host key verification failed.

Follow the instructions provided within the warning and remove the obsolete information at ~/.ssh/known_hosts. Try running the command:

$ssh-keygen -f "/home/USER/.ssh/known_hosts" -R "[127.0.0.1]:5679"

To avoid having to enter your password each time, register the Host's identity in the Guest credentials list with the following command from the Host:

$ ssh-copy-id myGuestUser@127.0.0.1 -p 5679

Once connected to Guest, we will need to help the operating system recognize the second network adapter. First, we find out what this adapter is called.

$ ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 08:00:27:49:19:b0 brd ff:ff:ff:ff:ff:ff
3: enp0s8: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 08:00:27:bd:c6:75 brd ff:ff:ff:ff:ff:ff

In this case, we can see that the adapter is named enp0s8. Then we edit the following file:

myGuestUser@guest:~$ sudo vim /etc/netplan/00-installer-config.yaml

It will probably have the following format:

# 00-installer-config.yaml

network:
  ethernets:
    enp0s3:
      dhcp4: true
  version: 2

Here, we need to add the second adapter. At this point, we need to remember the subnet that the Host's physical adapter belongs to: 192.168.11.x. Just associate a static IP within that subnet so that we can access it externally without surprises:

# 00-installer-config.yaml

network:
  ethernets:
    enp0s3:
      dhcp4: true
    enp0s8:
      addresses: [192.168.11.89/24]
      gateway4: 192.168.11.1
      dhcp4: true
  version: 2

Once you save and close this file, don't forget to apply the changes:

$ sudo netplan apply

Check that the address indicated was associated with the virtual adapter:

$ ifconfig enp0s8 | grep "inet "
        inet 192.168.11.89  netmask 255.255.255.0  broadcast 192.168.11.255

Excellent! We now have a route that allows us to access the Guest's localhost from the Host. To make sure everything has been set up correctly, try to ping the Host from the Guest:

myGuestUser@guest:~$ ping 192.168.11.4 -c 3
PING 192.168.11.4 (192.168.11.4) 56(84) bytes of data.
64 bytes from 192.168.11.4: icmp_seq=1 ttl=64 time=0.109 ms
64 bytes from 192.168.11.4: icmp_seq=2 ttl=64 time=0.285 ms
64 bytes from 192.168.11.4: icmp_seq=3 ttl=64 time=0.167 ms

--- 192.168.11.4 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2052ms
rtt min/avg/max/mdev = 0.109/0.187/0.285/0.073 ms

And vice versa, try to ping the Guest from the Host:

myHostUser@host:~$ ping 192.168.11.89 -c 3
PING 192.168.11.89 (192.168.11.89) 56(84) bytes of data.
64 bytes from 192.168.11.89: icmp_seq=1 ttl=64 time=0.247 ms
64 bytes from 192.168.11.89: icmp_seq=2 ttl=64 time=0.223 ms
64 bytes from 192.168.11.89: icmp_seq=3 ttl=64 time=0.245 ms

--- 192.168.11.89 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2024ms
rtt min/avg/max/mdev = 0.223/0.238/0.247/0.010 ms

Conclusions

That’s it! We now have a virtual machine running locally that can be accessed via SSH, just like a remote server.

This guide continues in Part 2. So don't shut down your virtual “remote” server! Next, we'll see how to set up a Django application using NGINX on that server.

Did you like this post? Did you have any questions? Do you have any suggestions? Leave a comment!