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" inetd
fa 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. inetd
Il 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()
.
tcpserver
quindi 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 setuidgid
che è 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 run
script 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 setuidgid
utilità, proprio come le altre. Una leggera differenza è che è utilizzabile con i systemd
servizi "LISTEN_FDS" e con i servizi UCSPI-TCP, quindi il tcpserver
programma tradizionale è sostituito da due programmi separati: tcp-socket-listen
e 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 run
sceneggiatura qmail-smtpd
che 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 smtp
porta 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 daemontools
e UCSPI-TCP.
run
gli script sarebbero più o meno gli stessi, fatta eccezione per la sostituzione di s6-tcpserver
for tcpserver
e s6-setuidgid
for 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 chpst
e tcpsvd
questa 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 fnord
una run
sceneggiatura:
#!/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 inetd
può fare . Tuttavia, non utilizza una suite di piccoli programmi autonomi. systemd
Sfortunatamente si deve fare il controllo nella sua interezza.
Con systemd
uno si creano i file di configurazione per definire un socket in systemd
ascolto e un servizio che si systemd
avvia. 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, systemd
fa 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.