Consentire il processo non root da associare alle porte 80 e 443?


104

È possibile mettere a punto un parametro del kernel per consentire a un programma utente di collegarsi alle porte 80 e 443?

Il motivo per cui lo chiedo è che penso che sia sciocco consentire a un processo privilegiato di aprire un socket e ascoltare. Tutto ciò che apre un socket e ascolta è ad alto rischio e le applicazioni ad alto rischio non dovrebbero essere in esecuzione come root.

Preferirei di gran lunga cercare di capire quale processo senza privilegi sta ascoltando sulla porta 80 piuttosto che cercare di rimuovere il malware che si è nascosto con i privilegi di root.



10
La risposta lunga è sì .. quindi anche la risposta breve dovrebbe essere sì.
BT,

4
La risposta breve è sì.
Jason C

Risposte:


163

Non sono sicuro di cosa si riferiscano le altre risposte e commenti qui. Questo è possibile piuttosto facilmente. Esistono due opzioni, entrambe che consentono l'accesso a porte con numero basso senza dover elevare il processo alla radice:

Opzione 1: utilizzare CAP_NET_BIND_SERVICEper concedere l'accesso alla porta con numero basso a un processo:

Con questo è possibile concedere l'accesso permanente a un binario specifico da associare a porte con numero basso tramite il setcapcomando:

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

Per maggiori dettagli sulla parte e / i / p, vedere cap_from_text.

Dopo averlo fatto, /path/to/binarysarà in grado di collegarsi a porte con numero basso. Si noti che è necessario utilizzare setcapsul binario stesso piuttosto che un collegamento simbolico.

Opzione 2: utilizzare authbindper concedere l'accesso singolo, con un controllo utente / gruppo / porta più preciso:

Lo strumento authbind( pagina man ) esiste proprio per questo.

  1. Installa authbindusando il tuo gestore pacchetti preferito.

  2. Configurarlo per consentire l'accesso alle porte pertinenti, ad esempio per consentire 80 e 443 a tutti gli utenti e gruppi:

    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. Ora esegui il tuo comando tramite authbind(specificando facoltativamente --deepo altri argomenti, vedi la pagina man):

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

    Per esempio

    authbind --deep java -jar SomeServer.jar
    

Ci sono aspetti positivi e negativi di entrambi i precedenti. L'opzione 1 garantisce l'attendibilità al file binario ma non fornisce alcun controllo sull'accesso per porta. L'opzione 2 garantisce fiducia all'utente / al gruppo e fornisce il controllo sull'accesso per porta ma, AFAIK, supporta solo IPv4.


Ha davvero bisogno di un rwxpermesso?
opaco

Per ripristinare l'operazione nell'Opzione 1, eseguiresti di nuovo il comando usando -pintead of +eip?
Eugene1832,

5
Fare attenzione che, con setcap, se si sovrascrive l'eseguibile a cui si concedono i privilegi (es: fare una ricostruzione), allora perde il suo stato di porta privilegiato e si devono dare nuovamente i privilegi: |
rogerdpack,

1
Qualcosa con cui ho dovuto giocherellare; Stavo cercando di eseguire un servizio sysv, che esegue un eseguibile ruby ​​che utilizza ruby. Devi dare il setcappermesso all'eseguibile ruby ​​specifico per la versione , ad es./usr/bin/ruby1.9.1
Christian Rondeau,

3
Ho i miei dubbi sul fatto che chmoding a 777 byportfile sia la migliore idea. Ho visto dare permessi che vanno da 500a 744. Sarei bloccato a quello più restrittivo che funziona per te.
Pere,

28

Dale Hagglund è perfetto. Quindi dirò la stessa cosa ma in modo diverso, con alcuni dettagli ed esempi. ☺

La cosa giusta da fare nei mondi Unix e Linux è:

  • avere un programma piccolo, semplice, facilmente controllabile che gira come superutente e lega la presa di ascolto;
  • avere un altro programma piccolo, semplice, facilmente controllabile, che abbandoni i privilegi, generato dal primo programma;
  • avere la carne del servizio, in un terzo programma separato , eseguito con un account non superutente e una catena caricata dal secondo programma, aspettandosi di ereditare semplicemente un descrittore di file aperto per il socket.

Hai un'idea sbagliata di dove sia l'alto rischio. L'alto rischio è nel leggere dalla rete e agire su ciò che viene letto non nei semplici atti di apertura di un socket, associazione a una porta e chiamata listen(). È la parte di un servizio che fa la comunicazione effettiva ad alto rischio. Le parti che si aprono, bind()e listen(), e persino (in una certa misura) la parte accepts(), non sono ad alto rischio e possono essere eseguite sotto l'egida del superutente. Non usano e non agiscono su (ad eccezione degli indirizzi IP di origine nel accept()caso) dati che sono sotto il controllo di estranei non fidati sulla rete.

Ci sono molti modi per farlo.

inetd

Come dice Dale Hagglund, il vecchio "superserver di rete" inetdfa questo. L'account con cui viene eseguito il processo di servizio è una delle colonne in inetd.conf. Non separa la parte di ascolto e quella dei privilegi di rilascio in due programmi separati, piccoli e facilmente controllabili, ma separa il codice di servizio principale in un programma separato, exec()edito in un processo di servizio che genera con un descrittore di file aperto per la presa.

La difficoltà di controllo non è un grosso problema, in quanto si deve solo controllare il programma. inetdIl problema principale non è il controllo tanto ma è piuttosto che non fornisce un semplice controllo del servizio di runtime a grana fine, rispetto agli strumenti più recenti.

UCSPI-TCP e daemontools

I pacchetti UCSPI-TCP e daemontools di Daniel J. Bernstein sono stati progettati per fare questo insieme. In alternativa, è possibile utilizzare un set di strumenti per il daemontools-encore ampiamente equivalente di Bruce Guenter .

Il programma per aprire il descrittore di file socket e collegarsi alla porta locale privilegiata è tcpserver, da UCSPI-TCP. Fa sia il listen()che il accept().

tcpserverquindi genera un programma di servizio che elimina i privilegi di root (poiché il protocollo che viene offerto comporta l'avvio come superutente e quindi "accesso", come nel caso, ad esempio, di un demone FTP o SSH) o setuidgidche è un programma autonomo di piccole dimensioni e facilmente controllabile che elimina solo i privilegi e quindi carica la catena nel programma di servizio corretto (nessuna parte del quale funziona quindi con privilegi di superutente, come nel caso, diciamo, qmail-smtpd).

Uno runscript di servizio sarebbe quindi ad esempio (questo per dummyidentd per fornire un servizio IDENT null):

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

fare uno spuntino

Il mio pacchetto nosh è progettato per fare questo. Ha una piccola setuidgidutilità, proprio come le altre. Una leggera differenza è che è utilizzabile con i systemdservizi "LISTEN_FDS" e con i servizi UCSPI-TCP, quindi il tcpserverprogramma tradizionale è sostituito da due programmi separati: tcp-socket-listene tcp-socket-accept.

Ancora una volta, le utility monouso si generano e si caricano a vicenda. Una stranezza interessante del design è che si possono abbandonare i privilegi di superutente dopo listen()ma anche prima accept(). Ecco una runsceneggiatura qmail-smtpdche fa esattamente questo:

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

I programmi che vengono eseguiti sotto l'egida del superutente sono i piccoli attrezzi catena di carico di servizio-agnostic fdmove, clearenv, envdir, softlimit, tcp-socket-listen, e setuidgid. A partire dal punto in cui shè stato avviato, il socket è aperto e associato alla smtpporta e il processo non ha più i privilegi di superutente.

s6, s6-networking ed execline

I pacchetti di networking s6 e s6 di Laurent Bercot sono stati progettati per fare questo insieme. I comandi sono strutturalmente molto simili a quelli di daemontoolse UCSPI-TCP.

rungli script sarebbero più o meno gli stessi, fatta eccezione per la sostituzione di s6-tcpserverfor tcpservere s6-setuidgidfor setuidgid. Tuttavia, si potrebbe anche scegliere di utilizzare contemporaneamente il set di strumenti di execline di M. Bercot .

Ecco un esempio di un servizio FTP, leggermente modificato dall'originale di Wayne Marshall , che utilizza execline, s6, s6-networking e il programma server FTP da 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

Ipsvd di Gerrit Pape è un altro set di strumenti che gira sulle stesse linee di ucspi-tcp e s6-networking. Gli strumenti lo sono chpste tcpsvdquesta volta, ma fanno la stessa cosa, e il codice ad alto rischio che esegue la lettura, l'elaborazione e la scrittura di cose inviate in rete da client non attendibili è ancora in un programma separato.

Ecco l' esempio di M. Pape di eseguire fnorduna runsceneggiatura:

#!/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, il nuovo sistema di supervisione e init del servizio che può essere trovato in alcune distribuzioni Linux, è destinato a fare ciò che inetdpuò fare . Tuttavia, non utilizza una suite di piccoli programmi autonomi. systemdSfortunatamente si deve fare il controllo nella sua interezza.

Con systemduno si creano i file di configurazione per definire un socket in systemdascolto e un servizio che si systemdavvia. Il file "unità" di servizio ha impostazioni che consentono di avere un grande controllo sul processo di servizio, incluso l'utente come viene eseguito.

Con quell'utente impostato per non essere un superutente, systemdfa tutto il lavoro di apertura del socket, associazione a una porta e chiamata listen()(e, se necessario, accept()) nel processo n. 1 come superutente e il processo di processo che esso spawns funziona senza privilegi di superutente.


2
Grazie per il complimento. Questa è una grande raccolta di consigli concreti. +1.
Dale Hagglund,

11

Ho un approccio piuttosto diverso. Volevo usare la porta 80 per un server node.js. Non sono stato in grado di farlo poiché Node.js è stato installato per un utente non sudo. Ho provato a utilizzare i collegamenti simbolici, ma non ha funzionato per me.

Poi ho saputo che posso inoltrare le connessioni da una porta a un'altra porta. Quindi ho avviato il server sulla porta 3000 e impostato una porta in avanti dalla porta 80 alla porta 3000.

Questo collegamento fornisce i comandi effettivi che è possibile utilizzare per fare ciò. Ecco i comandi -

localhost / loopback

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

esterno

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

Ho usato il secondo comando e ha funzionato per me. Quindi penso che questa sia una via di mezzo per non consentire all'utente-processo di accedere direttamente alle porte inferiori, ma dare loro l'accesso usando il port forwarding.


6
+1 per pensare fuori dagli schemi
Richard Wiseman il

4

I tuoi istinti sono del tutto corretti: è una cattiva idea far girare un grosso programma complesso come root, perché la loro complessità li rende difficili da fidare.

Ma è anche una cattiva idea consentire agli utenti regolari di collegarsi a porte privilegiate, poiché tali porte di solito rappresentano importanti servizi di sistema.

L'approccio standard per risolvere questa apparente contraddizione è la separazione dei privilegi . L'idea di base è quella di separare il programma in due (o più) parti, ognuna delle quali fa una parte ben definita dell'applicazione complessiva e che comunicano tramite semplici interfacce limitate.

Nell'esempio fornito, si desidera separare il programma in due parti. Uno che funziona come root e si apre e si lega al socket privilegiato, quindi lo passa in qualche modo all'altra parte, che viene eseguita come utente normale.

Questi due modi principali per ottenere questa separazione.

  1. Un singolo programma che inizia come root. La prima cosa che fa è creare il socket necessario, nel modo più semplice e limitato possibile. Quindi, elimina i privilegi, ovvero si converte in un normale processo in modalità utente e fa tutto il resto. Abbandonare correttamente i privilegi è complicato, quindi ti preghiamo di dedicare del tempo a studiare il modo giusto per farlo.

  2. Una coppia di programmi che comunicano su una coppia socket creata da un processo padre. Un programma driver senza privilegi riceve argomenti iniziali e forse esegue alcune convalide degli argomenti di base. Crea una coppia di socket collegati tramite socketpair (), quindi esegue il fork e esegue altri due programmi che eseguiranno il vero lavoro e comunicheranno tramite la coppia di socket. Uno di questi è privilegiato e creerà il socket del server e qualsiasi altra operazione privilegiata, mentre l'altro eseguirà l'esecuzione dell'applicazione più complessa e quindi meno affidabile.

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


Ciò che la tua proposta non è considerata la migliore pratica. Potresti guardare inetd, che può ascoltare su un socket privilegiato e poi passare quel socket a un programma senza privilegi.
Dale Hagglund,

3

Soluzione più semplice: rimuovere tutte le porte privilegiate su Linux

Funziona su Ubuntu / Debian:

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

(funziona bene per VirtualBox con account non root)

Ora, fai attenzione alla sicurezza perché tutti gli utenti possono associare tutte le porte!


È intelligente. Un piccolo nit: la configurazione apre 80 e 443, ma apre anche tutte le altre porte. Potrebbero non essere necessarie autorizzazioni rilassanti sulle altre porte.
JWW
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.