Files
SGU-CredentialProvider/scripts/Install-SguRustDeskLinuxEnrollment.ps1
T

129 lines
5.5 KiB
PowerShell

[CmdletBinding(SupportsShouldProcess)]
param(
[Parameter(Mandatory)]
[ValidatePattern('^[A-Za-z0-9][A-Za-z0-9.-]*$')]
[string]$DomainName,
[Parameter(Mandatory)]
[ValidatePattern('^[A-Za-z0-9][A-Za-z0-9.-]*$')]
[string]$ServerAddress,
[Parameter(Mandatory)]
[ValidatePattern('^[A-Za-z0-9+/=]+$')]
[string]$ServerPublicKey,
[string]$RegistrationShareName = 'SGU-RustDesk-Enrollment$',
[string]$DataRoot = "$env:ProgramData\SGU\RustDesk\LinuxEnrollment",
[string]$ProcessorScriptPath = (Join-Path $PSScriptRoot 'Invoke-SguRustDeskLinuxRegistrationProcessor.ps1')
)
$ErrorActionPreference = 'Stop'
function Assert-Administrator {
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
$principal = [Security.Principal.WindowsPrincipal]::new($identity)
if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
throw 'Only a local administrator can install Linux RustDesk enrollment.'
}
}
function Get-EnrollmentCertificate {
param([Parameter(Mandatory)][string]$FriendlyName)
$certificate = Get-ChildItem -Path Cert:\LocalMachine\My |
Where-Object FriendlyName -eq $FriendlyName |
Where-Object HasPrivateKey |
Select-Object -First 1
if (-not $certificate) {
$certificate = New-SelfSignedCertificate `
-Subject 'CN=SGU RustDesk Linux enrollment' `
-FriendlyName $FriendlyName `
-CertStoreLocation 'Cert:\LocalMachine\My' `
-KeyAlgorithm RSA `
-KeyLength 3072 `
-KeyUsage KeyEncipherment,DigitalSignature `
-NotAfter (Get-Date).AddYears(5)
}
return $certificate
}
function Set-EnrollmentDirectoryAcl {
param(
[Parameter(Mandatory)][string]$Path,
[Parameter(Mandatory)][string]$DomainNetbiosName
)
New-Item -ItemType Directory -Path $Path -Force | Out-Null
$arguments = @(
"`"$Path`"", '/inheritance:r',
'/grant:r', 'SYSTEM:(OI)(CI)(F)',
'BUILTIN\Administrators:(OI)(CI)(F)',
"$DomainNetbiosName\Domain Computers:(OI)(CI)(M)"
)
& icacls.exe @arguments | Out-Null
if ($LASTEXITCODE -ne 0) {
throw "Could not secure the Linux RustDesk enrollment directory $Path."
}
}
Assert-Administrator
Import-Module ActiveDirectory -ErrorAction Stop
if (-not (Test-Path -LiteralPath $ProcessorScriptPath -PathType Leaf)) {
throw "The Linux RustDesk registration processor is missing: $ProcessorScriptPath"
}
if (-not $PSCmdlet.ShouldProcess($env:COMPUTERNAME, 'Install the protected Linux RustDesk enrollment endpoint')) {
return
}
$domain = Get-ADDomain -Identity $DomainName
$publicRoot = Join-Path $DataRoot 'Public'
$requestsRoot = Join-Path $publicRoot 'Requests'
$archiveRoot = Join-Path $publicRoot 'Archive'
$rejectedRoot = Join-Path $publicRoot 'Rejected'
foreach ($path in @($DataRoot, $publicRoot, $requestsRoot, $archiveRoot, $rejectedRoot)) {
Set-EnrollmentDirectoryAcl -Path $path -DomainNetbiosName $domain.NetBIOSName
}
$certificate = Get-EnrollmentCertificate -FriendlyName 'SGU RustDesk Linux enrollment'
$publicCertificatePath = Join-Path $publicRoot 'registration-public.cer'
Export-Certificate -Cert $certificate -FilePath $publicCertificatePath -Force | Out-Null
$clientConfiguration = [ordered]@{
ServerAddress = $ServerAddress
ServerPublicKey = $ServerPublicKey
RegistrationShare = "\\$env:COMPUTERNAME\$RegistrationShareName"
UpdatedAt = (Get-Date).ToString('o')
}
[IO.File]::WriteAllText((Join-Path $publicRoot 'rustdesk-client.json'),
($clientConfiguration | ConvertTo-Json), [Text.UTF8Encoding]::new($false))
$share = Get-SmbShare -Name $RegistrationShareName -ErrorAction SilentlyContinue
if (-not $share) {
New-SmbShare -Name $RegistrationShareName -Path $publicRoot `
-FullAccess @('SYSTEM', 'BUILTIN\Administrators') `
-ChangeAccess "$($domain.NetBIOSName)\Domain Computers" | Out-Null
}
elseif ($share.Path -ne $publicRoot) {
throw "The existing SMB share $RegistrationShareName points to $($share.Path), not $publicRoot."
}
$installedProcessor = Join-Path $DataRoot 'Invoke-SguRustDeskLinuxRegistrationProcessor.ps1'
Copy-Item -LiteralPath $ProcessorScriptPath -Destination $installedProcessor -Force
$processorArguments = "-NoProfile -NonInteractive -ExecutionPolicy Bypass -File `"$installedProcessor`" -DataRoot `"$DataRoot`" -CertificateThumbprint $($certificate.Thumbprint)"
$action = New-ScheduledTaskAction -Execute (Join-Path $env:WINDIR 'System32\WindowsPowerShell\v1.0\powershell.exe') `
-Argument $processorArguments
$trigger = New-ScheduledTaskTrigger -Once -At (Get-Date).AddMinutes(1) `
-RepetitionInterval (New-TimeSpan -Minutes 1) -RepetitionDuration (New-TimeSpan -Days 3650)
$principal = New-ScheduledTaskPrincipal -UserId 'SYSTEM' -LogonType ServiceAccount -RunLevel Highest
Register-ScheduledTask -TaskName 'SGU-RustDesk-LinuxRegistration' -Action $action -Trigger $trigger `
-Principal $principal -Description 'Registers encrypted RustDesk credentials sent by domain-joined Linux computers.' -Force | Out-Null
New-NetFirewallRule -DisplayName 'SGU RustDesk Linux enrollment SMB' -Group 'SGU RustDesk' `
-Direction Inbound -Action Allow -Protocol TCP -LocalPort 445 -Profile Domain -ErrorAction SilentlyContinue | Out-Null
[pscustomobject]@{
RegistrationShare = "\\$env:COMPUTERNAME\$RegistrationShareName"
PublicCertificatePath = $publicCertificatePath
RegistrationTask = 'SGU-RustDesk-LinuxRegistration'
CertificateThumbprint = $certificate.Thumbprint
}