C'est très ennuyant d'avoir cette limitation sur ma boîte de développement, alors qu'il n'y aura jamais d'autres utilisateurs que moi.
Je suis conscient de les solutions de contournement standard , mais aucun ne fait exactement ce que je veux:
Existe-t-il une variable simple sysctl
permettant aux processus non root de se lier à des ports "privilégiés" (ports inférieurs à 1024) sous Linux, ou ai-je tout simplement de la chance?
EDIT: Dans certains cas, vous pouvez tiliser des fonctionnalités pour le faire.
Très bien, merci aux personnes qui ont signalé le système de capacités et la capacité CAP_NET_BIND_SERVICE
. Si vous avez un noyau récent, il est en effet possible de l'utiliser pour démarrer un service en tant que lecteur non root mais lier des ports faibles. La réponse courte est que vous faites:
setcap 'cap_net_bind_service=+ep' /path/to/program
Et puis à tout moment, program
est exécuté par la suite, il aura la capacité CAP_NET_BIND_SERVICE
. setcap
est dans le paquet Debian libcap2-bin
.
Maintenant pour les mises en garde:
program
disposant de privilèges élevés, comme setcap
ou suid
. Ainsi, si votre program
utilise son propre .../lib/
, vous devrez peut-être envisager une autre option, telle que la redirection de port.Ressources:
setcap
.Remarque: RHEL a d'abord ajouté ceci dans la v6 .
La méthode standard consiste à les définir comme "setuid" afin qu'ils démarrent en tant que root, puis ils jettent ce privilège root dès qu'ils se sont connectés au port, mais avant de commencer à accepter les connexions avec celui-ci. Vous pouvez en voir de bons exemples dans le code source d’Apache et de 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. Seuls un ou deux d'entre eux (qui ne font que 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.
Vous pouvez faire une redirection de port. C’est ce que je fais pour un serveur de règles Silverlight fonctionnant sous Linux
iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 943 -j REDIRECT --to-port 1300
Ou corrigez votre noyau et supprimez la vérification.
(Option de dernier recours, non recommandé).
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.
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 présente l’avantage de travailler avec des serveurs de script et d’être très simple.
Les fonctionnalités de fichier ne sont pas idéales, car elles peuvent se rompre après la mise à jour d'un paquet.
La solution idéale, à mon humble avis, devrait être la possibilité de créer un shell avec un jeu CAP_NET_BIND_SERVICE
héritable.
Voici une façon un peu compliquée de faire ceci:
sg $DAEMONUSER "capsh --keep=1 --uid=`id -u $DAEMONUSER` \
--caps='cap_net_bind_service+pei' -- \
YOUR_COMMAND_GOES_HERE"
L'utilitaire capsh
peut être trouvé dans le paquet libcap2-bin des distributions Debian/Ubuntu. Voici ce qui se passe:
sg
change l'identifiant de groupe effectif en celui de l'utilisateur démon. Ceci est nécessaire parce que capsh
laisse GID inchangé et nous ne l’en voulons absolument pas.$DAEMONUSER
--keep=1
), à l'exception de cap_net_bind_service
pouvant être héritéLe résultat est un processus avec l'utilisateur et le groupe spécifiés, ainsi que les privilèges cap_net_bind_service
.
Par exemple, une ligne du script de démarrage ejabberd
:
sg $EJABBERDUSER "capsh --keep=1 --uid=`id -u $EJABBERDUSER` --caps='cap_net_bind_service+pei' -- $EJABBERD --noshell -detached"
Mise à jour 2017:
Authbind accorde la confiance à l'utilisateur/au groupe et permet de contrôler l'accès par port. Il prend en charge IPv4 et IPv6 (la prise en charge d'IPv6 a été ajoutée récemment).
Installer: apt-get install authbind
Configurez l’accès aux ports appropriés, par exemple 80 et 443 pour tous les utilisateurs et groupes:
Touchez Sudo/etc/authbind/byport/80
Sudo touch// etc/authbind/byport/443
Sudo chmod 777/etc/authbind/byport/80
Sudo chmod 777/etc/authbind/byport/443
Exécutez votre commande via authbind
(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
En guise de suivi de la recommandation fabuleuse (= non recommandée à moins que vous ne sachiez ce que vous faites) de pirater le noyau:
Je l'ai d'abord posté ici .
Facile. Avec un noyau normal ou ancien, ce n'est pas le cas.
Comme l'ont souligné d'autres personnes, 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 une limite sur l'interpréteur Shell, 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 JVM Java
Sudo /sbin/setcap 'cap_net_bind_service=ep' /usr/lib/jvm/Java-8-openjdk/jre/bin/Java
Cela signifie évidemment que tout programme Java peut lier des ports système.
Dito pour mono/.NET.
Je suis également convaincu que xinetd n'est pas la meilleure des idées.
Mais puisque 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 exécuter le vôtre.
Vous venez de télécharger le code source du dernier noyau (ou du même logiciel 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 le changer en
#define PROT_SOCK 0
si vous ne voulez pas avoir une situation ssh non sécurisée, vous la changez en ceci: #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 vous compilez 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 bref, 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.
Deux autres possibilités simples:
Il existe une ancienne solution (non à la mode) au "démon qui se lie sur un port faible et qui contrôle le démon". Cela s'appelle inetd (ou xinetd). Les inconvénients sont:
Avantages:
Une autre alternative: un proxy piraté (netcat ou même quelque chose plus robuste) du port privilégié à un port arbitraire portant un 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 dev", non?). De cette façon, vous pouvez continuer à utiliser une version de votre serveur compatible réseau, vous n’auriez besoin que de root/Sudo pour démarrer le proxy (au démarrage), sans compter sur des capacités complexes/potentiellement fragiles.
Mon "solution de contournement standard" utilise socat comme redirecteur d'espace utilisateur:
socat tcp6-listen:80,fork tcp6:8080
Attention, cela n'échelonnera pas, le bricolage coûte cher, mais c'est la façon dont fonctionne le socat.
Je sais que c’est une vieille question, mais avec les noyaux récents (> = 4.3), il ya finalement 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 binaire progs/capsh
résultant quelque part (/usr/local/bin
est un bon choix). Ensuite, en tant que root, lancez 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
cap_net_bind_service
aux ensembles & ambient héritésbash -c 'your-command'
(puisque capsh
démarre automatiquement bash avec les arguments après --
)Il se passe beaucoup de choses sous le capot ici.
Tout d’abord, nous fonctionnons en tant qu’utilisateur root. Par conséquent, nous disposons par défaut d’un ensemble complet de fonctionnalités. Cela inclut la possibilité de commuter uid & gid avec les appels système setuid
et setgid
. Cependant, normalement, lorsqu'un programme fait cela, il perd son ensemble de capacités - c'est ainsi que l'ancienne méthode de suppression de racine avec setuid
fonctionne toujours. L’indicateur --keep=1
indique à capsh
d’émettre le prctl(PR_SET_KEEPCAPS)
syscall, qui désactive la suppression de fonctionnalités lors du changement d’utilisateur. Le changement effectif d'utilisateurs de capsh
a lieu avec l'indicateur --user
, qui exécute setuid
et setgid
.
Le problème suivant que nous devons résoudre est de savoir comment définir les capacités de manière à ce que nous continuions après exec
nos 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 sur un execve (2)" [ capacités (7) ]. Bien que cela semble résoudre notre problème (il suffit de définir la capacité cap_net_bind_service
sur héritée, non?), Cela ne s'applique en réalité qu'aux processus privilégiés - et notre processus n'est plus privilégié, car nous avons déjà changé d'utilisateur (avec le --user
drapeau).
Le nouvel ensemble de capacités ambiantes contourne ce problème: il s'agit "d'un ensemble de capacités préservées sur un execve (2) d'un programme non privilégié". En plaçant cap_net_bind_service
dans le jeu d'ambiances, lorsque capsh
exec est notre programme serveur, notre programme héritera de cette capacité et pourra lier les écouteurs à des ports bas.
Si vous souhaitez en savoir plus, les capacités page de manuel expliquent cela en détail. Utiliser capsh
à strace
est également très instructif!
Linux prend en charge fonctionnalités pour prendre en charge des autorisations plus détaillées que "cette application est exécutée en tant que root". Une de ces fonctionnalités est CAP_NET_BIND_SERVICE
, qui concerne 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 lui donnant CAP_NET_BIND_SERVICE
(utilisant probablement setcap
, mais il y a forcément une solution existante pour cela) .
TLDR: Pour "la réponse" (à mon avis), sautez à 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 s'excuser pour la promotion de une autre réponse (les deux ici et sur Twitter) que je pensais être "le meilleur", mais après l'avoir essayé, j'ai découvert que je m'étais trompé à ce sujet. Apprenez de mon erreur, 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 consistait à utiliser systemd
avec 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 fonctionnalités sont destinées à restreindre les processus racine!
Comme l'OP l'a sagement déclaré, 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 fonctionnalités avec User=
et Group=
dans systemd
fichiers d'unité, car les fonctionnalités sont TOUJOURS réinitialisées lorsque execev
(ou quelle que soit la fonction) est appelée. En d’autres termes, lorsque systemd
forche et abandonne ses perms, les capacités sont réinitialisées. Il n'y a pas 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 Capabilities fournira jamais la bonne réponse à cette question (du moins dans un avenir rapproché). Incidemment, setcap
, comme d'autres l'ont mentionné, n'est pas une solution. Cela n'a pas fonctionné pour moi, cela ne fonctionne pas très bien avec les scripts, et ceux-ci sont réinitialisés quand le fichier change.
Dans ma maigre défense, j’ai indiqué (dans le commentaire que j’ai maintenant supprimé), que la suggestion de James iptables (que le PO mentionne également) était la "2e meilleure solution" ". :-P
>> TLDR <<
La solution consiste à combiner systemd
avec des commandes à la volée iptables
, comme ceci ( tiré 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 les tâches suivantes:
iptables
systemd
nettoie les règles de pare-feu pour nous, en veillant à les supprimer lorsque le démon n'est pas en cours d'exécution.systemd
prétend à), même si le démon est compromis et définit uid=0
.iptables
est malheureusement toujours un utilitaire assez laid et difficile à utiliser. Si le démon écoute sur eth0:0
au lieu de eth0
, par exemple, les commandes sont légèrement différent .
systemd est un remplacement sysvinit doté d'une option permettant de lancer un démon doté de fonctionnalités spécifiques. Options Capabilities =, CapabilityBoundingSet = in systemd.exec (5) page de manuel.
Pour une raison quelconque, personne ne mentionne la possibilité d'abaisser 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 connecter 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:
En fonction de la situation, je choisirais entre sysctl, CAP, authbind et iptables-redirect. Et c’est formidable que nous ayons tant d’options.
La redirection de port nous paraissait la plus logique, mais nous avons rencontré un problème dans lequel notre application résoudrait localement une URL qui devait également être redirigé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
Au démarrage:
iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8080
Ensuite, vous pouvez vous connecter au port que vous transférez.
Avec systemd, il vous suffit de modifier légèrement votre service pour accepter les sockets préactivés.
Vous pouvez utiliser plus tard activation de la prise systemd .
Aucune capacité, iptables ou autres astuces sont nécessaires.
Ceci est le contenu des fichiers systemd pertinents de cet exemple de simple serveur python http
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
Il y a aussi la "manière de djb". Vous pouvez utiliser cette méthode pour démarrer votre processus en tant qu'utilisateur root sur n'importe quel port sous tcpserver. Le contrôle du processus sera ensuite confié à l'utilisateur spécifié immédiatement après le démarrage du processus.
#!/bin/sh
UID=`id -u yourusername`
GID=`id -g yourusername`
exec tcpserver -u $UID -g $GID -RHl0 0 portnumber /path/to/your/process &
Pour plus d'informations, voir: http://thedjbway.b0llix.net/daemontools/uidgid.html
Utilisez l'utilitaire privbind : il permet à une application sans privilège de se lier à des ports réservés.
J'ai essayé la méthode iptables PREROUTING REDIRECT. Dans les noyaux plus anciens, il semble que ce type de règle n'était pas supporté par IPv6 . Mais apparemment, il est maintenant supporté par ip6tables v1.4.18 et le noyau Linux v3.8.
J'ai également constaté que PREROUTING REDIRECT ne fonctionnait pas pour les connexions initiées dans la machine. Pour utiliser les connexions de la machine locale, ajoutez également une règle OUTPUT - voir la redirection du 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 interfaces (par exemple, si elle agit comme un point d’accès Wi-Fi connecté à un réseau Ethernet), la règle iptables intercepte également les connexions des clients connectés aux destinations Internet et les redirige 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 ne pouvais que faire en sorte que les paquets adressés à la boîte soient affectés 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 TCP transfert de port. Par exemple. en utilisant socat
:
socat TCP4-LISTEN:www,reuseaddr,fork TCP4:localhost:8080
Toutefois, l’un des inconvénients 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, à des fins de journalisation ou d’identification).
Puisque le PO n’est qu’une phase de développement/test, il peut être utile de trouver des solutions moins sophistiquées:
setcap peut être utilisé sur un interpréteur de script pour attribuer des fonctionnalités à des scripts. Si setcaps sur le binaire d'interprète global n'est pas acceptable, créez une copie locale du fichier 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 fonctionnalité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 suit 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 $*
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
Linux moderne supporte /sbin/sysctl -w net.ipv4.ip_unprivileged_port_start=0
.