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.