47 votes

Suppression des permissions de la racine en Python

J'aimerais qu'un programme Python commence à écouter le port 80, mais qu'il s'exécute ensuite sans les autorisations de Root. Existe-t-il un moyen de supprimer Root ou d'obtenir le port 80 sans lui ?

3 votes

4 votes

Sous Linux moderne, vous n'avez besoin que de la capacité CAP_NET_BIND_SERVICE pour vous lier au port 80, vous n'avez PAS besoin d'être Root, pas même au démarrage de l'application. Les capacités sont une norme POSIX, 1003.1e, qui consiste à diviser le tout puissant privilège Root en un ensemble de privilèges distincts. Voir : python-cap-ng Et /sbin/setcap, /sbin/getcap (équivalents à chmod setuid, et ls -l)

0 votes

Pour Python2 et peut-être d'autres interprètes, l'acquisition de capacités est la partie avec laquelle il faut être prudent -- libcap-ng peut supprimer des caps mais ne les accorde pas. Cette réponse à la question posée par Ian est un moyen relativement sûr de distribuer un cap à la fois pour des projets spécifiques : stackoverflow.com/a/21895123/1724577

58voto

Tamás Points 18211

Vous ne pourrez pas ouvrir un serveur sur le port 80 sans les privilèges Root, il s'agit d'une restriction au niveau du système d'exploitation. La seule solution est donc de supprimer les privilèges Root après avoir ouvert le port.

Voici une solution possible pour supprimer les privilèges Root dans Python : Suppression de privilèges en Python . C'est une bonne solution en général, mais vous devrez également ajouter os.setgroups([]) à la fonction pour s'assurer que l'appartenance au groupe de l'utilisateur racine n'est pas conservée.

J'ai copié et nettoyé un peu le code, et j'ai supprimé la journalisation et les gestionnaires d'exception pour vous laisser le soin de les gérer. OSError correctement (elle sera levée si le processus n'est pas autorisé à changer son UID ou GID effectif) :

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 votes

Gardez à l'esprit que le répertoire HOME sera toujours /root et non /home/uid_name et que uid_name ne pourra rien faire avec ~/ qui s'étend ensuite à /root/ . Cela peut affecter des modules comme matplotlib qui stockent les données de configuration dans le répertoire HOME. authbind semble être la bonne façon de traiter ce problème.

1 votes

Et la variable HOME ne sera pas la seule à penser que l'utilisateur actuel est Root.

1 votes

"Vous ne pourrez pas ouvrir un serveur sur le port 80 sans les privilèges Root..." - Ce n'est pas nécessairement vrai (peut-être plus ?). Voir aussi Permettre à un processus non-Root de se lier aux ports 80 et 443 ? sur SuperUser et Existe-t-il un moyen pour les processus non-Root de se lier à des ports "privilégiés" sous Linux ?

12voto

Allen Points 3497

Je recommande d'utiliser authbind pour lancer votre programme Python, de sorte qu'aucune partie de celui-ci n'ait à s'exécuter en tant que Root.

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

0 votes

Un exemple d'utilisation authbind - goo.gl/fxFde6 - Remplacer NodeJS par ce que vous voulez (ex : python)

0 votes

L'exemple de mutelight.org/authbind fait un bon travail d'explication de la propriété et des autorisations sur le site de la /etc/authbind/byport/80 .

7voto

Sandeep Datta Points 7344

Ce n'est pas une bonne idée de demander à l'utilisateur de saisir son nom d'utilisateur et son groupe chaque fois que je dois supprimer des privilèges. Voici une version légèrement modifiée du code de Tamás qui supprimera les privilèges et passera à l'utilisateur qui a lancé la commande sudo. Je suppose que vous utilisez sudo (si ce n'est pas le cas, utilisez le code de 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())

0 votes

Je n'aime pas cela. Cette approche suppose l'existence de SUDO_USER, qui n'est défini que si vous utilisez sudo. Elle n'est pas adaptée à la suppression des privilèges lors du démarrage via systemd ou si vous vous exécutez directement via Root. Oui, vous pouvez faire fonctionner cette approche en définissant SUDO_USER=someone mais il s'agit d'un piratage. Il est beaucoup plus courant que les démons démarrent en tant que Root, fassent le travail privilégié dont ils ont besoin pour mettre les choses en place, puis passent à 'nobody' ou à un utilisateur spécifié via leur configuration pour leur travail quotidien.

5voto

Rudd-O Points 66
  1. systemd peut le faire pour vous, si vous démarrez votre programme via systemd, systemd peut lui transférer le socket d'écoute déjà ouvert, et il peut également activer votre programme à la première connexion. et vous n'avez même pas besoin de le démoniser.

  2. Si vous optez pour l'approche autonome, vous avez besoin de la capacité CAP_NET_BIND_SERVICE (consultez la page de manuel des capacités). Cela peut être fait programme par programme avec l'outil de ligne de commande approprié, ou en faisant en sorte que votre application (1) soit suid Root (2) démarre (3) écoute le port (4) abandonne immédiatement les privilèges/capacités.

Rappelez-vous que les programmes suid Root sont accompagnés de nombreuses considérations de sécurité (environnement propre et sécurisé, umask, privilèges, rlimits, toutes ces choses sont des éléments que votre programme devra configurer correctement). Si vous pouvez utiliser quelque chose comme systemd, c'est encore mieux.

2voto

Brantley Harris Points 157

La plupart de ces méthodes fonctionnent, à moins que vous n'ayez besoin de demander le socket après avoir fait d'autres choses pour lesquelles vous ne voulez pas être super-utilisateur.

J'ai créé un projet intitulé pochette d'échange il y a quelque temps. Il permet de passer des sockets entre processus sur un système posix. Ce que je fais, c'est que je crée un processus au début qui reste superutilisateur, et le reste du processus descend dans les permissions et demande ensuite la socket à l'autre.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X