2014-02-02 05:48:05 +0000 2014-02-02 05:48:05 +0000
117
117

Zezwolić procesowi nie będącemu rootem na łączenie się z portem 80 i 443?

Czy możliwe jest dostrojenie parametrów jądra, aby umożliwić programowi userland wiązanie się z portem 80 i 443?

Powodem, dla którego pytam jest to, że uważam, że głupotą jest pozwolić uprzywilejowanemu procesowi na otwarcie gniazda i nasłuchiwanie. Wszystko, co otwiera gniazdo i nasłuchuje jest wysokiego ryzyka, a aplikacje wysokiego ryzyka nie powinny być uruchamiane jako root.

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

Odpowiedzi (5)

176
176
176
2015-03-21 21:12:41 +0000

Nie jestem pewien, do czego odnoszą się inne odpowiedzi i komentarze tutaj. Jest to możliwe dość łatwo. Istnieją dwie opcje, obie, które umożliwiają dostęp do portów o niskim numerze bez konieczności podnoszenia procesu do roota:

Opcja 1: Użyj CAP_NET_BIND_SERVICE , aby przyznać dostęp do portów o niskiej numeracji procesowi:

Za pomocą tego możesz przyznać stały dostęp do określonej binarnej do wiązania się do portów o niskiej numeracji za pomocą polecenia setcap:

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

Więcej szczegółów na temat części e/i/p znajdziesz w cap_from_text .

Po wykonaniu tych czynności, /path/to/binary będzie w stanie wiązać się z portami o niskich numerach. Zauważ, że musisz użyć setcap na samej binarce, a nie na symlinku.

Opcja 2: Użyj authbind do przyznania jednorazowego dostępu, z dokładniejszą kontrolą użytkownika/grupy/portu:

Narzędzie authbind strona man ) istnieje właśnie po to.

  1. Zainstaluj authbind używając swojego ulubionego menedżera pakietów.

  2. Skonfiguruj go tak, aby przyznawał dostęp do odpowiednich portów, np. aby zezwalał na 80 i 443 od wszystkich użytkowników i grup:

  3. Teraz wykonaj swoje polecenie za pomocą authbind (opcjonalnie podając --deep lub inne argumenty, patrz strona man):


Istnieją plusy i minusy obu powyższych rozwiązań. Opcja 1 przyznaje zaufanie do binary, ale nie zapewnia żadnej kontroli nad dostępem per-port. Opcja 2 przyznaje zaufanie do user/group i zapewnia kontrolę nad dostępem per-port, ale starsze wersje obsługiwały tylko IPv4 (od czasu, gdy pierwotnie napisałem to, nowsze wersje z obsługą IPv6 zostały wydane).

29
29
29
2014-02-02 16:21:39 +0000

Dale Hagglund ma rację. Więc powiem to samo, ale w inny sposób, z kilkoma konkretami i przykładami. ☺

Właściwą rzeczą do zrobienia w świecie Uniksa i Linuksa jest:

  • mieć mały, prosty, łatwy do skontrolowania, program, który działa jako superużytkownik i wiąże gniazdo nasłuchujące;
  • mieć inny mały, prosty, łatwy do skontrolowania, program zrzucający uprawnienia, wywołany przez pierwszy program;
  • mieć mięso usługi, w oddzielnym trzecim programie, uruchomionym pod kontem nie-superużytkownika i ładowanym łańcuchowo przez drugi program, oczekując, że po prostu odziedziczy otwarty deskryptor pliku dla gniazda.

Masz złe wyobrażenie o tym, gdzie jest wysokie ryzyko. Wysokie ryzyko jest w czytaniu z sieci i działaniu na tym, co jest czytane, a nie w prostych czynnościach otwierania gniazda, wiązania go z portem i wywoływania listen(). To jest część usługi, która wykonuje rzeczywistą komunikację, która jest wysokim ryzykiem. Części, które otwierają, bind(), i listen(), a nawet (w pewnym stopniu) część, która accepts(), nie są wysokiego ryzyka i mogą być uruchamiane pod egidą superużytkownika. Nie używają one i nie działają na (z wyjątkiem źródłowych adresów IP w przypadku accept()) danych, które są pod kontrolą niezaufanych obcych osób w sieci.

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

inetd

Jak mówi Dale Hagglund, robi to stary “superserwer sieciowy” inetd. Konto, pod którym uruchamiany jest proces usługi, jest jedną z kolumn w inetd.conf. Nie rozdziela części nasłuchującej i części zrzucającej uprawnienia na dwa oddzielne programy, małe i łatwe do skontrolowania, ale oddziela główny kod usługi do oddzielnego programu, exec()ed w procesie usługowym, który wywołuje z otwartym deskryptorem pliku dla gniazda.

Trudności z audytem nie są aż tak duże, ponieważ trzeba audytować tylko jeden program. Głównym problemem inetd nie jest audyt, ale to, że nie zapewnia on prostej, drobnoziarnistej kontroli usług runtime, w porównaniu z nowszymi narzędziami.

UCSPI-TCP i daemontools

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

Programem do otwierania deskryptora pliku gniazda i łączenia się z uprzywilejowanym portem lokalnym jest tcpserver , z UCSPI-TCP. Wykonuje on zarówno listen() jak i accept().

tcpserver następnie wywołuje albo program usługowy, który sam zrzuca uprawnienia roota (ponieważ obsługiwany protokół wymaga rozpoczęcia pracy jako superużytkownik, a następnie “zalogowania się”, jak to ma miejsce w przypadku np, FTP lub demona SSH) lub setuidgid , który jest samodzielnym, małym i łatwym do skontrolowania programem, który wyłącznie zrzuca przywileje, a następnie ładuje łańcuchowo do właściwego programu usługowego (żadna część tego programu nigdy nie działa z przywilejami superużytkownika, jak w przypadku, powiedzmy, qmail-smtpd ).

Skrypt usługi run byłby na przykład taki (ten dla dummyidentd do zapewnienia usługi null IDENT):

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

nosh

Mój pakiet nosh jest do tego przeznaczony. Posiada on małe narzędzie setuidgid , tak jak inne. Jedną drobną różnicą jest to, że można go używać z usługami w stylu systemd “LISTEN_FDS”, jak również z usługami UCSPI-TCP, więc tradycyjny program tcpserver jest zastąpiony przez dwa oddzielne programy: tcp-socket-listen i tcp-socket-accept.

Ponownie, narzędzia jednozadaniowe spawnują i ładują się łańcuchowo jedno po drugim. Ciekawostką tego projektu jest to, że można pozbyć się uprawnień superużytkownika po listen(), ale przed accept(). Oto skrypt run dla qmail-smtpd, który rzeczywiście to robi:

#!/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ą superużytkownika, to małe narzędzia do ładowania łańcuchów serwisowych fdmove, clearenv, envdir, softlimit, tcp-socket-listen i setuidgid. W momencie uruchomienia sh, gniazdo jest otwarte i powiązane z portem smtp, a proces nie ma już przywilejów superużytkownika.

s6, s6-networking, i execline

Pakiety s6 i s6-networking Laurenta Bercota zostały zaprojektowane do wykonywania tego w połączeniu. Komendy są strukturalnie bardzo podobne do tych z daemontools i UCSPI-TCP. Skrypty

run byłyby takie same, z wyjątkiem zamiany s6-tcpserver na tcpserver i s6-setuidgid na setuidgid. Jednakże, można również zdecydować się na użycie zestawu narzędzi execline M. Bercota w tym samym czasie.

Oto przykład usługi FTP, lekko zmodyfikowanej w stosunku do oryginału Wayne'a Marshalla , która używa execline, s6, s6-networking, oraz programu serwera FTP z publicfile :

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

Gerrita Pape'a ipsvd jest kolejnym zestawem narzędzi, który działa na tych samych zasadach co ucspi-tcp i s6-networking. Tym razem są to narzędzia chpst i tcpsvd, ale robią to samo, a kod wysokiego ryzyka, który odczytuje, przetwarza i zapisuje rzeczy przesyłane przez sieć przez niezaufanych klientów jest nadal w oddzielnym programie.

Oto przykład M. Pape uruchomienia fnord w skrypcie run:

#!/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 inetd , nowy system nadzoru usług i init, który można znaleźć w niektórych dystrybucjach Linuksa, [ ma robić to, co systemd potrafi robić ]0x3&. Jednak nie używa on zestawu małych, samodzielnych programów. Niestety, trzeba audytować systemd w całości.

Za pomocą systemd tworzy się pliki konfiguracyjne definiujące gniazdo, na którym systemd nasłuchuje, oraz usługę, którą systemd uruchamia. Plik “unit” usługi ma ustawienia, które pozwalają na dużą kontrolę nad procesem usługi, w tym nad tym, jako jaki użytkownik jest uruchamiany.

Jeśli użytkownik jest ustawiony jako nie-nadużytkownik, listen() wykonuje całą pracę związaną z otwieraniem gniazda, wiązaniem go z portem i wywoływaniem accept() (oraz, jeśli jest to wymagane, 0x6&) w procesie #1 jako superużytkownik, a uruchamiany przez niego proces usługowy działa bez uprawnień superużytkownika.

17
17
17
2018-06-27 07:00:56 +0000

Mam raczej inne podejście. Chciałem użyć portu 80 dla serwera node.js. Nie byłem w stanie tego zrobić, ponieważ Node.js został zainstalowany dla użytkownika nie-sudo. Próbowałem użyć symlinków, ale to nie działało dla mnie.

Wtedy dowiedziałem się, że mogę przekierować połączenia z jednego portu na inny port. Uruchomiłem więc serwer na porcie 3000 i skonfigurowałem przekierowanie z portu 80 na port 3000. Ten link zawiera faktyczne komendy, które mogą być użyte, aby to zrobić. Oto te polecenia -

localhost/loopback

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

external

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

Użyłem drugiego polecenia i zadziałało dla mnie. Myślę więc, że jest to pośredni sposób, aby nie pozwolić procesowi użytkownika na bezpośredni dostęp do niższych portów, ale dać im dostęp przy użyciu przekierowania portów.

4
4
4
2014-02-02 06:49:22 +0000

Twój instynkt jest całkowicie poprawny: złym pomysłem jest uruchamianie dużych, złożonych programów jako root, ponieważ ich złożoność sprawia, że trudno im zaufać.

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

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

W podanym przez ciebie przykładzie, chcesz rozdzielić swój program na dwie części. Jeden, który działa jako root, otwiera i łączy się z uprzywilejowanym gniazdem, a następnie przekazuje je w jakiś sposób do drugiej części, która działa jako zwykły użytkownik.

Są dwa główne sposoby na osiągnięcie tej separacji.

  1. Pojedynczy program, który uruchamia się jako root. Pierwszą rzeczą jaką robi jest utworzenie niezbędnego gniazda, w możliwie prosty i ograniczony sposób. Następnie zrzuca przywileje, czyli przekształca się w zwykły proces trybu użytkownika i wykonuje całą resztę pracy. Prawidłowe zrzucanie przywilejów jest trudne, więc proszę poświęcić trochę czasu na przestudiowanie właściwego sposobu, w jaki należy to robić.

  2. Para programów, które komunikują się przez parę gniazd utworzonych przez proces nadrzędny. Nieuprzywilejowany program sterujący otrzymuje początkowe argumenty i być może przeprowadza podstawową walidację 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 będą komunikować się za pośrednictwem pary gniazd. Jeden z nich jest uprzywilejowany i utworzy gniazdo serwera oraz wykona inne uprzywilejowane operacje, a drugi wykona bardziej złożoną i dlatego mniej godną zaufania aplikację.

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

3
3
3
2019-09-13 07:38:46 +0000

Najprostsze rozwiązanie : usunąć wszystkie uprzywilejowane porty na linuxie

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 VirtualBoxa z kontem nieroot)

Teraz należy uważać na bezpieczeństwo, ponieważ wszyscy użytkownicy mogą bindować wszystkie porty !