Add read-only hostd ops for update auditing (Sam & Claude)
Closes the third leg of today's update-question arc. Sysadmin (and any tenant agent diagnosing its own jail) can now answer "any updates available?" without root by calling these hostd ops directly: - pkg-audit (with optional jail param) — runs `pkg audit -F` to fetch the vulnerability database and report CVEs against installed packages. Jail-aware via bastille cmd, mirroring the service-status pattern. - freebsd-update-status — runs `freebsd-update updatesready`. Exits 0 (none ready) or 2 (ready); no fetch, no install. The actual fetch + install lives in the daily 06:50 cron from system-update-cron. - freebsd-version — runs `freebsd-version -kru` for kernel/running/ userland visibility. Useful for the reboot-pending check that system-update already does internally. Authorization: all three are unconditionally allowed in hostd-authorization.ts alongside ping / bastille-list / pkg-version — they're diagnostic reads, never mutate state, safe for tenant-agents. Per docs/internal/SUDO_REPLACEMENT.md, this is the right shape: extend hostd's read-only surface rather than reach for mac_do/sudo replacement. --- 2259 tests passing locally (+5 covering host-default dispatch, jail dispatch, freebsd-update-status, freebsd-version, plus tenant-readable auth). Pre-existing argon2/controlplane-*/cms.test failures unchanged. --- Build: FAIL | Tests: FAIL — 16 failed --- Build: FAIL | Tests: FAIL — 16 failed
This commit is contained in:
parent
26336acaaf
commit
16bea08721
4 changed files with 77 additions and 0 deletions
|
|
@ -75,6 +75,24 @@ describe('authorizeHostdOperation', () => {
|
|||
).toEqual({ allowed: true, owner: 'shared-platform' });
|
||||
});
|
||||
|
||||
it('allows read-only update audits for tenant agents', () => {
|
||||
// pkg-audit, freebsd-update-status, freebsd-version are diagnostic reads —
|
||||
// tenant agents should be able to call them without operator approval.
|
||||
for (const op of [
|
||||
'pkg-audit',
|
||||
'freebsd-update-status',
|
||||
'freebsd-version',
|
||||
] as const) {
|
||||
expect(
|
||||
authorizeHostdOperation(
|
||||
op,
|
||||
{},
|
||||
{ tenantId: 'alpha', caller: 'tenant-agent', registry },
|
||||
),
|
||||
).toEqual({ allowed: true });
|
||||
}
|
||||
});
|
||||
|
||||
it('blocks shared jails for tenant agents', () => {
|
||||
expect(
|
||||
authorizeHostdOperation(
|
||||
|
|
|
|||
|
|
@ -81,6 +81,9 @@ export function authorizeHostdOperation(
|
|||
case 'ping':
|
||||
case 'bastille-list':
|
||||
case 'pkg-version':
|
||||
case 'pkg-audit':
|
||||
case 'freebsd-update-status':
|
||||
case 'freebsd-version':
|
||||
return { allowed: true };
|
||||
|
||||
case 'service-start':
|
||||
|
|
|
|||
|
|
@ -466,6 +466,42 @@ describe('handleOp — spawnSync args (RC services)', () => {
|
|||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
|
||||
it('pkg-audit runs pkg audit -F on the host by default', () => {
|
||||
handleOp('pkg-audit', {});
|
||||
expect(mockSpawnSync).toHaveBeenCalledWith(
|
||||
'pkg',
|
||||
['audit', '-F'],
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
|
||||
it('pkg-audit dispatches via bastille cmd when jail is provided', () => {
|
||||
handleOp('pkg-audit', { jail: 'cms' });
|
||||
expect(mockSpawnSync).toHaveBeenCalledWith(
|
||||
'bastille',
|
||||
['cmd', 'cms', 'pkg', 'audit', '-F'],
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
|
||||
it('freebsd-update-status uses updatesready (no fetch, no install)', () => {
|
||||
handleOp('freebsd-update-status', {});
|
||||
expect(mockSpawnSync).toHaveBeenCalledWith(
|
||||
'freebsd-update',
|
||||
['updatesready'],
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
|
||||
it('freebsd-version reports kernel + running + userland', () => {
|
||||
handleOp('freebsd-version', {});
|
||||
expect(mockSpawnSync).toHaveBeenCalledWith(
|
||||
'freebsd-version',
|
||||
['-kru'],
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('handleOp — spawnSync args (packages + sysrc + sanoid)', () => {
|
||||
|
|
|
|||
|
|
@ -374,6 +374,26 @@ const OPS: Record<string, OpEntry> = {
|
|||
handler: () => exec('pkg', ['version', '-vRUL=']),
|
||||
},
|
||||
|
||||
// ── Read-only update audits ──────────────────────────────────────────────
|
||||
// These let sysadmin (and any tenant agent diagnosing its own jail) answer
|
||||
// "any updates available?" without root. None of them mutate state.
|
||||
'pkg-audit': {
|
||||
schema: z.object({ jail: jailName.optional() }),
|
||||
handler: (p) => p.jail
|
||||
? exec('bastille', ['cmd', String(p.jail), 'pkg', 'audit', '-F'])
|
||||
: exec('pkg', ['audit', '-F']),
|
||||
},
|
||||
'freebsd-update-status': {
|
||||
// updatesready exits 0 (none ready) or 2 (ready) — no fetching, no
|
||||
// installing. The daily cron at 06:50 handles actual fetch+install.
|
||||
schema: z.object({}),
|
||||
handler: () => exec('freebsd-update', ['updatesready']),
|
||||
},
|
||||
'freebsd-version': {
|
||||
schema: z.object({}),
|
||||
handler: () => exec('freebsd-version', ['-kru']),
|
||||
},
|
||||
|
||||
'pkg-upgrade': {
|
||||
schema: z.object({ repo: pkgRepo, dryRun: z.boolean().optional() }),
|
||||
handler: (p) => {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue