sliftutils 1.7.130 → 1.7.131
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/index.d.ts +4 -51
- package/misc/dist/ownIPs.ts.cache +2 -2
- package/package.json +1 -1
- package/security/authorizedKeys/authorizedKeys.d.ts +37 -0
- package/security/authorizedKeys/daemon/changes.d.ts +17 -0
- package/security/authorizedKeys/daemon/dist/changes.ts.cache +2 -2
- package/security/authorizedKeys/daemon/dist/git.ts.cache +2 -2
- package/security/authorizedKeys/daemon/dist/notify.ts.cache +2 -2
- package/security/authorizedKeys/daemon/dist/paths.ts.cache +2 -2
- package/security/authorizedKeys/daemon/dist/repoFiles.ts.cache +2 -2
- package/security/authorizedKeys/daemon/dist/revocation.ts.cache +0 -0
- package/security/authorizedKeys/daemon/dist/sessions.ts.cache +2 -2
- package/security/authorizedKeys/daemon/dist/state.ts.cache +2 -2
- package/security/authorizedKeys/daemon/dist/trust.ts.cache +2 -2
- package/security/authorizedKeys/daemon/git.d.ts +35 -0
- package/security/authorizedKeys/daemon/notify.d.ts +8 -0
- package/security/authorizedKeys/daemon/paths.d.ts +15 -0
- package/security/authorizedKeys/daemon/repoFiles.d.ts +25 -0
- package/security/authorizedKeys/daemon/revocation.d.ts +74 -0
- package/security/authorizedKeys/daemon/sessions.d.ts +19 -0
- package/security/authorizedKeys/daemon/state.d.ts +67 -0
- package/security/authorizedKeys/daemon/trust.d.ts +20 -0
- package/security/authorizedKeys/dist/authorizedKeys.ts.cache +2 -2
- package/security/authorizedKeys/dist/keysRepo.ts.cache +2 -2
- package/security/authorizedKeys/dist/revokeSource.ts.cache +2 -2
- package/security/authorizedKeys/dist/sources.ts.cache +2 -2
- package/security/authorizedKeys/keysRepo.d.ts +10 -0
- package/security/authorizedKeys/revokeSource.d.ts +13 -0
- package/security/authorizedKeys/sources.d.ts +17 -0
- package/security/helpers/dist/paths.ts.cache +2 -2
- package/security/helpers/dist/spawn.ts.cache +2 -2
- package/security/helpers/paths.d.ts +4 -0
- package/security/helpers/spawn.d.ts +16 -0
- package/security/keys/deriveKey.d.ts +16 -0
- package/security/keys/dist/deriveKey.ts.cache +2 -2
- package/security/keys/dist/sshKeyFile.ts.cache +2 -2
- package/security/keys/sshKeyFile.d.ts +18 -0
- package/security/machines/addMachine.ts +76 -24
- package/security/machines/dist/addMachine.ts.cache +6 -5
- package/security/machines/dist/identity.ts.cache +2 -2
- package/security/machines/dist/machines.ts.cache +81 -14
- package/security/machines/machines.d.ts +81 -0
- package/security/machines/machines.ts +89 -14
- package/security/notifications/discord.d.ts +36 -0
- package/security/notifications/dist/discord.ts.cache +2 -2
- package/security/signedFiles/dist/manifest.ts.cache +2 -2
- package/security/signedFiles/manifest.d.ts +44 -0
- package/storage/remoteStorage/ArchivesRemote.ts +8 -10
- package/storage/remoteStorage/accessPage.tsx +13 -90
- package/storage/remoteStorage/dist/ArchivesRemote.ts.cache +11 -12
- package/storage/remoteStorage/dist/serverConfig.ts.cache +7 -49
- package/storage/remoteStorage/dist/storageController.ts.cache +33 -114
- package/storage/remoteStorage/serverConfig.d.ts +0 -10
- package/storage/remoteStorage/serverConfig.ts +6 -48
- package/storage/remoteStorage/storageController.d.ts +4 -36
- package/storage/remoteStorage/storageController.ts +40 -126
- package/storage/remoteStorage/storageServer.ts +1 -8
- package/storage/remoteStorage/storageServerCli.ts +14 -0
- package/storage/remoteStorage/grantAccess.js +0 -4
- package/storage/remoteStorage/grantAccessCli.d.ts +0 -1
- package/storage/remoteStorage/grantAccessCli.ts +0 -25
|
@@ -1,19 +1,13 @@
|
|
|
1
1
|
import path from "path";
|
|
2
|
-
import { getFileStorageNested2 } from "../FileFolderAPI";
|
|
3
|
-
import { TransactionStorage } from "../TransactionStorage";
|
|
4
|
-
import { JSONStorage } from "../JSONStorage";
|
|
5
|
-
import type { IStorage } from "../IStorage";
|
|
6
|
-
import type { AccessRequest, TrustRecord } from "./storageController";
|
|
7
2
|
import { getArg } from "./cliArgs";
|
|
8
3
|
|
|
9
|
-
// The storage server's process-level identity and system state: which server we are (config, extra
|
|
4
|
+
// The storage server's process-level identity and system state: which server we are (config, extra
|
|
5
|
+
// ports), and whether writes are allowed.
|
|
10
6
|
|
|
11
7
|
export type StorageServerConfig = {
|
|
12
8
|
domain: string;
|
|
13
9
|
port: number;
|
|
14
10
|
rootDomain: string;
|
|
15
|
-
sshTarget: string;
|
|
16
|
-
serverCommand: string;
|
|
17
11
|
folder: string;
|
|
18
12
|
};
|
|
19
13
|
|
|
@@ -52,46 +46,10 @@ export function getStorageFolder(): string {
|
|
|
52
46
|
return path.resolve(folder);
|
|
53
47
|
}
|
|
54
48
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
storage = (async () => {
|
|
60
|
-
let root = await getFileStorageNested2(getStorageFolder());
|
|
61
|
-
let system = await root.folder.getStorage("system2");
|
|
62
|
-
let transactionName = "storage" + name[0].toUpperCase() + name.slice(1);
|
|
63
|
-
return new JSONStorage<unknown>(new TransactionStorage(await system.folder.getStorage(name), transactionName));
|
|
64
|
-
})();
|
|
65
|
-
systemStorages.set(name, storage);
|
|
66
|
-
}
|
|
67
|
-
return storage as Promise<IStorage<T>>;
|
|
68
|
-
}
|
|
69
|
-
export function getTrust(): Promise<IStorage<TrustRecord>> {
|
|
70
|
-
return getSystemStorage<TrustRecord>("trust");
|
|
71
|
-
}
|
|
72
|
-
export function getRequests(): Promise<IStorage<AccessRequest[]>> {
|
|
73
|
-
return getSystemStorage<AccessRequest[]>("requests");
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export async function setTrustedMachines(config: { account: string; machineIds: string[] }): Promise<void> {
|
|
77
|
-
let trust = await getTrust();
|
|
78
|
-
let prefix = `${config.account}|`;
|
|
79
|
-
let desired = new Set(config.machineIds);
|
|
80
|
-
for (let key of await trust.getKeys()) {
|
|
81
|
-
if (!key.startsWith(prefix)) continue;
|
|
82
|
-
let machineId = key.slice(prefix.length);
|
|
83
|
-
if (desired.has(machineId)) {
|
|
84
|
-
desired.delete(machineId);
|
|
85
|
-
continue;
|
|
86
|
-
}
|
|
87
|
-
console.log(`Removing trust for machine ${machineId} on account ${config.account}`);
|
|
88
|
-
await trust.remove(key);
|
|
89
|
-
}
|
|
90
|
-
for (let machineId of desired) {
|
|
91
|
-
console.log(`Adding trust for machine ${machineId} on account ${config.account}`);
|
|
92
|
-
await trust.set(`${prefix}${machineId}`, { account: config.account, machineId, ip: "", time: Date.now() });
|
|
93
|
-
}
|
|
94
|
-
}
|
|
49
|
+
// Which machines this server trusts is not kept here any more. It lives in the signed
|
|
50
|
+
// authorized_keys repo, read through security/machines, so every machine shares one list, with
|
|
51
|
+
// revocation and address checking that a per-server store never had. setTrustedMachines, the trust
|
|
52
|
+
// store and the access request store all went with it.
|
|
95
53
|
|
|
96
54
|
const extraListenPorts = new Set<number>();
|
|
97
55
|
export function addExtraListenPort(port: number): void {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
/// <reference types="node" />
|
|
3
3
|
import { ArchiveFileInfo, ArchivesConfig, ArchivesSyncStatus, FindConfig, SourceConfig } from "../IArchives";
|
|
4
4
|
import { ActiveBucketInfo, ServerBucketInfo } from "./storageServerState";
|
|
5
|
+
import { MachineState } from "../../security/machines/machines";
|
|
5
6
|
import { AccessTotals, AccessSummaryState } from "./accessStats";
|
|
6
7
|
import { LogFileInfo } from "../StreamingLogs";
|
|
7
8
|
import type { SummaryEntry } from "../../treeSummary";
|
|
@@ -19,25 +20,13 @@ export type AuthToken = {
|
|
|
19
20
|
signature: string;
|
|
20
21
|
data: AuthTokenData;
|
|
21
22
|
};
|
|
22
|
-
export type AccessRequest = {
|
|
23
|
-
requestId: string;
|
|
24
|
-
account: string;
|
|
25
|
-
machineId: string;
|
|
26
|
-
ip: string;
|
|
27
|
-
time: number;
|
|
28
|
-
};
|
|
29
|
-
export type TrustRecord = {
|
|
30
|
-
account: string;
|
|
31
|
-
machineId: string;
|
|
32
|
-
ip: string;
|
|
33
|
-
time: number;
|
|
34
|
-
};
|
|
35
23
|
export type AccessState = {
|
|
36
24
|
machineId: string;
|
|
37
25
|
ip: string;
|
|
38
26
|
hasAccess: boolean;
|
|
39
|
-
|
|
40
|
-
|
|
27
|
+
reason?: string;
|
|
28
|
+
addMachineCommand?: string;
|
|
29
|
+
trustedMachines?: MachineState[];
|
|
41
30
|
};
|
|
42
31
|
export declare function broadcastRoutingChanged(): void;
|
|
43
32
|
export declare const RemoteStorageController: import("socket-function/SocketFunctionTypes").SocketRegistered<{
|
|
@@ -46,34 +35,13 @@ export declare const RemoteStorageController: import("socket-function/SocketFunc
|
|
|
46
35
|
machineId: string;
|
|
47
36
|
ip: string;
|
|
48
37
|
}>;
|
|
49
|
-
requestAccess: (config: {
|
|
50
|
-
account: string;
|
|
51
|
-
}) => Promise<{
|
|
52
|
-
machineId: string;
|
|
53
|
-
ip: string;
|
|
54
|
-
requestId: string;
|
|
55
|
-
grantAccessCommand: string;
|
|
56
|
-
}>;
|
|
57
38
|
getAccessState: (config: {
|
|
58
39
|
account: string;
|
|
59
40
|
}) => Promise<AccessState>;
|
|
60
|
-
listRequestsForIP: (config: {
|
|
61
|
-
account: string;
|
|
62
|
-
ip: string;
|
|
63
|
-
}) => Promise<AccessRequest[]>;
|
|
64
|
-
grantAccess: (config: {
|
|
65
|
-
requestId: string;
|
|
66
|
-
}) => Promise<TrustRecord>;
|
|
67
41
|
adminListActiveBuckets: () => Promise<{
|
|
68
42
|
account: string;
|
|
69
43
|
bucketName: string;
|
|
70
44
|
}[]>;
|
|
71
|
-
adminListRequests: (config: {
|
|
72
|
-
ip: string;
|
|
73
|
-
}) => Promise<AccessRequest[]>;
|
|
74
|
-
adminGrantAccess: (config: {
|
|
75
|
-
requestId: string;
|
|
76
|
-
}) => Promise<TrustRecord>;
|
|
77
45
|
get2: (config: {
|
|
78
46
|
account: string;
|
|
79
47
|
bucketName: string;
|
|
@@ -15,7 +15,8 @@ import {
|
|
|
15
15
|
debugGetActiveBucket, activateBucket, getActiveBucketKeys,
|
|
16
16
|
} from "./storageServerState";
|
|
17
17
|
import { debugClearAccountWriteStats } from "./accessStats";
|
|
18
|
-
import { getStorageServerConfig,
|
|
18
|
+
import { getStorageServerConfig, assertWritesAllowed } from "./serverConfig";
|
|
19
|
+
import { getTrustedMachines, isMachineAccepted, MachineState } from "../../security/machines/machines";
|
|
19
20
|
import { BlobStore } from "./blobStore";
|
|
20
21
|
import { getRoutingFileResult } from "./bucketDisk";
|
|
21
22
|
import { StorageClientController } from "./storageClientController";
|
|
@@ -45,25 +46,15 @@ export type AuthToken = {
|
|
|
45
46
|
signature: string;
|
|
46
47
|
data: AuthTokenData;
|
|
47
48
|
};
|
|
48
|
-
export type AccessRequest = {
|
|
49
|
-
requestId: string;
|
|
50
|
-
account: string;
|
|
51
|
-
machineId: string;
|
|
52
|
-
ip: string;
|
|
53
|
-
time: number;
|
|
54
|
-
};
|
|
55
|
-
export type TrustRecord = {
|
|
56
|
-
account: string;
|
|
57
|
-
machineId: string;
|
|
58
|
-
ip: string;
|
|
59
|
-
time: number;
|
|
60
|
-
};
|
|
61
49
|
export type AccessState = {
|
|
62
50
|
machineId: string;
|
|
63
51
|
ip: string;
|
|
64
52
|
hasAccess: boolean;
|
|
65
|
-
|
|
66
|
-
|
|
53
|
+
// Why not, and what to run about it, when there is no access.
|
|
54
|
+
reason?: string;
|
|
55
|
+
addMachineCommand?: string;
|
|
56
|
+
// Everything the repo trusts, when there is.
|
|
57
|
+
trustedMachines?: MachineState[];
|
|
67
58
|
};
|
|
68
59
|
|
|
69
60
|
const sessions = new Map<string, string>();
|
|
@@ -114,24 +105,29 @@ function requireAdmin(): string {
|
|
|
114
105
|
}
|
|
115
106
|
return machineId;
|
|
116
107
|
}
|
|
108
|
+
/** Who may talk to this server, decided by the signed authorized_keys repo rather than by anything
|
|
109
|
+
this server keeps for itself.
|
|
110
|
+
|
|
111
|
+
The identity is the same machine id it always was - a machine that was trusted here before is
|
|
112
|
+
trusted the moment its id is in the repo - but the record of who is trusted now lives in one
|
|
113
|
+
signed place shared by every machine, with revocation and everything else that comes with it.
|
|
114
|
+
The address is checked too, which the trust store here never did. */
|
|
117
115
|
async function requireAccess(account: string): Promise<string> {
|
|
118
116
|
assertValidName(account, "account");
|
|
119
117
|
let machineId = getCallerMachineId();
|
|
120
118
|
if (isAdmin(machineId)) return machineId;
|
|
121
|
-
let
|
|
122
|
-
let
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
throw new Error(
|
|
119
|
+
let { rootDomain } = getStorageServerConfig();
|
|
120
|
+
let ip = getCallerIP();
|
|
121
|
+
let verdict = await isMachineAccepted({ machineId, ip, domain: rootDomain });
|
|
122
|
+
if (!verdict.accepted) {
|
|
123
|
+
throw new Error(
|
|
124
|
+
`${STORAGE_ACCESS_DENIED} ${verdict.reason}`
|
|
125
|
+
+ ` Machine ${machineId}, address ${ip}, account ${JSON.stringify(account)}.`
|
|
126
|
+
);
|
|
126
127
|
}
|
|
127
128
|
return machineId;
|
|
128
129
|
}
|
|
129
130
|
|
|
130
|
-
function getGrantAccessCommand(requestId: string): string {
|
|
131
|
-
let { sshTarget, serverCommand } = getStorageServerConfig();
|
|
132
|
-
return `ssh ${sshTarget} '${serverCommand} --requestId ${requestId}'`;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
131
|
class RemoteStorageControllerBase {
|
|
136
132
|
async ping(): Promise<{}> {
|
|
137
133
|
trackCaller();
|
|
@@ -166,114 +162,37 @@ class RemoteStorageControllerBase {
|
|
|
166
162
|
return { machineId, ip: getCallerIP() };
|
|
167
163
|
}
|
|
168
164
|
|
|
169
|
-
|
|
170
|
-
async requestAccess(config: { account: string }): Promise<{ machineId: string; ip: string; requestId: string; grantAccessCommand: string }> {
|
|
171
|
-
let account = config.account;
|
|
172
|
-
let machineId = getCallerMachineId();
|
|
173
|
-
let ip = getCallerIP();
|
|
174
|
-
let requestsStorage = await getRequests();
|
|
175
|
-
let requests = await requestsStorage.get(ip) || [];
|
|
176
|
-
let existing = requests.find(x => x.account === account && x.machineId === machineId);
|
|
177
|
-
if (existing) {
|
|
178
|
-
existing.time = Date.now();
|
|
179
|
-
} else {
|
|
180
|
-
existing = {
|
|
181
|
-
requestId: Math.random().toString(36).slice(2, 10),
|
|
182
|
-
account,
|
|
183
|
-
machineId,
|
|
184
|
-
ip,
|
|
185
|
-
time: Date.now(),
|
|
186
|
-
};
|
|
187
|
-
requests.push(existing);
|
|
188
|
-
}
|
|
189
|
-
while (requests.length > MAX_REQUESTS_PER_IP) requests.shift();
|
|
190
|
-
await requestsStorage.set(ip, requests);
|
|
191
|
-
return { machineId, ip, requestId: existing.requestId, grantAccessCommand: getGrantAccessCommand(existing.requestId) };
|
|
192
|
-
}
|
|
165
|
+
/** Whether the caller may use this account, and if not, exactly what to run to change that.
|
|
193
166
|
|
|
167
|
+
There is nothing to request and nothing to grant here any more: trust lives in the signed
|
|
168
|
+
authorized_keys repo, so the answer is "run addmachine in that repo", which only somebody
|
|
169
|
+
holding the hardware key can do. */
|
|
194
170
|
@assertValidArgs
|
|
195
171
|
async getAccessState(config: { account: string }): Promise<AccessState> {
|
|
196
|
-
|
|
172
|
+
assertValidName(config.account, "account");
|
|
197
173
|
let machineId = getCallerMachineId();
|
|
198
174
|
let ip = getCallerIP();
|
|
199
|
-
let
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
if (!hasAccess) {
|
|
203
|
-
let requests = await getRequests();
|
|
204
|
-
let ownRequest = (await requests.get(ip) || []).find(x => x.account === account && x.machineId === machineId);
|
|
205
|
-
if (ownRequest) {
|
|
206
|
-
result.grantAccessCommand = getGrantAccessCommand(ownRequest.requestId);
|
|
207
|
-
}
|
|
208
|
-
return result;
|
|
175
|
+
let { rootDomain } = getStorageServerConfig();
|
|
176
|
+
if (isAdmin(machineId)) {
|
|
177
|
+
return { machineId, ip, hasAccess: true, trustedMachines: await getTrustedMachines() };
|
|
209
178
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
async listRequestsForIP(config: { account: string; ip: string }): Promise<AccessRequest[]> {
|
|
222
|
-
let requests = await getRequests();
|
|
223
|
-
return (await requests.get(config.ip) || []).filter(x => x.account === config.account);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
async grantAccess(config: { requestId: string }): Promise<TrustRecord> {
|
|
227
|
-
let requestId = config.requestId;
|
|
228
|
-
let callerMachineId = getCallerMachineId();
|
|
229
|
-
let trust = await getTrust();
|
|
230
|
-
let requests = await getRequests();
|
|
231
|
-
for (let ip of await requests.getKeys()) {
|
|
232
|
-
for (let request of await requests.get(ip) || []) {
|
|
233
|
-
if (request.requestId !== requestId) continue;
|
|
234
|
-
if (!isAdmin(callerMachineId) && !await trust.get(`${request.account}|${callerMachineId}`)) {
|
|
235
|
-
throw new Error(`${STORAGE_ACCESS_DENIED} Machine has no access to this account. Machine ${callerMachineId}, account ${JSON.stringify(request.account)}`);
|
|
236
|
-
}
|
|
237
|
-
let record: TrustRecord = {
|
|
238
|
-
account: request.account,
|
|
239
|
-
machineId: request.machineId,
|
|
240
|
-
ip: request.ip,
|
|
241
|
-
time: Date.now(),
|
|
242
|
-
};
|
|
243
|
-
await trust.set(`${request.account}|${request.machineId}`, record);
|
|
244
|
-
return record;
|
|
245
|
-
}
|
|
179
|
+
let verdict = await isMachineAccepted({ machineId, ip, domain: rootDomain });
|
|
180
|
+
if (!verdict.accepted) {
|
|
181
|
+
return {
|
|
182
|
+
machineId,
|
|
183
|
+
ip,
|
|
184
|
+
hasAccess: false,
|
|
185
|
+
reason: verdict.reason,
|
|
186
|
+
addMachineCommand: `yarn addmachine ${rootDomain} ${machineId} ${ip} git`,
|
|
187
|
+
};
|
|
246
188
|
}
|
|
247
|
-
|
|
189
|
+
return { machineId, ip, hasAccess: true, trustedMachines: await getTrustedMachines() };
|
|
248
190
|
}
|
|
249
191
|
|
|
250
192
|
/** Admin (so only this machine's own processes can call it): the buckets this process has loaded. A deploy successor asks its predecessor for this, so it activates exactly the buckets that are in use instead of every bucket on disk. */
|
|
251
193
|
async adminListActiveBuckets(): Promise<{ account: string; bucketName: string }[]> {
|
|
252
194
|
return getActiveBucketKeys();
|
|
253
195
|
}
|
|
254
|
-
async adminListRequests(config: { ip: string }): Promise<AccessRequest[]> {
|
|
255
|
-
let requests = await getRequests();
|
|
256
|
-
return await requests.get(config.ip) || [];
|
|
257
|
-
}
|
|
258
|
-
async adminGrantAccess(config: { requestId: string }): Promise<TrustRecord> {
|
|
259
|
-
let requestId = config.requestId;
|
|
260
|
-
let trust = await getTrust();
|
|
261
|
-
let requests = await getRequests();
|
|
262
|
-
for (let ip of await requests.getKeys()) {
|
|
263
|
-
for (let request of await requests.get(ip) || []) {
|
|
264
|
-
if (request.requestId !== requestId) continue;
|
|
265
|
-
let record: TrustRecord = {
|
|
266
|
-
account: request.account,
|
|
267
|
-
machineId: request.machineId,
|
|
268
|
-
ip: request.ip,
|
|
269
|
-
time: Date.now(),
|
|
270
|
-
};
|
|
271
|
-
await trust.set(`${request.account}|${request.machineId}`, record);
|
|
272
|
-
return record;
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
throw new Error(`No access request found with id ${JSON.stringify(requestId)}. It may have already been granted or expired.`);
|
|
276
|
-
}
|
|
277
196
|
|
|
278
197
|
@assertValidArgs
|
|
279
198
|
@trackAccessCall("get")
|
|
@@ -586,13 +505,8 @@ export const RemoteStorageController = SocketFunction.register(
|
|
|
586
505
|
() => ({
|
|
587
506
|
ping: {},
|
|
588
507
|
authenticate: {},
|
|
589
|
-
requestAccess: {},
|
|
590
508
|
getAccessState: {},
|
|
591
|
-
listRequestsForIP: { hooks: [accountAccess] },
|
|
592
|
-
grantAccess: {},
|
|
593
|
-
adminListRequests: { hooks: [adminAccess] },
|
|
594
509
|
adminListActiveBuckets: { hooks: [adminAccess] },
|
|
595
|
-
adminGrantAccess: { hooks: [adminAccess] },
|
|
596
510
|
get2: { hooks: [accountAccess] },
|
|
597
511
|
set: { hooks: [accountAccess] },
|
|
598
512
|
del: { hooks: [accountAccess] },
|
|
@@ -36,7 +36,7 @@ function getInternalIP(): string {
|
|
|
36
36
|
return candidates.find(ip => /^(10\.|192\.168\.|172\.(1[6-9]|2\d|3[01])\.)/.test(ip)) || candidates[0];
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
// The remote storage server, as a library function: consumers call hostStorageServer() from their own process to start hosting (or use the storageserver bin, see storageServerCli.ts).
|
|
39
|
+
// The remote storage server, as a library function: consumers call hostStorageServer() from their own process to start hosting (or use the storageserver bin, see storageServerCli.ts). Who may talk to it comes from the signed authorized_keys repo, read through security/machines - there is nothing to grant here.
|
|
40
40
|
|
|
41
41
|
export type HostStorageServerConfig = {
|
|
42
42
|
// Full URL of this storage server, e.g. "https://1-2-3-4.example.com:4444". The subdomain must be an ip domain - this machine's external IP with dashes, or 127-0-0-1 for local testing (see the validation in hostStorageServer). The domain and port are extracted from it (bucket routing URLs clients use look like
|
|
@@ -82,11 +82,6 @@ async function checkDiskSpace(config: { folder: string; threshold: number }): Pr
|
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
// Full path to the grantAccess CLI bootstrap that lives next to this file. The SSH command shown on the access page invokes it via `node <path> ...` (found through __dirname so consumers don't have to know where our source lives).
|
|
86
|
-
function getGrantAccessCliPath(): string {
|
|
87
|
-
return path.join(__dirname, "grantAccess.js");
|
|
88
|
-
}
|
|
89
|
-
|
|
90
85
|
export async function hostStorageServer(config: HostStorageServerConfig): Promise<void> {
|
|
91
86
|
let { url, folder } = config;
|
|
92
87
|
let { address: domain, port } = parseStorageUrl(url);
|
|
@@ -104,8 +99,6 @@ export async function hostStorageServer(config: HostStorageServerConfig): Promis
|
|
|
104
99
|
domain,
|
|
105
100
|
port,
|
|
106
101
|
rootDomain,
|
|
107
|
-
sshTarget: `${os.userInfo().username}@${ip}`,
|
|
108
|
-
serverCommand: `node ${getGrantAccessCliPath()} --url ${url}`,
|
|
109
102
|
folder: path.resolve(folder),
|
|
110
103
|
});
|
|
111
104
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { hostStorageServer } from "./storageServer";
|
|
2
2
|
import { getArg, getFlag } from "./cliArgs";
|
|
3
|
+
import { keysRepo } from "../../security/machines/machines";
|
|
3
4
|
|
|
4
5
|
// Hosts a storage server from the command line (via the storageserver bin, see package.json).
|
|
5
6
|
|
|
@@ -11,6 +12,19 @@ process.on("uncaughtException", (error) => {
|
|
|
11
12
|
});
|
|
12
13
|
|
|
13
14
|
async function main() {
|
|
15
|
+
// Checked before anything is hosted. Who may talk to this server comes from the authorized_keys
|
|
16
|
+
// repo, so a machine without one would start up and refuse every caller with an error about a
|
|
17
|
+
// repo, which is a confusing way to learn the machine was never set up.
|
|
18
|
+
try {
|
|
19
|
+
let { repoPath, sourceURL } = await keysRepo();
|
|
20
|
+
console.log(`Trusting the machines in ${sourceURL} (${repoPath})`);
|
|
21
|
+
} catch (e) {
|
|
22
|
+
throw new Error(
|
|
23
|
+
`This machine has no authorized_keys repo, so it has no way to know which machines to`
|
|
24
|
+
+ ` trust, and every caller would be refused.\n${e}`
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
14
28
|
let url = getArg("url");
|
|
15
29
|
if (!url) throw new Error(`--url is required (ex: --url https://storage.example.com:4444)`);
|
|
16
30
|
let folder = getArg("folder");
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { SocketFunction } from "socket-function/SocketFunction";
|
|
2
|
-
import { RemoteStorageController } from "./storageController";
|
|
3
|
-
import { authenticateStorage, parseStorageUrl } from "./ArchivesRemote";
|
|
4
|
-
import { getArg } from "./cliArgs";
|
|
5
|
-
|
|
6
|
-
// The grantAccess CLI. Invoked via the sibling grantAccess.js bootstrap (which loads typenode and then requires this file). Grants a specific access request by its requestId; must be run on the storage machine itself (its certs.ts identity is what the server trusts as admin).
|
|
7
|
-
|
|
8
|
-
async function main() {
|
|
9
|
-
let url = getArg("url");
|
|
10
|
-
if (!url) throw new Error(`--url is required (ex: --url https://storage.example.com:4444)`);
|
|
11
|
-
let requestId = getArg("requestId");
|
|
12
|
-
if (!requestId) throw new Error(`--requestId is required (the request id shown on the access page)`);
|
|
13
|
-
|
|
14
|
-
let { address, port } = parseStorageUrl(url);
|
|
15
|
-
let nodeId = SocketFunction.connect({ address, port });
|
|
16
|
-
await authenticateStorage({ address, port, nodeId });
|
|
17
|
-
let record = await RemoteStorageController.nodes[nodeId].adminGrantAccess({ requestId });
|
|
18
|
-
console.log(`Granted machine ${record.machineId} access to account ${JSON.stringify(record.account)}`);
|
|
19
|
-
process.exit(0);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
main().catch(e => {
|
|
23
|
-
console.error(e);
|
|
24
|
-
process.exit(1);
|
|
25
|
-
});
|