dsh-mobile 0.6.12 → 0.6.14
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 +16 -0
- package/CONTRIBUTORS.md +11 -3
- package/README.en.md +11 -4
- package/README.md +11 -4
- package/SECURITY.md +1 -0
- package/docs/SELF_HOSTED_ORIGIN.en.md +34 -6
- package/docs/SELF_HOSTED_ORIGIN.md +34 -6
- package/docs/SLOW_CONNECTIONS.en.md +8 -0
- package/docs/SLOW_CONNECTIONS.md +8 -0
- package/lib/client.js +96 -31
- package/lib/client.js.map +1 -1
- package/lib/index.d.mts +30 -2
- package/lib/index.mjs +180 -24
- package/lib/index.mjs.map +1 -1
- package/package.json +2 -1
- package/packages/question-fixes/package.json +1 -1
package/lib/index.d.mts
CHANGED
|
@@ -540,6 +540,33 @@ interface AuthFailureEntry {
|
|
|
540
540
|
readonly suppressed?: number;
|
|
541
541
|
}
|
|
542
542
|
//#endregion
|
|
543
|
+
//#region src/http-failure-log.d.ts
|
|
544
|
+
/** Fixed parser details safe to record without Node's raw packet, message or request headers. */
|
|
545
|
+
type HttpParserCode = 'ERR_HTTP_REQUEST_TIMEOUT' | 'HPE_HEADER_OVERFLOW' | 'HPE_INVALID_CHUNK_SIZE' | 'HPE_INVALID_CONTENT_LENGTH' | 'HPE_INVALID_EOF_STATE' | 'HPE_INVALID_HEADER_TOKEN' | 'HPE_INVALID_TRANSFER_ENCODING' | 'HPE_UNEXPECTED_CONTENT_LENGTH' | 'other';
|
|
546
|
+
/** A rejected inbound HTTP message's public response and bounded diagnostic category. */
|
|
547
|
+
interface HttpParserFailure {
|
|
548
|
+
readonly status: 400 | 408 | 431;
|
|
549
|
+
readonly code: 'bad_request' | 'request_timeout' | 'headers_too_large';
|
|
550
|
+
readonly parserCode: HttpParserCode;
|
|
551
|
+
}
|
|
552
|
+
/** One sampled rejection. Arbitrary paths, query strings, body data and credentials are excluded. */
|
|
553
|
+
type HttpFailureEntry = ({
|
|
554
|
+
readonly kind: 'parser';
|
|
555
|
+
readonly status: HttpParserFailure['status'];
|
|
556
|
+
readonly code: HttpParserCode;
|
|
557
|
+
} | {
|
|
558
|
+
readonly kind: 'target';
|
|
559
|
+
readonly status: 400;
|
|
560
|
+
readonly code: 'bad_request';
|
|
561
|
+
} | {
|
|
562
|
+
readonly kind: 'upstream';
|
|
563
|
+
readonly status: number;
|
|
564
|
+
readonly method: 'POST';
|
|
565
|
+
readonly path: '/api/session/prompt';
|
|
566
|
+
}) & {
|
|
567
|
+
readonly suppressed?: number;
|
|
568
|
+
};
|
|
569
|
+
//#endregion
|
|
543
570
|
//#region src/http-security.d.ts
|
|
544
571
|
export declare const DEVICE_COOKIE = "dsh_ma_device";
|
|
545
572
|
export declare const SESSION_COOKIE = "dsh_ma_session";
|
|
@@ -1002,9 +1029,10 @@ export declare class MobileAccessGateway {
|
|
|
1002
1029
|
private readonly clientModuleTasks;
|
|
1003
1030
|
private readonly clientModuleRevocations;
|
|
1004
1031
|
private readonly authFailures;
|
|
1032
|
+
private readonly httpFailures;
|
|
1005
1033
|
constructor(config: ResolvedGatewayConfig, store: DeviceStore, extensions?: MobileAccessService | undefined, upstreamAuthenticatedUrl?: string | undefined, extraWebSocketPaths?: {
|
|
1006
1034
|
has(pathname: string): boolean;
|
|
1007
|
-
} | undefined, blockedUpgradeLog?: BlockedUpgradePathLog | undefined, onDiscoveryDegraded?: ((source: 'broadcast' | 'mdns', code: string) => void) | undefined, clientModulePreferences?: ClientModulePreferenceStore, authFailureLog?: (entry: AuthFailureEntry) => void);
|
|
1035
|
+
} | undefined, blockedUpgradeLog?: BlockedUpgradePathLog | undefined, onDiscoveryDegraded?: ((source: 'broadcast' | 'mdns', code: string) => void) | undefined, clientModulePreferences?: ClientModulePreferenceStore, authFailureLog?: (entry: AuthFailureEntry) => void, httpFailureLog?: (entry: HttpFailureEntry) => void);
|
|
1008
1036
|
/** Initialize durable state, validate TLS, and bind the externally reachable listener. */
|
|
1009
1037
|
start(): Promise<void>;
|
|
1010
1038
|
/** Install a newly signed provided leaf for future TLS handshakes without dropping sessions. */
|
|
@@ -1921,7 +1949,7 @@ interface OriginConfigurationStatus {
|
|
|
1921
1949
|
}
|
|
1922
1950
|
/** Require a public HTTPS origin; custom external ports are supported. */
|
|
1923
1951
|
export declare function validateOriginPublicOrigin(value: unknown): string;
|
|
1924
|
-
/** Bind
|
|
1952
|
+
/** Bind one explicit loopback, RFC1918 IPv4 or IPv6 ULA interface; zone IDs are unsupported. */
|
|
1925
1953
|
export declare function validateOriginListenHost(value: unknown): string;
|
|
1926
1954
|
export declare function validateOriginListenPort(value: unknown): number;
|
|
1927
1955
|
/** Authorize direct proxy socket peers, not untrusted forwarded client headers. */
|
package/lib/index.mjs
CHANGED
|
@@ -9,7 +9,7 @@ import { performance as performance$1 } from "node:perf_hooks";
|
|
|
9
9
|
import { execFile, spawn } from "node:child_process";
|
|
10
10
|
import { createSocket } from "node:dgram";
|
|
11
11
|
import { homedir, hostname, networkInterfaces, tmpdir } from "node:os";
|
|
12
|
-
import { createServer as createServer$1, request } from "node:http";
|
|
12
|
+
import { STATUS_CODES, createServer as createServer$1, request } from "node:http";
|
|
13
13
|
import { createServer as createServer$2, request as request$1 } from "node:https";
|
|
14
14
|
import { Readable, Transform, finished } from "node:stream";
|
|
15
15
|
import { finished as finished$1, pipeline } from "node:stream/promises";
|
|
@@ -1968,7 +1968,7 @@ function authenticatedCookie(values, authenticate, accepts = () => true) {
|
|
|
1968
1968
|
}
|
|
1969
1969
|
//#endregion
|
|
1970
1970
|
//#region src/auth-failure-log.ts
|
|
1971
|
-
const LOG_INTERVAL_MS = 3e4;
|
|
1971
|
+
const LOG_INTERVAL_MS$1 = 3e4;
|
|
1972
1972
|
const MAX_LOGGED_ORIGIN_LENGTH = 512;
|
|
1973
1973
|
function requestPathCategory(path) {
|
|
1974
1974
|
switch (path) {
|
|
@@ -2065,7 +2065,7 @@ var AuthFailureRecorder = class {
|
|
|
2065
2065
|
return;
|
|
2066
2066
|
}
|
|
2067
2067
|
const suppressed = window.suppressed;
|
|
2068
|
-
window.nextLogAt = now + LOG_INTERVAL_MS;
|
|
2068
|
+
window.nextLogAt = now + LOG_INTERVAL_MS$1;
|
|
2069
2069
|
window.suppressed = 0;
|
|
2070
2070
|
try {
|
|
2071
2071
|
this.observe({
|
|
@@ -2082,6 +2082,129 @@ var AuthFailureRecorder = class {
|
|
|
2082
2082
|
}
|
|
2083
2083
|
};
|
|
2084
2084
|
//#endregion
|
|
2085
|
+
//#region src/http-failure-log.ts
|
|
2086
|
+
function safeParserCode(code) {
|
|
2087
|
+
switch (code) {
|
|
2088
|
+
case "ERR_HTTP_REQUEST_TIMEOUT":
|
|
2089
|
+
case "HPE_HEADER_OVERFLOW":
|
|
2090
|
+
case "HPE_INVALID_CHUNK_SIZE":
|
|
2091
|
+
case "HPE_INVALID_CONTENT_LENGTH":
|
|
2092
|
+
case "HPE_INVALID_EOF_STATE":
|
|
2093
|
+
case "HPE_INVALID_HEADER_TOKEN":
|
|
2094
|
+
case "HPE_INVALID_TRANSFER_ENCODING":
|
|
2095
|
+
case "HPE_UNEXPECTED_CONTENT_LENGTH": return code;
|
|
2096
|
+
default: return "other";
|
|
2097
|
+
}
|
|
2098
|
+
}
|
|
2099
|
+
/**
|
|
2100
|
+
* Classify Node's HTTP rejection without reflecting an unrecognized error code.
|
|
2101
|
+
* @param nodeCode - The parser error's machine code, not its message or raw packet.
|
|
2102
|
+
* @returns The fixed public HTTP status/category and allowlisted diagnostic code.
|
|
2103
|
+
*/
|
|
2104
|
+
function classifyHttpParserFailure(nodeCode) {
|
|
2105
|
+
const parserCode = safeParserCode(nodeCode);
|
|
2106
|
+
if (parserCode === "ERR_HTTP_REQUEST_TIMEOUT") return {
|
|
2107
|
+
status: 408,
|
|
2108
|
+
code: "request_timeout",
|
|
2109
|
+
parserCode
|
|
2110
|
+
};
|
|
2111
|
+
if (parserCode === "HPE_HEADER_OVERFLOW") return {
|
|
2112
|
+
status: 431,
|
|
2113
|
+
code: "headers_too_large",
|
|
2114
|
+
parserCode
|
|
2115
|
+
};
|
|
2116
|
+
return {
|
|
2117
|
+
status: 400,
|
|
2118
|
+
code: "bad_request",
|
|
2119
|
+
parserCode
|
|
2120
|
+
};
|
|
2121
|
+
}
|
|
2122
|
+
const LOG_INTERVAL_MS = 3e4;
|
|
2123
|
+
/**
|
|
2124
|
+
* Per-Gateway diagnostics with three fixed sampling slots and no timers or request-selected keys.
|
|
2125
|
+
* Each kind records once immediately, then at most once per 30 seconds with suppressed counts.
|
|
2126
|
+
* A sink failure cannot change a response, retry a write or expose the diagnostic sink's error.
|
|
2127
|
+
*/
|
|
2128
|
+
var HttpFailureRecorder = class {
|
|
2129
|
+
observe;
|
|
2130
|
+
now;
|
|
2131
|
+
reportObserverFailure;
|
|
2132
|
+
windows = {
|
|
2133
|
+
parser: {
|
|
2134
|
+
nextLogAt: 0,
|
|
2135
|
+
suppressed: 0
|
|
2136
|
+
},
|
|
2137
|
+
target: {
|
|
2138
|
+
nextLogAt: 0,
|
|
2139
|
+
suppressed: 0
|
|
2140
|
+
},
|
|
2141
|
+
upstream: {
|
|
2142
|
+
nextLogAt: 0,
|
|
2143
|
+
suppressed: 0
|
|
2144
|
+
}
|
|
2145
|
+
};
|
|
2146
|
+
observerFailureReported = false;
|
|
2147
|
+
constructor(observe, now = () => performance.now(), reportObserverFailure = () => {
|
|
2148
|
+
process.emitWarning("Mobile access HTTP diagnostics could not be recorded", { code: "DSH_MOBILE_HTTP_LOG_FAILED" });
|
|
2149
|
+
}) {
|
|
2150
|
+
this.observe = observe;
|
|
2151
|
+
this.now = now;
|
|
2152
|
+
this.reportObserverFailure = reportObserverFailure;
|
|
2153
|
+
}
|
|
2154
|
+
/** Classify an inbound parser rejection and sample only its fixed status and safe code. */
|
|
2155
|
+
recordParser(nodeCode) {
|
|
2156
|
+
const failure = classifyHttpParserFailure(nodeCode);
|
|
2157
|
+
this.record({
|
|
2158
|
+
kind: "parser",
|
|
2159
|
+
status: failure.status,
|
|
2160
|
+
code: failure.parserCode
|
|
2161
|
+
});
|
|
2162
|
+
return failure;
|
|
2163
|
+
}
|
|
2164
|
+
/** Sample an invalid request target without retaining its raw or decoded URL. */
|
|
2165
|
+
recordTarget() {
|
|
2166
|
+
this.record({
|
|
2167
|
+
kind: "target",
|
|
2168
|
+
status: 400,
|
|
2169
|
+
code: "bad_request"
|
|
2170
|
+
});
|
|
2171
|
+
}
|
|
2172
|
+
/** Sample only authenticated prompt rejections; the caller must authorize before forwarding. */
|
|
2173
|
+
recordUpstream(method, pathname, status) {
|
|
2174
|
+
if (method !== "POST" || pathname !== "/api/session/prompt" || !Number.isInteger(status) || status < 400 || status > 599) return;
|
|
2175
|
+
this.record({
|
|
2176
|
+
kind: "upstream",
|
|
2177
|
+
status,
|
|
2178
|
+
method: "POST",
|
|
2179
|
+
path: "/api/session/prompt"
|
|
2180
|
+
});
|
|
2181
|
+
}
|
|
2182
|
+
record(entry) {
|
|
2183
|
+
if (this.observe === void 0) return;
|
|
2184
|
+
const now = this.now();
|
|
2185
|
+
const window = this.windows[entry.kind];
|
|
2186
|
+
if (now < window.nextLogAt) {
|
|
2187
|
+
window.suppressed = Math.min(Number.MAX_SAFE_INTEGER, window.suppressed + 1);
|
|
2188
|
+
return;
|
|
2189
|
+
}
|
|
2190
|
+
const suppressed = window.suppressed;
|
|
2191
|
+
window.nextLogAt = now + LOG_INTERVAL_MS;
|
|
2192
|
+
window.suppressed = 0;
|
|
2193
|
+
try {
|
|
2194
|
+
this.observe({
|
|
2195
|
+
...entry,
|
|
2196
|
+
...suppressed === 0 ? {} : { suppressed }
|
|
2197
|
+
});
|
|
2198
|
+
} catch (observerError) {
|
|
2199
|
+
if (this.observerFailureReported) return;
|
|
2200
|
+
this.observerFailureReported = true;
|
|
2201
|
+
try {
|
|
2202
|
+
this.reportObserverFailure();
|
|
2203
|
+
} catch (reporterError) {}
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2206
|
+
};
|
|
2207
|
+
//#endregion
|
|
2085
2208
|
//#region src/client-module-recovery.ts
|
|
2086
2209
|
const CLIENT_MODULE_RECOVERY_COPY = {
|
|
2087
2210
|
zh: {
|
|
@@ -5052,7 +5175,7 @@ function rejectUpgrade(socket, status, code) {
|
|
|
5052
5175
|
if (socket.destroyed) return;
|
|
5053
5176
|
const body = `${JSON.stringify({ error: code })}\n`;
|
|
5054
5177
|
socket.end([
|
|
5055
|
-
`HTTP/1.1 ${String(status)} ${status
|
|
5178
|
+
`HTTP/1.1 ${String(status)} ${STATUS_CODES[status] ?? "Bad Request"}`,
|
|
5056
5179
|
"Connection: close",
|
|
5057
5180
|
"Cache-Control: no-store",
|
|
5058
5181
|
"Content-Type: application/json; charset=utf-8",
|
|
@@ -5423,7 +5546,8 @@ var MobileAccessGateway = class {
|
|
|
5423
5546
|
clientModuleTasks = /* @__PURE__ */ new Set();
|
|
5424
5547
|
clientModuleRevocations = /* @__PURE__ */ new Set();
|
|
5425
5548
|
authFailures;
|
|
5426
|
-
|
|
5549
|
+
httpFailures;
|
|
5550
|
+
constructor(config, store, extensions, upstreamAuthenticatedUrl, extraWebSocketPaths, blockedUpgradeLog, onDiscoveryDegraded, clientModulePreferences, authFailureLog, httpFailureLog) {
|
|
5427
5551
|
this.config = config;
|
|
5428
5552
|
this.extensions = extensions;
|
|
5429
5553
|
this.upstreamAuthenticatedUrl = upstreamAuthenticatedUrl;
|
|
@@ -5431,6 +5555,7 @@ var MobileAccessGateway = class {
|
|
|
5431
5555
|
this.blockedUpgradeLog = blockedUpgradeLog;
|
|
5432
5556
|
this.onDiscoveryDegraded = onDiscoveryDegraded;
|
|
5433
5557
|
this.authFailures = new AuthFailureRecorder(authFailureLog);
|
|
5558
|
+
this.httpFailures = new HttpFailureRecorder(httpFailureLog);
|
|
5434
5559
|
this.clientModulePreferences = clientModulePreferences ?? new ClientModulePreferenceStore(join(dirname(config.stateFile), `${basename(config.stateFile)}.client-modules.json`), config.excludedClientModules);
|
|
5435
5560
|
this.listenerTlsEnabled = config.tls.mode === "provided";
|
|
5436
5561
|
this.tlsEnabled = config.publicTls;
|
|
@@ -5527,8 +5652,10 @@ var MobileAccessGateway = class {
|
|
|
5527
5652
|
rejectUpgrade(client, mapped.status, mapped.code);
|
|
5528
5653
|
});
|
|
5529
5654
|
});
|
|
5530
|
-
server.on("clientError", (
|
|
5531
|
-
|
|
5655
|
+
server.on("clientError", (error, socket) => {
|
|
5656
|
+
const nodeCode = "code" in error && typeof error.code === "string" ? error.code : void 0;
|
|
5657
|
+
const failure = this.httpFailures.recordParser(nodeCode);
|
|
5658
|
+
rejectUpgrade(socket, failure.status, failure.code);
|
|
5532
5659
|
});
|
|
5533
5660
|
await new Promise((resolve, reject) => {
|
|
5534
5661
|
const failed = (error) => {
|
|
@@ -5932,7 +6059,13 @@ var MobileAccessGateway = class {
|
|
|
5932
6059
|
sendJson(response, 200, { loggedOut: true }, this.tlsEnabled);
|
|
5933
6060
|
}
|
|
5934
6061
|
async handleExternalRequest(request, response) {
|
|
5935
|
-
|
|
6062
|
+
let target;
|
|
6063
|
+
try {
|
|
6064
|
+
target = parseRequestTarget(request.url);
|
|
6065
|
+
} catch (error) {
|
|
6066
|
+
if (error instanceof HttpError && error.status === 400) this.httpFailures.recordTarget();
|
|
6067
|
+
throw error;
|
|
6068
|
+
}
|
|
5936
6069
|
const policy = this.requirePolicy();
|
|
5937
6070
|
const isMutation = request.method !== "GET" && request.method !== "HEAD";
|
|
5938
6071
|
assertExternalTrust(request, policy, isMutation);
|
|
@@ -7093,6 +7226,7 @@ var MobileAccessGateway = class {
|
|
|
7093
7226
|
setSecurityHeaders(response, this.tlsEnabled, "proxied");
|
|
7094
7227
|
const headers = sanitizeResponseHeaders(proxied.headers, this.config.upstreamOrigin);
|
|
7095
7228
|
const statusCode = proxied.statusCode ?? 502;
|
|
7229
|
+
this.httpFailures.recordUpstream(request$2.method, request$2.url?.split("?", 1)[0] ?? "", statusCode);
|
|
7096
7230
|
const cacheControl = revisionedStaticCacheControl(request$2, statusCode);
|
|
7097
7231
|
if (cacheControl !== void 0) headers["cache-control"] = cacheControl;
|
|
7098
7232
|
const compressible = canCompressResponse(request$2, proxied);
|
|
@@ -11620,12 +11754,16 @@ const MIN_ORIGIN_LISTEN_PORT = 1024;
|
|
|
11620
11754
|
const RESERVED_LAN_GATEWAY_PORT$1 = 3443;
|
|
11621
11755
|
/** DSH's own WebServer port; the plugin cannot function while it is taken. */
|
|
11622
11756
|
const RESERVED_DSH_WEB_PORT = 3080;
|
|
11623
|
-
const
|
|
11624
|
-
|
|
11625
|
-
|
|
11626
|
-
|
|
11627
|
-
|
|
11628
|
-
|
|
11757
|
+
const TRUSTED_NETWORKS = [
|
|
11758
|
+
...[
|
|
11759
|
+
"10.0.0.0/8",
|
|
11760
|
+
"172.16.0.0/12",
|
|
11761
|
+
"192.168.0.0/16",
|
|
11762
|
+
"fc00::/7"
|
|
11763
|
+
].map(parseCidr),
|
|
11764
|
+
parseCidr("127.0.0.0/8"),
|
|
11765
|
+
parseCidr("::1/128")
|
|
11766
|
+
];
|
|
11629
11767
|
/** Require a public HTTPS origin; custom external ports are supported. */
|
|
11630
11768
|
function validateOriginPublicOrigin(value) {
|
|
11631
11769
|
if (typeof value !== "string" || value.length > 512 || /[\s\u0000-\u001f\u007f\\@?#]/u.test(value)) throw new Error("origin_public_origin_invalid");
|
|
@@ -11647,10 +11785,15 @@ function validateOriginPublicOrigin(value) {
|
|
|
11647
11785
|
if (url.protocol !== "https:" || url.pathname !== "/" || url.username !== "" || url.password !== "" || url.search !== "" || url.hash !== "" || url.port !== "" && Number(url.port) < 1 || (isIP(host) === 4 ? !isGloballyRoutableIpv4(host) : isIP(host) !== 0 || !validHostname)) throw new Error("origin_public_origin_invalid");
|
|
11648
11786
|
return url.origin;
|
|
11649
11787
|
}
|
|
11650
|
-
/** Bind
|
|
11788
|
+
/** Bind one explicit loopback, RFC1918 IPv4 or IPv6 ULA interface; zone IDs are unsupported. */
|
|
11651
11789
|
function validateOriginListenHost(value) {
|
|
11652
|
-
|
|
11653
|
-
|
|
11790
|
+
const addressFamily = isIP(value);
|
|
11791
|
+
if (typeof value !== "string" || value.includes("%") || addressFamily === 0 || !addressAllowed(value, TRUSTED_NETWORKS.filter((range) => range.bits === (addressFamily === 6 ? 128 : 32)))) throw new Error("origin_listen_host_invalid");
|
|
11792
|
+
return addressFamily === 6 ? new URL("http://[" + value + "]").hostname.slice(1, -1) : value;
|
|
11793
|
+
}
|
|
11794
|
+
/** Format a validated backend listener, including the brackets required by IPv6 URLs. */
|
|
11795
|
+
function formatOriginBackendOrigin(listenHost, listenPort) {
|
|
11796
|
+
return "http://" + (isIP(listenHost) === 6 ? "[" + listenHost + "]" : listenHost) + ":" + String(listenPort);
|
|
11654
11797
|
}
|
|
11655
11798
|
function validateOriginListenPort(value) {
|
|
11656
11799
|
if (!Number.isSafeInteger(value) || Number(value) < MIN_ORIGIN_LISTEN_PORT || Number(value) > 65535) throw new Error("origin_listen_port_invalid");
|
|
@@ -11659,14 +11802,15 @@ function validateOriginListenPort(value) {
|
|
|
11659
11802
|
}
|
|
11660
11803
|
/** Authorize direct proxy socket peers, not untrusted forwarded client headers. */
|
|
11661
11804
|
function validateOriginAllowedCidrs(value, listenHost) {
|
|
11662
|
-
const
|
|
11805
|
+
const addressFamily = isIP(listenHost);
|
|
11806
|
+
const input = value === void 0 && isLoopbackAddress(listenHost) ? [addressFamily === 6 ? "::1/128" : "127.0.0.0/8"] : value;
|
|
11663
11807
|
if (!Array.isArray(input) || input.length === 0 || input.length > 16) throw new Error("origin_allowed_cidrs_invalid");
|
|
11664
11808
|
const cidrs = [];
|
|
11665
11809
|
for (const entry of input) {
|
|
11666
|
-
if (typeof entry !== "string" || entry.length > 64 || isIP(entry.split("/")[0] ?? "") !==
|
|
11810
|
+
if (typeof entry !== "string" || entry.length > 64 || entry.includes("%") || isIP(entry.split("/")[0] ?? "") !== addressFamily) throw new Error("origin_allowed_cidrs_invalid");
|
|
11667
11811
|
try {
|
|
11668
11812
|
const cidr = parseCidr(entry);
|
|
11669
|
-
if (cidr.bits !== 32 || !TRUSTED_NETWORKS.some((range) => cidr.prefix >= range.prefix && addressAllowed(entry.split("/")[0], [range]))) throw new Error("untrusted CIDR");
|
|
11813
|
+
if (cidr.bits !== (addressFamily === 6 ? 128 : 32) || !TRUSTED_NETWORKS.some((range) => cidr.bits === range.bits && cidr.prefix >= range.prefix && addressAllowed(entry.split("/")[0], [range]))) throw new Error("untrusted CIDR");
|
|
11670
11814
|
cidrs.push(cidr.source);
|
|
11671
11815
|
} catch {
|
|
11672
11816
|
throw new Error("origin_allowed_cidrs_invalid");
|
|
@@ -11744,7 +11888,7 @@ var OriginConfigStore = class {
|
|
|
11744
11888
|
listenHost: settings.listenHost,
|
|
11745
11889
|
listenPort: settings.listenPort,
|
|
11746
11890
|
allowedCidrs: settings.allowedCidrs,
|
|
11747
|
-
backendOrigin:
|
|
11891
|
+
backendOrigin: formatOriginBackendOrigin(settings.listenHost, settings.listenPort)
|
|
11748
11892
|
},
|
|
11749
11893
|
storagePath: this.stateRoot,
|
|
11750
11894
|
...this.errorCode === void 0 ? {} : { errorCode: this.errorCode }
|
|
@@ -11904,7 +12048,7 @@ var OriginController = class {
|
|
|
11904
12048
|
enabled: true,
|
|
11905
12049
|
state: "ready",
|
|
11906
12050
|
origin: settings.publicOrigin,
|
|
11907
|
-
backendOrigin:
|
|
12051
|
+
backendOrigin: formatOriginBackendOrigin(settings.listenHost, gateway.address().port)
|
|
11908
12052
|
});
|
|
11909
12053
|
}
|
|
11910
12054
|
async stop() {
|
|
@@ -17601,6 +17745,8 @@ async function apply(ctx, config) {
|
|
|
17601
17745
|
logger.warn("%s discovery degraded while DSH remains available: %s", source, code);
|
|
17602
17746
|
}, clientModulePreferences, (entry) => {
|
|
17603
17747
|
logger.warn("auth failure %s", JSON.stringify(entry));
|
|
17748
|
+
}, (entry) => {
|
|
17749
|
+
logger.warn("http failure %s", JSON.stringify(entry));
|
|
17604
17750
|
});
|
|
17605
17751
|
await candidate.start();
|
|
17606
17752
|
lanGateway = candidate;
|
|
@@ -17659,6 +17805,8 @@ async function apply(ctx, config) {
|
|
|
17659
17805
|
const resolved = remoteGatewayConfig(template, publicOrigin, remoteDeviceFile, remoteInstanceId, listenPort, instanceId);
|
|
17660
17806
|
const candidate = new MobileAccessGateway(resolved, new JsonDeviceStore(resolved.stateFile, resolved.maxDevices), mobileAccess, upstreamLoginUrl, webSocketPaths, blockedUpgradePaths, void 0, clientModulePreferences, (entry) => {
|
|
17661
17807
|
logger.warn("auth failure %s", JSON.stringify(entry));
|
|
17808
|
+
}, (entry) => {
|
|
17809
|
+
logger.warn("http failure %s", JSON.stringify(entry));
|
|
17662
17810
|
});
|
|
17663
17811
|
await candidate.start();
|
|
17664
17812
|
return candidate;
|
|
@@ -17675,7 +17823,11 @@ async function apply(ctx, config) {
|
|
|
17675
17823
|
if (!isFrpSelfSignedIngress(settings)) return createRemoteGateway(publicOrigin);
|
|
17676
17824
|
const ingress = await ensureFrpIngressCertificate(settings, remoteDeviceFile, Date.now(), void 0, stateDirectory);
|
|
17677
17825
|
const resolved = frpIngressGatewayConfig(template, settings, remoteDeviceFile, ingress);
|
|
17678
|
-
const candidate = new MobileAccessGateway(resolved, new JsonDeviceStore(resolved.stateFile, resolved.maxDevices), mobileAccess, upstreamLoginUrl, webSocketPaths, blockedUpgradePaths, void 0, clientModulePreferences)
|
|
17826
|
+
const candidate = new MobileAccessGateway(resolved, new JsonDeviceStore(resolved.stateFile, resolved.maxDevices), mobileAccess, upstreamLoginUrl, webSocketPaths, blockedUpgradePaths, void 0, clientModulePreferences, (entry) => {
|
|
17827
|
+
logger.warn("auth failure %s", JSON.stringify(entry));
|
|
17828
|
+
}, (entry) => {
|
|
17829
|
+
logger.warn("http failure %s", JSON.stringify(entry));
|
|
17830
|
+
});
|
|
17679
17831
|
try {
|
|
17680
17832
|
await candidate.start();
|
|
17681
17833
|
} catch (error) {
|
|
@@ -17686,7 +17838,11 @@ async function apply(ctx, config) {
|
|
|
17686
17838
|
};
|
|
17687
17839
|
const createOriginGateway = async (settings) => {
|
|
17688
17840
|
const resolved = originGatewayConfig(template, settings, remoteDeviceFile, remoteInstanceId, settings.listenPort, instanceId);
|
|
17689
|
-
const candidate = new MobileAccessGateway(resolved, new JsonDeviceStore(resolved.stateFile, resolved.maxDevices), mobileAccess, upstreamLoginUrl, webSocketPaths, blockedUpgradePaths, void 0, clientModulePreferences)
|
|
17841
|
+
const candidate = new MobileAccessGateway(resolved, new JsonDeviceStore(resolved.stateFile, resolved.maxDevices), mobileAccess, upstreamLoginUrl, webSocketPaths, blockedUpgradePaths, void 0, clientModulePreferences, (entry) => {
|
|
17842
|
+
logger.warn("auth failure %s", JSON.stringify(entry));
|
|
17843
|
+
}, (entry) => {
|
|
17844
|
+
logger.warn("http failure %s", JSON.stringify(entry));
|
|
17845
|
+
});
|
|
17690
17846
|
try {
|
|
17691
17847
|
await candidate.start();
|
|
17692
17848
|
} catch (error) {
|