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:
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user