So you have a shell script which needs to drop or modify its privileges by switching to an appropriate system user before continuing its execution.
Here are some alternatives to accomplish this while preserving all original command line arguments properly. Privileges are dropped by switching to the nobody
user, adapt the RUN_AS
variable as desired.
Alternative 1 – using only su
, requires being root (uid 0) or the target user to run
#!/bin/sh # This script must run as RUN_AS=nobody if [ `id -nu` != $RUN_AS ]; then if [ `id -u` -ne 0 ]; then echo >&2 "Sorry, you must be either root or $RUN_AS to run me." exit 1 fi # This environment variable is just a safe guard for endless re-exec loop # and something the script can use to test up to this point if it has # dropped privileges by re-executing itself if [ "$EXEC_SU" ]; then echo >&2 "Re-exec loop circuit breaker engaged, something is wrong" exit 1 fi exec su $RUN_AS -s /bin/sh -c "EXEC_SU=1 \"$0\" \"\$@\"" -- "$0" "$@" fi # At this point, we can be sure we are running as the desired user. echo Running as `id -nu` for arg in "$@"; do echo Argument $((n=n+1)): $arg done
This alternative requires being root or the target user when invoking the script. Command line arguments are preserved after switching.
Alternative 2 – using sudo
#!/bin/sh # This script must run as RUN_AS=nobody if [ `id -nu` != $RUN_AS ]; then if [ -z "$SUDO_EXEC" ]; then exec sudo -u $RUN_AS SUDO_EXEC=1 "$0" "$@" else echo >&2 'Re-exec loop circuit breaker engaged, something is wrong' exit 1 fi fi # At this point, we can be sure we are running as the desired user. echo Running as `id -nu` for arg in "$@"; do echo Argument $((n=n+1)): $arg done
Unlike alternative 1, this method does not require the script to be invoked by root or the target user directly, if switching to a system user that typically has no password set on its own. (Su will also prompt for password automatically, but that requires the target user password.)
Alternative 3 – when a clean login environment is required
If you require the script to run in a clean login environment for the target user, then things become slightly more complicated. It can be accomplished by combining sudo
with su
:
#!/bin/sh # This script must run as RUN_AS=nobody if [ `id -nu` != $RUN_AS ]; then if [ -z "$SUDO_SU_EXEC" ]; then exec sudo su -s /bin/sh - $RUN_AS -c "SUDO_SU_EXEC=1 \"$0\" \"\$@\"" -- "$0" "$@" else echo >&2 'Re-exec loop circuit breaker engaged, something is wrong' exit 1 fi fi # At this point, we can be sure we are running as the desired user. echo Running as `id -nu` echo USER=$USER HOME=$HOME for arg in "$@"; do echo Argument $((n=n+1)): $arg done
Note that since the example above switches to nobody
, which is a system user that by default does not have a shell configured, we explicitly set the shell using su
argument "-s /bin/sh"
.