Questa è la risposta più completa che troverai da condividere, ma prima guardala attentamente e vedi se può essere adattata al tuo progetto.
Soluzione:
Non sono sicuro di cosa si riferiscano le altre risposte e i commenti qui presenti. Questo è possibile piuttosto facilmente. Esistono due opzioni, che consentono entrambe di accedere alle porte a bassa numerazione senza dover elevare il processo a root:
Opzione 1: utilizzare CAP_NET_BIND_SERVICE
per concedere l'accesso alle porte a basso numero a un processo:
In questo modo è possibile concedere l'accesso permanente a un binario specifico per effettuare il bind alle porte a basso numero tramite il parametro setcap
tramite il comando setcap
:
sudo setcap CAP_NET_BIND_SERVICE=+eip /path/to/binary
Per maggiori dettagli sulla parte e/i/p, vedere cap_from_text
.
Dopo aver fatto questo, /path/to/binary
sarà in grado di effettuare il binding alle porte a basso numero. Si noti che è necessario utilizzare setcap
sul binario stesso piuttosto che su un link simbolico.
Opzione 2: utilizzare authbind
per concedere l'accesso una tantum, con un controllo più preciso su utenti/gruppi/porta:
Il authbind
(pagina man) esiste proprio per questo.
-
Installare
authbind
usando il vostro gestore di pacchetti preferito. -
Configuratelo per garantire 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
-
Ora eseguite il comando tramite
authbind
(specificando facoltativamente--deep
o altri argomenti, vedere la pagina man):authbind --deep /path/to/binary command line args
Ad esempio
authbind --deep java -jar SomeServer.jar
Entrambe le opzioni hanno lati positivi e negativi. L'opzione 1 concede fiducia al file binario ma non fornisce alcun controllo sull'accesso per porta. L'opzione 2 concede la fiducia al binario utente/gruppo e fornisce il controllo sull'accesso per porta, ma le versioni precedenti supportavano solo IPv4 (da quando ho scritto questo articolo sono state rilasciate nuove versioni con supporto IPv6).
Dale Hagglund ha ragione. Quindi dirò la stessa cosa ma in modo diverso, con alcune specifiche ed esempi ☺.
La cosa giusta da fare nel mondo Unix e Linux è:
- avere un programma piccolo, semplice, facilmente verificabile, che viene eseguito come superutente e che vincola il socket di ascolto;
- avere un altro piccolo programma, semplice e facilmente verificabile, che elimina i privilegi, generato dal primo programma;
- avere la parte principale del servizio, in un programma separato terzo eseguito sotto un account non superutente e caricato a catena dal secondo programma, aspettandosi di ereditare semplicemente un descrittore di file aperto per il socket.
Avete un'idea sbagliata di dove sia l'alto rischio. Il rischio elevato è in nel leggere dalla rete e nell'agire su ciò che viene letto non nei semplici atti di apertura di un socket, di collegamento a una porta e di chiamata a listen()
. È la parte di un servizio che esegue la comunicazione vera e propria a essere ad alto rischio. Le parti che aprono, bind()
e listen()
e persino (in una certa misura) la parte che accepts()
non sono ad alto rischio e possono essere eseguiti sotto l'egida del superutente. Non utilizzano e non agiscono su (con l'eccezione degli indirizzi IP di origine nella sezione accept()
caso) dati che sono sotto il controllo di estranei non fidati attraverso la rete.
Ci sono molti modi per farlo.
inetd
Come afferma Dale Hagglund, il vecchio "superserver di rete" inetd
fa questo. L'account sotto il quale viene eseguito il processo di servizio è una delle colonne di inetd.conf
. Non separa la parte di ascolto e la parte di eliminazione dei privilegi in due programmi separati, piccoli e facilmente verificabili, ma separa il codice di servizio principale in un programma separato, exec()
in un processo di servizio che viene generato con un descrittore di file aperto per il socket.
La difficoltà di auditing non è un problema, dato che si deve controllare solo un programma. inetd
Il problema principale di inetd
non è tanto l'auditing, quanto piuttosto il fatto che non fornisce un semplice controllo dei servizi di runtime a grana fine, rispetto a 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 si può usare il set di strumenti daemontools-encore di Bruce Guenter, in gran parte equivalente.
Il programma per aprire il descrittore di file del socket e fare il bind alla porta locale privilegiata è tcpserver
, da UCSPI-TCP. Esegue entrambe le operazioni di listen()
e il accept()
.
tcpserver
e poi genera un programma di servizio che si priva dei privilegi di root (perché il protocollo da servire prevede di iniziare come superutente e poi di "loggarsi", come avviene, ad esempio, con un demone FTP o SSH) oppure setuidgid
che è un programma autonomo, piccolo e facilmente verificabile, che si limita a togliere i privilegi e poi a caricare a catena il programma di servizio vero e proprio (nessuna parte del quale viene quindi eseguita con i privilegi di superutente, come nel caso, ad esempio, di qmail-smtpd
).
),qmail-smtpd
).
Un servizio run
potrebbe essere un esempio (questo per dummyidentd per fornire il servizio null IDENT):
#!/bin/sh -e
exec 2>&1
exec
tcpserver 0 113
setuidgid nobody
dummyidentd.pl
nosh
Il mio pacchetto nosh è progettato per fare questo. Ha un piccolo file setuidgid
proprio come gli altri. Una piccola differenza è che è utilizzabile con systemd
-come i servizi "LISTEN_FDS" e con i servizi UCSPI-TCP, per cui il tradizionale sistema di tcpserver
è sostituito da due programmi separati: tcp-socket-listen
e tcp-socket-accept
.
Anche in questo caso, le utilità a scopo singolo si riproducono e si caricano a catena l'una con l'altra. Un'interessante stranezza del progetto è che si possono abbandonare i privilegi di superutente dopo il passaggio a listen()
ma prima ancora di accept()
. Ecco un run
per 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 strumenti per il caricamento della catena dei servizi fdmove
, clearenv
, envdir
, softlimit
, tcp-socket-listen
e setuidgid
. Al punto che sh
viene avviato, il socket è aperto e legato alla cartella smtp
e il processo non ha più i privilegi di superutente.
s6, s6-networking ed execline
I pacchetti s6 e s6-networking di Laurent Bercot sono stati progettati per svolgere questo compito insieme. I comandi sono strutturalmente molto simili a quelli di daemontools
e UCSPI-TCP.
run
sarebbero più o meno gli stessi, tranne che per la sostituzione di s6-tcpserver
per tcpserver
e s6-setuidgid
per setuidgid
. Tuttavia, si potrebbe anche scegliere di utilizzare contemporaneamente il set di strumenti execline di M. Bercot.
Ecco un esempio di servizio FTP, leggermente modificato dall'originale di Wayne Marshall, che utilizza execline, s6, s6-networking e il programma server FTP di 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 insieme di strumenti che funziona sulla falsariga di ucspi-tcp e s6-networking. Gli strumenti sono chpst
e tcpsvd
questa volta, ma fanno la stessa cosa e il codice ad alto rischio che si occupa della lettura, dell'elaborazione e della scrittura delle cose inviate in rete da client non attendibili è ancora in un programma separato.
Ecco l'esempio di M. Pape di esecuzione di fnord
in un programma run
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
systemd
systemd
, il nuovo sistema di supervisione dei servizi e di init che si trova in alcune distribuzioni Linux, è destinato a fare ciò che inetd
può fare. Tuttavia, non utilizza una suite di piccoli programmi autonomi. È necessario controllare systemd
nella sua interezza, purtroppo.
Con systemd
si creano file di configurazione per definire un socket che systemd
ascolta e un servizio che systemd
avvia. Il file "unit" del servizio ha delle impostazioni che permettono di avere un grande controllo sul processo del servizio, compreso l'utente con cui viene eseguito.
Con l'utente impostato come non superutente, systemd
esegue tutto il lavoro di apertura del socket, il binding con una porta e la chiamata a listen()
(e, se necessario, accept()
) nel processo #1 come superutente, mentre il processo di servizio che genera viene eseguito senza privilegi di superutente.
Io ho un approccio piuttosto diverso. Volevo usare la porta 80 per un server node.js. Non ho potuto farlo perché Node.js è stato installato per un utente non-sudo. Ho provato a usare i link simbolici, ma non ha funzionato.
Poi ho capito che posso inoltrare le connessioni da una porta a un'altra. Così ho avviato il server sulla porta 3000 e ho impostato un port forward dalla porta 80 alla porta 3000.
Questo link fornisce i comandi effettivi che possono essere usati per fare questo. 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. Quindi penso che questa sia una via di mezzo per non permettere ai processi utente di accedere direttamente alle porte inferiori, ma per consentire loro di accedervi utilizzando il port-forwarding.
Ti mostriamo recensioni e valutazioni
Se ti è piaciuto il nostro lavoro, sei libero di lasciare una nota su ciò che ti è piaciuto di questo articolo.