Improve SGU logon resilience and client UX
This commit is contained in:
@@ -22,6 +22,9 @@ param(
|
||||
[string]$BaseDn = 'DC=lci,DC=lasalle,DC=mx',
|
||||
[string]$DomainNetbios = 'LCI',
|
||||
[string]$UpnSuffix = 'lci.lasalle.mx',
|
||||
[string]$RemoteDesktopGroupDn = '',
|
||||
[ValidateRange(10, 60)]
|
||||
[int]$NtlmTimeoutSeconds = 15,
|
||||
[switch]$CreateMissingOus,
|
||||
[switch]$DisableCertificateRevocationCheckForLab
|
||||
)
|
||||
@@ -96,6 +99,15 @@ if ($CreateMissingOus) {
|
||||
}
|
||||
}
|
||||
|
||||
if ($RemoteDesktopGroupDn) {
|
||||
Import-Module ActiveDirectory -ErrorAction Stop
|
||||
$remoteDesktopGroup = Get-ADGroup -Identity $RemoteDesktopGroupDn -Server $LdapHost -ErrorAction Stop
|
||||
if ($remoteDesktopGroup.GroupCategory -ne 'Security' -or
|
||||
-not $remoteDesktopGroup.DistinguishedName.EndsWith(",$BaseDn", [StringComparison]::OrdinalIgnoreCase)) {
|
||||
throw 'RemoteDesktopGroupDn must identify a security group beneath BaseDn.'
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($file in @('SGU.AuthBroker.exe', 'SGU.AuthBroker.dll', 'appsettings.json')) {
|
||||
if (-not (Test-Path -LiteralPath (Join-Path $PublishPath $file))) {
|
||||
throw "PublishPath is missing $file."
|
||||
@@ -124,7 +136,7 @@ $productionSettings = @{
|
||||
Ntlm = @{
|
||||
Endpoint = $NtlmEndpoint
|
||||
Domain = ''
|
||||
TimeoutSeconds = 15
|
||||
TimeoutSeconds = $NtlmTimeoutSeconds
|
||||
MaxRedirects = 5
|
||||
AdministrativeProfilePath = $AdministrativeProfilePath
|
||||
MenuProfilePath = $MenuProfilePath
|
||||
@@ -139,6 +151,7 @@ $productionSettings = @{
|
||||
ProfessorOuDn = "OU=Docentes,OU=Usuarios-SGU,$BaseDn"
|
||||
StudentOuDn = "OU=Alumnos,OU=Usuarios-SGU,$BaseDn"
|
||||
AdministrativeOuDn = "OU=Administrativos,OU=Usuarios-SGU,$BaseDn"
|
||||
RemoteDesktopGroupDn = $RemoteDesktopGroupDn
|
||||
CreateMissingOus = [bool]$CreateMissingOus
|
||||
}
|
||||
}
|
||||
@@ -147,6 +160,13 @@ $productionSettings = @{
|
||||
if ($PSCmdlet.ShouldProcess($installPath, 'Install the SGU Authentication Broker Windows service')) {
|
||||
if (Get-Service -Name $serviceName -ErrorAction SilentlyContinue) {
|
||||
Stop-Service -Name $serviceName -Force
|
||||
(Get-Service -Name $serviceName).WaitForStatus(
|
||||
[System.ServiceProcess.ServiceControllerStatus]::Stopped,
|
||||
[TimeSpan]::FromSeconds(15))
|
||||
|
||||
# A self-contained .NET process can briefly retain mapped runtime files
|
||||
# after SCM reports Stopped. Give Windows time to release those handles.
|
||||
Start-Sleep -Seconds 2
|
||||
}
|
||||
|
||||
New-Item -ItemType Directory -Path $installPath -Force | Out-Null
|
||||
@@ -165,6 +185,18 @@ if ($PSCmdlet.ShouldProcess($installPath, 'Install the SGU Authentication Broker
|
||||
-BinaryPathName ('"{0}"' -f (Join-Path $installPath 'SGU.AuthBroker.exe')) `
|
||||
-StartupType Automatic
|
||||
}
|
||||
else {
|
||||
Set-Service -Name $serviceName -StartupType Automatic
|
||||
}
|
||||
|
||||
& sc.exe failure $serviceName 'reset=' '86400' 'actions=' 'restart/5000/restart/15000/restart/60000' | Out-Null
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw 'Could not configure automatic recovery for SGUAuthBroker.'
|
||||
}
|
||||
& sc.exe failureflag $serviceName '1' | Out-Null
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw 'Could not enable recovery for non-crash SGUAuthBroker failures.'
|
||||
}
|
||||
|
||||
if (-not (Get-NetFirewallRule -DisplayName 'SGU Authentication Broker (mTLS)' -ErrorAction SilentlyContinue)) {
|
||||
New-NetFirewallRule -DisplayName 'SGU Authentication Broker (mTLS)' `
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
[CmdletBinding(SupportsShouldProcess)]
|
||||
param(
|
||||
[string]$RemoteDesktopPrincipal = 'LCI\SG-Laboratorio-Usuarios-RDP',
|
||||
[switch]$EnableAdministrativeFirewallGroups
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
|
||||
$principal = [Security.Principal.WindowsPrincipal]::new($identity)
|
||||
if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
|
||||
throw 'Run this script from an elevated Windows PowerShell session.'
|
||||
}
|
||||
|
||||
$computer = Get-CimInstance Win32_ComputerSystem
|
||||
if (-not $computer.PartOfDomain) {
|
||||
throw 'Join the computer to the domain before enabling domain-scoped remote access.'
|
||||
}
|
||||
|
||||
$remoteDesktopUsersSid = [Security.Principal.SecurityIdentifier]::new('S-1-5-32-555')
|
||||
$remoteDesktopUsersGroup = ($remoteDesktopUsersSid.Translate([Security.Principal.NTAccount]).Value -split '\\', 2)[1]
|
||||
|
||||
if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, "Enable RDP and grant $RemoteDesktopPrincipal access")) {
|
||||
Set-ItemProperty -LiteralPath 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server' `
|
||||
-Name fDenyTSConnections -Type DWord -Value 0
|
||||
Set-ItemProperty -LiteralPath 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp' `
|
||||
-Name UserAuthentication -Type DWord -Value 1
|
||||
|
||||
Set-Service -Name TermService -StartupType Automatic
|
||||
Start-Service -Name TermService
|
||||
|
||||
Get-NetFirewallRule -Name 'RemoteDesktop-UserMode-In-TCP','RemoteDesktop-UserMode-In-UDP' `
|
||||
-ErrorAction SilentlyContinue |
|
||||
Set-NetFirewallRule -Enabled True -Profile Domain
|
||||
|
||||
$existingMembers = @(Get-LocalGroupMember -Group $remoteDesktopUsersGroup -ErrorAction SilentlyContinue)
|
||||
if ($existingMembers.Name -notcontains $RemoteDesktopPrincipal) {
|
||||
Add-LocalGroupMember -Group $remoteDesktopUsersGroup -Member $RemoteDesktopPrincipal
|
||||
}
|
||||
|
||||
# Use Windows PowerShell so both the inbox and compatible remoting endpoints
|
||||
# are configured even when this helper is launched from PowerShell 7.
|
||||
$enableRemoting = Start-Process -FilePath "$env:SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe" `
|
||||
-ArgumentList @('-NoLogo', '-NoProfile', '-NonInteractive', '-Command',
|
||||
'Enable-PSRemoting -Force -SkipNetworkProfileCheck') `
|
||||
-Wait -PassThru -WindowStyle Hidden
|
||||
if ($enableRemoting.ExitCode -ne 0) {
|
||||
throw "Enable-PSRemoting returned $($enableRemoting.ExitCode)."
|
||||
}
|
||||
|
||||
Set-Service -Name WinRM -StartupType Automatic
|
||||
Start-Service -Name WinRM
|
||||
Get-NetFirewallRule -Name 'WINRM-HTTP-In-TCP','WINRM-HTTP-In-TCP-NoScope' `
|
||||
-ErrorAction SilentlyContinue |
|
||||
Set-NetFirewallRule -Enabled True -Profile Domain
|
||||
Get-NetFirewallRule -Name 'WINRM-HTTP-In-TCP-PUBLIC' -ErrorAction SilentlyContinue |
|
||||
Disable-NetFirewallRule
|
||||
|
||||
if ($EnableAdministrativeFirewallGroups) {
|
||||
$administrativeRules = @(
|
||||
'RemoteEventLogSvc-In-TCP',
|
||||
'RemoteEventLogSvc-NP-In-TCP',
|
||||
'RemoteEventLogSvc-RPCSS-In-TCP',
|
||||
'RemoteSvcAdmin-In-TCP',
|
||||
'RemoteSvcAdmin-NP-In-TCP',
|
||||
'RemoteSvcAdmin-RPCSS-In-TCP',
|
||||
'WMI-RPCSS-In-TCP',
|
||||
'WMI-WINMGMT-In-TCP',
|
||||
'WMI-ASYNC-In-TCP'
|
||||
)
|
||||
Get-NetFirewallRule -Name $administrativeRules -ErrorAction SilentlyContinue |
|
||||
Set-NetFirewallRule -Enabled True -Profile Domain
|
||||
}
|
||||
}
|
||||
|
||||
$rdpMembers = @(Get-LocalGroupMember -Group $remoteDesktopUsersGroup -ErrorAction SilentlyContinue)
|
||||
[pscustomobject]@{
|
||||
ComputerName = $env:COMPUTERNAME
|
||||
Domain = $computer.Domain
|
||||
RemoteDesktopEnabled = (Get-ItemPropertyValue `
|
||||
'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server' `
|
||||
-Name fDenyTSConnections) -eq 0
|
||||
NetworkLevelAuthentication = (Get-ItemPropertyValue `
|
||||
'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp' `
|
||||
-Name UserAuthentication) -eq 1
|
||||
RemoteDesktopPrincipal = $RemoteDesktopPrincipal
|
||||
PrincipalIsAuthorized = $rdpMembers.Name -contains $RemoteDesktopPrincipal
|
||||
TermService = (Get-Service TermService).Status
|
||||
WinRM = (Get-Service WinRM).Status
|
||||
FirewallProfile = 'Domain'
|
||||
}
|
||||
@@ -17,8 +17,8 @@ param(
|
||||
|
||||
[string]$DomainNetbios = 'LCI',
|
||||
|
||||
[ValidateRange(2, 30)]
|
||||
[int]$TimeoutSeconds = 6,
|
||||
[ValidateRange(2, 60)]
|
||||
[int]$TimeoutSeconds = 20,
|
||||
|
||||
[switch]$InstallDotNetRuntime,
|
||||
|
||||
@@ -27,7 +27,7 @@ param(
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$providerClassId = '{D789CFD8-5AD4-489F-9B83-7EB5D9D09335}'
|
||||
$installPath = Join-Path $env:ProgramFiles 'SGU\CredentialProvider'
|
||||
$installRoot = Join-Path $env:ProgramFiles 'SGU\CredentialProvider'
|
||||
$settingsPath = Join-Path $env:ProgramData 'SGU\CredentialProvider\settings.json'
|
||||
$providerRegistryPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\$providerClassId"
|
||||
$classRegistryPath = "HKLM:\SOFTWARE\Classes\CLSID\$providerClassId\InprocServer32"
|
||||
@@ -100,6 +100,30 @@ foreach ($file in $requiredFiles) {
|
||||
}
|
||||
}
|
||||
|
||||
$resolvedPublishPath = (Resolve-Path -LiteralPath $PublishPath).Path.TrimEnd('\')
|
||||
$packageManifest = Get-ChildItem -LiteralPath $resolvedPublishPath -Recurse -File |
|
||||
Sort-Object FullName |
|
||||
ForEach-Object {
|
||||
$relativePath = $_.FullName.Substring($resolvedPublishPath.Length).TrimStart('\')
|
||||
'{0}={1}' -f $relativePath, (Get-FileHash -LiteralPath $_.FullName -Algorithm SHA256).Hash
|
||||
}
|
||||
$manifestBytes = [Text.Encoding]::UTF8.GetBytes(($packageManifest -join "`n"))
|
||||
$sha256 = [Security.Cryptography.SHA256]::Create()
|
||||
try {
|
||||
$packageHash = -join ($sha256.ComputeHash($manifestBytes) | ForEach-Object { $_.ToString('x2') })
|
||||
}
|
||||
finally {
|
||||
$sha256.Dispose()
|
||||
}
|
||||
|
||||
$versionId = $packageHash.Substring(0, 16)
|
||||
$installPath = Join-Path $installRoot "versions\$versionId"
|
||||
$completeMarker = Join-Path $installPath '.complete'
|
||||
if ((Test-Path -LiteralPath $installPath) -and -not (Test-Path -LiteralPath $completeMarker)) {
|
||||
$installPath = '{0}-{1}' -f $installPath, ([Guid]::NewGuid().ToString('N').Substring(0, 8))
|
||||
$completeMarker = Join-Path $installPath '.complete'
|
||||
}
|
||||
|
||||
$clientThumbprint = $ClientCertificateThumbprint -replace ' ', ''
|
||||
$serverThumbprint = $ServerCertificateThumbprint -replace ' ', ''
|
||||
if ($clientThumbprint.Length -ne 40 -or $serverThumbprint.Length -ne 40) {
|
||||
@@ -121,8 +145,11 @@ if (-not $serverCertificate) {
|
||||
}
|
||||
|
||||
if ($PSCmdlet.ShouldProcess($installPath, 'Install and register the SGU Credential Provider')) {
|
||||
New-Item -ItemType Directory -Path $installPath -Force | Out-Null
|
||||
Copy-Item -Path (Join-Path $PublishPath '*') -Destination $installPath -Recurse -Force
|
||||
if (-not (Test-Path -LiteralPath $completeMarker)) {
|
||||
New-Item -ItemType Directory -Path $installPath -Force | Out-Null
|
||||
Copy-Item -Path (Join-Path $resolvedPublishPath '*') -Destination $installPath -Recurse -Force
|
||||
[IO.File]::WriteAllText($completeMarker, $packageHash, [Text.UTF8Encoding]::new($false))
|
||||
}
|
||||
|
||||
New-Item -ItemType Directory -Path (Split-Path $settingsPath -Parent) -Force | Out-Null
|
||||
$settingsJson = @{
|
||||
|
||||
Reference in New Issue
Block a user