MAATRIX / Блог / Мониторинг VPN-подключений на Windows Server

Мониторинг VPN-подключений на Windows Server

Мониторинг VPN-подключений на Windows Server

MAATRIX

WireGuard на Windows Server поднимается быстро, но у него нет встроенного журнала подключений — только wg show, который показывает текущее состояние и забывает его после перезапуска службы. Если через сервер ходят несколько человек, рано или поздно возникает вопрос «кто вообще заходил вчера ночью» — и ответить нечем. Ниже — рабочая связка: PowerShell-скрипт читает wg show, пишет события в Event Viewer, поднимает тревогу на незнакомого клиента и отчитывается в Uptime Kuma, чтобы вы видели не только «кто», но и «жив ли туннель вообще».

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Почему WireGuard «невидим» по умолчанию

WireGuard спроектирован минималистично: ни служба, ни драйвер не пишут в лог факт подключения пира. Есть только текущее состояние — wg show покажет время последнего handshake, но после перезапуска службы эта информация исчезает бесследно. У OpenVPN логирование коннектов есть из коробки, а для WireGuard эту дыру обычно закрывают сторонним скриптом — на Linux через cron и syslog, на Windows Server готового пути нет вовсе.

Если WireGuard на сервере ещё не установлен — начните с установки WireGuard на Windows Server.

Смотрим текущие подключения: `wg show`

Утилита wg.exe устанавливается вместе с WireGuard for Windows, обычно лежит в C:\Program Files\WireGuard\wg.exe. Из elevated PowerShell:

& "C:\Program Files\WireGuard\wg.exe" show

Строка peer: AbCdEf...12= — публичный ключ клиента, единственный устойчивый идентификатор (IP у мобильных клиентов постоянно меняется). latest handshake больше 2-3 минут назад означает, что клиент неактивен, endpoint — его внешний IP на момент согласования.

Для скрипта нужен машиночитаемый вывод — wg show wg0 dump. Первая строка — данные интерфейса, дальше по строке на пира через табуляцию: public-key preshared-key endpoint allowed-ips latest-handshake transfer-rx transfer-tx keepalive. Его и парсит скрипт ниже.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать Windows-сервер

Логирование в Event Viewer

Один раз создаём отдельный лог и источник событий (нужны права администратора):

New-EventLog -LogName "WireGuardMonitor" -Source "WireGuardMonitor"

После этого лог WireGuardMonitor появится в eventvwr.msc (Applications and Services Logs) — в него можно писать структурированные записи через Write-EventLog, не засоряя общий Application и не мешая Event Forwarding, если позже понадобится стянуть логи с нескольких серверов. Три кода событий: 1000 — известный пир активен, 2000 — незнакомый ключ, 2001 — служба WireGuard не отвечает.

Скрипт: парсим `wg show` и пишем в лог

Рабочий скрипт C:\WireGuardMonitor\Watch-WireGuard.ps1: читает дамп, сравнивает пиров со списком известных ключей, пишет и в Event Viewer, и в текстовый лог — так удобнее смотреть историю без открытия оснастки:

$wg = "C:\Program Files\WireGuard\wg.exe"; $tunnel = "wg0"
$logFile = "C:\WireGuardMonitor\connections.log"
$eventLog = "WireGuardMonitor"; $source = "WireGuardMonitor"
$knownPeers = @{ "AbCdEf1234567890abcdef1234567890abcdef12=" = "laptop-telim" }

function Write-Line($t) {
    Add-Content -Path $logFile -Value "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] $t"
}

try { $dump = & $wg show $tunnel dump 2>$null } catch { $dump = $null }
if (-not $dump) {
    Write-EventLog -LogName $eventLog -Source $source -EntryType Error -EventId 2001 -Message "Служба $tunnel не отвечает"; exit 1
}

$peerLines = ($dump -split "`n" | Where-Object { $_.Trim() -ne "" }) | Select-Object -Skip 1
foreach ($line in $peerLines) {
    $f = $line -split "`t"
    $pubKey = $f[0]; $endpoint = $f[2]; $handshake = [int]$f[4]
    $name = $knownPeers[$pubKey]
    $secondsAgo = if ($handshake -eq 0) { -1 } else { [int]((Get-Date -UFormat %s) - $handshake) }
    $isActive = $secondsAgo -ge 0 -and $secondsAgo -lt 180

    if ($isActive -and $name) {
        Write-Line "OK $name endpoint=$endpoint"
        Write-EventLog -LogName $eventLog -Source $source -EntryType Information -EventId 1000 -Message "Пир '$name' активен, $endpoint"
    } elseif ($isActive -and -not $name) {
        Write-Line "ALERT unknown peer=$pubKey endpoint=$endpoint"
        Write-EventLog -LogName $eventLog -Source $source -EntryType Warning -EventId 2000 -Message "НЕИЗВЕСТНЫЙ пир $pubKey с $endpoint"
        # внешнее оповещение — см. ниже
    }
}

Скрипт не хранит состояние между запусками, кроме лога, поэтому не боится падений сервера. Событие в Event Viewer хорошо смотреть постфактум, но узнать «прямо сейчас» проще через телеграм-бот — вместо комментария # внешнее оповещение добавьте:

$botToken = "123456789:AA...токен"; $chatId = "987654321"
$text = [uri]::EscapeDataString("WireGuard: неизвестный пир $pubKey с $endpoint")
Invoke-RestMethod -Method Post -Uri "https://api.telegram.org/bot$botToken/sendMessage?chat_id=$chatId&text=$text"

Токен и chat_id вынесите в secrets.ps1 с ограниченными NTFS-правами. Та же идея работает через Send-MailMessage или вебхук Slack/Discord. И не забывайте вписывать ключи новых легитимных клиентов в $knownPeers — иначе алерты быстро научитесь игнорировать.

Автозапуск и доступность

Ручной запуск бесполезен — нужен Task Scheduler с триггером по расписанию каждые 2 минуты:

$action = New-ScheduledTaskAction -Execute "powershell.exe" `
  -Argument "-NoProfile -ExecutionPolicy Bypass -File C:\WireGuardMonitor\Watch-WireGuard.ps1"
$trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) `
  -RepetitionInterval (New-TimeSpan -Minutes 2) -RepetitionDuration ([TimeSpan]::MaxValue)
Register-ScheduledTask -TaskName "WireGuard-Monitor" -Action $action -Trigger $trigger `
  -Principal (New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest)

Интервал совпадает с периодичностью пересогласования handshake — короткую сессию не пропустите, а лишней нагрузкой сервер не грузите. Запуск от SYSTEM снимает вопрос с правами на чтение состояния службы и запись в Event Log. Если заодно нужно ограничить доступ к серверу поверх WireGuard, смотрите настройку файрвола для туннеля.

Интеграция с Uptime Kuma

Список «кто подключался» не отвечает на другой вопрос — жив ли вообще туннель и сам скрипт. Здесь пригодится тип монитора Push: Kuma выдаёт URL вида .../api/push/<token>?status=up&msg=OK&ping=, и любая система, умеющая дёргать HTTP, отчитывается «я жива» с заданным интервалом. Не пришёл пуш вовремя — Kuma помечает монитор как down. Добавьте в конец скрипта:

try {
    Invoke-WebRequest -Uri "https://kuma.example.com/api/push/XXXXXXXXXXXXXXXX?status=up&msg=OK" -UseBasicParsing -TimeoutSec 5 | Out-Null
} catch { Write-Line "Heartbeat в Uptime Kuma не отправлен" }

Смысл именно в heartbeat из самого скрипта, а не в проверке WireGuard-порта снаружи: UDP 51820 не отвечает на ping или TCP-проверку осмысленно, а вот «скрипт выполнился и достучался до Kuma» — надёжный косвенный признак, что WireGuard, Event Viewer и Task Scheduler работают штатно. Принцип настройки Push и других мониторов — в статье про Uptime Kuma для мониторинга сайта и сервера. Если туннель используется прежде всего для RDP, ту же связку логично дополнить мониторингом RDP через WireGuard.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать Windows-сервер

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Частые вопросы

Можно обойтись без Event Viewer, только текстовым файлом?

Да, но теряется штатная фильтрация и Event Forwarding. Для одного сервера текстового лога хватит, для парка серверов Event Viewer окупается быстрее.

А если WireGuard на Linux?

Логика та же: wg show <iface> dump идентичен, вместо Event Viewer — logger в syslog, вместо Task Scheduler — cron. Скрипт для Linux не подойдёт, но парсинг дампа переносится один в один.

Не проще ли перейти на OpenVPN, раз там логирование из коробки?

Это компромисс между логированием и простотой WireGuard — сравнение в статье WireGuard или OpenVPN: что выбрать для сервера. Обычно 60 строк PowerShell дешевле, чем миграция инфраструктуры.

Скрипт не видит пиров, хотя wg show их показывает.

Чаще всего дело в правах: задача должна запускаться от SYSTEM или администратора — у обычного пользователя может не быть доступа к каналу службы WireGuard.

Как часто проверять подключения?

Раз в 1-2 минуты нормально. Ниже 30 секунд обычно бессмысленно — handshake пересогласуется не чаще раза в пару минут.

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.