@coffre/cli 0.1.0 → 0.1.2
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 +4 -1
- package/dist/access-CO1kGjWW.js +74 -0
- package/dist/connect-BONR52al.js +770 -0
- package/dist/connect-BZdPYNbZ.js +2 -0
- package/dist/esm-B69mMLcR.js +2 -0
- package/dist/esm-Bts-esAl.js +4202 -0
- package/dist/keys-D9CG_wYV.js +64 -0
- package/dist/main.js +12 -73
- package/dist/migrations/postgres/0000_baseline.sql +505 -0
- package/dist/migrations/postgres/meta/0000_snapshot.json +1939 -0
- package/dist/migrations/postgres/meta/_journal.json +13 -0
- package/dist/migrator-W1a-ooWi.js +37 -0
- package/dist/node-postgres-BlNvYfq_.js +2 -0
- package/dist/node-postgres-_y-EeSHQ.js +2831 -0
- package/dist/portable-CPh2thNN.js +87 -0
- package/dist/relations-CJCGPRst.js +2712 -0
- package/dist/schema-sqlite-Byp7B3yI.js +1078 -0
- package/dist/setup-DPHVyr2N.js +521 -0
- package/dist/templates/node/README.md +38 -33
- package/dist/templates/node/package.json +3 -3
- package/dist/templates/node/server.env.example +4 -3
- package/dist/templates/node/src/server.ts +1 -1
- package/dist/templates/node/src/vault.ts +5 -3
- package/dist/templates/node/vault.env.example +4 -5
- package/dist/templates/workers/README.md +40 -42
- package/dist/templates/workers/app/wrangler.jsonc +1 -0
- package/dist/templates/workers/package.json +4 -4
- package/dist/templates/workers/vault/src/worker.ts +5 -4
- package/dist/templates/workers/vault/wrangler.jsonc +4 -3
- package/package.json +7 -4
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { parseArgs } from "node:util";
|
|
2
|
+
import { randomBytes } from "node:crypto";
|
|
3
|
+
//#region src/keys.ts
|
|
4
|
+
/**
|
|
5
|
+
* Two fresh keys, each 32 random bytes in base64, and an id for the KEK
|
|
6
|
+
* dated to the day, so that a rotation, even one the same month, gets a new
|
|
7
|
+
* one: the vault refuses two KEKs with the same id.
|
|
8
|
+
*/
|
|
9
|
+
function generateKeys(now = /* @__PURE__ */ new Date()) {
|
|
10
|
+
const key = () => randomBytes(32).toString("base64");
|
|
11
|
+
return {
|
|
12
|
+
KEK_ID: `kek-${now.toISOString().slice(0, 10)}`,
|
|
13
|
+
KEK: key(),
|
|
14
|
+
AUDIT_CHAIN_KEY: key()
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/** What each key is for, as comments: `coffre keys` and `coffre setup` both say it. */
|
|
18
|
+
const KEYS_EXPLAINED = `# Two keys, one for each component, so that the app, which faces the
|
|
19
|
+
# network, never holds what decrypts a value:
|
|
20
|
+
#
|
|
21
|
+
# KEK the vault's. Decrypts every value, and signs the vault's
|
|
22
|
+
# log entries, member rows and checkpoints. Lose it, and
|
|
23
|
+
# every value is lost for good.
|
|
24
|
+
# AUDIT_CHAIN_KEY the app's. Signs the app's log entries, sessions and
|
|
25
|
+
# tokens. Lose it, and everyone is signed out and the log
|
|
26
|
+
# stops verifying.
|
|
27
|
+
# KEK_ID names the KEK; not secret.
|
|
28
|
+
#
|
|
29
|
+
# Whoever has the KEK and a copy of the database has every value: keep it
|
|
30
|
+
# apart from the backups.
|
|
31
|
+
`;
|
|
32
|
+
/** The keys as a dotenv block, then, as comments, what each is for and where it goes. */
|
|
33
|
+
function formatKeys(keys) {
|
|
34
|
+
return `KEK_ID=${keys.KEK_ID}
|
|
35
|
+
KEK=${keys.KEK}
|
|
36
|
+
AUDIT_CHAIN_KEY=${keys.AUDIT_CHAIN_KEY}
|
|
37
|
+
|
|
38
|
+
# Save all three in your password manager now. They are shown once, and
|
|
39
|
+
# coffre keeps no copy.
|
|
40
|
+
#
|
|
41
|
+
${KEYS_EXPLAINED}#
|
|
42
|
+
# On Workers, KEK_ID is a var in vault/wrangler.jsonc, and KEK and
|
|
43
|
+
# AUDIT_CHAIN_KEY are Worker secrets (wrangler secret put). On Node, KEK_ID
|
|
44
|
+
# and KEK go in vault.env, and AUDIT_CHAIN_KEY in server.env.
|
|
45
|
+
#
|
|
46
|
+
# These are for a new deployment. To rotate a deployment's KEK, take only
|
|
47
|
+
# KEK_ID and KEK, and keep the old pair in previousKeks, for what it wrapped
|
|
48
|
+
# and signed. AUDIT_CHAIN_KEY cannot be changed.
|
|
49
|
+
`;
|
|
50
|
+
}
|
|
51
|
+
function keys(args) {
|
|
52
|
+
const { values } = parseArgs({
|
|
53
|
+
args,
|
|
54
|
+
options: { json: {
|
|
55
|
+
type: "boolean",
|
|
56
|
+
default: false
|
|
57
|
+
} },
|
|
58
|
+
allowPositionals: false
|
|
59
|
+
});
|
|
60
|
+
const fresh = generateKeys();
|
|
61
|
+
process.stdout.write(values.json ? `${JSON.stringify(fresh)}\n` : formatKeys(fresh));
|
|
62
|
+
}
|
|
63
|
+
//#endregion
|
|
64
|
+
export { generateKeys as n, keys as r, KEYS_EXPLAINED as t };
|
package/dist/main.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { r as keys } from "./keys-D9CG_wYV.js";
|
|
3
|
+
import { i as isRole, r as assignableToEnvironment, t as ROLES } from "./access-CO1kGjWW.js";
|
|
2
4
|
import { parseArgs, promisify } from "node:util";
|
|
3
5
|
import { execFile, spawn } from "node:child_process";
|
|
4
6
|
import { chmodSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
@@ -466,79 +468,6 @@ async function planImport(coffre, path, entries) {
|
|
|
466
468
|
};
|
|
467
469
|
}
|
|
468
470
|
//#endregion
|
|
469
|
-
//#region ../core/dist/access.js
|
|
470
|
-
/**
|
|
471
|
-
* Who may do what: seven permissions and the six roles that bundle them.
|
|
472
|
-
*
|
|
473
|
-
* Deliberately constants and not tables or a policy language. A grant names
|
|
474
|
-
* one role for one member on one place, and a check is a lookup in this file,
|
|
475
|
-
* so authorisation stays something you can read end to end.
|
|
476
|
-
*/
|
|
477
|
-
const PERMISSIONS = [
|
|
478
|
-
"secret.read",
|
|
479
|
-
"secret.write",
|
|
480
|
-
"secret.archive",
|
|
481
|
-
"audit.read",
|
|
482
|
-
"environment.manage",
|
|
483
|
-
"grant.manage",
|
|
484
|
-
"project.manage"
|
|
485
|
-
];
|
|
486
|
-
/**
|
|
487
|
-
* Permissions that only make sense on a project. Granting
|
|
488
|
-
* `environment.manage` on one environment would be incoherent: what it
|
|
489
|
-
* authorises is creating that environment's siblings.
|
|
490
|
-
*/
|
|
491
|
-
const PROJECT_ONLY_PERMISSIONS = [
|
|
492
|
-
"environment.manage",
|
|
493
|
-
"grant.manage",
|
|
494
|
-
"project.manage"
|
|
495
|
-
];
|
|
496
|
-
const ROLES = {
|
|
497
|
-
viewer: {
|
|
498
|
-
name: "Viewer",
|
|
499
|
-
description: "Read secret values.",
|
|
500
|
-
permissions: ["secret.read"]
|
|
501
|
-
},
|
|
502
|
-
developer: {
|
|
503
|
-
name: "Developer",
|
|
504
|
-
description: "Read and write secrets.",
|
|
505
|
-
permissions: ["secret.read", "secret.write"]
|
|
506
|
-
},
|
|
507
|
-
maintainer: {
|
|
508
|
-
name: "Maintainer",
|
|
509
|
-
description: "Read, write and retire secrets, and manage environments.",
|
|
510
|
-
permissions: [
|
|
511
|
-
"secret.read",
|
|
512
|
-
"secret.write",
|
|
513
|
-
"secret.archive",
|
|
514
|
-
"environment.manage"
|
|
515
|
-
]
|
|
516
|
-
},
|
|
517
|
-
"access-manager": {
|
|
518
|
-
name: "Access manager",
|
|
519
|
-
description: "Manage who has access. Cannot read secret values.",
|
|
520
|
-
permissions: ["grant.manage"]
|
|
521
|
-
},
|
|
522
|
-
auditor: {
|
|
523
|
-
name: "Auditor",
|
|
524
|
-
description: "Read the audit log. Cannot read secret values.",
|
|
525
|
-
permissions: ["audit.read"]
|
|
526
|
-
},
|
|
527
|
-
owner: {
|
|
528
|
-
name: "Owner",
|
|
529
|
-
description: "Everything, including reading secret values.",
|
|
530
|
-
permissions: [...PERMISSIONS]
|
|
531
|
-
}
|
|
532
|
-
};
|
|
533
|
-
Object.keys(ROLES);
|
|
534
|
-
function isRole(value) {
|
|
535
|
-
return Object.hasOwn(ROLES, value);
|
|
536
|
-
}
|
|
537
|
-
/** Roles that hold a project-only permission can only be granted on a project. */
|
|
538
|
-
function assignableToEnvironment(role) {
|
|
539
|
-
return !ROLES[role].permissions.some((permission) => PROJECT_ONLY_PERMISSIONS.includes(permission));
|
|
540
|
-
}
|
|
541
|
-
//#endregion
|
|
542
471
|
//#region ../core/dist/dotenv.js
|
|
543
472
|
const KEY_RE = /^[A-Za-z_][A-Za-z0-9_]{0,127}$/;
|
|
544
473
|
function parseDotenv(input) {
|
|
@@ -1531,6 +1460,10 @@ const USAGE = `coffre - secrets, with an audit log
|
|
|
1531
1460
|
New deployment
|
|
1532
1461
|
coffre init --workers [<dir>] two Cloudflare Workers: the app and its vault
|
|
1533
1462
|
coffre init --node [<dir>] a Node server, and its vault beside it
|
|
1463
|
+
coffre setup [--reset-passwords] [--json]
|
|
1464
|
+
its database logins, migrations and keys, in one go,
|
|
1465
|
+
shown once (the connection string is asked for)
|
|
1466
|
+
coffre keys [--json] its keys and the KEK's id alone, made here and shown once
|
|
1534
1467
|
|
|
1535
1468
|
Session
|
|
1536
1469
|
coffre login [<url>] [--no-browser] sign in, and make <url> the current instance
|
|
@@ -1579,6 +1512,12 @@ switch (command) {
|
|
|
1579
1512
|
case "init":
|
|
1580
1513
|
initProject(rest);
|
|
1581
1514
|
break;
|
|
1515
|
+
case "keys":
|
|
1516
|
+
keys(rest);
|
|
1517
|
+
break;
|
|
1518
|
+
case "setup":
|
|
1519
|
+
await (await import("./setup-DPHVyr2N.js")).setup(rest);
|
|
1520
|
+
break;
|
|
1582
1521
|
case "login":
|
|
1583
1522
|
await login(rest);
|
|
1584
1523
|
break;
|