querysub 0.2.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/.dependency-cruiser.js +304 -0
- package/.eslintrc.js +51 -0
- package/.github/copilot-instructions.md +1 -0
- package/.vscode/settings.json +25 -0
- package/bin/deploy.js +4 -0
- package/bin/function.js +4 -0
- package/bin/server.js +4 -0
- package/costsBenefits.txt +112 -0
- package/deploy.ts +3 -0
- package/inject.ts +1 -0
- package/package.json +60 -0
- package/prompts.txt +54 -0
- package/spec.txt +820 -0
- package/src/-a-archives/archiveCache.ts +913 -0
- package/src/-a-archives/archives.ts +148 -0
- package/src/-a-archives/archivesBackBlaze.ts +792 -0
- package/src/-a-archives/archivesDisk.ts +418 -0
- package/src/-a-archives/copyLocalToBackblaze.ts +24 -0
- package/src/-a-auth/certs.ts +517 -0
- package/src/-a-auth/der.ts +122 -0
- package/src/-a-auth/ed25519.ts +1015 -0
- package/src/-a-auth/node-forge-ed25519.d.ts +17 -0
- package/src/-b-authorities/dnsAuthority.ts +203 -0
- package/src/-b-authorities/emailAuthority.ts +57 -0
- package/src/-c-identity/IdentityController.ts +200 -0
- package/src/-d-trust/NetworkTrust2.ts +150 -0
- package/src/-e-certs/EdgeCertController.ts +288 -0
- package/src/-e-certs/certAuthority.ts +192 -0
- package/src/-f-node-discovery/NodeDiscovery.ts +543 -0
- package/src/-g-core-values/NodeCapabilities.ts +134 -0
- package/src/-g-core-values/oneTimeForward.ts +91 -0
- package/src/-h-path-value-serialize/PathValueSerializer.ts +769 -0
- package/src/-h-path-value-serialize/stringSerializer.ts +176 -0
- package/src/0-path-value-core/LoggingClient.tsx +24 -0
- package/src/0-path-value-core/NodePathAuthorities.ts +978 -0
- package/src/0-path-value-core/PathController.ts +1 -0
- package/src/0-path-value-core/PathValueCommitter.ts +565 -0
- package/src/0-path-value-core/PathValueController.ts +231 -0
- package/src/0-path-value-core/archiveLocks/ArchiveLocks.ts +154 -0
- package/src/0-path-value-core/archiveLocks/ArchiveLocks2.ts +820 -0
- package/src/0-path-value-core/archiveLocks/archiveSnapshots.ts +180 -0
- package/src/0-path-value-core/debugLogs.ts +90 -0
- package/src/0-path-value-core/pathValueArchives.ts +483 -0
- package/src/0-path-value-core/pathValueCore.ts +2217 -0
- package/src/1-path-client/RemoteWatcher.ts +558 -0
- package/src/1-path-client/pathValueClientWatcher.ts +702 -0
- package/src/2-proxy/PathValueProxyWatcher.ts +1857 -0
- package/src/2-proxy/archiveMoveHarness.ts +376 -0
- package/src/2-proxy/garbageCollection.ts +753 -0
- package/src/2-proxy/pathDatabaseProxyBase.ts +37 -0
- package/src/2-proxy/pathValueProxy.ts +139 -0
- package/src/2-proxy/schema2.ts +518 -0
- package/src/3-path-functions/PathFunctionHelpers.ts +129 -0
- package/src/3-path-functions/PathFunctionRunner.ts +619 -0
- package/src/3-path-functions/PathFunctionRunnerMain.ts +67 -0
- package/src/3-path-functions/deployBlock.ts +10 -0
- package/src/3-path-functions/deployCheck.ts +7 -0
- package/src/3-path-functions/deployMain.ts +160 -0
- package/src/3-path-functions/pathFunctionLoader.ts +282 -0
- package/src/3-path-functions/syncSchema.ts +475 -0
- package/src/3-path-functions/tests/functionsTest.ts +135 -0
- package/src/3-path-functions/tests/rejectTest.ts +77 -0
- package/src/4-dom/css.tsx +29 -0
- package/src/4-dom/cssTypes.d.ts +212 -0
- package/src/4-dom/qreact.tsx +2322 -0
- package/src/4-dom/qreactTest.tsx +417 -0
- package/src/4-querysub/Querysub.ts +877 -0
- package/src/4-querysub/QuerysubController.ts +620 -0
- package/src/4-querysub/copyEvent.ts +0 -0
- package/src/4-querysub/permissions.ts +289 -0
- package/src/4-querysub/permissionsShared.ts +1 -0
- package/src/4-querysub/querysubPrediction.ts +525 -0
- package/src/5-diagnostics/FullscreenModal.tsx +67 -0
- package/src/5-diagnostics/GenericFormat.tsx +165 -0
- package/src/5-diagnostics/Modal.tsx +79 -0
- package/src/5-diagnostics/Table.tsx +183 -0
- package/src/5-diagnostics/TimeGrouper.tsx +114 -0
- package/src/5-diagnostics/diskValueAudit.ts +216 -0
- package/src/5-diagnostics/memoryValueAudit.ts +442 -0
- package/src/5-diagnostics/nodeMetadata.ts +135 -0
- package/src/5-diagnostics/qreactDebug.tsx +309 -0
- package/src/5-diagnostics/shared.ts +26 -0
- package/src/5-diagnostics/synchronousLagTracking.ts +47 -0
- package/src/TestController.ts +35 -0
- package/src/allowclient.flag +0 -0
- package/src/bits.ts +86 -0
- package/src/buffers.ts +69 -0
- package/src/config.ts +53 -0
- package/src/config2.ts +48 -0
- package/src/diagnostics/ActionsHistory.ts +56 -0
- package/src/diagnostics/NodeViewer.tsx +503 -0
- package/src/diagnostics/SizeLimiter.ts +62 -0
- package/src/diagnostics/TimeDebug.tsx +18 -0
- package/src/diagnostics/benchmark.ts +139 -0
- package/src/diagnostics/errorLogs/ErrorLogController.ts +515 -0
- package/src/diagnostics/errorLogs/ErrorLogCore.ts +274 -0
- package/src/diagnostics/errorLogs/LogClassifiers.tsx +302 -0
- package/src/diagnostics/errorLogs/LogFilterUI.tsx +84 -0
- package/src/diagnostics/errorLogs/LogNotify.tsx +101 -0
- package/src/diagnostics/errorLogs/LogTimeSelector.tsx +724 -0
- package/src/diagnostics/errorLogs/LogViewer.tsx +757 -0
- package/src/diagnostics/errorLogs/hookErrors.ts +60 -0
- package/src/diagnostics/errorLogs/logFiltering.tsx +149 -0
- package/src/diagnostics/heapTag.ts +13 -0
- package/src/diagnostics/listenOnDebugger.ts +77 -0
- package/src/diagnostics/logs/DiskLoggerPage.tsx +572 -0
- package/src/diagnostics/logs/ObjectDisplay.tsx +165 -0
- package/src/diagnostics/logs/ansiFormat.ts +108 -0
- package/src/diagnostics/logs/diskLogGlobalContext.ts +38 -0
- package/src/diagnostics/logs/diskLogger.ts +305 -0
- package/src/diagnostics/logs/diskShimConsoleLogs.ts +32 -0
- package/src/diagnostics/logs/injectFileLocationToConsole.ts +50 -0
- package/src/diagnostics/logs/logGitHashes.ts +30 -0
- package/src/diagnostics/managementPages.tsx +289 -0
- package/src/diagnostics/periodic.ts +89 -0
- package/src/diagnostics/runSaturationTest.ts +416 -0
- package/src/diagnostics/satSchema.ts +64 -0
- package/src/diagnostics/trackResources.ts +82 -0
- package/src/diagnostics/watchdog.ts +55 -0
- package/src/errors.ts +132 -0
- package/src/forceProduction.ts +3 -0
- package/src/fs.ts +72 -0
- package/src/heapDumps.ts +666 -0
- package/src/https.ts +2 -0
- package/src/inject.ts +1 -0
- package/src/library-components/ATag.tsx +84 -0
- package/src/library-components/Button.tsx +344 -0
- package/src/library-components/ButtonSelector.tsx +64 -0
- package/src/library-components/DropdownCustom.tsx +151 -0
- package/src/library-components/DropdownSelector.tsx +32 -0
- package/src/library-components/Input.tsx +334 -0
- package/src/library-components/InputLabel.tsx +198 -0
- package/src/library-components/InputPicker.tsx +125 -0
- package/src/library-components/LazyComponent.tsx +62 -0
- package/src/library-components/MeasureHeightCSS.tsx +48 -0
- package/src/library-components/MeasuredDiv.tsx +47 -0
- package/src/library-components/ShowMore.tsx +51 -0
- package/src/library-components/SyncedController.ts +171 -0
- package/src/library-components/TimeRangeSelector.tsx +407 -0
- package/src/library-components/URLParam.ts +263 -0
- package/src/library-components/colors.tsx +14 -0
- package/src/library-components/drag.ts +114 -0
- package/src/library-components/icons.tsx +692 -0
- package/src/library-components/niceStringify.ts +50 -0
- package/src/library-components/renderToString.ts +52 -0
- package/src/misc/PromiseRace.ts +101 -0
- package/src/misc/color.ts +30 -0
- package/src/misc/getParentProcessId.cs +53 -0
- package/src/misc/getParentProcessId.ts +53 -0
- package/src/misc/hash.ts +83 -0
- package/src/misc/ipPong.js +13 -0
- package/src/misc/networking.ts +2 -0
- package/src/misc/random.ts +45 -0
- package/src/misc.ts +19 -0
- package/src/noserverhotreload.flag +0 -0
- package/src/path.ts +226 -0
- package/src/persistentLocalStore.ts +37 -0
- package/src/promise.ts +15 -0
- package/src/server.ts +73 -0
- package/src/src.d.ts +1 -0
- package/src/test/heapProcess.ts +36 -0
- package/src/test/mongoSatTest.tsx +55 -0
- package/src/test/satTest.ts +193 -0
- package/src/test/test.tsx +552 -0
- package/src/zip.ts +92 -0
- package/src/zipThreaded.ts +106 -0
- package/src/zipThreadedWorker.js +19 -0
- package/tsconfig.json +27 -0
- package/yarnSpec.txt +56 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
declare module "node-forge" {
|
|
4
|
+
declare type Ed25519PublicKey = {
|
|
5
|
+
publicKeyBytes: Buffer;
|
|
6
|
+
} & Buffer;
|
|
7
|
+
declare type Ed25519PrivateKey = {
|
|
8
|
+
privateKeyBytes: Buffer;
|
|
9
|
+
} & Buffer;
|
|
10
|
+
class ed25519 {
|
|
11
|
+
static generateKeyPair(): { publicKey: Ed25519PublicKey, privateKey: Ed25519PrivateKey };
|
|
12
|
+
static privateKeyToPem(key: Ed25519PrivateKey): string;
|
|
13
|
+
static privateKeyFromPem(pem: string): Ed25519PrivateKey;
|
|
14
|
+
static publicKeyToPem(key: Ed25519PublicKey): string;
|
|
15
|
+
static publicKeyFromPem(pem: string): Ed25519PublicKey;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import os from "os";
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
import { cache, lazy } from "socket-function/src/caching";
|
|
4
|
+
import { isNode } from "socket-function/src/misc";
|
|
5
|
+
import { httpsRequest } from "../https";
|
|
6
|
+
import { getStorageDir } from "../fs";
|
|
7
|
+
import { SocketFunction } from "socket-function/SocketFunction";
|
|
8
|
+
import { delay } from "socket-function/src/batching";
|
|
9
|
+
import debugbreak from "debugbreak";
|
|
10
|
+
import { isClient } from "../config2";
|
|
11
|
+
import { getArchives } from "../-a-archives/archives";
|
|
12
|
+
|
|
13
|
+
const DNS_TTLSeconds = {
|
|
14
|
+
"TXT": 60,
|
|
15
|
+
"A": 60,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const keys = getArchives("keys");
|
|
19
|
+
|
|
20
|
+
export const getDNSCreds = lazy(async (): Promise<{ key: string; email: string }> => {
|
|
21
|
+
let credsJSON = await keys.get("cloudflare.json");
|
|
22
|
+
if (!credsJSON) throw new Error(`b2:/keys/cloudflare.json is missing. It should contain { "key": "your-key", "email": "your-email" }`);
|
|
23
|
+
return JSON.parse(credsJSON.toString());
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
export const hasDNSWritePermissions = lazy(async () => {
|
|
27
|
+
if (!isNode()) return false;
|
|
28
|
+
if (isClient()) return false;
|
|
29
|
+
try {
|
|
30
|
+
await getDNSCreds();
|
|
31
|
+
return true;
|
|
32
|
+
} catch {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const getZoneId = cache(async (rootDomain: string): Promise<string> => {
|
|
38
|
+
let zones = await cloudflareGETCall<{ id: string; name: string }[]>("/zones", {});
|
|
39
|
+
let selected = zones.find(x => x.name === rootDomain);
|
|
40
|
+
if (!selected) {
|
|
41
|
+
throw new Error(`Could not find zone for ${rootDomain}. Found ${zones.map(x => x.name).join(", ")}`);
|
|
42
|
+
}
|
|
43
|
+
return selected.id;
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
function getRootDomain(key: string) {
|
|
47
|
+
if (key.endsWith(".")) {
|
|
48
|
+
key = key.slice(0, -1);
|
|
49
|
+
}
|
|
50
|
+
return key.split(".").slice(-2).join(".");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function getRecordsRaw(type: string, key: string) {
|
|
54
|
+
if (key.endsWith(".")) key = key.slice(0, -1);
|
|
55
|
+
let zoneId = await getZoneId(getRootDomain(key));
|
|
56
|
+
let results = await cloudflareGETCall<{ id: string; type: string; name: string; content: string }[]>(`/zones/${zoneId}/dns_records`);
|
|
57
|
+
return results.filter(x => x.type === type && x.name === key);
|
|
58
|
+
}
|
|
59
|
+
export async function getRecords(type: string, key: string) {
|
|
60
|
+
if (key.endsWith(".")) key = key.slice(0, -1);
|
|
61
|
+
let raw = await getRecordsRaw(type, key);
|
|
62
|
+
return raw.map(x => x.content);
|
|
63
|
+
}
|
|
64
|
+
export async function deleteRecord(type: string, key: string, value: string) {
|
|
65
|
+
if (key.endsWith(".")) key = key.slice(0, -1);
|
|
66
|
+
let zoneId = await getZoneId(getRootDomain(key));
|
|
67
|
+
let prevValues = await getRecordsRaw(type, key);
|
|
68
|
+
prevValues = prevValues.filter(x => x.content === value);
|
|
69
|
+
if (prevValues.length === 0) {
|
|
70
|
+
if (!SocketFunction.silent) {
|
|
71
|
+
console.log(`No need to delete record, it was not found. ${JSON.stringify(value)} value was not in ${type} for ${key}, values ${JSON.stringify(prevValues.map(x => x.content))}`);
|
|
72
|
+
}
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
console.log(`Removing records of ${type} for ${key}, values ${JSON.stringify(prevValues.map(x => x.content))}`);
|
|
77
|
+
for (let value of prevValues) {
|
|
78
|
+
await cloudflareCall(`/zones/${zoneId}/dns_records/${value.id}`, Buffer.from([]), "DELETE");
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/** Removes all existing records (unless the record is already present) */
|
|
82
|
+
export async function setRecord(type: string, key: string, value: string, proxied?: "proxied") {
|
|
83
|
+
let stack = new Error();
|
|
84
|
+
if (key.endsWith(".")) key = key.slice(0, -1);
|
|
85
|
+
let zoneId = await getZoneId(getRootDomain(key));
|
|
86
|
+
let prevValues = await getRecordsRaw(type, key);
|
|
87
|
+
if (prevValues.some(x => x.content === value)) return;
|
|
88
|
+
|
|
89
|
+
console.log(`Removing previous records of ${type} for ${key} ${JSON.stringify(prevValues.map(x => x.content))}`);
|
|
90
|
+
let didDeletions = false;
|
|
91
|
+
for (let value of prevValues) {
|
|
92
|
+
didDeletions = true;
|
|
93
|
+
await cloudflareCall(`/zones/${zoneId}/dns_records/${value.id}`, Buffer.from([]), "DELETE");
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
console.log(`Setting ${type} record for ${key} to ${value} (previously had ${JSON.stringify(prevValues.map(x => x.content))})`);
|
|
97
|
+
const ttl = DNS_TTLSeconds[type as "A"] || 60;
|
|
98
|
+
await cloudflarePOSTCall(`/zones/${zoneId}/dns_records`, {
|
|
99
|
+
type: type,
|
|
100
|
+
name: key,
|
|
101
|
+
content: value,
|
|
102
|
+
ttl,
|
|
103
|
+
proxied: proxied === "proxied",
|
|
104
|
+
});
|
|
105
|
+
// NOTE: Apparently... even if the record didn't exist, we still have to wait...
|
|
106
|
+
console.log(`Waiting ${ttl} seconds for DNS to propagate...`);
|
|
107
|
+
await delay(ttl * 1000);
|
|
108
|
+
console.log(`Done waiting for DNS to update.`);
|
|
109
|
+
|
|
110
|
+
}
|
|
111
|
+
/** Keeps existing records */
|
|
112
|
+
export async function addRecord(type: string, key: string, value: string, proxied?: "proxied") {
|
|
113
|
+
if (key.endsWith(".")) key = key.slice(0, -1);
|
|
114
|
+
let zoneId = await getZoneId(getRootDomain(key));
|
|
115
|
+
let prevValues = await getRecords(type, key);
|
|
116
|
+
if (prevValues.includes(value)) return;
|
|
117
|
+
console.log(`Adding ${type} record for ${key} to ${value} (previously had ${JSON.stringify(prevValues)})`);
|
|
118
|
+
const ttl = DNS_TTLSeconds[type as "A"] || 60;
|
|
119
|
+
await cloudflarePOSTCall(`/zones/${zoneId}/dns_records`, {
|
|
120
|
+
type: type,
|
|
121
|
+
name: key,
|
|
122
|
+
content: value,
|
|
123
|
+
ttl,
|
|
124
|
+
proxied: proxied === "proxied",
|
|
125
|
+
});
|
|
126
|
+
console.log(`Waiting ${ttl} seconds for DNS to propagate...`);
|
|
127
|
+
await delay(ttl * 1000);
|
|
128
|
+
console.log(`Done waiting for DNS to update.`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
async function cloudflareGETCall<T>(path: string, params?: { [key: string]: string }): Promise<T> {
|
|
133
|
+
let url = new URL(`https://api.cloudflare.com/client/v4` + path);
|
|
134
|
+
for (let key in params) {
|
|
135
|
+
url.searchParams.set(key, params[key]);
|
|
136
|
+
}
|
|
137
|
+
let creds = await getDNSCreds();
|
|
138
|
+
let result = await httpsRequest(url.toString(), [], "GET", undefined, {
|
|
139
|
+
headers: {
|
|
140
|
+
"Content-Type": "application/json",
|
|
141
|
+
"X-Auth-Email": creds.email,
|
|
142
|
+
"X-Auth-User-Service-Key": creds.key,
|
|
143
|
+
"X-Auth-Key": creds.key,
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
let result2 = JSON.parse(result.toString()) as { result: unknown; success: boolean; errors: { code: number; message: string }[] };
|
|
147
|
+
if (!result2.success) {
|
|
148
|
+
throw new Error(`Cloudflare call failed: ${result2.errors.map(x => x.message).join(", ")}`);
|
|
149
|
+
}
|
|
150
|
+
return result2.result as T;
|
|
151
|
+
}
|
|
152
|
+
async function cloudflarePOSTCall<T>(path: string, params: { [key: string]: unknown }): Promise<T> {
|
|
153
|
+
return await cloudflareCall(path, Buffer.from(JSON.stringify(params)), "POST");
|
|
154
|
+
}
|
|
155
|
+
async function cloudflareCall<T>(path: string, payload: Buffer, method: string): Promise<T> {
|
|
156
|
+
let url = new URL(`https://api.cloudflare.com/client/v4` + path);
|
|
157
|
+
let creds = await getDNSCreds();
|
|
158
|
+
let result = await httpsRequest(url.toString(), payload, method, undefined, {
|
|
159
|
+
headers: {
|
|
160
|
+
"Content-Type": "application/json",
|
|
161
|
+
"X-Auth-Email": creds.email,
|
|
162
|
+
"X-Auth-User-Service-Key": creds.key,
|
|
163
|
+
"X-Auth-Key": creds.key,
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
let result2 = JSON.parse(result.toString()) as { result: unknown; success: boolean; errors: { code: number; message: string }[] };
|
|
167
|
+
if (!result2.success) {
|
|
168
|
+
throw new Error(`Cloudflare call failed: ${result2.errors.map(x => x.message).join(", ")}`);
|
|
169
|
+
}
|
|
170
|
+
return result2.result as T;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
/*
|
|
175
|
+
async function setRecord(googleDns: googleCloud.DNS, zone: googleCloud.Zone, type: string, key: string, value: unknown) {
|
|
176
|
+
let recordsResponse = await zone.getRecords();
|
|
177
|
+
let prevValue = recordsResponse[0].filter(x => x.metadata.name === key && x.type === type);
|
|
178
|
+
|
|
179
|
+
// NOTE: It seems like the data is ALWAYS an array, even if we set a value? So... just assume it is an array,
|
|
180
|
+
// and take the first entry. If we ever need to actually support arrays this will change, but... I don't
|
|
181
|
+
// think we will...
|
|
182
|
+
if (prevValue.some(x => JSON.stringify((x.data as any)?.[0]) === JSON.stringify(value))) {
|
|
183
|
+
console.log(`Record already set, (${type} ${zone.metadata.dnsName}) ${key} === ${JSON.stringify(value)}`);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
for (let record of prevValue) {
|
|
188
|
+
console.log(`Deleting record (${type} ${zone.metadata.dnsName}) ${key} = ${JSON.stringify(record.data)}`);
|
|
189
|
+
await record.delete();
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
console.log((`Setting record (${type} in ${zone.metadata.dnsName}) ${key} = ${JSON.stringify(value)}`));
|
|
193
|
+
const TTL = DNS_TTLSeconds[type as "A"] || 60;
|
|
194
|
+
await zone.addRecords(zone.record(type, {
|
|
195
|
+
name: key,
|
|
196
|
+
data: value as any,
|
|
197
|
+
ttl: TTL,
|
|
198
|
+
}));
|
|
199
|
+
|
|
200
|
+
// Wait for the record to propagate
|
|
201
|
+
await delay(TTL * 1000 * 2);
|
|
202
|
+
}
|
|
203
|
+
*/
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import os from "os";
|
|
3
|
+
import { lazy } from "socket-function/src/caching";
|
|
4
|
+
import { isNode } from "socket-function/src/misc";
|
|
5
|
+
import sendGrid from "@sendgrid/mail";
|
|
6
|
+
import { addRecord } from "./dnsAuthority";
|
|
7
|
+
import debugbreak from "debugbreak";
|
|
8
|
+
import { getStorageDir } from "../fs";
|
|
9
|
+
import { red } from "socket-function/src/formatting/logColors";
|
|
10
|
+
|
|
11
|
+
let sendGridPath = lazy(() => {
|
|
12
|
+
return getStorageDir() + "sendgrid.txt";
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export const hasEmailSendPermissions = lazy(() => {
|
|
16
|
+
if (!isNode()) return undefined;
|
|
17
|
+
let path = sendGridPath();
|
|
18
|
+
if (!fs.existsSync(path)) return undefined;
|
|
19
|
+
let sendGridKey = fs.readFileSync(path, "utf8");
|
|
20
|
+
sendGrid.setApiKey(sendGridKey);
|
|
21
|
+
return sendGrid;
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export async function sendEmail(config: {
|
|
25
|
+
from: string;
|
|
26
|
+
to: string[];
|
|
27
|
+
subject: string;
|
|
28
|
+
html: string;
|
|
29
|
+
}) {
|
|
30
|
+
console.log(red(`!!! Sending email to ${config.to.join(", ")}, from ${config.from}, subject: ${JSON.stringify(config.subject)}`));
|
|
31
|
+
const sendGrid = hasEmailSendPermissions();
|
|
32
|
+
if (!sendGrid) {
|
|
33
|
+
throw new Error(`Sendgrid get not found at ${sendGridPath()}`);
|
|
34
|
+
}
|
|
35
|
+
await sendGrid.send(config);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// NOTE: Sending SMTP yourself is actually easy, but... the port will be blocked by all ISPs, and many
|
|
39
|
+
// hosting providing, including digital ocean. So... we're just going to have to use a service to do it...
|
|
40
|
+
// SPF
|
|
41
|
+
// - Just adding a DNS entry for our IP?
|
|
42
|
+
// Ah, so... add a TXT record to point to a subdomain
|
|
43
|
+
// - Ex: "v=spf1 redirect=_spf.google.com"
|
|
44
|
+
// - With the subdomain record looking like: "v=spf1 ip4:35.190.247.0/24 ip4:64.233.160.0/19 ip4:66.102.0.0/20 ip4:66.249.80.0/20 ip4:72.14.192.0/18 ip4:74.125.0.0/16 ip4:108.177.8.0/21 ip4:173.194.0.0/16 ip4:209.85.128.0/17 ip4:216.58.192.0/19 ip4:216.239.32.0/19 -all"
|
|
45
|
+
// - Maybe ~all, but, -all seems safer?
|
|
46
|
+
// - This just makes it easier to update the record, as the redirect will be constant,
|
|
47
|
+
// and then the subdomain value can be clobbered every time (as it will only have 1 TXT record)
|
|
48
|
+
// DKIM
|
|
49
|
+
// - Public key published in DNS
|
|
50
|
+
// - Under [selector]._domainkey.[domain]
|
|
51
|
+
// - Where selector is specified in the email
|
|
52
|
+
// - And then we sign the email, and pass the signature with it (somehow)
|
|
53
|
+
// DMARC
|
|
54
|
+
// - A DNS entry specifying that we are using SPF and DKIM
|
|
55
|
+
// - Fairly simple, even constant, but I'm not sure why I can't see it in gmail.com or perspectanalytics.com
|
|
56
|
+
// TXT records. Oh well, we can probably just publish it ourselves
|
|
57
|
+
// - https://www.cloudflare.com/learning/dns/dns-records/dns-dmarc-record/
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Allows clients to set their certificate, as long as it meets our strict schema requirements.
|
|
3
|
+
* - Our requirements ensure that even though the certificates are self signed, they can't
|
|
4
|
+
* be impersonated by other users (because their common name contains their public key).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import debugbreak from "debugbreak";
|
|
8
|
+
import { SocketFunction } from "socket-function/SocketFunction";
|
|
9
|
+
import { CallerContext } from "socket-function/SocketFunctionTypes";
|
|
10
|
+
import { cache, lazy } from "socket-function/src/caching";
|
|
11
|
+
import { getClientNodeId, getNodeId, getNodeIdDomain, getNodeIdIP, getNodeIdLocation, isClientNodeId } from "socket-function/src/nodeCache";
|
|
12
|
+
import { getCommonName, getIdentityCA, getMachineId, getPublicIdentifier, getThreadKeyCert, parseCert, sign, validateCertificate, verify } from "../-a-auth/certs";
|
|
13
|
+
import { getShortNumber } from "../bits";
|
|
14
|
+
import { measureFnc, measureWrap } from "socket-function/src/profiling/measure";
|
|
15
|
+
import { timeoutToError } from "../errors";
|
|
16
|
+
import { delay } from "socket-function/src/batching";
|
|
17
|
+
|
|
18
|
+
let callerInfo = new Map<CallerContext, {
|
|
19
|
+
reconnectNodeId: string | undefined;
|
|
20
|
+
machineId: string;
|
|
21
|
+
cert: CertInfo;
|
|
22
|
+
pubKey: Buffer;
|
|
23
|
+
pubKeyShort: number;
|
|
24
|
+
}>();
|
|
25
|
+
const callerInfoErrorString = `Internal error, caller did not updated their identity. Is this an HTTPS call (instead of a websocket call)? The caller should import the server controller, which imports this function, which will add a client hook to always update the identity`;
|
|
26
|
+
|
|
27
|
+
/** Gets the nodeId of the caller suitable for reconnecting (to the same process).
|
|
28
|
+
* - Also useful for logs, to identify a node on the network.
|
|
29
|
+
* - Is global, so can be passed between nodes (although, the chance of the other process
|
|
30
|
+
* staying alive for a long period of time is low).
|
|
31
|
+
*/
|
|
32
|
+
export function IdentityController_getReconnectNodeId(callerContext: CallerContext, allowUndefined?: "allowUndefined"): string | undefined {
|
|
33
|
+
if (!isClientNodeId(callerContext.nodeId)) {
|
|
34
|
+
return callerContext.nodeId;
|
|
35
|
+
}
|
|
36
|
+
let info = callerInfo.get(callerContext);
|
|
37
|
+
if (!info) {
|
|
38
|
+
if (allowUndefined) return undefined;
|
|
39
|
+
throw new Error(callerInfoErrorString);
|
|
40
|
+
}
|
|
41
|
+
return info.reconnectNodeId;
|
|
42
|
+
}
|
|
43
|
+
export function IdentityController_getReconnectNodeIdAssert(callerContext: CallerContext): string {
|
|
44
|
+
if (!isClientNodeId(callerContext.nodeId)) {
|
|
45
|
+
return callerContext.nodeId;
|
|
46
|
+
}
|
|
47
|
+
let reconnectId = IdentityController_getReconnectNodeId(callerContext);
|
|
48
|
+
if (!reconnectId) throw new Error(`Caller did not mount before connecting. This call requires the caller to be listening.`);
|
|
49
|
+
return reconnectId;
|
|
50
|
+
}
|
|
51
|
+
export function IdentityController_getSecureIP(callerContext: CallerContext): string {
|
|
52
|
+
return getNodeIdIP(callerContext.nodeId);
|
|
53
|
+
}
|
|
54
|
+
export function IdentityController_getCurrentReconnectNodeIdAssert(): string {
|
|
55
|
+
return IdentityController_getReconnectNodeIdAssert(SocketFunction.getCaller());
|
|
56
|
+
}
|
|
57
|
+
export function IdentityController_getCurrentReconnectNodeId(): string | undefined {
|
|
58
|
+
return IdentityController_getReconnectNodeId(SocketFunction.getCaller());
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function debugNodeId(nodeId: string) {
|
|
62
|
+
let info = Array.from(callerInfo.entries()).find(x => x[0].nodeId === nodeId);
|
|
63
|
+
return info?.[1].reconnectNodeId || nodeId;
|
|
64
|
+
};
|
|
65
|
+
(globalThis as any).debugNodeId = debugNodeId;
|
|
66
|
+
|
|
67
|
+
/** Gets the nodeId of the machine, which should be consistent. When deciding to trust a node
|
|
68
|
+
* this should be used instead of the reconnectId (otherwise you will have to trust
|
|
69
|
+
* every single process individually, which will take forever!)
|
|
70
|
+
*/
|
|
71
|
+
export function IdentityController_getMachineId(callerContext: CallerContext, allowEmpty?: "allowEmpty"): string {
|
|
72
|
+
let location = getNodeIdLocation(callerContext.nodeId);
|
|
73
|
+
if (location) {
|
|
74
|
+
return getMachineId(location.address);
|
|
75
|
+
}
|
|
76
|
+
let info = callerInfo.get(callerContext);
|
|
77
|
+
if (!info) {
|
|
78
|
+
if (allowEmpty) return "";
|
|
79
|
+
throw new Error(callerInfoErrorString);
|
|
80
|
+
}
|
|
81
|
+
return info.machineId;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export type CertInfo = { certPEM: Buffer | string; issuerPEM: Buffer | string; };
|
|
85
|
+
export function IdentityController_getCertInfo(callerContext: CallerContext): CertInfo {
|
|
86
|
+
let info = callerInfo.get(callerContext);
|
|
87
|
+
if (!info) throw new Error(callerInfoErrorString);
|
|
88
|
+
return info.cert;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function IdentityController_getPubKeyShort(callerContext: CallerContext): number {
|
|
92
|
+
let info = callerInfo.get(callerContext);
|
|
93
|
+
if (!info) throw new Error(callerInfoErrorString);
|
|
94
|
+
return info.pubKeyShort;
|
|
95
|
+
}
|
|
96
|
+
export const IdentityController_getOwnPubKeyShort = lazy((): number => {
|
|
97
|
+
let cert = getThreadKeyCert();
|
|
98
|
+
let pubKey = getPublicIdentifier(cert.cert);
|
|
99
|
+
return getShortNumber(pubKey);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
export interface ChangeIdentityPayload {
|
|
103
|
+
time: number;
|
|
104
|
+
cert: string;
|
|
105
|
+
certIssuer: string;
|
|
106
|
+
serverId: string;
|
|
107
|
+
mountedPort: number | undefined;
|
|
108
|
+
}
|
|
109
|
+
class IdentityControllerBase {
|
|
110
|
+
// IMPORTANT! We HAVE to call changeIdentity NOT JUST because we can't use peer certificates in the browser, BUT, also
|
|
111
|
+
// because this removes the need to load peer certificates into the trust store. This greatly simplifies
|
|
112
|
+
// a lot of the trust system and the trust call order, allowing us to use connections to determine
|
|
113
|
+
// if we want to trust a node (instead of having to trust a node before it can connect to us, because
|
|
114
|
+
// NodeJS will throw/ignore the peer cert if it isn't trusted).
|
|
115
|
+
@measureFnc
|
|
116
|
+
public async changeIdentity(signature: string, payload: ChangeIdentityPayload) {
|
|
117
|
+
const caller = SocketFunction.getCaller();
|
|
118
|
+
// Verify it wasn't signed too long ago (to make signature stealing WAY more difficult).
|
|
119
|
+
const signedThreshold = Date.now() - 1000 * 30;
|
|
120
|
+
if (payload.time < signedThreshold) {
|
|
121
|
+
throw new Error(`Signed payload too old, ${payload.time} < ${signedThreshold} from ${caller.localNodeId} (${caller.nodeId})`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Verify the signature is meant for us, otherwise any other site can hijack the login!
|
|
125
|
+
// (We don't have to worry about other servers on the same domain, as all servers
|
|
126
|
+
// on the same domain should be the same!)
|
|
127
|
+
let localNodeId = caller.localNodeId;
|
|
128
|
+
if (payload.serverId !== localNodeId) {
|
|
129
|
+
throw new Error(`Identity is for another server! The connection is calling us ${localNodeId}, but signature is for ${payload.serverId}`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Verify the caller can sign as the cert
|
|
133
|
+
verify(payload.cert, signature, payload);
|
|
134
|
+
|
|
135
|
+
// Verify we even trust the issuer/cert pair
|
|
136
|
+
validateCertificate(payload.cert, payload.certIssuer);
|
|
137
|
+
|
|
138
|
+
let reconnectNodeId: string | undefined;
|
|
139
|
+
if (payload.mountedPort) {
|
|
140
|
+
// NOTE: We use the common name, and not getNodeIdIP... because anything connecting will need
|
|
141
|
+
// to use HTTPS, so connecting over the IP won't work! (unless they turn off certificate validation,
|
|
142
|
+
// which we shouldn't do...)
|
|
143
|
+
reconnectNodeId = getNodeId(getCommonName(payload.cert), payload.mountedPort);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
let pubKey = getPublicIdentifier(payload.cert);
|
|
147
|
+
|
|
148
|
+
callerInfo.set(caller, {
|
|
149
|
+
cert: { certPEM: payload.cert, issuerPEM: payload.certIssuer },
|
|
150
|
+
machineId: getCommonName(payload.certIssuer),
|
|
151
|
+
reconnectNodeId,
|
|
152
|
+
pubKey,
|
|
153
|
+
pubKeyShort: getShortNumber(pubKey),
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const IdentityController = SocketFunction.register(
|
|
159
|
+
"IdentityController-4a1b77d7-2825-433e-a27c-e2b8a2e74457",
|
|
160
|
+
new IdentityControllerBase(),
|
|
161
|
+
() => ({
|
|
162
|
+
changeIdentity: {
|
|
163
|
+
// If we use hooks here, it can cause an infinite loop (which SocketFunction just turns into
|
|
164
|
+
// a deadlock). So... we just don't use any hooks for this call.
|
|
165
|
+
// Actually.. I think we were seeing a different issue. And client hooks are required,
|
|
166
|
+
// so we can trust the remote cert correctly.
|
|
167
|
+
// noClientHooks: true,
|
|
168
|
+
// noDefaultHooks: true,
|
|
169
|
+
},
|
|
170
|
+
})
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
const changeIdentityOnce = cache(measureWrap(async function changeIdentityOnce(connectionId: { nodeId: string }) {
|
|
174
|
+
let nodeId = connectionId.nodeId;
|
|
175
|
+
let threadKeyCert = getThreadKeyCert();
|
|
176
|
+
let issuer = getIdentityCA();
|
|
177
|
+
let payload: ChangeIdentityPayload = {
|
|
178
|
+
time: Date.now(),
|
|
179
|
+
serverId: nodeId,
|
|
180
|
+
cert: threadKeyCert.cert.toString(),
|
|
181
|
+
certIssuer: issuer.cert.toString(),
|
|
182
|
+
mountedPort: getNodeIdLocation(SocketFunction.mountedNodeId)?.port,
|
|
183
|
+
};
|
|
184
|
+
let signature = sign(threadKeyCert, payload);
|
|
185
|
+
await measureWrap(async function callChangeIdentity() {
|
|
186
|
+
await timeoutToError(
|
|
187
|
+
10 * 1000,
|
|
188
|
+
IdentityController.nodes[nodeId].changeIdentity(signature, payload),
|
|
189
|
+
() => new Error(`Timeout calling changeIdentity for ${nodeId}`)
|
|
190
|
+
);
|
|
191
|
+
}, `callChangeIdentity|${nodeId}`)();
|
|
192
|
+
}));
|
|
193
|
+
SocketFunction.addGlobalClientHook(async function identityHook(context) {
|
|
194
|
+
if (context.call.classGuid === IdentityController._classGuid) return;
|
|
195
|
+
// If we are talking to a client they should already know who we are (they established the connection!)
|
|
196
|
+
if (isClientNodeId(context.call.nodeId)) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
await changeIdentityOnce(context.connectionId);
|
|
200
|
+
});
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { measureWrap } from "socket-function/src/profiling/measure";
|
|
2
|
+
import { getCommonName, getIdentityCA, getMachineId, getOwnMachineId } from "../-a-auth/certs";
|
|
3
|
+
import { getArchives } from "../-a-archives/archives";
|
|
4
|
+
import { isNode, timeInSecond } from "socket-function/src/misc";
|
|
5
|
+
import { SocketFunctionHook } from "socket-function/SocketFunctionTypes";
|
|
6
|
+
import { SocketFunction } from "socket-function/SocketFunction";
|
|
7
|
+
import { IdentityController_getMachineId } from "../-c-identity/IdentityController";
|
|
8
|
+
import { cache, lazy } from "socket-function/src/caching";
|
|
9
|
+
import { getNodeIdDomainMaybeUndefined, getNodeIdLocation } from "socket-function/src/nodeCache";
|
|
10
|
+
import { trustCertificate } from "socket-function/src/certStore";
|
|
11
|
+
import { isClient, isServer } from "../config2";
|
|
12
|
+
import debugbreak from "debugbreak";
|
|
13
|
+
import { devDebugbreak, getDomain, isDevDebugbreak } from "../config";
|
|
14
|
+
|
|
15
|
+
// Cache the untrust list, to prevent bugs from causing too many backend reads (while also allowing
|
|
16
|
+
// bad servers which make request before their trust is verified from staying broken).
|
|
17
|
+
const UNTRUST_CACHE_TIME = 30 * timeInSecond;
|
|
18
|
+
|
|
19
|
+
const archives = lazy(() => getArchives("trust/"));
|
|
20
|
+
|
|
21
|
+
export const requiresNetworkTrustHook: SocketFunctionHook = async config => {
|
|
22
|
+
// HACK: On the clientside we strip the domain process and machine id, so we can no longer determine
|
|
23
|
+
// who it is. So... we trust anyone. This isn't so bad, as:
|
|
24
|
+
// 1) They have to at least have an HTTPS certificate, which... most nodes should have?
|
|
25
|
+
// 2) We only really talk to nodes we discovery, and only network trusted nodes can register themselves,
|
|
26
|
+
// so... they are probably already trusted.
|
|
27
|
+
// ALSO, if in NodeJS, but acting as a client, we only connect to remote servers via
|
|
28
|
+
// HTTPS, so we know we can trust them, as their certificates are verified.
|
|
29
|
+
// - Also, if we are a client we don't really have any privileged data, except
|
|
30
|
+
// the data we send servers, which we explicitly connect to.
|
|
31
|
+
// NOTE: This also exists in isTrusted.
|
|
32
|
+
if (isClient()) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
let machineId = IdentityController_getMachineId(SocketFunction.getCaller());
|
|
36
|
+
let trusted = await isTrusted(machineId);
|
|
37
|
+
if (!trusted) {
|
|
38
|
+
devDebugbreak();
|
|
39
|
+
throw new Error(`Calling machine is not trusted. Caller ${machineId} is not trusted by ${SocketFunction.mountedNodeId} to make call ${config.call.classGuid}.${config.call.functionName}. To gain trust add backblaze permissions (see hasBackblazePermissions) or set --nonetwork.`);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
export const assertIsNetworkTrusted = requiresNetworkTrustHook;
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
let trustedCache = new Set<string>();
|
|
46
|
+
let untrustedCache = new Map<string, number>();
|
|
47
|
+
export async function isTrusted(machineId: string) {
|
|
48
|
+
// See the comment in requiresNetworkTrustHook for why clients have to trust all callers.
|
|
49
|
+
if (isClient()) return true;
|
|
50
|
+
|
|
51
|
+
if (trustedCache.has(machineId)) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
let untrustedTime = untrustedCache.get(machineId);
|
|
55
|
+
if (untrustedTime !== undefined && untrustedTime > Date.now()) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Find is faster than get, and usually we don't need the full certificate
|
|
60
|
+
let trustedMachineIds = await archives().find("");
|
|
61
|
+
// Always trust ourself
|
|
62
|
+
trustedMachineIds.push(getOwnMachineId());
|
|
63
|
+
// IF developing, trust localhost. This allows us to develop without port forwards,
|
|
64
|
+
// on our services, which is INCREASES security, and prevents dev machines from being
|
|
65
|
+
// connected to by attackers (as dev machines might reveal unfinished content, or even
|
|
66
|
+
// have security vulnerabilities).
|
|
67
|
+
if (isDevDebugbreak()) {
|
|
68
|
+
trustedMachineIds.push("127-0-0-1." + getDomain());
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// NOTE: This should be safe from collisions with existing files, because... while there might be a metadata
|
|
72
|
+
// file which exists, it will never also be a valid hash/public key, which machineId will always be.
|
|
73
|
+
for (let trustedMachineId of trustedMachineIds) {
|
|
74
|
+
trustedCache.add(trustedMachineId);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (!trustedCache.has(machineId)) {
|
|
78
|
+
untrustedCache.set(machineId, Date.now() + UNTRUST_CACHE_TIME);
|
|
79
|
+
return false;
|
|
80
|
+
} else {
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export async function isNodeTrusted(nodeId: string) {
|
|
86
|
+
let domainName = getNodeIdDomainMaybeUndefined(nodeId);
|
|
87
|
+
if (!domainName) return false;
|
|
88
|
+
let machineId = getMachineId(domainName);
|
|
89
|
+
return await isTrusted(machineId);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const loadServerCert = cache(async (machineId: string) => {
|
|
93
|
+
// This cert isn't stored in the archives, but... this is fine?
|
|
94
|
+
if (machineId === "127-0-0-1." + getDomain()) return;
|
|
95
|
+
let certFile = await archives().get(machineId);
|
|
96
|
+
if (!certFile) {
|
|
97
|
+
console.warn(`Could not find certificate in archives for ${machineId}`);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
trustCertificate(certFile);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const ensureWeAreTrusted = lazy(measureWrap(async () => {
|
|
104
|
+
let machineKeyCert = getIdentityCA();
|
|
105
|
+
let machineId = getCommonName(machineKeyCert.cert);
|
|
106
|
+
let inArchives = await archives().get(machineId);
|
|
107
|
+
if (!inArchives) {
|
|
108
|
+
await archives().set(machineId, machineKeyCert.cert);
|
|
109
|
+
}
|
|
110
|
+
}));
|
|
111
|
+
|
|
112
|
+
export const isTrustedByNode = cache(async function isTrustedByNode(nodeId: string) {
|
|
113
|
+
return await TrustedController.nodes[nodeId].isTrusted();
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
class IsTrustedControllerBase {
|
|
117
|
+
public async isTrusted() {
|
|
118
|
+
return await isTrusted(IdentityController_getMachineId(SocketFunction.getCaller()));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const TrustedController = SocketFunction.register(
|
|
123
|
+
"IsTrustedController-5d0b8a77-f5f7-4584-96f4-9cd88ce5d59a",
|
|
124
|
+
new IsTrustedControllerBase(),
|
|
125
|
+
() => ({
|
|
126
|
+
isTrusted: {},
|
|
127
|
+
})
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
if (isNode()) {
|
|
132
|
+
|
|
133
|
+
// We have to be trusted if we make calls to a trusted endpoint, OR our mounting
|
|
134
|
+
// (really only if we are mounting a trusted endpoint, but we don't actually know that)
|
|
135
|
+
requiresNetworkTrustHook.clientHook = async config => {
|
|
136
|
+
await ensureWeAreTrusted();
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
// Load the remote certificate, in the almost certain case it isn't a real certificate, and is just internal
|
|
140
|
+
SocketFunction.addGlobalClientHook(async config => {
|
|
141
|
+
await ensureWeAreTrusted();
|
|
142
|
+
let location = getNodeIdLocation(config.call.nodeId);
|
|
143
|
+
if (location) {
|
|
144
|
+
let machineId = getMachineId(location.address);
|
|
145
|
+
if (await isTrusted(machineId)) {
|
|
146
|
+
await loadServerCert(machineId);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}
|