From 859c8cdba92c6f9d8185fbc13ed1226cba5243e7 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 9 Jul 2026 08:45:38 +0000 Subject: [PATCH] =?UTF-8?q?Fix:=20=D0=B2=D1=8B=D0=B4=D0=B0=D0=B2=D0=B0?= =?UTF-8?q?=D1=82=D1=8C=20TSGateway=20=D0=BF=D1=80=D0=B0=D0=B2=D0=B0=20?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=B7=D0=B0=D0=BA=D1=80=D1=8B=D1=82=D1=8B=D0=B9?= =?UTF-8?q?=20=D0=BA=D0=BB=D1=8E=D1=87=20=D0=BF=D0=B5=D1=80=D0=B5=D0=B4=20?= =?UTF-8?q?=D0=BF=D1=80=D0=B8=D0=B2=D1=8F=D0=B7=D0=BA=D0=BE=D0=B9=20=D0=BA?= =?UTF-8?q?=20RD=20Gateway?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Set-RDGWCertificate импортирует pfx через Import-PfxCertificate, который не выдаёт NETWORK SERVICE (учётная запись службы TSGateway) доступ к закрытому ключу — из-за этого сертификат либо не применяется, либо привязка вручную падает с "Отказано в доступе". Также добавлена явная -ErrorAction Stop и проверка применённого отпечатка: Set-RDGWCertificate гасит свои ошибки через trap {...; return} и превращает их в non-terminating Write-Error, которая раньше проходила мимо try/catch скрипта без единого сообщения. --- windows/Update-RDGWCertificate.ps1 | 49 +++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/windows/Update-RDGWCertificate.ps1 b/windows/Update-RDGWCertificate.ps1 index 80c37b9..c782f26 100644 --- a/windows/Update-RDGWCertificate.ps1 +++ b/windows/Update-RDGWCertificate.ps1 @@ -33,6 +33,37 @@ function Write-ToEventLog { Write-Host "[$Type] $Message" } +function Grant-RDGWKeyAccess { + # Import-PfxCertificate (его вызывает Set-RDGWCertificate) не выдаёт + # NETWORK SERVICE доступ к закрытому ключу — из-за этого служба TSGateway + # не может прочитать ключ нового сертификата и молча (или с "Отказано + # в доступе" при ручной привязке) не применяет его. + param( + [Parameter(Mandatory)][System.Security.Cryptography.X509Certificates.X509Certificate2]$Certificate, + [string]$Account = 'NT AUTHORITY\NETWORK SERVICE' + ) + + $rsaKey = [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($Certificate) + if (-not $rsaKey) { + throw "У сертификата $($Certificate.Thumbprint) не найден закрытый ключ в LocalMachine\My." + } + + if ($rsaKey -is [System.Security.Cryptography.RSACng]) { + $keyPath = Join-Path $env:ProgramData "Microsoft\Crypto\Keys\$($rsaKey.Key.UniqueName)" + } + else { + $keyPath = Join-Path $env:ProgramData "Microsoft\Crypto\RSA\MachineKeys\$($rsaKey.CspKeyContainerInfo.UniqueKeyContainerName)" + } + if (-not (Test-Path $keyPath)) { + throw "Файл закрытого ключа не найден: $keyPath" + } + + $acl = Get-Acl -Path $keyPath + $rule = New-Object System.Security.AccessControl.FileSystemAccessRule($Account, 'Read', 'Allow') + $acl.AddAccessRule($rule) + Set-Acl -Path $keyPath -AclObject $acl +} + try { # --- Рабочий каталог --- if (-not (Test-Path $poshAcmeHome)) { @@ -101,9 +132,25 @@ try { Write-ToEventLog "Сертификат успешно продлён." } + # --- Импорт сертификата и права на закрытый ключ --- + $x509 = Get-Item "Cert:\LocalMachine\My\$($cert.Thumbprint)" -ErrorAction SilentlyContinue + if (-not $x509) { + Write-ToEventLog "Импортируем сертификат в LocalMachine\My" + $x509 = Import-PfxCertificate -FilePath $cert.PfxFile -CertStoreLocation Cert:\LocalMachine\My -Password $cert.PfxPass + } + Grant-RDGWKeyAccess -Certificate $x509 + # --- Установка на RD Gateway --- # Set-RDGWCertificate идемпотентен: если отпечаток не изменился — ничего не делает. - $cert | Set-RDGWCertificate -RemoveOldCert + # -ErrorAction Stop обязателен: функция сама перехватывает свои ошибки через + # trap {...; return} и превращает их в non-terminating Write-Error, которая + # без явного -ErrorAction Stop проходит мимо try/catch этого скрипта. + $cert | Set-RDGWCertificate -RemoveOldCert -ErrorAction Stop + + $appliedThumb = (Get-Item RDS:\GatewayServer\SSLCertificate\Thumbprint).CurrentValue + if ($appliedThumb -ne $cert.Thumbprint) { + throw "Set-RDGWCertificate завершился без ошибки, но на шлюзе установлен отпечаток $appliedThumb вместо ожидаемого $($cert.Thumbprint)." + } Write-ToEventLog "Сертификат установлен на RD Gateway. Отпечаток: $($cert.Thumbprint). Действителен до: $($cert.NotAfter)." } catch {