hostctl 0.1.36 → 0.1.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/hostctl.js +1 -1
- package/dist/bin/hostctl.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +66 -47
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1282,9 +1282,11 @@ declare namespace k3s {
|
|
|
1282
1282
|
interface CopyIdParams {
|
|
1283
1283
|
public_key: string;
|
|
1284
1284
|
user?: string;
|
|
1285
|
+
sudo?: boolean;
|
|
1285
1286
|
}
|
|
1286
1287
|
interface CopyIdResult {
|
|
1287
1288
|
success: boolean;
|
|
1289
|
+
changed: boolean;
|
|
1288
1290
|
}
|
|
1289
1291
|
declare const _default$w: TaskFn<CopyIdParams, CopyIdResult>;
|
|
1290
1292
|
|
|
@@ -1309,6 +1311,8 @@ interface GrantNopasswdParams {
|
|
|
1309
1311
|
interface GrantNopasswdResult {
|
|
1310
1312
|
/** True if the operation was successful. */
|
|
1311
1313
|
success: boolean;
|
|
1314
|
+
/** True if any state was changed. */
|
|
1315
|
+
changed: boolean;
|
|
1312
1316
|
/** The path to the created sudoers file. */
|
|
1313
1317
|
filePath: string;
|
|
1314
1318
|
}
|
package/dist/index.js
CHANGED
|
@@ -2552,7 +2552,7 @@ import process3 from "process";
|
|
|
2552
2552
|
import * as cmdr from "commander";
|
|
2553
2553
|
|
|
2554
2554
|
// src/version.ts
|
|
2555
|
-
var version = "0.1.
|
|
2555
|
+
var version = "0.1.37";
|
|
2556
2556
|
|
|
2557
2557
|
// src/cli.ts
|
|
2558
2558
|
import JSON5 from "json5";
|
|
@@ -5402,8 +5402,10 @@ async function runFn2(context) {
|
|
|
5402
5402
|
}
|
|
5403
5403
|
const sudoPrefix = sudo ? ["sudo"] : [];
|
|
5404
5404
|
if (append !== void 0) {
|
|
5405
|
-
const
|
|
5406
|
-
const
|
|
5405
|
+
const escapedContent = append.replace(/'/g, "'\\''");
|
|
5406
|
+
const command = `echo '${escapedContent}' >> "${file}"`;
|
|
5407
|
+
info(`Appending with command: ${command}`);
|
|
5408
|
+
const { success } = await exec([...sudoPrefix, "sh", "-c", command], { sudo });
|
|
5407
5409
|
return { changed: true, success };
|
|
5408
5410
|
}
|
|
5409
5411
|
if (block !== void 0) {
|
|
@@ -5498,11 +5500,12 @@ ${marker_end}`;
|
|
|
5498
5500
|
}
|
|
5499
5501
|
return { changed: true, success: result.success };
|
|
5500
5502
|
} else {
|
|
5501
|
-
const
|
|
5502
|
-
|
|
5503
|
-
|
|
5503
|
+
const escapedLine = line.replace(/'/g, "'\\''");
|
|
5504
|
+
const command = `echo '${escapedLine}' >> "${file}"`;
|
|
5505
|
+
info(`Appending with command: ${command}`);
|
|
5506
|
+
const result = await exec([...sudoPrefix, "sh", "-c", command], { sudo });
|
|
5504
5507
|
if (!result.success) {
|
|
5505
|
-
info(`
|
|
5508
|
+
info(`Append command failed. stderr: ${result.stderr}`);
|
|
5506
5509
|
}
|
|
5507
5510
|
return { changed: true, success: result.success };
|
|
5508
5511
|
}
|
|
@@ -6398,44 +6401,45 @@ var get_username_default = task(run45, { name: "core.user.get_username", descrip
|
|
|
6398
6401
|
|
|
6399
6402
|
// src/core/ssh/copy_id.ts
|
|
6400
6403
|
async function run46(context) {
|
|
6401
|
-
const { params, run: runTask, exec, log, info } = context;
|
|
6402
|
-
let { public_key, user } = params;
|
|
6404
|
+
const { params, run: runTask, exec, log, info, error } = context;
|
|
6405
|
+
let { public_key, user, sudo } = params;
|
|
6403
6406
|
const publicKeyTrimmed = public_key.trim();
|
|
6407
|
+
const sudoPrefix = sudo ? ["sudo"] : [];
|
|
6404
6408
|
if (!user) {
|
|
6405
6409
|
const usernameResult = await runTask(get_username_default());
|
|
6406
6410
|
if (usernameResult instanceof Error) {
|
|
6407
6411
|
info(`Failed to get current username: ${usernameResult.message}`);
|
|
6408
|
-
return { success: false };
|
|
6412
|
+
return { success: false, changed: false };
|
|
6409
6413
|
}
|
|
6410
6414
|
if (typeof usernameResult?.username !== "string") {
|
|
6411
6415
|
info("Could not determine current username from task result.");
|
|
6412
|
-
return { success: false };
|
|
6416
|
+
return { success: false, changed: false };
|
|
6413
6417
|
}
|
|
6414
6418
|
user = usernameResult.username;
|
|
6415
6419
|
}
|
|
6416
6420
|
if (!user) {
|
|
6417
6421
|
info("User for SSH key copy is undefined.");
|
|
6418
|
-
return { success: false };
|
|
6422
|
+
return { success: false, changed: false };
|
|
6419
6423
|
}
|
|
6420
6424
|
const userExistsResult = await runTask(exists_default4({ user }));
|
|
6421
6425
|
if (userExistsResult instanceof Error) {
|
|
6422
6426
|
info(`Error checking if user '${user}' exists: ${userExistsResult.message}`);
|
|
6423
|
-
return { success: false };
|
|
6427
|
+
return { success: false, changed: false };
|
|
6424
6428
|
}
|
|
6425
6429
|
if (!userExistsResult?.exists) {
|
|
6426
6430
|
const hostnameResult = await runTask(hostname_default());
|
|
6427
6431
|
const hostname = hostnameResult instanceof Error || !hostnameResult?.hostname ? "current host" : hostnameResult.hostname;
|
|
6428
6432
|
info(`User '${user}' does not exist on ${hostname}. Cannot copy SSH ID.`);
|
|
6429
|
-
return { success: false };
|
|
6433
|
+
return { success: false, changed: false };
|
|
6430
6434
|
}
|
|
6431
6435
|
const homeDirResult = await runTask(home_dir_default({ user }));
|
|
6432
6436
|
if (homeDirResult instanceof Error) {
|
|
6433
6437
|
info(`Error getting home directory for user '${user}': ${homeDirResult.message}`);
|
|
6434
|
-
return { success: false };
|
|
6438
|
+
return { success: false, changed: false };
|
|
6435
6439
|
}
|
|
6436
6440
|
if (typeof homeDirResult?.path !== "string") {
|
|
6437
6441
|
info(`Could not determine home directory path for user '${user}'.`);
|
|
6438
|
-
return { success: false };
|
|
6442
|
+
return { success: false, changed: false };
|
|
6439
6443
|
}
|
|
6440
6444
|
const userHome = homeDirResult.path;
|
|
6441
6445
|
const sshDir = path3.join(userHome, ".ssh");
|
|
@@ -6444,7 +6448,7 @@ async function run46(context) {
|
|
|
6444
6448
|
info(
|
|
6445
6449
|
`Failed to create or set permissions for ${sshDir}: ${dirCreateResult instanceof Error ? dirCreateResult.message : "Task reported failure"}`
|
|
6446
6450
|
);
|
|
6447
|
-
return { success: false };
|
|
6451
|
+
return { success: false, changed: false };
|
|
6448
6452
|
}
|
|
6449
6453
|
const authorizedKeysFile = path3.join(sshDir, "authorized_keys");
|
|
6450
6454
|
const touchResult = await runTask(touch_default({ file: authorizedKeysFile, mode: "600", owner: user }));
|
|
@@ -6452,30 +6456,28 @@ async function run46(context) {
|
|
|
6452
6456
|
info(
|
|
6453
6457
|
`Failed to touch or set permissions for ${authorizedKeysFile}: ${touchResult instanceof Error ? touchResult.message : "Task reported failure"}`
|
|
6454
6458
|
);
|
|
6455
|
-
return { success: false };
|
|
6459
|
+
return { success: false, changed: false };
|
|
6456
6460
|
}
|
|
6457
6461
|
const checkKeyCommandParts = [];
|
|
6458
6462
|
checkKeyCommandParts.push("sudo", "-u", user, "grep", "-xqF", publicKeyTrimmed, authorizedKeysFile);
|
|
6459
6463
|
const checkKeyCmdResult = await exec(checkKeyCommandParts);
|
|
6460
6464
|
if (checkKeyCmdResult.exitCode === 0) {
|
|
6461
6465
|
info(`SSH key already exists in ${authorizedKeysFile} for user ${user}.`);
|
|
6462
|
-
return { success: true };
|
|
6466
|
+
return { success: true, changed: false };
|
|
6463
6467
|
} else if (checkKeyCmdResult.exitCode !== 1) {
|
|
6464
6468
|
info(`Error checking for existing SSH key: ${checkKeyCmdResult.stderr || checkKeyCmdResult.stdout}`);
|
|
6465
|
-
return { success: false };
|
|
6469
|
+
return { success: false, changed: false };
|
|
6466
6470
|
}
|
|
6467
|
-
const sudoCmd = `sudo -u ${user}`;
|
|
6468
6471
|
const escapedPublicKey = publicKeyTrimmed.replace(/"/g, '\\"').replace(/\$/g, "\\$").replace(/`/g, "\\`");
|
|
6469
|
-
const
|
|
6470
|
-
|
|
6471
|
-
const addKeyResult = await exec(["sh", "-c", shellCommand]);
|
|
6472
|
+
const command = `echo "${escapedPublicKey}" >> "${authorizedKeysFile}"`;
|
|
6473
|
+
const addKeyResult = await exec([...sudoPrefix, "sh", "-c", command], { sudo });
|
|
6472
6474
|
if (!addKeyResult.success) {
|
|
6473
|
-
|
|
6474
|
-
|
|
6475
|
-
);
|
|
6475
|
+
error(`Failed to append public key to ${authorizedKeysFile}: ${addKeyResult.stderr}`);
|
|
6476
|
+
return { success: false, changed: false };
|
|
6476
6477
|
}
|
|
6477
6478
|
return {
|
|
6478
|
-
success:
|
|
6479
|
+
success: true,
|
|
6480
|
+
changed: true
|
|
6479
6481
|
};
|
|
6480
6482
|
}
|
|
6481
6483
|
var copy_id_default = task(run46, { name: "core.ssh.copy_id", description: "Ssh copy id." });
|
|
@@ -6500,41 +6502,58 @@ var check_default = task(run47, { name: "core.sudoers.check", description: "Sudo
|
|
|
6500
6502
|
|
|
6501
6503
|
// src/core/sudoers/grant-nopasswd.ts
|
|
6502
6504
|
async function run48(context) {
|
|
6503
|
-
const { params, exec, log, error } = context;
|
|
6505
|
+
const { params, exec, log, error, info } = context;
|
|
6504
6506
|
const { user } = params;
|
|
6505
6507
|
if (!user) {
|
|
6506
6508
|
throw new Error("The 'user' parameter is required.");
|
|
6507
6509
|
}
|
|
6508
6510
|
const sudoersFileName = params.name || user;
|
|
6509
6511
|
const sudoersFilePath = `/etc/sudoers.d/${sudoersFileName}`;
|
|
6510
|
-
const sudoersContent = `${user} ALL=(ALL) NOPASSWD: ALL
|
|
6512
|
+
const sudoersContent = `${user} ALL=(ALL) NOPASSWD: ALL`;
|
|
6511
6513
|
try {
|
|
6512
|
-
const
|
|
6513
|
-
|
|
6514
|
-
|
|
6515
|
-
|
|
6516
|
-
|
|
6517
|
-
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
}
|
|
6522
|
-
const
|
|
6523
|
-
|
|
6524
|
-
|
|
6525
|
-
|
|
6514
|
+
const editResult = await context.run(
|
|
6515
|
+
core_default.file.edit({
|
|
6516
|
+
file: sudoersFilePath,
|
|
6517
|
+
line: sudoersContent,
|
|
6518
|
+
sudo: true
|
|
6519
|
+
})
|
|
6520
|
+
);
|
|
6521
|
+
if (editResult instanceof Error || !editResult.success) {
|
|
6522
|
+
throw new Error(`Failed to edit sudoers file at ${sudoersFilePath}`);
|
|
6523
|
+
}
|
|
6524
|
+
const chmodResult = await context.run(
|
|
6525
|
+
core_default.file.chmod({
|
|
6526
|
+
path: sudoersFilePath,
|
|
6527
|
+
mode: "0440",
|
|
6528
|
+
sudo: true
|
|
6529
|
+
})
|
|
6530
|
+
);
|
|
6531
|
+
if (chmodResult instanceof Error || !chmodResult.success) {
|
|
6532
|
+
throw new Error(`Failed to set permissions on ${sudoersFilePath}`);
|
|
6533
|
+
}
|
|
6534
|
+
const chownResult = await context.run(
|
|
6535
|
+
core_default.file.chown({
|
|
6536
|
+
path: sudoersFilePath,
|
|
6537
|
+
owner: "root",
|
|
6538
|
+
group: "root",
|
|
6539
|
+
sudo: true
|
|
6540
|
+
})
|
|
6541
|
+
);
|
|
6542
|
+
if (chownResult instanceof Error || !chownResult.success) {
|
|
6543
|
+
throw new Error(`Failed to set ownership on ${sudoersFilePath}`);
|
|
6526
6544
|
}
|
|
6527
6545
|
const checkCmd = await exec(["sudo", "visudo", "-cf", sudoersFilePath]);
|
|
6528
6546
|
if (!checkCmd.success) {
|
|
6529
6547
|
error(`Syntax check failed for ${sudoersFilePath}: ${checkCmd.stderr}`);
|
|
6530
6548
|
await exec(["sudo", "rm", "-f", sudoersFilePath]);
|
|
6531
|
-
return { success: false, filePath: sudoersFilePath };
|
|
6549
|
+
return { success: false, changed: false, filePath: sudoersFilePath };
|
|
6532
6550
|
}
|
|
6551
|
+
const changed = editResult.changed;
|
|
6533
6552
|
log(Verbosity.INFO, `Successfully granted passwordless sudo to user '${user}' via ${sudoersFilePath}`);
|
|
6534
|
-
return { success: true, filePath: sudoersFilePath };
|
|
6553
|
+
return { success: true, changed, filePath: sudoersFilePath };
|
|
6535
6554
|
} catch (e) {
|
|
6536
6555
|
error(`An unexpected error occurred: ${e.message}`);
|
|
6537
|
-
|
|
6556
|
+
return { success: false, changed: false, filePath: "" };
|
|
6538
6557
|
}
|
|
6539
6558
|
}
|
|
6540
6559
|
var grant_nopasswd_default = task(run48, {
|