Non sono sicuro a cosa si riferiscano le altre risposte e commenti qui. Questo è possibile piuttosto facilmente. Ci sono due opzioni, entrambe che consentono l'accesso a porte con un numero basso senza dover elevare il processo a root:
Opzione 1:utilizza CAP_NET_BIND_SERVICE
per concedere a un processo l'accesso a una porta con un numero basso:
Con questo puoi concedere l'accesso permanente a un binario specifico da associare a porte con un numero basso tramite setcap
comando:
sudo setcap CAP_NET_BIND_SERVICE=+eip /path/to/binary
Per maggiori dettagli sulla parte e/i/p, vedi cap_from_text
.
Dopo aver fatto ciò, /path/to/binary
sarà in grado di collegarsi a porte con un numero basso. Nota che devi usare setcap
sul file binario stesso piuttosto che su un collegamento simbolico.
Opzione 2:utilizza authbind
per concedere l'accesso una tantum, con un controllo utente/gruppo/porta più accurato:
Il authbind
(man page) esiste proprio per questo.
-
Installa
authbind
utilizzando il tuo gestore di pacchetti preferito. -
Configuralo per concedere l'accesso alle porte pertinenti, ad es. per consentire 80 e 443 da 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 esegui il tuo comando tramite
authbind
(facoltativamente specificando--deep
o altri argomenti, vedere la pagina man):authbind --deep /path/to/binary command line args
Ad esempio
authbind --deep java -jar SomeServer.jar
Ci sono lati positivi e negativi in entrambi i precedenti. L'opzione 1 concede fiducia al binario ma non fornisce alcun controllo sull'accesso per porta. L'opzione 2 concede fiducia all'utente/gruppo e fornisce il controllo sull'accesso per porta, ma le versioni precedenti supportavano solo IPv4 (da quando l'ho scritto originariamente, sono state rilasciate versioni più recenti con supporto IPv6).
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 viene eseguito come superutente e collega il socket di ascolto;
- avere un altro programma piccolo, semplice, facilmente controllabile, che elimina i privilegi, generato dal primo programma;
- per avere la carne del servizio, in un terzo separato programma, eseguito con un account non superutente e 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. Il rischio elevato è leggere dalla rete e agire in base a ciò che viene letto non nei semplici atti di aprire un socket, associarlo a una porta e chiamare listen()
. È la parte di un servizio che fa la comunicazione effettiva che è ad alto rischio. Le parti che si aprono, bind()
e listen()
, e anche (in una certa misura) la parte che accepts()
, non sono ad alto rischio e possono essere eseguiti sotto l'egida del superutente. Non usano e non agiscono su (con l'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 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()
ed in un processo di servizio che genera con un descrittore di file aperto per il socket.
La difficoltà dell'auditing non è un gran problema, dato che si deve audire solo un programma. inetd
Il problema principale di non è tanto l'auditing, quanto piuttosto il fatto che non fornisce un semplice controllo granulare del servizio di runtime, 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 il set di strumenti 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 entrambi i listen()
e il accept()
.
tcpserver
quindi genera un programma di servizio che elimina i privilegi di root (poiché il protocollo servito prevede l'avvio come superutente e quindi il "accesso", come nel caso, ad esempio, di un demone FTP o SSH) o setuidgid
che è un piccolo programma autonomo e facilmente verificabile che elimina esclusivamente i privilegi e quindi concatena i carichi al programma di servizio vero e proprio (nessuna parte del quale quindi viene mai eseguita con privilegi di superutente, come nel caso, ad esempio, di qmail-smtpd
).
Un servizio run
script sarebbe quindi ad esempio (questo per dummyidentd per fornire un servizio IDENT nullo):
#!/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 setuidgid
utilità, proprio come gli altri. Una piccola differenza è che è utilizzabile con systemd
in stile "LISTEN_FDS" così come con i servizi UCSPI-TCP, quindi il tradizionale tcpserver
program è sostituito da due programmi separati:tcp-socket-listen
e tcp-socket-accept
.
Ancora una volta, le utilità monouso si generano e si caricano a catena l'una con l'altra. Una stranezza interessante del design è che si possono eliminare i privilegi di superutente dopo listen()
ma prima ancora di accept()
. Ecco un run
script per qmail-smtpd
che in effetti 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 di caricamento a catena indipendenti dai servizi fdmove
, clearenv
, envdir
, softlimit
, tcp-socket-listen
e setuidgid
. Al punto che sh
viene avviato, il socket è aperto e associato al smtp
port e il processo non ha più privilegi di superutente.
s6, s6-networking ed execline
I pacchetti di rete 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, eccetto 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 un servizio FTP, leggermente modificato rispetto all'originale di Wayne Marshall, che utilizza execline, s6, s6-networking e il programma del 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 funziona sulla stessa linea 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 esegue la lettura, l'elaborazione e la scrittura di cose inviate in rete da client non fidati è ancora in un programma separato.
Ecco l'esempio di M. Pape sull'esecuzione di fnord
in un 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 init che si trova in alcune distribuzioni Linux, ha lo scopo di fare ciò che inetd
può fare. Tuttavia, non utilizza una suite di piccoli programmi autonomi. Bisogna audire systemd
nella sua interezza, purtroppo.
Con systemd
uno crea file di configurazione per definire un socket che systemd
in ascolto e un servizio che systemd
inizia. Il file "unità" del servizio ha impostazioni che consentono un grande controllo sul processo del servizio, incluso l'utente con cui viene eseguito.
Con quell'utente impostato come non superutente, systemd
fa tutto il lavoro di aprire il socket, associarlo a una porta e chiamare listen()
(e, se richiesto, accept()
) nel processo n. 1 come superutente e il processo di servizio che genera viene eseguito senza privilegi di superutente.
Ho un approccio piuttosto diverso. Volevo usare la porta 80 per un server node.js. Non sono riuscito a 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 scoperto che posso inoltrare le connessioni da una porta a un'altra. Quindi ho avviato il server sulla porta 3000 e ho impostato un port forward dalla porta 80 alla porta 3000.
Questo collegamento fornisce i comandi effettivi che possono essere utilizzati per eseguire questa operazione. Ecco i comandi -
host locale/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 al processo utente di accedere direttamente alle porte inferiori, ma concedere loro l'accesso utilizzando il port forwarding.