The "--up" option in OpenVPN is normally used for routing etc. And so it is processed before OpenVPN drops root privileges to run as nobody. However, I am invoking shell scripts that need to run as an unprivileged user.

How do I do that? I have studied Drop Process Privileges, especially polynomial's and tylerl's answers, but I do not understand how to implement. I am working in Centos 6.5, and suid is blocked, both as "chmod u+s" and as "setuid()".

There is an OpenVPN plugin ("openvpn-down-root.so") which enables scripts invoked by the "--down" option to run as root. There could be an equivalent, such as "openvpn-up-user.so", but I have not found it.


Per Nikola Kotur's answer, I've installed Ian Meyer's runit-rpm. Although the chpst command works in terminal, in the up script it fails with "command not found". What works is "sudo chpst" plus setting the proper display and language. Please see Why doesn't my terminal output unicode characters properly? Given that, the up script needs these four lines:

LANG="en_US.UTF-8"; export LANG
GDM_LANG="en_US.UTF-8"; export GDM_LANG
sudo chpst -u user -U user /home/user/unprivileged.sh &


Per 0xC0000022L's comment, I find that "sudo -u user" works as well as "sudo chpst -u user -U user":

LANG="en_US.UTF-8"; export LANG
GDM_LANG="en_US.UTF-8"; export GDM_LANG
sudo -u user /home/user/unprivileged.sh &

I'll study man sudoers and update if/when I get sudo alone to work.

  • comment left by @user66229: "May I suggest that you surf to sourceforge.net/p/openvpn/mailman and subscribe to the lists that you find most appropriate." – slm May 30 '14 at 13:52
  • @user66229 Thank you. But I don't believe that this is an OpenVPN-specific question. Why do you believe that it is? – mirimir May 31 '14 at 11:05

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
  • Cool :) Thank you. Is github.com/imeyer/runit-rpm the best way to get runit into Centos 6.5? Even with Red Hat repos, I'm not finding a package. – mirimir May 30 '14 at 10:23
  • @mirimir, yes, I use that repo when I need to build a runit package for CentOS. – Nikola Kotur Jun 2 '14 at 14:22

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

  • While playing around I found runuser from util-linux. Does the job for me. runuser -u user -- command – Ken Sharp Aug 28 '20 at 14:01
  • ..and then I scrolled down. Oops. – Ken Sharp Aug 28 '20 at 14:02

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


id=`id -u`

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


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)
  • While using "sudo -u user" works for some commands, it doesn't provide enough user environment for my purposes. And while "su -l user" works fine in terminal, it does nothing in scripts. I suppose that's a security feature. So it seems that I'm left with installing runit. – mirimir May 31 '14 at 2:08
  • 1
    @mirimir: why? /etc/sudoers allows to be very specific what environment variables can be conveyed to the program run by sudo and so on. man sudoers. Honestly, whoever uses sudo merely for sudo su - or to switch user context has no clue about what's behind this awesome program and how much you can lock down things for individual programs, users, hosts etc ... – 0xC0000022L Jun 1 '14 at 0:19
  • @0xC0000022L Thanks. I took another look at sudo. And yes, "sudo -u user" plus display and language also works. – mirimir Jun 1 '14 at 1:50
  • 1
    Speaking of dangerous -- you can't trust $0; the caller can set a script's $0 to literally anything they want it to be. And use more quotes. – Charles Duffy Jun 2 '17 at 0:59
  • 2
    ...if your script's $0 is /directory/name with spaces/script (and remember, users can create arbitrary symlinks in locations they own, even if they don't use exec -a somename yourscript to call yourscript with a $0 of somename), then you'd get sudo -u nobody /directory/name with spaces, with with and spaces passed as separate arguments to your command. – Charles Duffy Jun 2 '17 at 1:03

On Linux-based systems you can setpriv (from util-linux):

setpriv --reuid=1000 --regid=1000 --init-groups COMMAND [ARGUMENTS...]  # Like su/runuser/sudo
setpriv --reuid=1000 --regid=1000 --clear-groups COMMAND [ARGUMENTS...]  # Like daemontools setuid(8)

Use runuser if a PAM session is required (also from util-linux):

runuser -u USER [--] COMMAND [ARGUMENTS...]

From the su man page:

su is mostly designed for unprivileged users, the recommended solution for privileged users (e.g. scripts executed by root) is to use non-set-user-ID command runuser(1) that does not require authentication and provide separate PAM configuration. If the PAM session is not required at all then the recommend solution is to use command setpriv(1).

  • 1
    runuser starts a PAM session. setpriv does not. – ens Jan 31 at 9:02
  • @ens That's correct, though I can see how my wording might be misleading. I'll rephase my answer. – dcoles Jan 31 at 19:46

What about:

sudo -Eu <user> <command>

You complained about environment in a previous comment, so you might also compare the differences between the outputs:

sudo -u <user> printenv
sudo -Eu <user> printenv

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.