Add firewall script for managing inbound rules; support for domain profiles and interface aliases
This commit is contained in:
+37
@@ -36,6 +36,43 @@ powershell -NoProfile -ExecutionPolicy Bypass -File "$env:LOCALAPPDATA\Programs\
|
||||
Правила брандмауэра установщик не меняет. Для подключения из домашней сети
|
||||
потребуется разрешённый доступ к TCP-порту агента и UDP 5353 в частной сети.
|
||||
|
||||
## Брандмауэр
|
||||
|
||||
Установленный `Firewall.ps1` создаёт два входящих правила для конкретного exe:
|
||||
TCP 8765 (HTTPS/WSS) и UDP 5353 (mDNS). Только профиль Private и адреса
|
||||
LocalSubnet; Edge Traversal запрещён. Профили сети и чужие правила не меняются.
|
||||
По умолчанию в доменной или публичной сети эти правила не разрешат подключение.
|
||||
Если домашний Ethernet имеет доменный профиль, можно явно передать
|
||||
`-Profile Domain -InterfaceAlias Ethernet`. Для Domain обязательно точное имя
|
||||
интерфейса; маски и `Any` запрещены. Адреса остаются ограничены LocalSubnet.
|
||||
Профиль Public не поддерживается. Повторное применение заменяет профиль и
|
||||
интерфейс собственных правил для этого exe, а не добавляет ещё одну пару правил.
|
||||
|
||||
Просмотр без изменений:
|
||||
|
||||
```powershell
|
||||
& "$env:LOCALAPPDATA\Programs\MusicBridge\Firewall.ps1" -Action Preview
|
||||
```
|
||||
|
||||
Применение из PowerShell администратора:
|
||||
|
||||
```powershell
|
||||
& "$env:LOCALAPPDATA\Programs\MusicBridge\Firewall.ps1" -Action Apply
|
||||
```
|
||||
|
||||
Для нестандартной установки указать абсолютный `-ProgramPath` к exe;
|
||||
для другого порта — `-Port`. `-Action Apply -WhatIf` показывает план без изменений.
|
||||
Повторное применение обновляет собственные правила этого пути, включая порт.
|
||||
Скрипт не сужает доступ, который уже разрешён другими правилами Windows.
|
||||
Перед удалением приложения можно удалить его правила из PowerShell администратора:
|
||||
|
||||
```powershell
|
||||
& "$env:LOCALAPPDATA\Programs\MusicBridge\Firewall.ps1" -Action Remove
|
||||
```
|
||||
|
||||
Удаление правил выполняется отдельно: установщик и деинсталлятор не запрашивают
|
||||
административные права. Для нестандартной установки передать тот же `-ProgramPath`.
|
||||
|
||||
## Для разработчика
|
||||
|
||||
Собрать пакет: `powershell -NoProfile -File Packaging/Publish.ps1`.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# MusicBridge — план проекта
|
||||
|
||||
Обновлено: 2026-09-09. Репозиторий находится в `MusicBridge.Agent/`.
|
||||
Обновлено: 2026-09-10. Репозиторий находится в `MusicBridge.Agent/`.
|
||||
|
||||
## Цель
|
||||
|
||||
@@ -86,6 +86,9 @@ Windows Core Audio → системная громкость устройств
|
||||
- [x] Автозапуск текущего пользователя через меню трея; скрытый старт `--minimized`.
|
||||
- [x] Самостоятельный Windows x64 пакет со встроенными Runtime, ZIP-установка,
|
||||
ярлык в меню Пуск и скрипт удаления. Это не MSI и не подписанный установщик.
|
||||
- [x] Скрипт брандмауэра: просмотр, применение и удаление собственных правил;
|
||||
только конкретный exe, Private + LocalSubnet, TCP порта агента и UDP 5353.
|
||||
- [x] Восстановление опроса после ошибок чтения; освобождение WSS после обрывов.
|
||||
|
||||
### 4. Нативное приложение iPhone — отложено пользователем
|
||||
|
||||
@@ -112,8 +115,40 @@ Windows Core Audio → системная громкость устройств
|
||||
|
||||
## Проверки и следующий шаг
|
||||
|
||||
2026-09-10:
|
||||
|
||||
- Пользователь подтвердил: Ethernet подключён к домашней сети. В `Firewall.ps1`
|
||||
добавлены `-Profile Domain -InterfaceAlias Ethernet`; по умолчанию Private.
|
||||
Domain требует точный интерфейс, Public и маски запрещены. LocalSubnet,
|
||||
привязка к exe и порты сохранены. Preview/WhatIf и проверки ограничений прошли.
|
||||
- После повторного запроса пользователя правила применены через Windows UAC.
|
||||
В ActiveStore проверены оба правила: включены, Inbound Allow, Domain,
|
||||
интерфейс Ethernet, LocalSubnet, EdgeTraversal Block, TCP 8765 и UDP 5353.
|
||||
Привязаны к `bin/Release/net10.0-windows10.0.19041.0/MusicBridge.Agent.exe`;
|
||||
имена `MusicBridge-0F2EC7194BF18EBC-HTTPS` и `MusicBridge-0F2EC7194BF18EBC-mDNS`.
|
||||
Агент во время проверки не запущен. При установке в другой каталог нужны
|
||||
правила для нового пути. Подключение со второго устройства ещё не проверено.
|
||||
|
||||
2026-09-09:
|
||||
|
||||
- Последний этап — устойчивость: **112 проверок прошли**, сборка без ошибок и
|
||||
предупреждений. Конфигурация `Resilience`, параметры `--desktop-smoke
|
||||
--qr-window-smoke --mdns-smoke --live-read`. Работающий Release-агент не остановлен.
|
||||
Проверены ошибки первого и последующих чтений, восстановление состояния,
|
||||
очистка устаревших данных при ошибке и 12 последовательных обрывов WSS.
|
||||
Чтение получает отмену через 5 секунд; отправка WSS ограничена 5 секундами,
|
||||
keepalive — 20 секунд с ожиданием ответа 10 секунд. После длинного перерыва
|
||||
mDNS пересоздаёт объявления. Реальный сон/пробуждение ещё не проверен.
|
||||
- `Packaging/Test-Firewall.ps1` проверил Preview и Apply -WhatIf без изменения
|
||||
системы. Правила ещё не применены: Ethernet имеет профиль DomainAuthenticated,
|
||||
Tailscale — Private, tun2 — Public. Правила Private не откроют доступ через
|
||||
Ethernet. Категории сети не менять автоматически; доступ для Domain требует
|
||||
отдельного решения пользователя.
|
||||
- ZIP пересобран с изменениями устойчивости и `Firewall.ps1`. Повторная проверка
|
||||
установки, запуска со встроенным Runtime, обновления, защиты занятого exe и
|
||||
удаления прошла. Проверка правил в режиме WhatIf также прошла; реальных
|
||||
изменений брандмауэра и установки в рабочую папку пользователя не выполнялось.
|
||||
|
||||
- Последний этап — автозапуск и упаковка: **107 проверок прошло** с
|
||||
`-c Packaging --no-build -- --desktop-smoke --qr-window-smoke --mdns-smoke --live-read`.
|
||||
Проверены запись/удаление тестовой записи автозапуска и скрытый старт окна.
|
||||
@@ -194,7 +229,8 @@ Windows Core Audio → системная громкость устройств
|
||||
объявляется на домашних IPv4-адаптерах с multicast. Автоизменения брандмауэра нет.
|
||||
|
||||
Следующий шаг: проверка с другого устройства и устойчивости при смене сети,
|
||||
проверка входа Windows с автозапуском и подготовка правил частного брандмауэра.
|
||||
проверка входа Windows с автозапуском. Правила для домашнего Ethernet применены
|
||||
и проверены для текущего пути Release-сборки (см. запись от 2026-09-10).
|
||||
Трей, автозапуск и ZIP-упаковка реализованы. iPhone-клиент пока не начинать.
|
||||
Точные границы реализации и следующие шаги — в `CONNECTION-ROADMAP.md`.
|
||||
Начать разработку iOS только после новой команды пользователя.
|
||||
@@ -221,6 +257,9 @@ Windows Core Audio → системная громкость устройств
|
||||
- `Desktop/DesktopHost.cs`: STA-поток, значок/меню трея и жизненный цикл окна.
|
||||
- `Desktop/StartupSettings.cs`: HKCU Run, экранированная команда автозапуска.
|
||||
- `Packaging/`: сборка standalone ZIP, установка/удаление и изолированный smoke-тест.
|
||||
- `Packaging/Firewall.ps1`: Preview/Apply/Remove правил LocalSubnet; Private по
|
||||
умолчанию, Domain только с явно указанным интерфейсом домашней сети.
|
||||
- `Packaging/Test-Firewall.ps1`: проверка определений правил без изменения системы.
|
||||
- `INSTALL.md`: установка, автозапуск, обновление и удаление.
|
||||
- `Network/LocalAddresses.cs`: подходящие домашние IPv4-адреса.
|
||||
- `Network/DiscoveryDescriptor.cs`: публичные поля DNS-SD.
|
||||
|
||||
@@ -235,6 +235,11 @@ Windows-окно с QR; `B` создаёт новый код. При `--lan` а
|
||||
Поддерживается до 8 одновременных WebSocket. После потери соединения клиент
|
||||
переподключается с задержкой и получает полный снимок. Медленный клиент получает
|
||||
последнее состояние; промежуточные снимки не накапливаются в неограниченной очереди.
|
||||
Отправка снимка ограничена 5 секундами. Keepalive отправляется через 20 секунд,
|
||||
ожидание ответа ограничено 10 секундами; оборванные соединения освобождают слот.
|
||||
При ошибке чтения медиасессии сервер публикует пустое состояние с `mediaError`
|
||||
и продолжает опрос. После восстановления очередной снимок содержит свежие данные.
|
||||
На чтение передаётся отмена через 5 секунд.
|
||||
Запросы с Origin
|
||||
отклоняются: браузерные клиенты в проекте не поддерживаются.
|
||||
|
||||
|
||||
+13
-4
@@ -2,9 +2,17 @@
|
||||
param(
|
||||
[ValidateSet('Preview','Apply','Remove')][string]$Action = 'Preview',
|
||||
[string]$ProgramPath = (Join-Path $env:LOCALAPPDATA 'Programs\MusicBridge\MusicBridge.Agent.exe'),
|
||||
[ValidateRange(1,65535)][int]$Port = 8765
|
||||
[ValidateRange(1,65535)][int]$Port = 8765,
|
||||
[ValidateSet('Private','Domain')][string]$Profile = 'Private',
|
||||
[string]$InterfaceAlias
|
||||
)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
if ($Profile -eq 'Domain' -and [string]::IsNullOrWhiteSpace($InterfaceAlias)) {
|
||||
throw 'Domain access requires an explicit home network InterfaceAlias.'
|
||||
}
|
||||
if ($InterfaceAlias -and ([string]::IsNullOrWhiteSpace($InterfaceAlias) -or $InterfaceAlias -eq 'Any' -or [Management.Automation.WildcardPattern]::ContainsWildcardCharacters($InterfaceAlias))) {
|
||||
throw 'Select one exact network interface, without wildcards.'
|
||||
}
|
||||
if (![IO.Path]::IsPathRooted($ProgramPath)) { throw 'ProgramPath must be absolute.' }
|
||||
$exe = [IO.Path]::GetFullPath($ProgramPath)
|
||||
if ([IO.Path]::GetFileName($exe) -ne 'MusicBridge.Agent.exe') { throw 'Select MusicBridge.Agent.exe.' }
|
||||
@@ -13,12 +21,13 @@ $sha = [Security.Cryptography.SHA256]::Create()
|
||||
try { $id = ([BitConverter]::ToString($sha.ComputeHash([Text.Encoding]::UTF8.GetBytes($exe.ToUpperInvariant())))).Replace('-','').Substring(0,16) }
|
||||
finally { $sha.Dispose() }
|
||||
$definitions = @(
|
||||
@{Name="MusicBridge-$id-HTTPS"; DisplayName='MusicBridge HTTPS (private LAN)'; Protocol='TCP'; LocalPort=$Port},
|
||||
@{Name="MusicBridge-$id-mDNS"; DisplayName='MusicBridge mDNS (private LAN)'; Protocol='UDP'; LocalPort=5353}
|
||||
@{Name="MusicBridge-$id-HTTPS"; DisplayName='MusicBridge HTTPS (local subnet)'; Protocol='TCP'; LocalPort=$Port},
|
||||
@{Name="MusicBridge-$id-mDNS"; DisplayName='MusicBridge mDNS (local subnet)'; Protocol='UDP'; LocalPort=5353}
|
||||
)
|
||||
foreach ($definition in $definitions) {
|
||||
$definition.Program = $exe
|
||||
$definition.Profile = 'Private'
|
||||
$definition.Profile = $Profile
|
||||
$definition.InterfaceAlias = if ($InterfaceAlias) { $InterfaceAlias } else { 'Any' }
|
||||
$definition.Direction = 'Inbound'
|
||||
$definition.Action = 'Allow'
|
||||
$definition.RemoteAddress = 'LocalSubnet'
|
||||
|
||||
@@ -13,4 +13,16 @@ if (($rules | Where-Object Protocol -eq UDP).LocalPort -ne 5353) { throw 'mDNS p
|
||||
$again = @(& $script -Action Preview -ProgramPath $exe -Port 8765)
|
||||
if (($rules.Name -join ',') -ne ($again.Name -join ',')) { throw 'Changing port would leave old rule names behind.' }
|
||||
& $script -Action Apply -ProgramPath $exe -WhatIf
|
||||
$domain = @(& $script -Action Preview -ProgramPath $exe -Profile Domain -InterfaceAlias Ethernet)
|
||||
foreach ($rule in $domain) {
|
||||
if ($rule.Profile -ne 'Domain' -or $rule.InterfaceAlias -ne 'Ethernet' -or $rule.RemoteAddress -ne 'LocalSubnet') { throw 'Domain scope mismatch.' }
|
||||
}
|
||||
if (($domain.Name -join ',') -ne ($rules.Name -join ',')) { throw 'Changing profile would leave old rules behind.' }
|
||||
foreach ($badAlias in @('', 'Any', 'Ether*', 'Ether?et')) {
|
||||
$rejected = $false
|
||||
try { & $script -Action Preview -ProgramPath $exe -Profile Domain -InterfaceAlias $badAlias | Out-Null }
|
||||
catch { $rejected = $true }
|
||||
if (!$rejected) { throw 'Domain scope accepted an unrestricted interface.' }
|
||||
}
|
||||
& $script -Action Apply -ProgramPath $exe -Profile Domain -InterfaceAlias Ethernet -WhatIf
|
||||
Write-Host 'PASS: private profile, local subnet, executable binding, ports, stable names and dry-run. No firewall changes.'
|
||||
|
||||
Reference in New Issue
Block a user