Сообщить об ошибке.

Использование sudo c модулем sh в Python

Есть 3 способа использования команды терминала sudo для выполнения команд в скрипте, требующих прав суперпользователя. Они перечислены в порядке полезности и безопасности. В большинстве случаев следует просто использовать вариант sh.contrib.sudo().

sh.contrib.sudo()

Так как команда sudo используется очень часто, разработчики модуля sh добавили версию этой команды в подмодуль contrib, чтобы сделать использование sudo более интуитивным. Версия contrib представляет собой обычную обертку для команды sh.sudo raw со специальным ключевым аргументом для ввода пароля суперпользователя, чтобы она работала правильно.

Выполнение sh.contrib.sudo() через менеджер контекста with с вводом пароля через терминал:

import sh

with sh.contrib.sudo:
    print(ls("/root"))

# [sudo] password for youruser: *************
# your_root_files.txt

Или, альтернативно, с помощью подкоманд:

import sh
print(sh.contrib.sudo.ls("/root"))
# [sudo] password for youruser: *************
# your_root_files.txt

В приведенном выше примере вызов sh.contrib.sudo автоматически запрашивает пароль, используя под капотом встроенный модуль Python getpass.getpass().

Этот метод является наиболее безопасным, потому что он снижает шансы сделать что-то небезопасное, например, включить ваш пароль в скрипт python или сказать, что конкретный пользователь может выполнить что-либо внутри конкретного скрипта (метод NOPASSWD).

Примечание. sh.contrib.sudo не выполняет кэширование паролей, как это делает двоичный файл sudo. Это означает, что каждый раз, когда в скрипте запускается команда sudo, будет предложено ввести пароль.

Также можно указать пароль к sh.contrib.sudo в виде строки:

import sh

password = 'password'

with sh.contrib.sudo(password, _with=True):
    print(ls("/root"))

Предупреждение. Этот метод менее безопасен, потому что явно указывается пароль в скрипте python, а это плохая идея. Однако он более гибкий, поскольку позволяет получить пароль из другого источника, если конечным результатом является строка.

/etc/sudoers NOPASSWD.

С помощью этого метода можно использовать необработанную команду sh.sudo напрямую, потому что гарантировано, что система не будет запрашивать пароль. Для этого сначала необходимо настроить пользователя на получение привилегий выполнения root.

Отредактируйте свой файл sudoers:

$ sudo visudo
# Добавьте или отредактируйте строку, 
# описывающую права доступа пользователя:
yourusername ALL = (root) NOPASSWD: /path/to/your/program

Добавляемая строка говорит о том, что при вводе команды терминала sudo пользователь yourusername на ВСЕХ ALL хостах сможет работать как (root), и что не будет запрашиваться пароль NOPASSWD для выполнения /path/to/your/program.

Предупреждение. Этот метод может быть небезопасным, если непривилегированный пользователь сможет редактировать этот сценарий и таким образом поместит что-то плохое в этот сценарий.

Использование необработанной команды sh.sudo (которая преобразуется непосредственно в двоичный файл sudo) без метода NOPASSWD возможно при условии, если самостоятельно подключить специальный ключевой аргумент. Этот метод обсуждается в основном в образовательных целях. Если вы потратите время на то, чтобы подключить ключевой аргумент к sh.sudo самостоятельно, то вы, по сути, воссоздали sh.contrib.sudo.

import sh

# пароль должен заканчиваться новой строкой
my_password = "password\n"

# `-S` говорит: "получите пароль от `stdin`"
# метод `.bake` аналогичен `functools.partial()`
my_sudo = sh.sudo.bake("-S", _in=my_password)
# вызываем созданную функцию `my_sudo`
# с зашитыми в нее параметрами 
my_sudo.ls("root")

По сути метод .bake делает то же самое что и functools.partial().

Использование аргумента _fg=True

Еще один менее очевидный способ использования sudo - выполнить необработанную команду sh.sudo, но также вывести ее на передний план. Таким образом, sudo будет работать правильно, автоматически подключая stdin/out/err и запрашивая пароль, если он требуется. Однако недостатком использования _fg=True является то, что нельзя записать куда либо его вывод - все просто выводится на терминал.

import sh
sh.sudo.ls("/root", _fg=True)