Czy proces inny niż root może się połączyć z portami 80 i 443?

104

Czy można dostroić parametr jądra, aby umożliwić programowi użytkownika powiązanie z portami 80 i 443?

Pytam, ponieważ uważam, że głupotą jest zezwolenie uprzywilejowanemu procesowi na otwarcie gniazda i słuchanie. Wszystko, co otwiera gniazdo i nasłuchuje, jest ryzykowne, a aplikacje wysokiego ryzyka nie powinny działać jako root.

Wolałbym raczej dowiedzieć się, jaki nieuprzywilejowany proces nasłuchuje na porcie 80, niż próbować usunąć złośliwe oprogramowanie, które zagrzebało się w uprawnieniach administratora.

jww
źródło
1
Zobacz serverfault.com/questions/268099 i stackoverflow.com/questions/413807 . Krótka odpowiedź brzmi: nie.
Sami Laine
10
Długa odpowiedź brzmi „tak”, więc krótka odpowiedź również powinna brzmieć „tak”.
BT
4
Krótka odpowiedź brzmi: tak.
Jason C

Odpowiedzi:

163

Nie jestem pewien, do czego odnoszą się inne odpowiedzi i komentarze tutaj. Jest to możliwe dość łatwo. Istnieją dwie opcje, które pozwalają na dostęp do portów o niższych numerach bez konieczności podnoszenia procesu rootowania:

Opcja 1: Użyj, CAP_NET_BIND_SERVICEaby przyznać dostęp do portu o niskim numerze procesowi:

Dzięki temu możesz przyznać stały dostęp do określonego pliku binarnego, aby połączyć się z portami o niskiej liczbie za pomocą setcappolecenia:

sudo setcap CAP_NET_BIND_SERVICE=+eip /path/to/binary

Aby uzyskać więcej informacji na temat części e / i / p, patrz cap_from_text.

Po wykonaniu tej czynności /path/to/binarybędzie można połączyć się z portami o niskim numerze. Pamiętaj, że musisz używać setcapsamego pliku binarnego, a nie dowiązania symbolicznego.

Opcja 2: Użyj, authbindaby przyznać jednorazowy dostęp, z lepszą kontrolą użytkownika / grupy / portu:

Narzędzie authbind( strona podręcznika) istnieje właśnie w tym celu.

  1. Zainstaluj authbindza pomocą swojego ulubionego menedżera pakietów.

  2. Skonfiguruj go, aby udzielić dostępu do odpowiednich portów, np. Aby zezwolić na 80 i 443 od wszystkich użytkowników i grup:

    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. Teraz wykonaj polecenie za pomocą authbind(opcjonalnie podając --deeplub inne argumenty, patrz strona podręcznika):

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

    Na przykład

    authbind --deep java -jar SomeServer.jar
    

Oba powyższe mają swoje zalety i wady. Opcja 1 zapewnia zaufanie do pliku binarnego, ale nie zapewnia kontroli dostępu do portu. Opcja 2 zapewnia zaufanie użytkownikowi / grupie i zapewnia kontrolę nad dostępem do portu, ale AFAIK obsługuje tylko IPv4.

Jason C.
źródło
Czy to naprawdę wymaga rwxpozwolenia?
mat
Aby cofnąć operację w opcji 1, czy uruchomiłbyś komendę ponownie, używając -pintead of +eip?
eugene1832
5
Uwaga: w przypadku setcap, jeśli nadpisujesz plik wykonywalny, któremu przyznajesz uprawnienia (np. Wykonaj przebudowę), wówczas traci on status uprzywilejowanego portu i musisz ponownie nadać mu uprawnienia: |
rogerdpack
1
Coś, z czym musiałem się bawić; Próbowałem uruchomić usługę sysv, która uruchamia plik wykonywalny ruby, który używa ruby. Musisz wyrazić setcapzgodę na specyficzny dla wersji plik wykonywalny ruby , np./usr/bin/ruby1.9.1
Christian Rondeau
3
Wątpię, chmodaby byportplik do 777 był najlepszym pomysłem. Widziałem dawanie pozwoleń od 500do 744. Trzymałbym się najbardziej restrykcyjnego, który działa dla ciebie.
Pere
28

Dale Hagglund jest na miejscu. Więc powiem to samo, ale w inny sposób, z pewnymi szczegółami i przykładami. ☺

W światach Uniksa i Linuksa właściwą rzeczą jest:

  • mieć mały, prosty, łatwo kontrolowany program, który działa jako superużytkownik i wiąże gniazdo nasłuchujące;
  • mieć inny mały, prosty, łatwy do skontrolowania program, który odrzuca uprawnienia, spawnowany przez pierwszy program;
  • aby mięso usługi, w osobnym trzecim programie, działało pod kontem nieużytkownika i łańcuchem ładowanym przez drugi program, oczekując po prostu odziedziczenia otwartego deskryptora pliku dla gniazda.

Nie masz pojęcia, gdzie jest wysokie ryzyko. Wysokie ryzyko polega na czytaniu z sieci i działaniu na podstawie tego, co nie jest odczytywane w prostych czynnościach otwierania gniazda, wiązania go z portem i wywoływania listen(). Jest to część usługi, która faktycznie komunikuje się, co jest dużym ryzykiem. Części, które otwarty, bind()i listen(), a nawet (do pewnego stopnia) część, która accepts(), nie są wysokie ryzyko i może być prowadzony pod egidą administratora. Nie używają i nie działają na danych (z wyjątkiem źródłowych adresów IP w accept()przypadku) danych, które są pod kontrolą niezaufanych nieznajomych w sieci.

Można to zrobić na wiele sposobów.

inetd

Jak mówi Dale Hagglund, inetdrobi to stary „superserver sieciowy” . Konto, na którym jest uruchamiany proces obsługi, jest jedną z kolumn w inetd.conf. Nie dzieli części nasłuchującej i upuszczania części na dwa osobne programy, małe i łatwe do skontrolowania, ale dzieli główny kod usługi na osobny program, exec()edytowany w procesie serwisowym, który spawnuje z otwartym deskryptorem pliku dla gniazda.

Trudność audytu nie stanowi większego problemu, ponieważ wystarczy skontrolować tylko jeden program. inetdGłównym problemem nie jest zbyt wiele inspekcji, ale raczej to, że nie zapewnia prostej, precyzyjnej kontroli usług środowiska wykonawczego w porównaniu z nowszymi narzędziami.

UCSPI-TCP i daemontools

Pakiety UCSPI-TCP i daemontools Daniela J. Bernsteina zostały zaprojektowane w tym celu w połączeniu. Alternatywnie można użyć w dużej mierze równoważnego zestawu narzędzi do bisów Daemontools Bruce'a Guentera .

Program do otwierania deskryptora pliku gniazda i łączenia się z uprzywilejowanym portem lokalnym pochodzi tcpserverz UCSPI-TCP. Robi zarówno listen()i accept().

tcpservernastępnie spawnuje albo program usługowy, który sam upuszcza uprawnienia roota (ponieważ obsługiwany protokół wymaga uruchomienia jako superużytkownik, a następnie „zalogowania się”, jak ma to miejsce na przykład w przypadku demona FTP lub SSH) lub setuidgidktóry jest samodzielny mały i łatwy do skontrolowania program, który jedynie upuszcza uprawnienia, a następnie łączy obciążenia z odpowiednim programem serwisowym (z którego żadna część nigdy nie działa z uprawnieniami administratora, jak ma to miejsce, powiedzmy, qmail-smtpd).

runSkrypt usługi byłby więc na przykład (ten dla dummyidentd zapewniający zerową usługę IDENT):

#!/bin/sh -e
exec 2>&1
exec \
tcpserver 0 113 \
setuidgid nobody \
dummyidentd.pl

nosh

Mój pakiet nosh jest do tego przeznaczony. Ma małą setuidgidużyteczność, podobnie jak inne. Jedyną niewielką różnicą jest to, że można go używać z systemdusługami typu „LISTEN_FDS”, a także z usługami UCSPI-TCP, więc tradycyjny tcpserverprogram został zastąpiony przez dwa osobne programy: tcp-socket-listeni tcp-socket-accept.

Ponownie, narzędzia jednocelowe odradzają się i ładują się nawzajem. Jednym z interesujących elementów tego projektu jest to, że można zrezygnować z uprawnień administratora po, listen()ale nawet wcześniej accept(). Oto runskrypt, qmail-smtpdktóry naprawdę robi dokładnie to:

#!/bin/nosh
fdmove -c 2 1
clearenv --keep-path --keep-locale
envdir env/
softlimit -m 70000000
tcp-socket-listen --combine4and6 --backlog 2 ::0 smtp
setuidgid qmaild
sh -c 'exec \
tcp-socket-accept -v -l "${LOCAL:-0}" -c "${MAXSMTPD:-1}" \
ucspi-socket-rules-check \
qmail-smtpd \
'

Programy, które działają pod egidą administratora są drobne narzędzia chain-loading service-agnostyk fdmove, clearenv, envdir, softlimit, tcp-socket-listen, i setuidgid. Do momentu shuruchomienia gniazdo jest otwarte i powiązane z smtpportem, a proces nie ma już uprawnień administratora.

s6, s6-networking i execline

Pakiety sieciowe Laurent Bercot s6 i s6 zostały zaprojektowane do tego w połączeniu. Polecenia są strukturalnie bardzo podobne do poleceń daemontoolsi UCSPI-TCP.

runskrypty byłyby prawie takie same, z wyjątkiem podstawienia na s6-tcpserverfor tcpserveri s6-setuidgidfor setuidgid. Jednakże, można również zdecydować się na korzystanie z M. Bercot za execline zestawu narzędzi jednocześnie.

Oto przykład usługi FTP, lekko zmodyfikowanej w stosunku do oryginału Wayne'a Marshalla , która wykorzystuje execline, s6, s6-networking i program serwera FTP z pliku publicznego :

#!/command/execlineb -PW
multisubstitute {
    define CONLIMIT 41
    define FTP_ARCHIVE "/var/public/ftp"
}
fdmove -c 2 1
s6-envuidgid pubftp 
s6-softlimit -o25 -d250000 
s6-tcpserver -vDRH -l0 -b50 -c ${CONLIMIT} -B '220 Features: a p .' 0 21 
ftpd ${FTP_ARCHIVE}

ipsvd

Ipsvd Gerrit Pape to kolejny zestaw narzędzi, który działa na tych samych zasadach, co ucspi-tcp i s6-networking. Narzędzia są chpsti tcpsvdtym razem, ale robią to samo, a kod wysokiego ryzyka, który odczytuje, przetwarza i zapisuje rzeczy wysyłane przez sieć przez niezaufanych klientów, nadal znajduje się w osobnym programie.

Oto przykład działania M. Pape'afnord w runskrypcie:

#!/bin/sh
exec 2>&1
cd /public/10.0.5.4
exec \
chpst -m300000 -Uwwwuser \
tcpsvd -v 10.0.5.4 443 sslio -v -unobody -//etc/fnord/jail -C./cert.pem \
fnord

systemd

systemd, nowy system nadzoru usług i inicjalizacji, który można znaleźć w niektórych dystrybucjach Linuksa, ma na celu robienie tego, co inetdmożna . Jednak nie używa pakietu małych samodzielnych programów. systemdNiestety, należy przeprowadzić audyt w całości.

Z systemdjednym tworzy pliki konfiguracyjne, aby określić, które gniazdo systemdnasłuchuje i usługi, które systemdrozpoczyna. Plik „jednostki” usługi ma ustawienia, które pozwalają na dużą kontrolę nad procesem obsługi, w tym nad tym, jak użytkownik działa.

Gdy ten użytkownik jest ustawiony jako użytkownik niebędący superużytkownikiem, systemdwykonuje całą pracę polegającą na otwarciu gniazda, powiązaniu go z portem i wywołaniu listen()(oraz, w razie potrzeby, accept()) w procesie nr 1 jako superużytkownik oraz procesie obsługi spawn działa bez uprawnień administratora.

JdeBP
źródło
2
Dziękuje za komplement. To świetny zbiór konkretnych porad. +1.
Dale Hagglund,
11

Mam raczej inne podejście. Chciałem użyć portu 80 dla serwera node.js. Nie mogłem tego zrobić, ponieważ Node.js został zainstalowany dla użytkownika innego niż sudo. Próbowałem użyć dowiązań symbolicznych, ale to nie działało dla mnie.

Potem dowiedziałem się, że mogę przekierowywać połączenia z jednego portu do drugiego. Więc uruchomiłem serwer na porcie 3000 i skonfigurowałem port przekierowania z portu 80 na port 3000.

Ten link zawiera rzeczywiste polecenia, których można użyć do tego celu. Oto polecenia -

localhost / loopback

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

zewnętrzny

sudo iptables -t nat -I PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 3000

Użyłem drugiego polecenia i zadziałało to dla mnie. Myślę więc, że jest to środek, dla którego proces użytkownika nie może bezpośrednio uzyskiwać dostępu do niższych portów, ale umożliwia im dostęp za pomocą przekierowania portów.

Nowicjusz
źródło
6
+1 za nieszablonowe myślenie
Richard Wiseman
4

Twoje instynkty są całkowicie poprawne: źle jest uruchamiać duży złożony program jako root, ponieważ ich złożoność sprawia, że ​​trudno jest im ufać.

Ale złym pomysłem jest również zezwolenie zwykłym użytkownikom na łączenie się z uprzywilejowanymi portami, ponieważ takie porty zwykle reprezentują ważne usługi systemowe.

Standardowym podejściem do rozwiązania tej pozornej sprzeczności jest rozdział przywilejów . Podstawową ideą jest podzielenie programu na dwie (lub więcej) części, z których każda stanowi dobrze zdefiniowaną część ogólnej aplikacji i które komunikują się za pomocą prostych ograniczonych interfejsów.

W podanym przykładzie chcesz podzielić swój program na dwie części. Ten, który działa jako root i otwiera się i łączy z uprzywilejowanym gniazdem, a następnie przekazuje go jakoś innej części, która działa jako zwykły użytkownik.

Te dwa główne sposoby osiągnięcia tego rozdziału.

  1. Pojedynczy program uruchamiany jako root. Pierwszą rzeczą, jaką robi, jest stworzenie niezbędnego gniazda, w możliwie najprostszy i ograniczony sposób. Następnie upuszcza uprawnienia, czyli przekształca się w zwykły proces w trybie użytkownika i wykonuje wszystkie inne czynności. Poprawne usuwanie uprawnień jest trudne, więc poświęć trochę czasu na studiowanie właściwego sposobu, aby to zrobić.

  2. Para programów komunikujących się za pośrednictwem pary gniazd utworzonej przez proces nadrzędny. Nieuprzywilejowany program sterownika odbiera początkowe argumenty i być może wykonuje pewne podstawowe sprawdzenie poprawności argumentów. Tworzy parę połączonych gniazd za pomocą socketpair (), a następnie rozwidla i wykonuje dwa inne programy, które wykonają prawdziwą pracę i komunikują się za pośrednictwem pary gniazd. Jedna z nich jest uprzywilejowana i stworzy gniazdo serwera oraz wszelkie inne operacje uprzywilejowane, a druga wykona bardziej złożone, a zatem mniej godne zaufania wykonanie aplikacji.

[1] http://en.m.wikipedia.org/wiki/Privilege_separation

Dale Hagglund
źródło
To, co proponujesz, nie jest uważane za najlepszą praktykę. Możesz spojrzeć na inetd, który może nasłuchiwać na uprzywilejowanym gnieździe, a następnie przekazać to gniazdo nieuprzywilejowanemu programowi.
Dale Hagglund
3

Najprostsze rozwiązanie: usuń wszystkie uprzywilejowane porty w systemie Linux

Działa na Ubuntu / Debian:

#save configuration permanently
echo 'net.ipv4.ip_unprivileged_port_start=0' > /etc/sysctl.d/50-unprivileged-ports.conf
#apply conf
sysctl --system

(działa dobrze dla VirtualBox z kontem innym niż root)

Teraz uważaj na bezpieczeństwo, ponieważ wszyscy użytkownicy mogą powiązać wszystkie porty!

soleuu
źródło
To sprytne. Jedna mała nitka: konfiguracja otwiera 80 i 443, ale otwiera także wszystkie pozostałe porty. Zrelaksowanie uprawnień w innych portach może nie być pożądane.
jww