Skip to content

FR: Windows (Host) Tailscale Daemon; Listen on IP of WSL2 VM virtual network #3530

@dhermes

Description

@dhermes

What are you trying to do?

Access the Windows (Host) Tailscale Daemon from within WSL2

How should we solve this?

At a high-level there are 3 discrete things you could do to make this work:

  • (1) Listen on the "vEthernet (WSL)" IP when running the Windows (Host) Tailscale Daemon; e.g. 172.27.64.1:41112. (Right now the daemon only binds to localhost:41112.)
  • (2) Auto-detect WSL2 environments in tailscale subcommands and then point at the host address (e.g. 172.27.64.1:41112) instead of the /var/run/tailscale/tailscaled.sock UDS
  • (3) Add the minimal required firewall rule to allow "remote" TCP traffic into the port exposed on the "vEthernet (WSL)" IP; can / should lock it down to the WSL2 subnet, e.g. 172.27.64.0/20. This is only necessary due to (1).

Some snippets for autodetecting "I'm in WSL2 Linux" in tailscale subcommands

func isWSL() (bool, error) {
	if runtime.GOOS != "linux" {
		return false, nil
	}

	utsname := syscall.Utsname{}
	err := syscall.Uname(&utsname)
	if err != nil {
		return false, err
	}

	release := toString(utsname.Release[:])
	fmt.Printf("release = %q\n", release)
	return strings.HasSuffix(release, "-microsoft-standard-WSL2"), nil
}

func toString(s []int8) string {
	b := make([]byte, 0, len(s))
	for _, c := range s {
		if c == 0 {
			break
		}
		b = append(b, byte(c))
	}
	return string(b)
}

This code won't actually compile with GOOS=windows or GOOS=darwin so you need some build tags to safely implement isWSL(). I'm also not sure if strings.HasSuffix(release, "-microsoft-standard-WSL2") is comprehensive enough.

I realize this may cause some refactoring to allow tailscale --socket to support a TCP socket on Linux in addition to a UDS. You could work around it by running a UDS as a proxy for the host address (e.g. 172.27.64.1:41112), but without systemd in WSL2 this is probably more trouble than it's worth. (I am out of my depth here though, I'm not sure what the full set of options are.)


Some snippets for getting "vEthernet (WSL)" info:

PS C:\Users\dhermes> ipconfig

Windows IP Configuration


Unknown adapter Tailscale:

   Connection-specific DNS Suffix  . : dhermes.github.beta.tailscale.net
   IPv6 Address. . . . . . . . . . . : fd7a:115c:a1e0:ab12:4843:cd96:6270:f073
   Link-local IPv6 Address . . . . . : fe80::99d0:ec2d:b2e7:536b%7
   IPv4 Address. . . . . . . . . . . : 100.112.240.115
   Subnet Mask . . . . . . . . . . . : 255.255.255.255
   Default Gateway . . . . . . . . . :

Ethernet adapter vEthernet (WSL):

   Connection-specific DNS Suffix  . :
   Link-local IPv6 Address . . . . . : fe80::ed6d:71c5:2607:3ce6%22
   IPv4 Address. . . . . . . . . . . : 172.27.64.1
   Subnet Mask . . . . . . . . . . . : 255.255.240.0
   Default Gateway . . . . . . . . . :

Wireless LAN adapter Local Area Connection* 1:
...

The following Go code will just spit out 172.27.64.1:

func getIPv4ForInterface(name string) (*net.IP, error) {
	interface_, err := net.InterfaceByName(name)
	if err != nil {
		return nil, err
	}

	addrs, err := interface_.Addrs()
	if err != nil {
		return nil, err
	}

	found := []*net.IP{}
	for _, addr := range addrs {
		maskedIP, ok := addr.(*net.IPNet)
		if !ok {
			continue
		}
		if maskedIP.IP.To4() == nil {
			continue
		}
		found = append(found, &maskedIP.IP)
	}

	if len(found) == 1 {
		return found[0], nil
	}

	return nil, fmt.Errorf("expected exactly one match, got %d", len(found))
}

func getIPv4ForWSL() (*net.IP, error) {
	return getIPv4ForInterface("vEthernet (WSL)")
}

What is the impact of not solving this?

I am manually running a 172.27.64.1:41113 -> localhost:41112 proxy on the Windows host and a /var/run/tailscale/tailscaled.sock -> 172.27.64.1:41113 proxy on WSL2 Linux.

See: https://github.com/dhermes/tailscale-wsl2

Anything else?

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions