Hypervisors like VMware Workstation, VMWare Fusion or VirtualBox usually offer three kinds of network interfaces: bridged (to a network on the host), NAT (sharing an IP address with the host via network address translation) and host-only (a connection exclusively between host and guest).
VMWare, at least on Linux, realizes NAT entirely in the kernel, using standard IP forwarding and setting up a DHCP server on the host that gives addresses to the guest. VirtualBox, on the other hand, handles NAT entirely in user-space, meaning all packets entering and leaving the VM really seem to be going to and from a process named VBoxHeadless or similar.
If you want to limit what kinds of connections a guest system can make, VMware lets you do that quite easily by adding rules to the FORWARD chain:
sudo iptables -I FORWARD -i vmnet8 -j ACCEPT -d github.com sudo iptables -I FORWARD -i vmnet8 -j REJECT
Unfortunately, things are a lot more difficult with VirtualBox. Since there is no dedicated interface, you need to filter based on process. iptables can’t do that directly, but you can use cgroups to add the necessary marks to the packets:
sudo mkdir /sys/fs/cgroup/net_cls/virtualbox echo 86 | sudo tee /sys/fs/cgroup/net_cls/virtualbox/net_cls.classid sudo iptables -N VIRTUALBOX sudo iptables -I OUTPUT -j VIRTUALBOX -m cgroup --cgroup 86 sudo iptables -I VIRTUALBOX -j ACCEPT -d github.com sudo iptables -I VIRTUALBOX -j REJECT
Of course, you now need to create the VirtualBox process inside that cgroup. One way is
sudo apt-get install cgroup-tools sudo cgexec -g net_cls:virtualbox vboxmanage startvm WindowsXP --type=headless
or you can use a wrapper script instead of vboxmanage:
#!/bin/sh -e CGROUP_NAME=virtualbox CGROUP_ID=86 if [ ! -d /sys/fs/cgroup/net_cls/$CGROUP_NAME/ ]; then mkdir /sys/fs/cgroup/net_cls/$CGROUP_NAME echo $CGROUP_ID > /sys/fs/cgroup/net_cls/$CGROUP_NAME/net_cls.classid fi /bin/echo $$ > /sys/fs/cgroup/net_cls/$CGROUP_NAME/tasks exec /usr/bin/vboxmanage "$@"