Wie führe ich einen Server an Port 80 als normaler Benutzer unter Linux aus?

311

Ich habe lange über eine Lösung gegoogelt, aber keine Antwort gefunden.

Ich arbeite unter Ubuntu Linux und möchte einen Server auf Port 80 ausführen. Aufgrund des Sicherheitsmechanismus von Ubuntu erhalte ich jedoch die folgende Fehlermeldung:

java.net.BindException: Berechtigung verweigert: 80

Ich denke, es sollte einfach genug sein, diesen Sicherheitsmechanismus entweder zu deaktivieren, damit Port 80 für alle Benutzer verfügbar ist, oder dem aktuellen Benutzer die erforderlichen Berechtigungen für den Zugriff auf Port 80 zuzuweisen.

Deepak Mittal
quelle
3
Was ist das Problem, wenn der Server an einem anderen, nicht privilegierten Port ausgeführt wird? Sie denken über so harte Dinge nach, wie das Deaktivieren des Sicherheitsmechanismus, ohne zumindest einen sehr schwerwiegenden Grund für die Ausführung eines Servers an diesem Port anzugeben. Ist der Server für die Bindung an Port 80 fest codiert? Wenn ja, werfen Sie es weg.
Anonym
5
Mögliches Duplikat eines regulären Benutzers mit Ports unter 1024
Roman
4
Das kannst du nicht. Ports unter 1024 sind privilegiert und nur Root kann Listening-Sockets auf ihnen öffnen. Es ist angebracht, die Berechtigungen nach dem Öffnen zu löschen.
Falcon Momot
2
"Anonym" - Die Benutzer auf der Welt wurden geschult, an bestimmten Häfen nach bestimmten Diensten zu suchen. In einigen Fällen wurde es standardisiert. Zum Beispiel HTTP an Port 80 und HTTPS an Port 443. Es ist schwierig, die Benutzer und Standards der Welt zu ändern.
1
@FalconMomot Wenn Ports unter 1024 privilegiert sind, warum benötigt der Apache-Server kein Passwort, um Root-Privilegien für Port 80 zu erhalten, und wie geschieht dies? Ich denke, der Fragesteller möchte wissen, wie man es so macht (ob es technisch als Root verwendet wird oder nicht). Sie können mir gerne sagen, ob ich falsch liege, Deepak Mittal.
Shule

Antworten:

355

Kurze Antwort: Das kannst du nicht. Ports unter 1024 können nur von root geöffnet werden. Wie im Kommentar erwähnt, können Sie mit CAP_NET_BIND_SERVICE , aber dieser Ansatz, der auf Java Bin angewendet wird, bewirkt , dass jedes Java-Programm mit dieser Einstellung ausgeführt wird. Dies ist unerwünscht, wenn nicht sogar ein Sicherheitsrisiko.

Die lange Antwort: Sie können Verbindungen auf Port 80 auf einen anderen Port umleiten, den Sie als normaler Benutzer öffnen können.

Als root ausführen:

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

Da Loopback-Geräte (wie localhost) die Prerouting-Regeln nicht verwenden, falls Sie localhost usw. verwenden müssen, fügen Sie auch diese Regel hinzu ( danke @Francesco ):

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

ANMERKUNG: Die obige Lösung eignet sich nicht für Systeme mit mehreren Benutzern, da jeder Benutzer den Port 8080 (oder einen anderen hohen Port, den Sie verwenden möchten) öffnen und so den Datenverkehr abfangen kann. ( Dank an CesarB ).

BEARBEITEN: gemäß Kommentarfrage - um die obige Regel zu löschen:

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

Dies wird ungefähr so ​​aussehen:

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

Die Regel, die Sie interessiert, ist nr. 2, um es zu löschen:

# iptables -t nat -D PREROUTING 2
Sonnig
quelle
13
@Sunny: Upvotes sind nicht für die schnellste Waffe im Westen, sondern für die besten Reaktionen. Ihr ist das Beste (ich habe nur iptables erwähnt; Sie haben tatsächlich die vollständige Befehlszeile angegeben). Das einzige, was meins nicht hat, ist die Einschränkung, dass andere Benutzer auch an Port 8080 binden können.
CesarB
3
Beachten Sie, dass dies mit IPv6 nicht funktioniert.
Emmanuel Bourg
3
Könnte jemand erklären, wie ich diese Regel später entfernen kann, wenn ich möchte? Nachdem ich es ausgeführt habe, funktioniert es, aber es ist anscheinend keine "Regel", weil es nicht auftaucht, wenn ich es tue sudo iptables --list. Ich weiß, was iptables ist und macht, aber ich habe es vorher noch nie wirklich benutzt.
Encoderer
4
Danke für deine Antwort ... wenn ich Ubuntu neu starte, verschwindet diese Regel und ich muss sie erneut ausführen. Gibt es eine Möglichkeit, es für immer zu retten?
Coderji
4
@Coderji: Überprüfen Sie den Abschnitt "Speichern" in der Community-Dokumentation: help.ubuntu.com/community/IptablesHowTo
Sunny
79

Benutze authbind .

Es funktioniert sogar mit Java, wenn Sie den Nur-IPv4-Stack von Java aktivieren. Ich benutze:

authbind --deep $JAVA_HOME/bin/java -Djava.net.preferIPv4Stack=true …
Geocar
quelle
4
Wenn der Server Tomcat ist, können Sie authbind automatisch verwenden, indem Sie AUTHBIND=yesin / etc / default / tomcat6
Emmanuel Bourg den
Ich konnte dies nicht auf Ubuntu-Server mit dem Standard-Java-Paket arbeiten ...
Ashley
Mir auch keine bekannte Lösung?
Markieren Sie den
10
Beachten Sie, dass Sie konfigurieren müssen authbind, um dies tatsächlich zuzulassen. Auf der Manpage: "/ etc / authbind / byport / port wird getestet. Wenn diese Datei gemäß access (2) für den aufrufenden Benutzer zur Ausführung verfügbar ist, wird die Bindung an den Port autorisiert." ZB für Port 80 sudo touch /etc/authbind/byport/80 ; sudo chmod 777 /etc/authbind/byport/80. Bei der Erstinstallation von sind authbindim Allgemeinen keine Berechtigungen vorkonfiguriert.
Jason C
4
Oh Nonononononono, chmod niemals irgendetwas!
Johannes
57

Wenn Ihr System dies unterstützt, können Sie möglicherweise Funktionen verwenden. Siehe man Fähigkeiten, die Sie benötigen, wäre CAP_NET_BIND_SERVICE.

Unter neuerem Debian / Ubuntu können Sie Folgendes ausführen:

sudo apt-get install libcap2-bin 
sudo setcap 'cap_net_bind_service=+ep' /path/to/program
Otto Kekäläinen
quelle
dies funktioniert für nodejs: setcap 'cap_net_bind_service = + ep' / usr / bin / nodejs
JasonS
5
Diese. Ich frage mich, warum diese Antwort nicht mehr positive Stimmen hat. Viel einfacher als die Option iptables imho.
Dominik R
5
Dies ist die richtige und effizienteste Antwort. Alle anderen Antworten verursachen einen Leistungseinbruch oder sind nur fragwürdig / unsicher.
OneOfOne
41

Eine andere Lösung besteht darin, Ihre App so einzurichten, dass sie mit Port 80 verbunden werden kann. Führen Sie als Root die folgenden Schritte aus

chown root ./myapp
chmod +S ./myapp

Bedenken Sie, dass dies potenzielle Sicherheitslücken birgt, wenn es nicht absolut richtig gemacht wird, da Ihre App mit dem Netzwerk kommuniziert und mit vollständigen Root-Berechtigungen ausgeführt wird. Wenn Sie diese Lösung wählen, sollten Sie sich den Quellcode für Apache oder Lighttpd oder ähnliches ansehen, wo sie die Root-Rechte verwenden, um den Port zu öffnen, aber dann sofort diese Rechte aufgeben und ein Benutzer mit niedrigeren Rechten "werden" Ein Hijacker kann nicht Ihren gesamten Computer übernehmen.

Update: Wie in dieser Frage zu sehen ist, haben Linux-Kernel seit 2.6.24 eine neue Funktion, mit der Sie eine ausführbare Datei (aber natürlich kein Skript) als " CAP_NET_BIND_SERVICE" fähig markieren können. Wenn Sie das Debian-Paket "libcap2-bin" installieren, können Sie dies tun, indem Sie den Befehl absetzen

setcap 'cap_net_bind_service=+ep' /path/to/program
Paul Tomblin
quelle
6
Das ist dasselbe wie das Ausführen als root, es sei denn, die App weiß, wie man Privilegien fallen lässt.
CesarB
14
Das ist gefährlich. Dies bedeutet, dass jede Anfrage als root ausgeführt wird. Es gibt einen Grund, warum sogar Apache als Root zum Binden beginnt und dann die Berechtigungen an einen anderen Benutzer abgibt.
Sunny
9
Paul: Die Iptables sind nicht so gefährlich, denn selbst wenn die App kompromittiert wird, wird das System nicht angegriffen, zumindest nicht mit Root-Rechten. Das Ausführen der App als root ist eine andere Geschichte.
Sunny
1
Die Gefahr für iptables besteht nur, wenn dies ein Mehrbenutzersystem ist, wie CesarB sagte, da jeder an 8080 binden und den Verkehr abfangen kann.
Sunny
1
lol, liebe, wie die akzeptierte Antwort eine -15 hat
Evan Teran
40

Ich benutze einfach Nginx vor. Es kann auch auf localhost ausgeführt werden.

  • apt-get install nginx

.. oder ..

  • pkg_add -r nginx

.. oder was auch immer zu Ihrem Betriebssystem passt.

Alles was Sie in der nginx.conf brauchen, wenn Sie auf localhost laufen, ist:

Server {
        höre 80;
        Servername some.domain.org;
        Standort / {
            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
quelle
Ich mag diese Lösung wirklich.
Thomasfedb
1
Dies ist eine der von JFrog selbst vorgeschlagenen Lösungen: jfrog.com/confluence/display/RTF/nginx
stolsvik
36

Lösungsvorschlag von Sunny und CesarB:

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

funktioniert gut, hat aber einen kleinen Nachteil - es hindert den Benutzer nicht daran, eine direkte Verbindung zu Port 8080 anstelle von 80 herzustellen.

Stellen Sie sich das folgende Szenario vor, wenn dies ein Problem sein kann.

Angenommen, wir haben einen Server, der HTTP-Verbindungen an Port 8080 und HTTPS-Verbindungen an Port 8181 akzeptiert.

Wir verwenden iptables, um die folgenden Weiterleitungen einzurichten:

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

Angenommen, unser Server leitet Benutzer von einer HTTP-Seite auf eine HTTPS-Seite um. Wenn wir die Antwort nicht sorgfältig umschreiben, wird sie an weitergeleitet https://host:8181/. An diesem Punkt sind wir geschraubt:

  • Einige Benutzer würden die https://host:8181/URL mit einem Lesezeichen versehen, und wir müssten diese URL beibehalten, um zu vermeiden, dass ihre Lesezeichen beschädigt werden.
  • Andere Benutzer können keine Verbindung herstellen, da ihre Proxyserver keine nicht standardmäßigen SSL-Ports unterstützen.

Ich benutze den folgenden Ansatz:

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

In Kombination mit der standardmäßigen REJECT-Regel in der INPUT-Kette verhindert dieser Ansatz, dass Benutzer eine direkte Verbindung zu den Ports 8080, 8181 herstellen

Mischa
quelle
1
Das ist gut, aber warum binden Sie den Dämon nicht einfach an localhost: 8080 statt 0.0.0.0:8080? Ich möchte das tun, aber ich brauche die Iptables dafür.
Amala
Es funktioniert wirklich cool.
8.
29

Traditionell kann unter Unix nur root Verbindungen zu niedrigen Ports herstellen (<1024).

Die einfachste Möglichkeit, dies zu umgehen, besteht darin, Ihren Server auf einem hohen Port (z. B. 8080) auszuführen und die Verbindungen von Port 80 nach Port 8080 mithilfe einer einfachen iptables-Regel weiterzuleiten. Beachten Sie, dass Sie dadurch den zusätzlichen Schutz von verlieren niedrige Häfen; Jeder Benutzer auf Ihrem Computer kann eine Bindung zu Port 8080 herstellen.

CesarB
quelle
23

Wenn Ihr System dies unterstützt, können Sie möglicherweise Funktionen verwenden. Sehen Sie man capabilities, derjenige, den Sie brauchen, wäre CAP_NET_BIND_SERVICE. Nein, ich habe sie noch nie benutzt und ich weiß nicht, ob sie wirklich funktionieren :-)

WMR
quelle
1
Sie funktionieren, ich verwende CAP_NET_RAW, um Tools wie tcpdump als normaler Benutzer auszuführen.
Justin
12

Verwenden Sie einen Reverse-Proxy (nginx, apache + mod_proxy) oder einen Caching-Reverse-Proxy (Squid, Varnish) vor Ihren Anwendungsservern!

Mit einem Reverse-Proxy können Sie viele interessante Dinge erreichen, wie:

  • Lastverteilung
  • Neustart Ihrer Anwendungsserver mit Benutzern, die eine ausgefallene Fehlerseite erhalten
  • Beschleunigen Sie die Dinge mit Cache
  • Fein abgestimmte Einstellungen, die Sie normalerweise mit einem Reverse-Proxy und nicht mit einem Anwendungsserver vornehmen
Giovanni Toraldo
quelle
6

Sie können das Redir-Programm verwenden:

sudo redir --lport=80 --laddr=192.168.0.101 --cport 9990 --caddr=127.0.0.1
user340994
quelle
4

Benutze sudo.

Konfigurieren Sie sudo so, dass der normale Benutzer die entsprechenden Befehle ausführen kann:

/etc/init.d/httpd start

Oder

apachectl stop

Oder

/usr/local/apache2/apachectl restart

Oder

/myapp/myappbin start

(Oder welcher andere Befehl / Skript, den Sie zum Starten / Stoppen eines bestimmten Webservers / einer bestimmten App verwenden)

Nicht jetzt
quelle
8
Dies hat das gleiche Problem - das Ausführen eines Dienstes als Root ist eine schlechte Praxis.
Kevin Panko
4

Die Antwort von sunny ist korrekt, es können jedoch weitere Probleme auftreten, da die Loopback-Schnittstelle die PREROUTING-Tabelle nicht verwendet.

Die Iptables-Regeln, die hinzugefügt werden müssen, sind zwei:

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
quelle
3

Unter Linux haben Sie zwei weitere Möglichkeiten:

Mit beiden Erweiterungen des Linux-Kernels können Zugriffsrechte auf einer sehr detaillierten Ebene erteilt werden. Auf diese Weise können Sie diesem Prozess das Öffnen von Port 80 gewähren, erbt jedoch keine der anderen Root-Rechte.

Soweit ich gehört habe, ist grsecurity viel einfacher zu verwenden, SELinux ist jedoch sicherer.

Aaron Digulla
quelle
2

Eine Lösung besteht darin, iptables zu verwenden, um PAT für Pakete für Port 80 auszuführen. Sie können dies beispielsweise verwenden, um die Pakete an den lokalen Port 8080 weiterzuleiten. Stellen Sie sicher, dass die ausgehenden Pakete wieder auf Port 80 eingestellt sind.

Nach meiner Erfahrung werden die detaillierten Berechtigungsfunktionen von Linux aus Sicherheitsgründen nicht in Standard-Kernel kompiliert.


quelle
2

Wenn Sie dies versuchen, damit ein vom Benutzer ausgeführter Befehl Port 80 verwenden kann, sind Ihre einzigen Lösungen die iptables-Tricks oder das Festlegen der ausführbaren Datei setuid-to-root.

So wie Apache dies tut (es wird an Port 80 gebunden, aber nicht als Root ausgeführt), wird als Root ausgeführt, an den Port gebunden und anschließend nach dem Port der Besitzer des Prozesses an den nicht privilegierten Benutzer geändert ist eingerichtet. Wenn die App schreiben Sie können laufen von root, können Sie es Eigentümer an den nicht-priv Benutzers ändern, nachdem die Ports eingerichtet werden. Wenn dies jedoch nur für einen durchschnittlichen Benutzer gilt, der über die Befehlszeile ausgeführt wird, müssen Sie eine der anderen Lösungen verwenden.

rjray
quelle
2

Wenn ich verschiedene Web-Serving-Anwendungen (Python-Skripte, Tomcat-Engines, ...) habe, die ich nicht als Root ausführen möchte, konfiguriere ich normalerweise einen Apache-Webserver vor ihnen. Apache lauscht auf Port 80 und Tomcat lauscht auf 8080.

In Apache: s Konfiguration:

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

Weitere Informationen finden Sie in der Mod-Proxy-Dokumentation: http://httpd.apache.org/docs/2.2/mod/mod_proxy.html


quelle
1
Der Nachteil dieser Lösung ist der Verlust der eingehenden IP-Adresse in den Tomcat-Protokollen.
Emmanuel Bourg
2

Ich denke, die beste Lösung ist es, Ihre App zu sgiden und sobald der Port gebunden ist, sollten die Berechtigungen durch einen Wechsel zu einem anderen Benutzer verworfen werden.

Anxy Dicy
quelle
1

Einige Hostsysteme erlauben die Verwendung des NAT-Moduls nicht. In diesem Fall löst 'iptables' das Problem nicht.

Wie wäre es mit Xinetd?

In meinem Fall (Ubuntu 10.04)

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

Fügen Sie die Konfiguration ein:

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

Dann:

# service xinetd restart

http://docs.codehaus.org/display/JETTY/port80 erklärt besser.

Thomas
quelle
1
Dies entspricht im Wesentlichen dem Platzieren eines Reverse-Proxys vor dem Server, ist jedoch im Vergleich zu dedizierten Lösungen wie HAproxy, Pound oder Varnish, die das HTTP-Protokoll verstehen, ineffizient und kann nützliche Header wie X-Forwarded-For hinzufügen.
Emmanuel Bourg
-1

Abgesehen davon können Sie dies mit FreeBSD und Solaris (jemand erinnert sich daran ?) Ohne erweiterte Rechte (dh mit Programmen zum Wechseln zu root) tun (an niedrige Ports binden). Da Sie Linux angegeben haben, sende ich dies nur als Hinweis an andere, die diese Frage möglicherweise finden.

Daniel C. Sobral
quelle
-1

Nekromanzierung.

Einfach. Mit einem normalen oder alten Kernel ist dies nicht der Fall.
Wie bereits von anderen erwähnt, können iptables einen Port weiterleiten.
Wie auch von anderen erwähnt, kann CAP_NET_BIND_SERVICE auch die Aufgabe übernehmen.
Natürlich schlägt CAP_NET_BIND_SERVICE fehl, wenn Sie Ihr Programm von einem Skript aus starten, es sei denn, Sie setzen die Obergrenze für den Shell-Interpreter, was sinnlos ist. Sie können Ihren Dienst genauso gut als root ausführen ...
z. B. für Java müssen Sie ihn anwenden an die JAVA JVM

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

Dies bedeutet natürlich, dass jedes Java-Programm System-Ports binden kann.
Dito für Mono / .NET.

Ich bin mir auch ziemlich sicher, dass xinetd nicht die besten Ideen sind.
Aber da beide Methoden Hacks sind, warum nicht einfach das Limit durch Aufheben der Beschränkung aufheben?
Niemand hat gesagt, dass Sie einen normalen Kernel ausführen müssen, also können Sie einfach Ihren eigenen Kernel ausführen.

Sie laden einfach den Quellcode für den neuesten Kernel herunter (oder den gleichen, den Sie aktuell haben). Anschließend gehen Sie zu:

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

Dort suchen Sie nach dieser Zeile

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

und ändere es auf

#define PROT_SOCK 0

Wenn Sie keine unsichere SSH-Situation haben möchten, ändern Sie diese folgendermaßen: #define PROT_SOCK 24

Im Allgemeinen würde ich die niedrigste Einstellung verwenden, die Sie benötigen, z. B. 79 für http oder 24, wenn Sie SMTP an Port 25 verwenden.

Das ist schon alles.
Kompilieren Sie den Kernel und installieren Sie ihn.
Starten Sie neu.
Fertig - diese dumme Grenze ist GEGANGEN und das funktioniert auch für Skripte.

So kompilieren Sie einen Kernel:

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

Kurz gesagt, verwenden Sie iptables, wenn Sie sicher bleiben möchten, und kompilieren Sie den Kernel, wenn Sie sicher sein möchten, dass diese Einschränkung Sie nie wieder stört.

Dilemma
quelle
Würde jemand bitte die Abstimmungen erklären? Kernel-Kompilierung funktioniert nicht? Oder liegt es nur daran, dass Sie danach kein Kernel-Upgrade von den normalen Repositorys mehr durchführen können?
Quandary,
Ich habe diese Lösung ausprobiert ( sudo setcap cap_net_bind_service=+ep /path-to-java/java), aber ich bekomme immer, java: error while loading shared libraries: libjli.so: cannot open shared object file: No such file or directorywenn Java Caps gesetzt hat. Siehe auch den ersten Teil von unix.stackexchange.com/a/16670/55508
Daniel Alder
1
Behebung des vorherigen Fehlers durch Hinzufügen <javahome>/lib/amd64/jli/von ld.so.confund Ausführen vonsudo ldconfig
Daniel Alder