<# SQLInsight Pro: least-privilege rettigheder til agentens konto på en Windows-server ===================================================================================== Giver SQLInsight-agentens konto præcis den læseadgang, den skal bruge for at overvåge denne server: CPU, hukommelse, diske, tjenester, hændelseslog og planlagte opgaver. Aldrig lokal administrator. Scriptet kan køres igen uden skade. Det ændrer kun adgangsrettigheder: det starter, stopper eller ændrer ingen tjenester, opgaver eller indstillinger, og det læser ingen data. Kør som administrator på den server, der skal overvåges (Windows PowerShell 2.0 eller nyere): .\grant-windows-server.ps1 -Account 'DOMÆNE\AGENTSERVER$' .\grant-windows-server.ps1 -Account 'DOMÆNE\AGENTSERVER$' -EnableFirewall -AgentAddress 10.0.0.15 Fjern rettighederne igen: .\grant-windows-server.ps1 -Account 'DOMÆNE\AGENTSERVER$' -Remove Agentens konto: kører agenten som NT SERVICE\SQLInsight Agent, LocalSystem eller NETWORK SERVICE, kommer den til andre servere som computerkontoen DOMÆNE\COMPUTERNAVN$. En gMSA eller domænekonto er den samme overalt. Hvad der gives, og hvorfor: Performance Monitor Users ydelsestællere: CPU, hukommelse, paging Event Log Readers System- og Application-loggen (aldrig Security-loggen) Distributed COM Users WMI/CIM over DCOM (RPC) Remote Management Users WMI/CIM over WinRM (WS-Man), Windows Server 2012 og nyere WMI: Enable Account + Remote Enable på root\cimv2 og root\Microsoft\Windows\TaskScheduler (ikke arvet) Service Control Manager: læse og liste tjenester (CC LC RP RC). Den oprindelige SDDL gemmes første gang i %ProgramData%\SQLInsight\scmanager-sddl-backup.txt Hvad der IKKE gives: lokal administrator, ret til at starte/stoppe tjenester, skrive i WMI, læse filer eller Security-loggen. Firewall (kun med -EnableFirewall, Windows Server 2012 og nyere): slår Windows' indbyggede regler til for WMI (DCOM), WinRM (TCP 5985) og Remote Event Log Management, eventuelt kun fra agentens adresse. #> param( [string] $Account = 'DOMÆNE\AGENTSERVER$', # <== agentens konto [switch] $Remove, [switch] $EnableFirewall, [string] $AgentAddress ) $ErrorActionPreference = 'Stop' $sid = (New-Object System.Security.Principal.NTAccount($Account)).Translate([System.Security.Principal.SecurityIdentifier]).Value if ($Remove) { $verb = 'Fjerner' } else { $verb = 'Giver' } Write-Host "$verb rettigheder til $Account ($sid) på $env:COMPUTERNAME" # 1. Lokale grupper. Fundet via deres SID, så det virker på dansk, engelsk og andre sprog. $groups = @( @('S-1-5-32-558', 'ydelsestællere (CPU, hukommelse, paging)'), @('S-1-5-32-573', 'System- og Application-loggen'), @('S-1-5-32-562', 'WMI over DCOM'), @('S-1-5-32-580', 'WMI over WinRM (WS-Man)') ) foreach ($g in $groups) { try { $groupName = (New-Object System.Security.Principal.SecurityIdentifier($g[0])).Translate([System.Security.Principal.NTAccount]).Value.Split('\')[1] } catch { Write-Host " - gruppen $($g[0]) ($($g[1])) findes ikke på denne Windows-version; springes over" continue } $group = [ADSI]("WinNT://$env:COMPUTERNAME/$groupName,group") $members = @($group.Invoke('Members') | ForEach-Object { $bytes = $_.GetType().InvokeMember('objectSid', 'GetProperty', $null, $_, $null) (New-Object System.Security.Principal.SecurityIdentifier($bytes, 0)).Value }) $isMember = $members -contains $sid $path = 'WinNT://' + $Account.Replace('\', '/') if ($Remove -and $isMember) { $group.Remove($path) Write-Host " - fjernet fra $groupName" } elseif (-not $Remove -and -not $isMember) { $group.Add($path) Write-Host " + medlem af $groupName ($($g[1]))" } else { Write-Host " = ${groupName}: uændret" } } # 2. WMI-navnerum: Enable Account (0x1) + Remote Enable (0x20), kun på selve navnerummet. function Set-NamespaceAccess([string] $Namespace) { $security = Get-WmiObject -Namespace $Namespace -Class __SystemSecurity $descriptor = $security.PsBase.InvokeMethod('GetSecurityDescriptor', $null).Descriptor $dacl = @($descriptor.DACL | Where-Object { $_.Trustee.SidString -ne $sid }) if (-not $Remove) { $trustee = ([WMIClass]'Win32_Trustee').CreateInstance() $trustee.SidString = $sid $ace = ([WMIClass]'Win32_ACE').CreateInstance() $ace.AccessMask = 0x21 $ace.AceFlags = 0 $ace.AceType = 0 $ace.Trustee = $trustee $dacl += $ace.PsObject.ImmediateBaseObject } $descriptor.DACL = $dacl $result = $security.PsBase.InvokeMethod('SetSecurityDescriptor', @($descriptor)) if ($result.ReturnValue -ne 0) { throw "SetSecurityDescriptor på $Namespace fejlede ($($result.ReturnValue))" } if ($Remove) { Write-Host " - WMI $Namespace" } else { Write-Host " + WMI $Namespace (Enable Account, Remote Enable)" } } Set-NamespaceAccess 'root\cimv2' if (Get-WmiObject -Namespace 'root\Microsoft\Windows' -Class __Namespace -Filter "Name='TaskScheduler'" -ErrorAction SilentlyContinue) { Set-NamespaceAccess 'root\Microsoft\Windows\TaskScheduler' } else { Write-Host ' - root\Microsoft\Windows\TaskScheduler findes ikke (før Windows Server 2012): planlagte opgaver kan ikke måles' } # 3. Service Control Manager: læse og liste tjenester. Over netværket må ellers kun administratorer liste tjenester. $scmAce = "(A;;CCLCRPRC;;;$sid)" $sddl = ((& sc.exe sdshow scmanager) | Where-Object { $_ -match '\S' }) -join '' if ($sddl -notmatch '^D:') { throw "Kunne ikke læse Service Control Managers sikkerhed: $sddl" } $backup = Join-Path $env:ProgramData 'SQLInsight\scmanager-sddl-backup.txt' if (-not (Test-Path $backup)) { New-Item -ItemType Directory -Force -Path (Split-Path $backup) | Out-Null Set-Content -Path $backup -Value $sddl } $clean = $sddl.Replace($scmAce, '') if ($Remove) { $new = $clean } else { $at = $clean.IndexOf('S:') if ($at -lt 0) { $new = $clean + $scmAce } else { $new = $clean.Substring(0, $at) + $scmAce + $clean.Substring($at) } } if ($new -ne $sddl) { & sc.exe sdset scmanager $new | Out-Null if ($LASTEXITCODE -ne 0) { throw "sc.exe sdset scmanager fejlede ($LASTEXITCODE). Den oprindelige SDDL står i $backup" } if ($Remove) { Write-Host ' - Service Control Manager' } else { Write-Host ' + Service Control Manager (CC LC RP RC: forbinde, liste, læse)' } } else { Write-Host ' = Service Control Manager: uændret' } # 4. Firewall (valgfrit): Windows' indbyggede regler, fundet på deres faste (ikke oversatte) navne. if ($EnableFirewall -and -not $Remove) { if (-not (Get-Command Enable-NetFirewallRule -ErrorAction SilentlyContinue)) { Write-Host ' - Enable-NetFirewallRule findes ikke (før Windows Server 2012): slå reglerne for WMI og Remote Event Log Management til manuelt' } else { foreach ($rule in 'WMI-WINMGMT-In-TCP', 'WMI-RPCSS-In-TCP', 'WMI-ASYNC-In-TCP', 'WINRM-HTTP-In-TCP', 'RemoteEventLogSvc-In-TCP', 'RemoteEventLogSvc-NP-In-TCP', 'RemoteEventLogSvc-RPCSS-In-TCP') { $found = Get-NetFirewallRule -Name $rule -ErrorAction SilentlyContinue if (-not $found) { Write-Host " - firewallreglen $rule findes ikke; springes over" continue } if ($AgentAddress) { $found | Set-NetFirewallRule -RemoteAddress $AgentAddress } $found | Enable-NetFirewallRule if ($AgentAddress) { Write-Host " + firewall: $rule (kun fra $AgentAddress)" } else { Write-Host " + firewall: $rule" } } } } Write-Host 'Færdig. Test forbindelsen fra SQLInsight (Servere > serveren > Test forbindelsen).'