在托管机器的网络上访问 WSL2 Ubuntu 的应用程序

在托管机器的网络上访问 WSL2 Ubuntu 的应用程序

我在 WSL2 中运行一个应用程序,其发行版是 Ubuntu 20.04。我可以使用 Ubuntu 的 IP 在 Windows 浏览器中访问该应用程序。

现在,我想在我的网络上访问此应用程序(我的意思是在我的办公室网络上的其他机器上)

我知道 ubuntu 的 IP 仅对主机可用。我怎样才能将其提供给网络上的其他人?

我们该怎么做?请帮忙。

答案1

WSL1 的简短答案:

配置一次防火墙规则,一切就都“正常工作”了。这是最简单的方法。

WSL2 的简短答案:

一旦正确配置完毕,你就可以使用 Ubuntu/WSL2 中的单个命令轻松启动端口转发:

ssh -f -N -R 8080:localhost:8080 "$(hostname).local"

配置方法如下。配置过程并不容易,但只需设置一次即可。


(更多)细节:

正如 Steeldriver 在评论中指出的那样,WSL1 和 WSL2 在这里的表现有所不同。

旁注只是为了澄清术语(因为在评论中略有不同):“WSL”是指控制两个版本(WSL1 和 WSL2)的子系统本身。

附注 2. 请不要让这篇文章的长度吓到你。我只是非常详细。考虑到相关 Github 问题目前有 536 条评论,相比之下我觉得我相当简洁 ;-)。


WSL1

WSL1 无疑是更简单的用例。由于它是 Linux 系统调用和 Windows 内核之间的“转换层”,因此它实际上利用了“真实的”Windows 网络接口。因此,您可以直接从网络上的另一台设备连接到 WSL1 中的端口。

你可能但是仍然需要防火墙规则。我将在 WSL2 答案中介绍这一点,因为它对两者都很常见。请参阅New-NetFirewallRuleWSL2 部分中的命令。只需为防火墙规则运行该命令(一次)。

但是,如果你的 Web 应用不需要 WSL2(大多数都需要)不是),通常从 WSL1 实例运行它要容易得多(或者至少以前是这样)。大多数人最终都会这么做。另一方面,使用我下面提出的新(至少对我来说)WSL2/SSH 方法,在 WSL2 中执行此操作并不像以前那么痛苦。我将选择哪条路线留给您。在这一点上,我认为两种方法都同样有效。

如果您选择使用 WSL1,我建议保留两个 Ubuntu 实例——一个使用 WSL1,另一个使用 WSL2。这就是我所做的。

要将现有的 WSL2 实例复制到新的 WSL1,请退出现有实例,然后从 PowerShell 中执行以下操作:

# Adjust base path as desired
$WSL_ROOT = "$env:USERPROFILE\WSL"
$WSL_IMAGE_NAME = "$(get-date -UFormat `"%Y-%m-%d`") Ubuntu Backup.tar"
mkdir -p "$WSL_ROOT\images"
mkdir -p "$WSL_ROOT\instances\Ubuntu_WSL1"
cd $WSL_ROOT
wsl -l -v
# Confirm name of distribution - If it isn't "Ubuntu", adjust the following line as needed
wsl --export Ubuntu "$WSL_ROOT\images\$WSL_IMAGE_NAME"
wsl --import Ubuntu_WSL1 .\instances\Ubuntu_WSL1\ .\images\$WSL_IMAGE_NAME --version 1
wsl ~ -d Ubuntu_WSL1

此时,您将处于 Ubuntu WSL1 实例中,但您将是 root 用户,因为 WSL 不会“记住” --import'd 实例的默认用户名。请按照我的“方法 1”从这个答案设置您的默认用户名。

此时,您有两个 WSL Ubuntu 实例,一个用于 WSL1(Ubuntu_WSL1),另一个用于 WSL2( 可能Ubuntu或 也许Ubuntu-20.04)。如果您使用 Windows 终端,它将检测到它们并创建用于启动的配置文件。或者,您也可以随时使用 手动启动wsl ~ -d <distroname>

另一个选择是简单的转变到 WSL1 并独占使用它。此操作的步骤类似于复制它,因为您可能仍想备份。再次退出实例,然后从 PowerShell 中:

# Adjust base path as desired
$WSL_ROOT = "$env:USERPROFILE\WSL"
$WSL_IMAGE_NAME = "$(get-date -UFormat `"%Y-%m-%d`") Ubuntu Backup.tar"
mkdir -p "$WSL_ROOT\images"
cd $WSL_ROOT
wsl -l -v
# Confirm name of distribution - If it isn't "Ubuntu", adjust the following line as needed
wsl --export Ubuntu "$WSL_ROOT\images\$WSL_IMAGE_NAME"
wsl --set-version Ubuntu 1

在这种情况下无需重置默认用户名。


WSL2

WSL2 开始变得更加复杂。虽然评论中有一个关于如何操作的文档链接,但我将向您指出原始 Github 问题和评论这确实使人们开始朝这个方向努力。

要在 WSL2 中实现此功能,必须解决两个实际问题:

  • 首先,WSL2 网络是一个虚拟网络(实际上是 Hyper-V vNIC)。正如您在问题中指出的那样,它并不驻留在您的“办公网络上”。您首先需要有某种方式告诉 Windows 主机将数据包路由到该端口的 WSL2 虚拟网络。

  • 由于虚拟 WSL2 网络地址在每次重新启动(或wsl --shutdown)时都会发生变化,因此转发过程很复杂。这意味着(至少使用评论和 Github 问题中记录的方法)您必须重复以下过程:

    • 查找 WSL2 IP 地址
    • 删除旧的防火墙规则
    • 删除旧的转发规则
    • 创建新的防火墙规则
    • 创建新的转发规则

...每次重启时。真痛苦,对吧?!

因此,我将提出一种我认为更简单的方法。如果您愿意,仍然可以使用另一种方法。这种方法有一些轻微地复杂的设置,但几乎所有设置都只需完成一次

此方法利用 SSH 提供端口转发。由于这是从 WSL2 端初始化的,因此它具有几个优点:

  • 首先,它可以作为运行应用程序(Web 服务器)时同一步骤的一部分完成。您没有提到应用程序的体系结构/语言,但我会假设它是最常见的一种——Node。如果是这种情况,您甚至可以将其包含在脚本中npm run。几乎可以肯定有一种适用于任何体系结构的技术。

  • 最重要的是,它不需要 WSL2 的 IP 地址。这样就避免了每次重启时都需要执行上述 4 个步骤。

那么,我们开始吧。首先,有一个“一次性”设置:

  • 启用 Windows OpenSSH 服务器。您可以按照 Microsoft指示,但我将在这里进行总结。首先以管理员身份打开 PowerShell 提示符,然后:

    Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0
    Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
    Set-Service -Name sshd -StartupType 'Automatic'
    

    这应该会自动创建 SSH 转发规则。再次查看文档如果您遇到任何麻烦。

  • 编辑C:\ProgramData\ssh\sshd_config并确保GatewayPorts yes没有被注释掉。我相信默认情况下它是禁用的。

  • 返回管理 PowerShell,运行:

    Start-Service sshd
    
  • 您没有提到您的 Web 应用程序运行的端口号,因此我将选择8080这些示例。根据需要进行调整。仍然在管理 PowerShell 中,运行:

    New-NetFirewallRule -DisplayName 8080 -Direction Inbound -LocalPort 8080 -Protocol TCP -Action Allow -Profile Private
    

    这:

    • 允许入站 TCP 流量
    • 在端口 8080 上
    • 来自专用网络上的设备

    如果您的网络已设置Public,请删除-Profile Private

  • 退出管理员 PowerShell

完成这些后,一切就绪。要在此点开始转发,请从 Ubuntu/WSL2 执行以下操作:

ssh -f -N -R 8080:localhost:8080 "$(hostname).local"

用你的视窗用户名和密码。

此时,您应该可以从同一办公网络上的另一台计算机(或电话等)访问您的 Web 应用程序。

解释:

  • 连接Ubuntu/WSL2
  • 我们设置的 OpenSSH 服务器
  • 使用"$(hostname).local"哪个(应该)总是通过 mDNS 找到正确的 DNS 名称(解释这个答案
  • 它不分配终端( )并在请求登录凭据后-N在后台运行( )-f
  • 它告诉远程 SSH 主机(Windows)将其端口 8080 上接收的流量转发到本地(WSL2)端口 8080。
  • 因为我们GatewayPorts yes在服务器配置中指定了,这意味着它将把转发扩展到其他网络上的主机也是如此。

答案2

虽然回答由...提供@NotTheDr01ds是正确的,并且非常有用,这里是另一种解决方法,即在 WSL 上的 Ubuntu 上使用由 Bash 提供支持的 PowerShell :)

在文件底部添加以下函数~/.bashrc。如果您有多个 WSL 实例,则可以对每个实例执行此操作。

wsl_port根据需要更改和的值win_port。如果您想在特定网络接口(而不是所有可用接口)上进行监听,也可以选择更改 的值win_ip,这就是 的含义0.0.0.0

wsl_win_proxy() {
    wsl_ip="$(ip route | grep -oP '^.*src \K[0-9\.]+')"
    wsl_port="8080"

    win_ip="0.0.0.0"
    win_port="8080"

    rule_name="Inbound TCP ${win_port}"
    win_get_fw_rule_cmd="Get-NetFirewallRule | Where { \$_.DisplayName -eq '${rule_name}' }"
    win_new_fw_rule_cmd="New-NetFirewallRule -DisplayName '${rule_name}' -Direction Inbound -Action Allow -Protocol TCP -LocalPort ${win_port}"

    if ! netsh.exe interface portproxy show all | grep -q -P "${win_ip}\s+${win_port}\s+${wsl_ip}\s+${wsl_port}"
    then
        powershell.exe Start-Process -Verb runAs -FilePath "netsh.exe" \
            -ArgumentList "interface","portproxy","add","v4tov4",\
                    "listenport=$win_port","listenaddress=$win_ip",\
                    "connectport=$wsl_port","connectaddress=$wsl_ip"

        if [[ $? -eq 0 ]]
        then
            echo "Port proxy '${win_ip}:${win_port} > ${wsl_ip}:${wsl_port}' is created."
        else
            echo "Port proxy '${win_ip}:${win_port} > ${wsl_ip}:${wsl_port}' failed."
        fi

        if ! powershell.exe ${win_get_fw_rule_cmd} | grep -q "$rule_name"
        then
            echo "Open PowerShell as Admin and create the following firewall rule:"
            echo -e '\033[1;33m'"$win_new_fw_rule_cmd"'\033[0m'
        fi
    fi
}
wsl_win_proxy

现在当你打开 WSL 时该函数将创建端口代理自动。使用的 PowerShell 命令必须以管理员权限执行,因此 Windows 将要求您确认。如果端口代理已经存在什么也不会发生。

函数的第二部分将测试是否合适防火墙规则。测试将通过规则的 DisplayName 进行,因此您可以删除之前为目标端口创建的规则,以允许该功能正常工作。如果防火墙规则已经存在,则不会发生任何事情,否则将指导您如何创建它。只有当有新的端口代理被建造。

您可以选择删除或注释调用该函数的行wsl_win_proxy,并在需要时将其作为 shell 命令启动。


以下 PowerShell 命令将帮助您管理创建的端口代理

netsh interface portproxy show all
netsh interface portproxy reset

参考:

答案3

我建造了一个npm包裹自动下载并运行这个WSLHostPatcher

暴露-wsl

因此,如果你在 WSL 中安装了 Node.js,最简单的方法就是在 WSL 终端中运行它启动服务器应用程序:

npx expose-wsl@latest

相关内容