feat(authz): add email-authorized GHDR dual signing
This commit is contained in:
parent
84f7c4c198
commit
bec7a3d7a0
10 changed files with 771 additions and 5 deletions
116
server-tools/lake-lamp-authz/ghdr-controller-broker.test.js
Normal file
116
server-tools/lake-lamp-authz/ghdr-controller-broker.test.js
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
"use strict";
|
||||
|
||||
const assert = require("node:assert/strict");
|
||||
const crypto = require("node:crypto");
|
||||
const fs = require("node:fs");
|
||||
const os = require("node:os");
|
||||
const path = require("node:path");
|
||||
const test = require("node:test");
|
||||
const { GhdrAuthorizer } = require("./ghdr-authorizer");
|
||||
const { GhdrControllerBroker } = require("./ghdr-controller-broker");
|
||||
|
||||
test("two outbound controllers poll one email-authorized job and return independent layout signatures", async () => {
|
||||
const directory = fs.mkdtempSync(path.join(os.tmpdir(), "ghdr-broker-"));
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const nodes = [
|
||||
makeController("GH-CTRL-GZ-01", "tencent/ap-guangzhou/BS-GZ-006"),
|
||||
makeController("GH-CTRL-SG-01", "tencent/ap-singapore/ZY-SG-006"),
|
||||
];
|
||||
const authorizer = new GhdrAuthorizer({
|
||||
privateKeyFile: path.join(directory, "authorizer.pem"),
|
||||
now: () => now,
|
||||
});
|
||||
const broker = new GhdrControllerBroker({
|
||||
controllers: nodes.map(item => item.binding),
|
||||
stateDir: path.join(directory, "jobs"),
|
||||
authorizer,
|
||||
now: () => now,
|
||||
waitMs: 2000,
|
||||
});
|
||||
const plan = {
|
||||
schema: "guanghu.ghdr-signed-layout-plan/v1",
|
||||
payload: {
|
||||
node_id: "GH-CVM-MAIN-PROD-01",
|
||||
generation: 1,
|
||||
},
|
||||
signatures: [],
|
||||
};
|
||||
const digest = crypto.createHash("sha256").update(JSON.stringify(plan.payload)).digest("hex");
|
||||
const binding = {
|
||||
payload_sha256: digest,
|
||||
resource: `GH-CVM-MAIN-PROD-01:${digest}:1`,
|
||||
};
|
||||
try {
|
||||
const pending = broker.queueAndWait({
|
||||
plan,
|
||||
binding,
|
||||
workorderId: "00000000-0000-4000-8000-000000000001",
|
||||
});
|
||||
const submitted = [];
|
||||
for (const node of nodes) {
|
||||
const poll = {
|
||||
schema: "guanghu.ghdr-controller-poll/v1",
|
||||
node_id: node.binding.node_id,
|
||||
issued_at_unix: now,
|
||||
nonce: crypto.randomBytes(24).toString("base64url"),
|
||||
};
|
||||
const body = {
|
||||
request: poll,
|
||||
request_signature_hex: crypto.sign(null, Buffer.from(JSON.stringify(poll)), node.transport.privateKey).toString("hex"),
|
||||
};
|
||||
const job = broker.poll(body).job;
|
||||
assert.ok(job);
|
||||
assert.equal(broker.poll(body).error, "ghdr_controller_auth_replayed");
|
||||
const layoutSignatureHex = crypto.sign(
|
||||
null,
|
||||
Buffer.from(JSON.stringify(plan.payload)),
|
||||
node.layout.privateKey,
|
||||
).toString("hex");
|
||||
const result = {
|
||||
schema: "guanghu.ghdr-controller-result/v1",
|
||||
node_id: node.binding.node_id,
|
||||
job_id: job.job_id,
|
||||
layout_payload_sha256: digest,
|
||||
signature_hex: layoutSignatureHex,
|
||||
issued_at_unix: now,
|
||||
nonce: crypto.randomBytes(24).toString("base64url"),
|
||||
};
|
||||
const signature = {
|
||||
node_id: node.binding.node_id,
|
||||
failure_domain: node.binding.failure_domain,
|
||||
public_key_hex: node.binding.layout_public_key_hex,
|
||||
signature_hex: layoutSignatureHex,
|
||||
};
|
||||
assert.equal(broker.submit({
|
||||
request: result,
|
||||
request_signature_hex: crypto.sign(null, Buffer.from(JSON.stringify(result)), node.transport.privateKey).toString("hex"),
|
||||
signature,
|
||||
}).ok, true);
|
||||
submitted.push(signature);
|
||||
}
|
||||
const completed = await pending;
|
||||
assert.equal(completed.ok, true);
|
||||
assert.deepEqual(completed.signatures, submitted);
|
||||
} finally {
|
||||
fs.rmSync(directory, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
function makeController(nodeId, failureDomain) {
|
||||
const layout = crypto.generateKeyPairSync("ed25519");
|
||||
const transport = crypto.generateKeyPairSync("ed25519");
|
||||
return {
|
||||
layout,
|
||||
transport,
|
||||
binding: {
|
||||
node_id: nodeId,
|
||||
failure_domain: failureDomain,
|
||||
layout_public_key_hex: rawPublic(layout.publicKey),
|
||||
transport_public_key_hex: rawPublic(transport.publicKey),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function rawPublic(key) {
|
||||
return key.export({ type: "spki", format: "der" }).subarray(-32).toString("hex");
|
||||
}
|
||||
Loading…
Reference in a new issue