fix: fleets, proxmox and automations not getting the host sudo password

This commit is contained in:
LukeGus committed 2026-10-03 15:31:05 -05:00
1 parent 6f442c5863
commit 5e31642379
8 files changed
+65 -5

No files matched your search

+1
View File
@@ -17,6 +17,7 @@
"capabilities": [
"hosts:read",
"credentials:use",
"credentials:read",
"ssh:connect",
"db:own",
"events:core",
@@ -0,0 +1,17 @@
import { describe, expect, it } from "vitest";
import fs from "node:fs";
import path from "node:path";
// Elevated commands read the host's sudo password, which core only hands to
// a plugin holding credentials:read.
describe("sudo password access", () => {
it("declares credentials:read", () => {
const manifest = JSON.parse(
fs.readFileSync(
path.resolve(import.meta.dirname, "../../manifest.json"),
"utf8",
),
);
expect(manifest.capabilities).toContain("credentials:read");
});
});
+1
View File
@@ -18,6 +18,7 @@
"hosts:read",
"hosts:write",
"credentials:use",
"credentials:read",
"ssh:connect",
"db:own",
"network:serve",
+4 -4
View File
@@ -1357,9 +1357,9 @@ export function registerFleetRoutes(
fleetId,
"manage",
async (host) => {
// Package actions need the host's sudo password, an operational
// secret ctx.hosts.get() leaves out. ctx.ssh.connect resolves the
// full host the connect pipeline uses, which carries it.
// Package actions need the host's sudo password. connect() hands
// back a redacted host, so read it through resolveHost.
const full = await ctx.ssh.resolveHost(host.id);
const connection = await ctx.ssh.connect(host.id, {
purpose: "fleet",
});
@@ -1388,7 +1388,7 @@ export function registerFleetRoutes(
const result = await execElevated(
sshClient,
cmd,
connection.host.sudoPassword as string | undefined,
full?.sudoPassword as string | undefined,
{ forceSudo: true, timeoutMs: 600000 },
);
return {
@@ -0,0 +1,17 @@
import { describe, expect, it } from "vitest";
import fs from "node:fs";
import path from "node:path";
// Elevated commands read the host's sudo password, which core only hands to
// a plugin holding credentials:read.
describe("sudo password access", () => {
it("declares credentials:read", () => {
const manifest = JSON.parse(
fs.readFileSync(
path.resolve(import.meta.dirname, "../../manifest.json"),
"utf8",
),
);
expect(manifest.capabilities).toContain("credentials:read");
});
});
+1
View File
@@ -18,6 +18,7 @@
"hosts:read",
"hosts:write",
"credentials:use",
"credentials:read",
"ssh:connect",
"db:own",
"network:serve",
+7 -1
View File
@@ -275,7 +275,13 @@ async function discoverProxmoxGuestsForHost(
timeoutMs: 35000,
overrides: { tryKeyboard: false, readyTimeout: 30000 },
});
const hostWithSudo: PluginSshHostWithSudo = host;
// connect() always hands back a redacted host; the sudo password comes
// from resolveHost, which needs credentials:read.
const full = await ctx.ssh.resolveHost(parsedHostId);
const hostWithSudo: PluginSshHostWithSudo = {
...host,
sudoPassword: (full?.sudoPassword as string | undefined) ?? undefined,
};
const hostCredentialId = (hostWithSudo.credentialId as number | null) ?? null;
try {
@@ -0,0 +1,17 @@
import { describe, expect, it } from "vitest";
import fs from "node:fs";
import path from "node:path";
// Elevated commands read the host's sudo password, which core only hands to
// a plugin holding credentials:read.
describe("sudo password access", () => {
it("declares credentials:read", () => {
const manifest = JSON.parse(
fs.readFileSync(
path.resolve(import.meta.dirname, "../../manifest.json"),
"utf8",
),
);
expect(manifest.capabilities).toContain("credentials:read");
});
});