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
    Commented May 30, 2014 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
    Commented May 31, 2014 at 11:05

5 Answers 5


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


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
    Commented May 30, 2014 at 10:23
  • @mirimir, yes, I use that repo when I need to build a runit package for CentOS. Commented Jun 2, 2014 at 14:22

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
    Commented Jan 31, 2021 at 9:02
  • @ens That's correct, though I can see how my wording might be misleading. I'll rephase my answer.
    – dcoles
    Commented Jan 31, 2021 at 19:46

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
    Commented May 31, 2014 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 ... Commented Jun 1, 2014 at 0:19
  • @0xC0000022L Thanks. I took another look at sudo. And yes, "sudo -u user" plus display and language also works.
    – mirimir
    Commented Jun 1, 2014 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. Commented Jun 2, 2017 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. Commented Jun 2, 2017 at 1:03

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

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .