sudo: add kill-child.sh

ultimately, a non-previlieged child cannot kill previlieged parent.
all the notes and research in shell.js are not useful.
This commit is contained in:
Girish Ramakrishnan
2025-07-16 20:37:13 +02:00
parent e4ceedcac6
commit e03beba9bc
3 changed files with 55 additions and 6 deletions
+3
View File
@@ -71,4 +71,7 @@ yellowtent ALL=(root) NOPASSWD: /home/yellowtent/box/src/scripts/hdparm.sh
Defaults!/home/yellowtent/box/src/scripts/hdparm.sh env_keep="HOME BOX_ENV"
yellowtent ALL=(root) NOPASSWD: /home/yellowtent/box/src/scripts/logtail.sh
Defaults!/home/yellowtent/box/src/scripts/kill-child.sh env_keep="HOME BOX_ENV"
yellowtent ALL=(root) NOPASSWD: /home/yellowtent/box/src/scripts/kill-child.sh
cloudron-support ALL=(ALL) NOPASSWD: ALL
+6 -6
View File
@@ -9,6 +9,7 @@ const assert = require('assert'),
TransformStream = stream.Transform;
const LOGTAIL_CMD = path.join(__dirname, 'scripts/logtail.sh');
const KILL_CHILD_CMD = path.join(__dirname, 'scripts/kill-child.sh');
class LogStream extends TransformStream {
constructor(options) {
@@ -68,13 +69,12 @@ function tail(filePaths, options) {
if (options.follow) args.push('--follow');
if (options.sudo) {
const cp = child_process.spawn('/usr/bin/sudo', [ '-S', LOGTAIL_CMD, ...args, ...filePaths ]);
cp.terminate = () => { // see note in shell.js
child_process.spawn('kill', ['-SIGKILL', -cp.pid], { detached: true }, (error) => {
if (error) debug(`tail could not terminate`, error);
const cp = child_process.spawn('/usr/bin/sudo', [ LOGTAIL_CMD, ...args, ...filePaths ]);
cp.terminate = () => {
child_process.execFile('/usr/bin/sudo', [ KILL_CHILD_CMD, cp.pid, process.pid ], { encoding: 'utf8' }, (error, stdout, stderr) => {
if (error) debug(`tail: failed to kill children`, stdout, stderr);
});
};
cp.stdin.end();
return cp;
} else {
const cp = child_process.spawn('/usr/bin/tail', args.concat(filePaths));
@@ -96,7 +96,7 @@ function journalctl(unit, options) {
if (options.follow) args.push('--follow');
const cp = spawn('journalctl', args);
const cp = child_process.spawn('journalctl', args);
cp.terminate = () => cp.kill('SIGKILL');
return cp;
}
+46
View File
@@ -0,0 +1,46 @@
#!/bin/bash
set -eu -o pipefail
if [[ ${EUID} -ne 0 ]]; then
echo "This script should be run as root." > /dev/stderr
exit 1
fi
if [[ $# -eq 0 ]]; then
echo "No arguments supplied"
exit 1
fi
if [[ "$1" == "--check" ]]; then
echo "OK"
exit 0
fi
function killtree() {
local pid=$1
for cpid in $(pgrep -P "$pid"); do
killtree "${cpid}" || true
done
echo "kill-child: killing $pid"
kill -SIGKILL "${pid}" 2>/dev/null || true
}
readonly target_pid="$1"
readonly expected_parent_pid="$2"
readonly target_actual_parent_pid=$(ps -o ppid= -p "${target_pid}" 2>/dev/null | tr -d ' ')
if [[ -z "${target_actual_parent_pid}" ]]; then
echo "kill-child: target PID ${target_pid} does not exist."
exit 1
fi
if [[ "${target_actual_parent_pid}" -ne "${expected_parent_pid}" ]]; then
echo "kill-child: refusing to kill — PID ${target_pid} is not a child of ${expected_parent_pid}."
exit 1
fi
readonly child_cmd=$(ps -o cmd= -p "${target_pid}")
echo "kill-child: kill PID ${target_pid} (command: ${child_cmd})"
killtree ${target_pid}