将 WSL2 上的服务暴露给外部访问

WSL2 内启动的服务,如果想要被外部访问,需要在 Windows 上创建防火墙规则并开启端口转发,将该服务暴露出去。


管理端口转发

使用 Windows 自带的 Netsh(Network Shell) 可以管理端口转发,其中创建和删除操作需要管理员权限:

# 获取 WSL2 IP
PS> $WSL_IP = (wsl hostname -I).Trim() -split ' ' | Select-Object -First 1

# 创建端口转发
# text-embeddings-inference
PS> netsh interface portproxy add v4tov4 listenport=8080 listenaddress=0.0.0.0 connectport=8080 connectaddress=$WSL_IP

# 查看所有端口转发
PS> netsh interface portproxy show all

# 删除端口转发
PS> netsh interface portproxy delete v4tov4 listenport=9030 listenaddress=0.0.0.0

管理防火墙规则

开启端口转发后,外部请求可能仍然无法到达 WSL2,原因是数据包在到达物理网卡后就被 Windows 防火墙拦截下来了,此时需要创建防火墙规则允许入站请求,规则的创建和删除操作同样需要管理员权限:

# 创建防火墙规则,允许入站请求进入 Windows
PS> netsh advfirewall firewall add rule name="WSL 8080" dir=in action=allow protocol=TCP localport=8080 description="WSL TEI 服务"

# 查询防火墙规则
PS> netsh advfirewall firewall show rule name="WSL 8080"
# netsh 无法做到模糊匹配,可以使用 PowerShell cmdlet 来实现
# 查询以 WSL 开头的防火墙规则
PS> Get-NetFirewallRule -DisplayName "WSL *"
# 查询这些规则配置的端口
PS> Get-NetFirewallRule -DisplayName "WSL *" | Get-NetFirewallPortFilter

# 删除特定防火墙规则
PS> netsh advfirewall firewall delete rule name="WSL 8080"
# 批量删除防火墙规则
PS> Get-NetFirewallRule -DisplayName "WSL *" | Remove-NetFirewallRule

WSL2 执行 Powershell

在 WSL2 中也可以执行 Powershell 命令:

# 查看所有名字以 WSL 开头的防火墙规则以及对应的端口
$ powershell.exe -Command '
Get-NetFirewallRule -DisplayName "WSL *" | ForEach-Object {
$portFilter = $_ | Get-NetFirewallPortFilter
[PSCustomObject]@{
DisplayName = $_.DisplayName
Description = $_.Description
LocalPort = $portFilter.LocalPort
RemotePort = $portFilter.RemotePort
}
} | Sort-Object { [int]$_.LocalPort } | Format-Table -Wrap -AutoSize
'
DisplayName Description LocalPort RemotePort
----------- ----------- --------- ----------
WSL 5601 WSL Kibana 服务 5601 Any
WSL 8020 WSL Namenode RPC 服务 8020 Any
WSL 8030 WSL Doris WEBUI 服务 8030 Any
WSL 8080 WSL TEI 服务 8080 Any
WSL 8088 WSL YARN RM WEBUI 服务 8088 Any
WSL 9030 WSL Doris JDBC 服务 9030 Any
WSL 9091 WSL milvus 服务 9091 Any
WSL 9200 WSL ES 服务 9200 Any
WSL 9870 WSL Namenode WEBUI 服务 9870 Any
WSL 18042 WSL YARN NM WEBUI 服务 18042 Any
WSL 18080 WSL Spark HistoryServer WEBUI 服务 18080 Any
WSL 19888 WSL Mapreduce JobHistory WEBUI 服务 19888 Any

# 判断当前会话的用户身份是否在管理员列表中
$ powershell.exe -Command "[Security.Principal.WindowsPrincipal]::new([Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)"
True

注意到当前 WSL2 会话执行的 Powershell 命令具有管理员权限,在此基础上可以编写一个端口转发和防火墙规则管理脚本,在 WSL2 终端内就能完成端口的管理。

端口管理脚本

创建端口管理脚本 PortManager.ps1

PortManager.ps1 >folded
param(
# ===== 子命令:add =====
[Parameter(ParameterSetName="add", Mandatory=$true)]
[switch]$Add,

# ===== 子命令:remove =====
[Parameter(ParameterSetName="remove", Mandatory=$true)]
[switch]$Remove,

# ===== 子命令:search =====
[Parameter(ParameterSetName="search", Mandatory=$true)]
[switch]$Search,

# ===== 子命令:show =====
[Parameter(ParameterSetName="show", Mandatory=$true)]
[switch]$Show,

# ===== 参数 Port(仅对 add/remove/search 必需)=====
[Parameter(ParameterSetName="add", Mandatory=$true)]
[Parameter(ParameterSetName="remove", Mandatory=$true)]
[Parameter(ParameterSetName="search", Mandatory=$true)]
[int]$Port,

# ===== 参数 Desc(仅对 add 有效)=====
[Parameter(ParameterSetName="add")]
[string]$Desc = ""
)

# ===== 设置控制台输出编码为 UTF8 =====
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8

# ===== 防火墙规则名 =====
$RuleName = "WSL $Port"

# ===== 子命令:ADD =====
if ($Add) {
# 获取 WSL IP
$WSL_IP = (wsl hostname -I).Trim() -split ' ' | Select-Object -First 1

if (-not $WSL_IP) {
Write-Error "获取 WSL IP 失败"
exit 1
}

if (-not $Desc) {
$Desc = "WSL Port $Port"
}

Write-Host "WSL IP: $WSL_IP"
Write-Host "添加端口转发: $Port -> $WSL_IP`:$Port"

# 删除旧规则(避免冲突)
netsh interface portproxy delete v4tov4 `
listenaddress=0.0.0.0 listenport=$Port 2>$null

# 添加新规则
netsh interface portproxy add v4tov4 `
listenaddress=0.0.0.0 listenport=$Port `
connectaddress=$WSL_IP connectport=$Port

# 防火墙规则(先删再加)
netsh advfirewall firewall delete rule name="$RuleName" > $null 2>&1

netsh advfirewall firewall add rule `
name="$RuleName" `
dir=in action=allow protocol=TCP `
localport=$Port `
description="$Desc"

Write-Host "创建完成" -ForegroundColor Green
}

# ===== 子命令:REMOVE =====
elseif ($Remove) {
Write-Host "删除端口转发: $Port"

netsh interface portproxy delete v4tov4 `
listenaddress=0.0.0.0 listenport=$Port 2>$null

if ($LASTEXITCODE -eq 0) {
Write-Host "portproxy 已删除" -ForegroundColor Green
} else {
Write-Host "portproxy 不存在" -ForegroundColor Yellow
}

Write-Host "删除防火墙规则: $RuleName"

netsh advfirewall firewall delete rule `
name="$RuleName" > $null 2>&1

if ($LASTEXITCODE -eq 0) {
Write-Host "防火墙规则已删除" -ForegroundColor Green
} else {
Write-Host "防火墙规则不存在" -ForegroundColor Yellow
}

Write-Host "删除完成"
}

# ===== 子命令:SEARCH =====
elseif ($Search) {
Write-Host "查询端口: $Port"
Write-Host "------------------------"

# 查询 portproxy
Write-Host "[PortProxy]"
$pp_all = netsh interface portproxy show v4tov4
$pp_header = $pp_all[0..4]
$pp_data = $pp_all[5..($pp_all.Count-1)] | Select-String "\s+$Port(\s+|$)"
$pp = $pp_header + $pp_data

if ($pp_data) {
$pp | ForEach-Object {
Write-Host $_ -ForegroundColor Green
}
} else {
Write-Host "未找到 portproxy 规则" -ForegroundColor Yellow
}

Write-Host ""

# 查询防火墙规则
Write-Host "[Firewall Rule]"
$fw = netsh advfirewall firewall show rule name="$RuleName"

if ($fw -match "No rules match") {
Write-Host "未找到防火墙规则" -ForegroundColor Yellow
} else {
$fw | ForEach-Object {
Write-Host $_ -ForegroundColor Green
}
}
}

# ===== 子命令:SHOW =====
elseif ($Show) {
# 显示所有 portproxy
Write-Host "[PortProxy]"
$pp = netsh interface portproxy show v4tov4
if ($pp) {
$pp | ForEach-Object {
Write-Host $_ -ForegroundColor Green
}
} else {
Write-Host "未找到 portproxy 规则" -ForegroundColor Yellow
}

# 显示所有 WSL 开头的防火墙规则
Write-Host "[Firewall Rule]"
$fw = Get-NetFirewallRule -DisplayName "WSL *" | ForEach-Object {
$portFilter = $_ | Get-NetFirewallPortFilter
[PSCustomObject]@{
DisplayName = $_.DisplayName
Description = $_.Description
LocalPort = $portFilter.LocalPort
RemotePort = $portFilter.RemotePort
}
}
if ($fw) {
$fw | Sort-Object { [int]$_.LocalPort } | Format-Table -Wrap -AutoSize | Out-String | Write-Host -ForegroundColor Green
} else {
Write-Host "未找到防火墙规则" -ForegroundColor Yellow
}
}

使用方式如下,需要在有管理员权限的 Powershell 中运行:

# 开放端口(创建防火墙规则和端口转发)
PS> ./PortManager.ps1 -Add -Port 12345
PS> ./PortManager.ps1 -Add -Port 12346 -Desc "测试端口管理脚本"
# 取消开放端口(删除防火墙规则和端口转发)
PS> ./PortManager.ps1 -Remove -Port 12345
# 查询指定端口
PS> ./PortManager.ps1 -Search -Port 12345
# 查询所有端口
PS> ./PortManager.ps1 -Show

在 WSL 中可以调用:

$ powershell.exe -Command 'D:\Develop\PortManager.ps1 -Show'

将 WSL2 上的服务暴露给外部访问

https://thinklong.me/expose-wsl2-service/

作者

ThinkLong

发布于

2026-04-29

更新于

2026-04-29

许可协议

评论

+