-
-
Save MichaelBelgium/2243e5713833ba44b5675d844eeb1c85 to your computer and use it in GitHub Desktop.
# Requires -RunAsAdministrator | |
# Only works for WSL v2, this is completely not needed for WSL v1 where u always can use 127.0.0.1 in hosts file | |
Clear-Host | |
if ((Get-InstalledModule "Carbon" -ErrorAction SilentlyContinue) -eq $null) { | |
Install-Module -Name 'Carbon' -AllowClobber | |
} | |
Import-Module 'Carbon' | |
$wslIp = (wsl hostname -I) -replace ' ','' | |
Write-Host "Setting wsl v2 hosts entries to $wslIp" | |
$domains = @( | |
'wsl2.local' | |
# add more domains if necessary | |
) | |
foreach($domain in $domains) { | |
Set-HostsEntry -IPAddress $wslIp -HostName $domain | |
} | |
Write-Host "Done!" |
Thanks for providing the script!
Hey Michael,
thanks for this script.
I have been doing a bit of experimentation to see if I could take what you have done and extend it a bit to meet my specific use cases, some of which will likely apply to others. Not sure whether you want to expand this work or not, but figured it was worth sharing anyway. In particular I wanted to be able to update the Windows hosts file per WSL distro (I have several) ... more on that below. Here are some observations from playing around ...
- Using hostname -I is unreliable. I found that this worked some of the time but not consistently. At present my distros (I have several) are returning multiple IPs (eth0, docker0 and br-xxxxx) which is clearly not whats needed:
goffinf@DESKTOP-XXXXXXX:~$ hostname -I
172.26.103.206 172.17.0.1 172.22.0.1
I have found this approach to be reliable (shown from with Powershell because the call syntax is critical .. specifically using -- instead of -e .... also note $selectedDistro - more on that below):
$wslIp = (wsl -d $selectedDistro -- ip route get 1
| sed -n 's/^.src ([0-9.]) .*$/\1/p')`
- I run multiple WSL distros so I wanted the update to the Windows hosts file to ONLY change those for a selected distro. To enable that I did two things.
a. I used a specific naming convention in the hosts file, where each entry carries the name of the distribution it belongs to. For example ... these belong to my distro called 'Ubuntu-20.04'
# wsl2.Ubuntu-20.04.local
172.26.103.206 wsl2.Ubuntu-20.04.local
172.26.103.206 sample.k3d.wsl2.Ubuntu-20.04.local
172.26.103.206 rancher.server.wsl2.Ubuntu-20.04.local
172.26.103.206 kubernetes.dashboard.Ubuntu-20.04.local
172.31.46.82 rancher-k3d.wsl2.Ubuntu-20.04.local
and these to another distro configured for systemd and k8s called 'Ubuntu-20.04-systemd-k8s'
# wsl2.Ubuntu-20.04-systemd-k8s.local
172.31.46.82 wsl2.Ubuntu-20.04-systemd-k8s.local
172.31.46.82 rancher.server.wsl2.Ubuntu-20.04-systemd-k8s.local
172.31.46.82 rancher.k3d.academy.wsl2.Ubuntu-20.04-systemd-k8s.local
172.31.46.82 ingress.test.wsl2.Ubuntu-20.04-systemd-k8s.local
172.31.46.82 kubernetes.dashboard.wsl2.Ubuntu-20.04-systemd-k8s.local
172.31.46.82 nginx.wsl2.Ubuntu-20.04-systemd-k8s.local
172.31.46.82 kubernetes.dashboard.ingress.wsl2.Ubuntu-20.04-systemd-k8s.local
172.31.46.82 portainer.wsl2.Ubuntu-20.04-systemd-k8s.local
The choice of name is a bit arbitrary, but hopefully you see where I'm going with that.
b. In the Powershell script, I prompt the user for the name of the distribution they want to work with (using a Out-GridView populated from wsl --list), .. that sets the $selectedDistro variable mentioned above ... I use that variable to select the list of domains to update, then foreach around those and update just those entries in the hosts file using the Carbon module:
...
# Get a list of all wsl distros
$wsl_distros_raw = @(wsl --list) | Write-Output
# Remove empty entries
$wsl_distros = @($wsl_distros_raw.Where({ $_ -ne "" }))
# Remove null chars from entries
for ( $index = 0; $index -lt $wsl_distros.Count; $index++) {
$wsl_distros[$index] = $wsl_distros[$index] -replace '\x00', ''
}
# Put the array into a LIST
$wsl_distros_list = [System.Collections.Generic.List[String]]@($wsl_distros)
# Remove the first entry .. its W i n d o w s S u b s y s t e m f o r L i n u x D i s t r i b u t i o n s :
[void]$wsl_distros_list.Remove($wsl_distros_list[0])
# Select the required distro
$selectedDistro = $wsl_distros_list | Sort-Object | Out-GridView -PassThru -Title "Choose the WSL distribution to use to update the Windows hosts file"
$wslIp = (wsl -d $selectedDistro -- ip route get 1 `| sed -n 's/^.*src \([0-9.]*\) .*$/\1/p')
...
switch ( $selectedDistro ) {
Ubuntu_20_04 {
$domains = $domains_Ubuntu_20_04
}
Ubuntu_20_04_systemd_k8s {
$domains = $domains_Ubuntu_20_04_systemd_k8s
}
}
foreach($domain in $domains) {
Write-Output "Updating the WSL host IP address for for domain $domain for distribution $selectedDistro to $wslIp"
Set-HostsEntry -IPAddress $wslIp -HostName $domain
}
Anyway, not sure if any of this is of use to you, but hopefully it is.
Kind Regards
Fraser.
Hmmm, a bit more reading about network namespaces with WSL2 ... it appears that currently ALL WSL instances share the SAME network namespace because they also share the same virtual machine which has a single network interface. Therefore the IP address for all instances will also be the same !
It is possible to add another virtual ip to eth0 but AFAIK that wont be reachable from the Windows side.
It is possible to create an additional network namespaces within each WSL distro and have processes attach to that ... but that isn't available OOTB and I'm not sure its worth the effort ... back to the drawing board (sigh)
The tip around getting the WSL IP might still be useful though
Using hostname -I is unreliable. I found that this worked some of the time but not consistently. At present my distros (I have several) are returning multiple IPs (eth0, docker0 and br-xxxxx) which is clearly not whats needed:
@goffinf Interesting, I didn't know that as I only have one WSL instance (ubuntu). It probably lists all ips from all the instances u have yes/
That may be partly correct, in the sense that hostname -I appears to only return the IP associated with eth0 when only a single distro is running (although I couldn't swear to it be consistent all the time). As soon as another WSL distro is started (in my case one that has been configured for systemd) hostname -I startes returning the ip for each bound device, for example below we can see eth0, docker0 and br-f69dc3652dfd ... and it does so in any of the wsl sessions until they are all shutdown (terminating doesn't appear to have any impact). The alternate method will always return the IP for eth0 so I think thats a better bet ?
ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP> 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: bond0: <BROADCAST,MULTICAST,MASTER> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 22:d7:2a:85:69:2f brd ff:ff:ff:ff:ff:ff
3: dummy0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 5e:ac:84:b2:47:08 brd ff:ff:ff:ff:ff:ff
4: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:15:5d:68:fc:62 brd ff:ff:ff:ff:ff:ff
inet 172.26.19.142/20 brd 172.26.31.255 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::215:5dff:fe68:fc62/64 scope link
valid_lft forever preferred_lft forever
5: sit0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000
link/sit 0.0.0.0 brd 0.0.0.0
6: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:eb:f4:41:8a brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
7: br-f69dc3652dfd: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:c6:c9:99:21 brd ff:ff:ff:ff:ff:ff
inet 172.22.0.1/16 brd 172.22.255.255 scope global br-f69dc3652dfd
valid_lft forever preferred_lft forever
# Requires -RunAsAdministrator
# Only works for WSL v2, this is completely not needed for WSL v1 where u always can use 127.0.0.1 in hosts file
$currentUser = New-Object Security.Principal.WindowsPrincipal $([Security.Principal.WindowsIdentity]::GetCurrent())
$testadmin = $currentUser.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
if ($testadmin -eq $false) {
Start-Process powershell.exe -Verb RunAs -ArgumentList ('-noprofile -noexit -file "{0}" -elevated' -f ($myinvocation.MyCommand.Definition))
exit $LASTEXITCODE
}
Clear-Host
if ((Get-InstalledModule "Carbon" -ErrorAction SilentlyContinue) -eq $null) {
Install-Module -Name 'Carbon' -AllowClobber
}
Import-Module 'Carbon'
$wslHostIp = (wsl cat /etc/resolv.conf | Select-String -Pattern "\d{1,3}(\.\d{1,3}){3}" -AllMatches).Matches.Value
$wslSelfIp = (wsl hostname -I) -replace ' ',''
Write-Host "Setting wsl v2 host ip to $wslHostIp"
Write-Host "Setting wsl v2 self ip to $wslSelfIp"
$wsl_host_domains = @(
'wsl2.host'
)
$wsl_self_domains = @(
'wsl2.self'
)
foreach($domain in $wsl_host_domains) {
Set-HostsEntry -IPAddress $wslHostIp -HostName $domain
}
foreach($domain in $wsl_self_domains) {
Set-HostsEntry -IPAddress $wslSelfIp -HostName $domain
}
Write-Host "Done!"
This is the script that I changed a little bit.
#!/bin/sh
export WINIP=$(cat /etc/resolv.conf | grep 'nameserver' | cut -f 2 -d ' ')
export WSLIP=$(ip addr show eth0 | grep 'inet ' | cut -f 6 -d ' ' | cut -f 1 -d '/')
echo WINIP $WINIP
echo WSLIP $WSLIP
echo <Your Password> | sudo -S sed -i "/winip/c $WINIP\twinip" /etc/hosts
echo <Your Password> | sudo -S sed -i "/wslip/c $WSLIP\twslip" /etc/hosts
vim "+:%s/^.*winip/$WINIP\t\twinip/g" "+:%s/^.*wslip/$WSLIP\t\twslip/g" '+:wq' -E /mnt/c/Windows/System32/drivers/etc/hosts
This is another solution. Run this script in ~/.bashrc can set hosts file both in win and linux.
Hi Guys!
The @derik007 bash version not working. :(
This is my version using Powershell; https://github.com/kallookoo/lamp/blob/feccb3e3c507f2d97afcc0962cb7e449ec0eeffb/src/win/hosts.ps1
Amazing. Thank you. It's perfect for my use case.
just for info, the @derik007 solution needs to use PowerShell as admin and execute "wsl", I've changed the last line :
vim "+:%s/^.*winip/$WINIP\t\twinip/g" "+:%s/^.*wslip/$WSLIP\t\twslip/g" '+:wq' -E /mnt/c/Windows/System32/drivers/etc/hosts
to
vi "+:%s/^.*wsl2.local/^$WSLIP wsl2.local/g" "+:wq" -E /mnt/c/Windows/System32/drivers/etc/hosts
I forked: https://gist.github.com/kakkun61/2139c240ac0dce1bb1606c1e8bc8038c
Added features:
- admin check
- run as admin
- install Carbon module if necessary
- remove old hosts entry
- multiple ip addr support
FWIW, my take can be found at https://gist.github.com/petski/1414048ca2db37592da2e7af13d718f5
Very useful! Thanks!