@push.rocks/smartproxy 5.0.0 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.pp.interfaces.d.ts +23 -0
- package/dist_ts/classes.pp.networkproxybridge.d.ts +15 -1
- package/dist_ts/classes.pp.networkproxybridge.js +116 -21
- package/dist_ts/classes.pp.portproxy.d.ts +20 -4
- package/dist_ts/classes.pp.portproxy.js +321 -22
- package/dist_ts/index.d.ts +6 -6
- package/dist_ts/index.js +7 -7
- package/dist_ts/networkproxy/classes.np.certificatemanager.d.ts +77 -0
- package/dist_ts/networkproxy/classes.np.certificatemanager.js +354 -0
- package/dist_ts/networkproxy/classes.np.connectionpool.d.ts +47 -0
- package/dist_ts/networkproxy/classes.np.connectionpool.js +210 -0
- package/dist_ts/networkproxy/classes.np.networkproxy.d.ts +117 -0
- package/dist_ts/networkproxy/classes.np.networkproxy.js +375 -0
- package/dist_ts/networkproxy/classes.np.requesthandler.d.ts +51 -0
- package/dist_ts/networkproxy/classes.np.requesthandler.js +210 -0
- package/dist_ts/networkproxy/classes.np.types.d.ts +82 -0
- package/dist_ts/networkproxy/classes.np.types.js +35 -0
- package/dist_ts/networkproxy/classes.np.websockethandler.d.ts +38 -0
- package/dist_ts/networkproxy/classes.np.websockethandler.js +188 -0
- package/dist_ts/networkproxy/index.d.ts +6 -0
- package/dist_ts/networkproxy/index.js +8 -0
- package/dist_ts/nfttablesproxy/classes.nftablesproxy.d.ts +219 -0
- package/dist_ts/nfttablesproxy/classes.nftablesproxy.js +1542 -0
- package/dist_ts/port80handler/classes.port80handler.d.ts +260 -0
- package/dist_ts/port80handler/classes.port80handler.js +928 -0
- package/dist_ts/smartproxy/classes.pp.connectionhandler.d.ts +39 -0
- package/dist_ts/smartproxy/classes.pp.connectionhandler.js +754 -0
- package/dist_ts/smartproxy/classes.pp.connectionmanager.d.ts +78 -0
- package/dist_ts/smartproxy/classes.pp.connectionmanager.js +378 -0
- package/dist_ts/smartproxy/classes.pp.domainconfigmanager.d.ts +55 -0
- package/dist_ts/smartproxy/classes.pp.domainconfigmanager.js +103 -0
- package/dist_ts/smartproxy/classes.pp.interfaces.d.ts +133 -0
- package/dist_ts/smartproxy/classes.pp.interfaces.js +2 -0
- package/dist_ts/smartproxy/classes.pp.networkproxybridge.d.ts +57 -0
- package/dist_ts/smartproxy/classes.pp.networkproxybridge.js +306 -0
- package/dist_ts/smartproxy/classes.pp.portrangemanager.d.ts +56 -0
- package/dist_ts/smartproxy/classes.pp.portrangemanager.js +179 -0
- package/dist_ts/smartproxy/classes.pp.securitymanager.d.ts +47 -0
- package/dist_ts/smartproxy/classes.pp.securitymanager.js +126 -0
- package/dist_ts/smartproxy/classes.pp.snihandler.d.ts +153 -0
- package/dist_ts/smartproxy/classes.pp.snihandler.js +1053 -0
- package/dist_ts/smartproxy/classes.pp.timeoutmanager.d.ts +47 -0
- package/dist_ts/smartproxy/classes.pp.timeoutmanager.js +154 -0
- package/dist_ts/smartproxy/classes.pp.tlsalert.d.ts +149 -0
- package/dist_ts/smartproxy/classes.pp.tlsalert.js +225 -0
- package/dist_ts/smartproxy/classes.pp.tlsmanager.d.ts +57 -0
- package/dist_ts/smartproxy/classes.pp.tlsmanager.js +132 -0
- package/dist_ts/smartproxy/classes.smartproxy.d.ts +64 -0
- package/dist_ts/smartproxy/classes.smartproxy.js +567 -0
- package/package.json +1 -1
- package/readme.md +77 -27
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/index.ts +6 -6
- package/ts/networkproxy/classes.np.certificatemanager.ts +398 -0
- package/ts/networkproxy/classes.np.connectionpool.ts +241 -0
- package/ts/networkproxy/classes.np.networkproxy.ts +469 -0
- package/ts/networkproxy/classes.np.requesthandler.ts +278 -0
- package/ts/networkproxy/classes.np.types.ts +123 -0
- package/ts/networkproxy/classes.np.websockethandler.ts +226 -0
- package/ts/networkproxy/index.ts +7 -0
- package/ts/{classes.port80handler.ts → port80handler/classes.port80handler.ts} +249 -1
- package/ts/{classes.pp.connectionhandler.ts → smartproxy/classes.pp.connectionhandler.ts} +1 -1
- package/ts/{classes.pp.connectionmanager.ts → smartproxy/classes.pp.connectionmanager.ts} +1 -1
- package/ts/{classes.pp.domainconfigmanager.ts → smartproxy/classes.pp.domainconfigmanager.ts} +1 -1
- package/ts/{classes.pp.interfaces.ts → smartproxy/classes.pp.interfaces.ts} +31 -5
- package/ts/{classes.pp.networkproxybridge.ts → smartproxy/classes.pp.networkproxybridge.ts} +129 -28
- package/ts/{classes.pp.securitymanager.ts → smartproxy/classes.pp.securitymanager.ts} +1 -1
- package/ts/{classes.pp.tlsmanager.ts → smartproxy/classes.pp.tlsmanager.ts} +1 -1
- package/ts/smartproxy/classes.smartproxy.ts +679 -0
- package/ts/classes.networkproxy.ts +0 -1730
- package/ts/classes.pp.acmemanager.ts +0 -149
- package/ts/classes.pp.portproxy.ts +0 -344
- /package/ts/{classes.nftablesproxy.ts → nfttablesproxy/classes.nftablesproxy.ts} +0 -0
- /package/ts/{classes.pp.portrangemanager.ts → smartproxy/classes.pp.portrangemanager.ts} +0 -0
- /package/ts/{classes.pp.snihandler.ts → smartproxy/classes.pp.snihandler.ts} +0 -0
- /package/ts/{classes.pp.timeoutmanager.ts → smartproxy/classes.pp.timeoutmanager.ts} +0 -0
- /package/ts/{classes.pp.tlsalert.ts → smartproxy/classes.pp.tlsalert.ts} +0 -0
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import * as plugins from '
|
|
1
|
+
import * as plugins from '../plugins.js';
|
|
2
2
|
import { IncomingMessage, ServerResponse } from 'http';
|
|
3
|
+
import * as fs from 'fs';
|
|
4
|
+
import * as path from 'path';
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* Custom error classes for better error handling
|
|
@@ -73,6 +75,10 @@ interface IPort80HandlerOptions {
|
|
|
73
75
|
renewThresholdDays?: number;
|
|
74
76
|
httpsRedirectPort?: number;
|
|
75
77
|
renewCheckIntervalHours?: number;
|
|
78
|
+
enabled?: boolean; // Whether ACME is enabled at all
|
|
79
|
+
autoRenew?: boolean; // Whether to automatically renew certificates
|
|
80
|
+
certificateStore?: string; // Directory to store certificates
|
|
81
|
+
skipConfiguredCerts?: boolean; // Skip domains that already have certificates
|
|
76
82
|
}
|
|
77
83
|
|
|
78
84
|
/**
|
|
@@ -145,6 +151,10 @@ export class Port80Handler extends plugins.EventEmitter {
|
|
|
145
151
|
renewThresholdDays: options.renewThresholdDays ?? 10, // Changed to 10 days as per requirements
|
|
146
152
|
httpsRedirectPort: options.httpsRedirectPort ?? 443,
|
|
147
153
|
renewCheckIntervalHours: options.renewCheckIntervalHours ?? 24,
|
|
154
|
+
enabled: options.enabled ?? true, // Enable by default
|
|
155
|
+
autoRenew: options.autoRenew ?? true, // Auto-renew by default
|
|
156
|
+
certificateStore: options.certificateStore ?? './certs', // Default store location
|
|
157
|
+
skipConfiguredCerts: options.skipConfiguredCerts ?? false
|
|
148
158
|
};
|
|
149
159
|
}
|
|
150
160
|
|
|
@@ -160,8 +170,19 @@ export class Port80Handler extends plugins.EventEmitter {
|
|
|
160
170
|
throw new ServerError('Server is shutting down');
|
|
161
171
|
}
|
|
162
172
|
|
|
173
|
+
// Skip if disabled
|
|
174
|
+
if (this.options.enabled === false) {
|
|
175
|
+
console.log('Port80Handler is disabled, skipping start');
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
|
|
163
179
|
return new Promise((resolve, reject) => {
|
|
164
180
|
try {
|
|
181
|
+
// Load certificates from store if enabled
|
|
182
|
+
if (this.options.certificateStore) {
|
|
183
|
+
this.loadCertificatesFromStore();
|
|
184
|
+
}
|
|
185
|
+
|
|
165
186
|
this.server = plugins.http.createServer((req, res) => this.handleRequest(req, res));
|
|
166
187
|
|
|
167
188
|
this.server.on('error', (error: NodeJS.ErrnoException) => {
|
|
@@ -332,6 +353,11 @@ export class Port80Handler extends plugins.EventEmitter {
|
|
|
332
353
|
|
|
333
354
|
console.log(`Certificate set for ${domain}`);
|
|
334
355
|
|
|
356
|
+
// Save certificate to store if enabled
|
|
357
|
+
if (this.options.certificateStore) {
|
|
358
|
+
this.saveCertificateToStore(domain, certificate, privateKey);
|
|
359
|
+
}
|
|
360
|
+
|
|
335
361
|
// Emit certificate event
|
|
336
362
|
this.emitCertificateEvent(Port80HandlerEvents.CERTIFICATE_ISSUED, {
|
|
337
363
|
domain,
|
|
@@ -365,6 +391,135 @@ export class Port80Handler extends plugins.EventEmitter {
|
|
|
365
391
|
};
|
|
366
392
|
}
|
|
367
393
|
|
|
394
|
+
/**
|
|
395
|
+
* Saves a certificate to the filesystem store
|
|
396
|
+
* @param domain The domain for the certificate
|
|
397
|
+
* @param certificate The certificate (PEM format)
|
|
398
|
+
* @param privateKey The private key (PEM format)
|
|
399
|
+
* @private
|
|
400
|
+
*/
|
|
401
|
+
private saveCertificateToStore(domain: string, certificate: string, privateKey: string): void {
|
|
402
|
+
// Skip if certificate store is not enabled
|
|
403
|
+
if (!this.options.certificateStore) return;
|
|
404
|
+
|
|
405
|
+
try {
|
|
406
|
+
const storePath = this.options.certificateStore;
|
|
407
|
+
|
|
408
|
+
// Ensure the directory exists
|
|
409
|
+
if (!fs.existsSync(storePath)) {
|
|
410
|
+
fs.mkdirSync(storePath, { recursive: true });
|
|
411
|
+
console.log(`Created certificate store directory: ${storePath}`);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const certPath = path.join(storePath, `${domain}.cert.pem`);
|
|
415
|
+
const keyPath = path.join(storePath, `${domain}.key.pem`);
|
|
416
|
+
|
|
417
|
+
// Write certificate and private key files
|
|
418
|
+
fs.writeFileSync(certPath, certificate);
|
|
419
|
+
fs.writeFileSync(keyPath, privateKey);
|
|
420
|
+
|
|
421
|
+
// Set secure permissions for private key
|
|
422
|
+
try {
|
|
423
|
+
fs.chmodSync(keyPath, 0o600);
|
|
424
|
+
} catch (err) {
|
|
425
|
+
console.log(`Warning: Could not set secure permissions on ${keyPath}`);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
console.log(`Saved certificate for ${domain} to ${certPath}`);
|
|
429
|
+
} catch (err) {
|
|
430
|
+
console.error(`Error saving certificate for ${domain}:`, err);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Loads certificates from the certificate store
|
|
436
|
+
* @private
|
|
437
|
+
*/
|
|
438
|
+
private loadCertificatesFromStore(): void {
|
|
439
|
+
if (!this.options.certificateStore) return;
|
|
440
|
+
|
|
441
|
+
try {
|
|
442
|
+
const storePath = this.options.certificateStore;
|
|
443
|
+
|
|
444
|
+
// Ensure the directory exists
|
|
445
|
+
if (!fs.existsSync(storePath)) {
|
|
446
|
+
fs.mkdirSync(storePath, { recursive: true });
|
|
447
|
+
console.log(`Created certificate store directory: ${storePath}`);
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// Get list of certificate files
|
|
452
|
+
const files = fs.readdirSync(storePath);
|
|
453
|
+
const certFiles = files.filter(file => file.endsWith('.cert.pem'));
|
|
454
|
+
|
|
455
|
+
// Load each certificate
|
|
456
|
+
for (const certFile of certFiles) {
|
|
457
|
+
const domain = certFile.replace('.cert.pem', '');
|
|
458
|
+
const keyFile = `${domain}.key.pem`;
|
|
459
|
+
|
|
460
|
+
// Skip if key file doesn't exist
|
|
461
|
+
if (!files.includes(keyFile)) {
|
|
462
|
+
console.log(`Warning: Found certificate for ${domain} but no key file`);
|
|
463
|
+
continue;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// Skip if we should skip configured certs
|
|
467
|
+
if (this.options.skipConfiguredCerts) {
|
|
468
|
+
const domainInfo = this.domainCertificates.get(domain);
|
|
469
|
+
if (domainInfo && domainInfo.certObtained) {
|
|
470
|
+
console.log(`Skipping already configured certificate for ${domain}`);
|
|
471
|
+
continue;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// Load certificate and key
|
|
476
|
+
try {
|
|
477
|
+
const certificate = fs.readFileSync(path.join(storePath, certFile), 'utf8');
|
|
478
|
+
const privateKey = fs.readFileSync(path.join(storePath, keyFile), 'utf8');
|
|
479
|
+
|
|
480
|
+
// Extract expiry date
|
|
481
|
+
let expiryDate: Date | undefined;
|
|
482
|
+
try {
|
|
483
|
+
const matches = certificate.match(/Not After\s*:\s*(.*?)(?:\n|$)/i);
|
|
484
|
+
if (matches && matches[1]) {
|
|
485
|
+
expiryDate = new Date(matches[1]);
|
|
486
|
+
}
|
|
487
|
+
} catch (err) {
|
|
488
|
+
console.log(`Warning: Could not extract expiry date from certificate for ${domain}`);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// Check if domain is already registered
|
|
492
|
+
let domainInfo = this.domainCertificates.get(domain);
|
|
493
|
+
if (!domainInfo) {
|
|
494
|
+
// Register domain if not already registered
|
|
495
|
+
domainInfo = {
|
|
496
|
+
options: {
|
|
497
|
+
domainName: domain,
|
|
498
|
+
sslRedirect: true,
|
|
499
|
+
acmeMaintenance: true
|
|
500
|
+
},
|
|
501
|
+
certObtained: false,
|
|
502
|
+
obtainingInProgress: false
|
|
503
|
+
};
|
|
504
|
+
this.domainCertificates.set(domain, domainInfo);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// Set certificate
|
|
508
|
+
domainInfo.certificate = certificate;
|
|
509
|
+
domainInfo.privateKey = privateKey;
|
|
510
|
+
domainInfo.certObtained = true;
|
|
511
|
+
domainInfo.expiryDate = expiryDate;
|
|
512
|
+
|
|
513
|
+
console.log(`Loaded certificate for ${domain} from store, valid until ${expiryDate?.toISOString() || 'unknown'}`);
|
|
514
|
+
} catch (err) {
|
|
515
|
+
console.error(`Error loading certificate for ${domain}:`, err);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
} catch (err) {
|
|
519
|
+
console.error('Error loading certificates from store:', err);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
368
523
|
/**
|
|
369
524
|
* Check if a domain is a glob pattern
|
|
370
525
|
* @param domain Domain to check
|
|
@@ -710,6 +865,11 @@ export class Port80Handler extends plugins.EventEmitter {
|
|
|
710
865
|
|
|
711
866
|
console.log(`Certificate ${isRenewal ? 'renewed' : 'obtained'} for ${domain}`);
|
|
712
867
|
|
|
868
|
+
// Save the certificate to the store if enabled
|
|
869
|
+
if (this.options.certificateStore) {
|
|
870
|
+
this.saveCertificateToStore(domain, certificate, privateKey);
|
|
871
|
+
}
|
|
872
|
+
|
|
713
873
|
// Emit the appropriate event
|
|
714
874
|
const eventType = isRenewal
|
|
715
875
|
? Port80HandlerEvents.CERTIFICATE_RENEWED
|
|
@@ -834,6 +994,12 @@ export class Port80Handler extends plugins.EventEmitter {
|
|
|
834
994
|
return;
|
|
835
995
|
}
|
|
836
996
|
|
|
997
|
+
// Skip renewal if auto-renewal is disabled
|
|
998
|
+
if (this.options.autoRenew === false) {
|
|
999
|
+
console.log('Auto-renewal is disabled, skipping certificate renewal check');
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
1002
|
+
|
|
837
1003
|
console.log('Checking for certificates that need renewal...');
|
|
838
1004
|
|
|
839
1005
|
const now = new Date();
|
|
@@ -928,4 +1094,86 @@ export class Port80Handler extends plugins.EventEmitter {
|
|
|
928
1094
|
private emitCertificateEvent(eventType: Port80HandlerEvents, data: ICertificateData): void {
|
|
929
1095
|
this.emit(eventType, data);
|
|
930
1096
|
}
|
|
1097
|
+
|
|
1098
|
+
/**
|
|
1099
|
+
* Gets all domains and their certificate status
|
|
1100
|
+
* @returns Map of domains to certificate status
|
|
1101
|
+
*/
|
|
1102
|
+
public getDomainCertificateStatus(): Map<string, {
|
|
1103
|
+
certObtained: boolean;
|
|
1104
|
+
expiryDate?: Date;
|
|
1105
|
+
daysRemaining?: number;
|
|
1106
|
+
obtainingInProgress: boolean;
|
|
1107
|
+
lastRenewalAttempt?: Date;
|
|
1108
|
+
}> {
|
|
1109
|
+
const result = new Map<string, {
|
|
1110
|
+
certObtained: boolean;
|
|
1111
|
+
expiryDate?: Date;
|
|
1112
|
+
daysRemaining?: number;
|
|
1113
|
+
obtainingInProgress: boolean;
|
|
1114
|
+
lastRenewalAttempt?: Date;
|
|
1115
|
+
}>();
|
|
1116
|
+
|
|
1117
|
+
const now = new Date();
|
|
1118
|
+
|
|
1119
|
+
for (const [domain, domainInfo] of this.domainCertificates.entries()) {
|
|
1120
|
+
// Skip glob patterns
|
|
1121
|
+
if (this.isGlobPattern(domain)) continue;
|
|
1122
|
+
|
|
1123
|
+
const status: {
|
|
1124
|
+
certObtained: boolean;
|
|
1125
|
+
expiryDate?: Date;
|
|
1126
|
+
daysRemaining?: number;
|
|
1127
|
+
obtainingInProgress: boolean;
|
|
1128
|
+
lastRenewalAttempt?: Date;
|
|
1129
|
+
} = {
|
|
1130
|
+
certObtained: domainInfo.certObtained,
|
|
1131
|
+
expiryDate: domainInfo.expiryDate,
|
|
1132
|
+
obtainingInProgress: domainInfo.obtainingInProgress,
|
|
1133
|
+
lastRenewalAttempt: domainInfo.lastRenewalAttempt
|
|
1134
|
+
};
|
|
1135
|
+
|
|
1136
|
+
// Calculate days remaining if expiry date is available
|
|
1137
|
+
if (domainInfo.expiryDate) {
|
|
1138
|
+
const daysRemaining = Math.ceil(
|
|
1139
|
+
(domainInfo.expiryDate.getTime() - now.getTime()) / (24 * 60 * 60 * 1000)
|
|
1140
|
+
);
|
|
1141
|
+
status.daysRemaining = daysRemaining;
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
result.set(domain, status);
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
return result;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
/**
|
|
1151
|
+
* Gets information about managed domains
|
|
1152
|
+
* @returns Array of domain information
|
|
1153
|
+
*/
|
|
1154
|
+
public getManagedDomains(): Array<{
|
|
1155
|
+
domain: string;
|
|
1156
|
+
isGlobPattern: boolean;
|
|
1157
|
+
hasCertificate: boolean;
|
|
1158
|
+
hasForwarding: boolean;
|
|
1159
|
+
sslRedirect: boolean;
|
|
1160
|
+
acmeMaintenance: boolean;
|
|
1161
|
+
}> {
|
|
1162
|
+
return Array.from(this.domainCertificates.entries()).map(([domain, info]) => ({
|
|
1163
|
+
domain,
|
|
1164
|
+
isGlobPattern: this.isGlobPattern(domain),
|
|
1165
|
+
hasCertificate: info.certObtained,
|
|
1166
|
+
hasForwarding: !!info.options.forward,
|
|
1167
|
+
sslRedirect: info.options.sslRedirect,
|
|
1168
|
+
acmeMaintenance: info.options.acmeMaintenance
|
|
1169
|
+
}));
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
/**
|
|
1173
|
+
* Gets configuration details
|
|
1174
|
+
* @returns Current configuration
|
|
1175
|
+
*/
|
|
1176
|
+
public getConfig(): Required<IPort80HandlerOptions> {
|
|
1177
|
+
return { ...this.options };
|
|
1178
|
+
}
|
|
931
1179
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as plugins from '
|
|
1
|
+
import * as plugins from '../plugins.js';
|
|
2
2
|
import type { IConnectionRecord, IPortProxySettings } from './classes.pp.interfaces.js';
|
|
3
3
|
import { SecurityManager } from './classes.pp.securitymanager.js';
|
|
4
4
|
import { TimeoutManager } from './classes.pp.timeoutmanager.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as plugins from '
|
|
1
|
+
import * as plugins from '../plugins.js';
|
|
2
2
|
|
|
3
3
|
/** Domain configuration with per-domain allowed port ranges */
|
|
4
4
|
export interface IDomainConfig {
|
|
@@ -78,16 +78,42 @@ export interface IPortProxySettings {
|
|
|
78
78
|
useNetworkProxy?: number[]; // Array of ports to forward to NetworkProxy
|
|
79
79
|
networkProxyPort?: number; // Port where NetworkProxy is listening (default: 8443)
|
|
80
80
|
|
|
81
|
-
//
|
|
82
|
-
|
|
81
|
+
// Port80Handler configuration (replaces ACME configuration)
|
|
82
|
+
port80HandlerConfig?: {
|
|
83
83
|
enabled?: boolean; // Whether to enable automatic certificate management
|
|
84
|
-
port?: number; // Port to listen on for ACME challenges (default: 80)
|
|
84
|
+
port?: number; // Port to listen on for ACME challenges (default: 80)
|
|
85
85
|
contactEmail?: string; // Email for Let's Encrypt account
|
|
86
86
|
useProduction?: boolean; // Whether to use Let's Encrypt production (default: false for staging)
|
|
87
87
|
renewThresholdDays?: number; // Days before expiry to renew certificates (default: 30)
|
|
88
88
|
autoRenew?: boolean; // Whether to automatically renew certificates (default: true)
|
|
89
89
|
certificateStore?: string; // Directory to store certificates (default: ./certs)
|
|
90
|
-
skipConfiguredCerts?: boolean; // Skip domains that already have certificates
|
|
90
|
+
skipConfiguredCerts?: boolean; // Skip domains that already have certificates
|
|
91
|
+
httpsRedirectPort?: number; // Port to redirect HTTP requests to HTTPS (default: 443)
|
|
92
|
+
renewCheckIntervalHours?: number; // How often to check for renewals (default: 24)
|
|
93
|
+
// Domain-specific forwarding configurations
|
|
94
|
+
domainForwards?: Array<{
|
|
95
|
+
domain: string;
|
|
96
|
+
forwardConfig?: {
|
|
97
|
+
ip: string;
|
|
98
|
+
port: number;
|
|
99
|
+
};
|
|
100
|
+
acmeForwardConfig?: {
|
|
101
|
+
ip: string;
|
|
102
|
+
port: number;
|
|
103
|
+
};
|
|
104
|
+
}>;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// Legacy ACME configuration (deprecated, use port80HandlerConfig instead)
|
|
108
|
+
acme?: {
|
|
109
|
+
enabled?: boolean;
|
|
110
|
+
port?: number;
|
|
111
|
+
contactEmail?: string;
|
|
112
|
+
useProduction?: boolean;
|
|
113
|
+
renewThresholdDays?: number;
|
|
114
|
+
autoRenew?: boolean;
|
|
115
|
+
certificateStore?: string;
|
|
116
|
+
skipConfiguredCerts?: boolean;
|
|
91
117
|
};
|
|
92
118
|
}
|
|
93
119
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import * as plugins from '
|
|
2
|
-
import { NetworkProxy } from '
|
|
1
|
+
import * as plugins from '../plugins.js';
|
|
2
|
+
import { NetworkProxy } from '../networkproxy/classes.np.networkproxy.js';
|
|
3
|
+
import { Port80Handler, Port80HandlerEvents, type ICertificateData } from '../port80handler/classes.port80handler.js';
|
|
3
4
|
import type { IConnectionRecord, IPortProxySettings, IDomainConfig } from './classes.pp.interfaces.js';
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -7,9 +8,28 @@ import type { IConnectionRecord, IPortProxySettings, IDomainConfig } from './cla
|
|
|
7
8
|
*/
|
|
8
9
|
export class NetworkProxyBridge {
|
|
9
10
|
private networkProxy: NetworkProxy | null = null;
|
|
11
|
+
private port80Handler: Port80Handler | null = null;
|
|
10
12
|
|
|
11
13
|
constructor(private settings: IPortProxySettings) {}
|
|
12
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Set the Port80Handler to use for certificate management
|
|
17
|
+
*/
|
|
18
|
+
public setPort80Handler(handler: Port80Handler): void {
|
|
19
|
+
this.port80Handler = handler;
|
|
20
|
+
|
|
21
|
+
// Register for certificate events
|
|
22
|
+
handler.on(Port80HandlerEvents.CERTIFICATE_ISSUED, this.handleCertificateEvent.bind(this));
|
|
23
|
+
handler.on(Port80HandlerEvents.CERTIFICATE_RENEWED, this.handleCertificateEvent.bind(this));
|
|
24
|
+
|
|
25
|
+
// If NetworkProxy is already initialized, connect it with Port80Handler
|
|
26
|
+
if (this.networkProxy) {
|
|
27
|
+
this.networkProxy.setExternalPort80Handler(handler);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
console.log('Port80Handler connected to NetworkProxyBridge');
|
|
31
|
+
}
|
|
32
|
+
|
|
13
33
|
/**
|
|
14
34
|
* Initialize NetworkProxy instance
|
|
15
35
|
*/
|
|
@@ -20,22 +40,61 @@ export class NetworkProxyBridge {
|
|
|
20
40
|
port: this.settings.networkProxyPort!,
|
|
21
41
|
portProxyIntegration: true,
|
|
22
42
|
logLevel: this.settings.enableDetailedLogging ? 'debug' : 'info',
|
|
43
|
+
useExternalPort80Handler: !!this.port80Handler // Use Port80Handler if available
|
|
23
44
|
};
|
|
24
45
|
|
|
25
|
-
//
|
|
26
|
-
if (this.settings.acme) {
|
|
46
|
+
// Copy ACME settings for backward compatibility (if port80HandlerConfig not set)
|
|
47
|
+
if (!this.settings.port80HandlerConfig && this.settings.acme) {
|
|
27
48
|
networkProxyOptions.acme = { ...this.settings.acme };
|
|
28
49
|
}
|
|
29
50
|
|
|
30
51
|
this.networkProxy = new NetworkProxy(networkProxyOptions);
|
|
31
52
|
|
|
32
53
|
console.log(`Initialized NetworkProxy on port ${this.settings.networkProxyPort}`);
|
|
54
|
+
|
|
55
|
+
// Connect Port80Handler if available
|
|
56
|
+
if (this.port80Handler) {
|
|
57
|
+
this.networkProxy.setExternalPort80Handler(this.port80Handler);
|
|
58
|
+
}
|
|
33
59
|
|
|
34
60
|
// Convert and apply domain configurations to NetworkProxy
|
|
35
61
|
await this.syncDomainConfigsToNetworkProxy();
|
|
36
62
|
}
|
|
37
63
|
}
|
|
38
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Handle certificate issuance or renewal events
|
|
67
|
+
*/
|
|
68
|
+
private handleCertificateEvent(data: ICertificateData): void {
|
|
69
|
+
if (!this.networkProxy) return;
|
|
70
|
+
|
|
71
|
+
console.log(`Received certificate for ${data.domain} from Port80Handler, updating NetworkProxy`);
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
// Find existing config for this domain
|
|
75
|
+
const existingConfigs = this.networkProxy.getProxyConfigs()
|
|
76
|
+
.filter(config => config.hostName === data.domain);
|
|
77
|
+
|
|
78
|
+
if (existingConfigs.length > 0) {
|
|
79
|
+
// Update existing configs with new certificate
|
|
80
|
+
for (const config of existingConfigs) {
|
|
81
|
+
config.privateKey = data.privateKey;
|
|
82
|
+
config.publicKey = data.certificate;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Apply updated configs
|
|
86
|
+
this.networkProxy.updateProxyConfigs(existingConfigs)
|
|
87
|
+
.then(() => console.log(`Updated certificate for ${data.domain} in NetworkProxy`))
|
|
88
|
+
.catch(err => console.log(`Error updating certificate in NetworkProxy: ${err}`));
|
|
89
|
+
} else {
|
|
90
|
+
// Create a new config for this domain
|
|
91
|
+
console.log(`No existing config found for ${data.domain}, creating new config in NetworkProxy`);
|
|
92
|
+
}
|
|
93
|
+
} catch (err) {
|
|
94
|
+
console.log(`Error handling certificate event: ${err}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
39
98
|
/**
|
|
40
99
|
* Get the NetworkProxy instance
|
|
41
100
|
*/
|
|
@@ -57,22 +116,6 @@ export class NetworkProxyBridge {
|
|
|
57
116
|
if (this.networkProxy) {
|
|
58
117
|
await this.networkProxy.start();
|
|
59
118
|
console.log(`NetworkProxy started on port ${this.settings.networkProxyPort}`);
|
|
60
|
-
|
|
61
|
-
// Log ACME status
|
|
62
|
-
if (this.settings.acme?.enabled) {
|
|
63
|
-
console.log(
|
|
64
|
-
`ACME certificate management is enabled (${
|
|
65
|
-
this.settings.acme.useProduction ? 'Production' : 'Staging'
|
|
66
|
-
} mode)`
|
|
67
|
-
);
|
|
68
|
-
console.log(`ACME HTTP challenge server on port ${this.settings.acme.port}`);
|
|
69
|
-
|
|
70
|
-
// Register domains for ACME certificates if enabled
|
|
71
|
-
if (this.networkProxy.options.acme?.enabled) {
|
|
72
|
-
console.log('Registering domains with ACME certificate manager...');
|
|
73
|
-
// The NetworkProxy will handle this internally via registerDomainsWithAcmeManager()
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
119
|
}
|
|
77
120
|
}
|
|
78
121
|
|
|
@@ -85,17 +128,43 @@ export class NetworkProxyBridge {
|
|
|
85
128
|
console.log('Stopping NetworkProxy...');
|
|
86
129
|
await this.networkProxy.stop();
|
|
87
130
|
console.log('NetworkProxy stopped successfully');
|
|
88
|
-
|
|
89
|
-
// Log ACME shutdown if it was enabled
|
|
90
|
-
if (this.settings.acme?.enabled) {
|
|
91
|
-
console.log('ACME certificate manager stopped');
|
|
92
|
-
}
|
|
93
131
|
} catch (err) {
|
|
94
132
|
console.log(`Error stopping NetworkProxy: ${err}`);
|
|
95
133
|
}
|
|
96
134
|
}
|
|
97
135
|
}
|
|
98
136
|
|
|
137
|
+
/**
|
|
138
|
+
* Register domains with Port80Handler
|
|
139
|
+
*/
|
|
140
|
+
public registerDomainsWithPort80Handler(domains: string[]): void {
|
|
141
|
+
if (!this.port80Handler) {
|
|
142
|
+
console.log('Cannot register domains - Port80Handler not initialized');
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
for (const domain of domains) {
|
|
147
|
+
// Skip wildcards
|
|
148
|
+
if (domain.includes('*')) {
|
|
149
|
+
console.log(`Skipping wildcard domain for ACME: ${domain}`);
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Register the domain
|
|
154
|
+
try {
|
|
155
|
+
this.port80Handler.addDomain({
|
|
156
|
+
domainName: domain,
|
|
157
|
+
sslRedirect: true,
|
|
158
|
+
acmeMaintenance: true
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
console.log(`Registered domain with Port80Handler: ${domain}`);
|
|
162
|
+
} catch (err) {
|
|
163
|
+
console.log(`Error registering domain ${domain} with Port80Handler: ${err}`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
99
168
|
/**
|
|
100
169
|
* Forwards a TLS connection to a NetworkProxy for handling
|
|
101
170
|
*/
|
|
@@ -207,14 +276,20 @@ export class NetworkProxyBridge {
|
|
|
207
276
|
certPair
|
|
208
277
|
);
|
|
209
278
|
|
|
210
|
-
// Log ACME-eligible domains
|
|
211
|
-
|
|
279
|
+
// Log ACME-eligible domains
|
|
280
|
+
const acmeEnabled = this.settings.port80HandlerConfig?.enabled || this.settings.acme?.enabled;
|
|
281
|
+
if (acmeEnabled) {
|
|
212
282
|
const acmeEligibleDomains = proxyConfigs
|
|
213
283
|
.filter((config) => !config.hostName.includes('*')) // Exclude wildcards
|
|
214
284
|
.map((config) => config.hostName);
|
|
215
285
|
|
|
216
286
|
if (acmeEligibleDomains.length > 0) {
|
|
217
287
|
console.log(`Domains eligible for ACME certificates: ${acmeEligibleDomains.join(', ')}`);
|
|
288
|
+
|
|
289
|
+
// Register these domains with Port80Handler if available
|
|
290
|
+
if (this.port80Handler) {
|
|
291
|
+
this.registerDomainsWithPort80Handler(acmeEligibleDomains);
|
|
292
|
+
}
|
|
218
293
|
} else {
|
|
219
294
|
console.log('No domains eligible for ACME certificates found in configuration');
|
|
220
295
|
}
|
|
@@ -232,12 +307,38 @@ export class NetworkProxyBridge {
|
|
|
232
307
|
* Request a certificate for a specific domain
|
|
233
308
|
*/
|
|
234
309
|
public async requestCertificate(domain: string): Promise<boolean> {
|
|
310
|
+
// Delegate to Port80Handler if available
|
|
311
|
+
if (this.port80Handler) {
|
|
312
|
+
try {
|
|
313
|
+
// Check if the domain is already registered
|
|
314
|
+
const cert = this.port80Handler.getCertificate(domain);
|
|
315
|
+
if (cert) {
|
|
316
|
+
console.log(`Certificate already exists for ${domain}`);
|
|
317
|
+
return true;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Register the domain for certificate issuance
|
|
321
|
+
this.port80Handler.addDomain({
|
|
322
|
+
domainName: domain,
|
|
323
|
+
sslRedirect: true,
|
|
324
|
+
acmeMaintenance: true
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
console.log(`Domain ${domain} registered for certificate issuance`);
|
|
328
|
+
return true;
|
|
329
|
+
} catch (err) {
|
|
330
|
+
console.log(`Error requesting certificate: ${err}`);
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Fall back to NetworkProxy if Port80Handler is not available
|
|
235
336
|
if (!this.networkProxy) {
|
|
236
337
|
console.log('Cannot request certificate - NetworkProxy not initialized');
|
|
237
338
|
return false;
|
|
238
339
|
}
|
|
239
340
|
|
|
240
|
-
if (!this.settings.acme?.enabled) {
|
|
341
|
+
if (!this.settings.port80HandlerConfig?.enabled && !this.settings.acme?.enabled) {
|
|
241
342
|
console.log('Cannot request certificate - ACME is not enabled');
|
|
242
343
|
return false;
|
|
243
344
|
}
|