繁体   English   中英

从 local.network 访问运行在 WSL(Linux 的 Windows 子系统)上的 web 服务器

[英]Access a web server which is running on WSL (Windows Subsystem for Linux) from local network

将 Ubuntu 安装为 WSL(适用于 Linux 的 Windows 子系统)后,我运行了:

root@teclast:~# python3 -m http.server
Serving HTTP on 0.0.0.0 port 8000 ...

and try to access to this web server from my windows machine http://0.0.0.0:8000 or http://192.168.1.178:8000 but no success, web server available only by the address http://127.0.0.1:8000http://localhost:8000这意味着我无法从 my.network 中的另一台电脑连接到这个 web 服务器。 是否可以从外部访问 WSL?

现有的答案都不适合我,因为 WSL2 在自己的虚拟机中运行并且有自己的网络适配器。 您需要某种桥接或端口转发才能使非本地主机请求工作(即来自同一网络上的另一台主机)。

我在https://github.com/microsoft/WSL/issues/4150中找到了一个可以解决问题的脚本:

$remoteport = bash.exe -c "ifconfig eth0 | grep 'inet '"
$found = $remoteport -match '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}';

if( $found ){
  $remoteport = $matches[0];
} else{
  echo "The Script Exited, the ip address of WSL 2 cannot be found";
  exit;
}

#[Ports]

#All the ports you want to forward separated by coma
$ports=@(80,443,10000,3000,5000);


#[Static ip]
#You can change the addr to your ip config to listen to a specific address
$addr='0.0.0.0';
$ports_a = $ports -join ",";


#Remove Firewall Exception Rules
iex "Remove-NetFireWallRule -DisplayName 'WSL 2 Firewall Unlock' ";

#adding Exception Rules for inbound and outbound Rules
iex "New-NetFireWallRule -DisplayName 'WSL 2 Firewall Unlock' -Direction Outbound -LocalPort $ports_a -Action Allow -Protocol TCP";
iex "New-NetFireWallRule -DisplayName 'WSL 2 Firewall Unlock' -Direction Inbound -LocalPort $ports_a -Action Allow -Protocol TCP";

for( $i = 0; $i -lt $ports.length; $i++ ){
  $port = $ports[$i];
  iex "netsh interface portproxy delete v4tov4 listenport=$port listenaddress=$addr";
  iex "netsh interface portproxy add v4tov4 listenport=$port listenaddress=$addr connectport=$port connectaddress=$remoteport";
}

从提升/管理员 Powershell 会话运行此脚本对我来说是诀窍。 这允许访问在同一端口上可从 Windows 主机 IP 访问的端口上运行的 WSL2 服务。

在你的虚拟机中,执行 ifconfig

您将在第一部分 (eth0:) inet xxxx 中看到您的 IP

这个 xxxx 是您必须在浏览器中输入的 IP。

请按照@erazerbrecht 分享的链接中提到的步骤,通过提供您的 IP 地址(而不是使用 localhost)和端口号来运行您的 HTTP 服务器。

例子:
root@teclast:~# python3 -m http.server -b 192.168.1.178 8000 Serving HTTP on 192.168.1.178 port 8000 (http://192.168.1.178 :8000/) ...

否则,您也可以这样做,而不是点击链接
1.转到Windows防御者防火墙
2.选择inbound
3.创建new rule 下一个
4.规则类型选择Program 下一个
5.选择All Program 下一个
6.选择allow the connection 下一个
7. 检查所有 3 个(域、私有、公共); 下一个
8. 提供规则名称
9.完成
10. 你很高兴

对于刚接触这篇文章的任何人,请查看 Microsoft 文档https://docs.microsoft.com/en-us/windows/wsl/networking#accessing-linux-networking-apps-from-windows-localhost

根据文档,如果您运行的是旧版本的 Windows(Build 18945 或更低版本),则需要获取 Linux 主机 VM 的 IP 地址

这应该是 Windows Build 18945 之后的非问题。

我遵循@toran-sahu 关于向 Windows Defender 防火墙添加入站规则的答案,但最近(在添加第二个 wsl2 实例之后)它再次停止工作。 我遇到了这个问题线程并在 cmd 提示符下运行以下命令让它再次为我工作。

wsl --shutdown

更新:似乎这个问题来自启用快速启动https://stackoverflow.com/a/66793101/8917310

类似于@countach 的回答:

如果使用 Ubuntu,请在 WSL 终端中输入ip address 查找显示#: eth0 ...的条目,其中#是一个小数字。 那里有一个ip地址。 用那个。

我编写了一个PowerShell脚本,使用了 2 个对我来说非常有效的简单命令。

# get the IP address of the WSL
$str = wsl -- ip -o -4 -json addr list eth0 | ConvertFrom-Json | %{ $_.addr_info.local } | ?{ $_ }
echo "Wsl IP: $str"

# establish the connection with the WSL
# listenport,connectport ports can be changes as per the needs
netsh interface portproxy add v4tov4 listenport=8080 listenaddress=0.0.0.0 connectport=8080 connectaddress=$str

使用此代码创建一个.ps1文件并从 PowerShell 终端执行它。
可以将它添加到调度程序中,以便脚本每次在 Windows 启动时运行。

现在 (03.2022) 要从运行在 WSL 2 上的应用程序外部访问,我们需要执行以下操作:

  • 在防火墙中制定规则以接受应用程序运行的协议和端口上的传入(也可能是传出)连接(例如 TCP/80)

  • 获取 WSL 的 vm IP: hostname -I

  • 如本页所述( 从局域网 (LAN) 访问 WSL 2 发行版),使用此 IP 地址在 Windows 中添加一个代理,该代理侦听端口并重定向到 WSL 的 vm。 这是通过在以管理员身份运行的 PowerShell 中的以下命令完成的:

netsh interface portproxy add v4tov4 listenport=80 listenaddress=0.0.0.0 connectport=80 connectaddress=192.168.101.100

其中192.168.101.100是来自hostname -I的 vm 的 IP,而80是我们要向外部开放的端口。

由于 WSL 的 IP 在重新启动时会发生变化,因此应该在 PowerShell 脚本中自动执行此操作,其中删除了以前的代理并将新的代理设置为当前 IP。 所有的功劳都归功于 Github 上的 Edwindijas,这个脚本受到了很大的启发:

$ports=@(80,21,22) # the ports you want to open
$addr='0.0.0.0';

$wslIP = bash.exe -c "hostname -I"
$found = $wslIP -match '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}';

if(! $wslIP -match '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}') {
  echo "WSL's IP cannot be found. Aborting";
  exit;
}

$portsStr = $ports -join ",";
iex "Remove-NetFireWallRule -DisplayName 'WSL 2 Firewall Unlock' ";
iex "New-NetFireWallRule -DisplayName 'WSL 2 Firewall Unlock' -Direction Outbound -LocalPort $portsStr -Action Allow -Protocol TCP";
iex "New-NetFireWallRule -DisplayName 'WSL 2 Firewall Unlock' -Direction Inbound -LocalPort $portsStr -Action Allow -Protocol TCP";

for ($i = 0; $i -lt $ports.length; $i++) {
  $port = $ports[$i];
  iex "netsh interface portproxy delete v4tov4 listenport=$port listenaddress=$addr";
  iex "netsh interface portproxy add v4tov4 listenport=$port listenaddress=$addr connectport=$port connectaddress=$wslIP";
}

脚本中提到的 ifconfig 如何在 windows 中工作? 不需要是“ipconfig”吗?

避免使用 web 的某些答案中使用的防火墙规则,我看到其中一些创建了某种允许所有防火墙规则(允许来自任何 ip 和任何端口的任何 terrafic),这可能会导致安全问题

只需使用此答案中的这一行,这对我来说非常有效(只需创建一个端口代理):

netsh interface portproxy add v4tov4 listenport=<windows_port> listenaddress=0.0.0.0 connectport=<wsl_port> connectaddress=<WSL_IP>

其中<WSL_IP>是 wsl 的 ip,您可以通过在 wsl 上运行ifconfig来获取它。 <windows_port> windows_port> 是 windows 将监听的端口, <wsl_port>是在 WSL 上运行的端口服务器。

您可以使用以下命令列出当前端口代理:

netsh interface portproxy show all

这意味着我无法从我网络中的另一台电脑连接到这个 web 服务器。 是否可以从外部访问 WSL?

应该注意的是,在这里的所有答案中,这个问题最初是针对 WSL1(在被问到时给出的)并且由 OP自行回答为 WSL 或 Windows 问题,该问题已通过 Windows 更新解决。

具有讽刺意味的是,这是 WSL2 上的一个已知问题(见脚注),所以多年后,人们开始在这种情况下找到它(并回答它)。

在 WSL2 上有多种解决方案,但在不尊重其他回答者的情况下,他们都错过了两个最简单的解决方案:

  • WSL1 :首先,只需使用 WSL1。 对于大多数(但不是全部)web 开发任务,您需要从网络上的另一台机器访问您的应用程序,WSL1 将更擅长联网,并且可以很好地运行您的应用程序和工具。 WSL2 引入了真实的、虚拟化的 Linux kernel,但是 WSL1 的“假内核”(系统调用转换层)今天对于大多数任务仍然可以正常工作。

    When running python3 -m http.server under WSL1, you'll automatically be able to access it from other machines on the network via the Windows host's IP address (or DNS name). 正如自我回答中提到的 OP,那个讨厌的 WSL1 错误已经修复多年了。


  • SSH 反向隧道如果您确实需要使用 WSL2,这里有一个不需要端口转发或任何高级防火墙规则的选项,每次 WSL 重新启动时都需要更新。 这些规则的问题在于,每次重新启动时,WSL 的 IP 地址都会更改,这意味着必须不断删除并重新创建这些规则。

    另一方面,我们可以在 WSL2 中使用 SSH 连接到 Windows,其中的名称,甚至可能是 IP 地址,都是不变的。

    一次性设置:启用 SSH

    有一些一次性设置,但无论如何它都很有用:

    首先,安装/启用 Windows OpenSSH 服务器。 这是 Windows 内置的功能,只需启用即可。 您可以在此处找到完整的说明,但这通常只是(来自 Admin PowerShell)的问题:

     Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0 Start-Service sshd Set-Service -Name sshd -StartupType 'Automatic' if (,(Get-NetFirewallRule -Name "OpenSSH-Server-In-TCP" -ErrorAction SilentlyContinue | Select-Object Name, Enabled)) { Write-Output "Firewall Rule 'OpenSSH-Server-In-TCP' does not exist. creating it..." New-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22 } else { Write-Output "Firewall rule 'OpenSSH-Server-In-TCP' has been created and exists." }

    编辑您的C:\ProgramData\ssh\sshd_config并设置GatewayPorts yes 这使我们能够使用 SSH 作为网络上其他设备的网关。

    最后,当然,您需要 Windows 中的防火墙规则,允许您将使用的 HTTP(s)(或其他)端口。 示例(来自 Admin PowerShell):

     New-NetFirewallRule -DisplayName "WSL Python 8000" -LocalPort 8000 -Action Allow -Protocol TCP

    并重启 OpenSSH Server 服务:

     Restart-Service sshd

    创建隧道

    有了这些,创建我们需要的隧道就很简单了:

     ssh -R 8000:localhost:8000 NotTheDr01ds@$(hostname).local

    当然,用您自己的NotTheDr01ds用户名替换 NotTheDr01ds,如果它不同于 WSL 用户名

    这将使用您的Windows用户名和密码,因为 SSH 在 Windows 端运行。

    确保它有效后,还有另外两个建议:

    • 设置基于密钥的身份验证,这样您就无需每次都输入密码
    • 将 SSH 连接更改为:
       ssh -fN -R 8000:localhost:8000 NotTheDr01ds@$(hostname).local
      这会将 SSH session 置于后台,而不是为其分配终端。

在此处输入图像描述

现在有一个 npm package:

npx expose-wsl@latest

我已经在重新安装 WSL的 Microsoft Windows 更新(KB4532132)上进行了测试,它按预期工作。 似乎问题与旧 Windows 版本或旧 WSL 版本有关。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM