75 lines
2.5 KiB
JavaScript
75 lines
2.5 KiB
JavaScript
"use strict";
|
|
|
|
const crypto = require("node:crypto");
|
|
const fs = require("node:fs");
|
|
const path = require("node:path");
|
|
|
|
class GhdrAuthorizer {
|
|
constructor({
|
|
privateKeyFile = "/var/lib/guanghu/lake-lamp-authz/ghdr-authorizer-private.pem",
|
|
now = () => Math.floor(Date.now() / 1000),
|
|
} = {}) {
|
|
this.privateKeyFile = privateKeyFile;
|
|
this.now = now;
|
|
this.privateKey = this.loadOrCreate();
|
|
this.publicKey = crypto.createPublicKey(this.privateKey);
|
|
}
|
|
|
|
loadOrCreate() {
|
|
if (fs.existsSync(this.privateKeyFile)) {
|
|
const metadata = fs.lstatSync(this.privateKeyFile);
|
|
if (!metadata.isFile() || metadata.isSymbolicLink() || metadata.mode & 0o077) {
|
|
throw new Error("ghdr_authorizer_private_key_permissions_invalid");
|
|
}
|
|
return crypto.createPrivateKey(fs.readFileSync(this.privateKeyFile));
|
|
}
|
|
fs.mkdirSync(path.dirname(this.privateKeyFile), { recursive: true, mode: 0o700 });
|
|
const { privateKey } = crypto.generateKeyPairSync("ed25519");
|
|
const pem = privateKey.export({ type: "pkcs8", format: "pem" });
|
|
const temporary = `${this.privateKeyFile}.${process.pid}.tmp`;
|
|
fs.writeFileSync(temporary, pem, { mode: 0o600, flag: "wx" });
|
|
fs.renameSync(temporary, this.privateKeyFile);
|
|
return privateKey;
|
|
}
|
|
|
|
publicBinding() {
|
|
const publicPem = this.publicKey.export({ type: "spki", format: "pem" });
|
|
const fingerprint = crypto.createHash("sha256").update(publicPem).digest("hex");
|
|
return {
|
|
schema: "guanghu.ghdr-authorizer-public-binding/v1",
|
|
authorizer_id: "JD-FD-PRIMARY-LAKE-LAMP",
|
|
algorithm: "Ed25519",
|
|
public_key_pem: publicPem,
|
|
public_key_sha256: fingerprint,
|
|
};
|
|
}
|
|
|
|
issue({
|
|
controllerNodeId,
|
|
targetNodeId,
|
|
layoutPayloadSha256,
|
|
resource,
|
|
workorderId,
|
|
}) {
|
|
const issued = this.now();
|
|
const capability = {
|
|
schema: "guanghu.ghdr-signing-capability/v1",
|
|
authorizer_id: "JD-FD-PRIMARY-LAKE-LAMP",
|
|
controller_node_id: controllerNodeId,
|
|
target_node_id: targetNodeId,
|
|
layout_payload_sha256: layoutPayloadSha256,
|
|
resource,
|
|
workorder_id: workorderId,
|
|
issued_at_unix: issued,
|
|
expires_at_unix: issued + 120,
|
|
nonce: crypto.randomBytes(24).toString("base64url"),
|
|
};
|
|
const canonical = Buffer.from(JSON.stringify(capability));
|
|
return {
|
|
capability,
|
|
capability_signature_base64url: crypto.sign(null, canonical, this.privateKey).toString("base64url"),
|
|
};
|
|
}
|
|
}
|
|
|
|
module.exports = { GhdrAuthorizer };
|