Dank Docker gibt es in der aktuellen Preview von Windows 10 und in der Rosetta-Jubiläums-Ausgabe, die Ende Juli erscheint, endlich die Möglichkeit, virtuelle Maschinen in einem isolierten, privaten Netzwerk per NAT mit dem physikalischen Netzwerk zu verbinden, ohne eine Router-VM installieren zu müssen. Der NAT-Switch wurde notwendig, um per Docker bereitgestellte Anwendungen mit den Netzwerk zu verbinden. Und so richtet man einen NAT-Switch ein:
Legen Sie zuerste mit Hyper-V einen neuen internen VM-Switch an. In früheren Versionen von Windows 10 gab es einen speziellen NAT-Switch, aber der ist zugunsten des internen Switches wieder gewichen:
$NatSwitch = New-VMSwitch -SwitchName Nat -SwitchType Internal
Als nächstes benötigen wir eine Gateway-Adresse für das interne Netzwerk, die wir an den Switch binden. Ein interner Switch legt automatisch auch eine interne virtuelle Netzwerkkarte an. An diese wird die Gateway-IP gebunden. Das folgende Script fragt zunächst den zum Switch gehörenden Netwerkadapter ab. Das passiert in zwei Schritten: Erst wird die virtuelle Repräsentation mit Get-VMNetworkadapter abgefragt, und dann wird die dazugehörige physikalische Netzwerkkarte abgefragt. Es handelt sich dabei um dasselbe Gerät, aber mit zwei unterschiedlichen Cmdlets abgefragt. Diesen umständlichen Weg gehen wir, weil get-Vmnetworkadapter uns den Interface-Index nicht zurück liefert, den wir benötigen, um das NAT-Netzwerk einzurichten:
$natNetworkAdapter = Get-VMNetworkAdapter -ManagementOS -SwitchName $NatSwitch.Name
$networkAdapter = Get-NetAdapter | Where-Object -FilterScript {
$_.deviceid -eq $natNetworkAdapter.DeviceId
}
Im nächsten Schritt legen wir die Gateway-IP Adresse an und binden Sie an die virtuelle Netzwerkkarte des Switches:
New-NetIPAddress -IPAddress 10.1.255.254 -PrefixLength 16 -InterfaceIndex $networkAdapter.InterfaceIndex
New-NetNat -Name NatNetwork -InternalIPInterfaceAddressPrefix '10.1.0.0/16' -Verbose
Nun müssen Sie nur noch Ihre VMs an den neuen Switch binden, und schon haben Ihre VMs Zugriff auf Ihr Netzwerk, ohne dass Sie einen externen Switch erstellen oder eine Router-VM anlegen mußten. Der NAT-Switch kann aber auch Datenverkehr in das interne Netzwerk leiten. Verwenden Sie hierzu das Cmdlet Add-NetNatStaticMapping:
Add-NetNatStaticMapping -NatName Nat -Protocol TCP -ExternalIPAddress 0.0.0.0 -InternalIPAddress 10.1.100.200 -InternalPort 80 -ExternalPort 80
Zum Anlegen des Switches habe ich eine kleine Funktion gebaut, die das Anlegen deutlich vereinfacht:
#requires -Version 3 -Modules Hyper-V, NetAdapter, NetNat, NetTCPIP
<#
.Synopsis
Creates a NAT-Switch in Windows 10
.DESCRIPTION
This Script can create a NAT-Switch for you. Just Enter the internal IP,
Subnet-Prefix and a Name for the Switch, and your done! The NAT-Switch requires
Windows 10 Anniversary Edition (Redstone) and Hyper-V activated.
The NAT-Switch can connect your VMs from internal Networks with the physical Network
without a dedicated Router-VM
.EXAMPLE
new-natswitch -IpAddress 192.168.10.1 -prefix 24
Creates a new Switch with Name NAT and connects it to the internal Network 192.168.10.0
.EXAMPLE
new-natswitch -SwitchName ConnectExternal -IpAddress 192.168.10.1 -prefix 24
Creates a new Switch with Name ConnectExternal and connects it to the internal Network 192.168.10.0
.LINK
https://msdn.microsoft.com/en-us/virtualization/hyperv_on_windows/user_guide/setup_nat_network
https://technet.microsoft.com/de-de/library/dn283352%28v=wps.630%29.aspx?f=255&MSPPError=-2147217396
http://www.indented.co.uk/2010/01/23/powershell-subnet-math/
#>
function New-Natswitch
{
param
(
# Name of the virtual Switch.
[String]
[validateScript({
-not ( Get-VMSwitch -Name $_ )
})]
$SwitchName = 'Nat',
# Internal IP-Address of the Switch (= internal Gateway)
[IPAddress]
[Parameter(Mandatory)]
$IpAddress,
# The Subnet-Mask in CIDR-Form
[string]
[ValidateRange(8,30)]
[Parameter(Mandatory)]
$prefix,
# The Name of the virtual network. If not set, the network will be named like the Nat-Switch
[string]
$NetNatName
)
If ( Get-NetNat -ea SilentlyContinue )
{
throw 'You can only have one NatNetwork per Host'
}
If ( -not $NetNatName )
{
$NetNatName = $SwitchName
}
# Regular Expression needed to split 32 Bit into 8 Bit Octetts
$octettRegex = '(?<=\G\d{8})(?=.)'
# Convert Prefix into Bit-Representation
for ($x = 1; $x -le 32; $x++)
{
if ($x -le $prefix)
{
$bit = '1'
}
else
{
$bit = '0'
}
[string]$subnetmask += $bit
}
# Convert IP into Binary Representation
$ipOctettList = $IpAddress.IPAddressToString -split '\.' | foreach-object { ([convert]::ToString($_,2)).padleft(8,'0') }
$ipBinary = $ipOctettList -join ''
# Logical AND (band) calculates Subnet from IP
$SubnetBinary = [convert]::ToUInt64($ipBinary,2) -band [convert]::ToUInt64($subnetmask,2) |
ForEach-Object { ([convert]::ToString($_,2)).padleft(32,'0') }
# Convert Binary IP back into Decimal IP Representation
$subnetIP = ($SubnetBinary -split $octettRegex | ForEach-Object { [convert]::ToUInt64($_,2) }) -join '.'
# Create the switch
try
{
$NatSwitch = New-VMSwitch -SwitchName $SwitchName -SwitchType Internal
}
catch
{
'Failed to create Virtual Switch: {0}' -f $_.Exception
break
}
$natNetworkAdapter = Get-VMNetworkAdapter -ManagementOS -SwitchName $NatSwitch.Name
$networkAdapter = Get-NetAdapter | Where-Object -FilterScript {
$_.deviceid -eq $natNetworkAdapter.DeviceId
}
try
{
$null = New-NetIPAddress -IPAddress $IpAddress.IPAddressToString -PrefixLength $prefix -InterfaceIndex $networkAdapter.InterfaceIndex -EA Stop
New-NetNat -Name $NetNatName -InternalIPInterfaceAddressPrefix "$subnetIP/$prefix" -EA Stop
}
catch
{
'Fehler: {0}' -f $_.Exception
"`nNatswitch konnnte nicht angelegt werden."
$NatSwitch | Remove-VMSwitch -Force
}
}
Das ganze geht auch ein bißchen übersichtlicher mit Subroutinen, aber dann haben wir nicht mehr die ganze Funktionalität in einem Script. Mehr dazu im nächsten Tipp.