如何从本地网络访问在WSL(Windows Subsystem for Linux)上运行的Web服务器

Rom*_*man 10 linux windows ubuntu webserver windows-subsystem-for-linux

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

root@teclast:~# python3 -m http.server
Serving HTTP on 0.0.0.0 port 8000 ...
Run Code Online (Sandbox Code Playgroud)

并尝试从我的Windows计算机访问该Web服务器http://0.0.0.0:8000http://192.168.1.178:8000仅由地址,但没有成功,可用的网络服务器http://127.0.0.1:8000http://localhost:8000那就意味着我无法从我的网络中的另一台电脑连接到这个网络服务器.是否可以从外部访问WSL?

小智 9

请按照@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 Defender 防火墙
2. 选择inbound
3. 创建new rule; 下一步
4. 选择Program作为规则类型;下一个
5. 选择All Program; 下一个
6. 选择allow the connection; 接下来
7. 检查所有 3(域、私有、公共);下一个
8. 提供规则名称
9. 完成
10. 您可以开始了


Erf*_*ary 8

避免使用网络上某些答案中使用的防火墙规则。我看到他们中的一些人创建了某种允许所有的防火墙规则(允许来自任何 IP 地址和任何端口的任何流量)。这可能会导致安全问题。

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

netsh interface portproxy add v4tov4 listenport=<windows_port> listenaddress=0.0.0.0 connectport=<WSL_port> connectaddress=<WSL_IP>
Run Code Online (Sandbox Code Playgroud)

其中是WSL<WSL_IP>的 IP 地址。您可以通过在 WSL 上运行来获取它。也是Windows 将侦听的端口,并且端口服务器在 WSL 上运行。ifconfig<windows_port><wsl_port>

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

netsh interface portproxy show all


mli*_*bby 7

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

我在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";
}
Run Code Online (Sandbox Code Playgroud)

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

  • 对我来说效果非常好——确保你的 WSL2 发行版上安装了 `ifconfig`。这也可以在登录时添加到任务计划程序中,作为“powershell -executionpolicybypass -f path\to\script.ps1”,延迟至少 10 秒,以允许 WSL2 首先启动(使您的 Linux 实例可用于获取计算机的IP 来自)。 (4认同)
  • @Seangle 作者在这里。很高兴看到该解决方案仍在使用! (2认同)

小智 6

在您的虚拟机中,执行 ifconfig

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

这个 xxxx 是你必须放在浏览器中的 IP。

  • 难道 [`ifconfig` 在 Ubuntu/Linux 上已被***弃用***](https://dougvitale.wordpress.com/2011/12/21/deprecated-linux-networking-commands-and-their-replacements /) 超过 10 年?替换为 [`ip a`](https://en.wikipedia.org/wiki/Iproute2)、`ip link` 和 `ip -s`? (5认同)

Ada*_*ele 6

与countach\xe2\x80\x99s 答案类似,但使用iproute2

\n

如果使用 Ubuntu,请输入ip addressWSL 终端。查找显示某个小数字#: eth0 ...在哪里的条目。#那里有一个IP地址。用那个。

\n


ren*_*que 6

目前(2022 年 3 月)要从外部访问在 WSL 2 上运行的应用程序,我们需要执行以下操作:

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

  • 获取 WSL 的虚拟机 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
    
    Run Code Online (Sandbox Code Playgroud)

192.168.101.100虚拟机的IP地址来自哪里hostname -I以及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";
}
Run Code Online (Sandbox Code Playgroud)


Not*_*1ds 6

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

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

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

WSL2 上有多种解决方案,但无意冒犯这里的其他回答者,他们都错过了两个最简单的解决方案:

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

    在 WSL1 下运行时python3 -m http.server,您将自动能够通过 Windows 主机的 IP 地址(或 DNS 名称)从网络上的其他计算机访问它。正如 OP 在自我回答中提到的那样,那个讨厌的 WSL1 错误已经修复多年了。


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

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

    一次性设置:启用 SSH

    为此有一些一次性设置,但无论如何它都是有用的:

    首先,安装/启用 Windows OpenSSH 服务器。这是 Windows 内置的功能,只需启用即可。您可以在此处找到完整的说明,但这通常只是(来自管理员 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."
    }
    
    Run Code Online (Sandbox Code Playgroud)

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

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

    New-NetFirewallRule -DisplayName "WSL Python 8000" -LocalPort 8000 -Action Allow -Protocol TCP
    
    Run Code Online (Sandbox Code Playgroud)

    并重新启动 OpenSSH Server 服务:

    Restart-Service sshd
    
    Run Code Online (Sandbox Code Playgroud)

    创建隧道

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

    ssh -R 8000:localhost:8000 NotTheDr01ds@$(hostname).local
    
    Run Code Online (Sandbox Code Playgroud)

    当然,NotTheDr01ds如果与 WSL 用户名不同,请替换为您自己的 Windows 用户名

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

    一旦您确保其有效,还有另外两个建议:

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