Add managed RustDesk enrollment for Linux clients
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
[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
|
||||
}
|
||||
Reference in New Issue
Block a user