Ship SEOSoft's license key k20261001; agent defaults to the real hub
Some checks are pending
CI / Integration (mcr.microsoft.com/mssql/server:2017-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2019-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2022-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2025-latest) (push) Blocked by required conditions
CI / Build og unit tests (push) Successful in 1m13s
deploy community / deploy (push) Successful in 0s
deploy hub / deploy (push) Successful in 20s
CI / MSI-installer (push) Successful in 43s

- trusted-keys.json: the public key from keygen (k20261001). The hub and
  every agent built from now on verify licenses with it; no
  TrustedKeys configuration is needed any more.
- The test that required an empty key file now checks what it was
  guarding against: every embedded key must be keygen output (id
  k<yyyyMMdd>, P-256 public key), never a test key.
- Agent: default Hub:Url https://hub.sqlinsight.seosoft.dk/ (was
  hub.seosoft.dk, which does not exist).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Søren Eggert Lundsteen Olsen 2026-10-01 07:15:47 +02:00
parent bffb17cc63
commit b9afc30436
4 changed files with 21 additions and 7 deletions

View file

@ -10,7 +10,7 @@ public sealed class HubOptions
public bool Enabled { get; set; }
/// <summary>Base URL of SQLInsight Hub, e.g. https://hub.seosoft.dk/.</summary>
/// <summary>Base URL of SQLInsight Hub, e.g. https://hub.sqlinsight.seosoft.dk/.</summary>
public Uri? Url { get; set; }
/// <summary>The license text (SQLI1.…).</summary>

View file

@ -15,7 +15,7 @@
},
"Hub": {
"Enabled": false,
"Url": "https://hub.seosoft.dk/",
"Url": "https://hub.sqlinsight.seosoft.dk/",
"License": null,
"SendSensitiveEvidence": false,
"SendServerName": false,

View file

@ -1,4 +1,6 @@
{
"comment": "Public keys trusted to sign licenses and update manifests. Deliberately empty in source: the vendor generates a key pair with SQLInsight.LicenseTool (keygen) and adds the PUBLIC key here before building a release. The private key never goes into the repository.",
"keys": []
"comment": "Public keys trusted to sign licenses and update manifests. The vendor generates a key pair with SQLInsight.LicenseTool (keygen) and adds the PUBLIC key here. The private key never goes into the repository.",
"keys": [
{ "id": "k20261001", "publicKey": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPXpGrrrvlDm3UvDqAILGpEcxoHsJ1M+blxLmBdWofjYZsg5q7B1ELG9Vw4cf9B6k9ngoJtA4SNx7HbctNwJAyQ==" }
]
}

View file

@ -117,9 +117,21 @@ public class LicenseTests
}
[Fact]
public void Source_ships_without_trusted_keys()
public void Embedded_keys_are_vendor_public_keys()
{
// The vendor adds its public key before a release; a published test key would let anyone mint licenses.
Assert.Equal(0, TrustedKeys.Embedded().Count);
// Only SEOSoft's keygen public keys may ship. A test key (whose private half is in a repository) would let
// anyone mint licenses, so every entry must look like keygen output: id k<yyyyMMdd> and a P-256 public key.
using var stream = typeof(TrustedKeys).Assembly.GetManifestResourceStream("SQLInsight.Licensing.trusted-keys.json")!;
using var document = System.Text.Json.JsonDocument.Parse(stream);
var keys = document.RootElement.GetProperty("keys").EnumerateArray().ToList();
Assert.Equal(keys.Count, TrustedKeys.Embedded().Count);
foreach (var entry in keys)
{
Assert.Matches(@"^k\d{8}$", entry.GetProperty("id").GetString());
using var key = ECDsa.Create();
key.ImportSubjectPublicKeyInfo(Convert.FromBase64String(entry.GetProperty("publicKey").GetString()!), out _);
Assert.Equal(256, key.KeySize);
}
}
}