Запись в файл с привилегиями sudo в Python
Следующий код выдает ошибку, если он запускается пользователем без полномочий root для файла, принадлежащего root, даже если пользователь, не являющийся пользователем root, имеет привилегии sudo:
try:
f = open(filename, "w+")
except IOError:
sys.stderr.write('Error: Failed to open file %s' % (filename))
f.write(response + "\n" + new_line)
f.close()
Есть ли способ запустить open(filename, "w+")
с привилегиями sudo или альтернативную функцию, которая делает это?
Ответы
Ответ 1
У вас есть несколько вариантов:
- Запустите ваш скрипт как root или с помощью sudo
- Установите бит setuid и получите root для скрипта (хотя во многих системах это не будет работать со скриптами, и тогда скрипт будет вызываться кем угодно)
- Определите, что вы не работаете от имени пользователя root (
os.geteuid() != 0
), затем позвоните себе с помощью sudo
infront (который попросит пользователя ввести свой пароль) и выйдите:
import os
import sys
import subprocess
if os.geteuid() == 0:
print("We're root!")
else:
print("We're not root.")
subprocess.call(['sudo', 'python3', *sys.argv])
sys.exit()
Вызов это выглядит так:
$ python3 be_root.py
We're not root.
Password:
We're root!
Ответ 2
TL; DR:
Ответ L3viathan имеет SynaxError Edit: отлично работает на Python 3. 5+. Вот версия для Python 3.4.3 (распространяется по умолчанию в Ubuntu 16.04) и ниже:
if os.geteuid() == 0:
# do root things
else:
subprocess.call(['sudo', 'python3'] + sys.argv) # modified
Тем не менее, я выбрал другой подход, потому что в моем случае это сделало код проще:
if os.geteuid() != 0:
os.execvp('sudo', ['sudo', 'python3'] + sys.argv)
# do root things
Explaination
Ответ L3viathan зависит от PEP 448, который был включен в Python 3.5 и ввел дополнительные контексты, где допускается расширение звездного аргумента. Для Python 3.4 и ниже, конкатенация списков может использоваться для того же:
import os
import sys
import subprocess
if os.geteuid() == 0:
print("We're root!")
else:
print("We're not root.")
subprocess.call(['sudo', 'python3'] + sys.argv) # modified
Но обратите внимание: subprocess.call()
запускает дочерний процесс, то есть после того, как root завершит выполнение сценария, исходный пользователь также продолжит выполнение своего сценария. Это означает, что вам нужно поместить логику с повышенными правами в одну сторону блока if/else
чтобы при завершении исходного скрипта он не пытался запустить какую-либо логику, требующую повышения (это делает пример L3viathan).
Это не обязательно плохо - это означает, что как нормальная/повышенная логика может быть написана одним и тем же сценарием, так и хорошо разделены - но моя задача требовала root для всей логики Я не хотел тратить уровень отступов, если другой блок будет пустым, и я не понимал, как использование дочернего процесса повлияет на вещи, поэтому я попробовал это:
import os
import sys
import subprocess
if os.geteuid() != 0:
subprocess.call(['sudo', 'python3'] + sys.argv)
# do things that require root
... и он сломался, конечно, потому что после того, как root был сделан, обычный пользователь возобновил выполнение своего скрипта и попытался выполнить операторы, которые требовали root.
Затем я обнаружил, что этот Gist рекомендует os.execvp()
который заменяет запущенный процесс вместо запуска дочернего:
import os
import sys
if os.geteuid() != 0:
os.execvp('sudo', ['sudo', 'python3'] + sys.argv) # final version
# do things that require root
Это, кажется, ведет себя как ожидалось, сохраняет уровень отступа и 3 строки кода.
Предостережение: я не знал о os.execvp()
десять минут назад и пока ничего не знаю о возможных подводных камнях или тонкостях его использования. YMMV.
Ответ 3
Ваш script ограничивается разрешениями, с которыми он выполняется, поскольку вы не можете изменять пользователей, не имея уже привилегий root.
Как сказал Роб, единственный способ сделать это, не изменяя права на файл, - это запустить с sudo
.
sudo python ./your_file.py
Ответ 4
Наличие возможности использования sudo
не дает вам никаких привилегий, если вы на самом деле не используете его. Так как другие ребята предположили, что вы, вероятно, должны просто запустить свою программу с помощью sudo
. Но если вам не нравится эта идея (я не вижу причин для этого), вы можете сделать другой трюк.
Ваш script может проверить, запущен ли он с правами root или работает только с правами пользователя. Чем script может работать с более высокими привилегиями. Здесь у вас есть пример (обратите внимание, что сохранение пароля в исходном коде не является хорошей идеей).
import os.path
import subprocess
password_for_sudo = 'pass'
def started_as_root():
if subprocess.check_output('whoami').strip() == 'root':
return True
return False
def runing_with_root_privileges():
print 'I have the power!'
def main():
if started_as_root():
print 'OK, I have root privileges. Calling the function...'
runing_with_root_privileges()
else:
print "I'm just a user. Need to start new process with root privileges..."
current_script = os.path.realpath(__file__)
os.system('echo %s|sudo -S python %s' % (password_for_sudo, current_script))
if __name__ == '__main__':
main()
Вывод:
$ python test.py
I'm just a user. Need to start new process with root privileges...
OK, I have root privileges. Calling the function...
I have the power!
$ sudo python test.py
OK, I have root privileges.
Calling the function...
I have the power!
Ответ 5
Мне нравится ответ L3viathan. Я бы добавил четвертый вариант: Используйте subprocess.Popen() для выполнения команды sh для записи файла. Что-то вроде subprocess.Popen('sudo echo \'{}\' > {}'.format(new_line, filename))
.