Skip to content

Conversation

Aaronontheweb
Copy link
Member

@Aaronontheweb Aaronontheweb commented Oct 2, 2025

Summary

  • Implemented configurable mutual TLS (mTLS) authentication for Akka.Remote
  • Added secure-by-default configuration with require-mutual-authentication = true
  • Comprehensive security documentation updates with TLS best practices

Changes

Implementation

  • Added require-mutual-authentication configuration option to Remote.conf (defaults to true)
  • Updated SslSettings class to include RequireMutualAuthentication property
  • Modified DotNettyTransport.cs to enforce mutual TLS in both client and server pipelines:
    • Server requires client certificates when mTLS is enabled
    • Client only provides certificate when mTLS is enabled
  • Added DotNettyMutualTlsSpec.cs test suite covering:
    • Mutual TLS connections with valid certificates
    • Standard TLS (server-only) authentication
    • System startup validation

Documentation

  • Complete rewrite of security.md with:
    • Context on when TLS is needed vs optional
    • Detailed certificate configuration examples
    • Windows Certificate Store integration
    • Mermaid sequence diagrams showing TLS vs mTLS flows (pending Mermaid support)
    • PowerShell scripts for certificate management
  • Created TlsConfigurationSample.cs with DocFX-compatible code regions for:
    • Mutual TLS configuration
    • Standard TLS configuration
    • Windows Certificate Store configuration
    • Development configuration with self-signed certificates

Security Improvements

  • Defense-in-depth: Mutual TLS ensures both client and server authenticate, preventing asymmetric connectivity issues
  • Fail-fast validation: Combined with startup certificate validation (from previous PR), provides complete protection
  • Secure defaults: require-mutual-authentication = true by default when SSL is enabled

Backward Compatibility

  • Systems can opt-out by setting require-mutual-authentication = false
  • When disabled, falls back to standard server-only TLS authentication
  • All existing TLS tests continue to pass

Related Issues

Test Plan

  • All existing TLS tests pass (8/8)
  • New mutual TLS tests validate both enabled and disabled scenarios
  • Documentation builds without warnings
  • Manual testing of mutual TLS in cluster environment
  • Verify backward compatibility with v1.5 nodes

Implements mutual TLS (mTLS) authentication as a defense-in-depth security
measure for Akka.Remote TLS connections. When enabled, both client and server
must present valid certificates with accessible private keys during the TLS
handshake, ensuring symmetric authentication.

Key Changes:
- Add require-mutual-authentication config option (default: true)
- Update SslSettings to include RequireMutualAuthentication property
- Modify client TLS handler to provide certificate only when mutual TLS enabled
- Modify server TLS handler to require and validate client certificates when enabled
- Add comprehensive test suite for mutual TLS scenarios

Security Benefits:
- Prevents nodes with inaccessible private keys from connecting as clients
- Ensures complete bidirectional authentication (not just server-side)
- Works in conjunction with startup certificate validation for fail-fast behavior
- Provides defense-in-depth security for production deployments

Configuration:
  akka.remote.dot-netty.tcp.ssl {
    require-mutual-authentication = true  # Default: secure by default
  }

Set to false only if your environment cannot support client certificate authentication.

Related: Freshdesk akkadotnet#538 - TLS certificate private key validation
Major overhaul of the security documentation to reflect new TLS features
and provide comprehensive security guidance for production deployments.

Changes:
- Document new startup certificate validation feature (v1.5.52+)
- Document new mutual TLS authentication support (v1.5.52+)
- Add detailed suppress-validation guidance with security implications
- Provide Windows Certificate Store configuration examples
- Include PowerShell scripts for certificate management
- Add troubleshooting section for common TLS issues
- Update configuration examples from insecure to secure defaults
- Fix deprecated external links (Microsoft Learn, IETF, OWASP)
- Add security analysis for different configuration levels
- Include migration guide for upgrading to mutual TLS
- Add best practices summary with 10 key recommendations
- Document common pitfalls and their solutions

Security improvements:
- Changed example configs to use suppress-validation = false by default
- Added warnings about using suppress-validation = true in production
- Emphasized defense-in-depth with VPNs + TLS + mutual TLS
- Documented proper self-signed certificate usage for development

The documentation now provides clear guidance on:
- What TLS protects against (and what it doesn't)
- When to use mutual TLS vs standard TLS
- How to properly configure certificates in production
- How to troubleshoot common certificate permission issues

Related: Freshdesk akkadotnet#538 - TLS certificate validation improvements
- Create TlsConfigurationSample.cs with proper HOCON configuration examples
- Update security.md to reference code samples using DocFX syntax
- Add context explaining when TLS is needed vs optional
- Remove poorly designed region tags from test file

The documentation now follows Akka.NET documentation guidelines with
proper code references instead of inline configuration blocks.
Added configurable mutual TLS (mTLS) authentication for Akka.Remote to provide bidirectional certificate validation between client and server nodes. This feature enhances security by ensuring both sides of a connection authenticate with valid certificates.

Changes:
- Added `require-mutual-authentication` config option (defaults to true for security-by-default)
- Updated DotNettyTransport to enforce mutual TLS in both client and server pipelines
- Added comprehensive test suite for mutual TLS scenarios
- Updated security documentation with detailed TLS/mTLS configuration guidance
- Added code samples for various TLS configurations (standard, mutual, Windows cert store)
- Included Mermaid sequence diagrams for TLS vs mTLS flows (pending Mermaid support on site)

The implementation ensures backward compatibility while encouraging secure defaults. When mutual TLS is disabled, the system falls back to standard server-only authentication.

Related to Freshdesk ticket akkadotnet#538
- Fixed all heading title case to comply with markdownlint-rule-titlecase
- Changed 'suppress-validation' to 'Suppress-Validation' in headings
- Fixed error message headings to use proper title case
- All CI/CD checks should now pass
Added VPN provider names to the accepted words list to fix CI spellcheck failures
Copy link
Member Author

@Aaronontheweb Aaronontheweb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Detailed my changes - found some things especially around the tests that need to be improved.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modernized the documentation quite a bit, but I'll need to do another PR that adds Mermaid support in order to get the handshake diagram to work.

Worth noting: the new Quic transports in Akka.NET v1.6 will, 100% always require a certificate in order to work. That is baked into how Quic works and is an unavoidable requirement.

#
# Set to false only if your environment cannot support client certificate authentication.
# Default: true (secure by default)
require-mutual-authentication = true
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New mutual TLS requirement - this is technically a breaking behavioral change, but it closes a real security vulnerability so it has to be done.

/// When require-mutual-authentication is enabled, both client and server must
/// present valid certificates with accessible private keys.
/// </summary>
public class DotNettyMutualTlsSpec : AkkaSpec
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default SSL / TLS specs already cover the happy path scenario - we should rewrite this to ensure hard fail if the two different, but valid certificates are used - and maybe another scenario where the same certificate is used but the password / private key auth is incorrect on one of the nodes.

}

public SslSettings(X509Certificate2 certificate, bool suppressValidation)
public SslSettings(X509Certificate2 certificate, bool suppressValidation, bool requireMutualAuthentication = true)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically a breaking binary signature change - maybe I can fix that.

- Fixed binary compatibility by adding overloaded SslSettings constructor
- Added config test to verify RequireMutualAuthentication defaults to true
- Added test for mutual TLS failure when client has no certificate
- Added test to verify mutual TLS can be disabled for backward compatibility
- Enhanced DotNettyMutualTlsSpec with more comprehensive test scenarios
- Generated new self-signed certificate (akka-client-cert.pfx) for testing
- Modified CreateConfig to accept custom certificate paths
- Added test to verify mutual TLS fails when client and server have different valid certificates
- This ensures proper certificate validation in mutual TLS mode
- Generated new self-signed certificate (akka-client-cert.pfx) to test scenarios where client and server have different valid certificates
- Added certificate to project file as build output
- Force-added certificate to git (normally ignored by .gitignore)
- Added new test SSL_should_have_secure_defaults_when_enabled to verify secure defaults when SSL is enabled
- Removed SSL checks from non-SSL test Remoting_should_contain_correct_heliosTCP_values_in_ReferenceConf
- Fixed certificate path resolution using full path
- Tests now properly verify that require-mutual-authentication defaults to true and suppress-validation defaults to false
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a second cert to TLS verification specs.

Copy link
Member Author

@Aaronontheweb Aaronontheweb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have one more commit coming that removes a bunch of duplicate tests that all cover the same functionality, but otherwise this is complete and I'm satisfied with it.

/// </summary>
public class DotNettyMutualTlsSpec : AkkaSpec
{
private const string ValidCertPath = "Resources/akka-validcert.pfx";
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two different certs now.

try
{
// Server with mutual TLS using the original certificate
var serverConfig = CreateConfig(enableSsl: true, requireMutualAuth: true, suppressValidation: false,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use a different cert than the client

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fails with

[SERVER] [WARNING][10/03/2025 16:59:40.547Z][Thread 0033][TcpTransport (akka://ServerSystem)] Mutual TLS: Client certificate validation failed with errors: RemoteCertificateChainErrors
[SERVER] [ERROR][10/03/2025 16:59:40.548Z][Thread 0054][TcpServerHandler (akka://ServerSystem)] TLS handshake failed. Channel [[::ffff:127.0.0.1]:56636->[::ffff:127.0.0.1]:56638](Id=1c17f0a0)
Cause: System.AggregateException: One or more errors occurred. (The remote certificate was rejected by the provided RemoteCertificateValidationCallback.)
 ---> System.Security.Authentication.AuthenticationException: The remote certificate was rejected by the provided RemoteCertificateValidationCallback.
   at System.Net.Security.SslStream.CompleteHandshake(SslAuthenticationOptions sslAuthenticationOptions)
   at System.Net.Security.SslStream.ForceAuthenticationAsync[TIOAdapter](Boolean receiveFirst, Byte[] reAuthenticationData, CancellationToken cancellationToken)
   --- End of inner exception stack trace ---

- Restored DotNettySslSetupSpec which tests SSL configuration via Setup API
- This is distinct from HOCON-based configuration tested in DotNettySslSupportSpec
- Added test for configuring mutual TLS via DotNettySslSetup
- Enhanced TestActorSystemSetup to support mutual authentication parameter
- Removed unsupported requireMutualAuth parameter from DotNettySslSetup constructor
- Updated test to combine Setup API certificate with HOCON mutual TLS configuration
- Fixed shutdown method call to use correct API
@Aaronontheweb
Copy link
Member Author

There are some redundant tests we should clean up later, but that's not needed for now.

Copy link
Contributor

@Arkatufus Arkatufus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants