VirtualBox network access via NAT

VirtualBox has a few options for network access. By default VirtualBox NATs the guest OS out to the network via the host OS, so the guest can reach the internet but outside hosts can't initiate sessions to the guest. One option for running a server from the guest OS is probably to bridge a virtual interface with a physical one. Newer versions of VirtualBox also have the option of configuring port forwarding, which is what I'm going to write about here.

As of VirtualBox 3.0.2, port forwarding to the guest must be configured by either editing the VirtualBox.xml configuration file or using the VBoxManage setextradata command, which is nearly the same thing. There is no way to do this from the GUI. The example given in the user manual maps port 2222 on the host OS to port 22 on the guest OS for ssh access. On Unix/Linux hosts, the non-root user isn't allowed to bind to ports below 1024 and it would be bad practice to run VirtualBox as root, so it must bind to high port numbers. (But see below for a workaround.)

A single virtual network adapter with NAT is enabled by default. The command to explicitly enable this is
$ VBoxManage modifyvm nic1 nat

Once the NAT'd interface is up, the set of commands to enable port forwarding to the guest OS have this form:

$ VBoxManage setextradata "Guest VM" "VBoxInternal/Devices/e1000/0/LUN#0/Config/guestssh/Protocol" TCP
$ VBoxManage setextradata "Guest VM" "VBoxInternal/Devices/e1000/0/LUN#0/Config/guestssh/GuestPort" 22
$ VBoxManage setextradata "Guest VM" "VBoxInternal/Devices/e1000/0/LUN#0/Config/guestssh/HostPort" 2222
$ VBoxManage setextradata "Guest VM" "VBoxInternal/Devices/e1000/0/LUN#0/Config/guestssh/BindIP" "10.0.0.1"

Those commands will write these lines to the <ExtraData> section of the xml config file for the guest machine:

<ExtraDataItem name="VBoxInternal/Devices/e1000/0/LUN#0/Config/guestssh/Protocol" value="TCP"/>
<ExtraDataItem name="VBoxInternal/Devices/e1000/0/LUN#0/Config/guestssh/GuestPort" value="22"/>
<ExtraDataItem name="VBoxInternal/Devices/e1000/0/LUN#0/Config/guestssh/HostPort" value="2222"/>
<ExtraDataItem name="VBoxInternal/Devices/e1000/0/LUN#0/Config/guestssh/BindIP" value="10.0.0.1"/>

If you're inclined, it isn't more work to edit the xml file by hand.

"10.0.0.1" should be the IP address of the host machine. "Guest VM" above is obviously the name of the virtual machine. Less obvious is that /e1000/ parameter which refers to the virtual Intel PRO/1000 provided to the guest OS. If your virtual host is using an AMD PCNet card, that parameter should be /pcnet/ instead. The VirtualBox documentation states that an AMD PCNet is the default card, but in my experience with 3.0.2 OSE, the Intel card is the default option. To check which card is being used by your virtual machine, run VBoxManage showvminfo Machine_Name and check the NIC entries. The Intel cards are named 82540EM, 82543GC and 82545EM, so use /e1000/ if you have one of those. The AMD cards are Am79C970A and Am79C973 so use /pcnet/ for those.

According to the documentation, the /0/ parameter refers to the first virtual network interface. It should be possible to port forward to another interface, but I wasn't able to it to work with anything other than the first one.

The /guestssh/ parameter is arbitrary, so if you wanted to forward host port 8080 to guest port 80 for example, you might call it /guesthttp/ instead.

Finally, let's say you want a guest VM to bind a low port number on a Linux host machine. You can configure VirtualBox to listen to a high port number, then remap it with iptables. To map port 2222 to port 22, use this syntax:

$ iptables -t nat -A PREROUTING -p tcp -i eth0 --dport 22 -j DNAT --to-destination :2222

I'm assuming that eth0 is the listening interface. Obviously if you do this, you won't be able to ssh into your host machine anymore. A more likely example would be remap a web server:

$ VBoxManage setextradata "Guest VM" "VBoxInternal/Devices/e1000/0/LUN#0/Config/guesthttp/Protocol" TCP
$ VBoxManage setextradata "Guest VM" "VBoxInternal/Devices/e1000/0/LUN#0/Config/guesthttp/GuestPort" 80
$ VBoxManage setextradata "Guest VM" "VBoxInternal/Devices/e1000/0/LUN#0/Config/guesthttp/HostPort" 8080
$ VBoxManage setextradata "Guest VM" "VBoxInternal/Devices/e1000/0/LUN#0/Config/guesthttp/BindIP" "10.0.0.1"
$ iptables -t nat -A PREROUTING -p tcp -i eth0 --dport 80 -j DNAT --to-destination 10.0.0.1:8080

With this configuration, the guest VM is listening on port 80 on the virtual interface which is mapped to port 8080 on the physical interface. The Linux host OS uses IP tables to translate port 80 on the physical interface over to port 8080 which VirtualBox has permission to bind to. If you have multiple IP addresses on the host OS's network interface, you could map different IPs to separate VMs.