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.
$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 thevboxmanage
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!