188 lines
6.3 KiB
PowerShell
188 lines
6.3 KiB
PowerShell
param(
|
|
[string]$ListenAddress = "0.0.0.0",
|
|
[string]$WslIp = ""
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
$knownPorts = @{
|
|
8000 = @{ Name = "hm-biz-process"; RuleName = "HM-BIZ-PROCESS-8000" }
|
|
8010 = @{ Name = "my-intranet-app"; RuleName = "MyIntranetApp-8010" }
|
|
8020 = @{ Name = "b17301"; RuleName = "B17301-8020" }
|
|
}
|
|
|
|
function Get-WslListeningPorts {
|
|
$raw = wsl.exe bash -lc "ss -ltnH 2>/dev/null"
|
|
if (-not $raw) {
|
|
throw "WSL listening TCP 포트를 확인하지 못했습니다."
|
|
}
|
|
|
|
$portMap = @{}
|
|
|
|
foreach ($line in ($raw -split "`r?`n")) {
|
|
if ([string]::IsNullOrWhiteSpace($line)) {
|
|
continue
|
|
}
|
|
|
|
$parts = ($line -split "\s+") | Where-Object { $_ -ne "" }
|
|
if ($parts.Count -lt 4) {
|
|
continue
|
|
}
|
|
|
|
$localAddress = $parts[3].Trim()
|
|
if ([string]::IsNullOrWhiteSpace($localAddress)) {
|
|
continue
|
|
}
|
|
|
|
$hostPart = $localAddress
|
|
$portPart = ""
|
|
$lastColon = $localAddress.LastIndexOf(":")
|
|
if ($lastColon -lt 0) {
|
|
continue
|
|
}
|
|
|
|
$hostPart = $localAddress.Substring(0, $lastColon)
|
|
$portPart = $localAddress.Substring($lastColon + 1)
|
|
$parsedPort = 0
|
|
if (-not [int]::TryParse($portPart, [ref]$parsedPort)) {
|
|
continue
|
|
}
|
|
|
|
$port = [int]$parsedPort
|
|
if ($port -le 0) {
|
|
continue
|
|
}
|
|
|
|
$normalizedHost = $hostPart.Trim("[", "]")
|
|
$isLoopback = (
|
|
$normalizedHost -eq "127.0.0.1" -or
|
|
$normalizedHost -eq "::1" -or
|
|
$normalizedHost -eq "localhost"
|
|
)
|
|
|
|
if (-not $portMap.ContainsKey($port)) {
|
|
$portMap[$port] = [ordered]@{
|
|
Port = $port
|
|
Hosts = New-Object System.Collections.Generic.List[string]
|
|
ExternallyReachable = $false
|
|
}
|
|
}
|
|
|
|
$entry = $portMap[$port]
|
|
if (-not $entry.Hosts.Contains($normalizedHost)) {
|
|
$entry.Hosts.Add($normalizedHost)
|
|
}
|
|
|
|
if (-not $isLoopback) {
|
|
$entry.ExternallyReachable = $true
|
|
}
|
|
}
|
|
|
|
return $portMap.Values | Sort-Object Port
|
|
}
|
|
|
|
if ([string]::IsNullOrWhiteSpace($WslIp)) {
|
|
$detected = wsl.exe hostname -I 2>$null
|
|
if (-not $detected) {
|
|
throw "WSL IP를 자동으로 찾지 못했습니다. -WslIp 옵션으로 직접 지정해 주세요."
|
|
}
|
|
|
|
$WslIp = (($detected -split "\s+") | Where-Object { $_ -match '^\d+\.\d+\.\d+\.\d+$' } | Select-Object -First 1)
|
|
if (-not $WslIp) {
|
|
throw "WSL IP를 자동으로 파싱하지 못했습니다. -WslIp 옵션으로 직접 지정해 주세요."
|
|
}
|
|
}
|
|
|
|
$windowsIps = Get-NetIPAddress -AddressFamily IPv4 |
|
|
Where-Object {
|
|
$_.IPAddress -notlike '127.*' `
|
|
-and $_.IPAddress -notlike '169.254*' `
|
|
-and $_.InterfaceAlias -notlike '*WSL*'
|
|
} |
|
|
Select-Object -ExpandProperty IPAddress
|
|
|
|
$listenAddresses = @($ListenAddress)
|
|
if ($ListenAddress -eq "0.0.0.0") {
|
|
$listenAddresses += $windowsIps
|
|
}
|
|
$listenAddresses = $listenAddresses | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | Select-Object -Unique
|
|
|
|
Write-Host "Setting Windows portproxy for all active WSL TCP ports..." -ForegroundColor Cyan
|
|
Write-Host "WSL IP: $WslIp"
|
|
$portEntries = @(Get-WslListeningPorts)
|
|
if (-not $portEntries -or $portEntries.Count -eq 0) {
|
|
throw "외부에서 접근 가능한 WSL TCP 포트를 찾지 못했습니다."
|
|
}
|
|
|
|
Write-Host "Detected ports: $((@($portEntries | ForEach-Object { $_.Port })) -join ', ')"
|
|
|
|
$reachableEntries = @($portEntries | Where-Object { $_.ExternallyReachable })
|
|
$loopbackOnlyEntries = @($portEntries | Where-Object { -not $_.ExternallyReachable })
|
|
|
|
if (-not $reachableEntries -or $reachableEntries.Count -eq 0) {
|
|
throw "현재 WSL에서 내부망에 공개 가능한 포트를 찾지 못했습니다. 앱이 0.0.0.0 또는 WSL IP에 바인딩되어 있는지 확인해 주세요."
|
|
}
|
|
|
|
$configuredPorts = New-Object System.Collections.Generic.List[int]
|
|
|
|
foreach ($entry in $reachableEntries) {
|
|
$port = [int]$entry.Port
|
|
$appInfo = $knownPorts[$port]
|
|
$displayName = if ($appInfo) { [string]$appInfo.Name } else { "wsl-port-$port" }
|
|
$ruleName = if ($appInfo) { [string]$appInfo.RuleName } else { "WSL-PORT-$port" }
|
|
|
|
Write-Host ""
|
|
Write-Host "$displayName : $port ($($entry.Hosts -join ', '))" -ForegroundColor Cyan
|
|
|
|
foreach ($address in $listenAddresses) {
|
|
netsh interface portproxy delete v4tov4 listenport=$port listenaddress=$address | Out-Null
|
|
netsh interface portproxy add v4tov4 listenport=$port listenaddress=$address connectport=$port connectaddress=$WslIp
|
|
}
|
|
|
|
$existingRule = Get-NetFirewallRule -DisplayName $ruleName -ErrorAction SilentlyContinue
|
|
if ($existingRule) {
|
|
Remove-NetFirewallRule -DisplayName $ruleName | Out-Null
|
|
}
|
|
|
|
New-NetFirewallRule `
|
|
-DisplayName $ruleName `
|
|
-Direction Inbound `
|
|
-Action Allow `
|
|
-Protocol TCP `
|
|
-LocalPort $port | Out-Null
|
|
|
|
if (-not $configuredPorts.Contains($port)) {
|
|
$configuredPorts.Add($port)
|
|
}
|
|
}
|
|
|
|
Write-Host ""
|
|
Write-Host "Completed." -ForegroundColor Green
|
|
Write-Host "Use these URLs from another PC on the intranet:"
|
|
if ($windowsIps) {
|
|
foreach ($ip in $windowsIps) {
|
|
foreach ($port in $configuredPorts) {
|
|
$appInfo = $knownPorts[$port]
|
|
$label = if ($appInfo) { [string]$appInfo.Name } else { "wsl-port-$port" }
|
|
Write-Host ("{0,-15}: http://{1}:{2}" -f $label, $ip, $port)
|
|
}
|
|
}
|
|
} else {
|
|
foreach ($port in $configuredPorts) {
|
|
$appInfo = $knownPorts[$port]
|
|
$label = if ($appInfo) { [string]$appInfo.Name } else { "wsl-port-$port" }
|
|
Write-Host ("{0,-15}: http://<Windows-IP>:{1}" -f $label, $port)
|
|
}
|
|
}
|
|
|
|
if ($loopbackOnlyEntries.Count -gt 0) {
|
|
Write-Host ""
|
|
Write-Host "Loopback-only ports (not published to intranet):" -ForegroundColor Yellow
|
|
foreach ($entry in $loopbackOnlyEntries) {
|
|
Write-Host ("- {0}: {1}" -f $entry.Port, ($entry.Hosts -join ', '))
|
|
}
|
|
Write-Host "These ports are bound only to 127.0.0.1/::1 inside WSL. Run those apps on 0.0.0.0 (or the WSL IP) to expose them to the intranet." -ForegroundColor Yellow
|
|
}
|
|
Write-Host ""
|
|
Write-Host "Tip: if WSL restarts and its IP changes, run this script again."
|