Existe-t-il un moyen pour les processus non root de se lier aux ports «privilégiés» sous Linux?


389

C'est très ennuyeux d'avoir cette limitation sur ma boîte de développement, quand il n'y aura jamais d'autres utilisateurs que moi.

Je connais les solutions de contournement standard , mais aucun ne fait exactement ce que je veux:

  1. authbind (La version dans les tests Debian, 1.0, prend uniquement en charge IPv4)
  2. Utilisation de la cible iptables REDIRECT pour rediriger un port bas vers un port haut (la table "nat" n'est pas encore implémentée pour ip6tables, la version IPv6 d'iptables)
  3. sudo (Exécuter en tant que root est ce que j'essaie d'éviter)
  4. SELinux (ou similaire). (Ceci est juste ma boîte de développement, je ne veux pas introduire beaucoup de complexité supplémentaire.)

Existe-t-il une sysctlvariable simple pour permettre aux processus non root de se lier à des ports "privilégiés" (ports inférieurs à 1024) sous Linux, ou suis-je simplement malchanceux?

EDIT: Dans certains cas, vous pouvez utiliser des capacités pour ce faire.


5
D'après mon expérience, une des raisons de tenter cela est d'écrire un serveur Web plutôt que d'utiliser Apache (ou lighttpd).
S.Lott

J'ai ajouté le truc setcap à ma réponse au stackoverflow.com/questions/277991/…
Paul Tomblin

15
Parce que dans ce cas, j'utilisais IPv6, c'est pourquoi certaines des solutions de contournement "habituelles" (authbind et iptables REDIRECT) ne fonctionnaient pas pour moi.
Jason Creighton


1
Il y a plusieurs façons de procéder. Voir Autoriser le processus non root à se lier aux ports 80 et 443? sur Super User.
2017

Réponses:


392

D'accord, merci aux personnes qui ont souligné le système de CAP_NET_BIND_SERVICEcapacités et la capacité. Si vous avez un noyau récent, il est en effet possible de l'utiliser pour démarrer un service en tant que non root mais lier des ports bas. La réponse courte est que vous faites:

setcap 'cap_net_bind_service=+ep' /path/to/program

Et puis à tout moment programest exécuté par la suite, il aura la CAP_NET_BIND_SERVICEcapacité. setcapest dans le paquet debian libcap2-bin.

Maintenant pour les mises en garde:

  1. Vous aurez besoin d'au moins un noyau 2.6.24
  2. Cela ne fonctionnera pas si votre fichier est un script. (c'est-à-dire, utilise une ligne #! pour lancer un interpréteur). Dans ce cas, pour autant que je sache, vous devez appliquer la capacité à l'exécutable de l'interprète lui-même, ce qui est bien sûr un cauchemar pour la sécurité, car tout programme utilisant cet interprète aura la capacité. Je n'ai pas pu trouver de moyen propre et facile de contourner ce problème.
  3. Linux désactivera LD_LIBRARY_PATH sur tous ceux programqui ont des privilèges élevés comme setcapou suid. Donc, si votre programutilise le sien .../lib/, vous devrez peut-être chercher une autre option comme la redirection de port.

Ressources:

Remarque: RHEL a d'abord ajouté cela dans la v6 .


3
Solution partielle pour les scripts: créez une copie de l'interpréteur (par exemple bash), donnez-lui les capacités mais limitez l'accès à la copie aux utilisateurs qui en ont besoin. Bien sûr, ces utilisateurs doivent être fiables, mais ils peuvent quand même changer le script.
Erich Kitzmueller

3
Mis à part le paquet debian (binaire) susmentionné, le site du développeur est friedhoff.org/posixfilecaps.html documents / présentations / etc
associés

1
sur suse SLES 11.1 J'ai dû ajouter le paramètre de noyau file_caps = 1 à grub menu.lst pour que cela fonctionne.
Shay

2
Oui @ C.Ross, car il devrait être appliqué à /usr/bin/java, puis ouvrirait la capacité à n'importe quelle application java exécutée sur le système. Dommage que les capacités ne puissent pas non plus être définies par utilisateur.
joeytwiddle

5
Le paramètre setcap persiste-t-il lors des redémarrages; sinon, existe-t-il un endroit standard pour mettre cette règle afin qu'elle soit exécutée au démarrage du système? Est-ce /etc/security/capability.confsur Debian / Ubuntu une aide?
joeytwiddle

34

Vous pouvez faire une redirection de port. Voici ce que je fais pour un serveur de stratégie Silverlight fonctionnant sur une boîte Linux

iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 943 -j REDIRECT --to-port 1300

5
Malheureusement, cela ne fonctionnera que pour les connexions routées, c'est-à-dire pas depuis la machine locale.
zbyszek

@joeytwiddle Je pense que c'est dans le script qui exécute (démarre) votre serveur, mais je peux me tromper. J'aimerais aussi savoir: P
Camilo Martin

@CamiloMartin À nouveau, la documentation Debian qui a été liée à la question recommande de la placer dans votre script de pare-feu, ou d'en créer une à /etc/network/if-up.d/firewall.
joeytwiddle

8
@zbyszek Cela peut fonctionner pour les connexions de machines locales, avec une règle iptables supplémentaire: stackoverflow.com/a/31795603/1356953
00500005

Dans les noyaux plus anciens, il semble que cela ne soit pas pris en charge pour IPv6 . Mais apparemment, il est pris en charge dans ip6tables v1.4.18 et le noyau Linux v3.8.
Craig McQueen

31

La manière standard est de les rendre "setuid" afin qu'ils démarrent en tant que root, puis ils jettent ce privilège root dès qu'ils se sont liés au port mais avant de commencer à accepter les connexions à celui-ci. Vous pouvez voir de bons exemples de cela dans le code source d'Apache et INN. On me dit que Lighttpd est un autre bon exemple.

Un autre exemple est Postfix, qui utilise plusieurs démons qui communiquent via des canaux, et seulement un ou deux d'entre eux (qui font très peu sauf accepter ou émettre des octets) s'exécutent en tant que root et le reste s'exécute avec un privilège inférieur.


1
Fait intéressant, cela ne fonctionne pas sous les versions récentes de Linux (peut-être juste Ubuntu) sans CAP_SETUID défini. Donc, si vous avez besoin de setuid, vous devrez de toute façon définir cette capacité.
Matt

Supprimer les privilèges root est la bonne façon de procéder. Bien qu'il ne soit pas nécessaire de définir setuid si vous avez init / upstart / systemd démarrer le service.
Michael Hampton

2
Cela est difficile si le programme est écrit dans un langage interprété ou un interpréteur de bytecode tel que C # (Mono), Java, Python. (Apparemment Perl l'a fait via binfmt_miscet son drapeau 'C'; je ne suis pas sûr pour les autres.)
Craig McQueen

très peu sauf émettre des octets, cela ne fait pas très peu.
Ryan

Si vous définissez un setuid binaire, il s'exécutera en tant que processus racine. La question, cependant, demande comment l'accomplir sans exécuter votre binaire en tant que processus racine. Cette solution est la réponse à une question différente, à savoir: "Comment un utilisateur non privilégié peut-il avoir un processus lié à un port privilégié", mais ce n'est pas la question posée, à mon humble avis?
Michael Beer

24

Ou corrigez votre noyau et supprimez la coche.

(Option de dernier recours, non recommandée).

Dans net/ipv4/af_inet.c, supprimez les deux lignes qui lisent

      if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
              goto out;

et le noyau ne vérifiera plus les ports privilégiés.


22
Une très mauvaise idée pour une multitude de raisons.
Adam Lassek

24
C'est une mauvaise idée. Mais cela fonctionnerait réellement. Et bien sûr, ça m'a fait rire.
Oto Brglez

23
Bien sûr, c'est une mauvaise idée. C'est pourquoi j'ai dit en dernier recours. Le point de l'open source est que si cela ne fonctionne pas comme vous le souhaitez, vous pouvez le changer.
Joshua

18
Je ne sais pas pourquoi vous avez été mis à l'écart. Les auteurs de programmes malveillants ne se soucient pas du port sur lequel ils écoutent et ils sont plus qu'heureux d'ouvrir un port supérieur à 1024. Il me semble que tous les ports devraient nécessiter des privilèges, ou qu'aucun port ne devrait nécessiter de privilèges. Et demander à root d'ouvrir un port en dessous de 1024 signifie simplement que votre application à haut risque s'exécute en tant que root. Cela me semble une idée vraiment stupide. Peut-être que je manque quelque chose ici ...
jww

9
À l'époque, le port <1024 était utilisé dans les protocoles UNIX à UNIX pour prouver que le code exécuté à l'autre extrémité s'exécutait en tant que root. Cela fonctionnait assez bien pour un ensemble de serveurs UNIX avec une sécurité commune.
Joshua

22

Vous pouvez configurer un tunnel SSH local, par exemple si vous voulez que le port 80 frappe votre application liée à 3000:

sudo ssh $USERNAME@localhost -L 80:localhost:3000 -N

Cela a l'avantage de travailler avec des serveurs de script et d'être très simple.


1
Mon préféré. Très facile à allumer et éteindre et ne nécessite aucune modification à l'échelle du système. Bonne idée!
Dan Passaro du

Ceci est incroyable. De loin la réponse la plus simple.
michaelsnowden

1
Je m'attendrais à ce que ce soit modérément cher et pas une bonne idée dans toute situation liée aux performances, mais je ne peux pas être sûr sans le tester. Le chiffrement SSH coûte un montant différent de zéro.
lahwran

3
Cela pourrait être fait avec netcat sans surcharge ssh:sudo nc -l 80 | nc localhost 3000
Bugster

1
Vous chiffrez + déchiffrez uniquement pour déplacer les données vers un autre port sur la même machine? En outre, cela ne fonctionne pas pour le trafic UDP.
Daniel F

19

Mise à jour 2017:

Utiliser authbind


Beaucoup mieux que CAP_NET_BIND_SERVICE ou un noyau personnalisé.

  • CAP_NET_BIND_SERVICE accorde la confiance au binaire mais ne fournit aucun contrôle sur l'accès par port.
  • Authbind accorde la confiance à l'utilisateur / groupe et fournit un contrôle sur l'accès par port, et prend en charge IPv4 et IPv6 (la prise en charge IPv6 a été ajoutée récemment).

    1. Installer: apt-get install authbind

    2. Configurez l'accès aux ports appropriés, par exemple 80 et 443 pour 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 votre commande via authbind
      (en spécifiant éventuellement --deepou d'autres arguments, voir la page de manuel):

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

      par exemple

      authbind --deep java -jar SomeServer.jar
      

Dans le prolongement de la fabuleuse recommandation de Joshua (= déconseillée sauf si vous savez ce que vous faites) de pirater le noyau:

Je l'ai d'abord posté ici .

Facile. Avec un noyau normal ou ancien, vous ne le faites pas.
Comme souligné par d'autres, iptables peut transférer un port.
Comme d'autres l'ont également souligné, CAP_NET_BIND_SERVICE peut également faire le travail.
Bien sûr, CAP_NET_BIND_SERVICE échouera si vous lancez votre programme à partir d'un script, sauf si vous définissez le plafond sur l'interpréteur de commandes, ce qui est inutile, vous pourriez tout aussi bien exécuter votre service en tant que root ...
par exemple pour Java, vous devez l'appliquer à la JAVA JVM

sudo /sbin/setcap 'cap_net_bind_service=ep' /usr/lib/jvm/java-8-openjdk/jre/bin/java

Évidemment, cela signifie alors que tout programme Java peut lier des ports système.
Dito pour mono / .NET.

Je suis également sûr que xinetd n'est pas la meilleure des idées.
Mais comme les deux méthodes sont des hacks, pourquoi ne pas simplement lever la limite en levant la restriction?
Personne n'a dit que vous deviez exécuter un noyau normal, vous pouvez donc simplement exécuter le vôtre.

Vous venez de télécharger la source pour le dernier noyau (ou le même que vous avez actuellement). Ensuite, vous allez à:

/usr/src/linux-<version_number>/include/net/sock.h:

Là vous cherchez cette ligne

/* Sockets 0-1023 can't be bound to unless you are superuser */
#define PROT_SOCK       1024

et changez-le en

#define PROT_SOCK 0

si vous ne voulez pas avoir une situation ssh non sécurisée, vous la modifiez comme suit: #define PROT_SOCK 24

En règle générale, j'utiliserais le paramètre le plus bas dont vous avez besoin, par exemple 79 pour http, ou 24 lorsque vous utilisez SMTP sur le port 25.

C'est déjà tout.
Compilez le noyau et installez-le.
Redémarrez.
Terminé - cette limite stupide est GONE, et cela fonctionne également pour les scripts.

Voici comment compiler un noyau:

https://help.ubuntu.com/community/Kernel/Compile

# You can get the kernel-source via package linux-source, no manual download required
apt-get install linux-source fakeroot

mkdir ~/src
cd ~/src
tar xjvf /usr/src/linux-source-<version>.tar.bz2
cd linux-source-<version>

# Apply the changes to PROT_SOCK define in /include/net/sock.h

# Copy the kernel config file you are currently using
cp -vi /boot/config-`uname -r` .config

# Install ncurses libary, if you want to run menuconfig
apt-get install libncurses5 libncurses5-dev

# Run menuconfig (optional)
make menuconfig

# Define the number of threads you wanna use when compiling (should be <number CPU cores> - 1), e.g. for quad-core
export CONCURRENCY_LEVEL=3
# Now compile the custom kernel
fakeroot make-kpkg --initrd --append-to-version=custom kernel-image kernel-headers

# And wait a long long time

cd ..

En un mot, utilisez iptables si vous voulez rester en sécurité, compilez le noyau si vous voulez être sûr que cette restriction ne vous dérange plus.


Une raison pour le downvote? Root-hater, ou les instructions de compilation du noyau n'ont-elles pas fonctionné?
Stefan Steiger

4
la modification du noyau rend les futures mises à jour beaucoup plus pénibles. je ne ferais pas cela, car je sais que je serais trop paresseux pour continuer à mettre à jour mon noyau régulièrement. c'est bien que cela soit possible en théorie, mais ce n'est pas une option viable dans de nombreuses circonstances.
kritzikratzi

Peut-être une façon plus sûre de le faire est de changer le chmod 777 / etc / authbind / byport / 80 par chmod 544 / etc / authbind / byport / 23 que la connexion chown: group / etc / authbind / byport / 23
Jérôme B

18

Les capacités des fichiers ne sont pas idéales, car elles peuvent se casser après une mise à jour du package.

La solution idéale, à mon humble avis, devrait être une capacité à créer un shell avec héritable CAP_NET_BIND_SERVICE ensemble .

Voici une façon quelque peu compliquée de le faire:

sg $DAEMONUSER "capsh --keep=1 --uid=`id -u $DAEMONUSER` \
     --caps='cap_net_bind_service+pei' -- \
     YOUR_COMMAND_GOES_HERE"

capshl'utilitaire peut être trouvé dans le paquet libcap2-bin dans les distributions Debian / Ubuntu. Voici ce qui se passe:

  • sgremplace l'ID de groupe effectif par celui de l'utilisateur du démon. Ceci est nécessaire car capshGID reste inchangé et nous ne le voulons certainement pas.
  • Définit le bit «garder les capacités lors du changement d'UID».
  • Modifie l'UID en $DAEMONUSER
  • Supprime tous les plafonds (en ce moment, tous les plafonds sont toujours présents à cause de --keep=1), sauf héritablecap_net_bind_service
  • Exécute votre commande ('-' est un séparateur)

Le résultat est un processus avec un utilisateur et un groupe et des cap_net_bind_serviceprivilèges spécifiés .

À titre d'exemple, une ligne du ejabberdscript de démarrage:

sg $EJABBERDUSER "capsh --keep=1 --uid=`id -u $EJABBERDUSER` --caps='cap_net_bind_service+pei' -- $EJABBERD --noshell -detached"

Et cela ne fonctionne en fait PAS. Les majuscules ne sont pas conservées sur le commutateur d'ID utilisateur. Cela fonctionnera si vous souhaitez exécuter en tant que root, mais avec toutes les capacités supprimées.
Cyberax

Si j'essaye, j'obtiens "ne peut pas exécuter le fichier binaire". En fait, je ne peux pas capshfaire quoi que ce soit autre que « ne peut pas exécuter le fichier binaire » lorsque vous utilisez les --ou ==options. Je me demande ce qui me manque.
Craig McQueen

@CraigMcQueen, tout ce qui suit --est transmis à /bin/bash, donc vous voudrez peut-être essayer avec -c 'your command'. Hélas, je semble rencontrer le même problème que @Cyberax, car j'obtiens une "autorisation refusée" lorsque j'essaye bind.
Amir

Pour que cela fonctionne sans configurer les fonctionnalités de fichiers (ou en cours d' exécution en tant que root), vous devez utiliser les capacités ambiantes telles que décrites dans cette réponse Unix.SE . Vous pouvez également utiliser au --gidlieu de sg(ou --userqui ensembles --uid, --gidet --groups): sudo capsh --caps='cap_net_bind_service+eip cap_setpcap,cap_setuid,cap_setgid+ep' --keep=1 --user="$service_user" --addamb=cap_net_bind_service -- -c 'exec $service $service_args'
Kevinoid

18

Pour une raison quelconque, personne ne mentionne la réduction de sysctl net.ipv4.ip_unprivileged_port_start à la valeur dont vous avez besoin. Exemple: nous devons lier notre application au port 443.

sysctl net.ipv4.ip_unprivileged_port_start=443

Certains pourraient dire qu'il existe un problème de sécurité potentiel: les utilisateurs non privilégiés peuvent désormais se lier aux autres ports privilégiés (444-1024). Mais vous pouvez résoudre ce problème facilement avec iptables, en bloquant d'autres ports:

iptables -I INPUT -p tcp --dport 444:1024 -j DROP
iptables -I INPUT -p udp --dport 444:1024 -j DROP

Comparaison avec d'autres méthodes. Cette méthode:

  • à partir d'un certain point, (IMO) est encore plus sûr que de définir CAP_NET_BIND_SERVICE / setuid, car une application ne définit pas du tout setuid, même partiellement (les capacités le sont en fait). Par exemple, pour intercepter un coredump d'application à capacité activée, vous devrez modifier sysctl fs.suid_dumpable (ce qui entraîne d'autres problèmes de sécurité potentiels). De plus, lorsque CAP / suid est défini, le répertoire / proc / PID appartient à root, donc votre utilisateur non root n'aura pas toutes les informations / le contrôle du processus en cours d'exécution, par exemple, l'utilisateur ne pourra pas (dans le cas commun) déterminer quelles connexions appartiennent à l'application via / proc / PID / fd / (netstat -aptn | grep PID).
  • présente un inconvénient pour la sécurité: alors que votre application (ou toute application utilisant les ports 443-1024) est arrêtée pour une raison quelconque, une autre application peut prendre le port. Mais ce problème pourrait également être appliqué à CAP / suid (dans le cas où vous le définissez sur l'interpréteur, par exemple java / nodejs) et iptables-redirect. Utilisez la méthode systemd-socket pour exclure ce problème. Utilisez la méthode authbind pour autoriser uniquement la liaison utilisateur spéciale.
  • ne nécessite pas de définir CAP / suid chaque fois que vous déployez une nouvelle version de l'application.
  • ne nécessite pas de support / modification d'application, comme la méthode systemd-socket.
  • ne nécessite pas de reconstruction du noyau (si la version en cours d'exécution prend en charge ce paramètre sysctl)
  • ne fait pas LD_PRELOAD comme la méthode authbind / privbind, cela pourrait potentiellement affecter les performances, la sécurité, le comportement (n'est-ce pas? non testé). Dans le reste, authbind est une méthode vraiment flexible et sécurisée.
  • surpasse la méthode REDIRECT / DNAT iptables, car elle ne nécessite pas de traduction d'adresse, de suivi de l'état de la connexion, etc. Ceci n'est visible que sur les systèmes à forte charge.

Selon la situation, je choisirais entre sysctl, CAP, authbind et iptables-redirect. Et c'est formidable que nous ayons tant d'options.


2
Merci pour la bonne réponse! Il semble que cette fonctionnalité soit apparue pour la première fois dans Linux 4.11 en avril 2017 , donc elle n'existait pas en 2009 lorsque j'ai posé cette question pour la première fois. J'ai également fait un test rapide, et il semble également fonctionner pour IPV6, même si "ipv4" est dans le nom sysctl.
Jason Creighton

15

Deux autres possibilités simples:

Il existe une ancienne solution (démodée) au "démon qui se lie sur un port bas et remet le contrôle à votre démon". Cela s'appelle inetd (ou xinetd). Les inconvénients sont:

  • votre démon doit parler sur stdin / stdout (si vous ne contrôlez pas le démon - si vous n'avez pas la source - alors c'est peut-être un showstopper, bien que certains services puissent avoir un indicateur de compatibilité inetd)
  • un nouveau processus démon est bifurqué pour chaque connexion
  • c'est un maillon supplémentaire dans la chaîne

Avantages:

  • disponible sur tous les anciens UNIX
  • une fois que votre administrateur système a configuré la configuration, vous êtes prêt à poursuivre votre développement (lorsque vous reconstruisez votre démon, risquez-vous de perdre les capacités de setcap? Et puis vous devrez revenir à votre administrateur "s'il vous plaît monsieur .. . ")
  • le démon n'a pas à se soucier de ce truc de réseautage, il suffit de parler sur stdin / stdout
  • peut configurer pour exécuter votre démon en tant qu'utilisateur non root, comme demandé

Une autre alternative: un proxy piraté (netcat ou même quelque chose de plus robuste ) du port privilégié à un port arbitraire de numéro élevé où vous pouvez exécuter votre démon cible. (Netcat n'est évidemment pas une solution de production, mais "juste ma boîte de développement", non?). De cette façon, vous pourriez continuer à utiliser une version compatible réseau de votre serveur, n'auriez besoin que de root / sudo pour démarrer le proxy (au démarrage), ne dépendriez pas de capacités complexes / potentiellement fragiles.


1
Bonne suggestion. Pour une raison quelconque, je n'ai même pas pensé à utiliser inetd. Sauf que le service en question est basé sur UDP, il est donc légèrement plus compliqué que les services TCP. J'ai une solution qui fonctionne en ce moment avec setcap, mais je vais devoir y réfléchir.
Jason Creighton

Pour la deuxième option, vous pouvez probablement même démarrer le proxy en utilisant inetd ... (Mais IPTables est probablement une surcharge inférieure ...)
Gert van den Berg

14

Ma «solution de contournement standard» utilise socat comme redirecteur de l'espace utilisateur:

socat tcp6-listen:80,fork tcp6:8080

Attention, cela ne va pas évoluer, la fourche coûte cher mais c'est la façon dont socat fonctionne.


13

Linux prend en charge des capacités pour prendre en charge des autorisations plus précises que simplement "cette application est exécutée en tant que root". L'une de ces capacités CAP_NET_BIND_SERVICEconcerne la liaison à un port privilégié (<1024).

Malheureusement, je ne sais pas comment exploiter cela pour exécuter une application en tant que non root tout en la donnant CAP_NET_BIND_SERVICE(probablement en utilisant setcap, mais il y a forcément une solution existante pour cela).


Ne fonctionne pas pour les scripts ou les programmes JVM / mono individuels.
Stefan Steiger

13

Je sais que c'est une vieille question, mais maintenant avec les noyaux récents (> = 4.3), il y a enfin une bonne réponse à cela - les capacités ambiantes.

La réponse rapide est de récupérer une copie de la dernière version (non encore publiée) de libcap de git et de la compiler. Copiez le progs/capshbinaire résultant quelque part ( /usr/local/binc'est un bon choix). Ensuite, en tant que root, démarrez votre programme avec

/usr/local/bin/capsh --keep=1 --user='your-service-user-name' \
    --inh='cap_net_bind_service' --addamb='cap_net_bind_service' \ 
    -- -c 'your-program'

Dans l'ordre, nous sommes

  • Déclarant que lorsque nous changeons d'utilisateur, nous voulons conserver nos ensembles de capacités actuels
  • Changer d'utilisateur et de groupe en «nom-d'utilisateur-de-votre-service»
  • Ajout de la cap_net_bind_servicecapacité aux ensembles hérités et ambiants
  • Forking bash -c 'your-command'(puisque capshdémarre automatiquement bash avec les arguments après --)

Il se passe beaucoup de choses sous le capot ici.

Premièrement, nous fonctionnons en tant que root, donc par défaut, nous obtenons un ensemble complet de capacités. Inclus dans cela est la possibilité de changer uid & gid avec les appels système setuidet setgidsys. Cependant, habituellement, lorsqu'un programme fait cela, il perd son ensemble de capacités - c'est ainsi que l'ancienne méthode de suppression de root avec setuidfonctionne toujours. L' --keep=1indicateur indique capshd'émettre l' prctl(PR_SET_KEEPCAPS)appel système, qui désactive la suppression des capacités lors du changement d'utilisateur. Le changement réel d'utilisateurs capshse produit avec le --userdrapeau, qui s'exécute setuidet setgid.

Le prochain problème que nous devons résoudre est de savoir comment définir les capacités d'une manière qui continue après nous, execnos enfants. Le système de capacités a toujours eu un ensemble de capacités «héritées», qui est «un ensemble de capacités préservées à travers un execve (2)» [ capacités (7) ]. Bien que cela semble résoudre notre problème (définissez simplement la cap_net_bind_servicecapacité sur hérité, non?), Cela ne s'applique en fait que pour les processus privilégiés - et notre processus n'est plus privilégié, car nous avons déjà changé d'utilisateur (avec l' --userindicateur).

Le nouvel ensemble de capacités ambiantes contourne ce problème - c'est "un ensemble de capacités qui sont préservées sur un execve (2) d'un programme qui n'est pas privilégié". En mettant cap_net_bind_servicedans l'ensemble ambiant, lorsque capshexec est notre programme serveur, notre programme héritera de cette capacité et pourra lier des écouteurs à des ports bas.

Si vous souhaitez en savoir plus, la page du manuel des fonctionnalités explique cela en détail. Courir à capshtravers straceest également très instructif!


Le --addambdrapeau a été introduit avec libcap 2.26. Mon système avait 2.25 et je devais construire à partir des sources.
ngreen

12

TLDR: Pour "la réponse" (telle que je la vois), passez à la partie >> TLDR << de cette réponse.

OK, je l'ai compris (pour de vrai cette fois), la réponse à cette question, et cette réponse est aussi un moyen de m'excuser d'avoir fait la promotion d' une autre réponse (ici et sur Twitter) que je pensais être "la meilleure ", mais après l'avoir essayé, j'ai découvert que je me trompais. Apprenez de mes erreurs les enfants: ne faites pas la promotion de quelque chose avant de l'avoir essayé vous-même!

Encore une fois, j'ai passé en revue toutes les réponses ici. J'ai essayé certains d'entre eux (et j'ai choisi de ne pas en essayer d'autres parce que je n'aimais tout simplement pas les solutions). Je pensais que la solution était d'utiliser systemdavec ses paramètres Capabilities=et CapabilitiesBindingSet=. Après avoir lutté avec cela pendant un certain temps, j'ai découvert que ce n'était pas la solution car:

Les capacités visent à restreindre les processus root!

Comme l'OP l'a judicieusement indiqué, il est toujours préférable d'éviter cela (pour tous vos démons si possible!).

Vous ne pouvez pas utiliser les options liées aux capacités avec User=et Group=dans les systemdfichiers d'unité, car les capacités sont TOUJOURS réinitialisées lorsque execev(ou quelle que soit la fonction) est appelée. En d'autres termes, lorsque la systemdfourche et baisse ses perms, les capacités sont réinitialisées. Il n'y a aucun moyen de contourner cela, et toute cette logique de liaison dans le noyau est basique autour de uid = 0, pas de capacités. Cela signifie qu'il est peu probable que les capacités soient jamais la bonne réponse à cette question (au moins dans un proche avenir). Par ailleurs, setcapcomme d'autres l'ont mentionné, ce n'est pas une solution. Cela n'a pas fonctionné pour moi, cela ne fonctionne pas bien avec les scripts, et ceux-ci sont réinitialisés de toute façon chaque fois que le fichier change.

Dans ma maigre défense, j'ai déclaré (dans le commentaire que j'ai maintenant supprimé) que la suggestion de James iptables (que le PO mentionne également) était la "2ème meilleure solution". :-P

>> TLDR <<

La solution consiste à combiner systemdavec des iptablescommandes à la volée , comme celle-ci ( tirée de DNSChain ):

[Unit]
Description=dnschain
After=network.target
Wants=namecoin.service

[Service]
ExecStart=/usr/local/bin/dnschain
Environment=DNSCHAIN_SYSD_VER=0.0.1
PermissionsStartOnly=true
ExecStartPre=/sbin/sysctl -w net.ipv4.ip_forward=1
ExecStartPre=-/sbin/iptables -D INPUT -p udp --dport 5333 -j ACCEPT
ExecStartPre=-/sbin/iptables -t nat -D PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 5333
ExecStartPre=/sbin/iptables -A INPUT -p udp --dport 5333 -j ACCEPT
ExecStartPre=/sbin/iptables -t nat -A PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 5333
ExecStopPost=/sbin/iptables -D INPUT -p udp --dport 5333 -j ACCEPT
ExecStopPost=/sbin/iptables -t nat -D PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 5333
User=dns
Group=dns
Restart=always
RestartSec=5
WorkingDirectory=/home/dns
PrivateTmp=true
NoNewPrivileges=true
ReadOnlyDirectories=/etc

# Unfortunately, capabilities are basically worthless because they're designed to restrict root daemons. Instead, we use iptables to listen on privileged ports.
# Capabilities=cap_net_bind_service+pei
# SecureBits=keep-caps

[Install]
WantedBy=multi-user.target

Ici, nous accomplissons ce qui suit:

  • Le démon écoute sur 5333, mais les connexions sont acceptées avec succès sur 53 grâce à iptables
  • Nous pouvons inclure les commandes dans le fichier unitaire lui-même, et ainsi nous épargnons des maux de tête aux gens. systemdnettoie les règles de pare-feu pour nous, en veillant à les supprimer lorsque le démon n'est pas en cours d'exécution.
  • Nous ne courons jamais en tant que root, et nous rendons impossible une escalade de privilèges (du moins le systemdprétend), même si le démon est compromis et défini uid=0.

iptablesest encore, malheureusement, un utilitaire assez laid et difficile à utiliser. Si le démon écoute au eth0:0lieu de eth0, par exemple, les commandes sont légèrement différentes .


2
Personne ne devrait plus utiliser d'alias à l'ancienne, à eth0:0moins d'avoir une distribution Linux vraiment ancienne . Ils sont obsolètes depuis des années et finiront par disparaître.
Michael Hampton

1
Je pense que vous voulez dire OpenVZ. (SolusVM est un panneau de contrôle.) Et oui, OpenVZ fait beaucoup de choses de travers, la mise en réseau n'étant que l'une d'entre elles.
Michael Hampton

1
Non, je veux dire par SolusVM. Depuis / etc / network / interfaces:# Generated by SolusVM
Greg Slepak

1
Toujours pas OpenVZ ... Au moins, je ne l'utilise pas, ni mon VPS.
Greg Slepak

7
Merci d'avoir souligné la fonctionnalité de systemd. Cependant, il n'est pas nécessaire d'avoir des iptables complexes lorsque systemd démarre directement le binaire (pas un script). Le cadre AmbientCapabilities=CAP_NET_BIND_SERVICEa parfaitement fonctionné pour moi en combinaison avec User=.
Ruud

11

systemd est un remplacement de sysvinit qui a une option pour lancer un démon avec des capacités spécifiques. Options Capabilities =, CapabilityBoundingSet = dans la page de manuel systemd.exec (5) .


2
J'ai déjà recommandé cette réponse, mais après l'avoir essayée, je ne le fais plus. Voir ma réponse pour une alternative qui utilise toujours systemd.
Greg Slepak

10

La redirection de port était la plus logique pour nous, mais nous avons rencontré un problème où notre application résoudrait une URL localement qui devait également être réacheminée; (cela signifie que vous shindig ).

Cela vous permettra également d'être redirigé lors de l'accès à l'URL sur la machine locale.

iptables -A PREROUTING -t nat -p tcp --dport 80 -j REDIRECT --to-port 8080
iptables -A OUTPUT -t nat -p tcp --dport 80 -j REDIRECT --to-port 8080

9

Prise en charge de Linux moderne /sbin/sysctl -w net.ipv4.ip_unprivileged_port_start=0.


8

Avec systemd, il vous suffit de modifier légèrement votre service pour accepter les sockets préactivés.

Vous pourrez ultérieurement utiliser la prise systemd socket activate .

Aucune capacité, iptables ou autres astuces ne sont nécessaires.

C'est le contenu des fichiers systemd pertinents de cet exemple de serveur http python simple

Fichier httpd-true.service

[Unit]
Description=Httpd true 

[Service]
ExecStart=/usr/local/bin/httpd-true
User=subsonic

PrivateTmp=yes

Fichier httpd-true.socket

[Unit]
Description=HTTPD true

[Socket]
ListenStream=80

[Install]
WantedBy=default.target

7

Au démarrage:

iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8080

Ensuite, vous pouvez vous lier au port vers lequel vous transférez.


--to-portn'existe? man iptablesmentionne seulement --to-ports(pluriel).
Abdull

ive remarqué quelques différences et j'ai sauté
James

Voir cette réponse pour savoir comment combiner cela avec systemd.
Greg Slepak


3

Il y a aussi la «voie djb». Vous pouvez utiliser cette méthode pour démarrer votre processus en tant que root s'exécutant sur n'importe quel port sous tcpserver, puis il donnera le contrôle du processus à l'utilisateur que vous spécifiez immédiatement après le démarrage du processus.

#!/bin/sh

UID=$(id -u username)
GID=$(id -g username)
exec tcpserver -u "${UID}" -g "${GID}" -RHl0 0 port /path/to/binary &

Pour plus d'informations, voir: http://thedjbway.b0llix.net/daemontools/uidgid.html


1

Étant donné que l'OP n'est qu'un développement / test, des solutions moins qu'élégantes peuvent être utiles:

setcap peut être utilisé sur l'interpréteur d'un script pour accorder des capacités aux scripts. Si setcaps sur le binaire de l'interpréteur global n'est pas acceptable, faites une copie locale du binaire (n'importe quel utilisateur peut) et obtenez root pour setcap sur cette copie. Python2 (au moins) fonctionne correctement avec une copie locale de l'interpréteur dans votre arbre de développement de script. Aucun suid n'est nécessaire pour que l'utilisateur root puisse contrôler les capacités auxquelles les utilisateurs ont accès.

Si vous devez suivre les mises à jour de l'interpréteur à l'échelle du système, utilisez un script shell comme le suivant pour exécuter votre script:

#!/bin/sh
#
#  Watch for updates to the Python2 interpreter

PRG=python_net_raw
PRG_ORIG=/usr/bin/python2.7

cmp $PRG_ORIG $PRG || {
    echo ""
    echo "***** $PRG_ORIG has been updated *****"
    echo "Run the following commands to refresh $PRG:"
    echo ""
    echo "    $ cp $PRG_ORIG $PRG"
    echo "    # setcap cap_net_raw+ep $PRG"
    echo ""
    exit
}

./$PRG $*

1

J'ai essayé la méthode iptables PREROUTING REDIRECT. Dans les noyaux plus anciens, il semble que ce type de règle ne soit pas pris en charge pour IPv6 . Mais apparemment, il est désormais pris en charge dans ip6tables v1.4.18 et le noyau Linux v3.8.

J'ai également constaté que PREROUTING REDIRECT ne fonctionne pas pour les connexions initiées dans la machine. Pour travailler pour les connexions depuis la machine locale, ajoutez également une règle de SORTIE - voir La redirection de port iptables ne fonctionne pas pour localhost . Par exemple, quelque chose comme:

iptables -t nat -I OUTPUT -o lo -p tcp --dport 80 -j REDIRECT --to-port 8080

J'ai également constaté que PREROUTING REDIRECT affecte également les paquets transférés . Autrement dit, si la machine transfère également des paquets entre des interfaces (par exemple, si elle agit comme un point d'accès Wi-Fi connecté à un réseau Ethernet), la règle iptables interceptera également les connexions des clients connectés vers des destinations Internet et les redirigera vers la machine. Ce n'est pas ce que je voulais - je voulais seulement rediriger les connexions qui étaient dirigées vers la machine elle-même. J'ai trouvé que je peux faire en sorte qu'il n'affecte que les paquets adressés à la boîte, en ajoutant -m addrtype --dst-type LOCAL. Par exemple, quelque chose comme:

iptables -A PREROUTING -t nat -p tcp --dport 80 -m addrtype --dst-type LOCAL -j REDIRECT --to-port 8080

Une autre possibilité consiste à utiliser la redirection de port TCP. Par exemple en utilisant socat:

socat TCP4-LISTEN:www,reuseaddr,fork TCP4:localhost:8080

Cependant, un inconvénient de cette méthode est que l'application qui écoute sur le port 8080 ne connaît pas l'adresse source des connexions entrantes (par exemple pour la journalisation ou à d'autres fins d'identification).


0

Réponse à 2015 / septembre:

ip6tables prend désormais en charge IPV6 NAT: http://www.netfilter.org/projects/iptables/files/changes-iptables-1.4.17.txt

Vous aurez besoin du noyau 3.7+

Preuve:

[09:09:23] root@X:~ ip6tables -t nat -vnL
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination
    0     0 REDIRECT   tcp      eth0   *       ::/0                 ::/0                 tcp dpt:80 redir ports 8080
    0     0 REDIRECT   tcp      eth0   *       ::/0                 ::/0                 tcp dpt:443 redir ports 1443

Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy ACCEPT 6148 packets, 534K bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain POSTROUTING (policy ACCEPT 6148 packets, 534K bytes)
 pkts bytes target     prot opt in     out     source               destination
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.