2014-02-02 05:48:05 +0000 2014-02-02 05:48:05 +0000
117
117

Autoriser le processus non racinaire à se lier aux ports 80 et 443 ?

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

La raison pour laquelle je demande cela est que je pense qu'il est insensé de permettre à un processus privilégié d'ouvrir une socket et d'écouter. Tout ce qui ouvre une socket et écoute est à haut risque, et les applications à haut risque ne devraient pas être exécutées en tant que root.

Je préfère de loin 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 se sont infiltrés avec les privilèges de root.

Réponses (5)

176
176
176
2015-03-21 21:12:41 +0000

Je ne suis pas sûr de savoir à quoi se réfèrent les autres réponses et commentaires ici. Cela est possible assez facilement. Il existe deux options, qui permettent toutes deux d'accéder à des ports de faible capacité sans avoir à élever le processus à la racine :

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

Avec cette option, vous pouvez accorder un accès permanent à un binaire spécifique à lier aux 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 .

Après avoir fait cela, /path/to/binary pourra se lier aux ports de faible numérotation. 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 plus fin de l'utilisateur/du groupe/de 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 autoriser l'accès aux ports concernés, par exemple pour autoriser 80 et 443 de tous les utilisateurs et groupes :

  3. Exécutez maintenant votre commande via authbind (en spécifiant éventuellement --deep ou d'autres arguments, voir la page de manuel) :


Il y a des avantages et des inconvénients aux deux éléments ci-dessus. L'option 1 accorde la confiance au binaire mais ne permet pas de contrôler l'accès par port. L'option 2 accorde la confiance au utilisateur/groupe et permet de contrôler l'accès par port, mais les anciennes versions ne supportaient qu'IPv4 (depuis que j'ai écrit ceci, de nouvelles versions avec support IPv6 ont été publiées).

29
29
29
2014-02-02 16:21:39 +0000

Dale Hagglund est dans le coup. Je vais donc dire la même chose, mais d'une manière différente, avec quelques détails et exemples. ☺

La bonne chose à faire dans le monde Unix et Linux est :

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

Vous avez une mauvaise idée de l'endroit où se situe le risque élevé. Le risque élevé se situe dans la lecture depuis le réseau et l'action sur ce qui est lu et non dans les simples actes d'ouvrir une socket, de la lier à un port et d'appeler listen(). C'est la partie d'un service qui effectue la communication proprement dite qui constitue le risque élevé. Les parties qui ouvrent, bind(), et listen(), et même (dans une certaine mesure) la partie qui accepts(), ne constituent pas le risque élevé et peuvent être exécutées sous l'égide du super utilisateur. Elles n'utilisent pas et n'agissent pas sur les données (à l'exception des adresses IP sources dans le cas de accept()) qui sont sous le contrôle d'étrangers non fiables sur le réseau.

Il existe de nombreuses façons de le faire.

inetd

Comme le dit Dale Hagglund, l'ancien “super serveur réseau” inetd fait cela. 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 d'écoute et la partie d'abandon des privilèges en deux programmes séparés, petits et facilement contrôlables, mais il sépare le code principal du service en un programme séparé, exec()ed dans un processus de service qu'il engendre avec un descripteur de fichier ouvert pour la socket.

La difficulté de l'audit n'est pas un problème majeur, car il suffit d'auditer un seul programme. Le problème majeur de inetd n'est pas tant l'audit mais plutôt le fait qu'il ne permet pas un contrôle fin et simple du service d'exécution, comparé à des outils plus récents.

UCSPI-TCP et démonstrateurs

Les paquets UCSPI-TCP et démonstrateurs de Daniel J. Bernstein ont été conçus pour faire cela 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 lier au port local privilégié est tcpserver , de l'UCSPI-TCP. Il fait à la fois le listen() et le accept().

tcpserver engendre alors soit un programme de service qui supprime les privilèges root lui-même (parce que le protocole servi implique de commencer comme super-utilisateur puis de se “connecter”, comme c'est le cas avec, par exemple, un démon FTP ou SSH) ou setuidgid qui est un petit programme autonome et facilement vérifiable qui ne fait que supprimer des privilèges et enchaîne ensuite les chargements au programme de service proprement dit (dont aucune partie ne fonctionne donc jamais avec des privilèges de super-utilisateur, comme c'est le cas, par exemple, de qmail-smtpd ).

Un script de service run serait donc par exemple (celui-ci pour dummyidentd pour fournir le service null IDENT) :

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

nosh

Mon paquet nosh est conçu pour faire cela. Il a un petit utilitaire setuidgid, comme les autres. Une légère différence est qu'il est utilisable avec les services “LISTEN_FDS” de type systemd ainsi qu'avec les services UCSPI-TCP, de sorte que le programme tcpserver traditionnel est remplacé par deux programmes distincts : tcp-socket-listen et tcp-socket-accept.

Là encore, les services à usage unique se développent et se chargent mutuellement. Une particularité intéressante de la conception est que l'on peut supprimer les privilèges de super-utilisateur après listen() mais avant même accept(). Voici un script run pour 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 fonctionnent sous l'égide du super-utilisateur sont les petits outils de chargement de chaîne de diagnostic de service fdmove, clearenv, envdir, softlimit, tcp-socket-listen, setuidgid, et sh. Au moment où smtp est lancé, la socket est ouverte et liée au port daemontools, et le processus n'a plus les privilèges du super-utilisateur.

s6, s6-networking, et execline

Les paquets s6 et s6-networking de Laurent Bercot ont été conçus pour faire cela conjointement. Les commandes sont structurellement très similaires à celles de run et de l'UCSPI-TCP. Les scripts

s6-tcpserver seraient à peu près les mêmes, sauf pour la substitution de tcpserver à s6-setuidgid et de setuidgid à chpst. Toutefois, on pourrait également choisir d'utiliser en même temps le jeu d'outils execline de M. Bercot.

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 de 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 programme ipsvd de Gerrit Pape est un autre ensemble d'outils qui fonctionne selon les mêmes principes que ucspi-tcp et s6-networking. Les outils sont tcpsvd et fnord 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 Les clients non fiables font toujours l'objet d'un programme distinct.

Voici l'exemple de M. Pape de l'exécution de run dans un script systemd :

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

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

Avec systemd on crée des fichiers de configuration pour définir une socket sur laquelle systemd écoute, et un service que systemd démarre. Le fichier “unité” du service a des paramètres qui permettent de contrôler le processus du service, y compris l'utilisateur sous lequel il fonctionne.

Avec cet utilisateur défini comme non-super-utilisateur, listen() fait tout le travail d'ouverture de la socket, la lie à un port, et appelle accept() (et, si nécessaire, 0x6&) dans le processus #1 en tant que super-utilisateur, et le processus de service qu'il engendre s'exécute sans privilèges de super-utilisateur.

17
17
17
2018-06-27 07:00:56 +0000

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 non-Sudo. J'ai essayé d'utiliser des liens symboliques, mais cela n'a pas fonctionné pour moi.

Puis j'ai appris que je pouvais transférer des connexions d'un port à un autre. J'ai donc démarré le serveur sur le port 3000 et j'ai configuré une redirection 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 elle a fonctionné pour moi. Je pense donc que c'est un moyen terme pour ne pas permettre aux processus utilisateurs d'accéder directement aux ports inférieurs, mais pour leur donner accès en utilisant la redirection de port.

4
4
4
2014-02-02 06:49:22 +0000

Votre instinct est tout à fait juste : c'est une mauvaise idée de faire tourner un grand programme complexe comme racine, car leur complexité les rend difficiles à croire.

Mais c'est aussi une mauvaise idée de permettre aux utilisateurs réguliers de se lier à des ports privilégiés, parce que 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 plusieurs) parties, chacune d'entre elles faisant une partie bien définie de l'application globale, et qui communiquent par des interfaces simples et limitées.

Dans l'exemple que vous donnez, vous voulez séparer votre programme en deux parties. L'une s'exécute en tant que root, s'ouvre et se lie à la socket privilégiée, puis la transmet d'une manière ou d'une autre à l'autre partie, qui s'exécute comme un utilisateur normal.

Ce sont les deux principales façons 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 la 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 tous les autres travaux. Abandonner des privilèges correctement est délicat, alors prenez le temps d'étudier la bonne façon de le faire.

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

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

3
3
3
2019-09-13 07:38:46 +0000

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

Fonctionne sur ubuntu/debian :

#save configuration permanently
echo 'net.ipv4.ip_unprivileged_port_start=0' > /etc/sysctl.d/50-unprivileged-ports.conf
#apply conf
sysctl --system

(fonctionne bien pour VirtualBox avec un compte non root)

Maintenant, attention à la sécurité car tous les utilisateurs peuvent lier tous les ports !