Review 1.3: local Administrators may change the configuration until AdminGroups is set; MSI property ADMIN_GROUPS
With SQLInsight:Web:AdminGroups empty nobody could add the first server. The
installer now takes ADMIN_GROUPS (';'-separated) and writes it to the web's
sqlinsight.ini; while the setting is empty, members of the web server's
BUILTIN\Administrators (S-1-5-32-544 among the user's group SIDs; a remote
Negotiate login has the full token, a UAC-filtered deny-only SID does not count)
may change it. The page says who may.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
8e1b877fda
commit
2adeef4597
10 changed files with 86 additions and 20 deletions
|
|
@ -23,6 +23,7 @@ msiexec /i SQLInsight-Pro-1.0.0-x64.msi /qn /l*v install.log `
|
|||
REPOSITORY_CONNECTION="Server=sqlmon01;Database=SQLInsight;Integrated Security=true;Encrypt=true" `
|
||||
MONITOR_SERVER="sql01" `
|
||||
AGENT_ACCOUNT="DOMÆNE\svc-sqlinsight$" WEB_ACCOUNT="DOMÆNE\svc-sqlinsight-web$" WEB_PORT=5080 `
|
||||
ADMIN_GROUPS="DOMÆNE\SQL-Admins" `
|
||||
ADDLOCAL=ALL
|
||||
```
|
||||
|
||||
|
|
@ -33,6 +34,7 @@ msiexec /i SQLInsight-Pro-1.0.0-x64.msi /qn /l*v install.log `
|
|||
| `AGENT_ACCOUNT` | `NT SERVICE\SQLInsight Agent` | Tjenestekonto til agenten. Den virtuelle konto når andre servere som computerkontoen (`DOMÆNE\SERVER$`), så det er den, der skal have login og rettigheder på de overvågede instanser. En gMSA angives som `DOMÆNE\navn$` |
|
||||
| `WEB_ACCOUNT` | `NT SERVICE\SQLInsight Web` | Tjenestekonto til web. Skal have `db_datareader` på repositoriet |
|
||||
| `WEB_PORT` | `5080` | Port til webbrugerfladen. Installeren åbner den i Windows Firewall |
|
||||
| `ADMIN_GROUPS` | (ingen) | Windows-grupper, der må ændre, hvilke servere og databaser der overvåges, adskilt af `;` (fx `DOMÆNE\SQL-Admins;DOMÆNE\DBA`). Skrives som `SQLInsight:Web:AdminGroups` i webbens `sqlinsight.ini`. Uden den må den lokale gruppe Administratorer på webserveren (se [servere-og-databaser.md](servere-og-databaser.md#hvem-må-ændre-opsætningen)) |
|
||||
| `ADDLOCAL` | `Agent,Web` | `Agent`, `Web` og `UpdateTask` (planlagt opgave, der installerer hentede opdateringer som SYSTEM, se [opdatering.md](opdatering.md)). `ALL` = alle tre |
|
||||
| `SKIP_REPOSITORY_SETUP` | (ingen) | `1` = installeren opretter ikke database, logins og rettigheder. Brug det, når en DBA gør det (trin 2 og 3), eller når installationen kører som SYSTEM (SCCM/Intune) uden rettigheder på SQL Server |
|
||||
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ Alt, hvad webbrugerfladen viser, kan hentes som JSON under `/api/v1/` med de sam
|
|||
"ApiTokens": { "itsm": { "hash": "sha256:…", "scopes": [ "read", "support:write" ] } }
|
||||
```
|
||||
|
||||
og oprettes med `SQLInsight.Web.exe new-api-token itsm --scope read,support:write`. Windows-brugere med adgang til webbrugerfladen må det samme som i webbrugerfladen. Scopes: `read`, `support:write` (supportsager) og `config:write` (hvilke servere og databaser der overvåges; for Windows-brugere kræver det medlemskab af `SQLInsight:Web:AdminGroups`).
|
||||
og oprettes med `SQLInsight.Web.exe new-api-token itsm --scope read,support:write`. Windows-brugere med adgang til webbrugerfladen må det samme som i webbrugerfladen. Scopes: `read`, `support:write` (supportsager) og `config:write` (hvilke servere og databaser der overvåges; for Windows-brugere kræver det medlemskab af `SQLInsight:Web:AdminGroups`, eller af Administratorer på webserveren, når den er tom).
|
||||
|
||||
PowerShell:
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,9 @@ Alle med adgang til webbrugerfladen kan **se** opsætningen. Kun medlemmer af en
|
|||
}
|
||||
```
|
||||
|
||||
Er `AdminGroups` tom, kan ingen ændre noget i webbrugerfladen; siden viser så, hvor gruppen sættes. Genstart tjenesten `SQLInsight Web` efter en ændring.
|
||||
Installeren kan sætte gruppen med egenskaben `ADMIN_GROUPS` (flere grupper adskilt af `;`, fx `ADMIN_GROUPS="DOMÆNE\SQL-Admins;DOMÆNE\DBA"`); den skrives i webbens `sqlinsight.ini`. Genstart tjenesten `SQLInsight Web` efter en ændring.
|
||||
|
||||
Er `AdminGroups` tom (standard ved en installation uden `ADMIN_GROUPS`), kan medlemmer af den **lokale gruppe Administratorer på webserveren** (`BUILTIN\Administrators`, SID `S-1-5-32-544`) ændre opsætningen, så den første server kan tilføjes med det samme. Det afgøres ud fra brugerens Windows-grupper ved login. En Windows-login fra en anden computer (Negotiate/Kerberos) har den fulde token, så en domænekonto, der er administrator på webserveren (fx via Domain Admins), genkendes. Åbnes webbrugerfladen i en browser på selve webserveren, er brugerens token filtreret af UAC (Administratorer er kun *deny-only*), og så tæller det ikke; brug en anden computer, eller sæt `AdminGroups`. Så snart `AdminGroups` indeholder en gruppe, er det kun den, der gælder. Siden *Servere og databaser* viser, hvem der må ændre opsætningen.
|
||||
|
||||
## Tilføj en server
|
||||
|
||||
|
|
@ -69,7 +71,7 @@ Webbrugerfladens login har stadig kun `db_datareader` og ellers præcis det, den
|
|||
|
||||
## API
|
||||
|
||||
Alt på siden findes i API v1 (se [integration.md](integration.md#json-api)). Læsning kræver scope `read`; ændringer kræver scope `config:write` for tokens eller medlemskab af `AdminGroups` for Windows-brugere. Opret et token med `SQLInsight.Web.exe new-api-token automation --scope read,config:write`.
|
||||
Alt på siden findes i API v1 (se [integration.md](integration.md#json-api)). Læsning kræver scope `read`; ændringer kræver scope `config:write` for tokens eller medlemskab af `AdminGroups` for Windows-brugere (er den tom: den lokale gruppe Administratorer på webserveren). Opret et token med `SQLInsight.Web.exe new-api-token automation --scope read,config:write`.
|
||||
|
||||
| Endpoint | |
|
||||
|---|---|
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@
|
|||
which reaches other servers as the computer account (DOMAIN\SERVER$). A gMSA: DOMAIN\svc-sqlinsight$
|
||||
WEB_ACCOUNT Account for SQLInsight Web. Default NT SERVICE\SQLInsight Web
|
||||
WEB_PORT Port for the web UI (default 5080)
|
||||
ADMIN_GROUPS Windows groups that may change which servers and databases are monitored in the web UI,
|
||||
separated by ';' (e.g. DOMAIN\SQL-Admins). Written to the web's sqlinsight.ini as
|
||||
SQLInsight:Web:AdminGroups. Empty: the web server's local Administrators group may.
|
||||
SKIP_REPOSITORY_SETUP 1 = do not create the repository database, logins and rights (the admin does it, see
|
||||
docs/installation.md). Otherwise the installing user's Windows account does it.
|
||||
Features: Agent, Web (both by default) and UpdateTask under Agent (ADDLOCAL=ALL: installs downloaded updates hourly as SYSTEM).
|
||||
|
|
@ -36,6 +39,7 @@
|
|||
<Property Id="AGENT_ACCOUNT" Value="NT SERVICE\SQLInsight Agent" Secure="yes" />
|
||||
<Property Id="WEB_ACCOUNT" Value="NT SERVICE\SQLInsight Web" Secure="yes" />
|
||||
<Property Id="WEB_PORT" Value="5080" Secure="yes" />
|
||||
<Property Id="ADMIN_GROUPS" Secure="yes" />
|
||||
<Property Id="SKIP_REPOSITORY_SETUP" Secure="yes" />
|
||||
<!-- Set when the update task was installed by an earlier version, so an upgrade keeps it (see the UpdateTask feature). -->
|
||||
<Property Id="UPDATETASK_INSTALLED">
|
||||
|
|
@ -135,6 +139,13 @@
|
|||
<IniFile Id="WebIniUrl" Name="sqlinsight.ini" Directory="WEBDIR" Action="addLine"
|
||||
Section="Kestrel:Endpoints:Http" Key="Url" Value="http://*:[WEB_PORT]" />
|
||||
</Component>
|
||||
<!-- Who may change which servers and databases are monitored. Without it the web server's local Administrators may. -->
|
||||
<Component Id="WebIniAdminGroups" Guid="0B7E3E43-2C42-4F55-9A4C-6D1E0B6F2A15" Win64="yes" Permanent="yes">
|
||||
<Condition>ADMIN_GROUPS</Condition>
|
||||
<CreateFolder />
|
||||
<IniFile Id="WebIniAdminGroupsLine" Name="sqlinsight.ini" Directory="WEBDIR" Action="addLine"
|
||||
Section="SQLInsight:Web:AdminGroups" Key="0" Value="[ADMIN_GROUPS]" />
|
||||
</Component>
|
||||
<Component Id="WebServiceStart" Guid="0B7E3E43-2C42-4F55-9A4C-6D1E0B6F2A14" Win64="yes">
|
||||
<Condition>REPOSITORY_CONNECTION OR OLDVERSIONFOUND</Condition>
|
||||
<CreateFolder />
|
||||
|
|
@ -213,6 +224,7 @@
|
|||
<ComponentRef Id="WebService" />
|
||||
<ComponentRef Id="WebSettings" />
|
||||
<ComponentRef Id="WebIni" />
|
||||
<ComponentRef Id="WebIniAdminGroups" />
|
||||
<ComponentRef Id="WebServiceStart" />
|
||||
<ComponentGroupRef Id="WebFiles" />
|
||||
</Feature>
|
||||
|
|
|
|||
|
|
@ -28,10 +28,11 @@ sed -i 's/"MaxParallelInstances": 4/"MaxParallelInstances": 7/' "$P/Agent/appset
|
|||
printf '[SQLInsight:Instances:1]\nName=manuel\nServer=sql02\n' >> "$P/Agent/sqlinsight.ini"
|
||||
files_before=$(find "$P" -type f | wc -l)
|
||||
|
||||
msi /i "$NEW" /qn
|
||||
msi /i "$NEW" /qn ADMIN_GROUPS="FIRMA\SQL-Admins;FIRMA\DBA"
|
||||
check "opgradering" "[ $? = 0 ]"
|
||||
check "ændret appsettings.json er bevaret" "grep -q '\"MaxParallelInstances\": 7' '$P/Agent/appsettings.json'"
|
||||
check "ændret sqlinsight.ini er bevaret" "grep -q 'Server=sql02' '$P/Agent/sqlinsight.ini'"
|
||||
check "ADMIN_GROUPS i webbens sqlinsight.ini" "grep -q 'SQL-Admins;FIRMA' '$P/Web/sqlinsight.ini'"
|
||||
entries=$(reg 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall' /s | grep -c 'REG_SZ SQLInsight Pro')
|
||||
check "én post under Programmer ($entries)" "[ $entries = 1 ]"
|
||||
check "alle filer er der stadig" "[ \$(find '$P' -type f | wc -l) -ge $files_before ]"
|
||||
|
|
|
|||
|
|
@ -31,8 +31,8 @@ public sealed record InstanceDiscoveryBody(string? Host);
|
|||
|
||||
/// <summary>
|
||||
/// Which instances and databases are monitored, through the API: everything the page Servere og databaser does.
|
||||
/// Reading needs the scope read; changing needs config:write for tokens, or membership of SQLInsight:Web:AdminGroups
|
||||
/// for Windows users. Connection tests and discovery are queued for the agent (202 Accepted with a request id to
|
||||
/// Reading needs the scope read; changing needs config:write for tokens, or membership of SQLInsight:Web:AdminGroups (the
|
||||
/// web server's local Administrators while that is empty) for Windows users. Connection tests and discovery are queued for the agent (202 Accepted with a request id to
|
||||
/// follow at /api/v1/agent-requests/{id}). Passwords are write-only and never returned.
|
||||
/// </summary>
|
||||
public static class ConfigurationApiEndpoints
|
||||
|
|
@ -41,7 +41,7 @@ public static class ConfigurationApiEndpoints
|
|||
{
|
||||
ArgumentNullException.ThrowIfNull(app);
|
||||
var api = app.MapGroup("/api/v1").DisableAntiforgery().WithTags("SQLInsight Pro API v1").RequireRead();
|
||||
const string write = " Needs scope config:write for tokens, or SQLInsight:Web:AdminGroups for Windows users.";
|
||||
const string write = " Needs scope config:write for tokens, or for Windows users SQLInsight:Web:AdminGroups (empty: the web server's local Administrators).";
|
||||
|
||||
api.MapGet("/monitored-instances", ListAsync).WithSummary("Overvågede instanser / Monitored instances (configuration)");
|
||||
api.MapPost("/monitored-instances", AddAsync)
|
||||
|
|
@ -77,7 +77,7 @@ public static class ConfigurationApiEndpoints
|
|||
}
|
||||
|
||||
private static IResult Forbidden() => Results.Problem(statusCode: StatusCodes.Status403Forbidden, title: "Ikke tilladt",
|
||||
detail: "Ændringer kræver scope \"config:write\" (API-token) eller medlemskab af en gruppe i SQLInsight:Web:AdminGroups.");
|
||||
detail: "Ændringer kræver scope \"config:write\" (API-token) eller medlemskab af en gruppe i SQLInsight:Web:AdminGroups (er den tom: den lokale gruppe Administratorer på webserveren).");
|
||||
|
||||
private static IResult Invalid(string detail) => Results.Problem(statusCode: StatusCodes.Status422UnprocessableEntity, title: "Ugyldig", detail: detail);
|
||||
|
||||
|
|
|
|||
|
|
@ -24,13 +24,11 @@
|
|||
}
|
||||
@if (!_mayChange)
|
||||
{
|
||||
<p class="notice" role="note">
|
||||
Du kan se opsætningen, men kun administratorer kan ændre den.
|
||||
@if (Options.Value.AdminGroups.Count == 0)
|
||||
{
|
||||
<span>Der er ikke sat nogen administratorgruppe op endnu: tilføj en Windows-gruppe under <code>SQLInsight:Web:AdminGroups</code> i SQLInsight Webs <code>appsettings.json</code>.</span>
|
||||
}
|
||||
</p>
|
||||
<p class="notice" role="note">Du kan se opsætningen, men kun administratorer kan ændre den. @InstanceSetup.WhoMayChangeText(Options.Value)</p>
|
||||
}
|
||||
else if (Options.Value.AdminGroupNames.Count == 0 && !string.Equals(Options.Value.Authentication, "None", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
<p class="muted" role="note">@InstanceSetup.WhoMayChangeText(Options.Value)</p>
|
||||
}
|
||||
@if (_agentKey is null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ public sealed record InstanceInput(
|
|||
public static class InstanceSetup
|
||||
{
|
||||
public const string NotAdminText =
|
||||
"Kun administratorer kan ændre, hvilke servere og databaser der overvåges. Bed en administrator om at tilføje jeres gruppe under SQLInsight:Web:AdminGroups.";
|
||||
"Kun administratorer kan ændre, hvilke servere og databaser der overvåges (SQLInsight:Web:AdminGroups, eller den lokale gruppe Administratorer på webserveren, når den er tom). Bed en administrator om at tilføje jeres gruppe.";
|
||||
|
||||
public const string NoAgentKeyText =
|
||||
"Agenten har ikke meldt sig endnu, så adgangskoden kan ikke krypteres til den. Start tjenesten SQLInsight Agent (version 1.3 eller nyere), og prøv igen om lidt.";
|
||||
|
|
@ -39,8 +39,14 @@ public static class InstanceSetup
|
|||
|
||||
/// <summary>
|
||||
/// Whether the user may change the configuration: an API token needs the scope config:write; a Windows user must be
|
||||
/// in one of SQLInsight:Web:AdminGroups. With authentication off (development only) everyone may.
|
||||
/// in one of SQLInsight:Web:AdminGroups or, while that is empty, in the web server's local Administrators group.
|
||||
/// With authentication off (development only) everyone may.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Administrators is recognised by its well-known SID among the user's group SIDs (ClaimTypes.GroupSid). A remote
|
||||
/// Windows (Negotiate) login carries the full token, so a member has the SID as a group; a UAC-filtered local token
|
||||
/// has it only as deny-only (ClaimTypes.DenyOnlySid), which does not count.
|
||||
/// </remarks>
|
||||
public static bool MayChange(ClaimsPrincipal user, WebOptions options)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(user);
|
||||
|
|
@ -55,7 +61,21 @@ public static class InstanceSetup
|
|||
return true;
|
||||
}
|
||||
|
||||
return options.AdminGroups.Any(user.IsInRole);
|
||||
var groups = options.AdminGroupNames;
|
||||
return groups.Count > 0
|
||||
? groups.Any(user.IsInRole)
|
||||
: user.Identity?.IsAuthenticated == true && user.HasClaim(ClaimTypes.GroupSid, WebOptions.BuiltinAdministratorsSid);
|
||||
}
|
||||
|
||||
/// <summary>Who may change the configuration (Danish), for the page.</summary>
|
||||
public static string WhoMayChangeText(WebOptions options)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(options);
|
||||
var groups = options.AdminGroupNames;
|
||||
return groups.Count > 0
|
||||
? "Opsætningen kan ændres af medlemmer af " + string.Join(", ", groups) + " (SQLInsight:Web:AdminGroups)."
|
||||
: "SQLInsight:Web:AdminGroups er tom, så opsætningen kan ændres af den lokale gruppe Administratorer på webserveren (BUILTIN\\Administrators). "
|
||||
+ "Sæt en Windows-gruppe i AdminGroups i SQLInsight Webs appsettings.json (eller ADMIN_GROUPS i installeren) for at give andre adgang.";
|
||||
}
|
||||
|
||||
private static string? Validate(InstanceInput input)
|
||||
|
|
|
|||
|
|
@ -11,11 +11,20 @@ public sealed class WebOptions
|
|||
public IList<string> AllowedGroups { get; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Windows groups that may change which servers and databases are monitored (Konfiguration og sikkerhed → Servere
|
||||
/// og databaser), e.g. DOMAIN\SQL-Admins. Empty: nobody can change it in the UI; everyone with access can see it.
|
||||
/// Windows groups that may change which servers and databases are monitored (Overvågning → Servere og databaser),
|
||||
/// e.g. DOMAIN\SQL-Admins. An entry may hold several groups separated by ';' (the installer's ADMIN_GROUPS writes one
|
||||
/// entry). Empty: the web server's local Administrators group (BUILTIN\Administrators) may change it. Everyone with
|
||||
/// access to the UI can see it.
|
||||
/// </summary>
|
||||
public IList<string> AdminGroups { get; } = [];
|
||||
|
||||
/// <summary>The well-known SID of BUILTIN\Administrators: who may change the configuration while AdminGroups is empty.</summary>
|
||||
public const string BuiltinAdministratorsSid = "S-1-5-32-544";
|
||||
|
||||
/// <summary>The groups in <see cref="AdminGroups"/>, with entries like "A;B" split.</summary>
|
||||
public IReadOnlyList<string> AdminGroupNames =>
|
||||
AdminGroups.SelectMany(g => (g ?? string.Empty).Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)).ToList();
|
||||
|
||||
/// <summary>Time zone for display, e.g. Europe/Copenhagen. Defaults to the server's local time zone.</summary>
|
||||
public string? TimeZone { get; set; }
|
||||
|
||||
|
|
|
|||
|
|
@ -273,5 +273,27 @@ public sealed partial class InstanceSetupWebTests(SqlServerFixture sql) : IAsync
|
|||
Assert.False(InstanceSetup.MayChange(User(new Claim(ApiTokenHandler.ClaimType, "grafana"), new Claim(ApiTokenHandler.ScopeClaimType, ApiToken.Read),
|
||||
new Claim(ClaimTypes.Role, @"FIRMA\SQL-Admins")), windows));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Without_admin_groups_the_local_administrators_may_change_the_configuration()
|
||||
{
|
||||
static ClaimsPrincipal User(params Claim[] claims) => new(new ClaimsIdentity(claims, "Negotiate", ClaimTypes.Name, ClaimTypes.Role));
|
||||
var empty = new WebOptions { Authentication = "Windows" };
|
||||
var administrator = User(new Claim(ClaimTypes.GroupSid, WebOptions.BuiltinAdministratorsSid));
|
||||
|
||||
// A remote Windows login carries the full token: Administrators is a group SID.
|
||||
Assert.True(InstanceSetup.MayChange(administrator, empty));
|
||||
// A UAC-filtered token has it only as deny-only, and that does not count.
|
||||
Assert.False(InstanceSetup.MayChange(User(new Claim(ClaimTypes.DenyOnlySid, WebOptions.BuiltinAdministratorsSid)), empty));
|
||||
Assert.False(InstanceSetup.MayChange(User(new Claim(ClaimTypes.GroupSid, "S-1-5-32-545")), empty));
|
||||
Assert.False(InstanceSetup.MayChange(new ClaimsPrincipal(new ClaimsIdentity([new Claim(ClaimTypes.GroupSid, WebOptions.BuiltinAdministratorsSid)])), empty));
|
||||
|
||||
// Once groups are set they decide alone; ADMIN_GROUPS from the installer may list several separated by ';'.
|
||||
var groups = new WebOptions { Authentication = "Windows" };
|
||||
groups.AdminGroups.Add(@"FIRMA\SQL-Admins; FIRMA\DBA");
|
||||
Assert.False(InstanceSetup.MayChange(administrator, groups));
|
||||
Assert.True(InstanceSetup.MayChange(User(new Claim(ClaimTypes.Role, @"FIRMA\DBA")), groups));
|
||||
Assert.Equal([@"FIRMA\SQL-Admins", @"FIRMA\DBA"], groups.AdminGroupNames);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue