- 
                Notifications
    You must be signed in to change notification settings 
- Fork 1.1k
Closed
Milestone
Description
Problem Statement
Recent mTLS security improvements (PRs #7851, #7897, #7891) provide strong foundational security, but expose two issues:
Issue 1: Asymmetric Hostname Validation
- Client-side: Validates server certificate hostname when validate-certificate-hostname = true✅
- Server-side: IGNORES the setting when validating client certificates ❌
Impact: In a cluster with per-node certificates, servers accept any client cert with valid chain, regardless of hostname. Compromised nodes could connect with stolen certs.
Example: Cluster (node1.example.com, node2.example.com) with validate-certificate-hostname = true
- Client → Server: Validates hostname correctly ✅
- Server → Client: Accepts ANY valid cert chain ❌
Issue 2: Limited Validation Scenarios
Current config-based validation only supports:
- Chain validation (CA trust)
- Hostname matching
- Certificate presence
Users cannot easily implement custom validation for:
- Certificate pinning (whitelist known cert thumbprints)
- Subject/Issuer matching (organizational CA validation)
- Peer identity verification
- Custom business rules
Proposed Solution
1. Custom Validation Callbacks (Primary)
Add high-level CertificateValidationCallback delegate that users can implement or compose from helpers:
// User-facing delegate (more useful than RemoteCertificateValidationCallback)
public delegate bool CertificateValidationCallback(
    X509Certificate2 certificate,
    X509Chain chain,
    string remotePeer,        // Actual peer identifier
    SslPolicyErrors errors,
    ILoggingAdapter log);     // For diagnostics
// Usage: provide custom validator
var setup = new DotNettySslSetup(cert, suppressValidation: false,
    requireMutualAuthentication: true,
    customValidator: MyValidationStrategy);2. Pre-Built Helper Functions
Make common scenarios easy without writing custom logic:
// All available via CertificateValidation factory
CertificateValidation.ValidateChain(log)                 // CA chain validation
CertificateValidation.ValidateHostname(expectedHostname, log)  // CN/SAN matching
CertificateValidation.PinnedCertificate(thumbprints)    // Certificate pinning
CertificateValidation.ValidateSubject(pattern, log)     // Subject DN matching (with wildcards)
CertificateValidation.ValidateIssuer(pattern, log)      // Issuer DN matching
CertificateValidation.Combine(validators...)            // Compose multiple validators
CertificateValidation.ChainPlusThen(customCheck, log)   // Chain validation + custom logic3. Real-World Examples
Production TLS with pinning:
var validator = CertificateValidation.Combine(
    CertificateValidation.ValidateChain(Log),
    CertificateValidation.ValidateHostname(log: Log),
    CertificateValidation.PinnedCertificate(knownThumbprints)
);Internal cluster with per-node certs:
var validator = CertificateValidation.Combine(
    CertificateValidation.ValidateChain(Log),
    CertificateValidation.ValidateHostname(log: Log),
    CertificateValidation.ValidateIssuer("CN=Our-Internal-CA", Log)
);Custom peer validation:
var validator = CertificateValidation.ChainPlusThen(
    customCheck: (cert, chain, peer) =>
    {
        // Only accept certs for authorized nodes
        var cn = cert.GetNameInfo(X509NameType.DnsName, false);
        return authorizedNodeNames.Contains(cn);
    },
    log: Log
);Implementation Details
Changes Required
- DotNettySslSetup.cs: Add CustomValidatorproperty (new optional constructor parameter)
- DotNettyTransportSettings.cs:
- Define CertificateValidationCallbackdelegate
- Implement CertificateValidationhelper factory class
 
- Define 
- DotNettyTransport.cs:
- Update SetClientPipeline()to use custom validator when provided
- Update SetServerPipeline()to use custom validator when provided
- Fix asymmetry bug: Apply hostname validation bidirectionally
 
- Update 
- Tests: Full unit test suite for each helper (CertificateValidationHelpersSpec)
Backward Compatibility
- ✅ Existing config-based validation still works (via TlsValidationCallbacks)
- ✅ Falls back to TlsValidationCallbacksif no custom validator provided
- ✅ New parameter is optional
Benefits
- ✅ Maximum flexibility - Users can implement any validation logic
- ✅ Great ergonomics - Common cases have easy pre-built helpers
- ✅ Composable - Helpers combine with Combine()andChainPlusThen()
- ✅ Discoverable - IDE shows all available helpers
- ✅ Fixes asymmetry - Custom validators apply bidirectionally (both client and server)
- ✅ Idiomatic .NET - Similar to Polly policies, authorization attributes
- ✅ Testable - Each helper independently testable
Related PRs
- feat(remote): implement mutual TLS authentication support #7851 (mutual TLS enforcement)
- Fix TLS hostname validation bug and add configurable validation #7897 (hostname validation)
- Improve TLS/SSL certificate error messages during handshake failures #7891 (improved error messages)
Acceptance Criteria
-  CertificateValidationCallbackdelegate defined
-  CertificateValidationfactory class with all 7 helpers implemented
-  DotNettySslSetupupdated withCustomValidatorparameter
-  DotNettyTransportadapted to use custom validators bidirectionally
- Asymmetry bug fixed: server-side hostname validation respects setting
-  Comprehensive test suite: CertificateValidationHelpersSpec
- Backward compatibility verified
- Documentation updated with examples