Skip to content

Instantly share code, notes, and snippets.

@lalilaloe
Last active April 25, 2024 09:57
Show Gist options
  • Save lalilaloe/37d746b9a140b4b5679b5a9355dab623 to your computer and use it in GitHub Desktop.
Save lalilaloe/37d746b9a140b4b5679b5a9355dab623 to your computer and use it in GitHub Desktop.
Windows to WSL2 localhost access, ex. Mongodb

I basically avoided this issue by using WSL1 for the past years, but I found a solution that works for me; If you don't encounter this issue often just use $(hostname).local 😒

TLDR; This changes localhost to point to your Windows IP, 127.0.0.1 is still available for wsl. In windows you can access WSL via localhost as usual, because windows listens for exposed ports on WSL. Exposes your set windows port(s) via portproxies.

  1. Add this to your /etc/wsl.conf,
[boot]
command="sed -i \"s/127.0.0.1$(printf '\t')localhost/$(tail -1 /etc/resolv.conf | cut -d' ' -f2)$(printf '\t')localhost/g\" /etc/hosts 2>&1"

This command replaces the default localhost in /etc/hosts to point to your Windows IP, 127.0.0.1 is still available for wsl. This might require setting the host in your code to 127.0.0.1 for example in vite add server: { host: "127.0.0.1" } . Services running on Windows might need to be bind to 0.0.0.0 or it's local ip to be accessible in WSL. From windows you can access WSL via localhost as usual, because windows listens for exposed ports on WSL.

  1. a. And use this powershell script to configure portproxies fix-wsl2-port-forwading-to-windows-localhost. The firewall rules, might not be necessary but can be helpful if you encounter issues.

    b. While the above script also helps you set the firewall rules. You can also execute the following command to set the portproxies manually in (admin) Powershell. In this example ports 8080,27017. (credits above)

$addr='0.0.0.0'; $ports=@(8080,27017); # set ports according to your use case, what ports from windows should be exposed.
$remoteport = (wsl hostname -I).Trim(); for( $i = 0; $i -lt $ports.length; $i++ ){ $port = $ports[$i]; echo "added portproxy ${addr}:${port} to ${remoteport}:${port}"; iex "netsh interface portproxy delete v4tov4 listenport=$port listenaddress=$addr"; iex "netsh interface portproxy add    v4tov4 listenport=$port listenaddress=$addr connectport=$port connectaddress=$remoteport"; }

You can reset these proxies anytime using netsh interface portproxy reset

Because the WSL2 ip address changes every restart you need to add two scheduled tasks; 3. netsh with arguments interface portproxy reset to trigger 'On an event', Log 'Security' and Event ID '4647' (log off) + Event ID '1074' (shutdown/restart). 4. The above powershell command, you can save it to a .ps1 file and execute with powershell with arguments c:\path_to_script.ps1 to trigger 'At log on' 5. You can test the automated scheduled tasks manually, by simulating a restart using wsl --shutdown you'll see the ip has changed wsl.exe hostname -I.

This for me solves many of the main issues why I didn't switch, it's not the same as using WSL1. But does the job since you don't have to change localhost host configurations for any of your projects. Which is frustrating when using WSL2. Bonus you can enjoy the latest features such as WSLg and better performance 😀.

@lalilaloe
Copy link
Author

For mongodb i've added this function to the script. To not have Mongodb give the folowing error: Error setting up listener, {"code":9001,"codeName":"SocketException","errmsg":"An attempt was made to access a socket in a way forbidden by its access permissions."}

Source

function WaitUntilServices($searchString, $status)
{
    # Get all services where DisplayName matches $searchString and loop through each of them.
    foreach($service in (Get-Service -DisplayName $searchString))
    {
        # Wait for the service to reach the $status or a maximum of 30 seconds
        $service.WaitForStatus($status, '00:00:30')
    }
}

# and then

echo "wait till MongoDB is running"
WaitUntilServices "MongoDB *" "Running"

My full script

$ports=@(27017);
$addr='0.0.0.0';

function WaitUntilServices($searchString, $status)
{
    # Get all services where DisplayName matches $searchString and loop through each of them.
    foreach($service in (Get-Service -DisplayName $searchString))
    {
        # Wait for the service to reach the $status or a maximum of 30 seconds
        $service.WaitForStatus($status, '00:00:30')
    }
}

$remoteport = wsl hostname -I; 
$found      = $remoteport -match '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}';
if(!$found){
  echo "WSL ip address not found";
  exit;
}
echo "reset portproxy configuration"
netsh interface portproxy reset
echo "wait till MongoDB is running"
WaitUntilServices "MongoDB *" "Running"
for( $i = 0; $i -lt $ports.length; $i++ ){
  $port = $ports[$i];
  echo "portproxy ${addr}:${port} to ${remoteport}:${port}";
  iex "netsh interface portproxy delete v4tov4 listenport=$port listenaddress=$addr";
  iex "netsh interface portproxy add    v4tov4 listenport=$port listenaddress=$addr connectport=$port connectaddress=$remoteport";
}

@lalilaloe
Copy link
Author

Don't forget to bind ip to 0.0.0.0 in C:\Program Files\MongoDB\Server\7.0\bin\mongod.cfg

# network interfaces
net:
  port: 27017
  bindIp: 0.0.0.0

@lalilaloe
Copy link
Author

Basically

  1. Set startup command in /etc/wsl.conf/ to command="sed -i \"s/127.0.0.1$(printf '\t')localhost/$(tail -1 /etc/resolv.conf | cut -d' ' -f2)$(printf '\t')localhost/g\" /etc/hosts 2>&1"
  2. Set bindIp to 0.0.0.0 in C:\Program Files\MongoDB\Server\7.0\bin\mongod.cfg
  3. Copy above full script to C:\Users\{USER}\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup ex. wslmongo.ps1
  4. Reboot

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