我在 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-NetFirewallRule
WSL2 部分中的命令。只需为防火墙规则运行该命令(一次)。
但是,如果你的 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 中安装了 Node.js,最简单的方法就是在 WSL 终端中运行它前启动服务器应用程序:
npx expose-wsl@latest