Каков наилучший способ проверить, имеет ли пользователь Python script root-подобные привилегии?
У меня есть Python script, который будет делать много вещей, которые потребуют привилегий на уровне корневого уровня, таких как перемещение файлов в /etc, установка с помощью apt-get и т.д. В настоящее время у меня есть:
if os.geteuid() != 0:
exit("You need to have root privileges to run this script.\nPlease try again, this time using 'sudo'. Exiting.")
Это лучший способ сделать чек? Существуют ли другие рекомендации?
Ответы
Ответ 1
В соответствии с принципом "проще просить прощения, чем разрешения":
try:
os.rename('/etc/foo', '/etc/bar')
except IOError as e:
if (e[0] == errno.EPERM):
print >> sys.stderr, "You need root permissions to do this, laterz!"
sys.exit(1)
Если вас беспокоит непереносимость os.geteuid()
, вы, вероятно, не должны вставлять /etc
в любом случае.
Ответ 2
os.geteuid
получает эффективный идентификатор пользователя, который именно вы хотите, поэтому я не могу придумать лучшего способа выполнить такую проверку. Единственное, что неуверенно в том, что в заголовке "root-like": ваш код проверяет ровно root
, нет "как", и, действительно, я не знаю, что означает "root-like but not root" - так, если вы имеете в виду нечто иное, чем "точно root", возможно, вы можете прояснить, спасибо!
Ответ 3
Если вы действительно хотите, чтобы ваш код был надежным в самых разных конфигурациях Linux, я бы предположил, что вы рассматриваете угловые случаи, когда кто-то может использовать SELinux или ACL файловой системы или функции "возможностей", которые были в ядро Linux с версии 2.2 или около того. Ваш процесс может работать под некоторой оболочкой, которая использовала SELinux или некоторую библиотеку возможностей Linux, такую как libcap2 libcap-ng или fscaps или elfcap через что-то более экзотическое, как замечательный и грустно недооцениваемый Niels Provos systrace.
Все эти способы могут быть запущены как не-root, но для вашего процесса, возможно, был делегирован необходимый доступ для выполнения его работы без EUID == 0.
Поэтому я предлагаю вам рассмотреть возможность написания кода более Pythonically, путем переноса операций, которые могут завершиться неудачно из-за разрешений или других проблем с кодом обработки исключений. Если вы выполняете различные операции (например, с помощью модуля subprocess
), вы можете предложить префикс всех таких вызовов с помощью sudo
(например, в командной строке, среде или файле файла .rc)). Если он запускается в интерактивном режиме, вы можете предложить повторно выполнить любые команды, которые увеличивают связанные с разрешения исключения с помощью sudo
(необязательно, только если вы найдете sudo
в os.environ ['PATH']).
В целом, правда, что большинство систем Linux и UNIX по-прежнему имеют большую часть своего администрирования, выполняемого привилегированным пользователем root. Тем не менее, это старая школа, и мы, как программисты, должны пытаться поддерживать более новые модели. Попытка вашей работы и позволяющая обработке исключений выполнять свою работу, позволяет вашему коду работать в любой системе, которая прозрачно разрешает нужные вам операции, а знать и готово к использованию sudo
- это приятное прикосновение (так как это, наиболее распространенный инструмент для контролируемого делегирования системных привилегий).
Ответ 4
Вы можете запросить пользователя для доступа к sudo:
import os, subprocess
def prompt_sudo():
ret = 0
if os.geteuid() != 0:
msg = "[sudo] password for %u:"
ret = subprocess.check_call("sudo -v -p '%s'" % msg, shell=True)
return ret
if prompt_sudo() != 0:
# the user wasn't authenticated as a sudoer, exit?
Переключатель sudo -v
обновляет пользовательские кешированные учетные данные (см. man sudo
).
Ответ 5
Ответ на вторую часть вопроса
(извините, поле комментариев было слишком маленьким)
Пол Хоффман, вы правы, я только обратился к одной части вашего вопроса, посвященной внутренним характеристикам, но это был бы не достойный язык сценариев, если бы он не мог справиться с apt-get
. Предпочтительная библиотека - немного подробный, но она выполняет задание:
>>> apt_get = ['/usr/bin/apt-get', 'install', 'python']
>>> p = subprocess.Popen(apt_get, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
>>> p.wait()
100 # Houston, we have a problem.
>>> p.stderr.read()
'E: Could not open lock file /var/lib/dpkg/lock - open (13: Permission denied)'
'E: Unable to lock the administration directory (/var/lib/dpkg/), are you root?\n'
Но Popen
является обобщенным инструментом и может быть завербован для удобства:
$ cat apt.py
import errno
import subprocess
def get_install(package):
cmd = '/usr/bin/apt-get install'.split()
cmd.append(package)
output_kw = {'stdout': subprocess.PIPE, 'stderr': subprocess.PIPE}
p = subprocess.Popen(cmd, **output_kw)
status = p.wait()
error = p.stderr.read().lower()
if status and 'permission denied' in error:
raise OSError(errno.EACCES, 'Permission denied running apt-get')
# other conditions here as you require
$ python
>>> import apt
>>> apt.get_install('python')
Traceback ...
OSError: [Errno 13] Permission denied running apt-get
И теперь мы вернемся к обработке исключений. Я откажусь прокомментировать Java-подобную чрезмерную общность модуля подпроцесса.
Ответ 6
Мое приложение работает с этим кодом:
import os
user = os.getenv("SUDO_USER")
if user is None:
print "This program need 'sudo'"
exit()
Ответ 7
Мне нравится проверять sudo на переменные среды:
if not 'SUDO_UID' in os.environ.keys():
print "this program requires super user priv."
sys.exit(1)
Ответ 8
Все зависит от того, насколько портативным вы хотите, чтобы приложение было. Если вы имеете в виду бизнес, мы должны предположить, что учетная запись администратора не всегда равна 0. Это означает, что проверка на euid 0 недостаточна. Проблема в том, что бывают ситуации, когда одна команда будет вести себя так, как если бы вы были root, а затем с ошибкой отказались (думаю, SELinux и co.). Поэтому он действительно лучше терпеть неудачу изящно и проверять EPERM errno всякий раз, когда это уместно.