@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.
- package/changelog.md +12 -7
- package/dist_rust/rustproxy_linux_amd64 +0 -0
- package/dist_rust/rustproxy_linux_arm64 +0 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/index.d.ts +1 -1
- package/dist_ts/plugins.d.ts +4 -2
- package/dist_ts/plugins.js +5 -3
- package/dist_ts/proxies/smart-proxy/challenge-provider-relay-server.d.ts +31 -0
- package/dist_ts/proxies/smart-proxy/challenge-provider-relay-server.js +297 -0
- package/dist_ts/proxies/smart-proxy/models/index.d.ts +1 -1
- package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +14 -2
- package/dist_ts/proxies/smart-proxy/models/route-types.d.ts +11 -0
- package/dist_ts/proxies/smart-proxy/models/route-types.js +1 -1
- package/dist_ts/proxies/smart-proxy/models/rust-types.d.ts +5 -3
- package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.d.ts +2 -1
- package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.js +4 -1
- package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +19 -0
- package/dist_ts/proxies/smart-proxy/smart-proxy.js +339 -89
- package/dist_ts/proxies/smart-proxy/utils/route-validator.d.ts +3 -0
- package/dist_ts/proxies/smart-proxy/utils/route-validator.js +81 -1
- package/dist_ts/proxies/smart-proxy/utils/rust-config.d.ts +3 -2
- package/dist_ts/proxies/smart-proxy/utils/rust-config.js +20 -4
- package/package.json +2 -1
- package/readme.hints.md +2 -2
- package/readme.md +87 -7
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/index.ts +1 -1
- package/ts/plugins.ts +4 -1
- package/ts/proxies/smart-proxy/challenge-provider-relay-server.ts +338 -0
- package/ts/proxies/smart-proxy/models/index.ts +1 -1
- package/ts/proxies/smart-proxy/models/interfaces.ts +17 -2
- package/ts/proxies/smart-proxy/models/route-types.ts +17 -0
- package/ts/proxies/smart-proxy/models/rust-types.ts +8 -3
- package/ts/proxies/smart-proxy/rust-proxy-bridge.ts +6 -0
- package/ts/proxies/smart-proxy/smart-proxy.ts +400 -94
- package/ts/proxies/smart-proxy/utils/route-validator.ts +84 -1
- 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
|
-
|
|
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
|
}
|