Skip to content

Instantly share code, notes, and snippets.

@pothos
Created January 26, 2022 12:31
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pothos/73dd4f7694acc3b6bbed614438f6e2b1 to your computer and use it in GitHub Desktop.
Save pothos/73dd4f7694acc3b6bbed614438f6e2b1 to your computer and use it in GitHub Desktop.
systemd-sudo.sh: a non-setuid sudo-alike using systemd-run
#!/bin/bash
set -euo pipefail
MYNAME=$(basename "${0}")
opts=$(getopt --name "$MYNAME" --options 'hVsEnu:g:D:' \
--longoptions 'help,version,shell,preserve-env,non-interactive,user:,group:,chdir:' -- "${@}")
eval set -- "${opts}"
U=0
G=0
SHELLARGS=()
NOINTERACTIVEARGS=()
ENVARGS=()
WDIR="$PWD"
while true; do
case "$1" in
-h|--help)
echo "$MYNAME - execute a command as another user through systemd-run (no SUID binary involved except /usr/lib/polkit-1/polkit-agent-helper-1), recognizes set PATH in contrast to sudo"
echo "usage: $MYNAME -h"
echo "usage: $MYNAME [-En] [-D directory] [-g group] [-u user] [-s] [<command>]"
echo
echo "Options:"
echo "-D, --chdir=directory change the working directory before running command"
echo "-E, --preserve-env preserve user environment when running command (includes PATH in constrast to sudo)"
echo "-g, --group=group run command as the specified group name or ID"
echo "-h, --help display help message and exit"
echo "-n, --non-interactive non-interactive mode, no prompts are used"
echo "-s, --shell run shell as the target user; a command may also be specified"
echo "-u, --user=user run command (or edit file) as specified user name or ID"
echo "-V, --version display version information and exit"
echo "-- stop processing command line arguments"
echo "The following sudo options will not be supported: -A -B -C -e -h -i -K -k -l -p -r -S -t -U -v"
echo "TODO: implement setting env vars by specifying VAR=VALUE"
echo "TODO: implement --preserve-env=list"
echo "TODO: implement -P -S -b -H -R -T"
exit 1
;;
-V|--version)
echo "systemd-sudo version 0.1"
exit 0
;;
-s|--shell)
# runs "$SHELL"
SHELLARGS=("-S" "--pty")
;;
-E|--preserve-env)
ENVARGS=()
# Extra "sh -c" is needed to only export the exported variables
for VARNAME in $(sh -c 'compgen -v'); do
set +u
VAL="${!VARNAME}"
set -u
ENVARGS+=("--setenv" "${VARNAME}=${VAL}")
done
;;
-n|--non-interactive)
NOINTERACTIVEARGS=("--no-ask-password")
;;
-D|--chdir)
WDIR="$1"
shift
;;
-u|--user)
UNAME="$1"
shift
U=$(id -u "$UNAME")
;;
-g|--group)
GNAME="$1"
G=$(id -g "$GNAME")
shift
;;
--)
shift
break;;
esac
shift
done
exec systemd-run --system --quiet --wait --collect --pipe --working-directory "$WDIR" --uid "$U" --gid "$G" "${SHELLARGS[@]}" "${NOINTERACTIVEARGS[@]}" "${ENVARGS[@]}" -- "$@"
@pothos
Copy link
Author

pothos commented Jan 26, 2022

Known issue: Ctrl-C doesn't stop the process with --pipe, one could use --pty (but it leads to processes expecting stdin when there is none) or better let the wrapper script handle the INT trap and stop the service (or add a watchdog to the service that checks if the wrapper script process is still alive and stops the service if not).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment