fix(host): central https URLs get TLS from a node certificate or refuse to boot (ScadaBridge#108)
Central re-bound its HTTP surface from ASPNETCORE_URLS keeping only the port, so https://+:443 silently served plaintext HTTP. The parser now keeps the scheme (ParseHttpBindEndpoints); an https entry binds UseHttps with the node certificate from the new ScadaBridge:Node:Tls section (CertificatePath + CertificatePassword, or CertificateThumbprint + StoreName + StoreLocation; default unset) and the node refuses to boot, with a clear error, when https is requested without a certificate. http entries bind exactly as before. Tests: scheme parsing, refuse-to-boot, http endpoint config identical to the legacy inline binding (reflected effective Kestrel listen options), and a real Kestrel HTTPS listener with a generated .pfx serving h2 and HTTP/1.1 by ALPN while plaintext to the TLS port fails. Reverting the fix turns the refuse-to-boot and real-listener tests red. Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
This commit is contained in:
@@ -36,4 +36,38 @@ public class NodeOptions
|
||||
/// with the Akka remoting port a site node binds.
|
||||
/// </summary>
|
||||
public int MetricsPort { get; set; } = 8084;
|
||||
|
||||
/// <summary>
|
||||
/// This node's own TLS certificate for the Kestrel HTTP surface (ScadaBridge#108, design
|
||||
/// 2026-11-09-tls-production-profile stage 1). Default: nothing set. Required if (and only if)
|
||||
/// <c>ASPNETCORE_URLS</c> contains an <c>https://</c> entry; central then refuses to boot
|
||||
/// without it. Applies to central's HTTP/1.1+HTTP/2 surface only; the gRPC listener is unchanged.
|
||||
/// </summary>
|
||||
public NodeTlsOptions Tls { get; set; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Certificate source for <see cref="NodeOptions.Tls"/>: a .pfx path plus password, OR a
|
||||
/// certificate-store thumbprint. Same shape as the cluster's <c>RemotingTls</c> certificate fields.
|
||||
/// </summary>
|
||||
public sealed class NodeTlsOptions
|
||||
{
|
||||
/// <summary>Path to a PKCS#12 (.pfx) file with the private key. Set this OR <see cref="CertificateThumbprint"/>.</summary>
|
||||
public string? CertificatePath { get; set; }
|
||||
|
||||
/// <summary>Password for <see cref="CertificatePath"/>; write it as <c>${secret:NAME}</c>, never a literal.</summary>
|
||||
public string? CertificatePassword { get; set; }
|
||||
|
||||
/// <summary>Thumbprint of a certificate in the OS store. Set this OR <see cref="CertificatePath"/>.</summary>
|
||||
public string? CertificateThumbprint { get; set; }
|
||||
|
||||
/// <summary>Store name for <see cref="CertificateThumbprint"/>. Default <c>My</c>.</summary>
|
||||
public string StoreName { get; set; } = "My";
|
||||
|
||||
/// <summary>Store location for <see cref="CertificateThumbprint"/>: <c>local-machine</c> (default) or <c>current-user</c>.</summary>
|
||||
public string StoreLocation { get; set; } = "local-machine";
|
||||
|
||||
/// <summary>Whether any certificate source field is set.</summary>
|
||||
public bool IsConfigured =>
|
||||
!string.IsNullOrWhiteSpace(CertificatePath) || !string.IsNullOrWhiteSpace(CertificateThumbprint);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user