Обновление файла "host" Windows с IP-адресом WSL при запуске

Обновление файла "host" Windows с IP-адресом WSL при запуске
Обновление файла "host" Windows с IP-адресом WSL при запуске - traxer @ Unsplash

Я использую WSL2 в Windows 10.

Мой дистрибутив - Debian.

Я хотел установить статический IP для моего дистрибутива, но я узнал, что это невозможно сделать для WSL из этого ответа.

Однако я понял, что могут быть и другие возможности.

Возможно ли создать сценарий Powershell, который при запуске обновляет hosts etc на Windows 10 с новым IP WSL, указывающим на пользовательское доменное имя, например 172.18.225.26 dev.local? При этом не оставляя никаких предыдущих конфигураций WSL в файле хоста.

В настоящее время мой файл хоста выглядит следующим образом:

# Copyright (c) 1993-2009 Microsoft Corp.
#
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
#
# This file contains the mappings of IP addresses to host names. Each
# entry should be kept on an individual line. The IP address should
# be placed in the first column followed by the corresponding host name.
# The IP address and the host name should be separated by at least one
# space.
#
# Additionally, comments (such as these) may be inserted on individual
# lines or following the machine name denoted by a '#' symbol.
#
# For example:
#
#      102.54.94.97     rhino.acme.com          # source server
#       38.25.63.10     x.acme.com              # x client host

# localhost name resolution is handled within DNS itself.
#   127.0.0.1       localhost
#   ::1             localhost
# Added by Docker Desktop
192.168.8.101 host.docker.internal
192.168.8.101 gateway.docker.internal
# To allow the same kube context to work on the host and the container:
127.0.0.1 kubernetes.docker.internal
# End of section


# Added by Custom Script For WSL Distro
172.18.225.26 dev.local
#End of section

У меня нет знаний о Powershell сценариях, я буду очень признателен за помощь.

Я смог получить то, что хотел, благодаря помощи из этого ответа, но я сделал некоторые изменения.

В основном, я создал файл update_wsl_ip_to_domain.ps1 и сохранил в этом PATH C:\Scripts\update_wsl_ip_to_domain.ps1, затем я добавил следующие скрипты

#
# Powershell script for adding/removing/showing entries to the hosts file.
#
# Known limitations:
# - does not handle entries with comments afterwards ("<ip>    <host>    # comment")
#

$file = "C:\Windows\System32\drivers\etc\hosts"
$wsl_ip = (wsl -d debian hostname -I).trim()
$data = @('add','localwsl.com',$wsl_ip)

function add-host([string]$filename, [string]$hostname, [string]$ip) {
    remove-host $filename $hostname
    $ip + "`t`t" + $hostname | Out-File -encoding ASCII -append $filename
}

function remove-host([string]$filename, [string]$hostname) {
    $c = Get-Content $filename
    $newLines = @()

    foreach ($line in $c) {
        $bits = [regex]::Split($line, "\t+")
        if ($bits.count -eq 2) {
            if ($bits[1] -ne $hostname) {
                $newLines += $line
            }
        } else {
            $newLines += $line
        }
    }

    # Write file
    Clear-Content $filename
    foreach ($line in $newLines) {
        $line | Out-File -encoding ASCII -append $filename
    }
}

function print-hosts([string]$filename) {
    $c = Get-Content $filename

    foreach ($line in $c) {
        $bits = [regex]::Split($line, "\t+")
        if ($bits.count -eq 2) {
            Write-Host $bits[0] `t`t $bits[1]
        }
    }
}

try {
    if ($data[0] -eq "add") {

        if ($data.count -lt 3) {
            throw "Not enough arguments for add."
        } else {
            add-host $file $data[1] $data[2]
        }

    } elseif ($data[0] -eq "remove") {

        if ($data.count -lt 2) {
            throw "Not enough arguments for remove."
        } else {
            remove-host $file $data[1]
        }

    } elseif ($data[0] -eq "show") {
        print-hosts $file
    } else {
        throw "Invalid operation '" + $data[0] + "' - must be one of 'add', 'remove', 'show'."
    }
} catch  {
    Write-Host $error[0]
    Write-Host "`nUsage: hosts add <ip> <hostname>`n       hosts remove <hostname>`n       hosts show"
}

Затем я создал отдельный update_wsl_ip_to_domain.cmd файл в той же директории, что и ps1 файл, и добавил следующие команды.

PowerShell Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
Powershell -File C:\Scripts\update_wsl_ip_to_domain.ps1
PowerShell Set-ExecutionPolicy Restricted

Чтобы файл update_wsl_ip_to_domain.cmd запускался при запуске, я создал ярлык этого каталога C:\Users\<user_name>\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\ и дал ему права администратора.

Наконец, я также добавил еще один ярлык update_wsl_ip_to_domain.cmd файла на рабочем столе с правами администратора, который я запускаю вручную, потому что по какой-то причине предыдущий не всегда запускается при запуске.

UPDATES

На моем DebianWSL дистрибутиве я решил запустить apache на 80 порту и nginx на 81 порту.

Чтобы дать им статический IP и доменное имя, я отредактировал свой update_wsl_ip_to_domain.ps1 файл и добавил следующий код внизу.

#GIVING WSL NGINX A STATIC IP. IT RUNS AT PORT 81 ON MY DEBIAN
netsh interface portproxy add v4tov4 listenport=80 listenaddress=127.65.43.21 connectport=81 connectaddress=$wsl_ip

#GIVING WSL APACHE  A STATIC IP. IT RUNS AT PORT 80 ON MY DEBIAN
netsh interface portproxy add v4tov4 listenport=80 listenaddress=127.65.43.22 connectport=80 connectaddress=$wsl_ip

Затем я сделал следующие записи в моем hosts файле на Windows 10.

127.65.43.21        localwslnginx.com

127.65.43.22        localwslapache.com

Я считаю, что в этом есть большой потенциал, я просто смогу запускать несколько приложений на разных портах с помощью моего apache или nginx.

Я буду делать обновления по мере продвижения.

UPDATES 2 (28 декабря 2021 года)

Я смог запустить несколько приложений через wsl, при этом каждое из них имеет свои уникальные доменные имена. Вот как выглядит мой файл hosts на windows:

127.65.43.21        localwslnginx.com

127.65.43.22        localwslapache.com

# Laravel Apps
127.65.43.22        evangrest.test

127.65.43.22        firstbarcodes.test

Они должны иметь один и тот же статический IP с localwslapache.com или localwslnginx.com для apache и nginx соответственно.

Для apache я использовал следующий конфиг, чтобы запустить мое laravel приложение:

<VirtualHost *:80>
    DocumentRoot "/data/www/firstbarcodes/public"
    DirectoryIndex "index.php"
    ServerName firstbarcodes.test
    ServerAlias firstbarcodes.test    

    <Directory "/data/www/firstbarcodes">
       Options Indexes FollowSymLinks
       AllowOverride None
       Require all granted
    </Directory>
   
    <Directory "/data/www/firstbarcodes/public">
      Options Indexes FollowSymLinks MultiViews ExecCGI
      AllowOverride All
      Order allow,deny
      Allow from all
      Require all granted
    </Directory>
    
    #Alias /js /data/www/firstbarcodes/public/js
    #<Directory /data/www/firstbarcodes/public/js>
    #  Options Indexes FollowSymLinks MultiViews ExecCGI
    #  AllowOverride All
    #  Order allow,deny
    #  Allow from all
    #  Require all granted
    #</Directory>
</VirtualHost>

Вам также может понадобиться выполнить эту команду sudo a2enmod rewrite для apache.

Думаю, таким образом, я смог как-то создать хак для получения статического IP для всех моих apache и nginx приложений через WSL.

Что я хотел бы сделать дальше, так это заставить мои WSLDebianapache автоматически запускать мои mariadb и windows при update_wsl_ip_to_domain.ps1 запуске.

У меня была такая же проблема, и я пришел к собственному решению

Вот оно. В нем нет добавления статических IP netsh, но есть некоторые другие возможности :)

У меня есть несколько дистрибутивов WSL.

Для каждого из них я создаю файл с именем MYWSLDISTRONAME-hostnames.txt в каталоге scripts.

Этот файл содержит только доменные имена:

app.ds.local
pma.ds.local
sandbox.ds.local
test.ds.local

В начале, я добавляю эти доменные имена в конец файла hosts, только один раз, как например

127.0.0.1 app.ds.local
127.0.0.1 pma.ds.local
127.0.0.1 sandbox.ds.local
127.0.0.1 test.ds.local

Если мне нужно добавить другое доменное имя для моего дистрибутива, я останавливаю WSL, а затем просто добавляю

строку another.ds.local к MYWSLDISTRONAME-hostnames.txt и

127.0.0.1 another.ds.local к hosts

Каждый раз, когда мне нужно запустить мой WSL-дистрибутив, я запускаю следующий скрипт MYWSLDISTRONAME-start.ps1. Что он делает:

  • Запускает скрипт /root/start_services.sh внутри дистрибутива, который запускает необходимые службы и т.д. (тем самым решая проблему с отсутствием systemd)
  • Получает IP-адрес запущенного WSL-дистрибутива
  • Заменяет IP на новый в hosts для каждого доменного имени из MYWSLDISTRONAME-hostnames.txt. Все это делается с временным hosts файлом.
  • Проверяет, требует ли реальный hosts файл каких-либо изменений. Если да, то запускается отдельный pwsh файл, который запрашивает права администратора. Если нет, скрипт просто выводит сообщение.
$runningDirectory = Split-Path -Parent -Path $MyInvocation.MyCommand.Definition

$hostsFile        = "c:\windows\system32\drivers\etc\hosts"
$wslHostnamesFile = "$runningDirectory\MYWSLDISTRONAME-hostnames.txt"
$tmpFile          = "$runningDirectory\.new-hostnames.txt"
$distroName       = "MYWSLDISTRONAME"


# Start services inside the WSL
wsl -d $distroName -e /root/start_services.sh

# Get IP address of WSL distro
$wslIpAddr = wsl -d $distroName -- ip addr
$match = [System.Text.RegularExpressions.Regex]::Match($wslIpAddr, "(?<ip>(172|192\.168)\.[\d\.]*)/")
$ip = $match.Groups["ip"]

Write-Host "$distroName is available at $ip"


# read hosts file and change the IPs
$host_file_contents = Get-Content $hostsFile -Encoding UTF8 -Raw
$hostnames = Get-Content $wslHostnamesFile -Encoding UTF8
foreach ($hostname in $hostnames) {
    $host_file_contents = [System.Text.RegularExpressions.Regex]::Replace($host_file_contents, "(\b(?:(?:2(?:[0-4][0-9]|5[0-5])|[0-1]?[0-9]?[0-9])\.){3}(?:(?:2([0-4][0-9]|5[0-5])|[0-1]?[0-9]?[0-9]))\b)\s+$hostname", "$ip`t$hostname") 
}

# Save hosts file
$host_file_contents | Set-Content -Path $tmpFile -Encoding UTF8 

# Compare two files to avoid unncecessary popups
$result = Compare-Object -ReferenceObject ((Get-Content $hostsFile).trim() -ne '') -DifferenceObject ((Get-Content $tmpFile).trim() -ne '')
if ($result)
{
    # Prepare and execute encoded command (note that we use 'pwsh' instead of 'powershell')
    $command = "Get-Content -Path ""$tmpFile"" | Set-Content -Path ""$hostsFile"""
    Write-Host "Updating hosts file with: $command"
    $encodedCmd = [Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($command))
    Start-Process -FilePath pwsh.exe -Verb RunAs -ArgumentList "-encodedcommand $encodedCmd"
}
else 
{
    Write-Host "Hosts file is the same, modification is unnecessary"
}

Start-Sleep -Seconds 5

# Remove temporary file
Remove-Item $tmpFile

ПРИМЕЧАНИЕ: Мне очень нравится ваша идея с netsh и, вероятно, я добавлю ее в свои скрипты.

И, для полноты картины, у меня также есть MYWSLDISTRONAME-stop.ps1:

wsl.exe --shutdown -d MYWSLDISTRONAME

У меня также есть .lnk'ы для обоих .ps1 файлов с красивыми и отличительными иконками, чтобы легко идентифицировать каждый дистрибутив :).

Они могут быть запущены без прав администратора, скрипты запросят их при необходимости.


EDIT: изменил (?<ip>172\.[\d\.]*)/ на (?<ip>(172|192\.168)\.[\d\.]*)/, потому что кажется, что WSL2 тоже начал выдавать IP из этого диапазона (?). [отредактировано 17 февраля 2022 г.]


NevaDev, 9 февраля 2023 г., 00:47