feat: локальные скрипты починки RDP/TLS для Windows (fix-DAK-PC)

Fix-RdpTls.ps1 с фазами диагностики, cert, ACL, привязкой; самотесты; docs repair install и возврат TLS.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
PTah
2026-06-12 19:05:29 +10:00
commit 14348a0181
9 changed files with 1055 additions and 0 deletions
+175
View File
@@ -0,0 +1,175 @@
#Requires -Version 5.1
#Requires -RunAsAdministrator
<#
.SYNOPSIS
Локальная починка RDP TLS / сертификата TermService.
.PARAMETER DiagnoseOnly
Только диагностика и отчёт.
.PARAMETER EmergencyRdpOnly
SecurityLayer=0 (без TLS) для срочного доступа.
.PARAMETER RestoreTls
Вернуть SecurityLayer=2 после успешной привязки cert.
.PARAMETER SkipReboot
Не предлагать reboot при нативном сбросе.
.PARAMETER NonInteractive
Без вопросов (для автоматизации). $env:RDP_REPAIR_NONINTERACTIVE=1
#>
[CmdletBinding()]
param(
[switch]$DiagnoseOnly,
[switch]$EmergencyRdpOnly,
[switch]$RestoreTls,
[switch]$SkipReboot,
[switch]$NonInteractive
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
if ($NonInteractive) { $env:RDP_REPAIR_NONINTERACTIVE = '1' }
$libDir = Join-Path $PSScriptRoot 'lib'
. (Join-Path $libDir 'RdpRepair.Common.ps1')
$logPath = Join-Path (Initialize-RdpRepairDataRoot) ("repair-{0:yyyyMMdd-HHmmss}.log" -f (Get-Date))
function Show-RdpRepairClientCacheHint {
Write-RdpRepairLog 'На клиенте RDP выполните:' 'INFO' $logPath
Write-RdpRepairLog ' Remove-Item "HKCU:\Software\Microsoft\Terminal Server Client\Servers\<имя-сервера>" -Recurse -Force' 'INFO' $logPath
}
try {
Assert-RdpRepairAdministrator
Write-RdpRepairLog "=== Fix-RdpTls start $(Get-Date -Format o) ===" 'STEP' $logPath
Write-RdpRepairLog "Компьютер: $env:COMPUTERNAME" 'INFO' $logPath
$report = Export-RdpRepairDiagnosticReport -LogPath $logPath
Write-RdpRepairLog "Диагностика сохранена: $report" 'OK' $logPath
if ($DiagnoseOnly) {
Get-RdpRepairDiagnosticSnapshot | Format-List
exit 0
}
if ($EmergencyRdpOnly) {
Write-RdpRepairLog 'ЭКСТРЕННЫЙ РЕЖИМ: SecurityLayer=0 (RDP без TLS)' 'WARN' $logPath
Set-RdpRepairSecurityLayer -Layer 0 -LogPath $logPath
Show-RdpRepairClientCacheHint
Write-RdpRepairLog 'Проверьте RDP. Для TLS после repair install: .\Fix-RdpTls.ps1 -RestoreTls' 'OK' $logPath
exit 0
}
if ($RestoreTls) {
Write-RdpRepairLog 'Возврат SecurityLayer=2 (TLS)' 'STEP' $logPath
$certs = Get-RdpRepairRemoteDesktopCertificates
if ($certs.Count -eq 0) {
Write-RdpRepairLog 'No cert in Remote Desktop store - run full repair or repair install first' 'ERROR' $logPath
exit 2
}
$valid = $certs | Where-Object { $_.NotAfter -gt (Get-Date) } | Select-Object -First 1
if (-not $valid) {
Write-RdpRepairLog 'Все cert просрочены' 'ERROR' $logPath
exit 2
}
$thumb = $valid.Thumbprint
if (-not (Get-RdpRepairSslCertificateHash)) {
Write-RdpRepairLog "Привязка $($valid.Thumbprint)..." 'INFO' $logPath
Set-RdpRepairCertificateBinding -Thumbprint $thumb -LogPath $logPath | Out-Null
}
$bind = Test-RdpRepairCertificateBindingPersisted -ExpectedThumbprint $thumb -LogPath $logPath
if (-not $bind.Persisted) {
Write-RdpRepairLog 'TermService does not keep hash - cannot restore TLS. See docs\REPAIR-INSTALL.md' 'ERROR' $logPath
exit 3
}
Set-ItemProperty -Path $script:RdpRepairRdpTcpKey -Name SecurityLayer -Value 2
Restart-RdpRepairTermService
Write-RdpRepairLog 'SecurityLayer=2, TermService перезапущен' 'OK' $logPath
Show-RdpRepairClientCacheHint
exit 0
}
# --- Полный интерактивный ремонт ---
Write-RdpRepairLog 'ФАЗА 1: текущее состояние' 'STEP' $logPath
$snap = Get-RdpRepairDiagnosticSnapshot
$snap | Format-List ComputerName, OsCaption, SecurityLayer, SslHash, CertCount, Events1057_24h, GpoLicenseSrv
$expired = @($snap.Certificates | Where-Object { $_.NotAfter -lt (Get-Date) })
if ($expired.Count -gt 0) {
Write-RdpRepairLog "Найдено просроченных cert: $($expired.Count)" 'WARN' $logPath
}
if (-not (Confirm-RdpRepairContinue 'Продолжить: очистка cert и нативный сброс?')) {
exit 0
}
Write-RdpRepairLog 'ФАЗА 2: очистка stores + нативный сброс' 'STEP' $logPath
$native = Invoke-RdpRepairNativeReset -LogPath $logPath
if ($native.CertCount -gt 0) {
Write-RdpRepairLog "После сброса без reboot: нативный cert найден ($($native.CertCount))" 'OK' $logPath
$nativeCert = $native.Certificates | Select-Object -First 1
$bindNative = Test-RdpRepairCertificateBindingPersisted -ExpectedThumbprint $nativeCert.Thumbprint -LogPath $logPath
if ($bindNative.Persisted) {
Write-RdpRepairLog 'Native cert accepted - you can use RestoreTls' 'OK' $logPath
if (Confirm-RdpRepairContinue 'Установить SecurityLayer=2 сейчас?') {
Set-ItemProperty -Path $script:RdpRepairRdpTcpKey -Name SecurityLayer -Value 2
Restart-RdpRepairTermService
}
Show-RdpRepairClientCacheHint
exit 0
}
}
if (-not $SkipReboot -and (Confirm-RdpRepairContinue 'Нативный cert не прижился. Запланировать REBOOT через 60 с?')) {
Invoke-RdpRepairNativeReset -LogPath $logPath -Reboot | Out-Null
Write-RdpRepairLog 'После загрузки снова запустите: .\Fix-RdpTls.ps1' 'WARN' $logPath
exit 0
}
if (-not (Confirm-RdpRepairContinue 'ФАЗА 3: создать локальный cert и привязать?')) {
exit 0
}
Write-RdpRepairLog 'ФАЗА 3: создание cert' 'STEP' $logPath
Clear-RdpRepairCertificateStores -LogPath $logPath
$dns = @(Get-RdpRepairComputerDnsNames)
Write-RdpRepairLog "DNS: $($dns -join ', ')" 'INFO' $logPath
$cert = New-RdpRepairSelfSignedCertificate -DnsNames $dns -LogPath $logPath
Write-RdpRepairLog 'ФАЗА 4: ACL + Remote Desktop store' 'STEP' $logPath
Set-RdpRepairPrivateKeyAcl -Certificate $cert -LogPath $logPath | Out-Null
Add-RdpRepairCertificateToRemoteDesktopStore -Certificate $cert
Write-RdpRepairLog 'ФАЗА 5: привязка и проверка AFTER' 'STEP' $logPath
Set-RdpRepairCertificateBinding -Thumbprint $cert.Thumbprint -LogPath $logPath | Out-Null
$bind = Test-RdpRepairCertificateBindingPersisted -ExpectedThumbprint $cert.Thumbprint -LogPath $logPath
if ($bind.Persisted) {
if (Confirm-RdpRepairContinue 'Привязка OK. Установить SecurityLayer=2?') {
Set-ItemProperty -Path $script:RdpRepairRdpTcpKey -Name SecurityLayer -Value 2
Restart-RdpRepairTermService
}
Show-RdpRepairClientCacheHint
Write-RdpRepairLog '=== Ремонт TLS завершён успешно ===' 'OK' $logPath
exit 0
}
Write-RdpRepairLog '=== TermService отклоняет cert ===' 'ERROR' $logPath
Write-RdpRepairLog 'Варианты:' 'WARN' $logPath
Write-RdpRepairLog ' 1) docs\REPAIR-INSTALL.md - repair install Windows' 'WARN' $logPath
Write-RdpRepairLog ' 2) .\Fix-RdpTls.ps1 -EmergencyRdpOnly - RDP without TLS' 'WARN' $logPath
if (Confirm-RdpRepairContinue 'Включить Emergency SecurityLayer=0 сейчас?') {
Set-RdpRepairSecurityLayer -Layer 0 -LogPath $logPath
Show-RdpRepairClientCacheHint
}
exit 4
} catch {
Write-RdpRepairLog "FATAL: $($_.Exception.Message)" 'ERROR' $logPath
Write-RdpRepairLog $_.ScriptStackTrace 'ERROR' $logPath
exit 1
}