How do I drop root privileges in shell scripts?

Covering only runit and sudo misses a lot. There is actually a whole family of toolsets like runit, and a wide choice of tools for doing exactly this, the very task that they were designed for:

  • Daniel J. Bernstein's daemontools has setuidgid:
    setuidgid user /home/user/unprivileged.sh
  • util-linux (installed by default on newer Debians) has setpriv:
    setpriv --reuid=user --regid=group --init-groups --inh-caps=-all /home/user/unprivileged.sh
  • Adam Sampson's freedt has setuidgid:
    setuidgid user /home/user/unprivileged.sh
  • Bruce Guenter's daemontools-encore has setuidgid:
    setuidgid user /home/user/unprivileged.sh
  • Gerrit Pape's runit has chpst:
    chpst -u user /home/user/unprivileged.sh
  • Wayne Marshall's perp has runuid:
    runuid user /home/user/unprivileged.sh
  • Laurent Bercot's s6 has s6-setuidgid:
    s6-setuidgid user /home/user/unprivileged.sh
  • my nosh has setuidgid:
    setuidgid user /home/user/unprivileged.sh

They don't mix in the different task of adding privileges, and never fall into an interactive mode.

Your tacked-on problems are little more than your forgetting to have sbin as well as bin in your PATH, by the way.

Further reading

  • Jonathan de Boyne Pollard (2014). Don't abuse su for dropping user privileges. Frequently Given Answers.

I use runit's chpst tool for tasks like this. For example, from the up script call your unprivileged script:

chpst -u nobody /path/to/script

script which drops privileges and runs other script (but here I just made it to run itself):

#!/bin/sh

id=`id -u`
safeuser="nobody"

if [ $id = "0" ]
then
         # we're root. dangerous!
        sudo -u $safeuser "$0"      # Be sure to quote "$0"
else
    echo "I'm not root"
    id
fi

Example:

root@n3:/tmp/x# id
uid=0(root) gid=0(root) группы=0(root)
root@n3:/tmp/x# ./drop.sh
I'm not root
uid=65534(nobody) gid=65534(nogroup) группы=65534(nogroup)