5
[root@hostname ~]# python script.py                  # allow this

[user@hostname ~]$ sudo python script.py             # deny this
[user@hostname ~]$ sudo -E python script.py          # deny this
[user@hostname ~]$ sudo PATH=$PATH python script.py  # deny this
[user@hostname ~]$ python script.py                  # kindly refuse this

I'm trying to achieve the behavior above. Read further if you care why or if the example isn't sufficient enough. Sorry for the sharp tongue, but most of my Stack Exchange questions get hostile questions back instead of answers.

This question arises from requiring an admin to run my script, but the nature of the script requires root's environment variables (and not sudo's).

I've given this some thorough research... below is from this answer

if os.geteuid() == 0:
    pass  # sufficient to determine if elevated privileges

But then I started needing to access PATH inside of my script. I noticed that

sudo -E env | grep PATH; env | grep PATH

prints different PATH values. I found it was because of the security policy on PATH. I also found the workaround to PATH is sudo PATH=$PATH ...

However, it's not the only policy protected environment variable, and at that point, why push this enumeration of environment variables on the script user? It seems that requiring root explicitly is the best approach, and just warn the admin to use root explicitly from within the script otherwise.

Is there such a way to distinguish between root and sudo with Python?

user2357112
  • 260,549
  • 28
  • 431
  • 505

3 Answers3

2

Despite the reasons discussed to not pursue this solution, I actually did find it for others wondering if it's possible.

[user@hostname ~]$ sudo python
>>> import os
>>> os.environ["SUDO_UID"]  # UID of user running sudo
'uid'

And when logged in as root...

[root@hostname ~]# python
>>> import os
>>> try:
...     uid = os.environ["SUDO_UID"]
        raise AssertionError("Ran with sudo")
... except KeyError, e:
...     ...  # SUDO_UID, SUDO_USER, etc. not set without sudo

I also found a way to access root's PATH just running with sudo.

path = os.popen("su - -c env | grep ^PATH= | cut -d'=' -f2-").read().strip()

I think I like this solution better than relying on how my script is ran.

1

You're going to get "hostile questions" because the premise of your issue doesn't make much sense. In general if a command can be run as the root user via sudo then it should not matter whether it was run via sudo (or runas, etc.) or by some other mechanism that has the UID set to root such as an interactive login as the root user. You should not require running a program to be predicated on an interactive login as the root user account rather than via a setuid program like sudo or your program if it were setuid root.

A cheap and dirty solution is to ensure the interactive root login sets a unique env var that is unlikely to be set when your program is run via sudo. That is, however, obviously easy to spoof so if you're doing this for security then that approach is not acceptable.

Kurtis Rader
  • 6,734
  • 13
  • 20
  • Thank you. I realize now that what I'm asking for is probably not the proper solution, but you and others have helped me better understand what `sudo` really does. Nevertheless, I think other people could have this question in the future too and maybe could learn from my mistake as I did. I'm choosing @Andriy's suggestion to document it and leave it be. However, because this is predicated on security, it's somewhat irksome that I can't verify that I'm really checking through `root`'s `PATH` directories. –  May 31 '18 at 13:05
-4

Use the subprocess module to run commands and check the output:

from subprocess import check_output

uid = check_output(['bash', '-c', 'echo $UID']).decode().strip()
if uid != '0':
    sys.exit()  # or return

or

user = check_output(['whoami']).decode().strip()
if user != 'root':
    sys.exit()  # or return

It appears that aside from checking $PATH, root and sudo are indistinguishable.

Nelson
  • 922
  • 1
  • 9
  • 23