запуск команды в качестве суперпользователя из скрипта python

Итак, я пытаюсь запустить процесс от имени суперпользователя из скрипта Python, используя подпроцесс. В оболочке ipython что-то вроде

proc = subprocess.Popen('sudo apach2ctl restart',
                        shell=True, stdin=subprocess.PIPE,
                        stdout=subprocess.PIPE,
                        stderr=subprocess.PIPE)

работает нормально, но как только я втыкаю его в скрипт, я получаю: sudo: apach2ctl: command not found.

Я предполагаю, что это связано с тем, как sudo обрабатывает среды в Ubuntu. (Я также пробовал sudo -E apche2ctl restart и sudo env path=$PATH apache2ctl restart безрезультатно)

Итак, мой вопрос в основном таков: если я хочу запустить apache2ctl restart как суперпользователя, который при необходимости запрашивает у пользователя пароль суперпользователя, как мне это сделать? Я не собираюсь хранить пароли в скрипте.

Редактировать:

Я пробовал передавать команды как в виде строки, так и в виде списка. В интерпретаторе python со строкой я правильно получу запрос пароля (все еще не работает в сценарии python, как в моей исходной проблеме), список просто дает экран справки для sudo.

Редактировать 2:

Итак, я понимаю, что, хотя Popen будет работать с некоторыми командами так же, как со строками, когда shell=True, требуется

proc = subprocess.Popen(['sudo','/usr/sbin/apache2ctl','restart'])

без «shell = True», чтобы заставить sudo работать.

Спасибо!


person Community    schedule 19.02.2009    source источник
comment
Как насчет настройки sudo, чтобы этот пользователь мог запускать только эту команду, не требуя пароля?   -  person Harley Holcombe    schedule 20.02.2009
comment
Вы можете использовать proc.wait() в зависимости от того, что вы делаешь; wait не продолжается до тех пор, пока не завершится выполнение дочернего процесса.   -  person craymichael    schedule 08.11.2016


Ответы (8)


Попробуйте указать полный путь к apache2ctl.

person dwc    schedule 19.02.2009
comment
Я думаю, вы попали на него. Запуск скрипта из интерпретатора наследует $PATH от интерактивной оболочки, в которой он запущен. Однако при запуске в качестве сценария $PATH наследуется от НЕинтерактивной оболочки и вполне может быть другим. При использовании popen() лучше всегда указывать полные пути. - person Ben Blank; 20.02.2009
comment
Да, всякий раз, когда вы ссылаетесь на двоичные файлы из скриптов, вы не можете полагаться на $PATH. Кстати, apch2ctl написан с ошибкой. ;) - person ojrac; 20.02.2009

Пытаться:

subprocess.call(['sudo', 'apach2ctl', 'restart'])

Подпроцесс должен получить доступ к реальному stdin/out/err, чтобы он мог подсказать вам и прочитать ваш пароль. Если вы настроите их как каналы, вам нужно будет ввести пароль в этот канал самостоятельно.

Если вы их не определяете, то он захватывает sys.stdout и т. д.

person Mike Boers    schedule 19.02.2009
comment
Вы не решили проблему с паролем, как решено ниже. - person Gilco; 09.10.2020

Другой способ — сделать пользователя sudo user без пароля.

Введите следующее в командной строке:

sudo visudo

Затем добавьте следующее и замените <username> своим:

<username> ALL=(ALL) NOPASSWD: ALL

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

person Community    schedule 09.07.2014

Я использовал это для Python 3.5. Я сделал это с помощью модуля subprocess. Используя пароль как это очень небезопасно.

Модуль subprocess принимает команду в виде списка строк, поэтому либо создайте список заранее, используя split() или передать весь список позже. Прочтите документацию для получения дополнительной информации.

Здесь мы повторяем пароль, а затем с помощью pipe передаем его в sudo через аргумент '-S'.

#!/usr/bin/env python
import subprocess

sudo_password = 'mysecretpass'
command = 'apach2ctl restart'
command = command.split()

cmd1 = subprocess.Popen(['echo',sudo_password], stdout=subprocess.PIPE)
cmd2 = subprocess.Popen(['sudo','-S'] + command, stdin=cmd1.stdout, stdout=subprocess.PIPE)

output = cmd2.stdout.read().decode() 
person Community    schedule 05.04.2018
comment
если бы мне нужно было ввести пароль, я бы не сохранял пароль где-либо в коде, а вместо этого использовал бы что-то вроде getpass, делая вашей первой командой cmd1 = subprocess.Popen(['echo',getpass.getpass('password:')], stdout=subprocess.PIPE) или то, что упоминал Йожеф Тури - person Silfheed; 05.04.2018

Вы должны использовать Popen следующим образом:

cmd = ['sudo', 'apache2ctl', 'restart']
proc = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

Он ожидает список.

person Harley Holcombe    schedule 19.02.2009
comment
Прочтите docs.python.org/library/subprocess.html Существует разница между строка и аргументы последовательности, когда оболочка = True. - person jfs; 20.02.2009
comment
О, я вижу, я никогда не использовал его так раньше. - person Harley Holcombe; 20.02.2009
comment
Не устанавливайте shell=True, когда вы передаете в него список. - person आनंद; 28.04.2017

Самый безопасный способ сделать это — заранее запросить пароль, а затем передать его в команду. Запрос пароля позволит избежать сохранения пароля где-либо в вашем коде, а также он не будет отображаться в вашей истории bash. Вот пример:

from getpass import getpass
from subprocess import Popen, PIPE

password = getpass("Please enter your password: ")
# sudo requires the flag '-S' in order to take input from stdin
proc = Popen("sudo -S apach2ctl restart".split(), stdin=PIPE, stdout=PIPE, stderr=PIPE)
# Popen only accepts byte-arrays so you must encode the string
proc.communicate(password.encode())
person Community    schedule 29.05.2019
comment
Мне нравится этот. Однако я бы использовал shlex.split() вместо str.split() для более безопасного разделения аргументов. - person Szabolcs; 29.11.2019

Я пробовал все решения, но не работал. Хотел запускать длительные задачи с помощью Celery, но для этого мне нужно было запустить команду sudo chown с subprocess.call().

Вот что сработало для меня:

Чтобы добавить безопасные переменные среды, в командной строке введите:

export MY_SUDO_PASS="user_password_here"

Чтобы проверить, работает ли он:

echo $MY_SUDO_PASS
 > user_password_here

Чтобы запустить его при запуске системы, добавьте его в конец этого файла:

nano ~/.bashrc  

#.bashrc
...
existing_content:

  elif [ -f /etc/bash_completion ]; then
    . /etc/bash_completion
  fi
fi
...

export MY_SUDO_PASS="user_password_here"

Вы можете добавить все свои переменные среды, пароли, имена пользователей, хост и т. д. здесь позже.

Если ваши переменные готовы, вы можете запустить:

Обновить:

echo $MY_SUDO_PASS | sudo -S apt-get update

Или установить Midnight Commander

echo $MY_SUDO_PASS | sudo -S apt-get install mc

Чтобы запустить Midnight Commander с помощью sudo

echo $MY_SUDO_PASS | sudo -S mc

Или из оболочки python (или Django/Celery), чтобы рекурсивно изменить владельца каталога:

python
>> import subprocess
>> subprocess.call('echo $MY_SUDO_PASS | sudo -S chown -R username_here /home/username_here/folder_to_change_ownership_recursivley', shell=True)

Надеюсь, поможет.

person Community    schedule 15.03.2018

Чтобы запустить команду от имени пользователя root и передать ей пароль в командной строке, вы можете сделать это следующим образом:

import subprocess
from getpass import getpass

ls = "sudo -S ls -al".split()
cmd = subprocess.run(
    ls, stdout=subprocess.PIPE, input=getpass("password: "), encoding="ascii",
)
print(cmd.stdout)

Для вашего примера, вероятно, что-то вроде этого:

import subprocess
from getpass import getpass

restart_apache = "sudo /usr/sbin/apache2ctl restart".split()
proc = subprocess.run(
    restart_apache,
    stdout=subprocess.PIPE,
    input=getpass("password: "),
    encoding="ascii",
)
person Community    schedule 01.07.2020