fix(PortProxy): Fix TLS renegotiation handling and adjust TLS keep-alive timeouts in PortProxy implementation
This commit is contained in:
parent
009e3c4f0e
commit
c415a6c361
@ -1,5 +1,13 @@
|
||||
# Changelog
|
||||
|
||||
## 2025-03-10 - 3.30.4 - fix(PortProxy)
|
||||
Fix TLS renegotiation handling and adjust TLS keep-alive timeouts in PortProxy implementation
|
||||
|
||||
- Allow TLS renegotiation data without an explicit SNI extraction to pass through, ensuring valid renegotiations are not dropped (critical for Chrome).
|
||||
- Update TLS keep-alive timeout from an aggressive 30 minutes to a more generous 4 hours to reduce unnecessary reconnections.
|
||||
- Increase inactivity thresholds for TLS connections from 20 minutes to 2 hours with an additional verification interval extended from 5 to 15 minutes.
|
||||
- Adjust long-lived TLS connection timeout from 45 minutes to 8 hours for improved certificate context refresh in chained proxy scenarios.
|
||||
|
||||
## 2025-03-10 - 3.30.3 - fix(classes.portproxy.ts)
|
||||
Simplify timeout management in PortProxy and fix chained proxy certificate refresh issues
|
||||
|
||||
|
@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@push.rocks/smartproxy',
|
||||
version: '3.30.3',
|
||||
version: '3.30.4',
|
||||
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.'
|
||||
}
|
||||
|
@ -515,7 +515,9 @@ export class PortProxy {
|
||||
);
|
||||
}
|
||||
|
||||
// Let the NetworkProxy handle the TLS renegotiation
|
||||
// NOTE: We don't need to explicitly forward the renegotiation packets
|
||||
// because socket.pipe(proxySocket) is already handling that.
|
||||
// The pipe ensures all data (including renegotiation) flows through properly.
|
||||
// Just update the activity timestamp to prevent timeouts
|
||||
record.lastActivity = Date.now();
|
||||
}
|
||||
@ -848,11 +850,28 @@ export class PortProxy {
|
||||
|
||||
// Add the renegotiation listener for SNI validation
|
||||
if (serverName) {
|
||||
// This listener will check for TLS renegotiation attempts
|
||||
// Note: We don't need to explicitly forward the renegotiation packets
|
||||
// since socket.pipe(targetSocket) is already set up earlier and handles that
|
||||
socket.on('data', (renegChunk: Buffer) => {
|
||||
if (renegChunk.length > 0 && renegChunk.readUInt8(0) === 22) {
|
||||
try {
|
||||
// Try to extract SNI from potential renegotiation
|
||||
const newSNI = extractSNI(renegChunk, this.settings.enableTlsDebugLogging);
|
||||
|
||||
// IMPORTANT: If we can't extract an SNI from renegotiation, we MUST allow it through
|
||||
// Otherwise valid renegotiations that don't explicitly repeat the SNI will break
|
||||
if (newSNI === undefined) {
|
||||
if (this.settings.enableDetailedLogging) {
|
||||
console.log(
|
||||
`[${connectionId}] Rehandshake detected without SNI, allowing it through.`
|
||||
);
|
||||
}
|
||||
// Let it pass through - this is critical for Chrome's TLS handling
|
||||
return;
|
||||
}
|
||||
|
||||
// Only block if we positively identify a different SNI
|
||||
if (newSNI && newSNI !== record.lockedDomain) {
|
||||
console.log(
|
||||
`[${connectionId}] Rehandshake detected with different SNI: ${newSNI} vs locked ${record.lockedDomain}. Terminating connection.`
|
||||
@ -864,6 +883,8 @@ export class PortProxy {
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
// Always allow the renegotiation to continue if we encounter an error
|
||||
// This ensures Chrome can complete its TLS renegotiation
|
||||
console.log(
|
||||
`[${connectionId}] Error processing potential renegotiation: ${err}. Allowing connection to continue.`
|
||||
);
|
||||
@ -886,13 +907,12 @@ export class PortProxy {
|
||||
}
|
||||
// No cleanup timer for immortal connections
|
||||
}
|
||||
// For TLS keep-alive connections, use an aggressive timeout to ensure
|
||||
// certificates are regularly refreshed even in chained proxy scenarios
|
||||
// For TLS keep-alive connections, use a more generous timeout now that
|
||||
// we've fixed the renegotiation handling issue that was causing certificate problems
|
||||
else if (record.hasKeepAlive && record.isTLS) {
|
||||
// Use a much shorter timeout for TLS connections to ensure certificate contexts are refreshed frequently
|
||||
// This prevents issues with stale certificates in browser tabs that have been idle for a while
|
||||
// 30 minutes is aggressive enough to handle multi-proxy chains without causing too many reconnects
|
||||
const tlsKeepAliveTimeout = 30 * 60 * 1000; // 30 minutes for TLS keep-alive - dramatically reduced from 8 hours
|
||||
// Use a longer timeout for TLS connections now that renegotiation handling is fixed
|
||||
// This reduces unnecessary reconnections while still ensuring certificate freshness
|
||||
const tlsKeepAliveTimeout = 4 * 60 * 60 * 1000; // 4 hours for TLS keep-alive - increased from 30 minutes
|
||||
const safeTimeout = ensureSafeTimeout(tlsKeepAliveTimeout);
|
||||
|
||||
record.cleanupTimer = setTimeout(() => {
|
||||
@ -1063,34 +1083,34 @@ export class PortProxy {
|
||||
// For TLS keep-alive connections after sleep/long inactivity, force close
|
||||
// to make browser establish a new connection with fresh certificate context
|
||||
if (record.isTLS && record.tlsHandshakeComplete) {
|
||||
// Much more aggressive timeout (20 minutes) to ensure reliable operation in chained proxy scenarios
|
||||
if (timeDiff > 20 * 60 * 1000) {
|
||||
// If inactive for more than 20 minutes (reduced from 4 hours)
|
||||
// More generous timeout now that we've fixed the renegotiation handling
|
||||
if (timeDiff > 2 * 60 * 60 * 1000) {
|
||||
// If inactive for more than 2 hours (increased from 20 minutes)
|
||||
console.log(
|
||||
`[${record.id}] TLS connection inactive for ${plugins.prettyMs(timeDiff)}. ` +
|
||||
`Closing to force new connection with fresh certificate.`
|
||||
);
|
||||
return this.initiateCleanupOnce(record, 'certificate_refresh_needed');
|
||||
} else if (timeDiff > 10 * 60 * 1000) {
|
||||
// For shorter but still significant inactivity (10+ minutes), be more aggressive with refresh
|
||||
} else if (timeDiff > 30 * 60 * 1000) {
|
||||
// For shorter but still significant inactivity (30+ minutes), refresh TLS state
|
||||
console.log(
|
||||
`[${record.id}] TLS connection inactive for ${plugins.prettyMs(timeDiff)}. ` +
|
||||
`Aggressively refreshing TLS state to prevent certificate issues in proxy chains.`
|
||||
`Refreshing TLS state.`
|
||||
);
|
||||
this.refreshTlsStateAfterSleep(record);
|
||||
|
||||
// Add an additional check in 5 minutes if no activity
|
||||
// Add an additional check in 15 minutes if no activity
|
||||
const refreshCheckId = record.id;
|
||||
const refreshCheck = setTimeout(() => {
|
||||
const currentRecord = this.connectionRecords.get(refreshCheckId);
|
||||
if (currentRecord && Date.now() - currentRecord.lastActivity > 5 * 60 * 1000) {
|
||||
if (currentRecord && Date.now() - currentRecord.lastActivity > 15 * 60 * 1000) {
|
||||
console.log(
|
||||
`[${refreshCheckId}] No activity detected after TLS refresh. ` +
|
||||
`Closing connection to ensure certificate freshness.`
|
||||
);
|
||||
this.initiateCleanupOnce(currentRecord, 'tls_refresh_verification_failed');
|
||||
}
|
||||
}, 5 * 60 * 1000);
|
||||
}, 15 * 60 * 1000);
|
||||
|
||||
// Make sure timeout doesn't keep the process alive
|
||||
if (refreshCheck.unref) {
|
||||
@ -1133,9 +1153,9 @@ export class PortProxy {
|
||||
const connectionAge = Date.now() - record.incomingStartTime;
|
||||
const hourInMs = 60 * 60 * 1000;
|
||||
|
||||
// For TLS browser connections, use a much more aggressive timeout
|
||||
// to avoid certificate issues, especially in chained proxy scenarios
|
||||
if (record.isTLS && record.hasKeepAlive && connectionAge > 45 * 60 * 1000) { // 45 minutes instead of 12 hours
|
||||
// For TLS browser connections, use a more generous timeout now that
|
||||
// we've fixed the renegotiation handling issues
|
||||
if (record.isTLS && record.hasKeepAlive && connectionAge > 8 * hourInMs) { // 8 hours instead of 45 minutes
|
||||
console.log(
|
||||
`[${record.id}] Long-lived TLS connection (${plugins.prettyMs(connectionAge)}). ` +
|
||||
`Closing to ensure proper certificate handling on browser reconnect in proxy chain.`
|
||||
|
Loading…
x
Reference in New Issue
Block a user