@geekbeer/minion 2.56.1 → 2.58.1
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/core/routes/sudoers.js +70 -0
- package/linux/minion-cli.sh +5 -0
- package/linux/server.js +3 -0
- package/package.json +1 -1
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sudoers endpoint
|
|
3
|
+
*
|
|
4
|
+
* Endpoints:
|
|
5
|
+
* - GET /api/sudoers - List sudo permissions for the current user
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { exec } = require('child_process')
|
|
9
|
+
const { promisify } = require('util')
|
|
10
|
+
const execAsync = promisify(exec)
|
|
11
|
+
|
|
12
|
+
const { verifyToken } = require('../lib/auth')
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Parse `sudo -l` output into structured permission entries
|
|
16
|
+
* @param {string} output - Raw output from `sudo -l`
|
|
17
|
+
* @returns {string[]} List of allowed commands
|
|
18
|
+
*/
|
|
19
|
+
function parseSudoList(output) {
|
|
20
|
+
const lines = output.split('\n')
|
|
21
|
+
const commands = []
|
|
22
|
+
|
|
23
|
+
for (const line of lines) {
|
|
24
|
+
const trimmed = line.trim()
|
|
25
|
+
// Lines starting with (root) contain permission entries
|
|
26
|
+
if (trimmed.startsWith('(root)') || trimmed.startsWith('(ALL)')) {
|
|
27
|
+
// Extract the command part after NOPASSWD: or the run-as spec
|
|
28
|
+
const match = trimmed.match(/(?:NOPASSWD:\s*)(.+)$/)
|
|
29
|
+
if (match) {
|
|
30
|
+
commands.push(match[1].trim())
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return commands
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Register sudoers routes as Fastify plugin
|
|
40
|
+
* @param {import('fastify').FastifyInstance} fastify
|
|
41
|
+
*/
|
|
42
|
+
async function sudoersRoutes(fastify) {
|
|
43
|
+
fastify.get('/api/sudoers', async (request, reply) => {
|
|
44
|
+
if (!verifyToken(request)) {
|
|
45
|
+
reply.code(401)
|
|
46
|
+
return { success: false, error: 'Unauthorized' }
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
const { stdout } = await execAsync('sudo -n -l 2>/dev/null', { timeout: 5000 })
|
|
51
|
+
const commands = parseSudoList(stdout)
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
success: true,
|
|
55
|
+
commands,
|
|
56
|
+
raw: stdout.trim(),
|
|
57
|
+
}
|
|
58
|
+
} catch (error) {
|
|
59
|
+
// sudo -l may fail if sudo is not configured at all
|
|
60
|
+
return {
|
|
61
|
+
success: true,
|
|
62
|
+
commands: [],
|
|
63
|
+
raw: '',
|
|
64
|
+
note: 'sudo is not configured or no permissions granted',
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
module.exports = { sudoersRoutes }
|
package/linux/minion-cli.sh
CHANGED
|
@@ -323,7 +323,12 @@ do_setup() {
|
|
|
323
323
|
echo "${TARGET_USER} ALL=(root) NOPASSWD: ${NPM_BIN} install -g @geekbeer/minion@latest"
|
|
324
324
|
echo "${TARGET_USER} ALL=(root) NOPASSWD: ${NPM_BIN} install -g @geekbeer/minion@latest --registry *"
|
|
325
325
|
echo "${TARGET_USER} ALL=(root) NOPASSWD: /usr/bin/apt-get install *"
|
|
326
|
+
echo "${TARGET_USER} ALL=(root) NOPASSWD: /usr/bin/apt-get update"
|
|
326
327
|
echo "${TARGET_USER} ALL=(root) NOPASSWD: /usr/bin/apt-get remove *"
|
|
328
|
+
# Allow sh -c wrappers (used by playwright install-deps, etc.)
|
|
329
|
+
echo "${TARGET_USER} ALL=(root) NOPASSWD: /bin/sh -c *apt-get*"
|
|
330
|
+
echo "${TARGET_USER} ALL=(root) NOPASSWD: /usr/bin/sh -c *apt-get*"
|
|
331
|
+
echo "${TARGET_USER} ALL=(root) NOPASSWD: /bin/sh -c *dpkg*"
|
|
327
332
|
|
|
328
333
|
case "$PROC_MGR" in
|
|
329
334
|
systemd)
|
package/linux/server.js
CHANGED
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
* Secrets: GET /api/secrets, PUT/DELETE /api/secrets/:key
|
|
21
21
|
* Config: GET /api/config/backup, GET/PUT /api/config/env
|
|
22
22
|
* Executions: GET /api/executions, GET /api/executions/:id, etc.
|
|
23
|
+
* Sudoers: GET /api/sudoers
|
|
23
24
|
* ─────────────────────────────────────────────────────────────────────────────
|
|
24
25
|
*/
|
|
25
26
|
|
|
@@ -65,6 +66,7 @@ const { authRoutes } = require('../core/routes/auth')
|
|
|
65
66
|
const { variableRoutes } = require('../core/routes/variables')
|
|
66
67
|
const { memoryRoutes } = require('../core/routes/memory')
|
|
67
68
|
const { dailyLogRoutes } = require('../core/routes/daily-logs')
|
|
69
|
+
const { sudoersRoutes } = require('../core/routes/sudoers')
|
|
68
70
|
|
|
69
71
|
// Linux-specific routes
|
|
70
72
|
const { commandRoutes, getProcessManager, getAllowedCommands } = require('./routes/commands')
|
|
@@ -265,6 +267,7 @@ async function registerAllRoutes(app) {
|
|
|
265
267
|
await app.register(variableRoutes)
|
|
266
268
|
await app.register(memoryRoutes)
|
|
267
269
|
await app.register(dailyLogRoutes)
|
|
270
|
+
await app.register(sudoersRoutes)
|
|
268
271
|
|
|
269
272
|
// Linux-specific routes
|
|
270
273
|
await app.register(commandRoutes)
|