web-dev-qa-db-fra.com

Autoriser les processus non root à se lier au port 80 et 443?

Est-il possible d'ajuster un paramètre de noyau pour permettre à un programme utilisateur de se lier au port 80 et 443?

La raison pour laquelle je pose cette question est qu’il est idiot 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 doivent pas être exécutées en tant que root.

Je préférerais de beaucoup essayer de déterminer quel processus non privilégié écoute sur le port 80 plutôt que d'essayer de supprimer les logiciels malveillants qui s'y enfoncent avec des privilèges root.

89
jww

Je ne suis pas sûr de ce que les autres réponses et commentaires ici font référence. C'est possible assez facilement. Il existe deux options, toutes deux permettant l’accès à des ports peu nombreux sans avoir à élever le processus à la racine:

Option 1: utilisez CAP_NET_BIND_SERVICE pour accorder un accès au port avec un numéro bas à un processus:

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

Sudo setcap CAP_NET_BIND_SERVICE=+eip /path/to/binary

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

Cela fait, /path/to/binary pourra se lier aux ports dont le numéro est bas. Notez que vous devez utiliser setcap sur le binaire lui-même plutôt qu'un lien symbolique.

Option 2: Utilisez authbind pour accorder un accès unique, avec un contrôle utilisateur/groupe/port plus précis:

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 appropriés, par exemple. pour autoriser 80 et 443 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. Maintenant exécutez votre commande via authbind (en spécifiant éventuellement --deep ou d’autres arguments, voir la page de manuel):

    authbind --deep /path/to/binary command line args
    

    Par exemple.

    authbind --deep Java -jar SomeServer.jar
    

Il y a des avantages et des inconvénients à la fois de ce qui précède. L'option 1 accorde la confiance au binaire mais ne fournit aucun contrôle sur l'accès par port. L'option 2 accorde la confiance à utilisateur/groupe et permet de contrôler l'accès par port, mais AFAIK ne prend en charge que IPv4.

138
Jason C

Dale Hagglund est sur place. Je vais donc simplement 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 la suivante:

  • avoir un petit programme simple, facilement vérifiable, qui fonctionne en tant que superutilisateur et lie le socket en écoute;
  • avoir un autre petit programme simple, facilement vérifiable, qui supprime les privilèges créés par le premier programme;
  • avoir la viande du service, dans un programme third séparé, exécutée sous un compte non superutilisateur et une chaîne chargée par le second programme, en espérant simplement hériter d'un descripteur de fichier ouvert pour le socket.

Vous avez une mauvaise idée de l'endroit où le risque est élevé. Le risque élevé réside dans lecture sur le réseau et en agissant sur ce qui est lu pas dans les simples actions d'ouvrir un socket, de le lier à un port et d'appeler listen(). C'est la partie d'un service qui fait la communication réelle qui est le risque élevé. Les parties qui s'ouvrent, bind() et listen(), et même (dans une certaine mesure) la partie qui accepts() ne constituent pas un risque élevé et peuvent être exécutées sous l'égide du superutilisateur. Ils n'utilisent pas et ne traitent pas (à l'exception des adresses IP source dans le cas accept()) les données qui sont sous le contrôle d'inconnus inconnus sur le réseau.

Il y a plusieurs façons de le faire.

inetdname__

Comme le dit Dale Hagglund, l'ancien "réseau superserver" inetdle fait. Le compte sous lequel le processus de service est exécuté est l’une des colonnes de inetd.conf. Il ne sépare pas la partie écoute et la partie suppression des privilèges en deux programmes distincts, petits et faciles à contrôler, mais il sépare le code de service principal en un programme séparé, exec()ed, dans un processus de service créé avec un descripteur de fichier ouvert. pour la prise.

La difficulté de l'audit n'est pas un problème, il suffit de vérifier le programme en question. inetdname Le problème majeur de __ n'est pas tant l'audit, mais plutôt le fait qu'il ne fournit pas un contrôle de service d'exécution à granularité fine, comparé aux outils plus récents.

UCSPI-TCP et daemontools

Les paquets UCSPI-TCP et daemontools de Daniel J. Bernstein ont été conçus pour le faire conjointement. On peut également utiliser le jeu d'outils daemontools-encore largement équivalent de Bruce Guenter.

Le programme permettant d'ouvrir le descripteur de fichier de socket et de se connecter au port local privilégié est tcpserverNAME_ , à partir de UCSPI-TCP. Il effectue à la fois la listen() et la accept().

tcpservergénère ensuite un programme de service qui supprime lui-même les privilèges root (car le protocole utilisé implique de démarrer en tant que superutilisateur puis de se "connecter", comme c'est le cas par exemple avec un démon FTP ou SSH) ou setuidgidNAME_ qui est un petit programme autonome et facilement vérifiable qui supprime uniquement les privilèges, puis enchaîne les charges vers le programme de service proprement dit (aucune partie de ce programme ne s'exécute donc avec des privilèges de superutilisateur, comme c'est le cas avec qmail-smtpd ).

Un script de service runserait donc par exemple (celui-ci pour dummyidentd pour la fourniture d'un service null IDENT):

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

bouffe

My nosh package est conçu pour cela. Il a un petit utilitaire setuidgidname__, comme les autres. Une légère différence réside dans le fait qu’il est utilisable avec les services "LISTEN_FDS" ainsi que les services UCSPI-TCP de la même manière que le programme systemdname __- style. Le programme tcpservertraditionnel est remplacé par deux programmes distincts: tcp-socket-listen et tcp-socket-accept.

Encore une fois, les utilitaires à but unique apparaissent et se chargent en chaîne. Une particularité intéressante de la conception est que l'on peut supprimer les privilèges de superutilisateur après listen() mais avant même accept(). Voici un script runpour 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 superutilisateur sont les petits outils de chargement de chaîne indépendants du service fdmovename__, clearenvname__, envdirname__, softlimitname__, tcp-socket-listen et setuidgidname__. Au moment où shest démarré, le socket est ouvert et lié au port smtpet le processus ne dispose plus des privilèges de superutilisateur.

s6, s6-networking et execline

Les packages s6 _ et s6-networking de Laurent Bercot ont été conçus pour le faire conjointement. Les commandes sont structurellement très similaires à celles de daemontoolset UCSPI-TCP.

Les scripts runseraient sensiblement les mêmes, à l'exception de la substitution de s6-tcpserver pour tcpserveret s6-setuidgid pour setuidgidname__. Cependant, on pourrait aussi choisir d'utiliser le jeu d'outils execline de M. Bercot en même temps.

Voici un exemple de service FTP légèrement modifié par rapport à l'original de Wayne Marshall , qui utilise execline, s6, s6-networking et le programme du serveur FTP de publicfile :

#!/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

Le ipsvd de Gerrit Pape est un autre ensemble d'outils qui va dans le même sens que ucspi-tcp et s6-networking. Les outils sont chpstet tcpsvdcette fois, mais ils font la même chose, et le code à haut risque qui lit, traite et écrit les éléments envoyés sur le réseau par des clients non fiables est toujours dans un programme séparé.

Voici exemple de M. Pape sur _ { fnordNAME_ _ dans un script runname__:

#!/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

systemdname__

systemdNAME_ , le nouveau système de supervision du service et d'init disponible dans certaines distributions Linux, est destiné à faire ce que inetdpeut faire }. Cependant, il n’utilise pas une suite de petits programmes autonomes. Il faut malheureusement auditer systemddans son intégralité.

Avec systemdname__, on crée des fichiers de configuration pour définir un socket que systemdécoute et un service qui systemddémarre. Le fichier "unité" de service contient des paramètres qui permettent de contrôler le processus de service, y compris son utilisateur.

systemdeffectue tout le travail d'ouverture du socket en le liant à un port et en appelant listen() (et, si nécessaire, accept()) dans le processus n ° 1 en tant que superutilisateur et le service. Le processus qu’il génère s’exécute sans les privilèges de superutilisateur.

25
JdeBP

J'ai une approche assez différente. Je voulais utiliser le port 80 pour un serveur node.js. Je n'ai pas pu le faire car Node.js a été installé pour un utilisateur autre que Sudo. J'ai essayé d'utiliser des liens symboliques, mais cela n'a pas fonctionné pour moi.

Ensuite, j'ai appris que je pouvais transférer les connexions d'un port à un autre. J'ai donc démarré le serveur sur le port 3000 et configuré un transfert de port du port 80 au port 3000.

Ce lien fournit les commandes qui peuvent être utilisées à cette fin. 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 moyen terme pour ne pas autoriser les processus utilisateur à accéder directement aux ports inférieurs, mais leur permettre d'accéder via le transfert de port.

7
noob

Votre instinct est tout à fait correct: c’est une mauvaise idée d’avoir un grand programme complexe exécuté en tant que root, car leur complexité les rend difficiles à faire confiance.

Cependant, il est également déconseillé de permettre aux utilisateurs normaux de se connecter à 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 séparation des privilèges . L'idée de base est de séparer votre programme en deux parties (ou plus), chacune d'entre elles constituant une partie bien définie de l'application globale et communiquant par le biais d'interfaces limitées.

Dans l'exemple que vous donnez, vous voulez séparer votre programme en deux parties. Celui qui s'exécute en tant que root s'ouvre et se connecte au socket privilégié, puis le transfère d'une manière ou d'une autre à l'autre partie, qui s'exécute en tant qu'utilisateur normal.

Ces deux moyens principaux pour réaliser cette séparation.

  1. Un seul programme qui commence en tant que root. La première chose à faire est de créer le socket nécessaire, de la manière la plus simple et la plus limitée possible. Ensuite, il supprime les privilèges, c’est-à-dire qu’il se convertit en un processus normal en mode utilisateur et effectue tout autre travail. Il est difficile de supprimer des privilèges correctement. Veuillez donc prendre le temps d’étudier la meilleure façon de le faire.

  2. Une paire de programmes qui communiquent via une paire de sockets créée par un processus parent. Un programme de pilote non privilégié reçoit les arguments initiaux et effectue peut-être une validation de base des arguments. Il crée une paire de sockets connectés via socketpair (), puis lance et exécute deux autres programmes qui feront le vrai travail et communiqueront via la paire de sockets. L'une d'elles est privilégiée et créera le socket du serveur, ainsi que toute autre opération privilégiée, et l'autre effectuera l'exécution d'application la plus complexe et donc la moins digne de confiance.

[1] http://fr.m.wikipedia.org/wiki/Privilege_separation

4
Dale Hagglund