Удаление корневых разрешений в Python

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


person Jeremy    schedule 23.04.2010    source источник
comment
stackoverflow.com/questions/413807/   -  person Ian Bicking    schedule 23.04.2010
comment
В современном Linux вам нужна только возможность CAP_NET_BIND_SERVICE для привязки к порту 80, вам НЕ нужно быть пользователем root, даже при запуске приложения. Возможности - это стандарт POSIX, 1003.1e, который представляет собой разделение всех мощных привилегий root на набор отдельных привилегий. См .: python-cap-ng и / sbin / setcap, / sbin / getcap (они эквивалентны chmod setuid и ls –l)   -  person ctrl-alt-delor    schedule 17.09.2013
comment
Для Python2 и, возможно, других интерпретаторов, получение возможностей - это та часть, с которой вы должны быть осторожны: libcap-ng может отбрасывать заглавные буквы, но не предоставляет их. Этот ответ на вопрос, на который ссылается Ян, является относительно безопасным способом раздавать по одному ограничению за раз для конкретных проектов: stackoverflow.com/a/ 21895123/1724577   -  person duanev    schedule 21.02.2014


Ответы (6)


Вы не сможете открыть сервер на 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)
person Tamás    schedule 23.04.2010

Я рекомендую использовать authbind для запуска вашей программы Python, поэтому ни одна из них не должна запускаться от имени пользователя root.

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

person Allen    schedule 18.11.2011
comment
Пример использования authbind - goo.gl/fxFde6 - замените NodeJS на то, что вам нравится (например, python ) - person starlocke; 22.04.2015
comment
Пример на mutelight.org/authbind хорошо объясняет право собственности / разрешения на /etc/authbind/byport/80. - person mwfearnley; 15.03.2018

Не рекомендуется просить пользователя вводить свое имя пользователя и группу всякий раз, когда мне нужно отказаться от привилегий. Вот слегка измененная версия кода Тамаша, которая теряет привилегии и переключается на пользователя, инициировавшего команду sudo. Я предполагаю, что вы используете sudo (если нет, используйте код Тамаша).

#!/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())
person Autodidact    schedule 08.04.2014
comment
Мне это не нравится. Этот подход предполагает наличие SUDO_USER, который устанавливается, только если вы запускаете через sudo. Он не подходит для сброса привилегий при запуске через systemd или при запуске напрямую через root. Да, вы могли бы заставить его работать, установив SUDO_USER=someone, но это хитрость. Гораздо чаще демоны запускаются как root, выполняют привилегированную работу, необходимую им для настройки, а затем переходят к «никому» или пользователю, указанному в их конфигурации для своей повседневной работы. - person nevelis; 04.06.2019

  1. systemd может сделать это за вас, если вы запустите свою программу через systemd, systemd может передать ей уже открытый прослушивающий сокет, а также может активировать вашу программу при первом подключении. и вам даже не нужно демонизировать его.

  2. Если вы собираетесь использовать автономный подход, вам понадобится возможность CAP_NET_BIND_SERVICE (см. Справочную страницу о возможностях). Это может быть сделано для каждой программы с помощью правильного инструмента командной строки, или сделав ваше приложение (1) suid root (2) start up (3) прослушиванием порта (4) немедленно отбросив привилегии / возможности .

Помните, что программы suid root имеют множество соображений безопасности (чистая и безопасная среда, umask, привилегии, rlimits, все эти вещи, которые ваша программа должна будет настроить правильно). Если вы можете использовать что-то вроде systemd, тем лучше.

person Rudd-O    schedule 18.11.2011

Ниже приводится дальнейшая адаптация ответа Тамаша со следующими изменениями:

  • Используйте python-prctl модуль, чтобы сохранить возможности 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's 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])
person Craig McQueen    schedule 22.06.2017

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

Некоторое время назад я создал проект под названием tradesocket. Это позволяет передавать между процессами в системе posix сокеты вперед и назад. Что я делаю, так это вначале выделяю процесс, который остается суперпользователем, а остальная часть процесса падает в разрешениях, а затем запрашивает сокет у другого.

person Brantley Harris    schedule 19.11.2011