Comment faire fonctionner un serveur sur le port 80 en tant qu'utilisateur normal sous Linux?

311

Je cherche une solution sur Google depuis assez longtemps, mais je n’ai pas trouvé de réponse.

Je suis sous Ubuntu Linux et souhaite utiliser un serveur sur le port 80, mais en raison du mécanisme de sécurité d'Ubuntu, l'erreur suivante apparaît:

java.net.BindException: Autorisation refusée: 80

Je pense qu'il devrait être assez simple pour désactiver ce mécanisme de sécurité afin que le port 80 soit disponible pour tous les utilisateurs ou pour attribuer les privilèges requis à l'utilisateur actuel pour accéder au port 80.

Deepak Mittal
la source
3
Quel est le problème pour exécuter le serveur sur un autre port non privilégié? Vous pensez à quelque chose d'aussi sévère que de désactiver le mécanisme de sécurité sans fournir au moins une raison très sérieuse d'exécuter un serveur sur ce port. Le serveur est-il codé en dur pour se connecter au port 80? Si oui, jetez-le.
Anonyme
4
Tu ne peux pas. Les ports inférieurs à 1024 sont privilégiés et seule la racine peut ouvrir des sockets d’écoute dessus. La bonne chose à faire est de supprimer les autorisations après l’avoir ouvert.
Falcon Momot
2
"Anonymous" - les utilisateurs du monde entier ont été formés à la recherche de certains services dans certains ports. Dans certains cas, il a été normalisé. Par exemple, HTTP sur le port 80 et HTTPS sur le port 443. Il est difficile de changer les utilisateurs et les normes du monde.
1
@FalconMomot Si les ports inférieurs à 1024 sont privilégiés, pourquoi le serveur Apache n'en a-t-il pas besoin pour entrer un mot de passe pour obtenir les privilèges root sur le port 80, et comment procède-t-il de cette manière? Je pense que le questionneur veut savoir comment faire les choses de cette manière (que ce soit techniquement utilisé comme root ou non). N'hésitez pas à me dire si je me trompe, Deepak Mittal.
Shule

Réponses:

355

Réponse courte: vous ne pouvez pas. Les ports inférieurs à 1024 ne peuvent être ouverts que par root. Comme indiqué dans commentaire - vous pouvez utiliser CAP_NET_BIND_SERVICE , mais cette approche, appliquée à java bin, fera que tout programme java sera exécuté avec ce paramètre, ce qui est indésirable, si ce n’est un risque pour la sécurité.

La réponse longue: vous pouvez rediriger les connexions sur le port 80 vers un autre port que vous pouvez ouvrir en tant qu'utilisateur normal.

Exécuter en tant que root:

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

Comme les périphériques en boucle (tels que localhost) n'utilisent pas les règles de préroutage, si vous devez utiliser localhost, etc., ajoutez également cette règle ( merci @Francesco ):

# iptables -t nat -I OUTPUT -p tcp -d 127.0.0.1 --dport 80 -j REDIRECT --to-ports 8080

REMARQUE: La solution ci-dessus ne convient pas aux systèmes multi-utilisateurs, car tout utilisateur peut ouvrir le port 8080 (ou tout autre port haut que vous décidez d'utiliser), interceptant ainsi le trafic. (Crédits à CesarB ).

EDIT: selon la question de commentaire - pour supprimer la règle ci-dessus:

# iptables -t nat --line-numbers -n -L

Cela produira quelque chose comme:

Chain PREROUTING (policy ACCEPT)
num  target     prot opt source               destination         
1    REDIRECT   tcp  --  0.0.0.0/0            0.0.0.0/0           tcp dpt:8080 redir ports 8088
2    REDIRECT   tcp  --  0.0.0.0/0            0.0.0.0/0           tcp dpt:80 redir ports 8080

La règle qui vous intéresse est nr. 2, donc pour le supprimer:

# iptables -t nat -D PREROUTING 2
Ensoleillé
la source
13
@Sunny: les votes positifs ne concernent pas le canon le plus rapide de l'ouest, mais les meilleures réponses. Le vôtre est le meilleur jusqu'à présent (je n'ai mentionné que iptables; vous avez en fait fourni la ligne de commande complète). La seule chose que le mien n'a pas à vous, c'est la mise en garde sur le fait que d'autres utilisateurs peuvent également se
connecter
3
Notez que cela ne fonctionne pas avec IPv6.
Emmanuel Bourg
3
Quelqu'un pourrait-il expliquer comment je peux supprimer cette règle plus tard, quand je le souhaite? Après l'avoir exécuté, cela fonctionne, mais ce n'est apparemment pas une "règle" car il ne s'affiche pas quand je le fais sudo iptables --list. Je sais ce qu’est et ce que fait iptables, mais je ne l’avais jamais vraiment utilisé auparavant.
Encoderer
4
Merci pour votre réponse ... chaque fois que je redémarre Ubuntu, cette règle disparaît et je dois l'exécuter à nouveau. Y a-t-il un moyen de le sauver pour toujours?
Coderji
4
@Coderji: consultez la section "Enregistrer" de la documentation de la communauté: help.ubuntu.com/community/IptablesHowTo
Sunny
79

Utilisez authbind .

Cela fonctionne même avec Java si vous activez la pile Java uniquement pour IPv4. J'utilise:

authbind --deep $JAVA_HOME/bin/java -Djava.net.preferIPv4Stack=true …
géocar
la source
4
Si le serveur est Tomcat, vous pouvez utiliser automatiquement authbind en définissant AUTHBIND=yesdans / etc / default / tomcat6
Emmanuel Bourg
Je ne pouvais pas obtenir que cela fonctionne sur le serveur Ubuntu avec le package Java par défaut ...
Ashley
Moi non plus, aucune solution connue?
marquer
10
Notez que vous devez configurer authbindpour permettre réellement que cela se produise. À partir de la page de manuel: "/ etc / authbind / byport / port est testé. Si ce fichier est accessible à l'exécution par l'utilisateur appelant, conformément à access (2), la liaison au port est autorisée." , Par exemple pour le port 80, sudo touch /etc/authbind/byport/80 ; sudo chmod 777 /etc/authbind/byport/80. L'installation initiale de authbindn'a généralement aucune autorisation préconfigurée.
Jason C
4
Oh nonononononono, ne jamais rien chmod 777!
Johannes
57

Si votre système le prend en charge, vous pourriez peut-être utiliser des fonctionnalités. Voir les capacités man, celle dont vous avez besoin serait CAP_NET_BIND_SERVICE.

Sur les nouvelles Debian / Ubuntu, vous pouvez exécuter:

sudo apt-get install libcap2-bin 
sudo setcap 'cap_net_bind_service=+ep' /path/to/program
Otto Kekäläinen
la source
cela fonctionne pour nodejs: setcap 'cap_net_bind_service = + ep' / usr / bin / nodejs
JasonS
5
Cette. Je me demande pourquoi cette réponse n'a pas eu plus de votes positifs. Beaucoup plus facile que l'option iptables imho.
Dominik R
5
C’est la réponse la plus correcte et la plus efficace. Toutes les autres réponses ont un impact négatif sur les performances ou sont simplement incertaines / peu sûres.
OneOfOne
41

Une autre solution consiste à configurer votre application de sorte qu'elle puisse se connecter au port 80. En tant que root, procédez comme suit:

chown root ./myapp
chmod +S ./myapp

Gardez à l'esprit que cela, à moins que cela ne soit absolument correct, vous exposera à des failles de sécurité, car votre application communiquera avec le réseau et fonctionnera avec les privilèges root complets. Si vous prenez cette solution, vous devriez consulter le code source d’Apache ou de Lighttpd ou quelque chose de similaire, où ils utilisent les privilèges root pour ouvrir le port, puis abandonnent immédiatement ces privilèges et "deviennent" des utilisateurs avec des privilèges inférieurs. un pirate de l'air ne peut pas s'emparer de tout votre ordinateur.

Mise à jour: comme on le voit dans cette question , il apparaît que les noyaux Linux depuis la version 2.6.24 ont une nouvelle capacité qui vous permet de marquer un exécutable (mais pas un script, bien entendu) comme ayant la CAP_NET_BIND_SERVICEcapacité " ". Si vous installez le paquet debian "libcap2-bin", vous pouvez le faire en lançant la commande

setcap 'cap_net_bind_service=+ep' /path/to/program
Paul Tomblin
la source
6
Cela revient à s’exécuter en tant que root, à moins que l’application ne sache supprimer les privilèges.
CesarB
14
C'est dangereux. Cela signifie que toute demande sera exécutée en tant que root. C'est pour une raison que même apache commence par se connecter en tant que root, puis transfère les privilèges à un autre utilisateur.
Ensoleillé du
9
Paul: iptables n’est pas si dangereux, car même si l’application est compromise, elle n’exposera pas le système en cas d’attaque, du moins avec les privilèges root. Exécuter l'application en tant que root est une autre histoire.
Ensoleillé
1
Le danger pour iptables est que s’il s’agit d’un système multi-utilisateurs, comme le dit CesarB, tout le monde pouvant se lier à 8080 et intercepter le trafic.
Ensoleillé
1
lol, j'adore la réponse acceptée a -15
Evan Teran
40

J'utilise simplement Nginx devant. Il peut aussi fonctionner sur localhost.

  • apt-get install nginx

.. ou ..

  • pkg_add -r nginx

.. ou ce qui convient à votre système d'exploitation.

Tout ce dont vous avez besoin dans nginx.conf, si vous utilisez localhost, est:

serveur {
        écoutez 80;
        nom_serveur some.domain.org;
        emplacement / {
            proxy_set_header Host $ host;
            proxy_set_header X-Real-IP $ remote_addr;
            proxy_set_header X-Forwarded-For $ proxy_add_x_forwarded_for;
            proxy_pass http://127.0.0.1:8081;
        }
}
CosmicB
la source
J'aime beaucoup cette solution.
thomasfedb
1
Voici l'une des solutions suggérées par JFrog eux-mêmes: jfrog.com/confluence/display/RTF/nginx
stolsvik
36

Approche proposée par Sunny et CesarB:

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

fonctionne bien mais présente un petit inconvénient: cela n’empêche pas l’utilisateur de se connecter directement au port 8080 au lieu de 80.

Considérez le scénario suivant lorsque cela peut être un problème.

Disons que nous avons un serveur qui accepte les connexions HTTP sur le port 8080 et les connexions HTTPS sur le port 8181.

Nous utilisons iptables pour établir les redirections suivantes:

80  ---> 8080
443 ---> 8181

Supposons maintenant que notre serveur décide de rediriger l'utilisateur d'une page HTTP vers une page HTTPS. Si nous ne réécrivions pas soigneusement la réponse, elle serait redirigée vers https://host:8181/. À ce stade, nous sommes foutus:

  • Certains utilisateurs marqueraient l' https://host:8181/URL d'un signet et nous aurions besoin de la conserver pour éviter de casser leurs signets.
  • Les autres utilisateurs ne pourraient pas se connecter car leurs serveurs proxy ne prennent pas en charge les ports SSL non standard.

J'utilise l'approche suivante:

iptables -t mangle -A PREROUTING -p tcp --dport 80 -j MARK --set-mark 1
iptables -t mangle -A PREROUTING -p tcp --dport 443 -j MARK --set-mark 1
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080
iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 8181
iptables -I INPUT -m state --state NEW -m tcp -p tcp --dport 8080 -m mark --mark 1 -j ACCEPT
iptables -I INPUT -m state --state NEW -m tcp -p tcp --dport 8181 -m mark --mark 1 -j ACCEPT

Combinée à la règle REJECT par défaut sur la chaîne INPUT, cette approche empêche les utilisateurs de se connecter directement aux ports 8080 et 8181.

Misha
la source
1
C’est bien, mais pourquoi ne pas simplement lier le démon à localhost: 8080 au lieu de 0.0.0.0:8080? Je veux faire ça, mais j'ai besoin des iptables pour ça.
Amala
Ça marche, vraiment cool.
xtian
29

Traditionnellement sous Unix, seul root peut se lier aux ports bas (<1024).

La méthode la plus simple consiste à exécuter votre serveur sur un port élevé (par exemple, 8080) et à utiliser une simple règle iptables pour transférer les connexions du port 80 au port 8080. Notez que vous perdez ainsi la protection supplémentaire offerte par le ports bas; n'importe quel utilisateur de votre ordinateur peut se connecter au port 8080.

CesarB
la source
23

Si votre système le prend en charge, vous pourriez peut-être utiliser des fonctionnalités. Vous voyez man capabilities, celui dont vous avez besoin serait CAP_NET_BIND_SERVICE. Non, je ne les ai jamais utilisées moi-même et je ne sais pas si elles fonctionnent vraiment :-)

WMR
la source
1
ils fonctionnent, j’utilise CAP_NET_RAW pour exécuter des outils tels que tcpdump en tant qu’utilisateur normal.
Justin
12

Utilisez un proxy inverse (nginx, apache + mod_proxy) ou un proxy inverse en cache (Squid, Varnish) devant vos serveurs d'applications!

Avec un proxy inverse, vous pouvez réaliser beaucoup de choses intéressantes comme:

  • L'équilibrage de charge
  • Redémarrage de vos serveurs d'applications avec les utilisateurs recevant une page d'erreur sophistiquée
  • Accélérer les choses avec le cache
  • Paramètres détaillés que vous définissez normalement avec un proxy inverse et non avec un serveur d'applications
Giovanni Toraldo
la source
6

Vous pouvez utiliser le programme redir:

sudo redir --lport=80 --laddr=192.168.0.101 --cport 9990 --caddr=127.0.0.1
utilisateur340994
la source
4

Utilisez sudo.

Configurez sudo pour que l'utilisateur normal puisse exécuter les commandes appropriées:

/etc/init.d/httpd start

Ou

apachectl stop

Ou

/usr/local/apache2/apachectl restart

Ou

/myapp/myappbin start

(Ou quelque autre commande / script que vous utilisez pour démarrer / arrêter votre serveur Web / application en particulier)

Pas maintenant
la source
8
Cela pose le même problème: exécuter un service en tant que root est une mauvaise pratique.
Kevin Panko
4

La réponse de sunny est correcte, mais vous pouvez faire face à des problèmes supplémentaires car l'interface de bouclage n'utilise pas la table PREROUTING,

donc les règles iptables à ajouter sont deux:

iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080
iptables -t nat -I OUTPUT -p tcp -d 127.0.0.1 --dport 80 -j REDIRECT --to-ports 8080
Francesco
la source
3

Avec Linux, vous avez deux autres options:

Les deux extensions du noyau Linux permettent d’accorder des droits d’accès sur un niveau de grain très fin. Cela vous permettrait d'accorder à ce processus d'ouvrir le port 80, mais il n'hériterait d'aucun des autres droits root.

D'après ce que j'ai entendu, grsecurity est beaucoup plus simple à utiliser mais SELinux est plus sécurisé.

Aaron Digulla
la source
2

Une solution consiste à utiliser iptables pour exécuter PAT sur les paquets du port 80. Vous pouvez l’utiliser pour router les paquets vers le port local 8080, par exemple. Assurez-vous de régler les paquets sortants sur le port 80.

D'après mon expérience, les fonctionnalités d'autorisation détaillées de Linux ne sont pas compilées dans les noyaux standard en raison de problèmes de sécurité.


la source
2

Si vous essayez de faire cela pour qu'une commande exécutée par l'utilisateur puisse utiliser le port 80, vos seules solutions sont alors les astuces iptables ou la définition de l'exécutable setuid-to-root.

La manière dont Apache effectue cette opération (elle se connecte au port 80 mais s'exécute en tant que personne autre que root) consiste à s'exécuter en tant que root, à se lier au port, puis à changer la propriété du processus en utilisateur non privilégié après le port. est installé. Si l'application que vous écrivez peut être exécutée par la racine, vous pouvez lui demander de changer de propriétaire en utilisateur non privé après la configuration des ports. Mais s'il s'agit uniquement d'un utilisateur moyen à exécuter à partir de la ligne de commande, vous devrez utiliser l'une des autres solutions.

Rray
la source
2

Lorsque j'ai plusieurs applications de service Web (scripts python, moteurs tomcat, ...) que je ne souhaite pas exécuter en tant que root, je configure généralement un serveur Web Apache devant elles. Apache écoute le port 80 et Tomcat écoute le 8080.

Dans apache: s config:

ProxyPass /webapp http://localhost:8080/webapp
ProxyPassReverse /webapp http://localhost:8080/webapp

Voir la documentation mod-proxy pour plus d'informations: http://httpd.apache.org/docs/2.2/mod/mod_proxy.html


la source
1
L'inconvénient de cette solution est la perte de l'adresse IP entrante dans les journaux Tomcat.
Emmanuel Bourg
2

Je pense que la meilleure solution est de sgid votre application et dès que son port est lié, il devrait abandonner les privilèges en passant à un autre utilisateur.

Anxy Dicy
la source
1

Certains systèmes hôtes ne permettent pas d'utiliser le module NAT, 'iptables' ne résout pas le problème dans ce cas.

Que diriez-vous de xinetd?

Dans mon cas (Ubuntu 10.04)

# apt-get install xinetd
# touch /etc/xinetd.d/my_redirect
# vim /etc/xinetd.d/my_redirect

Collez la configuration:

service my_redirector_80
{
 disable = no
 socket_type = stream
 protocol = tcp
 user = root
 wait = no
 port = 80
 redirect = localhost 8080
 type = UNLISTED
}

Ensuite:

# service xinetd restart

http://docs.codehaus.org/display/JETTY/port80 explique mieux.

Thomas
la source
1
Cela équivaut en gros à placer un proxy inverse devant le serveur, mais cela s'avère plutôt inefficace par rapport à des solutions dédiées telles que HAproxy, Pound ou Varnish qui comprennent le protocole HTTP et peuvent ajouter des en-têtes utiles comme X-Forwarded-For.
Emmanuel Bourg
-1

Par ailleurs, FreeBSD et Solaris (tout le monde se souvient de ça ?) Vous permettent de le faire (liaison vers des ports bas) sans élévation de privilèges (c'est-à-dire, utiliser des programmes pour passer à la racine). Puisque vous avez spécifié Linux, je publie simplement ceci comme un avis à l'intention des autres utilisateurs susceptibles de trouver cette question.

Daniel C. Sobral
la source
-1

Nécromancie.

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 de shell, ce qui ne sert à rien, vous pourriez 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 bidouilles, pourquoi ne pas simplement lever la limite en levant la restriction?
Personne n'a dit que vous devez exécuter un noyau normal, vous pouvez donc utiliser 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émarrer.
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.

Dilemme
la source
Quelqu'un pourrait-il s'il vous plaît expliquer les votes négatifs? La compilation du noyau ne fonctionne pas? Ou est-ce simplement parce qu'après cela, vous ne pouvez plus effectuer de mise à niveau du noyau à partir des référentiels normaux?
Quandary
J'ai essayé cette solution ( sudo setcap cap_net_bind_service=+ep /path-to-java/java) mais j'obtiens toujours java: error while loading shared libraries: libjli.so: cannot open shared object file: No such file or directorysi java a mis des majuscules. voir aussi la première partie de unix.stackexchange.com/a/16670/55508
Daniel Alder
1
Résolu l'erreur précédente en ajoutant <javahome>/lib/amd64/jli/à ld.so.confet le fonctionnementsudo ldconfig
Daniel Alder