@forgezero/agent 0.1.57 → 0.1.59
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/README.md +17 -15
- package/dist/agent-heartbeat.js +1 -1
- package/dist/bootstrap.d.ts +22 -11
- package/dist/bootstrap.js +257 -159
- package/dist/cli/cloudflare-bootstrap.d.ts +14 -6
- package/dist/cloudflare-bootstrap.d.ts +10 -29
- package/dist/cloudflare-bootstrap.js +114 -81
- package/dist/cloudflare-edge.d.ts +28 -0
- package/dist/cloudflare-edge.js +78 -3
- package/dist/credential-schema.d.ts +9 -9
- package/dist/credential-schema.js +7 -7
- package/dist/fz-agent.js +27 -22
- package/dist/fz.js +392 -278
- package/dist/metal-bootstrap.js +1 -1
- package/dist/operator-bootstrap.d.ts +4 -1
- package/dist/operator-bootstrap.js +274 -179
- package/dist/platform-bootstrap-runtime.d.ts +1 -1
- package/dist/platform-bootstrap-runtime.js +3 -2
- package/dist/platform-fleet-verification.js +21 -14
- package/dist/provision.js +12 -6
- package/dist/recovery-host.js +2 -2
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
package/dist/cloudflare-edge.js
CHANGED
|
@@ -22,7 +22,7 @@ function privateDatabaseHostRoute(value) {
|
|
|
22
22
|
return `${address}/${isIP(address) === 4 ? 32 : 128}`;
|
|
23
23
|
}
|
|
24
24
|
var endpoint = "https://api.cloudflare.com/client/v4";
|
|
25
|
-
async function
|
|
25
|
+
async function cfEnvelope(config, path, init = {}, fetcher = fetch) {
|
|
26
26
|
const response = await fetcher(`${endpoint}${path}`, {
|
|
27
27
|
...init,
|
|
28
28
|
headers: {
|
|
@@ -35,7 +35,68 @@ async function cf(config, path, init = {}, fetcher = fetch) {
|
|
|
35
35
|
if (!response.ok || body.success !== true) {
|
|
36
36
|
throw new Error(body.errors?.map(({ message }) => message).filter(Boolean).join("; ") || `Cloudflare returned HTTP ${response.status}`);
|
|
37
37
|
}
|
|
38
|
-
return body
|
|
38
|
+
return body;
|
|
39
|
+
}
|
|
40
|
+
async function cf(config, path, init = {}, fetcher = fetch) {
|
|
41
|
+
return (await cfEnvelope(config, path, init, fetcher)).result;
|
|
42
|
+
}
|
|
43
|
+
async function cfPages(config, path, perPage, fetcher) {
|
|
44
|
+
const output = [];
|
|
45
|
+
for (let page = 1;page <= 100; page += 1) {
|
|
46
|
+
const separator = path.includes("?") ? "&" : "?";
|
|
47
|
+
const envelope = await cfEnvelope(config, `${path}${separator}page=${page}&per_page=${perPage}`, {}, fetcher);
|
|
48
|
+
const result = Array.isArray(envelope.result) ? envelope.result : [];
|
|
49
|
+
output.push(...result);
|
|
50
|
+
const totalPages = envelope.result_info?.total_pages;
|
|
51
|
+
if (Number.isInteger(totalPages) ? page >= totalPages : result.length < perPage)
|
|
52
|
+
return output;
|
|
53
|
+
}
|
|
54
|
+
throw new Error("Cloudflare pagination exceeded the reviewed 100-page bound");
|
|
55
|
+
}
|
|
56
|
+
var exactHexId = (value, label) => {
|
|
57
|
+
const normalized = String(value ?? "").trim().toLowerCase();
|
|
58
|
+
if (!/^[a-f0-9]{32}$/.test(normalized))
|
|
59
|
+
throw new Error(`${label} is malformed`);
|
|
60
|
+
return normalized;
|
|
61
|
+
};
|
|
62
|
+
async function discoverCloudflareBootstrapResources(config, fetcher = fetch) {
|
|
63
|
+
const zoneName = config.zoneName.trim().toLowerCase().replace(/\.$/, "");
|
|
64
|
+
const kvNamespaceTitle = config.kvNamespaceTitle.trim();
|
|
65
|
+
if (!/^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/.test(zoneName)) {
|
|
66
|
+
throw new Error("Cloudflare zone name is invalid");
|
|
67
|
+
}
|
|
68
|
+
if (!kvNamespaceTitle || kvNamespaceTitle.length > 512) {
|
|
69
|
+
throw new Error("Cloudflare KV namespace title is invalid");
|
|
70
|
+
}
|
|
71
|
+
const [managementStatus, runtimeStatus] = await Promise.all([
|
|
72
|
+
cf({ apiToken: config.tunnelToken }, "/user/tokens/verify", {}, fetcher),
|
|
73
|
+
cf({ apiToken: config.apiToken }, "/user/tokens/verify", {}, fetcher)
|
|
74
|
+
]);
|
|
75
|
+
if (managementStatus.status !== "active")
|
|
76
|
+
throw new Error("CF_TUNNEL_TOKEN is not active");
|
|
77
|
+
if (runtimeStatus.status !== "active")
|
|
78
|
+
throw new Error("CF_API_TOKEN is not active");
|
|
79
|
+
const zones = await cfPages({ apiToken: config.tunnelToken }, `/zones?name=${encodeURIComponent(zoneName)}&match=all&status=active`, 50, fetcher);
|
|
80
|
+
const matchingZones = zones.filter(({ name }) => name?.trim().toLowerCase() === zoneName);
|
|
81
|
+
if (matchingZones.length !== 1) {
|
|
82
|
+
throw new Error(`Cloudflare zone ${zoneName} must resolve to exactly one active zone`);
|
|
83
|
+
}
|
|
84
|
+
const zoneId = exactHexId(matchingZones[0].id, "Cloudflare zone id");
|
|
85
|
+
const accountId = exactHexId(matchingZones[0].account?.id, "Cloudflare account id");
|
|
86
|
+
const namespaces = await cfPages({ apiToken: config.apiToken }, `/accounts/${accountId}/storage/kv/namespaces?order=title&direction=asc`, 1000, fetcher);
|
|
87
|
+
const matchingNamespaces = namespaces.filter(({ title }) => title === kvNamespaceTitle);
|
|
88
|
+
if (matchingNamespaces.length !== 1) {
|
|
89
|
+
throw new Error(`Cloudflare KV namespace ${kvNamespaceTitle} must resolve to exactly one namespace`);
|
|
90
|
+
}
|
|
91
|
+
const kvNamespaceId = exactHexId(matchingNamespaces[0].id, "Cloudflare KV namespace id");
|
|
92
|
+
if (config.workerScriptName) {
|
|
93
|
+
await verifyCloudflareWorkerDurableObjects({
|
|
94
|
+
accountId,
|
|
95
|
+
scriptName: config.workerScriptName,
|
|
96
|
+
apiToken: config.apiToken
|
|
97
|
+
}, fetcher);
|
|
98
|
+
}
|
|
99
|
+
return { accountId, zoneId, kvNamespaceId };
|
|
39
100
|
}
|
|
40
101
|
async function ensureCloudflarePrivateRoute(config, fetcher = fetch) {
|
|
41
102
|
const [address, prefixText, ...extra] = config.network.split("/");
|
|
@@ -211,7 +272,7 @@ async function verifyCloudflareWorkerDurableObjects(config, fetcher = fetch) {
|
|
|
211
272
|
if (!/^[A-Za-z0-9][A-Za-z0-9_-]{0,62}$/.test(config.scriptName)) {
|
|
212
273
|
throw new Error("Cloudflare Worker script name is invalid");
|
|
213
274
|
}
|
|
214
|
-
const namespaces = await
|
|
275
|
+
const namespaces = await cfPages(config, `/accounts/${config.accountId}/workers/durable_objects/namespaces`, 1000, fetcher);
|
|
215
276
|
const owned = namespaces.filter(({ script }) => script === config.scriptName);
|
|
216
277
|
if (!owned.length)
|
|
217
278
|
throw new Error(`Cloudflare Worker ${config.scriptName} has no Durable Object namespace`);
|
|
@@ -283,8 +344,21 @@ async function ensureCloudflareTunnel(config, fetcher = fetch) {
|
|
|
283
344
|
}
|
|
284
345
|
return { tunnel, connectorToken, created };
|
|
285
346
|
}
|
|
347
|
+
async function retrieveCloudflareConnectorTokens(config, fetcher = fetch) {
|
|
348
|
+
if (!/^[a-f0-9]{32}$/i.test(config.accountId) || !/^[0-9a-f-]{36}$/i.test(config.tunnelId) || config.meshConnectorId && !/^[0-9a-f-]{36}$/i.test(config.meshConnectorId)) {
|
|
349
|
+
throw new Error("Cloudflare connector retrieval coordinates are invalid");
|
|
350
|
+
}
|
|
351
|
+
const connectorToken = await cf(config, `/accounts/${config.accountId}/cfd_tunnel/${encodeURIComponent(config.tunnelId)}/token`, {}, fetcher);
|
|
352
|
+
const meshConnectorToken = config.meshConnectorId ? await cf(config, `/accounts/${config.accountId}/warp_connector/${encodeURIComponent(config.meshConnectorId)}/token`, {}, fetcher) : undefined;
|
|
353
|
+
for (const value of [connectorToken, meshConnectorToken]) {
|
|
354
|
+
if (value !== undefined && (!value || value.length > 16384))
|
|
355
|
+
throw new Error("Cloudflare returned an invalid connector token");
|
|
356
|
+
}
|
|
357
|
+
return { connectorToken, ...meshConnectorToken ? { meshConnectorToken } : {} };
|
|
358
|
+
}
|
|
286
359
|
export {
|
|
287
360
|
verifyCloudflareWorkerDurableObjects,
|
|
361
|
+
retrieveCloudflareConnectorTokens,
|
|
288
362
|
removeCloudflareWarpDatabaseInclude,
|
|
289
363
|
removeCloudflarePrivateRoute,
|
|
290
364
|
removeCloudflarePrivateDatabaseRoute,
|
|
@@ -294,6 +368,7 @@ export {
|
|
|
294
368
|
ensureCloudflarePrivateRoute,
|
|
295
369
|
ensureCloudflarePrivateDatabaseRoute,
|
|
296
370
|
ensureCloudflareMeshConnector,
|
|
371
|
+
discoverCloudflareBootstrapResources,
|
|
297
372
|
configureCloudflareRealtimeSecrets,
|
|
298
373
|
configureCloudflareEdge
|
|
299
374
|
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* One credential policy for every place the Agent can run.
|
|
3
3
|
*
|
|
4
|
-
* Operator credentials are attended input
|
|
5
|
-
*
|
|
4
|
+
* Operator credentials are hidden attended input and are never persisted as a
|
|
5
|
+
* plaintext source. Physical-metal credentials are fixed host identities loaded
|
|
6
6
|
* only by systemd. Enrolled platform and tenant computes read their assigned
|
|
7
7
|
* project/environment Vault replica first and may use only an explicitly
|
|
8
8
|
* installed, same-name systemd credential while that replica cannot answer.
|
|
@@ -34,13 +34,13 @@ export declare const CLOUDFLARE_CREDENTIAL_NAMES: {
|
|
|
34
34
|
export declare const CLOUDFLARE_CREDENTIAL_SCHEMA: {
|
|
35
35
|
readonly CF_TUNNEL_TOKEN: {
|
|
36
36
|
readonly permissions: readonly ["Account:Cloudflare Tunnel Write", "Account:Cloudflare One Connector: WARP Write", "Account:Cloudflare One Networks Write", "Account:Zero Trust Write", "Zone:DNS Write"];
|
|
37
|
-
readonly platformBootstrap: "
|
|
38
|
-
readonly platformRuntime: "vault";
|
|
37
|
+
readonly platformBootstrap: "hidden-prompt-to-systemd";
|
|
38
|
+
readonly platformRuntime: "vault-then-systemd";
|
|
39
39
|
readonly tenantControl: "vault";
|
|
40
40
|
};
|
|
41
41
|
readonly CF_API_TOKEN: {
|
|
42
42
|
readonly permissions: readonly ["Account:Workers KV Storage Write", "Account:Workers Scripts Write"];
|
|
43
|
-
readonly platformBootstrap: "
|
|
43
|
+
readonly platformBootstrap: "hidden-prompt-to-systemd";
|
|
44
44
|
readonly platformRuntime: "vault-then-systemd";
|
|
45
45
|
readonly tenantControl: "vault";
|
|
46
46
|
};
|
|
@@ -67,22 +67,22 @@ export declare const AGENT_CREDENTIAL_POLICY: {
|
|
|
67
67
|
readonly operator: {
|
|
68
68
|
readonly vault: false;
|
|
69
69
|
readonly systemdFallback: false;
|
|
70
|
-
readonly
|
|
70
|
+
readonly hiddenInput: true;
|
|
71
71
|
};
|
|
72
72
|
readonly metal: {
|
|
73
73
|
readonly vault: false;
|
|
74
74
|
readonly systemdFallback: true;
|
|
75
|
-
readonly
|
|
75
|
+
readonly hiddenInput: false;
|
|
76
76
|
};
|
|
77
77
|
readonly 'platform-compute': {
|
|
78
78
|
readonly vault: true;
|
|
79
79
|
readonly systemdFallback: true;
|
|
80
|
-
readonly
|
|
80
|
+
readonly hiddenInput: false;
|
|
81
81
|
};
|
|
82
82
|
readonly 'tenant-compute': {
|
|
83
83
|
readonly vault: true;
|
|
84
84
|
readonly systemdFallback: true;
|
|
85
|
-
readonly
|
|
85
|
+
readonly hiddenInput: false;
|
|
86
86
|
};
|
|
87
87
|
};
|
|
88
88
|
export interface DeploymentCredentialBinding {
|
|
@@ -300,13 +300,13 @@ var CLOUDFLARE_CREDENTIAL_SCHEMA = {
|
|
|
300
300
|
"Account:Zero Trust Write",
|
|
301
301
|
"Zone:DNS Write"
|
|
302
302
|
],
|
|
303
|
-
platformBootstrap: "
|
|
304
|
-
platformRuntime: "vault",
|
|
303
|
+
platformBootstrap: "hidden-prompt-to-systemd",
|
|
304
|
+
platformRuntime: "vault-then-systemd",
|
|
305
305
|
tenantControl: "vault"
|
|
306
306
|
},
|
|
307
307
|
CF_API_TOKEN: {
|
|
308
308
|
permissions: ["Account:Workers KV Storage Write", "Account:Workers Scripts Write"],
|
|
309
|
-
platformBootstrap: "
|
|
309
|
+
platformBootstrap: "hidden-prompt-to-systemd",
|
|
310
310
|
platformRuntime: "vault-then-systemd",
|
|
311
311
|
tenantControl: "vault"
|
|
312
312
|
},
|
|
@@ -330,10 +330,10 @@ var CLOUDFLARE_CREDENTIAL_SCHEMA = {
|
|
|
330
330
|
}
|
|
331
331
|
};
|
|
332
332
|
var AGENT_CREDENTIAL_POLICY = {
|
|
333
|
-
operator: { vault: false, systemdFallback: false,
|
|
334
|
-
metal: { vault: false, systemdFallback: true,
|
|
335
|
-
"platform-compute": { vault: true, systemdFallback: true,
|
|
336
|
-
"tenant-compute": { vault: true, systemdFallback: true,
|
|
333
|
+
operator: { vault: false, systemdFallback: false, hiddenInput: true },
|
|
334
|
+
metal: { vault: false, systemdFallback: true, hiddenInput: false },
|
|
335
|
+
"platform-compute": { vault: true, systemdFallback: true, hiddenInput: false },
|
|
336
|
+
"tenant-compute": { vault: true, systemdFallback: true, hiddenInput: false }
|
|
337
337
|
};
|
|
338
338
|
var CREDENTIAL_NAME = /^[A-Z_][A-Z0-9_]*$/;
|
|
339
339
|
var SCOPE_PART2 = /^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$/;
|
package/dist/fz-agent.js
CHANGED
|
@@ -8448,7 +8448,7 @@ function assertSupportedGuestImage(imageKey) {
|
|
|
8448
8448
|
}
|
|
8449
8449
|
|
|
8450
8450
|
// src/version.ts
|
|
8451
|
-
var VERSION2 = "0.1.
|
|
8451
|
+
var VERSION2 = "0.1.59";
|
|
8452
8452
|
|
|
8453
8453
|
// src/ssh-bootstrap.ts
|
|
8454
8454
|
class SshBootstrapError extends Error {
|
|
@@ -8583,7 +8583,7 @@ var verifiedBunArchive = async (directory) => {
|
|
|
8583
8583
|
writeFileSync4(archive, bytes, { mode: 384, flag: "wx" });
|
|
8584
8584
|
return archive;
|
|
8585
8585
|
};
|
|
8586
|
-
var remote = (exec, ssh, destination, code, argv2, secret = false) => checked(exec, ["ssh", ...ssh, destination, "--", ...argv2], code, { secret });
|
|
8586
|
+
var remote = (exec, ssh, destination, code, argv2, secret = false, stdin) => checked(exec, ["ssh", ...ssh, destination, "--", ...argv2], code, { secret, ...stdin !== undefined ? { stdin } : {} });
|
|
8587
8587
|
async function executeSshBootstrap(claim, options) {
|
|
8588
8588
|
validateClaim(claim);
|
|
8589
8589
|
if (!options.sshKeyPath.startsWith("/") || /[\r\n]/.test(options.sshKeyPath))
|
|
@@ -8622,14 +8622,11 @@ async function executeSshBootstrap(claim, options) {
|
|
|
8622
8622
|
realm: claim.realm,
|
|
8623
8623
|
nodeHostname: claim.target.nodeHostname,
|
|
8624
8624
|
telemetryEndpoint: telemetry.origin,
|
|
8625
|
-
enrolTokenFile: "/run/forgezero-bootstrap/enrol.token",
|
|
8626
8625
|
profile: "app",
|
|
8627
8626
|
software: [{ id: "bun", version: PINNED_BUN_VERSION }]
|
|
8628
8627
|
};
|
|
8629
8628
|
const files = {
|
|
8630
8629
|
"config.json": `${JSON.stringify(config, null, 2)}
|
|
8631
|
-
`,
|
|
8632
|
-
"enrol.token": `${claim.enrolmentToken}
|
|
8633
8630
|
`
|
|
8634
8631
|
};
|
|
8635
8632
|
for (const [name, content] of Object.entries(files))
|
|
@@ -8753,7 +8750,7 @@ async function executeSshBootstrap(claim, options) {
|
|
|
8753
8750
|
"/usr/local/lib/forgezero/agent/fz.js",
|
|
8754
8751
|
"/usr/local/bin/fz"
|
|
8755
8752
|
]);
|
|
8756
|
-
for (const name of ["config.json"
|
|
8753
|
+
for (const name of ["config.json"])
|
|
8757
8754
|
await remote(exec, ssh, destination, "REMOTE_CONFIG_INSTALL_FAILED", [
|
|
8758
8755
|
"/usr/bin/sudo",
|
|
8759
8756
|
"-n",
|
|
@@ -8769,10 +8766,12 @@ async function executeSshBootstrap(claim, options) {
|
|
|
8769
8766
|
"/usr/local/bin/fz",
|
|
8770
8767
|
"agent",
|
|
8771
8768
|
"activate",
|
|
8769
|
+
"credentials-stdin",
|
|
8772
8770
|
"--bootstrap-config",
|
|
8773
8771
|
"/run/forgezero-bootstrap/config.json",
|
|
8774
8772
|
"--apply"
|
|
8775
|
-
], true)
|
|
8773
|
+
], true, `${JSON.stringify({ enrolmentToken: claim.enrolmentToken })}
|
|
8774
|
+
`);
|
|
8776
8775
|
await remote(exec, ssh, destination, "REMOTE_CLEANUP_FAILED", [
|
|
8777
8776
|
"/usr/bin/sudo",
|
|
8778
8777
|
"-n",
|
|
@@ -13078,10 +13077,10 @@ var AGENT_CREDENTIAL_LOCATIONS = [
|
|
|
13078
13077
|
"tenant-compute"
|
|
13079
13078
|
];
|
|
13080
13079
|
var AGENT_CREDENTIAL_POLICY = {
|
|
13081
|
-
operator: { vault: false, systemdFallback: false,
|
|
13082
|
-
metal: { vault: false, systemdFallback: true,
|
|
13083
|
-
"platform-compute": { vault: true, systemdFallback: true,
|
|
13084
|
-
"tenant-compute": { vault: true, systemdFallback: true,
|
|
13080
|
+
operator: { vault: false, systemdFallback: false, hiddenInput: true },
|
|
13081
|
+
metal: { vault: false, systemdFallback: true, hiddenInput: false },
|
|
13082
|
+
"platform-compute": { vault: true, systemdFallback: true, hiddenInput: false },
|
|
13083
|
+
"tenant-compute": { vault: true, systemdFallback: true, hiddenInput: false }
|
|
13085
13084
|
};
|
|
13086
13085
|
var CREDENTIAL_NAME = /^[A-Z_][A-Z0-9_]*$/;
|
|
13087
13086
|
var SCOPE_PART2 = /^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$/;
|
|
@@ -13365,7 +13364,7 @@ async function runRecoveryHost(args, run2 = execute2) {
|
|
|
13365
13364
|
nonEmpty("/etc/forgezero/creds/arangodb-jwt.cred");
|
|
13366
13365
|
const hasDatabase = role !== "none";
|
|
13367
13366
|
if (hasDatabase) {
|
|
13368
|
-
nonEmpty("/etc/forgezero/creds/backup
|
|
13367
|
+
nonEmpty("/etc/forgezero/creds/backup.s3.secretAccessKey.cred");
|
|
13369
13368
|
nonEmpty("/etc/forgezero/creds/backup-recovery-root.cred");
|
|
13370
13369
|
}
|
|
13371
13370
|
const slot = activeSlot();
|
|
@@ -13412,7 +13411,7 @@ async function runRecoveryHost(args, run2 = execute2) {
|
|
|
13412
13411
|
"--property=LoadCredentialEncrypted=arangodb-jwt:/etc/forgezero/creds/arangodb-jwt.cred",
|
|
13413
13412
|
...args.includes("--fleet-fenced") ? ["--property=Environment=FZ_RECOVERY_FLEET_FENCED=all-api-and-scheduler-replicas-are-stopped"] : [],
|
|
13414
13413
|
...args.includes("--backup-credentials") ? [
|
|
13415
|
-
"--property=LoadCredentialEncrypted=backup
|
|
13414
|
+
"--property=LoadCredentialEncrypted=backup.s3.secretAccessKey:/etc/forgezero/creds/backup.s3.secretAccessKey.cred",
|
|
13416
13415
|
"--property=LoadCredentialEncrypted=backup-recovery-root:/etc/forgezero/creds/backup-recovery-root.cred"
|
|
13417
13416
|
] : []
|
|
13418
13417
|
];
|
|
@@ -14177,10 +14176,11 @@ function planProvision(options) {
|
|
|
14177
14176
|
const warpEnabled = warpValues.every(Boolean);
|
|
14178
14177
|
if (warpValues.some(Boolean) && !warpEnabled)
|
|
14179
14178
|
throw new Error("WARP configuration must be supplied together");
|
|
14180
|
-
const enrolmentEnabled = Boolean(options.
|
|
14181
|
-
if (Boolean(options.
|
|
14182
|
-
throw new Error("direct enrolment paths must be supplied together");
|
|
14183
|
-
|
|
14179
|
+
const enrolmentEnabled = Boolean(options.enrolTokenCredentialPath && options.enrolStatePath);
|
|
14180
|
+
if (Boolean(options.enrolTokenCredentialPath) !== Boolean(options.enrolStatePath) || options.enrolTokenSourcePath && !enrolmentEnabled) {
|
|
14181
|
+
throw new Error("direct enrolment credential and state paths must be supplied together");
|
|
14182
|
+
}
|
|
14183
|
+
const enrolTokenSourcePath = options.enrolTokenSourcePath ? systemdPath(options.enrolTokenSourcePath, "enrolment source") : undefined;
|
|
14184
14184
|
const enrolTokenCredentialPath = enrolmentEnabled ? systemdPath(options.enrolTokenCredentialPath, "enrolment credential") : undefined;
|
|
14185
14185
|
const enrolStatePath = enrolmentEnabled ? systemdPath(options.enrolStatePath, "enrolment state") : undefined;
|
|
14186
14186
|
const enrolStateDir = enrolStatePath?.replace(/\/[^/]+$/, "");
|
|
@@ -14308,7 +14308,12 @@ function planProvision(options) {
|
|
|
14308
14308
|
] : [],
|
|
14309
14309
|
...enrolmentEnabled ? [
|
|
14310
14310
|
step2("enrolment state directory", { kind: "directories", directories: [{ path: enrolStateDir, mode: 448, owner: user, group: user }] }),
|
|
14311
|
-
step2("encrypted one-time enrolment capability", {
|
|
14311
|
+
...enrolTokenSourcePath ? [step2("encrypted one-time enrolment capability", {
|
|
14312
|
+
kind: "ensure-enrolment",
|
|
14313
|
+
state: enrolStatePath,
|
|
14314
|
+
source: enrolTokenSourcePath,
|
|
14315
|
+
credential: enrolTokenCredentialPath
|
|
14316
|
+
})] : []
|
|
14312
14317
|
] : [],
|
|
14313
14318
|
...deploymentEnabled ? [step2("deployment directories", { kind: "directories", directories: [
|
|
14314
14319
|
{ path: deployRoot, mode: 493, owner: "root", group: "root" },
|
|
@@ -14657,6 +14662,7 @@ var JWT_CREDENTIAL = `${CREDS}/arangodb-jwt.cred`;
|
|
|
14657
14662
|
var ENROL_CREDENTIAL = `${CREDS}/enrol-token.cred`;
|
|
14658
14663
|
var TUNNEL_CREDENTIAL = `${CREDS}/CF_TUNNEL_CONNECTOR_TOKEN.cred`;
|
|
14659
14664
|
var CF_API_CREDENTIAL = `${CREDS}/CF_API_TOKEN.cred`;
|
|
14665
|
+
var CF_TUNNEL_API_CREDENTIAL = `${CREDS}/CF_TUNNEL_TOKEN.cred`;
|
|
14660
14666
|
var WARP_CONNECTOR_CREDENTIAL = `${CREDS}/CF_WARP_CONNECTOR_TOKEN.cred`;
|
|
14661
14667
|
var REALTIME_PUBLISH_CREDENTIAL = `${CREDS}/REALTIME_PUBLISH_SECRET.cred`;
|
|
14662
14668
|
var REALTIME_TICKET_CREDENTIAL = `${CREDS}/REALTIME_TICKET_SECRET.cred`;
|
|
@@ -14887,10 +14893,10 @@ function localBootstrapHost() {
|
|
|
14887
14893
|
throw new Error(`Agent software requirements failed: ${result.output.trim()}`);
|
|
14888
14894
|
return result;
|
|
14889
14895
|
},
|
|
14890
|
-
async installAgent(config
|
|
14896
|
+
async installAgent(config) {
|
|
14891
14897
|
const capabilities = await readCapabilities(localRunner);
|
|
14892
14898
|
const deployRoot = config.deployRoot ?? "/opt/forgezero";
|
|
14893
|
-
const hasBinding = config.kind === "enrolled-compute" ||
|
|
14899
|
+
const hasBinding = config.kind === "enrolled-compute" || existsSync19(ENROL_CREDENTIAL) || existsSync19("/var/lib/forgezero/enrolment.json");
|
|
14894
14900
|
if (config.kind === "platform") {
|
|
14895
14901
|
const lifecycle = config.database.role === "none" ? {
|
|
14896
14902
|
apiUnits: ["forgezero@blue.service", "forgezero@green.service"],
|
|
@@ -14933,8 +14939,7 @@ function localBootstrapHost() {
|
|
|
14933
14939
|
telemetryEndpoint: config.telemetryEndpoint,
|
|
14934
14940
|
binPath: "/usr/local/lib/forgezero/agent/fz-agent",
|
|
14935
14941
|
sourceBinPath: PACKAGED_AGENT_BIN,
|
|
14936
|
-
...
|
|
14937
|
-
enrolTokenSourcePath: config.kind === "enrolled-compute" ? config.enrolTokenFile : enrolTokenSourcePath,
|
|
14942
|
+
...hasBinding ? {
|
|
14938
14943
|
enrolTokenCredentialPath: ENROL_CREDENTIAL,
|
|
14939
14944
|
enrolStatePath: "/var/lib/forgezero/enrolment.json",
|
|
14940
14945
|
apiUrl: config.apiUrl,
|