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

Niet-root processen toestaan om te binden op poort 80 en 443?

Is het mogelijk om een kernelparameter zo af te stellen dat een programma in het gebruikersland zich kan binden aan poort 80 en 443?

De reden dat ik dit vraag is dat ik denk dat het dom is om een gepriviligeerd proces toe te staan een socket te openen en te luisteren. Alles wat een socket opent en luistert is risicovol, en risicovolle applicaties horen niet als root te draaien.

Ik probeer er liever achter te komen welk proces zonder privileges op poort 80 luistert, dan malware te verwijderen die zich met root privileges ingegraven heeft.

Antwoorden (5)

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

Ik weet niet zeker waar de andere antwoorden en commentaren hier naar verwijzen. Dit is vrij eenvoudig mogelijk. Er zijn twee opties, die beide toegang tot laag genummerde poorten toestaan zonder het proces tot root te hoeven verheffen:

Optie 1: Gebruik CAP_NET_BIND_SERVICE om een proces toegang te verlenen tot laag genummerde poorten:

Hiermee kunt u een specifieke binary permanente toegang verlenen om te binden aan laag genummerde poorten via het setcap commando:

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

Voor meer details over het e/i/p gedeelte, zie cap_from_text .

Na dit gedaan te hebben, zal /path/to/binary in staat zijn om te binden aan laag genummerde poorten. Merk op dat je setcap op de binary zelf moet gebruiken in plaats van een symlink.

Optie 2: Gebruik authbind om eenmalige toegang te verlenen, met fijnere gebruiker/groep/poort controle:

De authbind man page ) tool bestaat precies hiervoor.

  1. Installeer authbind met uw favoriete package manager.

  2. Configureer het om toegang te verlenen tot de relevante poorten, bijvoorbeeld om 80 en 443 van alle gebruikers en groepen toe te staan:

  3. Voer nu uw commando uit via authbind (optioneel met vermelding van --deep of andere argumenten, zie de man page):

  • *

Er zijn voor- en nadelen aan beide bovenstaande opties. Optie 1 geeft vertrouwen aan de binary maar geeft geen controle over de toegang per poort. Optie 2 geeft vertrouwen aan de user/group en geeft controle over toegang per poort, maar oudere versies ondersteunden alleen IPv4 (sinds ik dit schreef, zijn er nieuwere versies met IPv6 ondersteuning uitgekomen).

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

Dale Hagglund heeft gelijk. Dus ik ga gewoon hetzelfde zeggen, maar op een andere manier, met wat details en voorbeelden. ☺

Het juiste om te doen in de Unix en Linux werelden is:

  • een klein, eenvoudig, gemakkelijk controleerbaar, programma hebben dat als superuser draait en de luisterende socket bindt;
  • een ander klein, eenvoudig, gemakkelijk controleerbaar, programma hebben dat privileges laat vallen, gespawned door het eerste programma;
  • het vlees van de service, in een apart derde programma hebben, dat draait onder een niet-superuser account en ketting geladen wordt door het tweede programma, in de verwachting dat het eenvoudig een open file descriptor voor de socket erft.

Je hebt het verkeerde idee van waar het grote risico zit. Het hoge risico zit in het lezen van het netwerk en het handelen naar wat er gelezen wordt, niet in het openen van een socket, het binden aan een poort, en het aanroepen van listen(). Het is het deel van een dienst dat de eigenlijke communicatie doet dat het hoge risico vormt. De delen die openen, bind(), en listen(), en zelfs (tot op zekere hoogte) het deel dat accepts() aanroept, zijn niet het hoge risico en kunnen worden uitgevoerd onder de bescherming van de superuser. Zij gebruiken en handelen niet op (met uitzondering van bron-IP-adressen in het geval van accept()) gegevens die onder controle staan van onvertrouwde vreemden over het netwerk.

Er zijn vele manieren om dit te doen.

inetd

Zoals Dale Hagglund zegt, de oude “netwerk superserver” inetd doet dit. De account waaronder het serviceproces wordt uitgevoerd is een van de kolommen in inetd.conf. Het scheidt het luistergedeelte en het droppen van privileges niet in twee aparte programma’s, klein en gemakkelijk controleerbaar, maar het scheidt wel de hoofd-service code af in een apart programma, exec()geïnstalleerd in een service proces dat het spawnt met een open file descriptor voor de socket.

De moeilijkheid om te auditen is niet zo'n probleem, omdat je maar één programma hoeft te auditen. Het grote probleem van inetd is niet zozeer auditing, maar veeleer dat het geen eenvoudige fijnkorrelige runtime service controle biedt, vergeleken met meer recente gereedschappen.

UCSPI-TCP en daemontools

Daniel J. Bernstein’s UCSPI-TCP en daemontools pakketten zijn ontworpen om dit in samenhang te doen. Als alternatief kan men Bruce Guenter’s grotendeels gelijkwaardige daemontools-encore gereedschapsset gebruiken.

Het programma om de socket file descriptor te openen en te binden aan de geprivilegieerde lokale poort is tcpserver , van UCSPI-TCP. Het doet zowel de listen() als de accept().

tcpserver spawnt dan ofwel een service programma dat zelf root privileges laat vallen (omdat het protocol dat geserveerd wordt inhoudt dat gestart wordt als de superuser en dan “inlogt”, zoals het geval is met, bijvoorbeeld, een FTP of een SSH daemon) of setuidgid dat een op zichzelf staand klein en gemakkelijk controleerbaar programma is dat alleen privileges laat vallen en dan ketting-laadt naar het eigenlijke serviceprogramma (geen enkel deel daarvan draait dus ooit met superuser-privileges, zoals het geval is met, bijvoorbeeld, qmail-smtpd ).

Een service run script zou dus bijvoorbeeld zijn (deze voor dummyidentd voor het leveren van de null IDENT service):

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

nosh

Mijn nosh pakket is ontworpen om dit te doen. Het heeft een klein setuidgid hulpprogramma, net als de anderen. Een klein verschil is dat het bruikbaar is met zowel systemd-style “LISTEN_FDS” diensten als met UCSPI-TCP diensten, dus het traditionele tcpserver programma is vervangen door twee aparte programma’s: tcp-socket-listen en tcp-socket-accept.

Ook hier spawnen en chainloaden utilities voor één doel elkaar. Een interessante eigenaardigheid van het ontwerp is dat men superuser-privileges kan laten vallen na listen() maar zelfs vóór accept(). Hier is een run script voor qmail-smtpd dat inderdaad precies dat doet:

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

De programma’s die onder auspiciën van de superuser draaien zijn de kleine service-agnostische chain-loading tools fdmove, clearenv, envdir, softlimit, en tcp-socket-listen. Op het moment dat setuidgid wordt gestart, is de socket open en gebonden aan de sh poort, en heeft het proces niet langer superuser rechten.

s6, s6-networking, en execline

Laurent Bercot’s s6 en s6-networking pakketten zijn ontworpen om dit in samenhang te doen. De commando’s lijken structureel erg op die van smtp en UCSPI-TCP.

daemontools scripts zouden vrijwel hetzelfde zijn, met uitzondering van de vervanging van run voor s6-tcpserver en tcpserver voor s6-setuidgid. Men zou er echter ook voor kunnen kiezen om gelijktijdig gebruik te maken van M. Bercot’s execline gereedschapsset.

Hier is een voorbeeld van een FTP service, lichtelijk aangepast van Wayne Marshall’s origineel , dat execline, s6, s6-networking, en het FTP server programma van publicfile gebruikt:

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

Gerrit Pape’s ipsvd is een andere toolset die op dezelfde manier werkt als ucspi-tcp en s6-networking. De tools zijn deze keer setuidgid en chpst, maar ze doen hetzelfde, en de hoog-risico code die het lezen, verwerken, en schrijven doet van dingen die over het netwerk worden gestuurd door onvertrouwde cliënten is nog steeds in een apart programma.

Hier is M. Pape’s voorbeeld van het draaien van tcpsvd in een fnord script:

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

run

systemd , het nieuwe service supervisie en init systeem dat in sommige Linux distributies gevonden kan worden, is bedoeld om te doen wat systemd kan doen . Het gebruikt echter geen suite van kleine op zichzelf staande programma’s. Men moet inetd in zijn geheel doorlichten, helaas.

Met systemd maakt men configuratie bestanden om een socket te definieren waar systemd op luistert, en een service die systemd start. Het service “unit” bestand heeft instellingen waarmee men veel controle heeft over het service proces, inclusief als welke gebruiker het draait.

Met die gebruiker ingesteld als niet-superuser, doet systemd al het werk van het openen van de socket, het binden aan een poort, en het aanroepen van systemd (en, indien nodig, listen()) in proces #1 als de superuser, en het serviceproces dat het spawnt draait zonder superuser-privileges.

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

Ik heb een nogal andere aanpak. Ik wilde poort 80 gebruiken voor een node.js server. Ik kon het niet doen omdat Node.js was geïnstalleerd voor een niet-sudo-gebruiker. Ik heb geprobeerd om symlinks te gebruiken, maar dat werkte niet voor mij.

Toen kwam ik erachter dat ik verbindingen kan doorsturen van de ene poort naar de andere poort. Dus startte ik de server op poort 3000 en zette een port forward op van poort 80 naar poort 3000. Deze link geeft de feitelijke commando’s die gebruikt kunnen worden om dit te doen. Hier zijn de commando’s -

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

Ik heb het tweede commando gebruikt en het werkte voor mij. Dus ik denk dat dit een middenweg is om gebruikers-processen niet direct toegang te geven tot de lagere poorten, maar ze toegang te geven met behulp van port-forwarding.

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

Je instinct is helemaal juist: het is een slecht idee om een groot complex programma als root te laten draaien, omdat hun complexiteit ze moeilijk te vertrouwen maakt.

Maar het is ook een slecht idee om gewone gebruikers toe te staan zich aan bevoorrechte poorten te binden, omdat zulke poorten meestal belangrijke systeemdiensten vertegenwoordigen.

De standaard aanpak om deze schijnbare tegenstrijdigheid op te lossen is “privilege separation”. Het basisidee is om je programma te scheiden in twee (of meer) delen, die elk een goed gedefinieerd deel van de totale applicatie doen, en die communiceren via eenvoudige beperkte interfaces.

In het voorbeeld dat je geeft, wil je je programma in twee stukken scheiden. Eén dat als root draait en de geprivilegieerde socket opent en daaraan bindt, en het dan op de een of andere manier doorgeeft aan het andere deel, dat als gewone gebruiker draait.

Deze twee hoofdmanieren om deze scheiding te bereiken.

  1. Een enkel programma dat als root start. Het allereerste wat het doet is de noodzakelijke socket aanmaken, op een zo eenvoudig en beperkt mogelijke manier. Daarna laat het privileges vallen, dat wil zeggen, het verandert zichzelf in een gewoon gebruikersmodus proces, en doet al het andere werk. Het correct droppen van privileges is lastig, dus neem de tijd om te bestuderen hoe je dit op de juiste manier doet.

  2. Een paar programma’s die communiceren over een socket paar dat is aangemaakt door een ouder proces. Een niet-geprivilegieerd stuurprogramma ontvangt initiële argumenten en doet misschien wat basis argumentvalidatie. Het creëert een paar verbonden sockets via socketpair(), en forkt en voert dan twee andere programma’s uit die het echte werk zullen doen, en zullen communiceren via het socket-paar. Een van deze is gepriviligeerd en zal de server socket aanmaken, en alle andere gepriviligeerde operaties, en de andere zal de meer complexe en daarom minder betrouwbare applicatie-uitvoering doen.

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

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

Eenvoudigste oplossing : verwijder alle gepriviligeerde poorten op linux

Werkt op ubuntu/debian :

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

(werkt goed voor VirtualBox met een niet-root account)

Wees nu voorzichtig met beveiliging omdat alle gebruikers alle poorten kunnen binden !