78 lines
2.6 KiB
JavaScript
78 lines
2.6 KiB
JavaScript
import {existsSync} from "node:fs";
|
|
import {loadBuiltinProviders} from "./providers.mjs";
|
|
import {breakGlassActor, loadKeyFile, Vault} from "./vault.mjs";
|
|
|
|
const databasePath = process.env.DATABASE_PATH || "/data/vault.db";
|
|
const masterKeyFile = process.env.MASTER_KEY_FILE || "/run/secrets/vault_master_key";
|
|
const [command, ...args] = process.argv.slice(2);
|
|
|
|
function usage() {
|
|
console.log(`Usage:
|
|
node cli.mjs list [owner-sub]
|
|
node cli.mjs audit [limit]
|
|
node cli.mjs verify <credential-id>
|
|
node cli.mjs delete <credential-id>
|
|
node cli.mjs reassign <credential-id> <issuer> <sub> <username>
|
|
node cli.mjs rotate-master <new-key-file>
|
|
node cli.mjs backup <destination.db>`);
|
|
}
|
|
|
|
if (!command) {
|
|
usage();
|
|
process.exit(1);
|
|
}
|
|
|
|
const vault = new Vault({
|
|
databasePath,
|
|
masterKey: loadKeyFile(masterKeyFile),
|
|
builtinProviders: loadBuiltinProviders(process.env.PROVIDER_CATALOG_FILE || "/app/providers.json")
|
|
});
|
|
const actor = breakGlassActor();
|
|
|
|
try {
|
|
if (command === "list") {
|
|
const credentials = vault.listCredentials(actor, {all: true});
|
|
const filtered = args[0]
|
|
? credentials.filter((credential) => credential.owner.sub === args[0])
|
|
: credentials;
|
|
console.table(filtered.map((credential) => ({
|
|
id: credential.id,
|
|
owner: credential.owner.username,
|
|
sub: credential.owner.sub,
|
|
provider: credential.provider,
|
|
name: credential.name,
|
|
fingerprint: credential.fingerprint,
|
|
status: credential.status
|
|
})));
|
|
} else if (command === "audit") {
|
|
console.table(vault.listAudit(actor, {all: true, limit: args[0] || 100}));
|
|
} else if (command === "verify" && args[0]) {
|
|
console.log(vault.verifyCredential(actor, args[0]));
|
|
} else if (command === "delete" && args[0]) {
|
|
console.log(vault.deleteCredential(actor, args[0]));
|
|
} else if (command === "reassign" && args.length >= 4) {
|
|
console.log(vault.reassignCredential(actor, args[0], {
|
|
issuer: args[1],
|
|
sub: args[2],
|
|
username: args[3],
|
|
type: "user"
|
|
}));
|
|
} else if (command === "rotate-master" && args[0]) {
|
|
if (!existsSync(args[0])) {
|
|
throw new Error(`New key file not found: ${args[0]}`);
|
|
}
|
|
console.log(vault.rotateMasterKey(actor, loadKeyFile(args[0])));
|
|
console.error("Rotation succeeded. Replace the mounted vault_master_key before restarting the service.");
|
|
} else if (command === "backup" && args[0]) {
|
|
if (existsSync(args[0])) {
|
|
throw new Error(`Refusing to overwrite existing backup: ${args[0]}`);
|
|
}
|
|
console.log(vault.backup(args[0]));
|
|
} else {
|
|
usage();
|
|
process.exitCode = 1;
|
|
}
|
|
} finally {
|
|
vault.close();
|
|
}
|