Exploring a Host-Only Network

What’s a Host-Only Network?

Often, the first subject I tackle when architecting new services is that of the Host-Only Network. The pattern I outline here is used as a foundation for all my POC work. I usually design products to run on multiple hosts, but I like to do all the modeling from one laptop. Host-Only Networking allows for this by establishing a private network of virtual machines internal to a single host.

In an Host-Only Network, the host assigns traffic for the network to a bridge, offering a device to use as a default gateway for outbound traffic. It also provides a route from the host to the internal network.

I’d like to take a moment to call special attention to the nomenclature. While the term “Host-Only Network” has many varieties and proprietary implementations (such as in VMWare), we’re dealing with a more generic use of the term. If you want a specific piece of technology to look up, try “VirtualBox Host-Only Network.”

Let’s Build One

As an example, I’ll use Vagrant and Virtualbox to launch a network of virtual machines internal to the host. This host-only network will be accessible only via forwarded ports (and those vagrant uses to ssh). After the sample network is built, I’ll explore the network some.

To begin, I’ll use vagrant to spin up this Vagrantfile, which will provide for 3 vms built on the 192.168.34.0/24 network. You should take a moment to examine the Vagrantfile. The most relevant part, for this discussion, are lines 9, 21, and 34:

    vm1.vm.network "private_network", ip: "192.168.34.2" # or .3 or .4

Due to these 3 lines, the VMs will receive those 3 IPs on a network reachable only from my host. VM1, VM2, and VM3 will reside on their own internal network segment. This is the scope of our Host-Only Network.

doug@OPI:~/hostonly$ vagrant up
Bringing machine 'vm1' up with 'virtualbox' provider...
Bringing machine 'vm2' up with 'virtualbox' provider...
Bringing machine 'vm3' up with 'virtualbox' provider...
...
    vm1: VM1 Provisioned
...
    vm2: VM2 Provisioned
...
    vm3: VM3 Provisioned

This could take several minutes, so now is a good time to fetch coffee. When you return, we’ll examine the network structure created by the host.

Exploring the Host Only Network.

Exploring The Host

First, we can examine the host. We’ll want to note a couple of things immediately. Namely, the bridge device and the route to the Host-Only Network.

The Bridge

Use the ‘ip’ command to look at the available devices on the host. Since the VirtualBox router will assign the VMs to the 192.168.34.0/24 subnet, then we know to look for a routable bridge device with the address of 192.168.34.1/24.

doug@OPI:~/hostonly$ ip addr
1: lo: ...
...
2: enp109s0f1: ...
...
3: wlp110s0: ...
...
4: vboxnet0:...
...
5: vboxnet1:  mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 0a:00:27:00:00:01 brd ff:ff:ff:ff:ff:ff
    inet 192.168.34.1/24 brd 192.168.34.255 scope global vboxnet1
       valid_lft forever preferred_lft forever
    inet6 fe80::800:27ff:fe00:1/64 scope link 
       valid_lft forever preferred_lft forever
doug@OPI:~/hostonly$

We find our bridge is called ‘vboxnet1’. It has it’s own ethernet address (assigned by virtualbox). It is, in fact, a virtual router created by VirtualBox for our Network.

The Host’s Route Table

Use the ‘route’ command to examine the routes on the host machine. We set up the virtual machines to sit on the 192.168.34.0/24 network, so we look for a route entry with a Destination of 192.168.34.0 and a Genmask of 255.255.255.0.

doug@OPI:~/hostonly$ route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         gateway         0.0.0.0         UG    600    0        0 wlp110s0
link-local      0.0.0.0         255.255.0.0     U     1000   0        0 wlp110s0
192.168.1.0     0.0.0.0         255.255.255.0   U     600    0        0 wlp110s0
192.168.33.0    0.0.0.0         255.255.255.0   U     0      0        0 vboxnet0
192.168.34.0    0.0.0.0         255.255.255.0   U     0      0        0 vboxnet1
doug@OPI:~/hostonly$

We can see that the route is available and points to Iface ‘vboxnet1’.

Testing on the Host

The route is there with an Iface of ‘vboxnet1’, which is the name of the bridge that we found. I can reasonably expect to be able to ping devices attached to that bridge with the 192.168.34.0/24 address range. Since I know the IP addresses of the VMs to be 192.168.34.2 (or .3/.4), I can put this hypotheses to the test:

doug@OPI:~/hostonly$ ping -c 5 192.168.34.2
PING 192.168.34.2 (192.168.34.2) 56(84) bytes of data.
64 bytes from 192.168.34.2: icmp_seq=1 ttl=64 time=0.375 ms
64 bytes from 192.168.34.2: icmp_seq=2 ttl=64 time=0.389 ms
64 bytes from 192.168.34.2: icmp_seq=3 ttl=64 time=0.422 ms
64 bytes from 192.168.34.2: icmp_seq=4 ttl=64 time=0.423 ms
64 bytes from 192.168.34.2: icmp_seq=5 ttl=64 time=0.427 ms
 
--- 192.168.34.2 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4101ms
rtt min/avg/max/mdev = 0.375/0.407/0.427/0.024 ms
doug@OPI:~/hostonly$

Success! So we can see that the Host-Only Network is, in fact, reachable from the host.

Exploring the VMs

Now that we’ve explored the host, we can examine what’s available on the VM. Since all these VMs are configured identically (with one notable exception that we’ll address later), we can log into vm1 and run our tests from there with the assumption that everything will be the same.

doug@OPI:~/hostonly$ vagrant ssh vm1
Last login: Thu Jan 11 17:11:55 2018 from 10.0.2.2
[vagrant@vm1 ~]$

The VMs network devices

We can run the ‘ip’ command to examine the devices assigned to the VM.

[vagrant@vm1 ~]$ ip a
1: lo:  mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0:  mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 52:54:00:22:4e:72 brd ff:ff:ff:ff:ff:ff
    inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic eth0
       valid_lft 66494sec preferred_lft 66494sec
    inet6 fe80::5054:ff:fe22:4e72/64 scope link 
       valid_lft forever preferred_lft forever
3: eth1:  mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 08:00:27:a2:53:a2 brd ff:ff:ff:ff:ff:ff
    inet 192.168.34.2/24 brd 192.168.34.255 scope global eth1
       valid_lft forever preferred_lft forever
    inet6 fe80::a00:27ff:fea2:53a2/64 scope link 
       valid_lft forever preferred_lft forever

We can see that we have a loopback (localhost), eth0, and eth1. When VirtualBox creates a VM, that VM typically resides on the 10.0.0.0/16 network an can only be reached from the host. When a secondary, host-only network is created for multiple VMs to reside on, a new device is added to the VM. In this case, eth0 is the 10. network and eth1 is our Host-Only Network.

We can safely assume this to be true because eth1 has an IP address of 192.168.34.2/24, which is the IP address assigned by VirtualBox and the IP network that the host’s ‘vboxnet1’ bridge device is attached to.

The VMs’ routes

Still using vm1, we can examine the route table next. Use the ‘route’ command to see the route table.

[vagrant@vm1 ~]$ route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         gateway         0.0.0.0         UG    0      0        0 eth0
10.0.2.0        0.0.0.0         255.255.255.0   U     0      0        0 eth0
192.168.34.0    0.0.0.0         255.255.255.0   U     0      0        0 eth1

It’s worth looking at all three entries. The first is the default gateway, with uses eth0, the interface created by VirtualBox to support the basic generation of this VM. The second is the 10.0.2.0/24 network, also on eth0. These two routes were originally intended to support all the traffic for this VM. However, we defined an eth1 in the Vagrantfile by requesting an Host-Only Network.

To support the requested extra IP and Host-Only Network, an extra device and route have been added. eth1 has an IP on the 192.168.34.0/24 network and also has the route provided for it as the last route on the routing table. Due to routing priority going to the most specific route, the 192.168.34.0/24 network will be routed to eth1 instead of the default gateway.

Testing the VM Network

The fastest way to test out this network is with pings. We want to know who can talk to which servers. Since we’re logged into vm1, we’ll try pinging to the following addresses:

  • 192.168.34.3 (vm2)
  • 192.168.34.4 (vm3)
  • 192.168.34.1 (the gateway router for this network)
  • 192.168.33.13 (this is a vm running on the 192.169.33.0/24 Host-Only Network, which is also running on this host)
  • 192.168.1.22 (this is the IP address of the Host I’m on (OPI))

With a quick command line execution, I can test all of these:

[vagrant@vm1 ~]$ for x in {192.168.34.3,192.168.34.4,192.168.34.1,192.168.33.13,192.168.1.22} ; do echo $x ; ping -c 2 $x ; echo ; done
192.168.34.3
PING 192.168.34.3 (192.168.34.3) 56(84) bytes of data.
64 bytes from 192.168.34.3: icmp_seq=1 ttl=64 time=0.230 ms
64 bytes from 192.168.34.3: icmp_seq=2 ttl=64 time=0.715 ms
 
--- 192.168.34.3 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1059ms
rtt min/avg/max/mdev = 0.230/0.472/0.715/0.243 ms
 
192.168.34.4
PING 192.168.34.4 (192.168.34.4) 56(84) bytes of data.
64 bytes from 192.168.34.4: icmp_seq=1 ttl=64 time=0.664 ms
64 bytes from 192.168.34.4: icmp_seq=2 ttl=64 time=0.600 ms
 
--- 192.168.34.4 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 0.600/0.632/0.664/0.032 ms
 
192.168.34.1
PING 192.168.34.1 (192.168.34.1) 56(84) bytes of data.
64 bytes from 192.168.34.1: icmp_seq=1 ttl=64 time=0.292 ms
64 bytes from 192.168.34.1: icmp_seq=2 ttl=64 time=0.337 ms
 
--- 192.168.34.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 0.292/0.314/0.337/0.028 ms
 
192.168.33.13
PING 192.168.33.13 (192.168.33.13) 56(84) bytes of data.
64 bytes from 192.168.33.13: icmp_seq=1 ttl=63 time=0.366 ms
64 bytes from 192.168.33.13: icmp_seq=2 ttl=63 time=0.494 ms
 
--- 192.168.33.13 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1043ms
rtt min/avg/max/mdev = 0.366/0.430/0.494/0.064 ms
 
192.168.1.22
PING 192.168.1.22 (192.168.1.22) 56(84) bytes of data.
64 bytes from 192.168.1.22: icmp_seq=1 ttl=63 time=0.216 ms
64 bytes from 192.168.1.22: icmp_seq=2 ttl=63 time=0.580 ms
 
--- 192.168.1.22 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1019ms
rtt min/avg/max/mdev = 0.216/0.398/0.580/0.182 ms
 
[vagrant@vm1 ~]$

Ping results and statistics show that all of these IPs are routable from within the Host-Only Network. In the case of the host IP or the 192.168.33.0/24 network, these packets are routed by the host’s routing system. However, if you were to attempt to ping this Host-Only Network from another host, the ping would be unroutable and would fail. That is the power of this type of network.

And Another Thing (a forwarded port)

Perhaps you noticed that vm3 is configured differently by the Vagrantfile. On line 33, we see this:

    vm3.vm.network "forwarded_port", guest: 80, host: 8080

Later in that same configuration segment, httpd is installed and started on vm3. Because we’ve requested a port forwarding, we can access the VM’s port by forwarding all traffic to it from a port on the host. To demonstrate, I access the command line on the host server and check for listening ports:

doug@OPI:~/hostonly$ netstat -nat | grep LISTEN
tcp        0      0 127.0.0.1:30666         0.0.0.0:*               LISTEN     
tcp        0      0 0.0.0.0:5355            0.0.0.0:*               LISTEN     
tcp        0      0 127.0.0.1:2222          0.0.0.0:*               LISTEN     
tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN     
...
doug@OPI:~/hostonly$

In the output, we see that we’re listening on port 8080. The virtual router attached to it will forward the port through to the Host-Only Network. If we take a moment to log into VM3, which receives the forwarded port on 80, we can check that this port is also open.

[vagrant@localhost ~]$ netstat -nat | grep LISTEN
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN     
tcp6       0      0 :::80                   :::*                    LISTEN     
tcp6       0      0 :::22                   :::*                    LISTEN     
[vagrant@localhost ~]$

So we can see that the port is open. The last step is to use the local host’s browser to try out the forwarded port. Point the browser to http://localhost:8080 and see if it brings up the standard Fedora branded splash page for httpd (Apache).

Conclusion

Thanks for reading this walk through of Host-Only Network. Armed with this Vagrantfile and advanced knowledge, you should be able to model any number of clusters and service patterns.


Also published on Medium.

One thought on “Exploring a Host-Only Network

Leave a Reply

Your email address will not be published. Required fields are marked *

*

*