fix(PortProxy): Improve TLS renegotiation handling in PortProxy by validating the new SNI against allowed domain configurations. If the new SNI is permitted based on existing IP rules, update the locked domain to allow connection reuse; otherwise, terminate the connection to prevent misrouting.

This commit is contained in:
Philipp Kunz 2025-03-11 02:18:56 +00:00
parent bffdaffe39
commit 74fdb58f84
3 changed files with 44 additions and 3 deletions

View File

@ -1,5 +1,11 @@
# Changelog
## 2025-03-11 - 3.30.6 - fix(PortProxy)
Improve TLS renegotiation handling in PortProxy by validating the new SNI against allowed domain configurations. If the new SNI is permitted based on existing IP rules, update the locked domain to allow connection reuse; otherwise, terminate the connection to prevent misrouting.
- Added logic to check if a new SNI during renegotiation is allowed by comparing IP rules from the matching domain configuration.
- Updated detailed logging to indicate when a valid SNI change is accepted and when it results in a mismatch termination.
## 2025-03-10 - 3.30.5 - fix(internal)
No uncommitted changes detected; project files and tests remain unchanged.

View File

@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@push.rocks/smartproxy',
version: '3.30.5',
version: '3.30.6',
description: 'A powerful proxy package that effectively handles high traffic, with features such as SSL/TLS support, port proxying, WebSocket handling, and dynamic routing with authentication options.'
}

View File

@ -871,10 +871,45 @@ export class PortProxy {
return;
}
// Only block if we positively identify a different SNI
// Check if the SNI has changed
if (newSNI && newSNI !== record.lockedDomain) {
// Instead of immediately terminating, check if the new SNI would be allowed
// by the same ruleset that allowed the initial connection
const newDomainConfig = this.settings.domainConfigs.find((config) =>
config.domains.some((d) => plugins.minimatch(newSNI, d))
);
// If we found a matching domain config, check IP rules
if (newDomainConfig) {
const effectiveAllowedIPs = [
...newDomainConfig.allowedIPs,
...(this.settings.defaultAllowedIPs || []),
];
const effectiveBlockedIPs = [
...(newDomainConfig.blockedIPs || []),
...(this.settings.defaultBlockedIPs || []),
];
// Check if the IP is allowed for the new domain
if (isGlobIPAllowed(record.remoteIP, effectiveAllowedIPs, effectiveBlockedIPs)) {
// Allow the domain switch - Chrome is reusing the connection for a different domain
if (this.settings.enableDetailedLogging) {
console.log(
`[${connectionId}] Rehandshake with new SNI: ${newSNI} (previously ${record.lockedDomain}). ` +
`New domain is allowed by rules, permitting connection reuse.`
);
}
// Update the locked domain to the new domain
record.lockedDomain = newSNI;
return;
}
}
// If we get here, either no matching domain config was found or the IP is not allowed
console.log(
`[${connectionId}] Rehandshake detected with different SNI: ${newSNI} vs locked ${record.lockedDomain}. Terminating connection.`
`[${connectionId}] Rehandshake detected with different SNI: ${newSNI} vs locked ${record.lockedDomain}. ` +
`New domain not allowed by rules. Terminating connection.`
);
this.initiateCleanupOnce(record, 'sni_mismatch');
} else if (newSNI && this.settings.enableDetailedLogging) {