Я хочу, чтобы программа Python начала прослушивать порт 80, но после этого выполняйте без прав root. Есть ли способ сбросить корень или получить порт 80 без него?
Удаление разрешений root в Python
Ответ 1
Вы не сможете открыть сервер на порту 80 без привилегий root, это ограничение на уровне ОС. Таким образом, единственным решением является сброс прав root после того, как вы открыли порт.
Вот возможное решение для удаления привилегий root в Python: Удаление привилегий в Python. Это хорошее решение в целом, но вам также нужно добавить os.setgroups([])
в функцию, чтобы гарантировать, что членство в группе пользователя root не сохраняется.
Я скопировал и немного очистил код и удалил ведение журнала и обработчики исключений, поэтому вам нужно правильно обрабатывать OSError
(он будет запущен, когда процессу не будет разрешено переключать его эффективный UID или GID):
import os, pwd, grp
def drop_privileges(uid_name='nobody', gid_name='nogroup'):
if os.getuid() != 0:
# We're not root so, like, whatever dude
return
# Get the uid/gid from the name
running_uid = pwd.getpwnam(uid_name).pw_uid
running_gid = grp.getgrnam(gid_name).gr_gid
# Remove group privileges
os.setgroups([])
# Try setting the new uid/gid
os.setgid(running_gid)
os.setuid(running_uid)
# Ensure a very conservative umask
old_umask = os.umask(077)
Ответ 2
Я рекомендую использовать authbind
для запуска вашей программы Python, поэтому ни один из них не должен запускаться с правами root.
Ответ 3
Не рекомендуется просить пользователя вводить имя пользователя и группу, когда мне нужно отказаться от привилегий. Вот немного модифицированная версия кода Tamás, которая отбрасывает привилегии и переключается на пользователя, который инициировал команду sudo. Я предполагаю, что вы используете sudo (если нет, используйте код Tamás).
#!/usr/bin/env python3
import os, pwd, grp
#Throws OSError exception (it will be thrown when the process is not allowed
#to switch its effective UID or GID):
def drop_privileges():
if os.getuid() != 0:
# We're not root so, like, whatever dude
return
# Get the uid/gid from the name
user_name = os.getenv("SUDO_USER")
pwnam = pwd.getpwnam(user_name)
# Remove group privileges
os.setgroups([])
# Try setting the new uid/gid
os.setgid(pwnam.pw_gid)
os.setuid(pwnam.pw_uid)
#Ensure a reasonable umask
old_umask = os.umask(0o22)
#Test by running...
#./drop_privileges
#sudo ./drop_privileges
if __name__ == '__main__':
print(os.getresuid())
drop_privileges()
print(os.getresuid())
Ответ 4
-
systemd может сделать это за вас, если вы запустите свою программу через systemd, systemd может передать ему уже открытый слушающий сокет, и он также может активировать вашу программу при первом подключении. и вам даже не нужно его демонизировать.
-
Если вы собираетесь использовать автономный подход, вам нужна возможность CAP_NET_BIND_SERVICE (проверить возможности man-страницы). Это можно сделать по программному принципу с помощью правильного инструмента командной строки или сделать ваше приложение (1) suid root (2) start up (3) прослушать порт (4) немедленно удалить привилегии/возможности.
Помните, что в корневых программах suid есть много соображений безопасности (чистая и безопасная среда, umask, привилегии, rlimits, все это вещи, которые ваша программа должна будет правильно настроить). Если вы можете использовать что-то вроде systemd, тем лучше.
Ответ 5
Большая часть этих действий работает, если вам не нужно запрашивать сокет после того, как вы сделаете некоторые другие вещи, которые вы не хотите быть суперпользователем.
Я сделал проект под названием tradesocket некоторое время назад. Он позволяет передавать туда и обратно сокеты в системе posix между процессами. То, что я делаю, это отключение процесса в начале, который остается суперпользователем, а остальная часть процесса падает в разрешениях, а затем запрашивает сокет от другого.
Ответ 6
Ниже приведена дальнейшая адаптация ответа Tamás со следующими изменениями:
- Используйте
python-prctl
module, чтобы отбросить возможности Linux до указанного списка сохраняемых возможностей. - Пользователь может быть передан в качестве параметра (по умолчанию он ищет пользователя, который запускал
sudo
). - Он устанавливает все группы пользователей и
HOME
. - Он может изменять каталог.
(Я относительно новичок в использовании этой функции, поэтому я, возможно, что-то пропустил. Возможно, это не сработает на старых ядрах (< 3.8) или ядрах с ограниченными возможностями файловой системы.)
def drop_privileges(user=None, rundir=None, caps=None):
import os
import pwd
if caps:
import prctl
if os.getuid() != 0:
# We're not root
raise PermissionError('Run with sudo or as root user')
if user is None:
user = os.getenv('SUDO_USER')
if user is None:
raise ValueError('Username not specified')
if rundir is None:
rundir = os.getcwd()
# Get the uid/gid from the name
pwnam = pwd.getpwnam(user)
if caps:
prctl.securebits.keep_caps=True
prctl.securebits.no_setuid_fixup=True
# Set user group privileges
os.setgroups(os.getgrouplist(pwnam.pw_name, pwnam.pw_gid))
# Try setting the new uid/gid
os.setgid(pwnam.pw_gid)
os.setuid(pwnam.pw_uid)
os.environ['HOME'] = pwnam.pw_dir
os.chdir(os.path.expanduser(rundir))
if caps:
prctl.capbset.limit(*caps)
try:
prctl.cap_permitted.limit(*caps)
except PermissionError:
pass
prctl.cap_effective.limit(*caps)
#Ensure a reasonable umask
old_umask = os.umask(0o22)
Его можно использовать следующим образом:
drop_privileges(user='www', rundir='~', caps=[prctl.CAP_NET_BIND_SERVICE])