Skip to content

Instantly share code, notes, and snippets.

@vilicvane
Last active December 3, 2022 06:25
Show Gist options
  • Save vilicvane/0edcb3bec10339a3b633bc9305faa8b5 to your computer and use it in GitHub Desktop.
Save vilicvane/0edcb3bec10339a3b633bc9305faa8b5 to your computer and use it in GitHub Desktop.
Access Windows host from WSL 2
# Access host ports from WSL 2.
# https://gist.github.com/vilic/0edcb3bec10339a3b633bc9305faa8b5
# Make sure WSL gets initialized.
bash.exe -c exit
# Record host name for /etc/hosts that points to host IP.
$HOST_NAME = "host.wsl";
# Ports listened on host localhost to forward, you don't need to add the port if it listens all addresses.
$HOST_LOCALHOST_PORTS = @(52698);
$FIREWALL_RULE_NAME = "wsl";
$FIREWALL_RULE_DISPLAY_NAME = "WSL";
Write-Output "Detecting WSL IP address...";
$hostIP = wsl -- bash -c "tail -1 /etc/resolv.conf | cut -d' ' -f2";
$wslIP = (wsl -- ip address show eth0 | Select-String -Pattern "inet ([\d.]+)").Matches.Groups[1].Value;
Write-Output "Host IP address: $hostIP";
Write-Output "WSL IP address: $wslIP";
Write-Output "Updating hosts record $HOST_NAME ($hostIP) for WSL...";
wsl --user root -- echo "$hostIP`t$HOST_NAME" ">>" /etc/hosts;
Write-Output "Updating firewall rule...";
Remove-NetFireWallRule -Name $FIREWALL_RULE_NAME -ErrorAction Ignore;
New-NetFireWallRule `
-Name $FIREWALL_RULE_NAME `
-DisplayName $FIREWALL_RULE_DISPLAY_NAME `
-Direction Inbound `
-LocalAddress @($hostIP)`
-Action Allow;
Write-Output "Setting up localhost port proxies...";
foreach ($port in $HOST_LOCALHOST_PORTS) {
$previousRecordGroups = (netsh interface portproxy show v4tov4 | Select-String "(\S+)\s+$port\s+127\.0\.0\.1\s+$port").Matches.Groups;
if ($null -ne $previousRecordGroups) {
$previousHostIP = $previousRecordGroups[1].Value;
netsh interface portproxy delete v4tov4 listenport=$port listenaddress=$previousHostIP | Out-Null;
}
netsh interface portproxy add v4tov4 listenport=$port listenaddress=$hostIP connectport=$port connectaddress=127.0.0.1 | Out-Null;
}
Write-Output "Done.";
@PavelSosin-320
Copy link

No, unfortunately, it only corrects nameserver issue. It looks like impossible to get Host IP easily because Host and WSL get their IP via different Interfaces: WiFi and Eth0 These 2 networks are bridges. My real Host IP I got using PS Get-NetIPAddress and it is 192.168.1.211 on WiFi got via my Router. WSL Dockeer Desktop ID is inet addr:172.19.175.100 Bcast:172.19.175.255 on Eth0 by ifconfig. I don't know what is DHCP for WSL but it is not in my Router diapason. I suppose that ping from WSL to Host goes through WiFi bridge because ping time is 0.6 ms, i.e. this is not loopback interface. Ping localhost inside WSL is 10 times faster.
CURL and other TCP based protocols are blocked by Windows firewall which sees them coming from WiFi.

@vilicvane
Copy link
Author

@PavelSosin-320 there are several different networks in the context and both of your host and WSL have multiple IP addresses. This script does basically two things:

  1. Provide a way to update what host.wsl resolves to within WSL (by updating the hosts file of host).
  2. Create firewall rule so that WSL can access host via their shared network.

It doesn't have to be the network of the router to get the host and WSL communicate.

@PavelSosin-320
Copy link

Indeed, files are updated but with wrong values. Windows don't need Docker Desktop IP because it uses a pipe to communicate with Docker daemon. But Ubuntu WSL VM needs Host Windows IP because all Docker ports are exposed on the Windows Node )!_ If I want to either access Docker via HTTP REST API from Ubuntu or use service running on Windows from Docker plugin I need the HOST IP.WSL VM IP doesn't help me because WSL ports are not shared between WSL distros if they are not exposed to the host. WSL switch is my overlay network in this case.
Maybe, this is a bug in the Docker daemon implementation which doesn't listen to all IPs , WSL VM and Host?

@vilicvane
Copy link
Author

vilicvane commented Jun 21, 2020

@PavelSosin-320 I see, I didn't know your use case. If you want to access localhost port within WSL, you may try the following command:

netsh interface portproxy add v4tov4 listenport=8080 listenaddress=$hostIP connectport=8080 connectaddress=127.0.0.1

Updated the script to add portproxy.

@PavelSosin-320
Copy link

And now it prevents Docker Desktop from starting at all! The smoke test of Docker Daemon availability is curl http://localhost/engine/state
returns curl: (7) Failed to connect to localhost port 80: Connection refused. The docker Host definition is placed into Windows hosts file in the WINDOWS ... Drivers\etc\hosts and it is correct - it points to the Host Machine IP on which all Docker and its Orchestration engines related ports are exposed and available for the outside world via host IP:port. The Host name in the Windows hosts file is called hot.docker.internal with IP achieved from external DHCP. All networks inside Windows are bridged to the host network by default or by explicit declaration about reuse of the Host communication layer.
Processes running inside WSL VM can communicate with each other via Host ports if Windows firewall allows access to the Hosts ports in both directions! It shall be defined by Windows defender rule which shall allow:
Client inside WSL VM TCP -> host.docker.internal : 80, 343, 256, 257, .., everything exposed by Docker . -> Docker engine inside WSL VM IP.
If requests from outside are served by a Proxy 1st such a proxy can run as Windows process or Docker container, i.e. inside WSL VM Proxy shall be able to reach every process, Windows service, WSL subprocess with own IP and be available from Linux CLI client.
Shortly - bidirectional communication between WSL and Host shall be allowed for certain ports.

C:\WINDOWS\system32>

@vilicvane
Copy link
Author

vilicvane commented Jul 4, 2020

Updated the implementation and now it append the hosts file inside WSL.

@PavelSosin-320
Copy link

I reconfigure my network in the top-down manner i.e. had configured IPV4 DNS list in my router, then configured Windows, shut down and re-start WSL, and in some magic way Resolv.conf got the top DNS in my DNS list in the Router's Resolv.conf. In other words, there are smart home routers, like Technicolor that can manage the Home network as an SDN network.
From the other hand, I afraid that it blocks any access to devices or WSL VMs via ports reserved for Internet using its built-in firewall

@sellonen
Copy link

Thank you, this helped me a great deal!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment