Подтвердить что ты не робот

Удаление разрешений root в Python

Я хочу, чтобы программа Python начала прослушивать порт 80, но после этого выполняйте без прав root. Есть ли способ сбросить корень или получить порт 80 без него?

4b9b3361

Ответ 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.

https://en.wikipedia.org/wiki/Authbind

Ответ 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])