sqlinsight-pro/db/permissions/grant-windows-server.ps1
Søren Eggert Lundsteen Olsen ae20954a91 Windows servers and synthetic checks core: data, disk forecast, event dedup, grant script
- Core/Servers: what the agent collects from a Windows server (CPU and memory from
  raw counters, disks, services, events, scheduled tasks), roles and sources, host
  validation and the computer behind a SQL Server data source.
- DiskForecaster: "fuld om ca. N dage" from a least-squares line over 14 days of
  SQLInsight's own samples, only since the volume last changed size, with sanity
  bounds (history, samples, R², max one year).
- ServerEventAnalysis: noise filter, grouping and a 24-hour window for System and
  Application events, like the SQL Server error log in 1.2.
- db/permissions/grant-windows-server.ps1: least-privilege rights for the agent's
  account (groups, WMI namespaces, SCM read), never local administrator; filled in
  by ServerGrantScript.
- Core/SyntheticChecks: kinds, settings, validation (read-only SQL), results.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 14:58:17 +02:00

148 lines
7.8 KiB
PowerShell

<#
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).'