Gibt es eine Möglichkeit für Nicht-Root-Prozesse, sich unter Linux an "privilegierte" Ports zu binden?


389

Es ist sehr ärgerlich, diese Einschränkung auf meiner Entwicklungsbox zu haben, wenn es nie andere Benutzer als mich geben wird.

Ich kenne die Standardumgehungen , aber keine von ihnen macht genau das, was ich will:

  1. authbind (Die Version in Debian-Test 1.0 unterstützt nur IPv4)
  2. Verwenden des Ziels iptables REDIRECT zum Umleiten eines Low-Ports zu einem High-Port (die Tabelle "nat" ist für ip6tables, die IPv6-Version von iptables, noch nicht implementiert).
  3. sudo (Laufen als root ist das, was ich zu vermeiden versuche)
  4. SELinux (oder ähnlich). (Dies ist nur meine Entwicklungsbox, ich möchte nicht viel zusätzliche Komplexität einführen.)

Gibt es eine einfache sysctlVariable, mit der Nicht-Root-Prozesse unter Linux an "privilegierte" Ports (Ports unter 1024) gebunden werden können, oder habe ich einfach Pech?

BEARBEITEN: In einigen Fällen können Sie dazu Funktionen verwenden.


5
Nach meiner Erfahrung besteht ein Grund für diesen Versuch darin, einen Webserver zu schreiben, anstatt Apache (oder lighttpd) zu verwenden.
S.Lott

Ich habe das Setcap-Zeug zu meiner Antwort auf den fast identischen stackoverflow.com/questions/277991/…
Paul Tomblin

15
Da ich in diesem Fall IPv6 verwendet habe, haben einige der "üblichen" Problemumgehungen (authbind und iptables REDIRECT) bei mir nicht funktioniert.
Jason Creighton


1
Es gibt verschiedene Möglichkeiten, dies zu tun. Siehe Zulassen, dass Nicht-Root-Prozesse an Port 80 und 443 binden? auf Super User.
JWW

Antworten:


392

Okay, danke an die Leute, die auf das System und die CAP_NET_BIND_SERVICEFähigkeiten der Fähigkeiten hingewiesen haben. Wenn Sie einen aktuellen Kernel haben, können Sie damit tatsächlich einen Dienst als Nicht-Root starten, aber niedrige Ports binden. Die kurze Antwort lautet:

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

Und dann wird es jederzeit programausgeführt, wenn es danach ausgeführt wird CAP_NET_BIND_SERVICE. setcapist im Debian-Paket libcap2-bin.

Nun zu den Vorbehalten:

  1. Sie benötigen mindestens einen 2.6.24-Kernel
  2. Dies funktioniert nicht, wenn Ihre Datei ein Skript ist. (dh verwendet eine #! -Zeile, um einen Interpreter zu starten). In diesem Fall müssten Sie, soweit ich weiß, die Funktion auf die ausführbare Interpreter-Datei selbst anwenden, was natürlich ein Sicherheitsalptraum ist, da jedes Programm, das diesen Interpreter verwendet, über diese Funktion verfügt. Ich konnte keinen sauberen und einfachen Weg finden, um dieses Problem zu umgehen.
  3. Linux deaktiviert LD_LIBRARY_PATH für alle Benutzer programmit erhöhten Berechtigungen wie setcapoder suid. Wenn Sie also programeine eigene verwenden .../lib/, müssen Sie möglicherweise eine andere Option wie die Portweiterleitung prüfen.

Ressourcen:

Hinweis: RHEL hat dies zuerst in Version 6 hinzugefügt .


3
Teilweise Problemumgehung für Skripte: Erstellen Sie eine Kopie des Interpreters (z. B. Bash), geben Sie ihm die Funktionen, aber beschränken Sie den Zugriff auf die Kopie auf diejenigen Benutzer, die sie benötigen. Natürlich müssen diese Benutzer vertrauenswürdig sein, aber sie können das Skript trotzdem ändern.
Erich Kitzmüller

3
Abgesehen von dem oben genannten Debian-Paket (Binärpaket) ist die Website des Entwicklers friedhoff.org/posixfilecaps.html zugehörige Artikel / Präsentationen / etc ...
RandomNickName42

1
unter suse SLES 11.1 musste ich den kernel param file_caps = 1 zu grub menu.lst hinzufügen, damit dies funktioniert.
Shay

2
Ja @ C. Ross, da es angewendet werden /usr/bin/javamüsste und dann die Funktion für jede auf dem System ausgeführte Java-App öffnen würde. Schade, dass Funktionen nicht auch pro Benutzer festgelegt werden können.
Joeytwiddle

5
Bleibt die Einstellung setcap bei Neustarts bestehen? Wenn nicht, gibt es einen Standardplatz, an dem diese Regel so platziert werden kann, dass sie während des Systemstarts ausgeführt wird? Gibt es /etc/security/capability.confauf Debian / Ubuntu Hilfe?
Joeytwiddle

34

Sie können eine Portumleitung durchführen. Dies ist, was ich für einen Silverlight-Richtlinienserver mache, der auf einer Linux-Box ausgeführt wird

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

5
Leider funktioniert dies nur für geroutete Verbindungen, dh nicht vom lokalen Computer.
Zbyszek

@joeytwiddle Ich denke, es ist in dem Skript, das Ihren Server ausführt (startet), aber ich könnte mich irren. Ich würde auch gerne wissen: P
Camilo Martin

@CamiloMartin In der Debian-Dokumentation, die mit der Frage verknüpft wurde, wird empfohlen, sie in Ihr Firewall-Skript einzufügen oder unter zu erstellen /etc/network/if-up.d/firewall.
Joeytwiddle

8
@zbyszek Dies kann für lokale Computerverbindungen mit einer zusätzlichen iptables-Regel funktionieren
00500005

In älteren Kerneln scheint dies für IPv6 nicht unterstützt zu werden . Aber anscheinend wird es in ip6tables v1.4.18 und Linux Kernel v3.8 unterstützt.
Craig McQueen

31

Die Standardmethode besteht darin, sie "setuid" zu machen, damit sie als Root starten, und dann dieses Root-Privileg wegzuwerfen, sobald sie an den Port gebunden sind, aber bevor sie Verbindungen zu ihm annehmen. Gute Beispiele dafür finden Sie im Quellcode für Apache und INN. Mir wurde gesagt, dass Lighttpd ein weiteres gutes Beispiel ist.

Ein weiteres Beispiel ist Postfix, bei dem mehrere Daemons verwendet werden, die über Pipes kommunizieren, und nur ein oder zwei von ihnen (die nur Akzeptieren oder Ausgeben von Bytes ausführen) als Root ausgeführt werden und der Rest mit einem niedrigeren Privileg ausgeführt wird.


1
Interessanterweise funktioniert dies unter neueren Linux-Versionen (möglicherweise nur Ubuntu) ohne CAP_SETUID nicht. Wenn Sie also setuid benötigen, müssen Sie diese Funktion trotzdem einstellen.
Matt

Das Löschen von Root-Privilegien ist der richtige Weg, um dies zu tun. Es ist jedoch nicht erforderlich, setuid festzulegen, wenn Sie init / upstart / systemd gestartet haben, um den Dienst zu starten.
Michael Hampton

2
Dies ist schwierig, wenn das Programm in einer interpretierten Sprache oder einem Bytecode-Interpreter wie C # (Mono), Java, Python geschrieben ist. (Anscheinend hat Perl es über binfmt_miscund seine 'C'-Flagge getan ; ich bin mir bei anderen nicht sicher.)
Craig McQueen

sehr wenig außer Bytes ausgeben, das macht nicht sehr wenig.
Ryan

Wenn Sie eine binäre Setuid festlegen, wird diese als Root-Prozess ausgeführt. In der Frage wird jedoch gefragt, wie dies erreicht werden kann, ohne dass Ihre Binärdatei als Root-Prozess ausgeführt wird. Diese Lösung ist die Antwort auf eine andere Frage: "Wie kann ein nicht privilegierter Benutzer einen Prozess an einen privilegierten Port binden lassen", aber dies ist meiner Meinung nach nicht die gestellte Frage.
Michael Beer

24

Oder patchen Sie Ihren Kernel und entfernen Sie die Prüfung.

(Option des letzten Auswegs, nicht empfohlen).

In net/ipv4/af_inet.c, entfernen Sie die beiden Linien , die gelesen

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

und der Kernel überprüft keine privilegierten Ports mehr.


22
Eine sehr schlechte Idee aus einer Vielzahl von Gründen.
Adam Lassek

24
Das ist eine schlechte Idee. Würde aber tatsächlich funktionieren. Und hat mich sicher zum Lachen gebracht.
Oto Brglez

23
Natürlich ist es eine schlechte Idee. Deshalb habe ich das letzte Mittel gesagt. Der Punkt von Open Source ist, wenn es nicht so funktioniert, wie Sie es möchten, können Sie es ändern.
Joshua

18
Ich bin mir nicht sicher, warum Sie abgelehnt wurden. Malware-Autoren kümmern sich nicht darum, welchen Port sie abhören, und sie sind mehr als glücklich, einen Port größer als 1024 zu öffnen. Es scheint mir, dass alle Ports Berechtigungen erfordern sollten oder keine Ports Berechtigungen erfordern sollten. Wenn root einen Port unter 1024 öffnen muss, bedeutet dies nur, dass eine Anwendung mit hohem Risiko als root ausgeführt wird. Das scheint mir eine wirklich dumme Idee zu sein. Vielleicht fehlt mir hier etwas ...
jww

9
Früher wurde Port <1024 in UNIX-zu-UNIX-Protokollen verwendet, um zu beweisen, dass der am anderen Ende ausgeführte Code als Root ausgeführt wurde. Dies funktionierte für eine Reihe von UNIX-Servern mit allgemeiner Sicherheit recht gut.
Joshua

22

Sie können einen lokalen SSH-Tunnel einrichten, z. B. wenn Port 80 Ihre an 3000 gebundene App treffen soll:

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

Dies hat den Vorteil, mit Skriptservern zu arbeiten und sehr einfach zu sein.


1
Mein persönlicher Favorit. Sehr einfach ein- und auszuschalten und erfordert keine systemweiten Änderungen. Großartige Idee!
Dan Passaro

Das ist großartig. Mit Abstand die einfachste Antwort.
michaelsnowden

1
Ich würde erwarten, dass dies mäßig teuer und in keiner leistungsabhängigen Situation eine gute Idee ist, aber ich kann nicht sicher sein, ohne es zu testen. Die SSH-Verschlüsselung kostet ungleich Null.
Lahwran

3
Dies könnte mit Netcat ohne SSH-Overhead gemacht werden:sudo nc -l 80 | nc localhost 3000
Bugster

1
Sie verschlüsseln + entschlüsseln nur, um die Daten an einen anderen Port auf demselben Computer zu verschieben? Dies funktioniert auch nicht für UDP-Verkehr.
Daniel F

19

Update 2017:

Verwenden Sie authbind


Viel besser als CAP_NET_BIND_SERVICE oder ein benutzerdefinierter Kernel.

  • CAP_NET_BIND_SERVICE gewährt der Binärdatei Vertrauen, bietet jedoch keine Kontrolle über den Zugriff pro Port.
  • Authbind gewährt dem Benutzer / der Gruppe Vertrauen, bietet Kontrolle über den Zugriff pro Port und unterstützt sowohl IPv4 als auch IPv6 (IPv6-Unterstützung wurde kürzlich hinzugefügt).

    1. Installieren: apt-get install authbind

    2. Konfigurieren Sie den Zugriff auf relevante Ports, z. B. 80 und 443, für alle Benutzer und Gruppen:

      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. Führen Sie Ihren Befehl aus über authbind
      (optional Angabe --deepoder andere Argumente, siehe Manpage):

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

      z.B

      authbind --deep java -jar SomeServer.jar
      

Als Folge von Joshuas fabelhafter Empfehlung (= nicht empfohlen, es sei denn, Sie wissen, was Sie tun), den Kernel zu hacken:

Ich habe es zuerst geschrieben hier .

Einfach. Mit einem normalen oder alten Kernel tun Sie das nicht.
Wie von anderen hervorgehoben, können iptables einen Port weiterleiten.
Wie auch von anderen hervorgehoben, kann CAP_NET_BIND_SERVICE die Aufgabe ebenfalls erledigen.
Natürlich schlägt CAP_NET_BIND_SERVICE fehl, wenn Sie Ihr Programm über ein Skript starten. Wenn Sie die Obergrenze für den Shell-Interpreter nicht festlegen, was sinnlos ist, können Sie Ihren Dienst genauso gut wie root ausführen. ZB
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

Das bedeutet natürlich, dass jedes Java-Programm Systemports binden kann.
Dito für Mono / .NET.

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

Sie laden einfach die Quelle für den neuesten Kernel herunter (oder den gleichen, den Sie derzeit haben). Danach gehen Sie zu:

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

Dort suchen Sie nach dieser Linie

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

und ändern Sie es in

#define PROT_SOCK 0

Wenn Sie keine unsichere SSH-Situation haben möchten, ändern Sie diese wie folgt: #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, kompilieren Sie den Kernel, wenn Sie sicher sein möchten, dass Sie diese Einschränkung nie wieder stört.


Gibt es einen Grund für die Ablehnung? Root-Hasser oder haben die Anweisungen zum Kernel-Kompilieren nicht funktioniert?
Stefan Steiger

4
Das Ändern des Kernels macht zukünftige Updates viel schmerzhafter. Ich würde das nicht tun, weil ich weiß, dass ich zu faul wäre, um meinen Kernel regelmäßig zu aktualisieren. Es ist schön, dass es theoretisch möglich ist, aber es ist unter vielen Umständen keine praktikable Option.
kritzikratzi

Möglicherweise ist es sicherer, das chmod 777 / etc / authbind / byport / 80 von chmod 544 / etc / authbind / byport / 23 zu ändern, als sich anzumelden: group / etc / authbind / byport / 23
Jérôme B

18

Dateifunktionen sind nicht ideal, da sie nach einer Paketaktualisierung unterbrochen werden können.

Die ideale Lösung, IMHO, sollte die Fähigkeit sein, eine Shell mit vererbbarem CAP_NET_BIND_SERVICESatz zu erstellen .

Hier ist ein etwas komplizierter Weg, dies zu tun:

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

capshDas Dienstprogramm finden Sie im libcap2-bin-Paket in Debian / Ubuntu-Distributionen. Folgendes geht vor:

  • sgÄndert die effektive Gruppen-ID in die des Daemon-Benutzers. Dies ist notwendig, da capshGID unverändert bleibt und wir es definitiv nicht wollen.
  • Setzt das Bit 'Funktionen bei UID-Änderung beibehalten'.
  • Ändert die UID in $DAEMONUSER
  • Löscht alle Kappen (in diesem Moment sind alle Kappen wegen vorhanden --keep=1), außer vererbbarcap_net_bind_service
  • Führt Ihren Befehl aus ('-' ist ein Trennzeichen)

Das Ergebnis ist ein Prozess mit angegebenen Benutzer- und Gruppen- und cap_net_bind_serviceBerechtigungen.

Als Beispiel eine Zeile aus dem ejabberdStartskript:

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

Und es funktioniert tatsächlich NICHT. Über dem Benutzer-ID-Schalter bleiben keine Kappen erhalten. Es funktioniert, wenn Sie als Root ausgeführt werden möchten, aber alle Funktionen nicht mehr verfügbar sind.
Cyberax

Wenn ich das versuche, erhalte ich die Meldung "Binärdatei kann nicht ausgeführt werden". Tatsächlich kann capshich nichts anderes tun als "Binärdatei kann nicht ausgeführt werden", wenn ich die Optionen --oder verwende ==. Ich frage mich, was ich vermisse.
Craig McQueen

@CraigMcQueen, alles danach --wird an weitergeleitet /bin/bash, also möchten Sie es vielleicht versuchen -c 'your command'. Leider habe ich anscheinend das gleiche Problem wie @Cyberax, da mir beim Versuch "Erlaubnis verweigert" wird bind.
Amir

Damit dies funktioniert, ohne die Dateifunktionen festzulegen (oder als Root auszuführen ), müssen Sie die Umgebungsfunktionen verwenden, wie in dieser Unix.SE-Antwort beschrieben . Sie können auch verwenden , --gidanstatt sg(oder --userdie Sätze --uid, --gidund --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

Aus irgendeinem Grund erwähnt niemand, dass sysctl net.ipv4.ip_unprivileged_port_start auf den Wert gesenkt werden soll, den Sie benötigen. Beispiel: Wir müssen unsere App an den 443-Port binden.

sysctl net.ipv4.ip_unprivileged_port_start=443

Einige mögen sagen, dass es ein potenzielles Sicherheitsproblem gibt: Nicht privilegierte Benutzer können sich jetzt an die anderen privilegierten Ports binden (444-1024). Sie können dieses Problem jedoch einfach mit iptables lösen, indem Sie andere Ports blockieren:

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

Vergleich mit anderen Methoden. Diese Methode:

  • Ab einem bestimmten Punkt ist (IMO) sogar noch sicherer als das Festlegen von CAP_NET_BIND_SERVICE / setuid, da eine Anwendung auch nur teilweise nicht festlegt (Funktionen sind es tatsächlich). Um beispielsweise einen Coredump einer funktionsfähigen Anwendung abzufangen, müssen Sie sysctl fs.suid_dumpable ändern (was zu weiteren potenziellen Sicherheitsproblemen führt). Wenn CAP / suid festgelegt ist, gehört das Verzeichnis / proc / PID dem Root Ihr Nicht-Root-Benutzer verfügt nicht über vollständige Informationen / Kontrolle über den laufenden Prozess. Beispielsweise kann der Benutzer (im allgemeinen Fall) nicht feststellen, welche Verbindungen über / proc / PID / fd / (netstat -aptn | grep zur Anwendung gehören) PID).
  • hat einen Sicherheitsnachteil: Während Ihre App (oder eine App, die die Ports 443-1024 verwendet) aus irgendeinem Grund nicht verfügbar ist, kann eine andere App den Port übernehmen. Dieses Problem kann aber auch auf CAP / suid (falls Sie es auf Interpreter setzen, z. B. java / nodejs) und iptables-redirect angewendet werden. Verwenden Sie die systemd-socket-Methode, um dieses Problem auszuschließen. Verwenden Sie die authbind-Methode, um nur spezielle Benutzerbindungen zuzulassen.
  • Es ist nicht erforderlich, CAP / suid jedes Mal festzulegen, wenn Sie eine neue Version der Anwendung bereitstellen.
  • erfordert keine Anwendungsunterstützung / -änderung, wie die systemd-socket-Methode.
  • erfordert keine Kernel-Neuerstellung (wenn die laufende Version diese Sysctl-Einstellung unterstützt)
  • LD_PRELOAD funktioniert nicht wie die Methode authbind / privbind. Dies kann möglicherweise die Leistung, Sicherheit und das Verhalten beeinträchtigen (nicht getestet). Im übrigen ist authbind eine wirklich flexible und sichere Methode.
  • übertrifft die iptables REDIRECT / DNAT-Methode, da keine Adressumsetzung, Verbindungsstatusverfolgung usw. erforderlich ist. Dies macht sich nur bei Hochlastsystemen bemerkbar.

Je nach Situation würde ich zwischen sysctl, CAP, authbind und iptables-redirect wählen. Und das ist toll, dass wir so viele Möglichkeiten haben.


2
Danke für die tolle Antwort! Es scheint, dass diese Funktionalität zum ersten Mal im April 2017 in Linux 4.11 verfügbar war. Es gab sie also nicht im Jahr 2009, als ich diese Frage zum ersten Mal stellte. Ich habe auch einen Schnelltest gemacht, und es scheint auch für IPV6 zu funktionieren, obwohl "ipv4" im sysctl-Namen steht.
Jason Creighton

15

Zwei weitere einfache Möglichkeiten:

Es gibt eine alte (unmoderne) Lösung für den "Daemon, der an einen niedrigen Port bindet und die Kontrolle an Ihren Daemon übergibt". Es heißt inetd (oder xinetd). Die Nachteile sind:

  • Ihr Daemon muss über stdin / stdout sprechen (wenn Sie den Daemon nicht steuern - wenn Sie nicht über die Quelle verfügen - ist dies möglicherweise ein Showstopper, obwohl einige Dienste möglicherweise ein Inetd-Kompatibilitätsflag haben).
  • Für jede Verbindung wird ein neuer Daemon-Prozess gegabelt
  • Es ist ein zusätzliches Glied in der Kette

Vorteile:

  • verfügbar unter jedem alten UNIX
  • Sobald Ihr Systemadministrator die Konfiguration eingerichtet hat, können Sie Ihre Entwicklung fortsetzen (wenn Sie Ihren Daemon neu erstellen, verlieren Sie möglicherweise die Setcap-Funktionen? Und dann müssen Sie zu Ihrem Administrator zurückkehren. "Bitte, Sir. . ")
  • Daemon muss sich nicht um das Netzwerk kümmern, sondern muss nur über stdin / stdout sprechen
  • kann konfigurieren, dass Ihr Daemon wie gewünscht als Nicht-Root-Benutzer ausgeführt wird

Eine andere Alternative: ein gehackter Proxy (Netcat oder etwas Robusteres ) vom privilegierten Port zu einem beliebigen Port mit hoher Nummer, an dem Sie Ihren Zieldämon ausführen können. (Netcat ist offensichtlich keine Produktionslösung, sondern "nur meine Entwicklungsbox", oder?) Auf diese Weise könnten Sie weiterhin eine netzwerkfähige Version Ihres Servers verwenden, benötigen nur root / sudo, um den Proxy (beim Booten) zu starten, und würden sich nicht auf komplexe / potenziell fragile Funktionen verlassen.


1
Guter Vorschlag. Aus irgendeinem Grund habe ich nicht einmal daran gedacht, inetd zu verwenden. Abgesehen davon, dass der betreffende Dienst UDP-basiert ist, ist er etwas komplizierter als TCP-Dienste. Ich habe gerade eine Lösung, die mit setcap funktioniert, aber ich muss darüber nachdenken.
Jason Creighton

Für die zweite Option können Sie wahrscheinlich sogar den Proxy mit inetd starten ... (Aber IPTables ist wahrscheinlich weniger Overhead ...)
Gert van den Berg

14

Meine "Standard-Problemumgehung" verwendet socat als User-Space-Redirector:

socat tcp6-listen:80,fork tcp6:8080

Beachten Sie, dass dies nicht skalierbar ist. Gabeln ist teuer, aber so funktioniert socat.


13

Linux unterstützt Funktionen zur Unterstützung detaillierterer Berechtigungen als nur "Diese Anwendung wird als Root ausgeführt". Eine dieser Funktionen besteht darin CAP_NET_BIND_SERVICE, sich an einen privilegierten Port zu binden (<1024).

Leider weiß ich nicht, wie ich das ausnutzen soll, um eine Anwendung als Nicht-Root auszuführen, während ich sie noch gebe CAP_NET_BIND_SERVICE(wahrscheinlich mit setcap, aber dafür gibt es bestimmt eine Lösung).


Funktioniert nicht für Skripte oder einzelne JVM / Mono-Programme.
Stefan Steiger

13

Ich weiß, dass dies eine alte Frage ist, aber jetzt mit den neuesten (> = 4,3) Kerneln gibt es endlich eine gute Antwort darauf - Umgebungsfähigkeiten.

Die schnelle Antwort besteht darin, eine Kopie der neuesten (noch nicht veröffentlichten) Version von libcap von git zu holen und zu kompilieren. Kopieren Sie die resultierende progs/capshBinärdatei irgendwo ( /usr/local/binist eine gute Wahl). Starten Sie dann als root Ihr Programm mit

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

In Ordnung sind wir

  • Wenn wir Benutzer wechseln, möchten wir unsere aktuellen Fähigkeiten beibehalten
  • Benutzer & Gruppe auf 'Ihr-Service-Benutzername' wechseln
  • Hinzufügen der cap_net_bind_serviceFunktion zu den geerbten und Umgebungssätzen
  • Forking bash -c 'your-command'(da capshBash automatisch mit den Argumenten danach startet --)

Hier ist viel unter der Haube los.

Erstens werden wir als root ausgeführt, sodass wir standardmäßig alle Funktionen erhalten. Darin enthalten ist die Möglichkeit, uid & gid mit setuidund setgidsyscalls zu wechseln . Wenn ein Programm dies tut, verliert es normalerweise seine Funktionen - dies ist so, dass die alte Methode, root mit zu löschen, setuidimmer noch funktioniert. Das --keep=1Flag weist capshdarauf hin, dass der prctl(PR_SET_KEEPCAPS)Systemaufruf ausgegeben werden soll, wodurch das Löschen von Funktionen beim Benutzerwechsel deaktiviert wird. Der eigentliche Benutzerwechsel durch capsherfolgt mit dem --userFlag, das ausgeführt wird setuidund setgid.

Das nächste Problem, das wir lösen müssen, ist, wie wir Fähigkeiten so einstellen können, dass sie nach execunseren Kindern weiter bestehen. Das Fähigkeitssystem hatte immer einen "geerbten" Satz von Fähigkeiten, dh "einen Satz von Fähigkeiten, die über eine Ausführung hinweg erhalten bleiben (2)" [ Fähigkeiten (7) ]. Während dies so klingt, als ob es unser Problem löst (setzen Sie einfach die cap_net_bind_serviceFähigkeit auf geerbt, oder?), Gilt dies tatsächlich nur für privilegierte Prozesse - und unser Prozess ist nicht mehr privilegiert, da wir bereits den Benutzer gewechselt haben (mit dem --userFlag).

Der neue Umgebungsfunktionssatz umgeht dieses Problem - er ist "ein Satz von Funktionen, die in einem Execve (2) eines Programms erhalten bleiben, das nicht privilegiert ist". Durch das Einfügen cap_net_bind_servicedes Umgebungssatzes capsherbt unser Programm beim Ausführen unseres Serverprogramms diese Fähigkeit und kann Listener an niedrige Ports binden.

Wenn Sie mehr erfahren möchten, wird dies auf der Seite mit den Funktionshandbüchern ausführlich erläutert. Laufen capshdurch straceist auch sehr informativ!


Das --addambFlag wurde mit libcap 2.26 eingeführt. Mein System hatte 2.25 und ich musste aus dem Quellcode bauen.
ngreen

12

TLDR: Für "die Antwort" (wie ich es sehe) springen Sie zum >> TLDR << Teil in dieser Antwort.

OK, ich habe es herausgefunden (diesmal wirklich), die Antwort auf diese Frage, und diese Antwort von mir ist auch eine Möglichkeit, sich dafür zu entschuldigen, dass ich eine andere Antwort (sowohl hier als auch auf Twitter) beworben habe, die ich für "die beste" hielt ", aber nachdem ich es versucht hatte, stellte ich fest, dass ich mich geirrt hatte. Lernen Sie aus meinen Fehlerkindern: Bewerben Sie etwas erst, wenn Sie es selbst ausprobiert haben!

Auch hier habe ich alle Antworten überprüft. Ich habe einige davon ausprobiert (und andere nicht ausprobiert, weil mir die Lösungen einfach nicht gefallen haben). Ich dachte, dass die Lösung systemdmit seinen Capabilities=und CapabilitiesBindingSet=Einstellungen verwendet werden sollte. Nachdem ich einige Zeit damit gerungen hatte, stellte ich fest, dass dies nicht die Lösung ist, weil:

Funktionen sollen Root-Prozesse einschränken!

Wie das OP mit Bedacht feststellte, ist es immer am besten, dies zu vermeiden (wenn möglich für alle Ihre Dämonen!).

Sie können die Optionen für Funktionen nicht mit User=und Group=in systemdEinheitendateien verwenden, da Funktionen IMMER zurückgesetzt werden, wenn execev(oder was auch immer die Funktion ist) aufgerufen wird. Mit anderen Worten, wenn systemddie Dauerwellen gegabelt und fallen gelassen werden, werden die Funktionen zurückgesetzt. Daran führt kein Weg vorbei, und die gesamte Bindungslogik im Kernel basiert auf uid = 0 und nicht auf Funktionen. Dies bedeutet, dass es unwahrscheinlich ist, dass Capabilities jemals die richtige Antwort auf diese Frage sein wird (zumindest in Kürze). Übrigens ist setcap, wie andere bereits erwähnt haben, keine Lösung. Es hat bei mir nicht funktioniert, es funktioniert nicht gut mit Skripten, und diese werden sowieso zurückgesetzt, wenn sich die Datei ändert.

In meiner mageren Verteidigung habe ich (in dem Kommentar, den ich jetzt gelöscht habe) festgestellt , dass James ' Vorschlag für iptables (den das OP auch erwähnt) die "zweitbeste Lösung" war. :-P

>> TLDR <<

Die Lösung besteht darin, sie systemdmit On-the-Fly- iptablesBefehlen wie diesen zu kombinieren ( aus 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

Hier erreichen wir Folgendes:

  • Der Daemon hört 5333 ab, aber Verbindungen werden dank 53 erfolgreich akzeptiert iptables
  • Wir können die Befehle in die Gerätedatei selbst aufnehmen und so den Menschen Kopfschmerzen ersparen. systemdBereinigt die Firewall-Regeln für uns und stellt sicher, dass sie entfernt werden, wenn der Dämon nicht ausgeführt wird.
  • Wir werden nie als Root ausgeführt und machen eine Eskalation von Berechtigungen unmöglich (zumindest systemdbehauptet dies), angeblich selbst dann, wenn der Dämon kompromittiert und festgelegt ist uid=0.

iptablesist leider immer noch ein ziemlich hässliches und schwer zu bedienendes Dienstprogramm. Wenn der Daemon auf zuhört eth0:0statt eth0, zum Beispiel sind die Befehle etwas anders .


2
Niemand sollte mehr Aliase im alten Stil verwenden, es eth0:0sei denn, er hat eine wirklich alte Linux-Distribution. Sie sind seit Jahren veraltet und werden irgendwann verschwinden.
Michael Hampton

1
Ich denke du meinst OpenVZ. (SolusVM ist ein Control Panel.) Und ja, OpenVZ macht viele Dinge falsch, Networking ist nur eine davon.
Michael Hampton

1
Nein, ich meine mit SolusVM. Von / etc / network / interfaces:# Generated by SolusVM
Greg Slepak

1
Immer noch nicht OpenVZ ... Zumindest benutze ich es nicht und mein VPS auch nicht.
Greg Slepak

7
Vielen Dank, dass Sie auf die Funktionen von systemd hingewiesen haben. Es sind jedoch keine komplexen iptables erforderlich, wenn systemd die Binärdatei direkt startet (kein Skript). Die Einstellung AmbientCapabilities=CAP_NET_BIND_SERVICEhat für mich in Kombination mit perfekt funktioniert User=.
Ruud

11

systemd ist ein sysvinit-Ersatz, mit dem ein Daemon mit bestimmten Funktionen gestartet werden kann. Optionen Capabilities =, CapabilityBoundingSet = in der Manpage systemd.exec (5) .


2
Ich habe diese Antwort zuvor empfohlen, aber nachdem ich sie ausprobiert habe, tue ich sie jetzt nicht mehr. In meiner Antwort finden Sie eine Alternative, die noch verwendet wird systemd.
Greg Slepak

10

Die Portumleitung war für uns am sinnvollsten, aber wir stießen auf ein Problem, bei dem unsere Anwendung eine URL lokal auflöste, die ebenfalls umgeleitet werden musste. (das heißt du shindig ).

Auf diese Weise können Sie auch umgeleitet werden, wenn Sie auf die URL auf dem lokalen Computer zugreifen.

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

Moderne Linux-Unterstützung /sbin/sysctl -w net.ipv4.ip_unprivileged_port_start=0.


8

Mit systemd müssen Sie Ihren Dienst nur geringfügig ändern, um voraktivierte Sockets zu akzeptieren.

Sie können später den systemd-Socket aktivieren .

Es werden keine Funktionen, Iptables oder andere Tricks benötigt.

Dies ist der Inhalt relevanter systemd-Dateien aus diesem Beispiel eines einfachen Python-http-Servers

Datei httpd-true.service

[Unit]
Description=Httpd true 

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

PrivateTmp=yes

Datei httpd-true.socket

[Unit]
Description=HTTPD true

[Socket]
ListenStream=80

[Install]
WantedBy=default.target

7

Am Anfang:

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

Dann können Sie an den Port binden, an den Sie weiterleiten.


nicht --to-portexistieren? man iptablesnur Erwähnungen --to-ports(Plural).
Abdull

Ich habe einige Unterschiede bemerkt und bin herumgesprungen
James

In dieser Antwort erfahren Sie, wie Sie dies kombinieren können systemd.
Greg Slepak


3

Es gibt auch den 'DJB-Weg'. Mit dieser Methode können Sie Ihren Prozess als Root starten, der auf einem beliebigen Port unter tcpserver ausgeführt wird. Anschließend wird die Steuerung des Prozesses unmittelbar nach dem Start des Prozesses an den von Ihnen angegebenen Benutzer übergeben.

#!/bin/sh

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

Weitere Informationen finden Sie unter: http://thedjbway.b0llix.net/daemontools/uidgid.html


1

Da es sich bei dem OP nur um Entwicklung / Test handelt, können weniger als schlanke Lösungen hilfreich sein:

setcap kann auf dem Interpreter eines Skripts verwendet werden, um Skripten Funktionen zu gewähren. Wenn setcaps für die globale Interpreter-Binärdatei nicht akzeptabel ist, erstellen Sie eine lokale Kopie der Binärdatei (jeder Benutzer kann dies) und veranlassen Sie root, setcap für diese Kopie festzulegen. Python2 funktioniert (zumindest) ordnungsgemäß mit einer lokalen Kopie des Interpreters in Ihrem Skriptentwicklungsbaum. Es wird kein Suid benötigt, damit der Root-Benutzer steuern kann, auf welche Funktionen Benutzer Zugriff haben.

Wenn Sie systemweite Aktualisierungen des Interpreters nachverfolgen müssen, verwenden Sie ein Shell-Skript wie das folgende, um Ihr Skript auszuführen:

#!/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

Ich habe die Methode iptables PREROUTING REDIRECT ausprobiert. In älteren Kerneln scheint diese Art von Regel für IPv6 nicht unterstützt zu werden . Aber anscheinend wird es jetzt in ip6tables v1.4.18 und Linux Kernel v3.8 unterstützt.

Ich habe auch festgestellt, dass PREROUTING REDIRECT nicht für Verbindungen funktioniert, die innerhalb des Computers initiiert wurden. Um für Verbindungen vom lokalen Computer zu arbeiten, fügen Sie auch eine OUTPUT-Regel hinzu - siehe iptables- Portumleitung funktioniert nicht für localhost . ZB so etwas wie:

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

Ich habe auch festgestellt, dass PREROUTING REDIRECT auch weitergeleitete Pakete betrifft . Das heißt, wenn der Computer auch Pakete zwischen Schnittstellen weiterleitet (z. B. wenn er als Wi-Fi-Zugangspunkt fungiert, der mit einem Ethernet-Netzwerk verbunden ist), erkennt die iptables-Regel auch die Verbindungen verbundener Clients zu Internetzielen und leitet sie an diese weiter Die Maschine. Das wollte ich nicht - ich wollte nur Verbindungen umleiten, die an die Maschine selbst gerichtet waren. Ich habe festgestellt, dass ich durch Hinzufügen nur die an die Box adressierten Pakete beeinflussen kann -m addrtype --dst-type LOCAL. ZB so etwas wie:

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

Eine andere Möglichkeit ist die Verwendung der TCP-Portweiterleitung. ZB mit socat:

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

Ein Nachteil dieser Methode ist jedoch, dass die Anwendung, die Port 8080 überwacht, die Quelladresse eingehender Verbindungen nicht kennt (z. B. zur Protokollierung oder für andere Identifikationszwecke).


0

Antwort am 2015 / Sep:

ip6tables unterstützt jetzt IPV6 NAT: http://www.netfilter.org/projects/iptables/files/changes-iptables-1.4.17.txt

Sie benötigen Kernel 3.7+

Beweis:

[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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.