@push.rocks/smartproxy 23.0.0 → 23.1.1

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.
Files changed (161) hide show
  1. package/changelog.md +17 -0
  2. package/dist_rust/{rustproxy → rustproxy_linux_amd64} +0 -0
  3. package/dist_rust/rustproxy_linux_arm64 +0 -0
  4. package/dist_ts/00_commitinfo_data.js +1 -1
  5. package/dist_ts/plugins.d.ts +2 -1
  6. package/dist_ts/plugins.js +3 -2
  7. package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.d.ts +9 -21
  8. package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.js +84 -212
  9. package/dist_ts/proxies/smart-proxy/smart-proxy.js +2 -3
  10. package/npmextra.json +3 -0
  11. package/package.json +13 -11
  12. package/readme.md +41 -11
  13. package/ts/00_commitinfo_data.ts +1 -1
  14. package/ts/plugins.ts +2 -0
  15. package/ts/proxies/smart-proxy/rust-proxy-bridge.ts +103 -233
  16. package/ts/proxies/smart-proxy/smart-proxy.ts +1 -2
  17. package/dist_ts/common/eventUtils.d.ts +0 -14
  18. package/dist_ts/common/eventUtils.js +0 -20
  19. package/dist_ts/common/types.d.ts +0 -82
  20. package/dist_ts/common/types.js +0 -15
  21. package/dist_ts/core/utils/event-system.d.ts +0 -200
  22. package/dist_ts/core/utils/event-system.js +0 -224
  23. package/dist_ts/core/utils/event-utils.d.ts +0 -15
  24. package/dist_ts/core/utils/event-utils.js +0 -11
  25. package/dist_ts/core/utils/route-manager.d.ts +0 -88
  26. package/dist_ts/core/utils/route-manager.js +0 -342
  27. package/dist_ts/core/utils/route-utils.d.ts +0 -28
  28. package/dist_ts/core/utils/route-utils.js +0 -67
  29. package/dist_ts/detection/detectors/http-detector-v2.d.ts +0 -33
  30. package/dist_ts/detection/detectors/http-detector-v2.js +0 -87
  31. package/dist_ts/detection/detectors/tls-detector-v2.d.ts +0 -33
  32. package/dist_ts/detection/detectors/tls-detector-v2.js +0 -80
  33. package/dist_ts/detection/protocol-detector-v2.d.ts +0 -46
  34. package/dist_ts/detection/protocol-detector-v2.js +0 -116
  35. package/dist_ts/forwarding/config/forwarding-types.d.ts +0 -42
  36. package/dist_ts/forwarding/config/forwarding-types.js +0 -18
  37. package/dist_ts/forwarding/config/index.d.ts +0 -9
  38. package/dist_ts/forwarding/config/index.js +0 -10
  39. package/dist_ts/forwarding/factory/forwarding-factory.d.ts +0 -25
  40. package/dist_ts/forwarding/factory/forwarding-factory.js +0 -172
  41. package/dist_ts/forwarding/factory/index.d.ts +0 -4
  42. package/dist_ts/forwarding/factory/index.js +0 -5
  43. package/dist_ts/forwarding/handlers/base-handler.d.ts +0 -62
  44. package/dist_ts/forwarding/handlers/base-handler.js +0 -121
  45. package/dist_ts/forwarding/handlers/http-handler.d.ts +0 -30
  46. package/dist_ts/forwarding/handlers/http-handler.js +0 -143
  47. package/dist_ts/forwarding/handlers/https-passthrough-handler.d.ts +0 -29
  48. package/dist_ts/forwarding/handlers/https-passthrough-handler.js +0 -156
  49. package/dist_ts/forwarding/handlers/https-terminate-to-http-handler.d.ts +0 -36
  50. package/dist_ts/forwarding/handlers/https-terminate-to-http-handler.js +0 -276
  51. package/dist_ts/forwarding/handlers/https-terminate-to-https-handler.d.ts +0 -35
  52. package/dist_ts/forwarding/handlers/https-terminate-to-https-handler.js +0 -261
  53. package/dist_ts/forwarding/handlers/index.d.ts +0 -8
  54. package/dist_ts/forwarding/handlers/index.js +0 -9
  55. package/dist_ts/forwarding/index.d.ts +0 -13
  56. package/dist_ts/forwarding/index.js +0 -16
  57. package/dist_ts/http/index.d.ts +0 -5
  58. package/dist_ts/http/index.js +0 -8
  59. package/dist_ts/http/models/http-types.d.ts +0 -6
  60. package/dist_ts/http/models/http-types.js +0 -7
  61. package/dist_ts/http/router/index.d.ts +0 -8
  62. package/dist_ts/http/router/index.js +0 -7
  63. package/dist_ts/http/router/proxy-router.d.ts +0 -115
  64. package/dist_ts/http/router/proxy-router.js +0 -325
  65. package/dist_ts/http/router/route-router.d.ts +0 -108
  66. package/dist_ts/http/router/route-router.js +0 -393
  67. package/dist_ts/protocols/tls/constants.d.ts +0 -122
  68. package/dist_ts/protocols/tls/constants.js +0 -135
  69. package/dist_ts/protocols/tls/parser.d.ts +0 -53
  70. package/dist_ts/protocols/tls/parser.js +0 -294
  71. package/dist_ts/protocols/tls/types.d.ts +0 -65
  72. package/dist_ts/protocols/tls/types.js +0 -5
  73. package/dist_ts/proxies/http-proxy/certificate-manager.d.ts +0 -95
  74. package/dist_ts/proxies/http-proxy/certificate-manager.js +0 -214
  75. package/dist_ts/proxies/http-proxy/connection-pool.d.ts +0 -47
  76. package/dist_ts/proxies/http-proxy/connection-pool.js +0 -195
  77. package/dist_ts/proxies/http-proxy/context-creator.d.ts +0 -34
  78. package/dist_ts/proxies/http-proxy/context-creator.js +0 -108
  79. package/dist_ts/proxies/http-proxy/default-certificates.d.ts +0 -54
  80. package/dist_ts/proxies/http-proxy/default-certificates.js +0 -127
  81. package/dist_ts/proxies/http-proxy/function-cache.d.ts +0 -95
  82. package/dist_ts/proxies/http-proxy/function-cache.js +0 -215
  83. package/dist_ts/proxies/http-proxy/handlers/index.d.ts +0 -4
  84. package/dist_ts/proxies/http-proxy/handlers/index.js +0 -6
  85. package/dist_ts/proxies/http-proxy/handlers/redirect-handler.d.ts +0 -18
  86. package/dist_ts/proxies/http-proxy/handlers/redirect-handler.js +0 -78
  87. package/dist_ts/proxies/http-proxy/handlers/static-handler.d.ts +0 -19
  88. package/dist_ts/proxies/http-proxy/handlers/static-handler.js +0 -211
  89. package/dist_ts/proxies/http-proxy/http-proxy.d.ts +0 -117
  90. package/dist_ts/proxies/http-proxy/http-proxy.js +0 -521
  91. package/dist_ts/proxies/http-proxy/http-request-handler.d.ts +0 -40
  92. package/dist_ts/proxies/http-proxy/http-request-handler.js +0 -257
  93. package/dist_ts/proxies/http-proxy/http2-request-handler.d.ts +0 -24
  94. package/dist_ts/proxies/http-proxy/http2-request-handler.js +0 -201
  95. package/dist_ts/proxies/http-proxy/index.d.ts +0 -14
  96. package/dist_ts/proxies/http-proxy/index.js +0 -16
  97. package/dist_ts/proxies/http-proxy/models/http-types.d.ts +0 -117
  98. package/dist_ts/proxies/http-proxy/models/http-types.js +0 -92
  99. package/dist_ts/proxies/http-proxy/models/index.d.ts +0 -5
  100. package/dist_ts/proxies/http-proxy/models/index.js +0 -6
  101. package/dist_ts/proxies/http-proxy/models/types.d.ts +0 -75
  102. package/dist_ts/proxies/http-proxy/models/types.js +0 -35
  103. package/dist_ts/proxies/http-proxy/request-handler.d.ts +0 -97
  104. package/dist_ts/proxies/http-proxy/request-handler.js +0 -737
  105. package/dist_ts/proxies/http-proxy/security-manager.d.ts +0 -98
  106. package/dist_ts/proxies/http-proxy/security-manager.js +0 -341
  107. package/dist_ts/proxies/http-proxy/websocket-handler.d.ts +0 -50
  108. package/dist_ts/proxies/http-proxy/websocket-handler.js +0 -505
  109. package/dist_ts/proxies/smart-proxy/acme-state-manager.d.ts +0 -42
  110. package/dist_ts/proxies/smart-proxy/acme-state-manager.js +0 -101
  111. package/dist_ts/proxies/smart-proxy/cert-store.d.ts +0 -10
  112. package/dist_ts/proxies/smart-proxy/cert-store.js +0 -72
  113. package/dist_ts/proxies/smart-proxy/certificate-manager.d.ts +0 -164
  114. package/dist_ts/proxies/smart-proxy/certificate-manager.js +0 -745
  115. package/dist_ts/proxies/smart-proxy/connection-manager.d.ts +0 -128
  116. package/dist_ts/proxies/smart-proxy/connection-manager.js +0 -689
  117. package/dist_ts/proxies/smart-proxy/http-proxy-bridge.d.ts +0 -43
  118. package/dist_ts/proxies/smart-proxy/http-proxy-bridge.js +0 -180
  119. package/dist_ts/proxies/smart-proxy/metrics-collector.d.ts +0 -98
  120. package/dist_ts/proxies/smart-proxy/metrics-collector.js +0 -355
  121. package/dist_ts/proxies/smart-proxy/nftables-manager.d.ts +0 -82
  122. package/dist_ts/proxies/smart-proxy/nftables-manager.js +0 -237
  123. package/dist_ts/proxies/smart-proxy/port-manager.d.ts +0 -117
  124. package/dist_ts/proxies/smart-proxy/port-manager.js +0 -318
  125. package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +0 -60
  126. package/dist_ts/proxies/smart-proxy/route-connection-handler.js +0 -1407
  127. package/dist_ts/proxies/smart-proxy/route-manager.d.ts +0 -112
  128. package/dist_ts/proxies/smart-proxy/route-manager.js +0 -453
  129. package/dist_ts/proxies/smart-proxy/route-orchestrator.d.ts +0 -56
  130. package/dist_ts/proxies/smart-proxy/route-orchestrator.js +0 -204
  131. package/dist_ts/proxies/smart-proxy/rust-binary-locator.d.ts +0 -23
  132. package/dist_ts/proxies/smart-proxy/rust-binary-locator.js +0 -104
  133. package/dist_ts/proxies/smart-proxy/security-manager.d.ts +0 -74
  134. package/dist_ts/proxies/smart-proxy/security-manager.js +0 -227
  135. package/dist_ts/proxies/smart-proxy/throughput-tracker.d.ts +0 -36
  136. package/dist_ts/proxies/smart-proxy/throughput-tracker.js +0 -115
  137. package/dist_ts/proxies/smart-proxy/timeout-manager.d.ts +0 -48
  138. package/dist_ts/proxies/smart-proxy/timeout-manager.js +0 -158
  139. package/dist_ts/proxies/smart-proxy/tls-manager.d.ts +0 -50
  140. package/dist_ts/proxies/smart-proxy/tls-manager.js +0 -110
  141. package/dist_ts/proxies/smart-proxy/utils/route-patterns.d.ts +0 -161
  142. package/dist_ts/proxies/smart-proxy/utils/route-patterns.js +0 -282
  143. package/dist_ts/proxies/smart-proxy/utils/route-validators.d.ts +0 -73
  144. package/dist_ts/proxies/smart-proxy/utils/route-validators.js +0 -259
  145. package/dist_ts/routing/router/proxy-router.d.ts +0 -115
  146. package/dist_ts/routing/router/proxy-router.js +0 -325
  147. package/dist_ts/routing/router/route-router.d.ts +0 -108
  148. package/dist_ts/routing/router/route-router.js +0 -393
  149. package/dist_ts/tls/alerts/index.d.ts +0 -4
  150. package/dist_ts/tls/alerts/index.js +0 -5
  151. package/dist_ts/tls/alerts/tls-alert.d.ts +0 -150
  152. package/dist_ts/tls/alerts/tls-alert.js +0 -226
  153. package/dist_ts/tls/sni/client-hello-parser.d.ts +0 -100
  154. package/dist_ts/tls/sni/client-hello-parser.js +0 -464
  155. package/dist_ts/tls/sni/sni-extraction.d.ts +0 -58
  156. package/dist_ts/tls/sni/sni-extraction.js +0 -275
  157. package/dist_ts/tls/utils/index.d.ts +0 -4
  158. package/dist_ts/tls/utils/index.js +0 -5
  159. package/dist_ts/tls/utils/tls-utils.d.ts +0 -49
  160. package/dist_ts/tls/utils/tls-utils.js +0 -75
  161. package/ts/proxies/smart-proxy/rust-binary-locator.ts +0 -112
@@ -1,745 +0,0 @@
1
- import * as plugins from '../../plugins.js';
2
- import { HttpProxy } from '../http-proxy/index.js';
3
- import { CertStore } from './cert-store.js';
4
- import { logger } from '../../core/utils/logger.js';
5
- import { SocketHandlers } from './utils/route-helpers.js';
6
- export class SmartCertManager {
7
- constructor(routes, certDir = './certs', acmeOptions, initialState) {
8
- this.routes = routes;
9
- this.certDir = certDir;
10
- this.acmeOptions = acmeOptions;
11
- this.initialState = initialState;
12
- this.smartAcme = null;
13
- this.httpProxy = null;
14
- this.renewalTimer = null;
15
- this.pendingChallenges = new Map();
16
- this.challengeRoute = null;
17
- // Track certificate status by route name
18
- this.certStatus = new Map();
19
- // Global ACME defaults from top-level configuration
20
- this.globalAcmeDefaults = null;
21
- // Flag to track if challenge route is currently active
22
- this.challengeRouteActive = false;
23
- // Flag to track if provisioning is in progress
24
- this.isProvisioning = false;
25
- // ACME state manager reference
26
- this.acmeStateManager = null;
27
- // Whether to fallback to ACME if custom provision fails
28
- this.certProvisionFallbackToAcme = true;
29
- this.certStore = new CertStore(certDir);
30
- // Apply initial state if provided
31
- if (initialState) {
32
- this.challengeRouteActive = initialState.challengeRouteActive || false;
33
- }
34
- }
35
- setHttpProxy(httpProxy) {
36
- this.httpProxy = httpProxy;
37
- }
38
- /**
39
- * Set the ACME state manager
40
- */
41
- setAcmeStateManager(stateManager) {
42
- this.acmeStateManager = stateManager;
43
- }
44
- /**
45
- * Set global ACME defaults from top-level configuration
46
- */
47
- setGlobalAcmeDefaults(defaults) {
48
- this.globalAcmeDefaults = defaults;
49
- }
50
- /**
51
- * Set custom certificate provision function
52
- */
53
- setCertProvisionFunction(fn) {
54
- this.certProvisionFunction = fn;
55
- }
56
- /**
57
- * Set whether to fallback to ACME if custom provision fails
58
- */
59
- setCertProvisionFallbackToAcme(fallback) {
60
- this.certProvisionFallbackToAcme = fallback;
61
- }
62
- /**
63
- * Update the routes array to keep it in sync with SmartProxy
64
- * This prevents stale route data when adding/removing challenge routes
65
- */
66
- setRoutes(routes) {
67
- this.routes = routes;
68
- }
69
- /**
70
- * Set callback for updating routes (used for challenge routes)
71
- */
72
- setUpdateRoutesCallback(callback) {
73
- this.updateRoutesCallback = callback;
74
- try {
75
- logger.log('debug', 'Route update callback set successfully', { component: 'certificate-manager' });
76
- }
77
- catch (error) {
78
- // Silently handle logging errors
79
- console.log('[DEBUG] Route update callback set successfully');
80
- }
81
- }
82
- /**
83
- * Initialize certificate manager and provision certificates for all routes
84
- */
85
- async initialize() {
86
- // Create certificate directory if it doesn't exist
87
- await this.certStore.initialize();
88
- // Initialize SmartAcme if we have any ACME routes
89
- const hasAcmeRoutes = this.routes.some(r => r.action.tls?.certificate === 'auto');
90
- if (hasAcmeRoutes && this.acmeOptions?.email) {
91
- // Create HTTP-01 challenge handler
92
- const http01Handler = new plugins.smartacme.handlers.Http01MemoryHandler();
93
- // Set up challenge handler integration with our routing
94
- this.setupChallengeHandler(http01Handler);
95
- // Create SmartAcme instance with built-in MemoryCertManager and HTTP-01 handler
96
- this.smartAcme = new plugins.smartacme.SmartAcme({
97
- accountEmail: this.acmeOptions.email,
98
- environment: this.acmeOptions.useProduction ? 'production' : 'integration',
99
- certManager: new plugins.smartacme.certmanagers.MemoryCertManager(),
100
- challengeHandlers: [http01Handler]
101
- });
102
- await this.smartAcme.start();
103
- // Add challenge route once at initialization if not already active
104
- if (!this.challengeRouteActive) {
105
- logger.log('info', 'Adding ACME challenge route during initialization', { component: 'certificate-manager' });
106
- await this.addChallengeRoute();
107
- }
108
- else {
109
- logger.log('info', 'Challenge route already active from previous instance', { component: 'certificate-manager' });
110
- }
111
- }
112
- // Skip automatic certificate provisioning during initialization
113
- // This will be called later after ports are listening
114
- logger.log('info', 'Certificate manager initialized. Deferring certificate provisioning until after ports are listening.', { component: 'certificate-manager' });
115
- // Start renewal timer
116
- this.startRenewalTimer();
117
- }
118
- /**
119
- * Provision certificates for all routes that need them
120
- */
121
- async provisionAllCertificates() {
122
- const certRoutes = this.routes.filter(r => r.action.tls?.mode === 'terminate' ||
123
- r.action.tls?.mode === 'terminate-and-reencrypt');
124
- // Set provisioning flag to prevent concurrent operations
125
- this.isProvisioning = true;
126
- try {
127
- for (const route of certRoutes) {
128
- try {
129
- await this.provisionCertificate(route, true); // Allow concurrent since we're managing it here
130
- }
131
- catch (error) {
132
- logger.log('error', `Failed to provision certificate for route ${route.name}`, { routeName: route.name, error, component: 'certificate-manager' });
133
- }
134
- }
135
- }
136
- finally {
137
- this.isProvisioning = false;
138
- }
139
- }
140
- /**
141
- * Provision certificate for a single route
142
- */
143
- async provisionCertificate(route, allowConcurrent = false) {
144
- const tls = route.action.tls;
145
- if (!tls || (tls.mode !== 'terminate' && tls.mode !== 'terminate-and-reencrypt')) {
146
- return;
147
- }
148
- // Check if provisioning is already in progress (prevent concurrent provisioning)
149
- if (!allowConcurrent && this.isProvisioning) {
150
- logger.log('info', `Certificate provisioning already in progress, skipping ${route.name}`, { routeName: route.name, component: 'certificate-manager' });
151
- return;
152
- }
153
- const domains = this.extractDomainsFromRoute(route);
154
- if (domains.length === 0) {
155
- logger.log('warn', `Route ${route.name} has TLS termination but no domains`, { routeName: route.name, component: 'certificate-manager' });
156
- return;
157
- }
158
- const primaryDomain = domains[0];
159
- if (tls.certificate === 'auto') {
160
- // ACME certificate
161
- await this.provisionAcmeCertificate(route, domains);
162
- }
163
- else if (typeof tls.certificate === 'object') {
164
- // Static certificate
165
- await this.provisionStaticCertificate(route, primaryDomain, tls.certificate);
166
- }
167
- }
168
- /**
169
- * Provision ACME certificate
170
- */
171
- async provisionAcmeCertificate(route, domains) {
172
- const primaryDomain = domains[0];
173
- const routeName = route.name || primaryDomain;
174
- // Check if we already have a valid certificate
175
- const existingCert = await this.certStore.getCertificate(routeName);
176
- if (existingCert && this.isCertificateValid(existingCert)) {
177
- logger.log('info', `Using existing valid certificate for ${primaryDomain}`, { domain: primaryDomain, component: 'certificate-manager' });
178
- await this.applyCertificate(primaryDomain, existingCert);
179
- this.updateCertStatus(routeName, 'valid', existingCert.source || 'acme', existingCert);
180
- return;
181
- }
182
- // Check for custom provision function first
183
- if (this.certProvisionFunction) {
184
- try {
185
- logger.log('info', `Attempting custom certificate provision for ${primaryDomain}`, { domain: primaryDomain, component: 'certificate-manager' });
186
- const result = await this.certProvisionFunction(primaryDomain);
187
- if (result === 'http01') {
188
- logger.log('info', `Custom function returned 'http01', falling back to Let's Encrypt for ${primaryDomain}`, { domain: primaryDomain, component: 'certificate-manager' });
189
- // Continue with existing ACME logic below
190
- }
191
- else {
192
- // Use custom certificate
193
- const customCert = result;
194
- // Convert to internal certificate format
195
- const certData = {
196
- cert: customCert.publicKey,
197
- key: customCert.privateKey,
198
- ca: '',
199
- issueDate: new Date(),
200
- expiryDate: this.extractExpiryDate(customCert.publicKey),
201
- source: 'custom'
202
- };
203
- // Store and apply certificate
204
- await this.certStore.saveCertificate(routeName, certData);
205
- await this.applyCertificate(primaryDomain, certData);
206
- this.updateCertStatus(routeName, 'valid', 'custom', certData);
207
- logger.log('info', `Custom certificate applied for ${primaryDomain}`, {
208
- domain: primaryDomain,
209
- expiryDate: certData.expiryDate,
210
- component: 'certificate-manager'
211
- });
212
- return;
213
- }
214
- }
215
- catch (error) {
216
- logger.log('error', `Custom cert provision failed for ${primaryDomain}: ${error.message}`, {
217
- domain: primaryDomain,
218
- error: error.message,
219
- component: 'certificate-manager'
220
- });
221
- // Check if we should fallback to ACME
222
- if (!this.certProvisionFallbackToAcme) {
223
- throw error;
224
- }
225
- logger.log('info', `Falling back to Let's Encrypt for ${primaryDomain}`, { domain: primaryDomain, component: 'certificate-manager' });
226
- }
227
- }
228
- if (!this.smartAcme) {
229
- throw new Error('SmartAcme not initialized. This usually means no ACME email was provided. ' +
230
- 'Please ensure you have configured ACME with an email address either:\n' +
231
- '1. In the top-level "acme" configuration\n' +
232
- '2. In the route\'s "tls.acme" configuration');
233
- }
234
- // Apply renewal threshold from global defaults or route config
235
- const renewThreshold = route.action.tls?.acme?.renewBeforeDays ||
236
- this.globalAcmeDefaults?.renewThresholdDays ||
237
- 30;
238
- logger.log('info', `Requesting ACME certificate for ${domains.join(', ')} (renew ${renewThreshold} days before expiry)`, { domains: domains.join(', '), renewThreshold, component: 'certificate-manager' });
239
- this.updateCertStatus(routeName, 'pending', 'acme');
240
- try {
241
- // Challenge route should already be active from initialization
242
- // No need to add it for each certificate
243
- // Determine if we should request a wildcard certificate
244
- // Only request wildcards if:
245
- // 1. The primary domain is not already a wildcard
246
- // 2. The domain has multiple parts (can have subdomains)
247
- // 3. We have DNS-01 challenge support (required for wildcards)
248
- const hasDnsChallenge = this.smartAcme.challengeHandlers?.some((handler) => handler.getSupportedTypes && handler.getSupportedTypes().includes('dns-01'));
249
- const shouldIncludeWildcard = !primaryDomain.startsWith('*.') &&
250
- primaryDomain.includes('.') &&
251
- primaryDomain.split('.').length >= 2 &&
252
- hasDnsChallenge;
253
- if (shouldIncludeWildcard) {
254
- logger.log('info', `Requesting wildcard certificate for ${primaryDomain} (DNS-01 available)`, { domain: primaryDomain, challengeType: 'DNS-01', component: 'certificate-manager' });
255
- }
256
- // Use smartacme to get certificate with optional wildcard
257
- const cert = await this.smartAcme.getCertificateForDomain(primaryDomain, shouldIncludeWildcard ? { includeWildcard: true } : undefined);
258
- // SmartAcme's Cert object has these properties:
259
- // - publicKey: The certificate PEM string
260
- // - privateKey: The private key PEM string
261
- // - csr: Certificate signing request
262
- // - validUntil: Timestamp in milliseconds
263
- // - domainName: The domain name
264
- const certData = {
265
- cert: cert.publicKey,
266
- key: cert.privateKey,
267
- ca: cert.publicKey, // Use same as cert for now
268
- expiryDate: new Date(cert.validUntil),
269
- issueDate: new Date(cert.created),
270
- source: 'acme'
271
- };
272
- await this.certStore.saveCertificate(routeName, certData);
273
- await this.applyCertificate(primaryDomain, certData);
274
- this.updateCertStatus(routeName, 'valid', 'acme', certData);
275
- logger.log('info', `Successfully provisioned ACME certificate for ${primaryDomain}`, { domain: primaryDomain, component: 'certificate-manager' });
276
- }
277
- catch (error) {
278
- logger.log('error', `Failed to provision ACME certificate for ${primaryDomain}: ${error.message}`, { domain: primaryDomain, error: error.message, component: 'certificate-manager' });
279
- this.updateCertStatus(routeName, 'error', 'acme', undefined, error.message);
280
- throw error;
281
- }
282
- }
283
- /**
284
- * Provision static certificate
285
- */
286
- async provisionStaticCertificate(route, domain, certConfig) {
287
- const routeName = route.name || domain;
288
- try {
289
- let key = certConfig.key;
290
- let cert = certConfig.cert;
291
- // Load from files if paths are provided
292
- const smartFileFactory = plugins.smartfile.SmartFileFactory.nodeFs();
293
- if (certConfig.keyFile) {
294
- const keyFile = await smartFileFactory.fromFilePath(certConfig.keyFile);
295
- key = keyFile.contents.toString();
296
- }
297
- if (certConfig.certFile) {
298
- const certFile = await smartFileFactory.fromFilePath(certConfig.certFile);
299
- cert = certFile.contents.toString();
300
- }
301
- // Parse certificate to get dates
302
- const expiryDate = this.extractExpiryDate(cert);
303
- const issueDate = new Date(); // Current date as issue date
304
- const certData = {
305
- cert,
306
- key,
307
- expiryDate,
308
- issueDate,
309
- source: 'static'
310
- };
311
- // Save to store for consistency
312
- await this.certStore.saveCertificate(routeName, certData);
313
- await this.applyCertificate(domain, certData);
314
- this.updateCertStatus(routeName, 'valid', 'static', certData);
315
- logger.log('info', `Successfully loaded static certificate for ${domain}`, { domain, component: 'certificate-manager' });
316
- }
317
- catch (error) {
318
- logger.log('error', `Failed to provision static certificate for ${domain}: ${error.message}`, { domain, error: error.message, component: 'certificate-manager' });
319
- this.updateCertStatus(routeName, 'error', 'static', undefined, error.message);
320
- throw error;
321
- }
322
- }
323
- /**
324
- * Apply certificate to HttpProxy
325
- */
326
- async applyCertificate(domain, certData) {
327
- if (!this.httpProxy) {
328
- logger.log('warn', `HttpProxy not set, cannot apply certificate for domain ${domain}`, { domain, component: 'certificate-manager' });
329
- return;
330
- }
331
- // Apply certificate to HttpProxy
332
- this.httpProxy.updateCertificate(domain, certData.cert, certData.key);
333
- // Also apply for wildcard if it's a subdomain
334
- if (domain.includes('.') && !domain.startsWith('*.')) {
335
- const parts = domain.split('.');
336
- if (parts.length >= 2) {
337
- const wildcardDomain = `*.${parts.slice(-2).join('.')}`;
338
- this.httpProxy.updateCertificate(wildcardDomain, certData.cert, certData.key);
339
- }
340
- }
341
- }
342
- /**
343
- * Extract domains from route configuration
344
- */
345
- extractDomainsFromRoute(route) {
346
- if (!route.match.domains) {
347
- return [];
348
- }
349
- const domains = Array.isArray(route.match.domains)
350
- ? route.match.domains
351
- : [route.match.domains];
352
- // Filter out wildcards and patterns
353
- return domains.filter(d => !d.includes('*') &&
354
- !d.includes('{') &&
355
- d.includes('.'));
356
- }
357
- /**
358
- * Check if certificate is valid
359
- */
360
- isCertificateValid(cert) {
361
- const now = new Date();
362
- // Use renewal threshold from global defaults or fallback to 30 days
363
- const renewThresholdDays = this.globalAcmeDefaults?.renewThresholdDays || 30;
364
- const expiryThreshold = new Date(now.getTime() + renewThresholdDays * 24 * 60 * 60 * 1000);
365
- return cert.expiryDate > expiryThreshold;
366
- }
367
- /**
368
- * Extract expiry date from a PEM certificate
369
- */
370
- extractExpiryDate(_certPem) {
371
- // For now, we'll default to 90 days for custom certificates
372
- // In production, you might want to use a proper X.509 parser
373
- // or require the custom cert provider to include expiry info
374
- logger.log('info', 'Using default 90-day expiry for custom certificate', {
375
- component: 'certificate-manager'
376
- });
377
- return new Date(Date.now() + 90 * 24 * 60 * 60 * 1000);
378
- }
379
- /**
380
- * Add challenge route to SmartProxy
381
- *
382
- * This method adds a special route for ACME HTTP-01 challenges, which typically uses port 80.
383
- * Since we may already be listening on port 80 for regular routes, we need to be
384
- * careful about how we add this route to avoid binding conflicts.
385
- */
386
- async addChallengeRoute() {
387
- // Check with state manager first - avoid duplication
388
- if (this.acmeStateManager && this.acmeStateManager.isChallengeRouteActive()) {
389
- try {
390
- logger.log('info', 'Challenge route already active in global state, skipping', { component: 'certificate-manager' });
391
- }
392
- catch (error) {
393
- // Silently handle logging errors
394
- console.log('[INFO] Challenge route already active in global state, skipping');
395
- }
396
- this.challengeRouteActive = true;
397
- return;
398
- }
399
- if (this.challengeRouteActive) {
400
- try {
401
- logger.log('info', 'Challenge route already active locally, skipping', { component: 'certificate-manager' });
402
- }
403
- catch (error) {
404
- // Silently handle logging errors
405
- console.log('[INFO] Challenge route already active locally, skipping');
406
- }
407
- return;
408
- }
409
- if (!this.updateRoutesCallback) {
410
- throw new Error('No route update callback set');
411
- }
412
- if (!this.challengeRoute) {
413
- throw new Error('Challenge route not initialized');
414
- }
415
- // Get the challenge port
416
- const challengePort = this.globalAcmeDefaults?.port || 80;
417
- // Check if any existing routes are already using this port
418
- // This helps us determine if we need to create a new binding or can reuse existing one
419
- const portInUseByRoutes = this.routes.some(route => {
420
- const routePorts = Array.isArray(route.match.ports) ? route.match.ports : [route.match.ports];
421
- return routePorts.some(p => {
422
- // Handle both number and port range objects
423
- if (typeof p === 'number') {
424
- return p === challengePort;
425
- }
426
- else if (typeof p === 'object' && 'from' in p && 'to' in p) {
427
- // Port range case - check if challengePort is in range
428
- return challengePort >= p.from && challengePort <= p.to;
429
- }
430
- return false;
431
- });
432
- });
433
- try {
434
- // Log whether port is already in use by other routes
435
- if (portInUseByRoutes) {
436
- try {
437
- logger.log('info', `Port ${challengePort} is already used by another route, merging ACME challenge route`, {
438
- port: challengePort,
439
- component: 'certificate-manager'
440
- });
441
- }
442
- catch (error) {
443
- // Silently handle logging errors
444
- console.log(`[INFO] Port ${challengePort} is already used by another route, merging ACME challenge route`);
445
- }
446
- }
447
- else {
448
- try {
449
- logger.log('info', `Adding new ACME challenge route on port ${challengePort}`, {
450
- port: challengePort,
451
- component: 'certificate-manager'
452
- });
453
- }
454
- catch (error) {
455
- // Silently handle logging errors
456
- console.log(`[INFO] Adding new ACME challenge route on port ${challengePort}`);
457
- }
458
- }
459
- // Add the challenge route to the existing routes
460
- const challengeRoute = this.challengeRoute;
461
- const updatedRoutes = [...this.routes, challengeRoute];
462
- // With the re-ordering of start(), port binding should already be done
463
- // This updateRoutes call should just add the route without binding again
464
- await this.updateRoutesCallback(updatedRoutes);
465
- // Keep local routes in sync after updating
466
- this.routes = updatedRoutes;
467
- this.challengeRouteActive = true;
468
- // Register with state manager
469
- if (this.acmeStateManager) {
470
- this.acmeStateManager.addChallengeRoute(challengeRoute);
471
- }
472
- try {
473
- logger.log('info', 'ACME challenge route successfully added', { component: 'certificate-manager' });
474
- }
475
- catch (error) {
476
- // Silently handle logging errors
477
- console.log('[INFO] ACME challenge route successfully added');
478
- }
479
- }
480
- catch (error) {
481
- // Enhanced error handling based on error type
482
- if (error.code === 'EADDRINUSE') {
483
- try {
484
- logger.log('warn', `Challenge port ${challengePort} is unavailable - it's already in use by another process. Consider configuring a different ACME port.`, {
485
- port: challengePort,
486
- error: error.message,
487
- component: 'certificate-manager'
488
- });
489
- }
490
- catch (logError) {
491
- // Silently handle logging errors
492
- console.log(`[WARN] Challenge port ${challengePort} is unavailable - it's already in use by another process. Consider configuring a different ACME port.`);
493
- }
494
- // Provide a more informative and actionable error message
495
- throw new Error(`ACME HTTP-01 challenge port ${challengePort} is already in use by another process. ` +
496
- `Please configure a different port using the acme.port setting (e.g., 8080).`);
497
- }
498
- else if (error.message && error.message.includes('EADDRINUSE')) {
499
- // Some Node.js versions embed the error code in the message rather than the code property
500
- try {
501
- logger.log('warn', `Port ${challengePort} conflict detected: ${error.message}`, {
502
- port: challengePort,
503
- component: 'certificate-manager'
504
- });
505
- }
506
- catch (logError) {
507
- // Silently handle logging errors
508
- console.log(`[WARN] Port ${challengePort} conflict detected: ${error.message}`);
509
- }
510
- // More detailed error message with suggestions
511
- throw new Error(`ACME HTTP challenge port ${challengePort} conflict detected. ` +
512
- `To resolve this issue, try one of these approaches:\n` +
513
- `1. Configure a different port in ACME settings (acme.port)\n` +
514
- `2. Add a regular route that uses port ${challengePort} before initializing the certificate manager\n` +
515
- `3. Stop any other services that might be using port ${challengePort}`);
516
- }
517
- // Log and rethrow other types of errors
518
- try {
519
- logger.log('error', `Failed to add challenge route: ${error.message}`, {
520
- error: error.message,
521
- component: 'certificate-manager'
522
- });
523
- }
524
- catch (logError) {
525
- // Silently handle logging errors
526
- console.log(`[ERROR] Failed to add challenge route: ${error.message}`);
527
- }
528
- throw error;
529
- }
530
- }
531
- /**
532
- * Remove challenge route from SmartProxy
533
- */
534
- async removeChallengeRoute() {
535
- if (!this.challengeRouteActive) {
536
- try {
537
- logger.log('info', 'Challenge route not active, skipping removal', { component: 'certificate-manager' });
538
- }
539
- catch (error) {
540
- // Silently handle logging errors
541
- console.log('[INFO] Challenge route not active, skipping removal');
542
- }
543
- return;
544
- }
545
- if (!this.updateRoutesCallback) {
546
- return;
547
- }
548
- try {
549
- const filteredRoutes = this.routes.filter(r => r.name !== 'acme-challenge');
550
- await this.updateRoutesCallback(filteredRoutes);
551
- // Keep local routes in sync after updating
552
- this.routes = filteredRoutes;
553
- this.challengeRouteActive = false;
554
- // Remove from state manager
555
- if (this.acmeStateManager) {
556
- this.acmeStateManager.removeChallengeRoute('acme-challenge');
557
- }
558
- try {
559
- logger.log('info', 'ACME challenge route successfully removed', { component: 'certificate-manager' });
560
- }
561
- catch (error) {
562
- // Silently handle logging errors
563
- console.log('[INFO] ACME challenge route successfully removed');
564
- }
565
- }
566
- catch (error) {
567
- try {
568
- logger.log('error', `Failed to remove challenge route: ${error.message}`, { error: error.message, component: 'certificate-manager' });
569
- }
570
- catch (logError) {
571
- // Silently handle logging errors
572
- console.log(`[ERROR] Failed to remove challenge route: ${error.message}`);
573
- }
574
- // Reset the flag even on error to avoid getting stuck
575
- this.challengeRouteActive = false;
576
- throw error;
577
- }
578
- }
579
- /**
580
- * Start renewal timer
581
- */
582
- startRenewalTimer() {
583
- // Check for renewals every 12 hours
584
- this.renewalTimer = setInterval(() => {
585
- this.checkAndRenewCertificates();
586
- }, 12 * 60 * 60 * 1000);
587
- // Unref the timer so it doesn't keep the process alive
588
- if (this.renewalTimer.unref) {
589
- this.renewalTimer.unref();
590
- }
591
- // Also do an immediate check
592
- this.checkAndRenewCertificates();
593
- }
594
- /**
595
- * Check and renew certificates that are expiring
596
- */
597
- async checkAndRenewCertificates() {
598
- for (const route of this.routes) {
599
- if (route.action.tls?.certificate === 'auto') {
600
- const routeName = route.name || this.extractDomainsFromRoute(route)[0];
601
- const cert = await this.certStore.getCertificate(routeName);
602
- if (cert && !this.isCertificateValid(cert)) {
603
- logger.log('info', `Certificate for ${routeName} needs renewal`, { routeName, component: 'certificate-manager' });
604
- try {
605
- await this.provisionCertificate(route);
606
- }
607
- catch (error) {
608
- logger.log('error', `Failed to renew certificate for ${routeName}: ${error.message}`, { routeName, error: error.message, component: 'certificate-manager' });
609
- }
610
- }
611
- }
612
- }
613
- }
614
- /**
615
- * Update certificate status
616
- */
617
- updateCertStatus(routeName, status, source, certData, error) {
618
- this.certStatus.set(routeName, {
619
- domain: routeName,
620
- status,
621
- source,
622
- expiryDate: certData?.expiryDate,
623
- issueDate: certData?.issueDate,
624
- error
625
- });
626
- }
627
- /**
628
- * Get certificate status for a route
629
- */
630
- getCertificateStatus(routeName) {
631
- return this.certStatus.get(routeName);
632
- }
633
- /**
634
- * Force renewal of a certificate
635
- */
636
- async renewCertificate(routeName) {
637
- const route = this.routes.find(r => r.name === routeName);
638
- if (!route) {
639
- throw new Error(`Route ${routeName} not found`);
640
- }
641
- // Remove existing certificate to force renewal
642
- await this.certStore.deleteCertificate(routeName);
643
- await this.provisionCertificate(route);
644
- }
645
- /**
646
- * Setup challenge handler integration with SmartProxy routing
647
- */
648
- setupChallengeHandler(http01Handler) {
649
- // Use challenge port from global config or default to 80
650
- const challengePort = this.globalAcmeDefaults?.port || 80;
651
- // Create a challenge route that delegates to SmartAcme's HTTP-01 handler
652
- const challengeRoute = {
653
- name: 'acme-challenge',
654
- priority: 1000, // High priority
655
- match: {
656
- ports: challengePort,
657
- path: '/.well-known/acme-challenge/*'
658
- },
659
- action: {
660
- type: 'socket-handler',
661
- socketHandler: SocketHandlers.httpServer((req, res) => {
662
- // Extract the token from the path
663
- const token = req.url?.split('/').pop();
664
- if (!token) {
665
- res.status(404);
666
- res.send('Not found');
667
- return;
668
- }
669
- // Create mock request/response objects for SmartAcme
670
- let responseData = null;
671
- const mockReq = {
672
- url: req.url,
673
- method: req.method,
674
- headers: req.headers
675
- };
676
- const mockRes = {
677
- statusCode: 200,
678
- setHeader: (name, value) => { },
679
- end: (data) => {
680
- responseData = data;
681
- }
682
- };
683
- // Use SmartAcme's handler
684
- const handleAcme = () => {
685
- http01Handler.handleRequest(mockReq, mockRes, () => {
686
- // Not handled by ACME
687
- res.status(404);
688
- res.send('Not found');
689
- });
690
- // Give it a moment to process, then send response
691
- setTimeout(() => {
692
- if (responseData) {
693
- res.header('Content-Type', 'text/plain');
694
- res.send(String(responseData));
695
- }
696
- else {
697
- res.status(404);
698
- res.send('Not found');
699
- }
700
- }, 100);
701
- };
702
- handleAcme();
703
- })
704
- }
705
- };
706
- // Store the challenge route to add it when needed
707
- this.challengeRoute = challengeRoute;
708
- }
709
- /**
710
- * Stop certificate manager
711
- */
712
- async stop() {
713
- if (this.renewalTimer) {
714
- clearInterval(this.renewalTimer);
715
- this.renewalTimer = null;
716
- }
717
- // Always remove challenge route on shutdown
718
- if (this.challengeRoute) {
719
- logger.log('info', 'Removing ACME challenge route during shutdown', { component: 'certificate-manager' });
720
- await this.removeChallengeRoute();
721
- }
722
- if (this.smartAcme) {
723
- await this.smartAcme.stop();
724
- }
725
- // Clear any pending challenges
726
- if (this.pendingChallenges.size > 0) {
727
- this.pendingChallenges.clear();
728
- }
729
- }
730
- /**
731
- * Get ACME options (for recreating after route updates)
732
- */
733
- getAcmeOptions() {
734
- return this.acmeOptions;
735
- }
736
- /**
737
- * Get certificate manager state
738
- */
739
- getState() {
740
- return {
741
- challengeRouteActive: this.challengeRouteActive
742
- };
743
- }
744
- }
745
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2VydGlmaWNhdGUtbWFuYWdlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3RzL3Byb3hpZXMvc21hcnQtcHJveHkvY2VydGlmaWNhdGUtbWFuYWdlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGtCQUFrQixDQUFDO0FBQzVDLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUduRCxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFFNUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQ3BELE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQW9CMUQsTUFBTSxPQUFPLGdCQUFnQjtJQWdDM0IsWUFDVSxNQUFzQixFQUN0QixVQUFrQixTQUFTLEVBQzNCLFdBSVAsRUFDTyxZQUVQO1FBVE8sV0FBTSxHQUFOLE1BQU0sQ0FBZ0I7UUFDdEIsWUFBTyxHQUFQLE9BQU8sQ0FBb0I7UUFDM0IsZ0JBQVcsR0FBWCxXQUFXLENBSWxCO1FBQ08saUJBQVksR0FBWixZQUFZLENBRW5CO1FBeENLLGNBQVMsR0FBdUMsSUFBSSxDQUFDO1FBQ3JELGNBQVMsR0FBcUIsSUFBSSxDQUFDO1FBQ25DLGlCQUFZLEdBQTBCLElBQUksQ0FBQztRQUMzQyxzQkFBaUIsR0FBd0IsSUFBSSxHQUFHLEVBQUUsQ0FBQztRQUNuRCxtQkFBYyxHQUF3QixJQUFJLENBQUM7UUFFbkQseUNBQXlDO1FBQ2pDLGVBQVUsR0FBNkIsSUFBSSxHQUFHLEVBQUUsQ0FBQztRQUV6RCxvREFBb0Q7UUFDNUMsdUJBQWtCLEdBQXdCLElBQUksQ0FBQztRQUt2RCx1REFBdUQ7UUFDL0MseUJBQW9CLEdBQVksS0FBSyxDQUFDO1FBRTlDLCtDQUErQztRQUN2QyxtQkFBYyxHQUFZLEtBQUssQ0FBQztRQUV4QywrQkFBK0I7UUFDdkIscUJBQWdCLEdB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