diff --git a/src/core/Akka.Remote.Tests/Transport/DotNettySslSupportSpec.cs b/src/core/Akka.Remote.Tests/Transport/DotNettySslSupportSpec.cs index 782cd13268f..2614faf5088 100644 --- a/src/core/Akka.Remote.Tests/Transport/DotNettySslSupportSpec.cs +++ b/src/core/Akka.Remote.Tests/Transport/DotNettySslSupportSpec.cs @@ -6,12 +6,15 @@ //----------------------------------------------------------------------- using System; +using System.Linq; +using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; using Akka.Actor; using Akka.Configuration; using Akka.TestKit; using Akka.TestKit.Xunit2.Attributes; +using FluentAssertions; using Xunit; using Xunit.Abstractions; using static Akka.Util.RuntimeDetector; @@ -23,7 +26,7 @@ public class DotNettySslSupportSpec : AkkaSpec #region Setup / Config // valid to 01/01/2037 - private static readonly string ValidCertPath = "Resources/akka-validcert.pfx"; + private const string ValidCertPath = "Resources/akka-validcert.pfx"; private const string Password = "password"; @@ -54,6 +57,32 @@ private static Config TestConfig(string certPath, string password) }"); } + private static Config TestConfig(bool enableSsl, string certPath, string password) + { + var config = ConfigurationFactory.ParseString(@" + akka { + loglevel = DEBUG + actor.provider = ""Akka.Remote.RemoteActorRefProvider,Akka.Remote"" + remote { + dot-netty.tcp { + port = 0 + hostname = ""127.0.0.1"" + enable-ssl = """ + enableSsl.ToString().ToLowerInvariant() + @""" + log-transport = true + } + } + }"); + return !enableSsl + ? config + : config.WithFallback(@"akka.remote.dot-netty.tcp.ssl { + suppress-validation = """ + enableSsl.ToString().ToLowerInvariant() + @""" + certificate { + path = """ + certPath + @""" + password = """ + password + @""" + } + }"); + } + private static Config TestThumbprintConfig(string thumbPrint) { var config = ConfigurationFactory.ParseString(@" @@ -100,6 +129,18 @@ private void Setup(string certPath, string password) _echoPath = new RootActorPath(_address2) / "user" / "echo"; } + private void Setup(bool enableSsl, string certPath, string password) + { + _sys2 = ActorSystem.Create("sys2", TestConfig(enableSsl, certPath, password)); + InitializeLogger(_sys2); + + var echo = _sys2.ActorOf(Props.Create(), "echo"); + + _address1 = RARP.For(Sys).Provider.DefaultAddress; + _address2 = RARP.For(_sys2).Provider.DefaultAddress; + _echoPath = new RootActorPath(_address2) / "user" / "echo"; + } + private void SetupThumbprint(string certPath, string password) { InstallCert(); @@ -180,6 +221,50 @@ await Assert.ThrowsAsync(async () => }); } + [Fact] + public async Task If_EnableSsl_configuration_is_true_but_not_valid_certificate_is_provided_than_ArgumentNullException_should_be_thrown() + { + // skip this test due to linux/mono certificate issues + if (IsMono) return; + + var aggregateException = await Assert.ThrowsAsync(async () => + { + Setup(true, null, Password); + }); + + var realException = GetInnerMostException(aggregateException); + Assert.NotNull(realException); + Assert.Equal("Path to SSL certificate was not found (by default it can be found under `akka.remote.dot-netty.tcp.ssl.certificate.path`) (Parameter 'certificatePath')", realException.Message); + } + + [Fact] + public async Task If_EnableSsl_configuration_is_true_but_not_valid_certificate_password_is_provided_than_WindowsCryptographicException_should_be_thrown() + { + // skip this test due to linux/mono certificate issues + if (IsMono) return; + + var aggregateException = await Assert.ThrowsAsync(async () => + { + Setup(true, ValidCertPath, null); + }); + + var realException = GetInnerMostException(aggregateException); + Assert.NotNull(realException); + // TODO: this error message is not correct, but wanted to keep this assertion here in case someone else + // wants to fix it in the future. + //Assert.Equal("The specified network password is not correct.", realException.Message); + } + + [Theory] + [InlineData(ValidCertPath, null)] + [InlineData(null, Password)] + [InlineData(null, null)] + [InlineData(ValidCertPath, Password)] + public void If_EnableSsl_configuration_is_false_than_no_exception_should_be_thrown_even_no_cert_detail_were_provided(string certPath, string password) + { + Setup(false, certPath, password); + } + #region helper classes / methods protected override async Task AfterAllAsync() @@ -214,6 +299,17 @@ private void RemoveCert() } } + private T GetInnerMostException(Exception ex) where T : Exception + { + Exception currentEx = ex; + while (currentEx.InnerException != null) + { + currentEx = currentEx.InnerException; + } + + return currentEx as T; + } + private class Echo : ReceiveActor { public Echo() diff --git a/src/core/Akka.Remote/Transport/DotNetty/DotNettyTransportSettings.cs b/src/core/Akka.Remote/Transport/DotNetty/DotNettyTransportSettings.cs index d680534a1b8..5c8e0753926 100644 --- a/src/core/Akka.Remote/Transport/DotNetty/DotNettyTransportSettings.cs +++ b/src/core/Akka.Remote/Transport/DotNetty/DotNettyTransportSettings.cs @@ -76,9 +76,11 @@ public static DotNettyTransportSettings Create(Config config) var batchWriterSettings = new BatchWriterSettings(config.GetConfig("batching")); + var enableSsl = config.GetBoolean("enable-ssl", false); + return new DotNettyTransportSettings( transportMode: transportMode == "tcp" ? TransportMode.Tcp : TransportMode.Udp, - enableSsl: config.GetBoolean("enable-ssl", false), + enableSsl: enableSsl, connectTimeout: config.GetTimeSpan("connection-timeout", TimeSpan.FromSeconds(15)), hostname: host, publicHostname: !string.IsNullOrEmpty(publicHost) ? publicHost : host, @@ -87,7 +89,7 @@ public static DotNettyTransportSettings Create(Config config) serverSocketWorkerPoolSize: ComputeWorkerPoolSize(config.GetConfig("server-socket-worker-pool")), clientSocketWorkerPoolSize: ComputeWorkerPoolSize(config.GetConfig("client-socket-worker-pool")), maxFrameSize: ToNullableInt(config.GetByteSize("maximum-frame-size", null)) ?? 128000, - ssl: config.HasPath("ssl") ? SslSettings.Create(config.GetConfig("ssl")) : SslSettings.Empty, + ssl: config.HasPath("ssl") && enableSsl ? SslSettings.Create(config.GetConfig("ssl")) : SslSettings.Empty, dnsUseIpv6: config.GetBoolean("dns-use-ipv6", false), tcpReuseAddr: ResolveTcpReuseAddrOption(config.GetString("tcp-reuse-addr", "off-for-windows")), tcpKeepAlive: config.GetBoolean("tcp-keepalive", true),