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

80

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.

jww
la source
1
Voir serverfault.com/questions/268099 et stackoverflow.com/questions/413807 . La réponse courte est non.
Sami Laine
6
La réponse longue est oui ... alors la réponse courte devrait être oui aussi.
B T
3
La réponse courte est Oui.
Jason C

Réponses:

123

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: utilisation CAP_NET_BIND_SERVICE pour accorder l'accès à un processus à un port de petit numéro:

Avec cela, vous pouvez accorder un accès permanent à un binaire spécifique pour vous lier à des ports de faible numéro 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 en mesure de se lier à des ports peu numérotés. Notez que vous devez utiliser setcap sur le binaire lui-même plutôt qu'un lien symbolique.

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

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 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 à ces deux aspects. L’option 1 accorde la confiance au binaire mais ne fournit aucun contrôle sur l'accès par port. L’option 2 donne confiance aux utilisateur / groupe et permet de contrôler l'accès par port, mais AFAIK ne prend en charge que IPv4.

Jason C
la source
1
Merci Jason. Cela semble être la meilleure réponse à la question, mais c'est arrivé un peu tard. Désolé de ne pas le voir plus tôt.
jww
1
Merci encore pour l'information. Je pense que cette question et ces réponses du super utilisateur sont plus que satisfaisantes. Et les utilisateurs de Super User sont beaucoup plus sympathiques et abordables.
jww
3
Attention, avec setcap, si vous écrasez l'exécutable auquel vous accordez des privilèges (par exemple: reconstruisez une reconstruction), il perd son statut de port privilégié et vous devez lui redonner des privilèges: |
rogerdpack
1
Quelque chose que j'ai dû tripoter; J'essayais de faire fonctionner un service sysv, qui exécute un exécutable ruby ​​qui utilise ruby. Vous devez donner le setcap permission sur le Ruby exécutable spécifique à la version , par exemple. /usr/bin/ruby1.9.1
Christian Rondeau
3
J'ai des doutes que chmod 777 la byport fichiers est la meilleure idée. J'ai vu donner des autorisations allant de 500 à 744. Je resterais sur le plus restrictif qui fonctionne pour vous.
Pere
24

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;
  • d'avoir la viande du service, dans un séparé troisième programme, exécuté sous un compte non superutilisateur et une chaîne chargée par le second programme, dans l’espoir d’hériter simplement 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é est en lire sur le réseau et agir sur ce qui est lu pas dans les simples gestes 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 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 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 "super serveur" 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 suppression des privilèges en deux programmes distincts, petits et faciles à auditer, mais il sépare le code de service principal en un programme séparé. exec() ed dans un processus de service qu’il génère avec un descripteur de fichier ouvert pour le socket.

La difficulté de l'audit n'est pas un problème, il suffit de vérifier le programme en question. inetd Le principal problème de l'audit 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

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

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

tcpserver puis génère 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 dans le cas, par exemple, d'un démon FTP ou SSH) ou setuidgid qui est un petit programme autonome facilement vérifiable qui abandonne uniquement les privilèges, puis enchaîne les charges vers le programme de service proprement dit (aucune partie de celui-ci ne s'exécute donc avec des privilèges de superutilisateur, comme c'est le cas avec, par exemple, qmail-smtpd ).

Un service run script serait donc par exemple (celui-ci pour dummyidentd pour fournir un service nul IDENT):

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

bouffe

Mon paquet est conçu pour le faire. Il a un petit setuidgid utilité, tout comme les autres. Une légère différence est qu’il est utilisable avec systemd style "LISTEN_FDS" ainsi qu’avec les 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 à 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 run script pour qmail-smtpd cela fait en effet 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 superutilisateur 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 ne dispose plus des privilèges de superutilisateur.

s6, s6-networking et execline

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

run 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 usage de la méthode de M. Bercot. execline ensemble 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 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

Gerrit Pape ipsvd C’est un autre ensemble d’outils qui va dans le même sens 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 lit, traite et écrit les choses envoyées sur le réseau par des clients non fiables fait toujours l’objet d’un programme séparé.

Voici 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 de service et init que l'on retrouve dans certaines distributions Linux est destiné à faire quoi inetd peut faire . Cependant, il n’utilise pas une suite de petits programmes autonomes. Il faut 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 "unité" de service contient des paramètres qui permettent de contrôler le processus de service, y compris son utilisateur.

Avec cet utilisateur défini pour être un non-superutilisateur, systemd effectue tout le travail consistant à 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'il génère est exécuté sans privilèges de superutilisateur.

JdeBP
la source
2
Merci pour le compliment. C'est une excellente collection de conseils concrets. +1
Dale Hagglund
1
Non pas que ce ne soit pas un bon conseil général, mais CAP_NET_BIND_SERVICE et authbind les deux existent à cet effet; pour permettre aux applications non root d’accéder aux ports dont le numéro est bas.
Jason C
Merci encore pour l'information. Je pense que cette question et ces réponses du super utilisateur sont plus que satisfaisantes. Et les utilisateurs de Super User sont beaucoup plus sympathiques et abordables.
jww
4

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 ils représentent généralement des services système importants.

L’approche standard pour résoudre cette contradiction apparente est séparation de privilège . 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://en.m.wikipedia.org/wiki/Privilege_separation

Dale Hagglund
la source
Merci Dale. Je viens du monde Windows avec une solide expérience du cycle de vie SDLC. Je comprends donc ce que vous dites. Je ne veux pas faire (1) parce que son risque est élevé. De plus, c’est un gâchis de le faire correctement en toutes circonstances (c.-à-d., Setuid démystifié ). Je ne veux pas faire (2) parce que cela ajoute à la complexité. Je veux simplement 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 une pratique exemplaire. Vous pouvez regarder inetd, qui peut écouter sur un socket privilégié, puis confier ce socket à un programme non privilégié.
Dale Hagglund
Merci encore pour l'information. Je pense que cette question et ces réponses du super utilisateur sont plus que satisfaisantes. Et les utilisateurs de Super User sont beaucoup plus sympathiques et abordables.
jww
2

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 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é 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.

noob
la source
3
+1 pour sortir des sentiers battus
Richard Wiseman