5761710fa1
Добавлены Get-RdpRepairCertificateArray/FirstCertificate, самотесты на вложенные массивы; Fix-RdpTls больше не использует Select-Object -First 1 на cert. Co-authored-by: Cursor <cursoragent@cursor.com>
177 lines
7.9 KiB
PowerShell
177 lines
7.9 KiB
PowerShell
#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 = @(Get-RdpRepairCertificateArray $certs | Where-Object { $_.NotAfter -gt (Get-Date) })[0]
|
||
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 = @(Get-RdpRepairCertificateArray $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 = Get-RdpRepairFirstCertificate -Certificates $native.Certificates
|
||
if (-not $nativeCert) { throw 'Native cert list empty after reset' }
|
||
$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
|
||
}
|