Sometimes you need your guest VMs to have secure access to the internet and your host machine without all the fuss and complexity of typical Hyper-V networking. Here’s an easy way to do just that by creating a separate NAT virtual network.
Don’t care about how it works and just want to get it done? Skip the background and get right to the setup!
So what exactly are we going to do?
We will create a virtual network space in a private subnet that uses the host machine as a gateway. This means your guests and host can interact, share files, SSH, etc. and your guests have internet access through your host machine using its physical IP address. Your guests can also use all the resources of your host network since the traffic appears to come from your host machine – that means private DNS servers, LAN machines, LAN servers, etc. Think of it this way: We will make your host machine into a ‘router’ behind which your guest VMs sit… just like your host machine is probably on a network behind a router that connects to your ISP. And yes, we will do all this via NAT, just like your physical network (shhh… no mentioning about double-natting please, just accept it).
What about the ‘Default Switch’?
Good question, the default switch has its place but there are also many times where it’s just not the right choice – as I explain in this article. For those unfamiliar, I’ll do a quick overview. Starting with the Windows 10 Fall Creators Update (build 1709), Hyper-V ships with a ‘Default Switch’ that is automatically installed. This is meant to feature match other virtualization platforms like VirtualBox. It automatically creates a network for your VMs to share the host’s internet connection. In addition, it provides DHCP and DNS forwarding functionality. Sounds great, right? Well, there are a few caveats. Namely, it is not configurable at all. This means your machines get an mshome.net DNS suffix whether they like it or not. In addition, they are always subject to changing IP addresses due to the pseudo-DHCP server. Finally, they work though Internet Connection Sharing (ICS) so the network itself is hardwired to MS defaults.
As I point out in my article about the Default Switch, it has it’s time and place and is excellent for basic internet access. However, it’s lack of configurability presents issues when hosting servers in your VMs, when you need fixed IP addresses and especially when you need port forwarding between your host and guest – in other words, SSH tunnels and stuff like that do not work. For these cases, there’s a much better way…
Why create a NAT network
The obvious answer to this question – to give our guest VMs internet access! The more detailed answer… By creating a NAT network, we overcome all the limitations of the Default Switch and give ourselves complete control over the virtual network. We also create a very simple way for us to provide network/internet access to our guest VMs. As I said earlier, we will make our host machine into a router for our virtual network and get the following features:
- Since we’re creating a standard Hyper-V internal network, our guest VMs can all talk to each other and talk to the host machine (remember, the latter is the difference between internal and private in the Hyper-V networking world)
- All outgoing (i.e. internet) traffic from guests will flow through the host machine and use its physical network IP address… in other words, NAT!
- Our guests have access to resources on the physical network unless blocked by the host machine
- Think of this kind of like a subnet
- Incoming access to guest VMs can be facilitated via port forwarding on the host machine… again, just like your home router (advanced topic, not covered in this article)
Let’s create the network!
Enough with the background, it sounds awesome so let’s do it. We will use exactly 3 PowerShell statements to get this all done. Seriously. Start up PowerShell as an administrator and let’s get going.
Like all things, you have to spend a few minutes planning this out so that you name stuff correctly and don’t confuse yourself a few months down the road. You need to decide on only two simple things:
- A descriptive name for the virtual network you are creating. You should pick a name for your virtual network that describes its purpose. For example, I have a virtual network called ‘LinuxInternal’ which I use for all my Linux virtual machines. For this site, I have a network called ‘MTTDomainInternal’ where I connect a Server 2016 virtual machine and 3 Windows 10 virtual machines together into a ‘lab’ domain environment to test out tutorials and walk-throughs for this site. Once you have a network name, you can easily pick names for your switches and NICs, etc. Makes sense? I hope so.
- The private network address range you will use. Because we are setting up a virtual network, you obviously need an address space. For simplicity, I just use IPv4. Therefore, you have the familiar address spaces you probably already know:
|10.0.0.0||/8 = 255.0.0.0||10.0.0.0 - 10.255.255.255|
|172.16.0.0||/12 = 255.240.0.0||172.16.0.0 - 172.31.255.255|
|192.168.0.0||/16 = 255.255.0.0||192.168.0.0 - 192.168.255.255|
My LAN is generally somewhere in the 192.168.0.0 range, so I choose to put my virtual networks in the 10.0.0.0 range. I usually set mine up something like this to keep things organized:
|WindowsInternal||10.0.0.0||24||10.0.0.0 - 10.0.0.255|
|LinuxInternal||10.0.1.0||24||10.0.1.0 - 10.0.1.255|
|MTTDomainInternal||10.0.2.0||24||10.0.2.0 - 10.0.2.255|
Once you’ve got your planning out of the way, everything else is quick and easy and very little thought is required.
1. Create the Internal Network Switch
Our first step is to create a simple internal switch. This switch connects to a virtual NIC in your system, so you don’t need any extra physical network cards or anything, don’t worry. Once this is done, any VMs connected to this switch will be able to talk to each other and can talk to the host machine, but not the host’s network or any other networks. This is a basic internal switch you can create via the GUI but PowerShell is cooler.
New-VMSwitch -SwitchName "NATNetworkSwitch" -SwitchType Internal
You can give your switch any name you like using the -SwitchName option. Based on the planning you did in the previous section (you did that, right?) you should be able to come up with a good name very easily. I would use something like ‘LinuxInternalSwitch’ or ‘MTTDomainInternalSwitch’, for example.
2. Connect the vNIC to your vSwitch
Remember I said that the virtual internal switch connects to a virtual NIC? This virtual NIC is the upstream connection from your VMs via the switch you just created to your host machine. It’s easier to think of this as just giving your host machine an extra IP address. All traffic from/to the switch and the VMs behind the switch will use this virtual NIC and thus, it’s IP address. In other words, this virtual NIC is acting as a gateway.
Based on your planning, you should already know what network you’ll be creating so picking an IP address should be trivial. However, since this NIC is a gateway, convention dictates that you pick an address at one end of the network range. So something like x.x.x.1 or x.x.x.254. I like low numbers for my gateways and high numbers for my servers, but you can do whatever. Use the following PowerShell command to get things setup:
New-NetIPAddress -IPAddress 10.0.1.1 -PrefixLength 24 -InterfaceAlias "vEthernet (NATNetwork)"
Just like before, you can use any name you want for the -InterfaceAlias but it should relate to your network name in some way. In addition, it’s convention that you label it ‘vEthernet’ so you can easily tell which NICs are real and which ones are linked to Hyper-V. Whatever you name it, that’s what will show up in your ‘Network Connections’ control panel applet and in ‘ipconfig’ reports, so pick something that makes sense!
I’m getting an ‘invalid argument’ error
Akito and Laurent pointed out in the comments that the above step no longer works and gives you an error complaining about an invalid argument. Following a Windows 10 update, we need to do this in 2 steps instead of one. When we make the virtual switch, a new vNIC is automatically created and named for us. This is new. To see what’s happening and understand this cryptic error message, run the following command after you get that error message:
Look for a Hyper-V Virtual Ethernet Adapter having a name starting with vEthernet and containing your switch name. Because this is already created, Windows is looking for this name as the InterfaceAlias in the command above. However, this name is messy and ugly so we are going to fix that now.
# see information in 'list' view so things aren't truncated Get-NetAdapter -InterfaceDescription 'Hyper-V*' | fl # rename things the way you want Rename-NetAdapter -Name "vEthernet (ulgy switch-based name)" -NewName "vEthernet (better name)"
For example, your command might look like:
Rename-NetAdapter -Name "vEthernet (vSwitch (NATNetwork))" -NewName "vEthernet (NATNetwork)"
Now that we’ve cleaned that up and the names look better, you can run the previous command properly without Powershell complaining:
New-NetIPAddress -IPAddress 10.0.1.1 -PrefixLength 24 -InterfaceAlias "vEthernet (NATNetwork)"
3. Setup NAT
Ok, the final step! Here you are going to tell PowerShell to create a NAT mapping from your virtual network’s private address range to your host’s physical NIC IP address.
New-NetNAT -Name "NATNetwork" -InternalIPInterfaceAddressPrefix 10.0.1.0/24
Just as before, you can choose anything for the -Name parameter but, it should be your previously decided upon network name so that your vSwitch, vNIC and vNetwork all relate to each other. It should go without saying that your vNIC address must be within the address range you select here for -InternalIPInterfaceAddressPrefix otherwise how would it function as the gateway for your machines, right?
Accessing guests & addressing
This setup gives your guest systems access to your host system, your host’s network and the internet but, it does NOT set up any form of DHCP or other such addressing system for your clients. Therefore you have two options. You can setup a DHCP server/relay that also runs/updates a DNS server for name resolution, or you can set up your VMs with static IP addresses and use your machine’s hosts file. The latter is much easier and more than sufficient for most situations.
The easiest way of accessing your guest VMs by name instead of IP is via the hosts file. The setup is pretty simple. Give each of your guest VMs a static IP (within the range of the NAT network we just setup, obviously!) then edit your hosts file to point to those addresses mapped to the desired hostnames.
You can easily edit your hosts file in notepad or your favourite text editor. Remember to open notepad or whatever as an administrator so you have permission to edit the hosts file! For reference, you can do the following from an administrative command-prompt:
Your hosts file is simply a plain text list of IP address, at least one space, then the desired hostname. You can include comments by prefixing the line with a # symbol. Here’s an example based on the default Windows host file:
# Copyright (c) 1993-2009 Microsoft Corp. # # This is a sample HOSTS file used by Microsoft TCP/IP for Windows. # This file contains the mappings of IP addresses to host names. Each # entry should be kept on an individual line. The IP address should # be placed in the first column followed by the corresponding host name. # The IP address and the host name should be separated by at least one # space. # Additionally, comments (such as these) may be inserted on individual # lines or following the machine name denoted by a '#' symbol. # For example: # 220.127.116.11 rhino.acme.com # source server # 18.104.22.168 x.acme.com # x client host # localhost name resolution is handled within DNS itself. # 127.0.0.1 localhost # ::1 localhost # This line is a comment. Everything above this line is included by # Microsoft by default. # My virtual machines # NATNetwork1 10.0.1.100 wordpress 10.0.1.125 linux2.test.network 10.0.1.150 linux3 10.0.1.200 www.mydomain.com # NATNetwork2 10.0.2.101 client1.windomain.local 10.0.2.102 client2.windomain.local 10.0.2.103 client3.windomain.local 10.0.2.254 server.windomain.local
In the above example, I’ve covered two fictional NAT networks for different VMs. However, they all work the exact same. In this case, from my (host) machine:
- I can access my WordPress VM by going to http://wordpress which maps to 10.0.1.100
- I can access a local version of www.mydomain.com on the Webserver VM at 10.0.1.200
- I can SSH to my Linux VM at 10.0.1.150 by opening a connection to linux3
- I can RDP to my Windows Server VM at 10.0.2.254 by opening a connection to server.windomain.local
I hope you see how this makes things easier to access your virtual machines. Keep in mind however, this is not a replacement for a DNS server. You can access your VMs from your host by this method, but they cannot look each other up via this method. For that, you’d need to edit each VM’s local hosts files in their OS, or you’d have to implement a DNS server of some sort. On that note, if you have a local DNS server available to you, you can simply do these updates on that server (instead of your hosts file) and point all your VMs to that DNS server since they have access to your host’s network.
I’m surprised how many overly complicated ways of accomplishing this I see around the internet or how many ‘official’ explanations tell you that the Default Switch is your only option and you have to live with not being able to configure any network settings. Hopefully this has helped you provide simple LAN/internet access to your VMs while maintaining control over the whole process. Not too painful, right?
Thanks for reading my techie-thoughts on this issue. Have any comments or suggestions? Want to add your tips? Things you want me to cover in a future article? Comment below!