Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
277 changes: 168 additions & 109 deletions docs/articles/remoting/security.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -860,12 +860,34 @@ namespace Akka.Remote.Transport
}
namespace Akka.Remote.Transport.DotNetty
{
[System.Runtime.CompilerServices.NullableAttribute(0)]
public class static CertificateValidation
{
public static Akka.Remote.Transport.DotNetty.CertificateValidationCallback ChainPlusThen([System.Runtime.CompilerServices.NullableAttribute(new byte[] {
1,
2,
2,
1})] System.Func<System.Security.Cryptography.X509Certificates.X509Certificate2, System.Security.Cryptography.X509Certificates.X509Chain, string, bool> customCheck, [System.Runtime.CompilerServices.NullableAttribute(2)] Akka.Event.ILoggingAdapter log = null) { }
public static Akka.Remote.Transport.DotNetty.CertificateValidationCallback Combine(params Akka.Remote.Transport.DotNetty.CertificateValidationCallback[] validators) { }
public static Akka.Remote.Transport.DotNetty.CertificateValidationCallback PinnedCertificate(params string[] allowedThumbprints) { }
public static Akka.Remote.Transport.DotNetty.CertificateValidationCallback ValidateChain([System.Runtime.CompilerServices.NullableAttribute(2)] Akka.Event.ILoggingAdapter log = null) { }
[return: System.Runtime.CompilerServices.NullableAttribute(1)]
public static Akka.Remote.Transport.DotNetty.CertificateValidationCallback ValidateHostname(string expectedHostname = null, Akka.Event.ILoggingAdapter log = null) { }
public static Akka.Remote.Transport.DotNetty.CertificateValidationCallback ValidateIssuer(string expectedIssuerPattern, [System.Runtime.CompilerServices.NullableAttribute(2)] Akka.Event.ILoggingAdapter log = null) { }
public static Akka.Remote.Transport.DotNetty.CertificateValidationCallback ValidateSubject(string expectedSubjectPattern, [System.Runtime.CompilerServices.NullableAttribute(2)] Akka.Event.ILoggingAdapter log = null) { }
}
public delegate bool CertificateValidationCallback([System.Runtime.CompilerServices.NullableAttribute(2)] System.Security.Cryptography.X509Certificates.X509Certificate2 certificate, [System.Runtime.CompilerServices.NullableAttribute(2)] System.Security.Cryptography.X509Certificates.X509Chain chain, string remotePeer, System.Net.Security.SslPolicyErrors errors, Akka.Event.ILoggingAdapter log);
[System.Runtime.CompilerServices.NullableAttribute(0)]
public sealed class DotNettySslSetup : Akka.Actor.Setup.Setup
{
public DotNettySslSetup(System.Security.Cryptography.X509Certificates.X509Certificate2 certificate, bool suppressValidation) { }
public DotNettySslSetup(System.Security.Cryptography.X509Certificates.X509Certificate2 certificate, bool suppressValidation, bool requireMutualAuthentication) { }
public DotNettySslSetup(System.Security.Cryptography.X509Certificates.X509Certificate2 certificate, bool suppressValidation, bool requireMutualAuthentication, bool validateCertificateHostname) { }
public DotNettySslSetup(System.Security.Cryptography.X509Certificates.X509Certificate2 certificate, bool suppressValidation, bool requireMutualAuthentication, [System.Runtime.CompilerServices.NullableAttribute(2)] Akka.Remote.Transport.DotNetty.CertificateValidationCallback customValidator) { }
public DotNettySslSetup(System.Security.Cryptography.X509Certificates.X509Certificate2 certificate, bool suppressValidation, bool requireMutualAuthentication, bool validateCertificateHostname, [System.Runtime.CompilerServices.NullableAttribute(2)] Akka.Remote.Transport.DotNetty.CertificateValidationCallback customValidator) { }
public System.Security.Cryptography.X509Certificates.X509Certificate2 Certificate { get; }
[System.Runtime.CompilerServices.NullableAttribute(2)]
public Akka.Remote.Transport.DotNetty.CertificateValidationCallback CustomValidator { get; }
public bool RequireMutualAuthentication { get; }
public bool SuppressValidation { get; }
public bool ValidateCertificateHostname { get; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -860,12 +860,34 @@ namespace Akka.Remote.Transport
}
namespace Akka.Remote.Transport.DotNetty
{
[System.Runtime.CompilerServices.NullableAttribute(0)]
public class static CertificateValidation
{
public static Akka.Remote.Transport.DotNetty.CertificateValidationCallback ChainPlusThen([System.Runtime.CompilerServices.NullableAttribute(new byte[] {
1,
2,
2,
1})] System.Func<System.Security.Cryptography.X509Certificates.X509Certificate2, System.Security.Cryptography.X509Certificates.X509Chain, string, bool> customCheck, [System.Runtime.CompilerServices.NullableAttribute(2)] Akka.Event.ILoggingAdapter log = null) { }
public static Akka.Remote.Transport.DotNetty.CertificateValidationCallback Combine(params Akka.Remote.Transport.DotNetty.CertificateValidationCallback[] validators) { }
public static Akka.Remote.Transport.DotNetty.CertificateValidationCallback PinnedCertificate(params string[] allowedThumbprints) { }
public static Akka.Remote.Transport.DotNetty.CertificateValidationCallback ValidateChain([System.Runtime.CompilerServices.NullableAttribute(2)] Akka.Event.ILoggingAdapter log = null) { }
[return: System.Runtime.CompilerServices.NullableAttribute(1)]
public static Akka.Remote.Transport.DotNetty.CertificateValidationCallback ValidateHostname(string expectedHostname = null, Akka.Event.ILoggingAdapter log = null) { }
public static Akka.Remote.Transport.DotNetty.CertificateValidationCallback ValidateIssuer(string expectedIssuerPattern, [System.Runtime.CompilerServices.NullableAttribute(2)] Akka.Event.ILoggingAdapter log = null) { }
public static Akka.Remote.Transport.DotNetty.CertificateValidationCallback ValidateSubject(string expectedSubjectPattern, [System.Runtime.CompilerServices.NullableAttribute(2)] Akka.Event.ILoggingAdapter log = null) { }
}
public delegate bool CertificateValidationCallback([System.Runtime.CompilerServices.NullableAttribute(2)] System.Security.Cryptography.X509Certificates.X509Certificate2 certificate, [System.Runtime.CompilerServices.NullableAttribute(2)] System.Security.Cryptography.X509Certificates.X509Chain chain, string remotePeer, System.Net.Security.SslPolicyErrors errors, Akka.Event.ILoggingAdapter log);
[System.Runtime.CompilerServices.NullableAttribute(0)]
public sealed class DotNettySslSetup : Akka.Actor.Setup.Setup
{
public DotNettySslSetup(System.Security.Cryptography.X509Certificates.X509Certificate2 certificate, bool suppressValidation) { }
public DotNettySslSetup(System.Security.Cryptography.X509Certificates.X509Certificate2 certificate, bool suppressValidation, bool requireMutualAuthentication) { }
public DotNettySslSetup(System.Security.Cryptography.X509Certificates.X509Certificate2 certificate, bool suppressValidation, bool requireMutualAuthentication, bool validateCertificateHostname) { }
public DotNettySslSetup(System.Security.Cryptography.X509Certificates.X509Certificate2 certificate, bool suppressValidation, bool requireMutualAuthentication, [System.Runtime.CompilerServices.NullableAttribute(2)] Akka.Remote.Transport.DotNetty.CertificateValidationCallback customValidator) { }
public DotNettySslSetup(System.Security.Cryptography.X509Certificates.X509Certificate2 certificate, bool suppressValidation, bool requireMutualAuthentication, bool validateCertificateHostname, [System.Runtime.CompilerServices.NullableAttribute(2)] Akka.Remote.Transport.DotNetty.CertificateValidationCallback customValidator) { }
public System.Security.Cryptography.X509Certificates.X509Certificate2 Certificate { get; }
[System.Runtime.CompilerServices.NullableAttribute(2)]
public Akka.Remote.Transport.DotNetty.CertificateValidationCallback CustomValidator { get; }
public bool RequireMutualAuthentication { get; }
public bool SuppressValidation { get; }
public bool ValidateCertificateHostname { get; }
Expand Down
131 changes: 131 additions & 0 deletions src/core/Akka.Docs.Tests/Configuration/TlsConfigurationSample.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
// </copyright>
//-----------------------------------------------------------------------

using System.Security.Cryptography.X509Certificates;
using Akka.Actor.Setup;
using Akka.Configuration;
using Akka.Remote.Transport.DotNetty;

namespace Akka.Docs.Tests.Configuration
{
Expand Down Expand Up @@ -80,5 +83,133 @@ public class TlsConfigurationSample
}
");
#endregion

#region ProgrammaticMutualTlsSetup
/// <summary>
/// Example of programmatic mutual TLS setup using DotNettySslSetup with custom validation.
/// This allows full programmatic control over certificate validation logic.
/// </summary>
public static void ProgrammaticMutualTlsSetup()
{
// Load or obtain your certificate
var certificate = new X509Certificate2("path/to/certificate.pfx", "password");

// Create custom validator combining multiple validation strategies
var customValidator = CertificateValidation.Combine(
// Validate the certificate chain
CertificateValidation.ValidateChain(),
// Also pin against known thumbprints for additional security
CertificateValidation.PinnedCertificate(certificate.Thumbprint)
);

// Setup SSL with custom validator taking precedence over HOCON config
var sslSetup = new DotNettySslSetup(
certificate: certificate,
suppressValidation: false,
requireMutualAuthentication: true,
customValidator: customValidator
);
}
#endregion

#region CertificatePinningExample
/// <summary>
/// Example of certificate pinning - only accept certificates with specific thumbprints.
/// Useful for preventing man-in-the-middle attacks with compromised CAs.
/// </summary>
public static void CertificatePinningSetup()
{
var certificate = new X509Certificate2("path/to/certificate.pfx", "password");

// Allow only specific certificates by thumbprint
var validator = CertificateValidation.PinnedCertificate(
"2531c78c51e5041d02564697a88af8bc7a7ce3e3", // Production cert
"abc123def456789ghi012jkl345mno678pqr901stu" // Backup cert
);

var sslSetup = new DotNettySslSetup(
certificate: certificate,
suppressValidation: false,
requireMutualAuthentication: true,
customValidator: validator
);
}
#endregion

#region CustomValidationLogicExample
/// <summary>
/// Example of custom certificate validation logic combined with standard validation.
/// Allows complete control over what certificates are accepted.
/// </summary>
public static void CustomValidationLogicSetup()
{
var certificate = new X509Certificate2("path/to/certificate.pfx", "password");

// Start with standard chain validation, then add custom logic
var validator = CertificateValidation.ChainPlusThen(
// Custom validation - check certificate subject matches expected peer
(cert, chain, peer) =>
{
// Accept only certificates from authorized-peer
if (cert?.Subject != null && cert.Subject.Contains("CN=authorized-peer"))
{
return true; // Accept this certificate
}
return false; // Reject all others
}
);

var sslSetup = new DotNettySslSetup(
certificate: certificate,
suppressValidation: false,
requireMutualAuthentication: true,
customValidator: validator
);
}
#endregion

#region HostnameValidationExample
/// <summary>
/// Example of enabling traditional hostname validation for client-server architectures.
/// Use when all nodes share the same certificate with matching CN/SAN.
/// </summary>
public static void HostnameValidationSetup()
{
var certificate = new X509Certificate2("path/to/certificate.pfx", "password");

// Enable both chain validation and hostname validation
var sslSetup = new DotNettySslSetup(
certificate: certificate,
suppressValidation: false,
requireMutualAuthentication: true,
validateCertificateHostname: true // Enable traditional TLS hostname validation
);
}
#endregion

#region SubjectValidationExample
/// <summary>
/// Example of subject DN validation - only accept certificates with specific subject names.
/// Useful for verifying peer identity based on certificate subject.
/// Supports wildcards: "CN=Akka-Node-*" matches "CN=Akka-Node-001"
/// </summary>
public static void SubjectValidationSetup()
{
var certificate = new X509Certificate2("path/to/certificate.pfx", "password");

// Accept certificates matching the subject pattern
// Wildcards are supported: CN=Akka-Node-* matches CN=Akka-Node-001
var validator = CertificateValidation.ValidateSubject(
"CN=Akka-Node-*" // Pattern to match
);

var sslSetup = new DotNettySslSetup(
certificate: certificate,
suppressValidation: false,
requireMutualAuthentication: true,
customValidator: validator
);
}
#endregion
}
}
Loading
Loading