@push.rocks/smartproxy 27.12.7 → 27.13.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.
Files changed (37) hide show
  1. package/changelog.md +12 -7
  2. package/dist_rust/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/index.d.ts +1 -1
  6. package/dist_ts/plugins.d.ts +4 -2
  7. package/dist_ts/plugins.js +5 -3
  8. package/dist_ts/proxies/smart-proxy/challenge-provider-relay-server.d.ts +31 -0
  9. package/dist_ts/proxies/smart-proxy/challenge-provider-relay-server.js +297 -0
  10. package/dist_ts/proxies/smart-proxy/models/index.d.ts +1 -1
  11. package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +14 -2
  12. package/dist_ts/proxies/smart-proxy/models/route-types.d.ts +11 -0
  13. package/dist_ts/proxies/smart-proxy/models/route-types.js +1 -1
  14. package/dist_ts/proxies/smart-proxy/models/rust-types.d.ts +5 -3
  15. package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.d.ts +2 -1
  16. package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.js +4 -1
  17. package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +19 -0
  18. package/dist_ts/proxies/smart-proxy/smart-proxy.js +339 -89
  19. package/dist_ts/proxies/smart-proxy/utils/route-validator.d.ts +3 -0
  20. package/dist_ts/proxies/smart-proxy/utils/route-validator.js +81 -1
  21. package/dist_ts/proxies/smart-proxy/utils/rust-config.d.ts +3 -2
  22. package/dist_ts/proxies/smart-proxy/utils/rust-config.js +20 -4
  23. package/package.json +2 -1
  24. package/readme.hints.md +2 -2
  25. package/readme.md +87 -7
  26. package/ts/00_commitinfo_data.ts +1 -1
  27. package/ts/index.ts +1 -1
  28. package/ts/plugins.ts +4 -1
  29. package/ts/proxies/smart-proxy/challenge-provider-relay-server.ts +338 -0
  30. package/ts/proxies/smart-proxy/models/index.ts +1 -1
  31. package/ts/proxies/smart-proxy/models/interfaces.ts +17 -2
  32. package/ts/proxies/smart-proxy/models/route-types.ts +17 -0
  33. package/ts/proxies/smart-proxy/models/rust-types.ts +8 -3
  34. package/ts/proxies/smart-proxy/rust-proxy-bridge.ts +6 -0
  35. package/ts/proxies/smart-proxy/smart-proxy.ts +400 -94
  36. package/ts/proxies/smart-proxy/utils/route-validator.ts +84 -1
  37. package/ts/proxies/smart-proxy/utils/rust-config.ts +22 -2
@@ -1,5 +1,5 @@
1
1
  import { logger } from '../../../core/utils/logger.js';
2
- import type { IRouteConfig, IRouteMatch, IRouteAction, TPortRange } from '../models/route-types.js';
2
+ import type { IInboundProxyProtocolPolicy, IRouteConfig, IRouteMatch, IRouteAction, TPortRange, TTransportProtocol } from '../models/route-types.js';
3
3
 
4
4
  /**
5
5
  * Validates route configurations for correctness and safety
@@ -93,6 +93,25 @@ export class RouteValidator {
93
93
  }
94
94
  }
95
95
 
96
+ // Validate inbound PROXY protocol policy
97
+ if (route.match.inboundProxyProtocol) {
98
+ const policy = route.match.inboundProxyProtocol;
99
+ if (!['reject', 'optional', 'required'].includes(policy.mode)) {
100
+ errors.push(`Invalid inbound PROXY protocol mode: ${policy.mode}`);
101
+ }
102
+ if (policy.trustedProxyIPs !== undefined) {
103
+ if (!Array.isArray(policy.trustedProxyIPs) || policy.trustedProxyIPs.length === 0) {
104
+ errors.push('inboundProxyProtocol.trustedProxyIPs must be a non-empty array when specified');
105
+ } else {
106
+ for (const ip of policy.trustedProxyIPs) {
107
+ if (!this.isValidIPPattern(ip)) {
108
+ errors.push(`Invalid trusted proxy IP pattern: ${ip}`);
109
+ }
110
+ }
111
+ }
112
+ }
113
+ }
114
+
96
115
  // Validate headers
97
116
  if (route.match.headers) {
98
117
  for (const [key, value] of Object.entries(route.match.headers)) {
@@ -344,9 +363,73 @@ export class RouteValidator {
344
363
  }
345
364
  }
346
365
 
366
+ // Inbound PROXY protocol is listener-scoped. A route-level declaration is
367
+ // a shorthand for the full port/transport listener policy, so every route
368
+ // sharing that listener must either all omit it or all use an identical one.
369
+ const listenerPolicies = new Map<string, { route: string; fingerprint: string }>();
370
+ for (const route of routes) {
371
+ if (!route.match?.ports) {
372
+ continue;
373
+ }
374
+
375
+ for (const port of this.expandPorts(route.match.ports)) {
376
+ for (const transport of this.transportsForMatch(route.match.transport)) {
377
+ const key = `${transport}:${port}`;
378
+ const fingerprint = this.inboundProxyPolicyFingerprint(route.match.inboundProxyProtocol);
379
+ const existing = listenerPolicies.get(key);
380
+ if (!existing) {
381
+ listenerPolicies.set(key, {
382
+ route: route.name || 'unnamed',
383
+ fingerprint,
384
+ });
385
+ } else if (existing.fingerprint !== fingerprint) {
386
+ conflicts.push({
387
+ route: route.name || 'unnamed',
388
+ message: `Conflicting inbound PROXY protocol policy on ${transport} port ${port} (differs from ${existing.route})`,
389
+ });
390
+ }
391
+ }
392
+ }
393
+ }
394
+
347
395
  return conflicts;
348
396
  }
349
397
 
398
+ private static expandPorts(ports: TPortRange): number[] {
399
+ const entries = Array.isArray(ports) ? ports : [ports];
400
+ const expandedPorts: number[] = [];
401
+ for (const port of entries) {
402
+ if (typeof port === 'number') {
403
+ expandedPorts.push(port);
404
+ } else if (typeof port === 'object' && 'from' in port && 'to' in port) {
405
+ for (let current = port.from; current <= port.to; current++) {
406
+ expandedPorts.push(current);
407
+ }
408
+ }
409
+ }
410
+ return expandedPorts;
411
+ }
412
+
413
+ private static transportsForMatch(transport?: TTransportProtocol): Array<'tcp' | 'udp'> {
414
+ if (transport === 'udp') {
415
+ return ['udp'];
416
+ }
417
+ if (transport === 'all') {
418
+ return ['tcp', 'udp'];
419
+ }
420
+ return ['tcp'];
421
+ }
422
+
423
+ private static inboundProxyPolicyFingerprint(policy?: IInboundProxyProtocolPolicy): string {
424
+ if (!policy) {
425
+ return 'default';
426
+ }
427
+ return JSON.stringify({
428
+ mode: policy.mode,
429
+ trustedProxyIPs: [...(policy.trustedProxyIPs ?? [])].sort(),
430
+ });
431
+ }
432
+
350
433
  private static isCatchAllDomainMatch(match: IRouteMatch): boolean {
351
434
  return !match.domains
352
435
  || (Array.isArray(match.domains) && match.domains.includes('*'))
@@ -4,6 +4,7 @@ import type {
4
4
  IRustAcmeOptions,
5
5
  IRustDefaultConfig,
6
6
  IRustProxyOptions,
7
+ IRustChallengeOptions,
7
8
  IRustRouteAction,
8
9
  IRustRouteConfig,
9
10
  IRustRouteMatch,
@@ -146,18 +147,36 @@ function serializeDefaultsForRust(defaults?: ISmartProxyOptions['defaults']): IR
146
147
  };
147
148
  }
148
149
 
150
+ export function buildChallengeOptionsForRust(settings: ISmartProxyOptions): IRustChallengeOptions | undefined {
151
+ const hasChallengeRoutes = settings.routes.some((route) => Boolean(route.security?.challenge));
152
+ if (!hasChallengeRoutes) {
153
+ return undefined;
154
+ }
155
+
156
+ const runtimeOptions = settings.challenge || {};
157
+ return {
158
+ cookieSigningKey: runtimeOptions.cookieSigningKey || '',
159
+ pendingCookieName: runtimeOptions.pendingCookieName || '__smartproxy_challenge_pending',
160
+ clearanceCookieName: runtimeOptions.clearanceCookieName || '__smartproxy_clearance',
161
+ reservedPathPrefix: runtimeOptions.reservedPathPrefix || '/.well-known/smartproxy-challenge',
162
+ relaySocketPath: runtimeOptions.relaySocketPath || '',
163
+ relayTimeoutMs: runtimeOptions.relayTimeoutMs ?? 5_000,
164
+ pendingTtlSeconds: runtimeOptions.pendingTtlSeconds ?? 300,
165
+ };
166
+ }
167
+
149
168
  export function buildRustProxyOptions(
150
169
  settings: ISmartProxyOptions,
151
170
  routes: IRustRouteConfig[],
152
171
  acmeOverride?: IAcmeOptions,
172
+ challengeOverride?: IRustChallengeOptions,
153
173
  ): IRustProxyOptions {
154
174
  const acme = acmeOverride !== undefined ? acmeOverride : settings.acme;
155
175
 
156
176
  return {
157
177
  routes,
158
178
  preserveSourceIp: settings.preserveSourceIP,
159
- proxyIps: settings.proxyIPs,
160
- acceptProxyProtocol: settings.acceptProxyProtocol,
179
+ trustedProxyIPs: settings.trustedProxyIPs,
161
180
  sendProxyProtocol: settings.sendProxyProtocol,
162
181
  defaults: serializeDefaultsForRust(settings.defaults),
163
182
  connectionTimeout: settings.connectionTimeout,
@@ -183,6 +202,7 @@ export function buildRustProxyOptions(
183
202
  extendedKeepAliveLifetime: settings.extendedKeepAliveLifetime,
184
203
  metrics: settings.metrics,
185
204
  securityPolicy: settings.securityPolicy,
205
+ challenge: challengeOverride || buildChallengeOptionsForRust(settings),
186
206
  acme: serializeAcmeForRust(acme),
187
207
  };
188
208
  }