윈도우에 WSL2가 설치된 경우 방화벽 및 포트포워딩 설명 입니다.
아래 명령어를 WSL이 설치된 윈도우에서 관리자권한으로 실행된 파워쉘에서 실행한다.
wsl -d Ubuntu ip addr show eth0 | Select-String -Pattern 'inet ' | ForEach-Object {($_ -split '\s+')[2] -split '/' | Select-Object -First 1}
- 위 명령은 WSL의 ip주소를 확인하는 코드로 실행시 주소가 표시됩니다.
netsh advfirewall firewall add rule name="WSL2 Web Access" dir=in action=allow protocol=TCP localport=9090
- 이 명령은 Windows 데스크톱의 9090번 포트로 들어오는 TCP 연결을 허용합니다.
netsh interface portproxy add v4tov4 listenport=9090 listenaddress=0.0.0.0 connectport=9090 connectaddress=172.19.224.230
- 이 명령은 Windows 데스크톱의 0.0.0.0:9090 (모든 IP의 9090 포트)으로 들어오는 요청을 WSL의 172.19.224.230:9090으로 전달합니다.
WSL은 부팅시 ip가 변동될 수 있음. 고정이 어려움. Ubuntu 터미널에서 ip확인하는 명령어
ip addr show eth0 | grep -oP 'inet \K[\d.]+'
ip 변경시 기존 포트 업데이트하는 스크립트
부팅시 ip 변경 가능성이 있으므로 작업스케줄러에 넣어 부팅시 시작하는 것이 좋음.
# 스크립트 이름: Update-WslPortForwarding.ps1
# 설명: WSL2 Ubuntu의 변경된 IP 주소를 감지하여 포트 포워딩 규칙을 자동으로 업데이트합니다.
# 사용법: 관리자 권한으로 실행되도록 작업 스케줄러에 등록합니다.
# PowerShell 콘솔 및 출력 인코딩을 UTF-8로 설정하여 문자 깨짐 방지
[System.Console]::OutputEncoding = [System.Text.Encoding]::UTF8
$OutputEncoding = [System.Text.Encoding]::UTF8
$LogFile = "C:\Scripts\WSL_PortForwarding_Log.txt" # 로그 파일 경로 (원하는 위치로 변경 가능)
$WslDistroName = "Ubuntu" # 사용할 WSL 배포판 이름 (기본 Ubuntu) - 이 값은 고정됩니다.
$PortConfigFile = "C:\Scripts\WSL_PortMappings.json" # 포트 매핑 설정 파일
# 로그 관리 설정
$MaxLogSizeMB = 10 # 로그 파일 최대 크기 (MB)
$MaxLogFiles = 5 # 보관할 로그 파일 개수
$LogCleanupDays = 30 # 로그 파일 보관 기간 (일)
# 기존 netsh 포트 포워딩 규칙에서 포트 매핑 추출
Function Get-ExistingPortMappings {
try {
Write-Log "Checking existing netsh port proxy rules..."
$netshOutput = netsh interface portproxy show all 2>$null
if ($LASTEXITCODE -ne 0 -or -not $netshOutput) {
Write-Log "No existing port proxy rules found or netsh command failed."
return @{}
}
$portMappings = @{}
# netsh 출력 파싱 (v4tov4 규칙만 추출)
$inV4toV4Section = $false
foreach ($line in $netshOutput) {
$line = $line.Trim()
# v4tov4 섹션 시작 확인
if ($line -match "Listen on ipv4:" -and $line -match "Connect to ipv4:") {
$inV4toV4Section = $true
continue
}
# 다른 섹션이 시작되면 v4tov4 섹션 종료
if ($line -match "Listen on" -and $line -notmatch "ipv4") {
$inV4toV4Section = $false
continue
}
# v4tov4 섹션 내에서 규칙 파싱
if ($inV4toV4Section -and $line -match "^\s*(\d+\.\d+\.\d+\.\d+)\s+(\d+)\s+(\d+\.\d+\.\d+\.\d+)\s+(\d+)") {
$listenAddress = $matches[1]
$listenPort = [int]$matches[2]
$connectAddress = $matches[3]
$connectPort = [int]$matches[4]
# 0.0.0.0으로 리슨하는 규칙만 WSL 관련으로 간주
if ($listenAddress -eq "0.0.0.0") {
$portMappings[$listenPort] = $connectPort
Write-Log "Found existing rule: $listenAddress`:$listenPort -> $connectAddress`:$connectPort"
}
}
}
return $portMappings
}
catch {
Write-Log "ERROR: Failed to parse existing port proxy rules. Error: $($_.Exception.Message)"
return @{}
}
}
# 포트 매핑을 JSON 파일로 저장
Function Save-PortMappings {
Param (
[hashtable]$PortMappings
)
try {
# 설정 파일 디렉토리 생성
$configDir = Split-Path -Path $PortConfigFile -Parent
if (-not (Test-Path -Path $configDir)) {
New-Item -Path $configDir -ItemType Directory -Force | Out-Null
}
# JSON으로 변환하여 저장 (읽기 쉬운 형태로)
# Hashtable을 PSCustomObject로 변환하여 JSON 직렬화 오류 방지
# HashTable의 Key를 String으로 변환하여 Json 직렬화에 적합하게 만듬
$tempObject = New-Object -TypeName System.Collections.Specialized.OrderedDictionary
foreach ($key in $PortMappings.Keys) {
$tempObject.Add($key.ToString(), $PortMappings[$key])
}
$jsonOutput = $tempObject | ConvertTo-Json -Depth 2
$jsonOutput | Out-File -FilePath $PortConfigFile -Encoding UTF8
Write-Log "Saved $($PortMappings.Count) port mapping(s) to config file: $PortConfigFile"
return $true
}
catch {
Write-Log "ERROR: Failed to save port mappings to config file. Error: $($_.Exception.Message)"
return $false
}
}
# 포트 매핑 설정 파일 로드 또는 생성
Function Load-PortMappings {
if (Test-Path -Path $PortConfigFile) {
try {
Write-Log "Loading port mappings from config file: $PortConfigFile"
$jsonContent = Get-Content -Path $PortConfigFile -Raw -Encoding UTF8
$tempObject = ConvertFrom-Json $jsonContent
$portMappings = @{}
if ($tempObject -is [System.Management.Automation.PSCustomObject] -and $tempObject.PSObject.Properties.Count -gt 0) {
# JSON에서 로드된 객체의 속성을 순회하며 해시테이블에 추가, 값은 정수형으로 강제 변환
foreach ($property in $tempObject.PSObject.Properties) {
try {
# 키는 문자열로, 값은 정수형으로 변환하여 저장
$portMappings[$property.Name] = [int]$property.Value
} catch {
Write-Log "WARN: Failed to convert port mapping value for key '$($property.Name)'. Error: $($_.Exception.Message)"
}
}
}
# 빈 설정 파일인 경우 기존 netsh 규칙에서 생성
if ($portMappings.Count -eq 0) {
Write-Log "Config file is empty or invalid. Checking existing netsh rules..."
$existingMappings = Get-ExistingPortMappings
if ($existingMappings.Count -gt 0) {
Write-Log "Found $($existingMappings.Count) existing port proxy rule(s). Updating config file..."
Save-PortMappings -PortMappings $existingMappings
return $existingMappings
}
else {
Write-Log "No existing rules found. Using default port mappings."
$defaultMappings = @{80 = 80; 9090 = 9090}
Save-PortMappings -PortMappings $defaultMappings
return $defaultMappings
}
}
Write-Log "Loaded $($portMappings.Count) port mapping(s) from config file."
return $portMappings
}
catch {
Write-Log "ERROR: Failed to load port mappings from config file. Error: $($_.Exception.Message)"
Write-Log "Attempting to generate from existing netsh rules..."
$existingMappings = Get-ExistingPortMappings
if ($existingMappings.Count -gt 0) {
Save-PortMappings -PortMappings $existingMappings
return $existingMappings
}
else {
Write-Log "Using default port mappings."
$defaultMappings = @{80 = 80; 9090 = 9090}
Save-PortMappings -PortMappings $defaultMappings
return $defaultMappings
}
}
}
else {
Write-Log "Port mapping config file not found: $PortConfigFile"
Write-Log "Checking for existing netsh port proxy rules to auto-generate config..."
$existingMappings = Get-ExistingPortMappings
if ($existingMappings.Count -gt 0) {
Write-Log "Found $($existingMappings.Count) existing port proxy rule(s). Creating config file..."
Save-PortMappings -PortMappings $existingMappings
Write-Log "Auto-generated config file from existing netsh rules."
return $existingMappings
}
else {
Write-Log "No existing port proxy rules found. Creating default config file..."
$defaultMappings = @{
80 = 80
9090 = 9090
}
Save-PortMappings -PortMappings $defaultMappings
Write-Log "Created default port mapping config file with ports: $($defaultMappings.Keys -join ', ')"
Write-Log "You can edit $PortConfigFile to add or remove port mappings."
return $defaultMappings
}
}
}
# 로그 파일 관리 함수
Function Manage-LogFiles {
try {
$LogDir = Split-Path -Path $LogFile -Parent
$LogFileName = [System.IO.Path]::GetFileNameWithoutExtension($LogFile)
$LogFileExtension = [System.IO.Path]::GetExtension($LogFile)
# 현재 로그 파일 크기 확인
if (Test-Path -Path $LogFile) {
$LogSizeMB = (Get-Item $LogFile).Length / 1MB
# 로그 파일이 최대 크기를 초과하면 아카이브
if ($LogSizeMB -gt $MaxLogSizeMB) {
$ArchiveFileName = "$LogFileName`_$(Get-Date -Format 'yyyyMMdd_HHmmss')$LogFileExtension"
$ArchiveFilePath = Join-Path -Path $LogDir -ChildPath $ArchiveFileName
# 현재 로그 파일을 아카이브로 이동
Move-Item -Path $LogFile -Destination $ArchiveFilePath -Force
# 새 로그 파일에 아카이브 정보 기록
$Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
Add-Content -Path $LogFile -Value "[$Timestamp] Log file archived to: $ArchiveFileName (Size: $([math]::Round($LogSizeMB, 2)) MB)" -Encoding UTF8 # UTF8 인코딩 명시
}
}
# 오래된 로그 파일 정리
$ArchiveFiles = Get-ChildItem -Path $LogDir -Filter "$LogFileName`_*$LogFileExtension" -ErrorAction SilentlyContinue
if ($ArchiveFiles.Count -gt 0) {
# 날짜 기준으로 오래된 파일 삭제
$CutoffDate = (Get-Date).AddDays(-$LogCleanupDays)
$OldFiles = $ArchiveFiles | Where-Object { $_.CreationTime -lt $CutoffDate }
foreach ($OldFile in $OldFiles) {
Remove-Item -Path $OldFile.FullName -Force
$Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
Add-Content -Path $LogFile -Value "[$Timestamp] Deleted old log file: $($OldFile.Name) (Created: $($OldFile.CreationTime.ToString('yyyy-MM-dd')))" -Encoding UTF8 # UTF8 인코딩 명시
}
# 개수 기준으로 오래된 파일 삭제 (날짜 기준 삭제 후에도 초과하는 경우)
$RemainingFiles = Get-ChildItem -Path $LogDir -Filter "$LogFileName`_*$LogFileExtension" -ErrorAction SilentlyContinue | Sort-Object CreationTime -Descending
if ($RemainingFiles.Count -gt $MaxLogFiles) {
$FilesToDelete = $RemainingFiles | Select-Object -Skip $MaxLogFiles
foreach ($FileToDelete in $FilesToDelete) {
Remove-Item -Path $FileToDelete.FullName -Force
$Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
Add-Content -Path $LogFile -Value "[$Timestamp] Deleted excess log file: $($FileToDelete.Name) (Keeping only $MaxLogFiles recent files)" -Encoding UTF8 # UTF8 인코딩 명시
}
}
}
}
catch {
# 로그 관리 실패 시에도 스크립트는 계속 진행
$Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
Add-Content -Path $LogFile -Value "[$Timestamp] WARNING: Log file management failed: $($_.Exception.Message)" -ErrorAction SilentlyContinue -Encoding UTF8 # UTF8 인코딩 명시
}
}
Function Write-Log {
Param (
[string]$Message
)
$Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
# 로그 디렉토리 확인 및 생성
$LogDir = Split-Path -Path $LogFile -Parent
if (-not (Test-Path -Path $LogDir)) {
New-Item -Path $LogDir -ItemType Directory -Force | Out-Null
}
# 로그 파일 관리 (첫 번째 로그 메시지 작성 전에 실행)
if (-not $Global:LogManagementDone) {
Manage-LogFiles
$Global:LogManagementDone = $true
}
Add-Content -Path $LogFile -Value "[$Timestamp] $Message" -Encoding UTF8 # UTF8 인코딩 명시
Write-Host "[$Timestamp] $Message"
}
# 관리자 권한 확인
if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
Write-Log "ERROR: This script must be run as Administrator."
exit 1
}
Write-Log "----------------------------------------------------"
Write-Log "WSL Port Forwarding Update Script Started."
Write-Log "Log Management: Max Size=${MaxLogSizeMB}MB, Max Files=${MaxLogFiles}, Cleanup Days=${LogCleanupDays}"
try {
# 1. 포트 매핑 설정 로드
$PortMappings = Load-PortMappings
Write-Log "Active port mappings:"
foreach ($ListenPort in $PortMappings.Keys) {
# PortMappings[$ListenPort]가 null이거나 비어있을 수 있으므로 기본값 설정
$connectPortValue = if ($PortMappings[$ListenPort] -ne $null) { $PortMappings[$ListenPort] } else { "(알 수 없음)" }
Write-Log " Windows:$ListenPort -> WSL:$connectPortValue"
}
# 2. WSL 배포판 시작 시도 및 IP 가져오기
Write-Log "Attempting to start WSL distribution '${WslDistroName}' and get IP address..."
# WSL 배포판을 시작합니다. 이미 실행 중이면 아무 작업도 수행하지 않습니다.
# -e 옵션으로 간단한 명령을 실행하여 WSL을 활성화하고, 오류 발생 시 바로 종료합니다.
wsl -d $WslDistroName -e echo "WSL distro '$WslDistroName' is starting or already running." 2>$null
if ($LASTEXITCODE -ne 0) {
Write-Log "ERROR: WSL 배포판 '$WslDistroName'을(를) 시작할 수 없습니다. 배포판 이름이 정확한지 또는 WSL이 제대로 설치되어 있는지 확인하세요."
exit 1
}
Start-Sleep -Seconds 5 # WSL이 IP를 할당받을 시간을 줍니다.
# IP 주소 가져오기
Write-Log "Getting current WSL IP address for ${WslDistroName}..."
$WslIpOutput = wsl -d $WslDistroName -e ip addr show eth0 2>$null
if ($LASTEXITCODE -ne 0) {
Write-Log "ERROR: WSL에서 네트워크 인터페이스 정보를 가져오지 못했습니다. 'ip addr show eth0' 명령어가 WSL 내에서 작동하는지 확인하세요."
exit 1
}
$ipMatch = $WslIpOutput | Select-String -Pattern 'inet (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' | Select-Object -First 1
if ($ipMatch) {
$WslIp = $ipMatch.Matches.Groups[1].Value
Write-Log "Current WSL IP for ${WslDistroName}: ${WslIp}"
} else {
Write-Log "ERROR: WSL 네트워크 인터페이스에서 IP 주소를 추출할 수 없습니다. WSL 내에서 'ip addr show eth0' 명령어가 작동하는지 확인하세요."
exit 1
}
if ([string]::IsNullOrWhiteSpace($WslIp)) {
Write-Log "ERROR: 가져온 IP 주소가 비어 있습니다."
exit 1
}
# IP 주소 형식 검증
if (-not ($WslIp -match '^\d+\.\d+\.\d+\.\d+$')) {
Write-Log "ERROR: 잘못된 IP 주소 형식: $WslIp"
exit 1
}
# 4. 기존 포트 포워딩 규칙 삭제
Write-Log "Deleting existing port proxy rules..."
foreach ($ListenPort in $PortMappings.Keys) {
$deleteResult = netsh interface portproxy delete v4tov4 listenport=$ListenPort listenaddress=0.0.0.0 2>&1
if ($LASTEXITCODE -eq 0) {
Write-Log "Deleted rule for port $ListenPort."
} else {
Write-Log "WARN: Could not delete rule for port $ListenPort. It might not exist."
}
}
# 5. 새로운 포트 포워딩 규칙 추가
Write-Log "Adding new port proxy rules..."
$SuccessCount = 0
foreach ($ListenPort in $PortMappings.Keys) {
$ConnectPort = $PortMappings[$ListenPort]
# $ConnectPort가 비어있을 수 있으므로 유효성 검사 추가 (선택 사항)
if ([string]::IsNullOrWhiteSpace($ConnectPort)) {
Write-Log "ERROR: 포트 $ListenPort에 대한 연결 포트 값이 유효하지 않습니다. 규칙을 추가할 수 없습니다."
continue
}
$addResult = netsh interface portproxy add v4tov4 listenport=$ListenPort listenaddress=0.0.0.0 connectport=$ConnectPort connectaddress=${WslIp} 2>&1
if ($LASTEXITCODE -eq 0) {
Write-Log "Added rule: listenport=$ListenPort -> connectaddress=${WslIp}:${ConnectPort}"
$SuccessCount++
} else {
Write-Log "ERROR: Failed to add rule for port $ListenPort. Output: $addResult"
}
}
# 6. 결과 확인
Write-Log "Verifying port proxy rules..."
$CurrentRules = netsh interface portproxy show v4tov4 2>$null
if ($CurrentRules) {
Write-Log "Current active port proxy rules:"
$CurrentRules | ForEach-Object { Write-Log " $_" }
} else {
Write-Log "No active port proxy rules found."
}
Write-Log "WSL Port Forwarding Update Script Finished Successfully. ($SuccessCount/$($PortMappings.Count) rules added)"
}
catch {
Write-Log "FATAL ERROR: An unhandled exception occurred: $($_.Exception.Message)"
Write-Log "Stack Trace: $($_.ScriptStackTrace)"
exit 1
}
finally {
Write-Log "----------------------------------------------------`n"
}
실행 명령어
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "C:\Scripts\Update-WslPortForwarding.ps1"
작업스케줄러 등록 방법
작업 스케줄러 등록:
|
'IT 관련 > 우분투(Ubuntu) 이것저것' 카테고리의 다른 글
| Homepage 설치하기 (0) | 2025.06.20 |
|---|---|
| WSL2 터미널 언어(로케일) 설정 방법 (0) | 2025.06.19 |
| Ubuntu 24.04 LTS에 Cockpit 및 Navigator 설치 (0) | 2025.06.18 |
| docker 설치 n8n 업데이트 설치 (0) | 2025.06.12 |
| Ubuntu Nginx 웹 서버 구축: Cloudflare, 시놀로지 NAS 역방향 프록시를 이용한 HTTPS 다중 웹사이트 호스팅 가이드 (0) | 2025.06.07 |