@DanielB's answer is an excellent explanation of why all WSL2 distributions (I like to call them "instances") share the same address.
Each WSL2 instance has a separate:
- PID namespace
- Mount namespace
- IPC namespace
- UTS namespace
- WSLg System Distribution
However, they all share the following with the parent WSL2 VM (and thus each other):
- User namespace
- (the only one we really care about here) Network namespace
- Cgroup namespace
- Device tree (other than
/dev/pts)
- CPU/Kernel/Memory/Swap (obviously)
/init binary (but not process)
The interesting thing is that we can use the same technology, namespaces, to "pretend" to create a separate IP address for each instance.
For the "n-tier testing" scenario you mention in the comments, this will hopefully be sufficient. We can script the creation of the namespace for each instance and (optionally) automatically enter it via the wsl command-line.
While I had a hunch that this would work, I had never played around with network namespaces before trying this out. Don't worry - It was a worthwhile learning exercise for me. Huge credit goes to this blog post for getting me 90% there.
With that said, here's a first (or second, or third ...) pass at a script to configure a network namespace that will allow each namespace in each instance to have a different IP.
Create this as something like /usr/local/sbin/wsl-netns.sh:
#!/usr/bin/env bash
instance_num=$1
#if [ -e /run/netns/]
Create the bridge that will be common to all instances.
Only a wsl --shutdown will terminate the bridge, unless
otherwise manually removed.
if [ ! -e /sys/devices/virtual/net/br1 ]
then
ip link add name br1 type bridge
ip addr add 10.0.0.253/24 brd + dev br1
ip link set br1 up
fi
Add namespace for this instance
if [ ! -e /run/netns/vnet${instance_num} ]
then
ip netns add vnet${instance_num}
fi
Adds a veth pair. The vethX
side will reside # inside the namespace
and be the primary NIC inside that namespace.
The br-vethX end will reside in the primary
namespace.
ip link add veth${instance_num} type veth peer name br-veth${instance_num}
ip link set veth${instance_num} netns vnet${instance_num}
Give it a unique IP based on the instance number
ip netns exec vnet${instance_num}
ip addr add 10.0.0.${instance_num}/24 dev veth${instance_num}
ip link set br-veth${instance_num} up
Add the bridged end of the veth pair
to br1
ip link set br-veth${instance_num} master br1
ip netns exec vnet${instance_num}
ip link set veth${instance_num} up
Set the default route in the namespace
ip netns exec vnet${instance_num}
ip route add default via 10.0.0.253
Enable loopback fort he namespace
ip netns exec vnet${instance_num}
ip link set up dev lo
Set up NAT for return traffic
iptables
-t nat
-A POSTROUTING
-s 10.0.0.0/24
-j MASQUERADE
Enable forwarding
sysctl -w net.ipv4.ip_forward=1
Optional - Start a namespace for the
default WSL user (UID 1000).
You can exit this namespace normally
via the exit comamnd or Ctrl+D.
default_username=$(getent passwd 1000 | cut -d: -f1)
nsenter -n/var/run/netns/vnet${instance_num} su - $default_username
Not much error handling in there at the moment, but it's fairly straightforward. Set it executable and run it from an instance with:
/usr/local/sbin/wsl-netns.sh 1 # replace with the instance number
Or start up WSL directly with it via:
wsl ~ -d centos7-1 -u root -e sh -c "/usr/local/sbin/wsl-netns.sh 1"
Just make sure to use a unique instance number for each. The address for each instance/namespace will be 10.0.0.<instance_number>, and you can access it from any other WSL2 instance.
To test, start a simple service inside one of the namespaces. For example, if you are in the namespace that the script dropped you into, you could just run:
python3 -m http.server
You can then access that server at 10.0.0.1:8000 from another instance. You will not be able to access the service via any other IP.
Alternatively, you don't have to use nsenter to drop into the namespace. You can directly start a service inside it via:
sudo ip netns exec vnet<num> \
service xyz start
Or whatever your bring-up command is.
Added bonus:
- IPv6 works since you aren't leaving internal WSL2 network.
Limitations:
- Tested in Ubuntu - May need small tweaks for CentOS.
- You cannot access those IP addresses from Windows, just other WSL2 instances. You would need to add additional forwarding rules to handle that.
- Currently, all communication is via address. If you want to assign hostnames, you'll need to override WSL's automatic
/etc/hosts generation and define your own. Or set it up via DNS, of course.