fix(host): resolve Host-003,004 — replace plaintext secrets with env placeholders, validate site seed-node ports; re-triage Host-002
This commit is contained in:
@@ -40,23 +40,55 @@ public static class StartupValidator
|
||||
errors.Add("ScadaLink:Security:JwtSigningKey required for Central");
|
||||
}
|
||||
|
||||
var seedNodes = configuration.GetSection("ScadaLink:Cluster:SeedNodes").Get<List<string>>();
|
||||
if (seedNodes == null || seedNodes.Count < 2)
|
||||
errors.Add("ScadaLink:Cluster:SeedNodes must have at least 2 entries");
|
||||
|
||||
if (role == "Site")
|
||||
{
|
||||
var grpcPortStr = nodeSection["GrpcPort"];
|
||||
if (grpcPortStr != null && (!int.TryParse(grpcPortStr, out var gp) || gp < 1 || gp > 65535))
|
||||
int grpcPort = 8083; // NodeOptions default when the key is absent
|
||||
if (grpcPortStr != null && (!int.TryParse(grpcPortStr, out grpcPort) || grpcPort < 1 || grpcPort > 65535))
|
||||
errors.Add("ScadaLink:Node:GrpcPort must be 1-65535");
|
||||
|
||||
var dbSection = configuration.GetSection("ScadaLink:Database");
|
||||
if (string.IsNullOrEmpty(dbSection["SiteDbPath"]))
|
||||
errors.Add("ScadaLink:Database:SiteDbPath required for Site nodes");
|
||||
}
|
||||
|
||||
var seedNodes = configuration.GetSection("ScadaLink:Cluster:SeedNodes").Get<List<string>>();
|
||||
if (seedNodes == null || seedNodes.Count < 2)
|
||||
errors.Add("ScadaLink:Cluster:SeedNodes must have at least 2 entries");
|
||||
// Host-004: a seed node must reference an Akka.Remote endpoint, never the
|
||||
// Kestrel HTTP/2 gRPC port. A seed entry whose port equals this node's
|
||||
// GrpcPort would make a joining node attempt an Akka.Remote TCP
|
||||
// association against the gRPC listener and fail.
|
||||
if (seedNodes != null)
|
||||
{
|
||||
foreach (var seed in seedNodes)
|
||||
{
|
||||
if (SeedNodePort(seed) == grpcPort)
|
||||
errors.Add(
|
||||
$"ScadaLink:Cluster:SeedNodes entry '{seed}' must not target the gRPC port " +
|
||||
$"({grpcPort}); seed nodes must reference Akka remoting ports");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (errors.Count > 0)
|
||||
throw new InvalidOperationException(
|
||||
$"Configuration validation failed:\n{string.Join("\n", errors.Select(e => $" - {e}"))}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts the TCP port from an Akka seed-node address of the form
|
||||
/// <c>akka.tcp://system@host:port</c>. Returns <c>-1</c> when no port can be parsed.
|
||||
/// </summary>
|
||||
private static int SeedNodePort(string seedNode)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(seedNode))
|
||||
return -1;
|
||||
|
||||
var lastColon = seedNode.LastIndexOf(':');
|
||||
if (lastColon < 0 || lastColon == seedNode.Length - 1)
|
||||
return -1;
|
||||
|
||||
return int.TryParse(seedNode[(lastColon + 1)..], out var port) ? port : -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,10 @@
|
||||
"FailureDetectionThreshold": "00:00:10",
|
||||
"MinNrOfMembers": 1
|
||||
},
|
||||
"_secrets": "Host-003: Secrets are NOT committed in this file. Supply them via environment variables, which the Host's configuration builder (AddEnvironmentVariables) overlays over this file. Required: ScadaLink__Database__ConfigurationDb, ScadaLink__Database__MachineDataDb, ScadaLink__Security__LdapServiceAccountPassword, ScadaLink__Security__JwtSigningKey. The ${...} placeholders below are intentionally non-functional and must be overridden per environment.",
|
||||
"Database": {
|
||||
"ConfigurationDb": "Server=localhost,1433;Database=ScadaLinkConfig;User Id=scadalink_app;Password=ScadaLink_Dev1#;TrustServerCertificate=true",
|
||||
"MachineDataDb": "Server=localhost,1433;Database=ScadaLinkMachineData;User Id=scadalink_app;Password=ScadaLink_Dev1#;TrustServerCertificate=true"
|
||||
"ConfigurationDb": "${SCADALINK_CONFIGURATIONDB_CONNECTION_STRING}",
|
||||
"MachineDataDb": "${SCADALINK_MACHINEDATADB_CONNECTION_STRING}"
|
||||
},
|
||||
"Security": {
|
||||
"LdapServer": "localhost",
|
||||
@@ -27,8 +28,8 @@
|
||||
"AllowInsecureLdap": true,
|
||||
"LdapSearchBase": "dc=scadalink,dc=local",
|
||||
"LdapServiceAccountDn": "cn=admin,dc=scadalink,dc=local",
|
||||
"LdapServiceAccountPassword": "password",
|
||||
"JwtSigningKey": "scadalink-dev-jwt-signing-key-must-be-at-least-32-characters-long",
|
||||
"LdapServiceAccountPassword": "${SCADALINK_LDAP_SERVICE_ACCOUNT_PASSWORD}",
|
||||
"JwtSigningKey": "${SCADALINK_JWT_SIGNING_KEY}",
|
||||
"JwtExpiryMinutes": 15,
|
||||
"IdleTimeoutMinutes": 30
|
||||
},
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"Cluster": {
|
||||
"SeedNodes": [
|
||||
"akka.tcp://scadalink@localhost:8082",
|
||||
"akka.tcp://scadalink@localhost:8083"
|
||||
"akka.tcp://scadalink@localhost:8084"
|
||||
],
|
||||
"SplitBrainResolverStrategy": "keep-oldest",
|
||||
"StableAfter": "00:00:15",
|
||||
|
||||
Reference in New Issue
Block a user