238 votes

Autoriser un processus non root à se lier aux ports 80 et 443?

Est-il possible de régler un paramètre du noyau pour permettre à un programme utilisateur de se lier aux ports 80 et 443 ?

La raison pour laquelle je demande est que je pense qu'il est absurde de permettre à un processus privilégié d'ouvrir un socket et d'écouter. Tout ce qui ouvre un socket et écoute présente un risque élevé, et les applications à haut risque ne devraient pas s'exécuter en tant que root.

Je préférerais beaucoup essayer de déterminer quel processus non privilégié écoute sur le port 80 plutôt que d'essayer de supprimer un logiciel malveillant qui s'est infiltré avec des privilèges root.

20 votes

La réponse longue est oui... donc la réponse courte devrait aussi être oui.

6 votes

La réponse courte est oui.

1 votes

Utilisez la réponse de noob qui utilise iptables pour rediriger le trafic du port. La solution la plus simple de loin, et facile à annuler si nécessaire.

319voto

Cédric Points 164

Je ne suis pas sûr de ce à quoi les autres réponses et commentaires ici font référence. C'est possible assez facilement. Il y a deux options, toutes deux permettant l'accès aux ports de numéro bas sans avoir à élever le processus en tant que root:

Option 1: Utiliser CAP_NET_BIND_SERVICE pour accorder l'accès aux ports de numéro bas à un processus:

Avec cela, vous pouvez accorder un accès permanent à un binaire spécifique pour lier des ports de numéro bas via la commande setcap:

sudo setcap CAP_NET_BIND_SERVICE=+eip /chemin/vers/le/binaire

Pour plus de détails sur la partie e/i/p, voir cap_from_text.

Après avoir fait cela, /chemin/vers/le/binaire pourra se lier à des ports de numéro bas. Notez que vous devez utiliser setcap sur le binaire lui-même plutôt que sur un lien symbolique.

Option 2: Utiliser authbind pour accorder un accès ponctuel, avec un contrôle plus précis sur l'utilisateur/groupe/port:

L'outil authbind (page de manuel) existe précisément pour cela.

  1. Installez authbind en utilisant votre gestionnaire de paquets préféré.

  2. Configurez-le pour accorder l'accès aux ports pertinents, par exemple, pour autoriser 80 et 443 à partir de tous les utilisateurs et groupes:

    sudo touch /etc/authbind/byport/80
    sudo touch /etc/authbind/byport/443
    sudo chmod 777 /etc/authbind/byport/80
    sudo chmod 777 /etc/authbind/byport/443
  3. Exécutez maintenant votre commande via authbind (en spécifiant éventuellement --deep ou d'autres arguments, voir la page de manuel):

    authbind --deep /chemin/vers/le/binaire arguments de la ligne de commande

    Par exemple

    authbind --deep java -jar SomeServer.jar

Il y a des avantages et des inconvénients aux deux options ci-dessus. L'option 1 accorde la confiance au binaire mais ne fournit pas de contrôle sur l'accès par port. L'option 2 accorde la confiance à l'utilisateur/groupe et fournit un contrôle sur l'accès par port mais les versions plus anciennes ne prenaient en charge que IPv4 (depuis que j'ai écrit ceci, de nouvelles versions avec prise en charge d'IPv6 ont été publiées).

3 votes

Est-ce que cela a vraiment besoin de la permission rwx ?

1 votes

Pour revenir en arrière avec l'opération dans l'Option 1, exécuteriez-vous à nouveau la commande en utilisant -p au lieu de +eip ?

0 votes

@eugene1832 Cela devrait être suffisant (et vous pourriez également faire -e pour par exemple désactiver la capacité mais laisser quand même dans l'ensemble autorisé). Voir kernel.org/pub/linux/libs/security/linux-privs/kernel-2.2/… question n°2 pour un peu plus d'informations sur la façon dont les capacités effectives et permises sont combinées. Vous devriez prendre la décision en fonction de votre situation.

60voto

noob Points 1109

J'ai une approche assez différente. Je voulais utiliser le port 80 pour un serveur node.js. Je n'ai pas réussi car Node.js était installé pour un utilisateur non-sudo. J'ai essayé d'utiliser des liens symboliques, mais cela n'a pas fonctionné pour moi.

Ensuite, j'ai découvert que je pouvais rediriger les connexions d'un port vers un autre. J'ai donc démarré le serveur sur le port 3000 et mis en place une redirection de port du port 80 vers le port 3000.

Ce lien fournit les commandes réelles qui peuvent être utilisées pour ce faire. Voici les commandes -

localhost/loopback

sudo iptables -t nat -I OUTPUT -p tcp -d 127.0.0.1 --dport 80 -j REDIRECT --to-ports 3000

externe

sudo iptables -t nat -I PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 3000

J'ai utilisé la deuxième commande et cela a fonctionné pour moi. Je pense donc que c'est un compromis pour ne pas permettre aux processus utilisateur d'accéder directement aux ports inférieurs, mais de leur permettre l'accès via la redirection de port.

0 votes

nginx est aussi une excellente option dans ce sens; facile à configurer et très puissant.

0 votes

@JasonC Je suis d'accord! C'est plus déclaratif et mieux pris en charge et à ma connaissance nginx utilise également le port-forward.

0 votes

Question rapide : J'ai exécuté la deuxième commande il y a un certain temps, et je cherche à la supprimer, car j'ai écrit un serveur de transfert automatique / répartiteur de charge, et je voudrais le déployer sous le port 80.

46voto

soleuu Points 319

Solution la plus simple : supprimer tous les ports privilégiés sur linux

Fonctionne sur ubuntu/debian :

# enregistrer la configuration de manière permanente
echo 'net.ipv4.ip_unprivileged_port_start=0' > /etc/sysctl.d/50-unprivileged-ports.conf
# appliquer la configuration
sysctl --system

(fonctionne bien pour VirtualBox avec un compte non-root)

Maintenant, soyez prudent en matière de sécurité car tous les utilisateurs peuvent lier tous les ports !

2 votes

C'est intelligent. Un petit inconvénient : la configuration ouvre les ports 80 et 443, mais elle ouvre également tous les autres ports. Détendre les autorisations sur les autres ports peut ne pas être souhaitable.

0 votes

Belle solution. Je l'ai utilisée pour IPv6 et ça fonctionne parfaitement. Voici ce que j'ai fait : docs.google.com/document/d/e/…

0 votes

Semble être la solution la plus simple, y a-t-il un moyen d'ouvrir uniquement 80 et 443 à un certain groupe ?

41voto

JdeBP Points 25711

Dale Hagglund a raison. Alors je vais juste dire la même chose mais d'une manière différente, avec des détails et des exemples. ☺

La bonne chose à faire dans les mondes Unix et Linux est :

  • d'avoir un petit programme, simple, facilement vérifiable, qui s'exécute en tant que super-utilisateur et lie le socket d'écoute ;
  • d'avoir un autre petit programme, simple, facilement vérifiable, qui supprime les privilèges, engendré par le premier programme ;
  • pour avoir la viande du service, dans un endroit séparé. troisième exécuté sous un compte non superutilisateur et chargé en chaîne par le second programme, s'attendant à hériter simplement d'un descripteur de fichier ouvert pour la socket.

Vous avez une mauvaise idée de l'endroit où se trouve le haut risque. Le risque élevé est dans lecture du réseau et action sur ce qui est lu pas dans les actes simples d'ouverture d'un socket, de liaison à un port, et d'appel listen() . C'est la partie d'un service qui fait la communication réelle qui est à haut risque. Les parties qui s'ouvrent, bind() y listen() et même (dans une certaine mesure) la partie qui accepts() ne présentent pas de risque élevé et peuvent être exécutés sous l'égide du superutilisateur. Ils n'utilisent pas et n'agissent pas sur (à l'exception des adresses IP sources dans le cadre de la accept() cas) des données qui sont sous le contrôle d'étrangers non fiables sur le réseau.

Il existe de nombreuses façons de procéder.

inetd

Comme le dit Dale Hagglund, le vieux "superserveur de réseau" inetd fait ceci. Le compte sous lequel le processus de service est exécuté est l'une des colonnes du tableau suivant inetd.conf . Il ne sépare pas la partie écoute et la partie abandon de privilèges en deux programmes distincts, petits et facilement auditables, mais il sépare le code de service principal en un programme distinct, exec() ed dans un processus de service qu'il génère avec un descripteur de fichier ouvert pour la socket.

La difficulté de l'audit n'est pas si importante, car il suffit d'auditer un seul programme. inetd Le principal problème de l'entreprise n'est pas tant l'audit que le fait qu'elle ne fournisse pas un contrôle simple et fin des services d'exécution, par rapport à des outils plus récents.

UCSPI-TCP et daemontools

Daniel J. Bernstein UCSPI-TCP y daemontools ont été conçus pour faire cela conjointement. On peut également utiliser la méthode largement équivalente de Bruce Guenter daemontools-encore d'outils.

Le programme permettant d'ouvrir le descripteur de fichier de la socket et de se lier au port local privilégié est le suivant tcpserver de l'UCSPI-TCP. Il fait à la fois le listen() und die accept() .

tcpserver génère alors un programme de service qui perd lui-même les privilèges de l'utilisateur root (parce que le protocole servi implique de commencer en tant que superutilisateur puis de se connecter, comme c'est le cas, par exemple, avec un démon FTP ou SSH) ou setuidgid qui est un petit programme autonome et facilement vérifiable qui abandonne uniquement les privilèges et charge ensuite en chaîne le programme de service proprement dit (dont aucune partie ne s'exécute donc jamais avec les privilèges du superutilisateur, comme c'est le cas avec, disons, qmail-smtpd ).

Un service run script serait donc par exemple (celui-ci pour tétined pour la fourniture du service IDENT nul) :

#!/bin/sh -e
exec 2>&1
exec \
tcpserver 0 113 \
setuidgid nobody \
dummyidentd.pl

sniffer

Mon paquet de snacks est conçu à cet effet. Il dispose d'un petit setuidgid utilitaire, tout comme les autres. Une petite différence est qu'il est utilisable avec systemd -LISTEN_FDS " ainsi qu'avec les services UCSPI-TCP. tcpserver est remplacé par deux programmes distincts : tcp-socket-listen y tcp-socket-accept .

Là encore, les utilitaires à usage unique se multiplient et s'enchaînent les uns aux autres. Une bizarrerie intéressante de la conception est que l'on peut abandonner les privilèges de super-utilisateur après que listen() mais avant même accept() . Voici un run scriptpour qmail-smtpd qui fait exactement cela :

#!/bin/nosh
fdmove -c 2 1
clearenv --keep-path --keep-locale
envdir env/
softlimit -m 70000000
tcp-socket-listen --combine4and6 --backlog 2 ::0 smtp
setuidgid qmaild
sh -c 'exec \
tcp-socket-accept -v -l "${LOCAL:-0}" -c "${MAXSMTPD:-1}" \
ucspi-socket-rules-check \
qmail-smtpd \
'

Les programmes qui s'exécutent sous l'égide du super-utilisateur sont les petits outils de chargement de chaîne agnostique de services. fdmove , clearenv , envdir , softlimit , tcp-socket-listen y setuidgid . Au point que sh est lancé, le socket est ouvert et lié à l'interface de l'utilisateur. smtp et le processus n'a plus les privilèges du super-utilisateur.

s6, s6-networking, et execline

Laurent Bercot s6 y s6-réseaux ont été conçus conjointement pour cela. Les commandes sont structurellement très similaires à celles de daemontools et UCSPI-TCP.

run scripts seraient à peu près les mêmes, sauf pour la substitution de s6-tcpserver para tcpserver y s6-setuidgid para setuidgid . Cependant, on peut également choisir d'utiliser la méthode de M. Bercot. execline d'outils en même temps.

Voici un exemple de service FTP, légèrement modifié à partir de L'original de Wayne Marshall qui utilise execline, s6, s6-networking, et le programme de serveur FTP de fichier public :

#!/command/execlineb -PW
multisubstitute {
    define CONLIMIT 41
    define FTP_ARCHIVE "/var/public/ftp"
}
fdmove -c 2 1
s6-envuidgid pubftp 
s6-softlimit -o25 -d250000 
s6-tcpserver -vDRH -l0 -b50 -c ${CONLIMIT} -B '220 Features: a p .' 0 21 
ftpd ${FTP_ARCHIVE}

ipsvd

Gerrit Pape ipsvd est un autre jeu d'outils qui fonctionne sur le même modèle que ucspi-tcp et s6-networking. Les outils sont chpst y tcpsvd cette fois, mais ils font la même chose, et le code à haut risque qui fait la lecture, le traitement et l'écriture des choses envoyées sur le réseau par des clients non fiables est toujours dans un programme séparé.

Voici M. L'exemple de Pape de la course fnord dans un run script :

#!/bin/sh
exec 2>&1
cd /public/10.0.5.4
exec \
chpst -m300000 -Uwwwuser \
tcpsvd -v 10.0.5.4 443 sslio -v -unobody -//etc/fnord/jail -C./cert.pem \
fnord

systemd

systemd le nouveau système de supervision des services et le système init que l'on trouve dans certaines distributions Linux, est destiné à faire ce que inetd peut faire . Cependant, il n'utilise pas une suite de petits programmes autonomes. On doit vérifier systemd dans son intégralité, malheureusement.

Avec systemd on crée des fichiers de configuration pour définir un socket qui systemd écoute, et un service qui systemd démarre. Le fichier "unit" du service contient des paramètres qui permettent de contrôler en grande partie le processus du service, y compris l'utilisateur sous lequel il est exécuté.

Avec cet utilisateur configuré pour être un non-superutilisateur, systemd fait tout le travail d'ouverture de la socket, la lie à un port, et appelle listen() (et, si nécessaire, accept() ) dans le processus #1 en tant que superutilisateur, et le processus de service qu'il génère s'exécute sans les privilèges du superutilisateur.

3 votes

Merci pour le compliment. Il s'agit d'une excellente collection de conseils concrets. +1.

1 votes

Tant de lecture... Je veux juste servir des fichiers statiques

0 votes

Peut-être pourriez-vous ajouter la phrase activation du socket ? Je pense que c'est le terme commun pour désigner cette fonctionnalité de systemd.

6voto

Andreas Holstenson Points 1198

Vos instincts sont entièrement corrects : c'est une mauvaise idée d'avoir un programme complexe et volumineux s'exécuter en tant que root, car leur complexité les rend difficiles à faire confiance.

Mais, il est également une mauvaise idée de permettre aux utilisateurs réguliers de se lier à des ports privilégiés, car ces ports représentent généralement des services système importants.

L'approche standard pour résoudre cette contradiction apparente est _la séparation des privilèges_. L’idée de base est de séparer votre programme en deux (ou plus) parties, chacune réalisant un morceau bien défini de l'application globale, et communiquant par des interfaces simples et limitées.

Dans l'exemple que vous donnez, vous voulez diviser votre programme en deux parties. Une qui s'exécute en tant que root et ouvre et se lie au socket privilégié, puis le transmet d'une manière ou d'une autre à l'autre partie, qui s'exécute en tant qu'utilisateur régulier.

Il y a deux façons principales de réaliser cette séparation.

  1. Un seul programme qui démarre en tant que root. La toute première chose qu'il fait est de créer le socket nécessaire, de la manière la plus simple et limitée possible. Ensuite, il abandonne les privilèges, c'est-à-dire qu'il se transforme en un processus en mode utilisateur régulier, et effectue tout le reste du travail. Abandonner correctement les privilèges est délicat, alors prenez le temps d'étudier la bonne manière de le faire.

  2. Une paire de programmes qui communiquent sur une paire de sockets créée par un processus parent. Un programme pilote non privilégié reçoit les arguments initiaux et effectue peut-être une validation basique des arguments. Il crée une paire de sockets connectés via socketpair(), puis il bifurque et exécute deux autres programmes qui effectueront le vrai travail, et communiqueront via la paire de sockets. L'un de ces programmes est privilégié et créera le socket serveur, et effectuera toutes les autres opérations privilégiées, et l'autre réalisera l'exécution de l'application plus complexe et donc moins digne de confiance.

1 votes

Ce que vous proposez n'est pas considéré comme une pratique optimale. Vous pourriez envisager inetd, qui peut écouter sur un socket privilégié puis transmettre ce socket à un programme non privilégié.

0 votes

Sans doute de bons conseils si vous concevez le programme. Si vous voulez simplement exécuter un programme qui accepte un port en argument, que feriez-vous ensuite?

0 votes

@jontejj Juste pour être sûr que je suis clair, tu parles d'un programme qui accepte un numéro de port à écouter via la ligne de commande? Je commencerais par voir s'il y avait un moyen d'utiliser un port non privilégié, pour éviter d'avoir besoin de privilèges root. Il pourrait y avoir un moyen d'utiliser des outils de capacité Linux pour assigner juste le droit d'ouvrir des ports privilégiés lorsque vous exécutez le programme.

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X