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


Est-il possible d'ajuster un paramètre du noyau pour permettre à un programme utilisateur de se connecter aux ports 80 et 443?

La raison pour laquelle je pose la question est que je trouve cela stupide de permettre à un processus privilégié d’ouvrir un socket et d’écouter. Tout ce qui ouvre un socket et écoute est à haut risque, et les applications à haut risque ne doivent pas être exécutées en tant que root.

Je préférerais plutôt essayer de savoir quel processus non privilégié est à l'écoute sur le port 80 plutôt que d'essayer de supprimer les logiciels malveillants enfouis sous les privilèges root.


66
2018-02-02 05:48


origine


Voir serverfault.com/questions/268099 et stackoverflow.com/questions/413807 . La réponse courte est non. - Sami Laine
La longue réponse est oui. La réponse courte devrait donc être oui. - B T
La réponse courte est Oui. - Jason C


Réponses:


Je ne sais pas à quoi les autres réponses et commentaires font référence. Ceci est possible assez facilement. Deux options permettent d’accéder à des ports à faible nombre de numéros sans avoir à élever le processus à la racine:

Option 1: utiliser CAP_NET_BIND_SERVICE accorder un accès de port à un numéro peu élevé à un processus:

Avec cela, vous pouvez accorder un accès permanent à un fichier binaire spécifique pour vous connecter à des ports de faible nombre via le setcap commander:

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 cela, /path/to/binary sera capable de se connecter à des ports à faible nombre. Notez que vous devez utiliser setcap sur le binaire lui-même plutôt qu'un lien symbolique.

Option 2: utiliser authbind pour accorder un accès unique, avec un contrôle utilisateur / groupe / port plus fin: 

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

  1. Installer 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 à 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 à ces deux points. 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 au utilisateur / groupe et permet de contrôler l'accès par port, mais AFAIK ne prend en charge que IPv4.


97
2018-03-21 21:12



Merci Jason. Cela semble être la meilleure réponse à la question, mais il est arrivé un peu tard. Désolé de ne pas le voir plus tôt. - jww
Merci encore pour l'information. Je pense que la question et les réponses de ce super utilisateur surpassent les réponses erronées du serveur. Et les gens sur Super User sont beaucoup plus amicaux et accessibles. - jww
Attention, avec setcap, si vous écrasez l'exécutable auquel vous accordez des privilèges (ex: faire une reconstruction), il perd son statut de port privilégié et vous devez lui redonner des privilèges: | - rogerdpack
Quelque chose que je devais tripoter; J'essayais d'exécuter un service sysv, qui exécute un exécutable Ruby qui utilise Ruby. Vous devez donner le setcap la permission sur le exécutable ruby ​​spécifique à la version, par exemple. /usr/bin/ruby1.9.1 - Christian Rondeau
J'ai des doutes que chmodà 777 le byport fichiers est la meilleure idée. J'ai vu donner des autorisations allant de 500 à 744. Je m'en tiens à la plus restrictive qui fonctionne pour vous. - Pere


Dale Hagglund est sur place. Je vais juste dire la même chose, mais différemment, 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 auditable, qui s'exécute en tant que superutilisateur et lie le socket d'écoute;
  • avoir un autre petit programme simple, facilement vérifiable, qui supprime les privilèges générés par le premier programme;
  • avoir la viande du service, dans une troisième programme, exécuté sous un compte non-superutilisateur et une chaîne chargée par le deuxième programme, s'attendant à simplement hériter d'un descripteur de fichier ouvert pour le socket.

Vous avez la mauvaise idée de l'endroit où le risque est élevé. Le risque élevé est dans lire sur le réseau et agir sur ce qui est lu pas simplement en ouvrant un socket, en le liant à un port et en appelant listen(). C'est la partie d'un service qui fait la communication réelle à haut risque. Les pièces qui s'ouvrent, bind(), et listen(), et même (dans une certaine mesure) la part que accepts(), ne sont pas à haut risque et peuvent être exécutés sous l'égide du superutilisateur. Ils n'utilisent pas et n'agissent pas (à l'exception des adresses IP source dans le accept() cas) des données qui sont sous le contrôle d’étrangers non fiables sur le réseau.

Il y a plusieurs façons de le faire.

inetd

Comme le dit Dale Hagglund, l’ancien "superserver réseau" inetd est ce que ca. 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 droits de dépôt en deux programmes distincts, petits et faciles à auditer, mais il sépare le code de service principal en un programme distinct, exec()ed dans un processus de service qu’elle génère avec un descripteur de fichier ouvert pour le socket.

La difficulté de l’audit ne pose pas vraiment de problème, car il suffit d’auditer le programme. inetdLe principal problème, ce n'est pas tant l'audit, mais plutôt le fait qu'il ne fournit pas un contrôle simple et précis du service d'exécution, comparé aux outils plus récents.

UCSPI-TCP et daemontools

Daniel J. Bernstein UCSPI-TCP et Daemon Tools les paquets ont été conçus pour le faire conjointement. On peut également utiliser les équivalents de Bruce Guenter daemontools-encore ensemble d'outils.

Le programme pour ouvrir le descripteur de fichier de socket et se connecter au port local privilégié est tcpserver, de UCSPI-TCP. Il fait les deux listen() et le accept().

tcpserver puis génère soit un programme de service qui supprime lui-même les privilèges root (car le protocole servi implique le démarrage en tant que superutilisateur, puis la "connexion", comme par exemple un démon FTP ou SSH) ou setuidgid qui est un programme autonome, petit et facilement vérifiable, qui supprime uniquement les privilèges et enchaîne les charges sur le programme de service proprement dit (aucune partie de ce programme ne fonctionnant ainsi avec les privilèges du superutilisateur, comme par exemple qmail-smtpd).

Un service run Le script 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

bouffe

Mon forfait nosh est conçu pour cela. Il a un petit setuidgid utilitaire, tout comme les autres. Une légère différence est qu’il est utilisable avec systemd-style "LISTEN_FDS" services ainsi que des services UCSPI-TCP, donc le traditionnel tcpserver programme est remplacé par deux programmes distincts: tcp-socket-listen et tcp-socket-accept.

Encore une fois, les utilitaires à usage unique apparaissent et se chargent les uns les autres. Une bizarrerie intéressante de la conception est que l'on peut supprimer les privilèges du superutilisateur après listen() mais avant même accept(). Voici un run script pour qmail-smtpd cela en effet 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 exécutés sous l’égide du super-utilisateur sont les petits outils de chargement en chaîne indépendants du service. fdmove, clearenv, envdir, softlimit, tcp-socket-listen, et setuidgid. Au point que sh est démarré, le socket est ouvert et lié au smtp port, et le processus n'a plus de privilèges de superutilisateur.

s6, s6-networking et execline

Laurent Bercot s6 et S6-réseautage les paquets ont été conçus pour le faire conjointement. Les commandes sont structurellement très similaires à celles de daemontools et UCSPI-TCP.

run les scripts seraient sensiblement les mêmes, sauf pour la substitution de s6-tcpserver pour tcpserver et s6-setuidgid pour setuidgid. Cependant, on pourrait aussi choisir de faire appel à M. Bercot exécuter ensemble d'outils en même temps.

Voici un exemple de service FTP légèrement modifié depuis L'original de Wayne Marshall, qui utilise execline, s6, s6-networking et le programme du 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 de la même manière que ucspi-tcp et s6-networking. Les outils sont chpst et tcpsvd Cette fois-ci, mais ils font la même chose, et le code à haut risque qui fait la lecture, le traitement et l’écriture des données envoyées sur le réseau par des clients non approuvés fait toujours partie d’un programme distinct.

Ici c'est L'exemple de M. Pape de courir fnord dans un run scénario:

#!/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 et d’initialisation des services que l’on peut trouver dans certaines distributions Linux, est destiné à faire quoi inetd peut faire. Cependant, il n'utilise pas une suite de petits programmes autonomes. On doit auditer 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éparts. Le fichier de service "unit" contient des paramètres qui permettent de contrôler le processus de service, y compris l'utilisateur sous lequel il s'exécute.

Avec cet utilisateur défini pour être un non-superutilisateur, systemd fait tout le travail pour ouvrir le socket, le lier à un port et appeler listen() (et, si nécessaire, accept()) dans le processus n ° 1 en tant que superutilisateur, et le processus de service qu’elle génère s'exécute sans privilèges de superutilisateur.


21
2018-02-02 16:21



Merci pour le compliment. Ceci est une grande collection de conseils concrets. +1. - Dale Hagglund
Pas que ce ne soit pas un grand conseil général, mais CAP_NET_BIND_SERVICE et authbind les deux existent à cet effet; autoriser les applications non root à accéder aux ports de faible numérotation. - Jason C
Merci encore pour l'information. Je pense que la question et les réponses de ce super utilisateur surpassent les réponses erronées du serveur. Et les gens sur Super User sont beaucoup plus amicaux et accessibles. - jww


Vos instincts sont tout à fait corrects: il est déconseillé d’exécuter un grand programme complexe en tant que root, car leur complexité les rend difficiles à faire confiance.

Cependant, il est également déconseillé de permettre aux utilisateurs réguliers 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 de privilèges. L'idée de base est de séparer votre programme en deux parties (ou plus), chacune d'entre elles faisant un morceau bien défini de l'application globale et communiquant par de simples 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, ouvre et se lie au socket privilégié, puis le transmet à 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 démarre en tant que root. La première chose à faire 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 de mode utilisateur normal et effectue tout le travail. La suppression correcte des privilèges est délicate, alors prenez le temps d'étudier la bonne 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 des arguments initiaux et effectue peut-être une validation de base des arguments. Il crée une paire de sockets connectées via socketpair (), puis pique 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'applications plus complexe et donc moins fiable.

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


4
2018-02-02 06:49



Merci Dale. Je viens du monde Windows avec une expérience de cycle de vie SDLC solide, donc je comprends ce que vous dites. Je ne veux pas le faire (1) en raison de son risque élevé. De plus, c’est un gâchis de le faire correctement dans toutes les circonstances (c.-à-d., Setuid démystifié). Je ne veux pas faire (2) car cela ajoute de la complexité. Je veux juste permettre à un utilisateur non-privilégié de se connecter aux ports 80 et 443. C'est le moyen le plus sûr et le plus simple de le faire. - jww
Ce que vous proposez n'est pas considéré comme la meilleure pratique. Vous pourriez regarder inetd, qui peut écouter sur un socket privilégié et ensuite remettre ce socket à un programme non privilégié. - Dale Hagglund
Merci encore pour l'information. Je pense que la question et les réponses de ce super utilisateur surpassent les réponses erronées du serveur. Et les gens sur Super User sont beaucoup plus amicaux et accessibles. - jww


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.

J'ai alors 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 port du port 80 vers le port 3000.

Ce lien fournit les commandes réelles qui peuvent être utilisées pour cela. 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é le deuxième commandement et cela a fonctionné pour moi. Je pense donc qu’il s’agit d’un moyen terme de ne pas permettre au processus utilisateur d’accéder directement aux ports inférieurs, mais de leur donner accès via le transfert de port.


0
2018-06-27 07:00