feat(ql3): expose cluster secret binding management

This commit is contained in:
whyour
2026-08-13 15:41:59 +08:00
parent 7016903fba
commit 73eeaed4de
13 changed files with 1127 additions and 48 deletions
@@ -199,6 +199,47 @@ function commands() {
operation: 'plugin-package.publisher-trust-transition.inspect',
request: inspection,
},
{
schemaVersion: 1,
operation: 'plugin-package.secret-binding.plan',
request: {
actionRef: 'secret-binding:cluster-monitor:1',
projectId: 'project-1',
packageName: 'cluster-monitor',
assignments: [{
name: 'TOKEN',
secretRef:
'qlsecret:v1:eyJwcm9qZWN0SWQiOiJwcm9qZWN0LTEiLCJuYW1lIjoicnVudGltZS10b2tlbiIsInZlcnNpb24iOjJ9',
}],
},
},
{
schemaVersion: 1,
operation: 'plugin-package.secret-binding.propose',
request: {
actionRef: 'secret-binding:cluster-monitor:1',
approvalRequestId: 'approval-secret-binding-1',
approvalAuditEventId: 'audit-secret-binding-approval-1',
},
},
{
schemaVersion: 1,
operation: 'plugin-package.secret-binding.decide',
request: {
...decision,
actionRef: 'secret-binding:cluster-monitor:1',
approvalRequestId: 'approval-secret-binding-1',
},
},
{
schemaVersion: 1,
operation: 'plugin-package.secret-binding.inspect',
request: {
actionRef: 'secret-binding:cluster-monitor:1',
approvalRequestId: 'approval-secret-binding-1',
inspectionId: 'inspection-secret-binding-1',
},
},
];
}
@@ -327,7 +368,70 @@ function lifecyclePlanSummary() {
};
}
function secretBindingPlanSummary() {
return {
actionRef: 'secret-binding:cluster-monitor:1',
projectId: 'project-1',
packageName: 'cluster-monitor',
installationId: 'install-cluster-monitor-1',
generation: 1,
generationDigest: '9'.repeat(64),
lockDigest: 'a'.repeat(64),
manifestDigest: 'b'.repeat(64),
entries: [{
name: 'TOKEN',
required: true,
secretRef:
'qlsecret:v1:eyJwcm9qZWN0SWQiOiJwcm9qZWN0LTEiLCJuYW1lIjoicnVudGltZS10b2tlbiIsInZlcnNpb24iOjJ9',
}],
plannedAtMs: 1_000,
expiresAtMs: 10_000,
planDigest: 'c'.repeat(64),
approvalPlanDigest: 'd'.repeat(64),
};
}
function successfulResult(operation) {
const secretApproval = {
...approvalSummary(),
id: 'approval-secret-binding-1',
actionDigest: 'd'.repeat(64),
previewDigest: 'c'.repeat(64),
};
if (operation === 'plugin-package.secret-binding.plan') {
return {
schemaVersion: 1,
operation,
status: 'created',
plan: secretBindingPlanSummary(),
};
}
if (operation === 'plugin-package.secret-binding.propose') {
return {
schemaVersion: 1,
operation,
approvalStatus: 'created',
plan: secretBindingPlanSummary(),
approval: secretApproval,
};
}
if (operation === 'plugin-package.secret-binding.inspect') {
return {
schemaVersion: 1,
operation,
plan: secretBindingPlanSummary(),
approval: secretApproval,
stale: false,
};
}
if (operation === 'plugin-package.secret-binding.decide') {
return {
schemaVersion: 1,
operation,
status: 'decided',
approval: secretApproval,
};
}
if (operation === 'plugin-package.installation.inspect') {
return {
schemaVersion: 1,
@@ -639,7 +743,7 @@ test('readiness probe rejects unreviewed status and bounded response drift', asy
}
});
test('permits and validates exactly the fourteen public management operations', async () => {
test('permits and validates exactly the eighteen public management operations', async () => {
const received = [];
const fixture = await startServer((request, response) => {
const chunks = [];
@@ -684,7 +788,7 @@ test('permits and validates exactly the fourteen public management operations',
return true;
},
);
assert.equal(received.length, 14);
assert.equal(received.length, 18);
} finally {
await fixture.close();
rmSync(files.directory, { recursive: true, force: true });
@@ -756,6 +860,137 @@ test('rejects installation inventory responses outside the requested project and
}
});
test('rejects Secret binding response drift before reporting success', async () => {
const command = commands().find(
({ operation }) => operation === 'plugin-package.secret-binding.propose',
);
const invalidResults = [
{
...successfulResult(command.operation),
plan: {
...secretBindingPlanSummary(),
actionRef: 'secret-binding:another-package:1',
},
},
{
...successfulResult(command.operation),
plan: {
...secretBindingPlanSummary(),
entries: [
{
...secretBindingPlanSummary().entries[0],
secretRef:
'qlsecret:v1:eyJwcm9qZWN0SWQiOiJhbm90aGVyLXByb2plY3QiLCJuYW1lIjoicnVudGltZS10b2tlbiIsInZlcnNpb24iOjJ9',
},
],
},
},
{
...successfulResult(command.operation),
plan: {
...secretBindingPlanSummary(),
entries: [
...secretBindingPlanSummary().entries,
...secretBindingPlanSummary().entries,
],
},
},
{
...successfulResult(command.operation),
approval: {
...successfulResult(command.operation).approval,
actionDigest: 'e'.repeat(64),
},
},
];
const fixture = await startServer((_request, response) => {
sendJson(response, 200, {
schemaVersion: 1,
requestId: `request-invalid-secret-binding-${invalidResults.length}`,
result: invalidResults.shift(),
});
});
const files = createClientFiles(fixture.port, command);
try {
for (let index = 0; index < 4; index += 1) {
await assert.rejects(
executeClusterPluginPackageManagementClient(files.paths),
ClusterPluginPackageManagementClientRequestError,
);
}
} finally {
await fixture.close();
rmSync(files.directory, { recursive: true, force: true });
}
});
test('binds Secret binding plan and plan-less inspection to the exact request', async () => {
const planCommand = commands().find(
({ operation }) => operation === 'plugin-package.secret-binding.plan',
);
const inspectCommand = commands().find(
({ operation }) => operation === 'plugin-package.secret-binding.inspect',
);
const invalidResponses = [
{
command: planCommand,
result: {
...successfulResult(planCommand.operation),
plan: {
...secretBindingPlanSummary(),
entries: [{
...secretBindingPlanSummary().entries[0],
secretRef:
'qlsecret:v1:eyJwcm9qZWN0SWQiOiJwcm9qZWN0LTEiLCJuYW1lIjoiYW5vdGhlci10b2tlbiIsInZlcnNpb24iOjJ9',
}],
},
},
},
{
command: inspectCommand,
result: {
...successfulResult(inspectCommand.operation),
plan: null,
approval: {
...successfulResult(inspectCommand.operation).approval,
id: 'approval-secret-binding-another',
},
},
},
{
command: inspectCommand,
result: {
...successfulResult(inspectCommand.operation),
plan: null,
approval: null,
},
},
];
const fixture = await startServer((_request, response) => {
sendJson(response, 200, {
schemaVersion: 1,
requestId: `request-exact-secret-binding-${invalidResponses.length}`,
result: invalidResponses[0].result,
});
});
try {
for (const invalid of invalidResponses) {
invalidResponses[0] = invalid;
const files = createClientFiles(fixture.port, invalid.command);
try {
await assert.rejects(
executeClusterPluginPackageManagementClient(files.paths),
ClusterPluginPackageManagementClientRequestError,
);
} finally {
rmSync(files.directory, { recursive: true, force: true });
}
}
} finally {
await fixture.close();
}
});
test('rejects non-private, symlinked, and non-exact input files before transport', async () => {
const files = createClientFiles(443);
try {
@@ -322,6 +322,94 @@ function authentication(value = principal()) {
};
}
function secretBindingPlan() {
return {
schema: 'qinglong/plugin-package-secret-binding-approval-plan@v1',
actionRef: 'secret-binding:cluster-monitor:1',
bindingPlan: {
schema: 'qinglong/plugin-package-secret-binding-plan@v1',
target: {
installationId: 'cluster-monitor-installation',
projectId: 'default',
packageName: 'cluster-monitor',
lockDigest: '1'.repeat(64),
generation: 1,
generationDigest: '2'.repeat(64),
manifestDigest: '3'.repeat(64),
},
entries: [{
name: 'TOKEN',
required: true,
secretRef:
'qlsecret:v1:eyJwcm9qZWN0SWQiOiJkZWZhdWx0IiwibmFtZSI6InJ1bnRpbWUtdG9rZW4iLCJ2ZXJzaW9uIjoyfQ',
}],
plannedAtMs: NOW - 10,
planDigest: '4'.repeat(64),
},
requestedBy: { type: 'user', id: 'cluster-reviewer' },
expiresAtMs: NOW + 10_000,
approvalPlanDigest: '5'.repeat(64),
};
}
function fakeSecretBinding() {
const calls = { plan: [], propose: [], decide: [], inspectAuthorized: [] };
return {
calls,
service: {
async plan(request) {
calls.plan.push(request);
return { status: 'created', plan: secretBindingPlan() };
},
async propose(request) {
calls.propose.push(request);
return {
plan: secretBindingPlan(),
approvalStatus: 'created',
approvalRequest: approval({
id: request.approvalRequestId,
action: {
permission: 'secret.manage',
actionType: 'plugin_package.secret_binding.bind',
actionRef: request.actionRef,
actionDigest: '5'.repeat(64),
previewDigest: '4'.repeat(64),
},
requestedBy: request.principal.subject,
}),
};
},
async decide(request) {
calls.decide.push(request);
return {
status: 'decided',
request: approval({
id: request.approvalRequestId,
version: 2,
state: request.decision,
decisionId: request.decisionId,
decision: request.decision,
decisionReasonCode: request.reasonCode,
decidedBy: request.principal.subject,
decisionAuthenticationId: request.principal.authenticationId,
decisionAssurance: request.principal.assurance,
decidedAtMs: NOW,
decisionFence: { projectVersion: 1, bindingVersion: 1 },
}),
};
},
async inspectAuthorized(request) {
calls.inspectAuthorized.push(request);
return {
plan: secretBindingPlan(),
approvalRequest: null,
stale: false,
};
},
},
};
}
function lifecyclePlan() {
return {
schema: 'qinglong/plugin-package-lifecycle-plan@v1',
@@ -818,6 +906,82 @@ test('routes lifecycle review without exposing executor mutation authority', asy
assert.deepEqual(management.calls.dispatch, []);
});
test('routes content-free Secret binding review without executor authority', async () => {
const management = fakeService();
const secretBinding = fakeSecretBinding();
const transport = createClusterPluginPackageManagementTransport({
service: management.service,
secretBinding: secretBinding.service,
now: () => NOW,
});
const planCommand = {
schemaVersion: 1,
operation: 'plugin-package.secret-binding.plan',
request: {
actionRef: secretBindingPlan().actionRef,
projectId: 'default',
packageName: 'cluster-monitor',
assignments: secretBindingPlan().bindingPlan.entries.map(
({ name, secretRef }) => ({ name, secretRef }),
),
},
};
const planned = await transport.execute(
planCommand,
authentication().authority,
);
assert.equal(planned.status, 'created');
assert.deepEqual(Object.keys(planned.plan).sort(), [
'actionRef', 'approvalPlanDigest', 'entries', 'expiresAtMs', 'generation',
'generationDigest', 'installationId', 'lockDigest', 'manifestDigest',
'packageName', 'planDigest', 'plannedAtMs', 'projectId',
]);
assert.equal(JSON.stringify(planned).includes('authenticationId'), false);
const proposed = await transport.execute({
schemaVersion: 1,
operation: 'plugin-package.secret-binding.propose',
request: {
actionRef: secretBindingPlan().actionRef,
approvalRequestId: 'approval-secret-binding-1',
approvalAuditEventId: 'audit-secret-binding-approval-1',
},
}, authentication().authority);
assert.equal(proposed.approvalStatus, 'created');
const decided = await transport.execute({
schemaVersion: 1,
operation: 'plugin-package.secret-binding.decide',
request: {
actionRef: secretBindingPlan().actionRef,
approvalRequestId: 'approval-secret-binding-1',
expectedVersion: 1,
decisionId: 'decision-secret-binding-1',
auditEventId: 'audit-secret-binding-decision-1',
decision: 'approved',
reasonCode: 'reviewed',
},
}, authentication().authority);
assert.equal(decided.status, 'decided');
const inspected = await transport.execute({
schemaVersion: 1,
operation: 'plugin-package.secret-binding.inspect',
request: {
actionRef: secretBindingPlan().actionRef,
approvalRequestId: 'approval-secret-binding-1',
inspectionId: 'inspection-secret-binding-1',
},
}, authentication().authority);
assert.equal(inspected.stale, false);
assert.equal(secretBinding.calls.plan.length, 1);
assert.equal(secretBinding.calls.propose.length, 1);
assert.equal(secretBinding.calls.decide.length, 1);
assert.equal(secretBinding.calls.inspectAuthorized.length, 1);
assert.deepEqual(management.calls.consume, []);
assert.deepEqual(management.calls.dispatch, []);
});
test('routes publisher revocation proposal with derived-only low-sensitive output', async () => {
const management = fakeService();
const publisherTrust = fakePublisherTrust();
@@ -379,3 +379,71 @@ test('rejects weak requester, self-decision and semantic actionRef replay drift'
(error) => error?.name === 'ApprovalSeparationOfDutyError',
);
});
test('consumes durable quota with stable operation identities', async () => {
const state = fixture();
const consumed = [];
const service = createClusterPluginPackageSecretBindingManagementService({
pool: state.pool,
now: () => 210,
planLifetimeMs: 1_000,
quota: {
async consume(command) {
consumed.push(command);
return { remaining: 9, resetAtMs: 2_000, observedAtMs: 210 };
},
},
});
const created = await service.plan(planRequest());
const proposed = await service.propose({
actionRef: created.plan.actionRef,
approvalRequestId: 'approval-secret-binding-quota',
approvalAuditEventId: '123e4567-e89b-42d3-a456-426614175211',
principal: REQUESTER,
});
await service.decide({
actionRef: created.plan.actionRef,
approvalRequestId: proposed.approvalRequest.id,
expectedVersion: 1,
decisionId: 'decision-secret-binding-quota',
auditEventId: '123e4567-e89b-42d3-a456-426614175212',
decision: 'approved',
reasonCode: 'reviewed',
principal: REVIEWER,
});
await service.inspectAuthorized({
actionRef: created.plan.actionRef,
approvalRequestId: proposed.approvalRequest.id,
inspectionId: 'inspection-secret-binding-quota',
principal: REVIEWER,
});
assert.deepEqual(
consumed.map(({ operation, idempotencyKey, subject }) => ({
operation,
idempotencyKey,
subject,
})),
[
{
operation: 'plugin-package.propose',
idempotencyKey: created.plan.actionRef,
subject: REQUESTER.subject,
},
{
operation: 'plugin-package.propose',
idempotencyKey: proposed.approvalRequest.id,
subject: REQUESTER.subject,
},
{
operation: 'plugin-package.decide',
idempotencyKey: 'decision-secret-binding-quota',
subject: REVIEWER.subject,
},
{
operation: 'plugin-package.inspect',
idempotencyKey: 'inspection-secret-binding-quota',
subject: REVIEWER.subject,
},
],
);
});
@@ -44,6 +44,9 @@ const {
const {
createClusterPluginPackageManagementService,
} = require('@qinglong/cluster-admin/plugin-package-management');
const {
createClusterPluginPackageManagementTransport,
} = require('@qinglong/cluster-admin/plugin-package-management-transport');
const {
createClusterPluginPackageApprovedActionDispatcher,
} = require('@qinglong/cluster-admin/plugin-package-approved-action');
@@ -363,42 +366,105 @@ if (!MIGRATION_URL || !MANAGER_URL || !EXECUTOR_URL) {
planLifetimeMs: 60_000,
approvalLifetimeMs: 60_000,
});
const planned = await secretManagement.plan({
actionRef: secretActionRef,
projectId,
packageName,
assignments: [{ name: 'TOKEN', secretRef }],
principal: principal(requesterSubject, `secret-owner-${suffix}`, now),
});
assert.equal(planned.status, 'created');
now = Math.max(now, planned.plan.bindingPlan.plannedAtMs);
const replay = await secretManagement.plan({
actionRef: secretActionRef,
projectId,
packageName,
assignments: [{ name: 'TOKEN', secretRef }],
principal: principal(requesterSubject, `secret-owner-${suffix}`, now),
const secretTransport = createClusterPluginPackageManagementTransport({
service: installManagement,
secretBinding: secretManagement,
now: () => now,
});
const requesterAuthentication = {
async authenticate() {
return principal(requesterSubject, `secret-owner-${suffix}`, now);
},
};
const plannedPublic = await secretTransport.execute(
{
schemaVersion: 1,
operation: 'plugin-package.secret-binding.plan',
request: {
actionRef: secretActionRef,
projectId,
packageName,
assignments: [{ name: 'TOKEN', secretRef }],
},
},
requesterAuthentication,
);
assert.equal(plannedPublic.status, 'created');
assert.equal(plannedPublic.plan.actionRef, secretActionRef);
assert.deepEqual(plannedPublic.plan.entries, [
{ name: 'TOKEN', required: false, secretRef },
]);
assert.equal(Object.hasOwn(plannedPublic.plan, 'authenticationId'), false);
now = Math.max(now, plannedPublic.plan.plannedAtMs);
const replay = await secretTransport.execute(
{
schemaVersion: 1,
operation: 'plugin-package.secret-binding.plan',
request: {
actionRef: secretActionRef,
projectId,
packageName,
assignments: [{ name: 'TOKEN', secretRef }],
},
},
requesterAuthentication,
);
assert.equal(replay.status, 'existing');
now += 10;
const secretProposed = await secretManagement.propose({
actionRef: secretActionRef,
approvalRequestId: secretApprovalId,
approvalAuditEventId: randomUUID(),
principal: principal(requesterSubject, `secret-owner-${suffix}`, now),
});
const secretProposed = await secretTransport.execute(
{
schemaVersion: 1,
operation: 'plugin-package.secret-binding.propose',
request: {
actionRef: secretActionRef,
approvalRequestId: secretApprovalId,
approvalAuditEventId: randomUUID(),
},
},
requesterAuthentication,
);
now += 10;
const secretDecision = await secretManagement.decide({
actionRef: secretActionRef,
approvalRequestId: secretApprovalId,
expectedVersion: secretProposed.approvalRequest.version,
decisionId: `secret-decision-${suffix}`,
auditEventId: randomUUID(),
decision: 'approved',
reasonCode: 'reviewed',
principal: principal(reviewerSubject, `secret-reviewer-${suffix}`, now),
});
const secretDecision = await secretTransport.execute(
{
schemaVersion: 1,
operation: 'plugin-package.secret-binding.decide',
request: {
actionRef: secretActionRef,
approvalRequestId: secretApprovalId,
expectedVersion: secretProposed.approval.version,
decisionId: `secret-decision-${suffix}`,
auditEventId: randomUUID(),
decision: 'approved',
reasonCode: 'reviewed',
},
},
{
async authenticate() {
return principal(
reviewerSubject,
`secret-reviewer-${suffix}`,
now,
);
},
},
);
assert.equal(secretDecision.status, 'decided');
const inspected = await secretTransport.execute(
{
schemaVersion: 1,
operation: 'plugin-package.secret-binding.inspect',
request: {
actionRef: secretActionRef,
approvalRequestId: secretApprovalId,
inspectionId: `secret-inspection-${suffix}`,
},
},
requesterAuthentication,
);
assert.deepEqual(inspected.plan, plannedPublic.plan);
assert.equal(inspected.approval?.state, 'approved');
assert.equal(inspected.approval?.decision, 'approved');
assert.equal(inspected.stale, false);
now += 10;
assert.deepEqual(
await consumeClusterPluginPackageSecretBindingApprovals({
@@ -432,7 +498,7 @@ if (!MIGRATION_URL || !MANAGER_URL || !EXECUTOR_URL) {
).inspect(pendingSecretExecution.dispatch),
{
status: 'ready',
actionDigest: planned.plan.approvalPlanDigest,
actionDigest: plannedPublic.plan.approvalPlanDigest,
},
);
id = 0;
@@ -448,12 +514,15 @@ if (!MIGRATION_URL || !MANAGER_URL || !EXECUTOR_URL) {
assert.equal(secretDispatch.succeeded, 1);
const bindings = new PostgresPluginPackageSecretBindingRepository(executor.pool);
const binding = await bindings.find(
planned.plan.bindingPlan.target.generationDigest,
plannedPublic.plan.generationDigest,
);
assert.ok(binding);
assert.equal(binding.authority.kind, 'approved-action-execution');
assert.equal(binding.authority.evidenceDigest, planned.plan.approvalPlanDigest);
assert.deepEqual(binding.entries, planned.plan.bindingPlan.entries);
assert.equal(
binding.authority.evidenceDigest,
plannedPublic.plan.approvalPlanDigest,
);
assert.deepEqual(binding.entries, plannedPublic.plan.entries);
assert.doesNotMatch(JSON.stringify(binding), /secret-value/);
assert.equal((await secretDispatcher.dispatchBatch({ limit: 4 })).scanned, 0);
await assert.rejects(