feat(cluster): opt-in Akka.Remote TLS block, default off (ScadaBridge#64)
ScadaBridge:Cluster:RemotingTls (ClusterRemotingTlsOptions) renders
akka.remote.dot-netty.tcp enable-ssl + ssl { certificate, mutual auth,
hostname validation } only when Enabled. Chain validation is always on
(no suppress-validation switch). Certificate is a .pfx path + password
(${secret:} reference) or an OS-store thumbprint.
ClusterOptionsValidator, TLS on only: exactly one certificate source, a
legal store location, and akka.ssl.tcp:// on every seed - Akka.Remote
advertises akka.ssl.tcp addresses once SSL is on, so an akka.tcp seed is
never answered and the join retries forever with no startup error.
ClusterBootstrapGuard.SeedPattern accepted only one scheme segment, so
akka.ssl.tcp seeds parsed as nothing and the guard would silently never
engage on a TLS pair; it now accepts any number.
Default HOCON is byte-identical: 12 rendered documents (3 downing
strategies x guard on/off x Central/Site) captured before the change and
diffed after, all identical; a permanent test pins that a disabled block
with every field set renders the default document. A real ActorSystem
booted on the emitted TLS document comes up as akka.ssl.tcp (mutation
check: forcing enable-ssl=false turns it red).
Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
This commit is contained in:
@@ -132,6 +132,8 @@ public class ClusterBootstrapGuardTests
|
||||
[InlineData("akka.tcp://scadabridge@scadabridge-site-a-a:8082", "scadabridge-site-a-a", 8082)]
|
||||
[InlineData("akka.tcp://scadabridge@10.0.0.5:8082/", "10.0.0.5", 8082)]
|
||||
[InlineData(" akka.tcp://scadabridge@host.lan:1234 ", "host.lan", 1234)]
|
||||
// Akka.Remote's scheme once dot-netty TLS is on (ScadaBridge#64) has TWO dot segments.
|
||||
[InlineData("akka.ssl.tcp://scadabridge@scadabridge-site-a-a:8082", "scadabridge-site-a-a", 8082)]
|
||||
public void TryParse_extracts_host_and_port(string seed, string expectedHost, int expectedPort)
|
||||
{
|
||||
Assert.True(ClusterBootstrapGuard.TryParse(seed, out var host, out var port));
|
||||
@@ -150,6 +152,25 @@ public class ClusterBootstrapGuardTests
|
||||
Assert.False(ClusterBootstrapGuard.TryParse(seed, out _, out _));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies the guard still engages for a TLS pair (ScadaBridge#64): with <c>akka.ssl.tcp</c>
|
||||
/// seeds the old single-segment scheme pattern parsed nothing, so <c>Applies</c> was false and
|
||||
/// the guard silently never ran.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Analyze_engages_for_a_tls_pair()
|
||||
{
|
||||
const string tlsA = "akka.ssl.tcp://scadabridge@scadabridge-site-a-a:8082";
|
||||
const string tlsB = "akka.ssl.tcp://scadabridge@scadabridge-site-a-b:8082";
|
||||
|
||||
var role = ClusterBootstrapGuard.Analyze(new[] { tlsB, tlsA }, "scadabridge-site-a-b", 8082);
|
||||
|
||||
Assert.True(role.Applies);
|
||||
Assert.False(role.IsFounder);
|
||||
Assert.Equal("scadabridge-site-a-a", role.PartnerHost);
|
||||
Assert.Equal(new[] { tlsB, tlsA }, role.SelfFirstOrder);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the port decides the tie-break when the hosts are equal.</summary>
|
||||
[Fact]
|
||||
public void Port_participates_in_the_tie_break_when_hosts_are_equal()
|
||||
|
||||
Reference in New Issue
Block a user