Docs: Windows-servere og syntetiske tjek (1.5)

- docs/windows-servere.md: what is collected and how often, how the agent reads
  servers (WS-Man, DCOM fallback, event log over MS-EVEN6), the exact least-privilege
  rights and grant-windows-server.ps1 (never local administrator), known limits,
  "ikke målbar" with the exact reason, firewall ports, the disk forecast, event
  noise filtering, rules with their thresholds, retention, configuration and API.
- docs/syntetiske-tjek.md: the kinds, the AX logon (port, then .NET Business
  Connector in a separate process, or clearly only the port), service running but
  not answering, sealed SQL passwords, rules, firewall, configuration and API.
- Linked from installation, integration (API table) and databehandling (retention,
  nothing sent to the hub); on the website in the documentation index, and the rule
  list shows the Windows server and synthetic check rules in their own areas.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Søren Eggert Lundsteen Olsen 2026-10-02 13:47:37 +02:00
parent 063243532d
commit 4c2d5252ad
8 changed files with 288 additions and 3 deletions

View file

@ -67,7 +67,7 @@ Der deles aldrig kundenavne, instans- eller servernavne, databasenavne, målinge
| Instanser, der ikke har rapporteret | 180 dage, med deres fund og versionshistorik |
| Alt om en kunde | Når aftalen ophører, eller kunden beder om det (`delete-customer`) |
Fristerne kan ændres i hubbens konfiguration (se [hub.md](hub.md)). I kundens eget repository styres opbevaringen af `SQLInsight:Retention`.
Fristerne kan ændres i hubbens konfiguration (se [hub.md](hub.md)). I kundens eget repository styres opbevaringen af `SQLInsight:Retention`, for Windows-servere og syntetiske tjek af `SQLInsight:Servers` og `SQLInsight:SyntheticChecks` (se [windows-servere.md](windows-servere.md#opbevaring)). Fund på Windows-servere og syntetiske tjek sendes ikke til SQLInsight Hub; hændelseslogtekster og fejlbeskeder bliver i kundens repository.
## Hvor ligger data?

View file

@ -92,6 +92,8 @@ Kør [`db/permissions/grant-least-privilege.sql`](../db/permissions/grant-least-
Overvåges Dynamics AX, kan agenten desuden få læseadgang til nogle få AX-systemtabeller (batchjob, nummerserier, RecId) med [`db/permissions/grant-dynamics-ax-read.sql`](../db/permissions/grant-dynamics-ax-read.sql). Det er et tilvalg pr. database; se [dynamics-ax.md](dynamics-ax.md).
Windows-serverne (CPU, diske, tjenester, hændelseslog, opgaver) læses med [`db/permissions/grant-windows-server.ps1`](../db/permissions/grant-windows-server.ps1), som giver agentens konto læseadgang uden lokal administrator; se [windows-servere.md](windows-servere.md#rettigheder-aldrig-lokal-administrator).
## 4. Agenten som Windows-tjeneste
```powershell
@ -186,4 +188,4 @@ Se [opdatering.md](opdatering.md): regelpakken opdateres automatisk, og nye vers
## Menuen i webbrugerfladen
Menuen er delt i grupper, der kan foldes ud og sammen: **Overvågning** (med *Servere og databaser*, se [servere-og-databaser.md](servere-og-databaser.md)), **Analyse**, **Konfiguration og sikkerhed**, **Rapporter**, **Dynamics AX** (*AX-databaser*, *Batchjob*, *Nummerserier*, *RecId* og *AX-brugere bag blokering*; kun når en database er genkendt som AX, se [dynamics-ax.md](dynamics-ax.md)), **Support**, **System** og **Community**. Gruppen med den side, man står på, er åben. Sider, der findes pr. instans (fx Analyse af tidsrum og Konfiguration), åbner et valg af instans; er der kun én instans, går de direkte til den. På en smal skærm (mobil) folder hele menuen sig sammen bag knappen **Menu**. Alt virker uden JavaScript.
Menuen er delt i grupper, der kan foldes ud og sammen: **Overvågning** (med *Servere og databaser*, se [servere-og-databaser.md](servere-og-databaser.md)), **Servere** (*Windows-servere* og *Syntetiske tjek*, se [windows-servere.md](windows-servere.md) og [syntetiske-tjek.md](syntetiske-tjek.md)), **Analyse**, **Konfiguration og sikkerhed**, **Rapporter**, **Dynamics AX** (*AX-databaser*, *Batchjob*, *Nummerserier*, *RecId* og *AX-brugere bag blokering*; kun når en database er genkendt som AX, se [dynamics-ax.md](dynamics-ax.md)), **Support**, **System** og **Community**. Gruppen med den side, man står på, er åben. Sider, der findes pr. instans (fx Analyse af tidsrum og Konfiguration), åbner et valg af instans; er der kun én instans, går de direkte til den. På en smal skærm (mobil) folder hele menuen sig sammen bag knappen **Menu**. Alt virker uden JavaScript.

View file

@ -91,6 +91,8 @@ Alt, hvad webbrugerfladen viser, kan hentes som JSON under `/api/v1/` med de sam
| `POST /api/v1/support-cases`, `GET /api/v1/support-cases[/{id}]`, `POST /api/v1/support-cases/{id}/close` | Supportsager: se [support.md](support.md) (kræver scope `support:write` for at skrive) |
| `GET /api/v1/dynamics-ax/databases`, `…/grant-script`, `…/batch-jobs`, `…/batch-runs`, `…/number-sequences`, `…/recids`, `…/sequence-samples`, `…/blocking` | Dynamics AX: genkendte AX-databaser med læseadgang og GRANT, batchjob og kørsler, nummerserier, RecId, målingerne bag prognoserne og AX-brugere bag blokering. AX-brugere og logins er `[udeladt]`; se [dynamics-ax.md](dynamics-ax.md#api) |
| `GET`/`POST /api/v1/monitored-instances`, `…/{id}`, `…/{id}/connection-test`, `…/{id}/discovery`, `…/{id}/databases`, `…/{id}/grant-script`, `POST /api/v1/instance-discovery`, `GET /api/v1/agent-requests/{id}` | Hvilke servere og databaser der overvåges, forbindelsestest, søgning og GRANT-script: se [servere-og-databaser.md](servere-og-databaser.md#api) (kræver scope `config:write` for at ændre) |
| `GET`/`POST /api/v1/windows-servers`, `…/{id}`, `…/{id}/test`, `…/{id}/grant-script`, `…/{id}/performance`, `…/{id}/disks`, `…/{id}/services`, `…/{id}/events`, `…/{id}/tasks`, `GET /api/v1/server-findings` | Windows-servere: CPU, hukommelse, diske med prognose, tjenester, hændelseslog, planlagte opgaver og deres fund: se [windows-servere.md](windows-servere.md#api) |
| `GET`/`POST /api/v1/synthetic-checks`, `…/{id}`, `…/{id}/run`, `…/{id}/results` | Syntetiske tjek: se [syntetiske-tjek.md](syntetiske-tjek.md#api) (kræver scope `config:write` for at ændre) |
| `GET /metrics` | Prometheus-format |
**Følsomme data:** forespørgselstekster, logins, personnavne, værts- og programnavne og tjenestekonti vises som `[udeladt]`, medmindre `SQLInsight:Web:ApiIncludesSensitiveEvidence` er `true`. Det gælder både fund, rapporter i JSON og sidernes data. PDF-rapporterne indeholder de samme data som i webbrugerfladen.

96
docs/syntetiske-tjek.md Normal file
View file

@ -0,0 +1,96 @@
# Syntetiske tjek
Et syntetisk tjek gør det, en bruger eller en applikation gør – forbinder til AOS-porten, henter en side, logger på SQL Server og kører en forespørgsel, slår et navn op, logger på Dynamics AX – med jævne mellemrum, fra agentens server og som agentens konto. Så opdages det også, når **en applikationsserver ikke svarer på det syntetiske tjek, selv om tjenesten kører**: noget, en tjenesteovervågning alene aldrig ser.
Tjekkene oprettes under **Servere → Syntetiske tjek** i webbrugerfladen eller med API'et (se [nedenfor](#api)). Agenten kører dem inden for et minut efter oprettelsen og derefter efter tjekkets interval (standard hvert 5. minut, mindst hvert minut). **Kør nu** kører et tjek med det samme. Intet tjek ændrer noget på de servere, det tjekker.
## Typer
| Type | Hvad agenten gør | Succes |
|---|---|---|
| **TCP-port** | Åbner en TCP-forbindelse til server:port (fx AOS'en på 2712) | Porten svarer |
| **HTTP(S)** | `GET` på adressen, følger op til 5 omdirigeringer, eventuelt som agentens Windows-konto (Enterprise Portal, Reporting Services) | Status 200–399 eller de forventede koder, og eventuelt en forventet tekst i de første 1 MB |
| **SQL-login og forespørgsel** | Logger på SQL Server (agentens Windows-konto eller et SQL-login), skifter eventuelt database og kører forespørgslen (standard `SELECT 1`) | Login og forespørgsel lykkes; svartiden måles for login og forespørgsel hver for sig |
| **DNS-opslag** | Slår navnet op | Navnet giver adresser (og en af de forventede, hvis de er angivet) |
| **Dynamics AX-logon** | Tester AOS-porten, og logger derefter på AX gennem .NET Business Connector, hvis den er installeret på agentens server | Se nedenfor |
**HTTPS:** certifikatets udløbsdato læses ved hvert tjek – også når certifikatet afvises. Et selvsigneret certifikat kan accepteres med *Accepter ugyldigt certifikat*; udløbsdatoen tjekkes stadig.
**SQL:** forespørgslen skal være én skrivebeskyttet `SELECT` (samme regler som community-reglerne, se [regler.md](regler.md)): ingen `INSERT`, `UPDATE`, `EXEC`, `INTO` osv. Det tjekkes, når tjekket gemmes, og igen, før det køres, og forespørgslen køres i en transaktion, der altid rulles tilbage, med `READ UNCOMMITTED` og `LOCK_TIMEOUT 5000`. Standardforespørgslen virker på SQL Server 2005 og nyere. Det præcise SQL står i [`SQL Scripts/SQLI-SYN-001.sql`](../SQL%20Scripts/SQLI-SYN-001.sql).
### Dynamics AX-logon
Tjekket gør sit bedste:
1. **AOS-porten** (standard 2712) skal svare. Gør den ikke, fejler tjekket.
2. Er **.NET Business Connector** (AX 2009 eller AX 2012) installeret på agentens server, logger agenten på AX gennem den – **i en separat Windows PowerShell-proces med timeout** (Business Connector er en .NET Framework-komponent og kan ikke indlæses i agenten) – og logger straks af igen. Der logges på som **agentens Windows-konto**, som derfor skal være oprettet som bruger i AX (i det angivne firma); der gemmes ingen adgangskode. Agenten læser intet i AX.
3. Er Business Connector **ikke** installeret (eller logon-testen slået fra), testes **kun porten**, og resultatet siger det tydeligt: *"Kun porten er testet: .NET Business Connector (AX 2009/2012) er ikke installeret på agentens server, så et AX-logon kan ikke prøves."*
Business Connector findes, hvor AX-installeren lægger den (`…\Microsoft Dynamics AX\60\BusinessConnector\Bin`, `…\50\Client\Bin`), eller hvor `SQLInsight:SyntheticChecks:BusinessConnectorPath` peger. AX 2009's Business Connector er bygget til .NET 2.0 og kræver Windows PowerShell 2.0-motoren (.NET Framework 3.5) på agentens server; mangler den, siger resultatet det. Object server angives som i AX-klientens konfiguration (fx `01@aos01:2712`); standard er server:port. Et logon optager kortvarigt en AX-session.
Kør agenten som en gMSA eller domænekonto, hvis AX-logon skal testes: computerkontoen `DOMÆNE\AGENTSERVER$` kan normalt ikke oprettes som AX-bruger.
## Tjeneste bag tjekket
Et tjek kan knyttes til en Windows-server (se [windows-servere.md](windows-servere.md)) og en tjeneste på den, fx AOS'en `AOS60$01` eller IIS `W3SVC`. Fejler tjekket, slår agenten tjenestens tilstand op, og fundet siger forskellen:
- *"AOS01 AX-logon svarer ikke, selv om tjenesten AOS60$01 kører"* – tjenesten hænger, er overbelastet, venter på databasen eller er blokeret af en firewall.
- *"AOS01 AX-logon svarer ikke: tjenesten AOS60$01 er stoppet"*.
Tjenesten læses med de samme rettigheder som Windows-serverne (Service Control Manager).
## Adgangskoder
Et SQL-login's adgangskode krypteres i webbrugerfladen i det øjeblik, den modtages, med **agentens offentlige nøgle** – præcis som i 1.3 (se [servere-og-databaser.md](servere-og-databaser.md#sql-login-adgangskoder)). Kun agenten kan læse den; den vises aldrig igen og returneres aldrig af API'et (kun `hasPassword`). Den er bundet til server, port og login: ændres de, skal den indtastes igen. Kan agenten ikke bruge den (fx efter en geninstallation), siger tjekket det, og den indtastes igen. HTTP-, TCP-, DNS- og AX-tjek bruger ingen adgangskoder.
## Regler
| Regel | Fund | Tærskel |
|---|---|---|
| SQLI-SYN-001 | Tjekket fejler (Høj) – med tjenestens tilstand, når den kendes | `SQLInsight:SyntheticChecks:FailuresBeforeFinding` 2 fejl i træk |
| SQLI-SYN-002 | Tjekket svarer langsomt (Middel) | tjekkets *Langsomt svar over (ms)*; `SlowRunsBeforeFinding` 3 gange i træk |
| SQLI-SYN-003 | TLS-certifikatet udløber (Middel; Høj inden for `CertificateCriticalDays` 7; Kritisk når udløbet) | `CertificateWarningDays` 21 |
Ét tabt svar giver ingen alarm; to i træk gør. Fundet løses, når tjekket lykkes igen. Fund vises på tjekkets side og sammen med serverfundene, sendes som notifikationer og findes i API'et.
## Firewall
Fra agentens server: de porte, tjekkene bruger (fx TCP 2712 til AOS'en, 80/443 til HTTP(S), 1433 eller instansens port til SQL Server, 53 til DNS). Tjenestens tilstand: som Windows-serverne (WinRM TCP 5985 eller DCOM/RPC).
## Opbevaring og konfiguration
Hver kørsel gemmes i 14 dage (`SQLInsight:SyntheticChecks:ResultDays`). Højst `MaxParallelChecks` (standard 4) tjek kører samtidig. Timeout pr. tjek er standard 15 sekunder (AX-logon 60), højst 120.
```json
"SyntheticChecks": {
"Enabled": true,
"MaxParallelChecks": 4,
"FailuresBeforeFinding": 2,
"SlowRunsBeforeFinding": 3,
"CertificateWarningDays": 21,
"CertificateCriticalDays": 7,
"ResultDays": 14,
"BusinessConnectorPath": null
}
```
## API
| Kald | Svar |
|---|---|
| `GET /api/v1/synthetic-checks` | Tjek med seneste resultat |
| `POST /api/v1/synthetic-checks` | Opret: `name`, `kind` (`Tcp`, `Http`, `Sql`, `Dns`, `AxLogon`), `serverId`, `intervalSeconds`, `timeoutSeconds`, `settings`, `password`; 201 med `runRequestId` |
| `GET`, `PUT`, `PATCH`, `DELETE /api/v1/synthetic-checks/{id}` | Ét tjek; ret (typen kan ikke ændres); pause; fjern |
| `POST /api/v1/synthetic-checks/{id}/run` | Kør nu (202, følg `GET /api/v1/agent-requests/{id}`) |
| `GET /api/v1/synthetic-checks/{id}/results?hours=24` | Kørsler med svartid, certifikat og tjenestens tilstand |
| `GET /api/v1/synthetic-checks/{id}/rule-states` | Reglernes udfald |
`settings` har felterne for typen: `host`, `port`, `url`, `expectedStatus`, `expectedText`, `useWindowsAuthentication`, `allowInvalidCertificate`, `server`, `database`, `authentication` (`Windows`/`SqlLogin`), `userName`, `encryption`, `trustServerCertificate`, `query`, `expectedAddresses`, `company`, `objectServer`, `configuration`, `businessConnectorLogon`, `serviceName`, `warnMilliseconds`.
```powershell
$h = @{ Authorization = "Bearer $token" }
$body = @{ name = 'AOS01 AX-logon'; kind = 'AxLogon'; serverId = 3; settings = @{ host = 'aos01'; company = 'DAT'; serviceName = 'AOS60$01' } } | ConvertTo-Json
Invoke-RestMethod https://sqlinsight.domæne.local/api/v1/synthetic-checks -Method Post -Headers $h -Body $body -ContentType 'application/json'
```
Fejlbeskeder kan nævne logins og servere: for fejlede kørsler er beskeden og følsomme målinger `[udeladt]` i API'et, medmindre `SQLInsight:Web:ApiIncludesSensitiveEvidence` er slået til. At ændre kræver scope `config:write` eller `SQLInsight:Web:AdminGroups`.

147
docs/windows-servere.md Normal file
View file

@ -0,0 +1,147 @@
# Windows-servere
Fra version 1.5 overvåger SQLInsight også selve Windows-serverne: **CPU, hukommelse, diske, tjenester, hændelseslog og planlagte opgaver** på SQL-, applikations-/AOS- og filservere – med varsel om, at en disk løber fuld om et antal dage, før det sker. Det findes under **Servere → Windows-servere** i webbrugerfladen og i API'et (se [nedenfor](#api)).
- **SQL Server-hosts kommer med af sig selv.** Serveren bag hver instans under *Servere og databaser* (se [servere-og-databaser.md](servere-og-databaser.md)) bliver en Windows-server med rollen *SQL Server*. Den forsvinder igen, når instansen fjernes, og kan sættes på pause. Azure SQL og LocalDB springes over. Slås fra med `SQLInsight:Servers:IncludeSqlHosts`.
- **Andre servere tilføjes** i webbrugerfladen: AOS- og applikationsservere, rapport- og filservere. Angiv servernavnet (uden instans og port), rollen og eventuelt de tjenester, der altid skal køre (fx `AOS60$01`, `W3SVC`).
SQLInsight **ændrer aldrig noget** på serverne: agenten læser kun. Den starter, stopper eller genstarter ingen tjenester og rydder ikke op – fundene siger, hvad der bør gøres, og en driftsansvarlig gør det.
## Sådan læser agenten serverne
Agenten læser serverne **som sin egen konto** (den samme som til SQL Server) med CIM – det samme, PowerShell's `Get-CimInstance` bruger:
1. **WinRM (WS-Man)**, TCP 5985, som er slået til på Windows Server 2012 og nyere. Foretrækkes.
2. **DCOM (RPC)**, TCP 135 og dynamiske porte, hvis WinRM ikke svarer eller afviser kontoen.
3. Agentens egen server læses lokalt, uden netværk.
`SQLInsight:Servers:Protocol` kan låse det til `WSMan` eller `DCOM` (standard `Auto`). Hændelsesloggen læses med hændelseslogtjenestens egen protokol (MS-EVEN6, over RPC), som filtrerer på serveren og derfor er hurtig; virker den ikke, bruges CIM (`Win32_NTLogEvent`), som er langsommere. Siden for serveren viser, hvordan den blev læst.
Kører agenten som den virtuelle konto `NT SERVICE\SQLInsight Agent` (standard), LocalSystem eller NETWORK SERVICE, kommer den til andre servere som **computerkontoen** `DOMÆNE\AGENTSERVER$`. En gMSA eller domænekonto er den samme overalt. Siden viser den konto, scriptet nedenfor skal give rettigheder til.
## Rettigheder (aldrig lokal administrator)
Agenten skal **ikke** være lokal administrator. Den har brug for præcis dette, og intet andet:
| Rettighed | Bruges til |
|---|---|
| Lokal gruppe **Performance Monitor Users** | Ydelsestællere: CPU, hukommelse, paging, commit |
| Lokal gruppe **Event Log Readers** | System- og Application-loggen (aldrig Security-loggen) |
| Lokal gruppe **Distributed COM Users** | CIM over DCOM |
| Lokal gruppe **Remote Management Users** | CIM over WinRM (Windows Server 2012 og nyere) |
| WMI: **Enable Account** + **Remote Enable** på `root\cimv2` | Styresystem, oppetid, diske, tjenester |
| WMI: **Enable Account** + **Remote Enable** på `root\Microsoft\Windows\TaskScheduler` | Planlagte opgaver (Windows Server 2012 og nyere) |
| **Service Control Manager**: forbinde, liste og læse (`CC LC RP RC`) | Over netværket må ellers kun administratorer liste tjenester |
Det hele gives af [`grant-windows-server.ps1`](../db/permissions/grant-windows-server.ps1). Webbrugerfladen viser scriptet udfyldt med agentens konto under **Rettigheder og forbindelse** på serverens side (kopiér eller hent som fil; API: `GET /api/v1/windows-servers/{id}/grant-script`). En administrator kører det på serveren:
```powershell
.\grant-windows-server.ps1 -Account 'DOMÆNE\AGENTSERVER$'
.\grant-windows-server.ps1 -Account 'DOMÆNE\AGENTSERVER$' -EnableFirewall -AgentAddress 10.0.0.15 # slår også Windows' egne firewallregler til
.\grant-windows-server.ps1 -Account 'DOMÆNE\AGENTSERVER$' -Remove # fjerner det hele igen
```
Scriptet kan køres igen uden skade og virker på Windows PowerShell 2.0 og nyere. Grupperne findes via deres SID, så det virker på dansk og engelsk Windows. Den oprindelige sikkerhed på Service Control Manager gemmes første gang i `%ProgramData%\SQLInsight\scmanager-sddl-backup.txt`.
Kendte begrænsninger med mindst mulige rettigheder:
- **Tjenester:** en tjeneste, hvis egen sikkerhed ikke lader kontoen læse den, ses ikke. Ser agenten slet ingen tjenester, er området "ikke målbart" med årsagen.
- **Planlagte opgaver:** agenten ser kun opgaver, kontoen må læse. Seneste resultat læses med Task Schedulers `GetInfo` (et skrivebeskyttet CIM-metodekald, som `Get-ScheduledTaskInfo`); WinRM tillader kun metodekald for kontoen, hvis den har Execute-ret i WinRM. Mangler den, siger fundet det; brug DCOM (`SQLInsight:Servers:Protocol`), eller giv retten med `winrm configSDDL default`.
- **Windows Server 2008 R2:** WinRM-gruppen og opgave-navnerummet findes ikke; brug DCOM, og planlagte opgaver er "ikke målbare".
### Når en rettighed mangler
Testen (**Test forbindelsen** på serverens side, eller `POST /api/v1/windows-servers/{id}/test`) udføres af agenten, som dens konto, og prøver hvert område én gang. Tabellen viser for hvert område *Kan læses* eller *Ikke målbar* med den præcise årsag, fx
> Adgang nægtet til hændelseslog på aos01.firma.local over WS-Man: Agentens konto (FIRMA\AGENT01$) mangler Event Log Readers. Kør tildelingsscriptet (grant-windows-server.ps1) som administrator på serveren, og test igen. Agenten skal ikke være lokal administrator.
Under overvågningen gælder det samme: et område, der ikke kan læses, gør kun dets regler "ikke målbare" med årsagen (under *Ikke målbart* på siden), og fund fra før forbliver åbne, indtil området kan læses igen. Problemet skrives én gang i agentens log, når det opstår eller ændrer sig – ikke hvert minut. Svarer serveren slet ikke, sendes én notifikation ("kan ikke nås") og én, når den svarer igen.
## Firewall
Fra agentens server til de overvågede servere:
| Port | Hvad | Windows' indbyggede regel |
|---|---|---|
| TCP 5985 | WinRM (WS-Man), foretrukket | Windows Remote Management (HTTP-In) |
| TCP 135 + dynamiske RPC-porte (49152–65535) | DCOM/WMI, hvis WinRM ikke bruges | Windows Management Instrumentation (DCOM-In, WMI-In, ASync-In) |
| TCP 135 + dynamiske RPC-porte, TCP 445 | Hændelsesloggen (MS-EVEN6) | Remote Event Log Management (RPC, NP-In, RPC-EPMAP) |
`grant-windows-server.ps1 -EnableFirewall` slår disse indbyggede regler til (Windows Server 2012 og nyere), eventuelt kun for agentens adresse med `-AgentAddress`. Syntetiske tjek bruger de porte, de tjekker (se [syntetiske-tjek.md](syntetiske-tjek.md)).
## Hvad måles, og hvor ofte
| Område | Interval | Hvad |
|---|---|---|
| CPU, hukommelse, oppetid | 1 minut | Gennemsnitlig CPU pr. minut (rå tællere, ikke et øjebliksbillede), ledig hukommelse, paging, commit, starttidspunkt |
| Diske | 15 minutter | Ledig plads pr. drev og monteringspunkt; prognose for, hvornår drevet er fuldt |
| Tjenester | 5 minutter | Alle tjenester med status og starttype |
| Hændelseslog | 5 minutter | Fejl og kritiske hændelser i System og Application siden sidst (første gang det seneste døgn) |
| Planlagte opgaver | 15 minutter | Opgaver uden for `\Microsoft\` med seneste kørsel, resultat og næste kørsel |
Intervallerne ændres under `SQLInsight:Servers:Intervals` (`performance`, `disks`, `services`, `events`, `tasks`). Højst `MaxParallelServers` (standard 4) servere læses ad gangen.
### "Disken er fuld om ca. N dage"
Ledig plads pr. drev gemmes hvert 15. minut i SQLInsights repository. Prognosen lægger en ret linje (mindste kvadraters metode) gennem timegennemsnittene for de seneste 14 dage (`Disks:ForecastHistoryDays`) – kun siden drevet sidst skiftede størrelse, så et udvidet drev starter forfra. Der gives kun en prognose, når der er mindst to dages historik og 12 målinger, linjen passer rimeligt (R² mindst 0,5), drevet faktisk fyldes (mindst 0,01 GB om dagen), og den rammer inden for et år. Et backupdrev, der fyldes om natten og ryddes om morgenen, giver derfor ingen alarm. Fundet kommer, når drevet forventes fuldt inden for 30 dage (`Disks:ForecastDays`): Middel, inden for 7 dage Høj, inden for 2 dage Kritisk.
### Hændelsesloggen
Som SQL Servers fejllog (se [fejllog.md](fejllog.md)) grupperes hændelserne pr. log, kilde og hændelses-id med antal, første og seneste tidspunkt og den nyeste tekst som eksempel. Kendt støj sorteres fra (DCOM 10016/10010, Schannel-alarmer, TPM-WMI, WMI 10 m.fl.), og det samme gør SQL Servers egne kilder (`MSSQLSERVER`, `MSSQL$…`, `SQLSERVERAGENT` …), som SQL Server-overvågningen allerede dækker. Egen støj tilføjes under `EventLog:Ignore` som `"Kilde:id"`, `"Kilde"` eller `"Kilde*"`. Disk- og filsystemfejl, opbrugte ressourcer og kritiske hændelser giver fund med det samme; tjenester der går ned, programnedbrud og Dynamics AX' AOS-fejl også; almindelige fejl først, når de er set 5 gange på et døgn (`EventLog:RepeatThreshold`). Et fund forbliver åbent et døgn efter hændelsen sidst blev set. Eksempelteksten kan nævne brugere, computere og stier: den vises i webbrugerfladen, men er `[udeladt]` i API'et.
## Regler
| Regel | Fund | Tærskel (`SQLInsight:Servers:…`) |
|---|---|---|
| SQLI-WIN-001 | CPU højt i længere tid | `Cpu:HighPercent` 90 % i `Cpu:SustainedMinutes` 15 min. |
| SQLI-WIN-002 | Næsten ingen ledig hukommelse | under `Memory:AvailablePercent` 5 % **og** `Memory:AvailableMegabytes` 1024 MB (5 min.) |
| SQLI-WIN-003 | Paging med lidt ledig hukommelse | `Memory:PagesPerSecond` 1000 (15 min.) og under 10 % ledig |
| SQLI-WIN-004 | Commit tæt på grænsen | `Memory:CommitPercent` 90 % |
| SQLI-WIN-005 | Lidt ledig plads på et drev | under `Disks:FreePercent` 10 % (Høj under `CriticalFreePercent` 5 %), medmindre mindst `EnoughFreeGigabytes` 50 GB er ledig |
| SQLI-WIN-006 | Drevet er fuldt om ca. N dage | `Disks:ForecastDays` 30 |
| SQLI-WIN-007 | Automatisk tjeneste kører ikke | `Services:Ignore`, `Services:StartupGraceMinutes` 15; tjenester der stopper af sig selv (sppsvc, opdateringstjenester …) giver ingen fund |
| SQLI-WIN-008 | Overvåget tjeneste kører ikke (eller findes ikke) | listen pr. server |
| SQLI-WIN-009 | Fejl i hændelsesloggen | `EventLog:RepeatThreshold` 5, `EventLog:MaxFindings` 15 |
| SQLI-WIN-010 | Serveren er genstartet (uventet: Høj; planlagt: Info i et døgn) | Kernel-Power 41, EventLog 6008, BugCheck 1001 |
| SQLI-WIN-011 | Ikke genstartet i lang tid | `UptimeDays` (standard 0 = slået fra) |
| SQLI-WIN-012 | Planlagt opgave fejlede | `Tasks:IncludeMicrosoftTasks`, `Tasks:Ignore`, `Tasks:MaxWatched` 50 |
| SQLI-WIN-013 | Planlagt opgave kørte ikke som planlagt | `Tasks:LateMinutes` 60 |
Hver regel har sit eget script i [`SQL Scripts/`](../SQL%20Scripts/) (fx `SQLI-WIN-006.sql`) med de præcise CIM/WQL-forespørgsler og en skrivebeskyttet PowerShell-linje, der efterprøver dem.
Fund på servere vises på serverens side, under **Aktive fund på servere og syntetiske tjek** på siden Windows-servere, sendes som notifikationer (med link til fundet) og findes i API'et. De sendes ikke til SQLInsight Hub.
## Opbevaring
| Data | Standard | Indstilling |
|---|---|---|
| CPU og hukommelse pr. minut | 14 dage | `SQLInsight:Servers:SampleDays` |
| Ledig plads pr. drev | 35 dage (mindst prognosens historik) | `SQLInsight:Servers:DiskHistoryDays` |
| Grupperede hændelser | 90 dage | `SQLInsight:Servers:EventDays` |
| Løste fund | 365 dage | `SQLInsight:Retention:ResolvedFindingDays` |
En server med 5 drev fylder omkring 1.440 rækker om dagen for CPU/hukommelse og 480 for diskene. Agenten sletter i små portioner hver time.
## Konfiguration
Alle tærskler, intervaller og opbevaringstider står i agentens `appsettings.json` under `SQLInsight:Servers`. Ændringer kræver en genstart af agenten. Servere, roller og tjenestelister ændres i webbrugerfladen og bruges inden for et minut.
## API
| Kald | Svar |
|---|---|
| `GET /api/v1/windows-servers` | Servere med status, styresystem, protokol og områdernes målbarhed |
| `POST /api/v1/windows-servers` | Tilføj (`host`, `name`, `role`, `watchedServices`, `ignoredServices`); 201 med `serverTestRequestId` |
| `GET`, `PATCH`, `DELETE /api/v1/windows-servers/{id}` | Én server; pause, rolle og tjenestelister; fjern (409 for SQL Server-hosts) |
| `POST`, `GET /api/v1/windows-servers/{id}/test` | Test forbindelse og rettigheder (202, følg `GET /api/v1/agent-requests/{id}`); seneste test med manglende områder |
| `GET /api/v1/windows-servers/{id}/grant-script` | Scriptet udfyldt med agentens konto |
| `GET /api/v1/windows-servers/{id}/performance?hours=24` | CPU og hukommelse pr. minut |
| `GET /api/v1/windows-servers/{id}/disks` | Drev med ledig plads og prognose |
| `GET /api/v1/windows-servers/{id}/services` | Tjenester og serverens lister |
| `GET /api/v1/windows-servers/{id}/events?days=1` | Grupperede hændelser (eksemplet er `[udeladt]`) |
| `GET /api/v1/windows-servers/{id}/tasks` | Overvågede planlagte opgaver |
| `GET /api/v1/windows-servers/{id}/rule-states` | Reglernes udfald med årsag |
| `GET /api/v1/server-findings?status=&scope=server&id=` | Fund på servere og syntetiske tjek |
| `GET /api/v1/server-findings/{id}` | Ét fund |
At ændre kræver scope `config:write` (API-token) eller medlemskab af `SQLInsight:Web:AdminGroups` (Windows-brugere), som for *Servere og databaser*.

View file

@ -25,6 +25,8 @@ public sealed class DocsService
("analyse", "Hvad skete der i et bestemt tidsrum: belastning, ventetider mod normalen, tungeste forespørgsler, blocking og deadlocks."),
("regler", "Skriv egne regler i SQL, og bidrag med community-regler."),
("servere-og-databaser", "Tilmeld servere i webbrugerfladen, vælg databaser, test forbindelsen og få GRANT-scriptet."),
("windows-servere", "CPU, hukommelse, diske med prognose, tjenester, hændelseslog og planlagte opgaver på Windows-servere – uden lokal administrator."),
("syntetiske-tjek", "TCP, HTTP(S), SQL-login, DNS og Dynamics AX-logon som en bruger: opdag en server, der ikke svarer, selv om tjenesten kører."),
("support", "Opret supportsager hos SEOSoft fra webbrugerfladen og følg deres status."),
("databehandling", "Hvilke data der behandles, hvor de ligger, og hvad der deles."),
("legacy-versions", "SQL Server 2000, 2005 og 2008: hvad der kan måles, og hvordan der forbindes."),

View file

@ -28,12 +28,15 @@ public static class RuleCatalog
["BLK"] = "Blocking og deadlocks",
["DLK"] = "Blocking og deadlocks",
["SEC"] = "Sikkerhed",
["WIN"] = "Windows-servere",
["SYN"] = "Syntetiske tjek",
};
public static IReadOnlyList<RuleEntry> All(IReadOnlyList<CustomRuleDefinition> community)
{
ArgumentNullException.ThrowIfNull(community);
IEnumerable<RuleEntry> builtIn = BuiltInRules.Create().Select(r => new RuleEntry(r.Id, r.Title, Area(r.Id), null, false));
IEnumerable<RuleEntry> builtIn = BuiltInRules.Create().Select(r => new RuleEntry(r.Id, r.Title, Area(r.Id), null, false))
.Concat(Rules.Servers.ServerRuleCatalog.All.Select(r => new RuleEntry(r.Id, r.Title, Area(r.Id), null, false)));
return builtIn.Concat(community.Select(r => new RuleEntry(r.Id, r.Title, Area(r.Id), r.Author, true))).ToList();
}

View file

@ -0,0 +1,33 @@
using Microsoft.Extensions.Options;
using SQLInsight.Community.Services;
namespace SQLInsight.Community.Tests;
/// <summary>1.5 on the website: the Windows server and synthetic check documentation and rules.</summary>
public sealed class ServerDocsTests
{
[Fact]
public void The_documentation_has_windows_servers_and_synthetic_checks()
{
var docs = new DocsService(Options.Create(new CommunityOptions()));
var servers = docs.Find("windows-servere");
var checks = docs.Find("syntetiske-tjek");
Assert.Equal("Windows-servere", servers!.Title);
Assert.Contains("aldrig lokal administrator", servers.Html, StringComparison.OrdinalIgnoreCase);
Assert.Contains("grant-windows-server.ps1", servers.Html, StringComparison.Ordinal);
Assert.Contains("5985", servers.Html, StringComparison.Ordinal);
Assert.Equal("Syntetiske tjek", checks!.Title);
Assert.Contains("Kun porten er testet", checks.Html, StringComparison.Ordinal);
}
[Fact]
public void The_rule_list_has_the_windows_server_and_check_rules_in_their_own_areas()
{
var rules = RuleCatalog.All([]);
Assert.Contains(rules, r => r.Id == "SQLI-WIN-006" && r.Area == "Windows-servere");
Assert.Contains(rules, r => r.Id == "SQLI-SYN-001" && r.Area == "Syntetiske tjek");
}
}