- 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>
148 lines
7.8 KiB
PowerShell
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).'
|