Czy istnieje sposób na powiązanie procesów użytkownika innego niż root z „uprzywilejowanymi” portami w systemie Linux?


389

To irytujące jest mieć to ograniczenie w moim pudełku programistycznym, kiedy nigdy nie będzie żadnych użytkowników innych niż ja.

Jestem świadomy standardowych obejść , ale żadne z nich nie robi dokładnie tego, co chcę:

  1. authbind (wersja w testowaniu Debiana, 1.0, obsługuje tylko IPv4)
  2. Wykorzystanie celu iptables REDIRECT do przekierowania niskiego portu do wysokiego portu (tabela „nat” nie jest jeszcze zaimplementowana dla ip6tables, wersji IPv6 iptables)
  3. sudo (staram się unikać roota)
  4. SELinux (lub podobny). (To tylko moje pudełko deweloperów, nie chcę wprowadzać dodatkowej złożoności.)

Czy istnieje jakaś prosta sysctlzmienna pozwalająca procesom innym niż root na łączenie się z „uprzywilejowanymi” portami (porty mniejsze niż 1024) w Linuksie, czy mam po prostu pecha?

EDYCJA: W niektórych przypadkach możesz użyć do tego możliwości.


5
Z mojego doświadczenia wynika, że ​​jednym z powodów jest próba napisania serwera WWW zamiast używania Apache (lub lighttpd).
S.Lott,

Dodałem rzeczy setcap do mojej odpowiedzi na prawie identyczny stackoverflow.com/questions/277991/...
Paul Tomblin

15
Ponieważ w tym przypadku korzystałem z IPv6, dlatego niektóre z „zwykłych” obejść (authbind i iptables REDIRECT) nie działały dla mnie.
Jason Creighton


1
Można to zrobić na kilka sposobów. Zobacz Zezwolić procesowi użytkownika innego niż root na łączenie się z portami 80 i 443? na superużytkowniku.
jww

Odpowiedzi:


392

Ok, dzięki ludziom, którzy wskazali system CAP_NET_BIND_SERVICEmożliwości i możliwości. Jeśli masz najnowsze jądro, rzeczywiście można go użyć do uruchomienia usługi innej niż root, ale powiązania niskich portów. Krótka odpowiedź brzmi:

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

A potem, kiedy tylko programzostanie wykonany, będzie miał taką CAP_NET_BIND_SERVICEmożliwość. setcapjest w pakiecie debian libcap2-bin.

Teraz ostrzeżenia:

  1. Będziesz potrzebował co najmniej jądra 2.6.24
  2. To nie zadziała, jeśli plik jest skryptem. (tzn. używa wiersza #! do uruchomienia interpretera). W tym przypadku, o ile rozumiem, musiałbyś zastosować tę zdolność do samego pliku wykonywalnego interpretera, co oczywiście jest koszmarem bezpieczeństwa, ponieważ każdy program używający tego interpretera będzie miał taką możliwość. Nie mogłem znaleźć żadnego czystego, łatwego sposobu obejścia tego problemu.
  3. Linux wyłączy LD_LIBRARY_PATH na każdym, programktóry ma podwyższone uprawnienia, takie jak setcaplub suid. Więc jeśli używasz programwłasnego .../lib/, być może będziesz musiał rozważyć inną opcję, taką jak przekierowanie portów.

Zasoby:

Uwaga: RHEL po raz pierwszy dodał to w wersji 6 .


3
Częściowe obejście dla skryptów: utwórz kopię interpretera (np. Bash), daj mu możliwości, ale ogranicz dostęp do kopii tylko tym użytkownikom, którzy jej potrzebują. Oczywiście tym użytkownikom należy ufać, ale i tak mogą zmienić skrypt.
Erich Kitzmueller,

3
Oprócz wspomnianego pakietu debian (binarnego), strona dewelopera to friedhoff.org/posixfilecaps.html powiązane dokumenty / prezentacje / etc ...
RandomNickName42

1
na suse SLES 11.1 musiałem dodać parametr jądra file_caps = 1 do menu grub.lst, aby to działało.
Shay,

2
Tak @ C.Ross, ponieważ musiałby zostać zastosowany, /usr/bin/javaa następnie otworzyłby możliwość dowolnej aplikacji Java uruchomionej w systemie. Szkodliwych możliwości nie można również ustawić dla poszczególnych użytkowników.
joeytwiddle

5
Czy ustawienie setcap utrzymuje się podczas ponownego uruchamiania; jeśli nie, czy istnieje standardowe miejsce na umieszczenie tej reguły, aby działała podczas uruchamiania systemu? Czy /etc/security/capability.confna Debianie / Ubuntu jest jakaś pomoc?
joeytwiddle

34

Możesz wykonać przekierowanie portu. To właśnie robię dla serwera strategii Silverlight działającego w systemie Linux

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

5
Niestety będzie to działać tylko w przypadku połączeń z routingiem, tj. Nie z lokalnego komputera.
zbyszek

@joeytwiddle Myślę, że jest w skrypcie, który uruchamia (uruchamia) serwer, ale mogę się mylić. Chciałbym również wiedzieć: P
Camilo Martin

@CamiloMartin Patrząc ponownie, dokumentacja Debiana , do której odsyłacz znajdował się w pytaniu, zaleca umieszczenie go w skrypcie zapory ogniowej lub utworzenie go w /etc/network/if-up.d/firewall.
joeytwiddle

8
@zbyszek Może to działać w przypadku lokalnych połączeń z maszyną, z dodatkową regułą iptables: stackoverflow.com/a/31795603/1356953
00500005

W starszych jądrach wydaje się, że nie było to obsługiwane dla IPv6 . Ale najwyraźniej jest obsługiwany w ip6tables v1.4.18 i jądrze Linux v3.8.
Craig McQueen,

31

Standardowym sposobem jest uczynienie ich „setuidami”, aby uruchomili się jako root, a następnie zrzekają się przywileju rootowania, gdy tylko połączą się z portem, ale zanim zaczną akceptować połączenia z nim. Dobre przykłady tego można zobaczyć w kodzie źródłowym Apache i INN. Powiedziano mi, że Lighttpd jest kolejnym dobrym przykładem.

Innym przykładem jest Postfix, który korzysta z wielu demonów komunikujących się przez potoki, a tylko jeden lub dwa z nich (które robią bardzo niewiele oprócz akceptowania lub wysyłania bajtów) działają jako root, a reszta działa z niższymi uprawnieniami.


1
Co ciekawe, nie działa to w najnowszych wersjach Linuksa (może tylko Ubuntu) bez zestawu CAP_SETUID. Jeśli więc potrzebujesz setuida, i tak będziesz musiał ustawić tę funkcję.
Matt

Porzucenie uprawnień roota jest właściwym sposobem na zrobienie tego. Chociaż ustawianie setuid nie jest konieczne, jeśli init / upstart / systemd uruchomił usługę.
Michael Hampton,

2
Jest to trudne, jeśli program jest napisany w interpretowanym języku lub interpretera kodu bajtowego, takiego jak C # (Mono), Java, Python. (Najwyraźniej Perl dokonał tego poprzez binfmt_miscswoją flagę „C”; nie jestem pewien co do innych).
Craig McQueen

bardzo mało oprócz emitowania bajtów, to nie robi niewiele.
Ryan,

Jeśli ustawisz binarny setuid, będzie on działał jako proces root. Pytanie jednak dotyczy tego, jak to zrobić bez uruchamiania pliku binarnego jako procesu rootowania. To rozwiązanie jest odpowiedzią na inne pytanie, a mianowicie: „W jaki sposób użytkownik nieuprzywilejowany może powiązać proces z uprzywilejowanym portem”, ale to nie jest postawione pytanie, IMHO?
Michael Beer

24

Lub załataj jądro i usuń zaznaczenie.

(Opcja ostateczna, niezalecana).

W net/ipv4/af_inet.cusuń dwa czytane wiersze

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

a jądro nie będzie już sprawdzać uprzywilejowanych portów.


22
Bardzo zły pomysł z wielu powodów.
Adam Lassek

24
To zły pomysł. Ale faktycznie by działało. I na pewno mnie rozśmieszyło.
Oto Brglez,

23
Oczywiście to zły pomysł. Dlatego powiedziałem w ostateczności. Punktem otwartym jest to, że jeśli nie działa tak, jak chcesz, możesz to zmienić.
Joshua,

18
Nie jestem pewien, dlaczego zostałeś przegłosowany. Twórcy szkodliwego oprogramowania nie dbają o port, na którym nasłuchują, i chętnie otwierają port większy niż 1024. Wydaje mi się, że wszystkie porty powinny wymagać uprawnień lub żaden port nie powinien wymagać uprawnień. Wymaganie roota, aby otworzyć port poniżej 1024, oznacza po prostu, że aplikacja o wysokim ryzyku działa jako root. To wydaje mi się naprawdę głupim pomysłem. Być może coś mi tu brakuje ...
jww

9
Wcześniej port <1024 był używany w protokołach UNIX do UNIX, aby udowodnić, że kod działający na drugim końcu działał jako root. Działało to dość dobrze dla zestawu serwerów UNIX ze wspólnymi zabezpieczeniami.
Joshua

22

Możesz skonfigurować lokalny tunel SSH, np. Jeśli chcesz, aby port 80 trafił do Twojej aplikacji powiązanej z 3000:

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

Ma to tę zaletę, że współpracuje z serwerami skryptów i jest bardzo proste.


1
Mój osobisty faworyt. Bardzo łatwe do włączania i wyłączania i nie wymaga zmian w całym systemie. Świetny pomysł!
Dan Passaro

To jest niesamowite. Zdecydowanie najprostsza odpowiedź.
michaelsnowden

1
Spodziewałbym się, że będzie to umiarkowanie drogie i nie jest dobrym pomysłem w żadnej sytuacji związanej z wydajnością, ale nie mogę być tego pewien bez przetestowania. Szyfrowanie SSH kosztuje niezerową kwotę.
lahwran

3
Można to zrobić za pomocą netcat bez narzutu ssh:sudo nc -l 80 | nc localhost 3000
Bugster

1
Szyfrujesz + deszyfrujesz tylko w celu przeniesienia danych do innego portu na tym samym komputerze? Nie działa to również w przypadku ruchu UDP.
Daniel F

19

Aktualizacja 2017:

Użyj authbind


Znacznie lepiej niż CAP_NET_BIND_SERVICE lub niestandardowe jądro.

  • CAP_NET_BIND_SERVICE zapewnia zaufanie do pliku binarnego, ale nie zapewnia kontroli dostępu do portu.
  • Authbind zapewnia zaufanie użytkownikowi / grupie i zapewnia kontrolę nad dostępem przez port oraz obsługuje zarówno IPv4, jak i IPv6 (obsługa IPv6 została ostatnio dodana).

    1. Zainstalować: apt-get install authbind

    2. Skonfiguruj dostęp do odpowiednich portów, np. 80 i 443 dla 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. 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
      

W następstwie fantastycznej (= niezalecanej, jeśli nie wiesz co robisz) rekomendacji Joshua do zhakowania jądra:

Po raz pierwszy opublikowałem to tutaj .

Prosty. Z normalnym lub starym jądrem nie.
Jak zauważyli inni, iptables może przekierować port.
Jak zauważyli także inni, CAP_NET_BIND_SERVICE może również wykonać zadanie.
Oczywiście CAP_NET_BIND_SERVICE nie powiedzie się, jeśli uruchomisz program ze skryptu, chyba że ustawisz ograniczenie interpretera powłoki, co jest bezcelowe, możesz równie dobrze uruchomić swoją usługę jak root ...
np. W przypadku Java, musisz ją zastosować do JVVA JVM

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

Oczywiście oznacza to, że każdy program Java może powiązać porty systemowe.
Dito dla mono / .NET.

Jestem też całkiem pewien, że xinetd nie jest najlepszym pomysłem.
Ale skoro obie metody to hacki, dlaczego nie po prostu podnieść limit przez zniesienie ograniczenia?
Nikt nie powiedział, że musisz uruchomić normalne jądro, więc możesz po prostu uruchomić własne.

Po prostu pobierasz źródło najnowszego jądra (lub tego samego, co aktualnie posiadasz). Następnie przejdziesz do:

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

Tam szukasz tej linii

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

i zmień na

#define PROT_SOCK 0

jeśli nie chcesz mieć niepewnej sytuacji ssh, zmień to na: # zdefiniuj PROT_SOCK 24

Zasadniczo użyłbym najniższego ustawienia, którego potrzebujesz, np. 79 dla http lub 24, gdy używasz SMTP na porcie 25.

To już wszystko.
Skompiluj jądro i zainstaluj je.
Restart.
Zakończone - ten głupi limit Zniknął, i działa również w przypadku skryptów.

Oto jak skompilujesz jądro:

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

W skrócie, użyj iptables, jeśli chcesz zachować bezpieczeństwo, skompiluj jądro, jeśli chcesz mieć pewność, że to ograniczenie nigdy więcej nie będzie Ci przeszkadzać.


Masz powód do głosowania? Hater-hater, czy też instrukcje kompilacji jądra nie działały?
Stefan Steiger,

4
modyfikacja jądra sprawia, że ​​przyszłe aktualizacje są o wiele bardziej bolesne. nie zrobiłbym tego, ponieważ wiem, że byłbym zbyt leniwy, aby regularnie aktualizować jądro. fajnie, że jest to teoretycznie możliwe, ale w wielu okolicznościach nie jest to realna opcja.
kritzikratzi

Być może bezpieczniejszym sposobem na to jest zmiana chmod 777 / etc / authbind / byport / 80 przez chmod 544 / etc / authbind / byport / 23 niż login chown: group / etc / authbind / byport / 23
Jérôme B

18

Funkcje plików nie są idealne, ponieważ mogą ulec uszkodzeniu po aktualizacji pakietu.

Idealnym rozwiązaniem, IMHO, powinna być zdolność do tworzenia powłoki z CAP_NET_BIND_SERVICEzestawem dziedzicznym .

Oto nieco skomplikowany sposób:

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

capshnarzędzie można znaleźć w pakiecie libcap2-bin w dystrybucjach Debian / Ubuntu. Oto, co się dzieje:

  • sgzmienia efektywny identyfikator grupy na identyfikator użytkownika demona. Jest to konieczne, ponieważ capshpozostawia GID bez zmian i zdecydowanie nie chcemy tego.
  • Ustawia bit „zachowaj możliwości zmiany UID”.
  • Zmienia UID na $DAEMONUSER
  • Zrzuca wszystkie czapki (w tym momencie wszystkie czapki są nadal obecne z powodu --keep=1), z wyjątkiem dziedzicznychcap_net_bind_service
  • Wykonuje twoje polecenie („-” jest separatorem)

Wynikiem jest proces z określonym użytkownikiem i grupą oraz cap_net_bind_serviceuprawnieniami.

Na przykład wiersz ze ejabberdskryptu uruchamiania:

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

I to faktycznie NIE działa. Czapki nie są zachowywane przez przełącznik ID użytkownika. Będzie działać, jeśli chcesz uruchomić jako root, ale ze wszystkimi możliwościami porzuconymi.
Cyberax,

Jeśli spróbuję, otrzymam komunikat „nie można wykonać pliku binarnego”. W rzeczywistości nie mogę capshzrobić nic innego niż „nie mogę wykonać pliku binarnego” podczas korzystania z opcji --lub ==. Zastanawiam się, czego mi brakuje.
Craig McQueen

@CraigMcQueen, wszystko później --jest przekazywane do /bin/bash, więc możesz spróbować -c 'your command'. Niestety, wydaje mi się, że mam ten sam problem co @Cyberax, ponieważ otrzymuję „odmowę dostępu” przy próbie bind.
Amir,

Aby to działało bez możliwości ustawiania plików (lub działa jako root), trzeba by użyć możliwości otoczenia jak opisane w tym Unix.SE odpowiedź . Można także użyć --gidzamiast sg(lub --userktóre zestawy --uid, --gidi --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

Z jakiegoś powodu nikt nie wspomniał o obniżeniu sysctl net.ipv4.ip_unprivileged_port_start do wartości, której potrzebujesz. Przykład: Musimy powiązać naszą aplikację z portem 443.

sysctl net.ipv4.ip_unprivileged_port_start=443

Niektórzy mogą powiedzieć, że istnieje potencjalny problem z bezpieczeństwem: nieuprzywilejowani użytkownicy mogą teraz łączyć się z innymi uprzywilejowanymi portami (444-1024). Możesz jednak łatwo rozwiązać ten problem za pomocą iptables, blokując inne porty:

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

Porównanie z innymi metodami. Ta metoda:

  • z jakiegoś punktu jest (IMO) jeszcze bardziej bezpieczne niż ustawienie CAP_NET_BIND_SERVICE / setuid, ponieważ aplikacja wcale nie setuid, nawet częściowo (możliwości są w rzeczywistości). Na przykład, aby złapać rdzeń aplikacji z włączonymi możliwościami, musisz zmienić sysctl fs.suid_dumpable (co prowadzi do innych potencjalnych problemów związanych z bezpieczeństwem). Po ustawieniu CAP / suid katalog / proc / PID jest własnością root, więc Twój użytkownik inny niż root nie będzie miał pełnej informacji / kontroli nad uruchomionym procesem, na przykład użytkownik nie będzie w stanie (w zwykłym przypadku) określić, które połączenia należą do aplikacji poprzez / proc / PID / fd / (netstat -aptn | grep PID).
  • ma wadę w zakresie bezpieczeństwa: podczas gdy Twoja aplikacja (lub dowolna aplikacja korzystająca z portów 443-1024) jest z jakiegoś powodu wyłączona, inna aplikacja może przejąć ten port. Ale ten problem można również zastosować do CAP / suid (w przypadku ustawienia go na interpreter, np. Java / nodejs) i iptables-redirect. Użyj metody systemd-socket, aby wykluczyć ten problem. Użyj metody authbind, aby zezwolić tylko na specjalne powiązanie użytkownika.
  • nie wymaga ustawiania CAP / suid za każdym razem, gdy wdrażasz nową wersję aplikacji.
  • nie wymaga obsługi / modyfikacji aplikacji, jak metoda systemd-socket.
  • nie wymaga przebudowy jądra (jeśli uruchomiona wersja obsługuje to ustawienie sysctl)
  • nie robi LD_PRELOAD jak metoda authbind / privbind, może to potencjalnie wpłynąć na wydajność, bezpieczeństwo i zachowanie (czy to nie? testowane). W pozostałej części authbind jest naprawdę elastyczną i bezpieczną metodą.
  • nadmiernie wykonuje metodę REDIRECT / DNAT iptables, ponieważ nie wymaga tłumaczenia adresu, śledzenia stanu połączenia itp. Jest to zauważalne tylko w systemach o dużym obciążeniu.

W zależności od sytuacji wybrałbym pomiędzy sysctl, CAP, authbind i iptables-redirect. I to świetnie, że mamy tak wiele opcji.


2
Dzięki za świetną odpowiedź! Wygląda na to, że ta funkcjonalność pojawiła się po raz pierwszy w Linuksie 4.11 w kwietniu 2017 r. , Więc nie było jej w 2009 roku, kiedy po raz pierwszy zadałem to pytanie. Zrobiłem też szybki test i wydaje się, że działa również dla IPV6, mimo że „ipv4” jest w nazwie sysctl.
Jason Creighton

15

Dwie inne proste możliwości:

Istnieje stare (niemodne) rozwiązanie „demona, który łączy się z niskim portem i zapewnia kontrolę nad twoim demonem”. Nazywa się to inetd (lub xinetd). Minusy to:

  • twój demon musi rozmawiać na stdin / stdout (jeśli nie kontrolujesz demona - jeśli nie masz źródła - wtedy może to być showstopper, chociaż niektóre usługi mogą mieć flagę zgodności inetd)
  • nowy proces demona jest rozwidlany dla każdego połączenia
  • to jedno dodatkowe ogniwo w łańcuchu

Plusy:

  • dostępne w każdym starym systemie UNIX
  • kiedy administrator systemu skonfiguruje konfigurację, możesz zacząć zajmować się rozwojem (kiedy odbudujesz demona, możesz stracić możliwości setcap? A potem będziesz musiał wrócić do administratora ”, proszę pana ... . ”)
  • demon nie musi się martwić o takie rzeczy związane z siecią, musi tylko rozmawiać na stdin / stdout
  • może skonfigurować, aby uruchomić demona jako użytkownik inny niż root, zgodnie z żądaniem

Inna alternatywa: zhakowany serwer proxy (netcat lub nawet coś bardziej niezawodnego ) z uprzywilejowanego portu do dowolnego dowolnego portu o wysokiej liczbie, gdzie można uruchomić docelowego demona. (Netcat oczywiście nie jest rozwiązaniem produkcyjnym, ale „tylko moim urządzeniem deweloperskim”, prawda?). W ten sposób możesz nadal korzystać z sieciowej wersji serwera, potrzebujesz tylko root / sudo, aby uruchomić proxy (podczas rozruchu), nie polegałbyś na złożonych / potencjalnie kruchych możliwościach.


1
Dobry pomysł. Z jakiegoś powodu nawet nie pomyślałem o użyciu inetd. Tyle że usługa ta jest oparta na UDP, więc jest nieco bardziej skomplikowana niż usługi TCP. Mam rozwiązanie działające teraz z setcap, ale muszę to przemyśleć.
Jason Creighton

W przypadku drugiej opcji możesz nawet uruchomić proxy za pomocą inetd ... (Ale IPTables prawdopodobnie obniża koszty ogólne ...)
Gert van den Berg

14

Moje „standardowe obejście” używa socat jako readresatora przestrzeni użytkownika:

socat tcp6-listen:80,fork tcp6:8080

Uważaj, że to się nie skaluje, rozwidlenie jest drogie, ale tak właśnie działa socat.


13

Linux obsługuje możliwości obsługi bardziej szczegółowych uprawnień niż tylko „ta aplikacja jest uruchamiana jako root”. Jedną z tych możliwości jest CAP_NET_BIND_SERVICEpowiązanie z uprzywilejowanym portem (<1024).

Niestety nie wiem, jak to wykorzystać, aby uruchomić aplikację jako użytkownik inny niż root, wciąż ją udostępniając CAP_NET_BIND_SERVICE(prawdopodobnie używa setcap, ale na pewno istnieje na to istniejące rozwiązanie).


Nie będzie działać dla skryptów ani pojedynczych programów JVM / mono.
Stefan Steiger,

13

Wiem, że to stare pytanie, ale teraz przy najnowszych (> = 4.3) jądrach jest na to dobra odpowiedź - możliwości otoczenia.

Szybką odpowiedzią jest pobranie najnowszej (jeszcze nie wydanej) wersji libcap z git i skompilowanie jej. Skopiuj progs/capshgdzieś wynikowy plik binarny ( /usr/local/binto dobry wybór). Następnie jako root uruchom swój program

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

W porządku jesteśmy

  • Oświadczając, że kiedy zmieniamy użytkowników, chcemy zachować nasze obecne zestawy możliwości
  • Przełączanie użytkownika i grupy na „nazwa użytkownika usługi”
  • Dodanie cap_net_bind_servicemożliwości do zbiorów odziedziczonych i otoczenia
  • Forking bash -c 'your-command'(ponieważ capshautomatycznie rozpoczyna bash z argumentami po --)

Pod maską dużo się dzieje.

Po pierwsze, działamy jako root, więc domyślnie otrzymujemy pełny zestaw możliwości. Obejmuje to możliwość przełączania UID i GID za pomocą setuidi setgidsyscalls. Jednak zwykle, gdy program to robi, traci swój zestaw możliwości - jest tak, że stary sposób upuszczania roota setuidnadal działa. --keep=1Flaga mówi capshwydania prctl(PR_SET_KEEPCAPS)syscall, który wyłącza możliwości spada przy zmianie użytkownika. Rzeczywista zmiana użytkowników przez capshdzieje z --userflagą, czyli setuida setgid.

Kolejnym problemem, który musimy rozwiązać, jest sposób ustawiania zdolności w sposób, który będzie kontynuowany po execnaszych dzieciach. System możliwości zawsze miał „odziedziczony” zestaw możliwości, który jest „zbiorem możliwości zachowanych w execve (2)” [ Możliwości (7) ]. Chociaż wydaje się, że to rozwiązuje nasz problem (wystarczy ustawić cap_net_bind_servicemożliwość dziedziczenia, prawda?), Tak naprawdę dotyczy to tylko procesów uprzywilejowanych - a nasz proces nie jest już uprzywilejowany, ponieważ już zmieniliśmy użytkownika (z --userflagą).

Nowy zestaw funkcji otoczenia rozwiązuje ten problem - jest to „zestaw funkcji zachowanych w execve (2) programu, który nie jest uprzywilejowany”. Poprzez umieszczenie cap_net_bind_servicew zbiorze otoczenia, gdy capshexec nasz program serwera, nasz program będzie dziedziczyć tej możliwości i być w stanie związać słuchaczy do niskich portach.

Jeśli chcesz dowiedzieć się więcej, strona podręcznika funkcji wyjaśnia to szczegółowo. Uruchomiony capshprzez straceto również bardzo pouczające!


--addambFlaga została wprowadzona libcap 2,26. Mój system miał 2.25 i musiałem budować ze źródła.
ngreen

12

TLDR: Aby uzyskać „odpowiedź” (tak jak ją widzę), zeskocz do części >> TLDR << w tej odpowiedzi.

OK, wymyśliłem (tym razem naprawdę) odpowiedź na to pytanie, a ta moja odpowiedź jest również sposobem przeprosin za promowanie kolejnej odpowiedzi (zarówno tutaj, jak i na Twitterze), która moim zdaniem była „najlepsza” ", ale po wypróbowaniu odkryłem, że się myliłem. Ucz się od moich błędnych dzieci: nie promuj czegoś, dopóki sam tego nie spróbujesz!

Ponownie przejrzałem wszystkie odpowiedzi tutaj. Próbowałem niektórych z nich (i postanowiłem nie wypróbowywać innych, ponieważ po prostu nie podobały mi się rozwiązania). Myślałem, że rozwiązaniem będzie użycie systemdjego ustawień Capabilities=i CapabilitiesBindingSet=ustawień. Po dłuższej walce z tym odkryłem, że to nie jest rozwiązanie, ponieważ:

Możliwości mają na celu ograniczenie procesów rootowania!

Jak mądrze stwierdził PO, zawsze najlepiej tego unikać (dla wszystkich demonów, jeśli to możliwe!).

Nie można korzystać z funkcji związanych z opcji User=oraz Group=w systemdplikach jednostkowych, bo możliwości są zawsze zresetować kiedy execev(lub cokolwiek funkcja jest) jest tzw. Innymi słowy, kiedy systemdrozwidlenia i upuszczenie perms, możliwości są resetowane. Nie da się tego obejść, a cała logika wiązania w jądrze jest podstawowa wokół uid = 0, a nie możliwości. Oznacza to, że jest mało prawdopodobne, aby Zdolności kiedykolwiek były właściwą odpowiedzią na to pytanie (przynajmniej w najbliższym czasie). Nawiasem mówiąc, setcapjak wspomnieli inni, nie jest rozwiązaniem. Nie działało to dla mnie, nie działa ładnie ze skryptami, które i tak są resetowane przy każdej zmianie pliku.

W mojej skromnej obronie stwierdziłem (w komentarzu, który teraz usunąłem), że sugestia Jamesa iptables (o której wspomina również PO), była „drugim najlepszym rozwiązaniem”. :-P

>> TLDR <<

Rozwiązaniem jest połączenie systemdz iptableskomendami „w locie” , takimi jak to ( wzięte z 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

Tutaj realizujemy następujące czynności:

  • Demon nasłuchuje na 5333, ale połączenia są pomyślnie akceptowane na 53 dzięki iptables
  • Możemy zawrzeć polecenia w samym pliku jednostkowym, dzięki czemu oszczędzamy ludziom bólu głowy. systemdczyści dla nas reguły zapory, usuwając je, gdy demon nie działa.
  • Nigdy nie działamy jako root i uniemożliwiamy eskalację uprawnień (przynajmniej systemdtwierdzi), podobno nawet jeśli demon zostanie przejęty i ustawiony uid=0.

iptablesjest nadal niestety dość brzydkim i trudnym w użyciu narzędziem. Jeśli demon nasłuchuje eth0:0zamiast eth0, na przykład, polecenia są nieco inne .


2
Nikt nie powinien już używać aliasów w starym stylu, eth0:0chyba że mają naprawdę starą dystrybucję Linuksa. Są one przestarzałe od lat i ostatecznie odejdą.
Michael Hampton,

1
Myślę, że masz na myśli OpenVZ. (SolusVM jest panelem sterowania.) I tak, OpenVZ robi wiele rzeczy źle, sieciowanie jest tylko jedną z nich.
Michael Hampton,

1
Nie, mam na myśli SolusVM. Z / etc / network / interfaces:# Generated by SolusVM
Greg Slepak

1
Wciąż nie OpenVZ ... Przynajmniej nie używam go, podobnie jak mój VPS.
Greg Slepak

7
Dziękujemy za wskazanie funkcji możliwości systemd. Jednak nie ma potrzeby tworzenia skomplikowanych plików iptables, gdy systemd uruchamia plik binarny bezpośrednio (nie skrypt). Ustawienie AmbientCapabilities=CAP_NET_BIND_SERVICEdziałało dla mnie idealnie w połączeniu z User=.
Ruud

11

systemd to zamiennik sysvinit, który ma opcję uruchomienia demona o określonych możliwościach. Opcje Możliwości =, CapabilityBoundingSet = na stronie systemd.exec (5) .


2
Wcześniej zalecałem tę odpowiedź, ale po wypróbowaniu jej teraz nie. Zobacz moją odpowiedź na alternatywę, która nadal jest używana systemd.
Greg Slepak

10

Przekierowanie portu było dla nas najbardziej sensowne, ale natrafiliśmy na problem polegający na tym, że nasza aplikacja rozwiązała adres URL lokalnie, który również musiał zostać przekierowany; (to znaczy, że jesteś shindig ).

Umożliwi to również przekierowanie podczas uzyskiwania dostępu do adresu URL na komputerze lokalnym.

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


8

Z systemd wystarczy nieznacznie zmodyfikować usługę, aby zaakceptować wstępnie aktywowane gniazda.

Możesz później użyć systemd aktywacji gniazda .

Nie są potrzebne żadne zdolności, iptables ani inne sztuczki.

To jest zawartość odpowiednich plików systemowych z tego przykładu prostego serwera http python

Plik httpd-true.service

[Unit]
Description=Httpd true 

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

PrivateTmp=yes

Plik httpd-true.socket

[Unit]
Description=HTTPD true

[Socket]
ListenStream=80

[Install]
WantedBy=default.target

7

Podczas uruchamiania:

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

Następnie możesz połączyć się z portem, do którego przekierowujesz.


nie --to-portistnieje? man iptableswymienia tylko --to-ports(liczba mnoga).
Abdull,

zauważyłem pewne różnice i skakałem
James

Zobacz tę odpowiedź, aby dowiedzieć się, jak to połączyć systemd.
Greg Slepak


3

Istnieje również „droga djb”. Możesz użyć tej metody, aby uruchomić proces jako root działający na dowolnym porcie w tcpserver, a następnie przekaże kontrolę nad procesem użytkownikowi określonemu natychmiast po uruchomieniu procesu.

#!/bin/sh

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

Aby uzyskać więcej informacji, zobacz: http://thedjbway.b0llix.net/daemontools/uidgid.html


1

Ponieważ PO to tylko programowanie / testowanie, pomocne mogą być mniej eleganckie rozwiązania:

setcap może być użyty w interpretera skryptu, aby nadać możliwości skryptom. Jeśli setcaps w globalnym pliku binarnym interpretera nie jest akceptowalny, zrób lokalną kopię pliku binarnego (każdy użytkownik może) i uzyskaj root do setcap na tej kopii. Python2 (przynajmniej) działa poprawnie z lokalną kopią interpretera w drzewie rozwoju skryptów. Suid nie jest potrzebny, aby użytkownik root mógł kontrolować, jakie możliwości mają użytkownicy.

Jeśli chcesz śledzić ogólnosystemowe aktualizacje interpretera, użyj skryptu powłoki takiego jak poniżej, aby uruchomić skrypt:

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

Wypróbowałem metodę iptables PREROUTING REDIRECT. W starszych jądrach wydaje się, że ten typ reguły nie był obsługiwany dla IPv6 . Ale najwyraźniej jest teraz obsługiwany w ip6tables v1.4.18 i jądrze Linux v3.8.

Odkryłem również, że PREROUTING REDIRECT nie działa dla połączeń inicjowanych w maszynie. Aby pracować dla połączeń z komputera lokalnego, dodaj również regułę WYJŚCIE - patrz przekierowanie portu iptables nie działa dla localhost . Np. Coś takiego:

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

Odkryłem również, że PREROUTING REDIRECT wpływa również na przesyłane pakiety . Oznacza to, że jeśli urządzenie przesyła również pakiety między interfejsami (np. Działa jako punkt dostępu Wi-Fi podłączony do sieci Ethernet), wówczas reguła iptables przechwytuje połączenia połączonych klientów do miejsc docelowych w Internecie i przekierowuje je do maszyna. Nie tego chciałem - chciałem tylko przekierowywać połączenia skierowane do samej maszyny. Stwierdziłem, że mogę sprawić, że będzie to wpływać tylko na pakiety adresowane do skrzynki, poprzez dodanie -m addrtype --dst-type LOCAL. Np. Coś takiego:

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

Inną możliwością jest użycie przekierowania portów TCP. Np. Używając socat:

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

Jednak jedną wadą tej metody jest to, że aplikacja nasłuchująca na porcie 8080 nie zna adresu źródłowego połączeń przychodzących (np. Do logowania lub innych celów identyfikacyjnych).


0

Odpowiedź na 2015 / wrzesień:

ip6tables obsługuje teraz NAT IPV6: http://www.netfilter.org/projects/iptables/files/changes-iptables-1.4.17.txt

Będziesz potrzebował jądra 3.7+

Dowód:

[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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.