Esiste un modo per i processi non root di collegarsi a porte "privilegiate" su Linux?


389

È molto fastidioso avere questa limitazione nella mia casella di sviluppo, quando non ci saranno mai altri utenti oltre a me.

Sono a conoscenza delle soluzioni alternative standard , ma nessuna di esse fa esattamente quello che voglio:

  1. authbind (La versione in Debian testing, 1.0, supporta solo IPv4)
  2. Utilizzo del target REDIRECT di iptables per reindirizzare una porta bassa a una porta alta (la tabella "nat" non è ancora implementata per ip6tables, la versione IPv6 di iptables)
  3. sudo (Correre come root è ciò che sto cercando di evitare)
  4. SELinux (o simile). (Questa è solo la mia casella di sviluppo, non voglio introdurre molta complessità extra.)

Esiste una semplice sysctlvariabile per consentire ai processi non root di collegarsi a porte "privilegiate" (porte inferiori a 1024) su Linux, o sono sfortunato?

EDIT: in alcuni casi, è possibile utilizzare le funzionalità per farlo.


5
Nella mia esperienza, uno dei motivi per tentare questo è di scrivere un server web piuttosto che usare Apache (o lighttpd).
S. Lott,

Ho aggiunto la roba setcap alla mia risposta alla quasi identico stackoverflow.com/questions/277991/...
Paul Tomblin

15
Perché in questo caso, stavo usando IPv6, motivo per cui alcune delle "solite" soluzioni alternative (authbind e iptables REDIRECT) non funzionavano per me.
Jason Creighton,


1
Ci sono alcuni modi per farlo. Vedere Consentire il processo non root da associare alle porte 80 e 443? su Super User.
jww

Risposte:


392

Va bene, grazie alle persone che hanno sottolineato il sistema e le CAP_NET_BIND_SERVICEcapacità. Se hai un kernel recente, è davvero possibile usarlo per avviare un servizio come non root ma associare le porte basse. La risposta breve è che fai:

setcap 'cap_net_bind_service=+ep' /path/to/program

E quindi in qualsiasi momento programviene eseguito in seguito avrà la CAP_NET_BIND_SERVICEcapacità. setcapè nel pacchetto debian libcap2-bin.

Ora per gli avvertimenti:

  1. Sarà necessario almeno un kernel 2.6.24
  2. Questo non funzionerà se il tuo file è uno script. (cioè usa una linea #! per lanciare un interprete). In questo caso, per quanto ho capito, dovresti applicare la capacità all'eseguibile interprete stesso, che ovviamente è un incubo di sicurezza, poiché qualsiasi programma che utilizza quell'interprete avrà la capacità. Non sono riuscito a trovare un modo semplice e pulito per aggirare questo problema.
  3. Linux disabiliterà LD_LIBRARY_PATH su chiunque programabbia privilegi elevati come setcapo suid. Quindi se programusi il tuo .../lib/, potresti dover cercare un'altra opzione come il port forwarding.

risorse:

Nota: RHEL lo ha aggiunto per la prima volta nella v6 .


3
Soluzione parziale per gli script: creare una copia dell'interprete (ad es. Bash), assegnargli le capacità ma limitare l'accesso alla copia a quegli utenti che ne hanno bisogno. Naturalmente, questi utenti devono essere fidati, ma potrebbero comunque modificare lo script.
Erich Kitzmueller,

3
A parte il suddetto pacchetto debian (binario), il sito dello sviluppatore è friedhoff.org/posixfilecaps.html documenti / presentazioni associati / ecc ...
RandomNickName42

1
su suse SLES 11.1 ho dovuto aggiungere il parametro del kernel file_caps = 1 a grub menu.lst perché questo funzionasse.
Shay,

2
Sì @ C.Ross poiché dovrebbe essere applicato /usr/bin/javae quindi aprirebbe la funzionalità a qualsiasi app java in esecuzione sul sistema. Non è possibile impostare anche funzionalità troppo povere per utente.
joeytwiddle,

5
L'impostazione setcap persiste tra i riavvii? in caso contrario esiste un posto standard in cui inserire questa regola in modo che venga eseguita all'avvio del sistema? È /etc/security/capability.confsu Debian / Ubuntu di aiuto?
joeytwiddle,

34

È possibile eseguire un reindirizzamento delle porte. Questo è ciò che faccio per un server delle politiche Silverlight in esecuzione su una scatola Linux

iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 943 -j REDIRECT --to-port 1300

5
Sfortunatamente questo funzionerà solo per le connessioni instradate, cioè non dal computer locale.
zbyszek,

@joeytwiddle Penso che sia nello script che esegue (avvia) il tuo server, ma potrei sbagliarmi. Vorrei anche sapere: P
Camilo Martin,

@CamiloMartin Guardando di nuovo, la documentazione Debian collegata alla domanda raccomanda di inserirla nello script del firewall o di crearne una su /etc/network/if-up.d/firewall.
joeytwiddle,

8
@zbyszek Questo lavoro può per le connessioni macchina locale, con un ulteriore iptables regola: stackoverflow.com/a/31795603/1356953
00.500.005

Nei kernel precedenti sembra che questo non sia supportato per IPv6 . Ma a quanto pare è supportato in ip6tables v1.4.18 e kernel Linux v3.8.
Craig McQueen,

31

Il modo standard è di renderli "setuid" in modo che si avviino come root, e quindi buttino via quel privilegio di root non appena si sono collegati alla porta ma prima che inizino ad accettare connessioni ad essa. Puoi vedere buoni esempi di ciò nel codice sorgente di Apache e INN. Mi è stato detto che Lighttpd è un altro buon esempio.

Un altro esempio è Postfix, che utilizza più demoni che comunicano tramite pipe e solo uno o due di essi (che fanno pochissimo tranne accettare o emettere byte) vengono eseguiti come root e il resto viene eseguito con un privilegio inferiore.


1
È interessante notare che questo non funziona con le versioni recenti di Linux (forse solo Ubuntu) senza CAP_SETUID impostato. Quindi, se hai bisogno di setuid, dovrai comunque impostare questa funzionalità.
Matt,

Eliminare i privilegi di root è il modo giusto per farlo. Sebbene non sia necessario impostare setuid se init / upstart / systemd avvia il servizio.
Michael Hampton,

2
Ciò è difficile se il programma è scritto in un linguaggio interpretato o in un interprete bytecode come C # (Mono), Java, Python. (Apparentemente Perl lo ha fatto tramite binfmt_misce la sua bandiera 'C'; non sono sicuro degli altri.)
Craig McQueen

molto poco tranne emettere byte, non sta facendo molto poco.
Ryan,

Se si imposta un setuid binario, verrà eseguito come processo di root. La domanda, tuttavia, chiede come eseguirlo senza eseguire il file binario come processo di root. Questa soluzione è la risposta a una domanda diversa, vale a dire: "Come può un utente non privilegiato avere un processo vincolato a una porta privilegiata", ma questa non è la domanda posta, IMHO?
Michael Beer,

24

O patch il kernel e rimuovere il segno di spunta.

(Opzione di ultima istanza, non consigliata).

In net/ipv4/af_inet.c, rimuovi le due righe che leggono

      if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
              goto out;

e il kernel non controlla più le porte privilegiate.


22
Un'idea pessima per una moltitudine di ragioni.
Adam Lassek,

24
Questa è una cattiva idea. Ma funzionerebbe davvero. E sicuramente mi ha fatto ridere.
Oto Brglez,

23
Certo è una cattiva idea. Ecco perché ho detto l'ultima risorsa. Il punto dell'open source è che se non funziona nel modo desiderato puoi cambiarlo.
Giosuè,

18
Non sono sicuro del motivo per cui sei stato sottovalutato. Gli autori di malware non si preoccupano della porta su cui sono in ascolto e sono più che felici di aprire una porta maggiore di 1024. Mi sembra che tutte le porte debbano richiedere privilegi o che nessuna porta debba richiedere privilegi. E richiedere a root di aprire una porta inferiore a 1024 significa solo che hai un'applicazione ad alto rischio in esecuzione come root. Mi sembra un'idea davvero stupida. Forse mi manca qualcosa qui ...
JWW

9
In passato, la porta <1024 veniva utilizzata nei protocolli da UNIX a UNIX per dimostrare che il codice in esecuzione sull'altra estremità era in esecuzione come root. Funzionava abbastanza bene per un set di server UNIX con sicurezza comune.
Giosuè,

22

Puoi configurare un tunnel SSH locale, ad esempio se vuoi che la porta 80 raggiunga la tua app legata a 3000:

sudo ssh $USERNAME@localhost -L 80:localhost:3000 -N

Questo ha il vantaggio di lavorare con i server di script e di essere molto semplice.


1
Il mio preferito personale. Molto facile da accendere e spegnere e non richiede modifiche a livello di sistema. Grande idea!
Dan Passaro,

Questo è fantastico Di gran lunga la risposta più semplice.
michaelsnowden,

1
Mi aspetto che questo sia moderatamente costoso e non sia una buona idea in qualsiasi situazione legata alle prestazioni, ma non posso esserne sicuro senza testarlo. La crittografia SSH costa un importo diverso da zero.
Lahwran,

3
Questo potrebbe essere fatto con netcat senza spese generali di SSH:sudo nc -l 80 | nc localhost 3000
Bugster

1
Stai crittografando + decrittografando solo per spostare i dati su un'altra porta sullo stesso computer? Inoltre, questo non funziona per il traffico UDP.
Daniel F,

19

Aggiornamento 2017:

Usa authbind


Molto meglio di CAP_NET_BIND_SERVICE o di un kernel personalizzato.

  • CAP_NET_BIND_SERVICE garantisce l'attendibilità al file binario ma non fornisce alcun controllo sull'accesso per porta.
  • Authbind garantisce fiducia all'utente / al gruppo e fornisce il controllo dell'accesso per porta e supporta sia IPv4 che IPv6 (il supporto IPv6 è stato aggiunto di recente).

    1. Installare: apt-get install authbind

    2. Configurare l'accesso alle porte pertinenti, ad esempio 80 e 443 per 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. Eseguire il comando tramite authbind
      (specificando facoltativamente --deepo altri argomenti, vedere la pagina man):

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

      per esempio

      authbind --deep java -jar SomeServer.jar
      

Come seguito alla favolosa raccomandazione di Joshua (= sconsigliata se non sai cosa fai) di hackerare il kernel:

L'ho pubblicato prima qui .

Semplice. Con un kernel normale o vecchio, non lo fai.
Come sottolineato da altri, iptables può inoltrare una porta.
Come sottolineato anche da altri, CAP_NET_BIND_SERVICE può anche fare il lavoro.
Ovviamente CAP_NET_BIND_SERVICE fallirà se avvii il tuo programma da uno script, a meno che tu non imposti il ​​cap sull'interprete della shell, il che è inutile, potresti anche eseguire il tuo servizio come root ...
ad esempio per Java, devi applicarlo alla JAVA JVM

sudo /sbin/setcap 'cap_net_bind_service=ep' /usr/lib/jvm/java-8-openjdk/jre/bin/java

Ovviamente, ciò significa che qualsiasi programma Java può associare le porte di sistema.
Dito per mono / .NET.

Sono anche abbastanza sicuro che xinetd non sia la migliore delle idee.
Ma poiché entrambi i metodi sono hack, perché non alzare il limite alzando la restrizione?
Nessuno ha detto che devi eseguire un kernel normale, quindi puoi semplicemente eseguire il tuo.

Devi solo scaricare il sorgente per l'ultimo kernel (o lo stesso che hai attualmente). Successivamente, vai a:

/usr/src/linux-<version_number>/include/net/sock.h:

Lì cerchi questa linea

/* Sockets 0-1023 can't be bound to unless you are superuser */
#define PROT_SOCK       1024

e cambiarlo in

#define PROT_SOCK 0

se non vuoi avere una situazione ssh insicura, la modifichi in questo: #define PROT_SOCK 24

In genere, utilizzerei l'impostazione più bassa di cui hai bisogno, ad esempio 79 per http o 24 quando utilizzo SMTP sulla porta 25.

Questo è già tutto.
Compilare il kernel e installarlo.
Reboot.
Finito: quello stupido limite è ANDATO e funziona anche per gli script.

Ecco come compilare un kernel:

https://help.ubuntu.com/community/Kernel/Compile

# You can get the kernel-source via package linux-source, no manual download required
apt-get install linux-source fakeroot

mkdir ~/src
cd ~/src
tar xjvf /usr/src/linux-source-<version>.tar.bz2
cd linux-source-<version>

# Apply the changes to PROT_SOCK define in /include/net/sock.h

# Copy the kernel config file you are currently using
cp -vi /boot/config-`uname -r` .config

# Install ncurses libary, if you want to run menuconfig
apt-get install libncurses5 libncurses5-dev

# Run menuconfig (optional)
make menuconfig

# Define the number of threads you wanna use when compiling (should be <number CPU cores> - 1), e.g. for quad-core
export CONCURRENCY_LEVEL=3
# Now compile the custom kernel
fakeroot make-kpkg --initrd --append-to-version=custom kernel-image kernel-headers

# And wait a long long time

cd ..

In poche parole, usa iptables se vuoi rimanere sicuro, compila il kernel se vuoi essere sicuro che questa restrizione non ti disturbi mai più.


Qualche motivo per il downvote? Root-hater o le istruzioni di compilazione del kernel non hanno funzionato?
Stefan Steiger,

4
la modifica del kernel rende gli aggiornamenti futuri molto più dolorosi. non lo farei, perché so che sarei troppo pigro per continuare ad aggiornare regolarmente il mio kernel. è bello che sia possibile in teoria, ma non è un'opzione praticabile in molte circostanze.
kritzikratzi,

Forse un modo più sicuro per farlo è cambiare chmod 777 / etc / authbind / byport / 80 di chmod 544 / etc / authbind / byport / 23 di chown login: group / etc / authbind / byport / 23
Jérôme B

18

Le funzionalità dei file non sono ideali, poiché possono rompersi dopo un aggiornamento del pacchetto.

La soluzione ideale, IMHO, dovrebbe essere la capacità di creare una shell con ereditabile CAP_NET_BIND_SERVICE set .

Ecco un modo un po 'contorto per farlo:

sg $DAEMONUSER "capsh --keep=1 --uid=`id -u $DAEMONUSER` \
     --caps='cap_net_bind_service+pei' -- \
     YOUR_COMMAND_GOES_HERE"

capshl'utilità può essere trovata nel pacchetto libcap2-bin nelle distribuzioni Debian / Ubuntu. Ecco cosa succede:

  • sgcambia l'ID del gruppo effettivo in quello dell'utente del demone. Ciò è necessario perché capshlascia GID invariato e sicuramente non lo vogliamo.
  • Imposta il bit 'mantieni le capacità sul cambio UID'.
  • Cambia l'UID in $DAEMONUSER
  • Elimina tutti i tappi (in questo momento tutti i tappi sono ancora presenti a causa di --keep=1), tranne ereditaricap_net_bind_service
  • Esegue il comando ('-' è un separatore)

Il risultato è un processo con utente e gruppo specificati e cap_net_bind_serviceprivilegi.

Ad esempio, una riga dallo ejabberdscript di avvio:

sg $EJABBERDUSER "capsh --keep=1 --uid=`id -u $EJABBERDUSER` --caps='cap_net_bind_service+pei' -- $EJABBERD --noshell -detached"

E in realtà NON funziona. I limiti non vengono mantenuti tramite l'interruttore ID utente. Funzionerà se si desidera eseguire come root, ma con tutte le funzionalità eliminate.
Cyberax,

Se lo provo, ottengo "impossibile eseguire il file binario". In realtà, non posso ottenere capshdi fare qualcosa di diverso da "non può eseguire file binario" quando si utilizzano le --o ==opzioni. Mi chiedo cosa mi manchi.
Craig McQueen,

@CraigMcQueen, tutto ciò che --viene passato viene passato a /bin/bash, quindi potresti provare con -c 'your command'. Purtroppo, sembra che stia riscontrando lo stesso problema di @Cyberax, perché mi viene negato il permesso quando provo a farlo bind.
Amir,

Per far funzionare tutto questo senza impostare le funzionalità di file (o in esecuzione come root), si avrebbe bisogno di utilizzare le capacità ambientali , come descritto in questa risposta Unix.SE . Si potrebbe anche usare --gidal posto di sg(o --userche gli insiemi --uid, --gide --groups): sudo capsh --caps='cap_net_bind_service+eip cap_setpcap,cap_setuid,cap_setgid+ep' --keep=1 --user="$service_user" --addamb=cap_net_bind_service -- -c 'exec $service $service_args'
Kevinoid

18

Per qualche ragione nessuno menziona la riduzione di sysctl net.ipv4.ip_unprivileged_port_start al valore che ti serve. Esempio: dobbiamo associare la nostra app alla porta 443.

sysctl net.ipv4.ip_unprivileged_port_start=443

Alcuni potrebbero dire che esiste un potenziale problema di sicurezza: gli utenti non privilegiati ora possono legarsi alle altre porte privilegiate (444-1024). Ma puoi risolvere questo problema facilmente con iptables, bloccando altre porte:

iptables -I INPUT -p tcp --dport 444:1024 -j DROP
iptables -I INPUT -p udp --dport 444:1024 -j DROP

Confronto con altri metodi. Questo metodo:

  • da un certo punto (IMO) è ancora più sicuro dell'impostazione di CAP_NET_BIND_SERVICE / setuid, dal momento che un'applicazione non è affatto impostata, anche parzialmente (le capacità in realtà lo sono). Ad esempio, per catturare un coredump di applicazioni abilitate per le funzionalità dovrai cambiare sysctl fs.suid_dumpable (che porta ad altri potenziali problemi di sicurezza) Inoltre, quando è impostato CAP / suid, la directory / proc / PID è di proprietà di root, quindi l'utente non root non disporrà di informazioni / controllo completi del processo in esecuzione, ad esempio l'utente non sarà in grado (nel caso comune) di determinare quali connessioni appartengono all'applicazione tramite / proc / PID / fd / (netstat -aptn | grep PID).
  • presenta uno svantaggio di sicurezza: mentre l'app (o qualsiasi app che utilizza le porte 443-1024) è inattiva per qualche motivo, un'altra app potrebbe prendere la porta. Ma questo problema potrebbe essere applicato anche a CAP / suid (nel caso in cui sia impostato su interprete, ad esempio java / nodejs) e iptables-redirect. Utilizzare il metodo systemd-socket per escludere questo problema. Utilizzare il metodo authbind per consentire solo l'associazione utente speciale.
  • non richiede l'impostazione di CAP / suid ogni volta che si distribuisce una nuova versione dell'applicazione.
  • non richiede supporto / modifica dell'applicazione, come il metodo systemd-socket.
  • non richiede la ricostruzione del kernel (se la versione in esecuzione supporta questa impostazione sysctl)
  • LD_PRELOAD non funziona come il metodo authbind / privbind, ciò potrebbe potenzialmente influire su prestazioni, sicurezza e comportamento (non è stato testato)? Nel resto authbind è un metodo davvero flessibile e sicuro.
  • esegue in modo eccessivo il metodo REDIRECT / DNAT di iptables, poiché non richiede la traduzione dell'indirizzo, il monitoraggio dello stato della connessione, ecc. Ciò è evidente solo sui sistemi ad alto carico.

A seconda della situazione, sceglierei tra sysctl, CAP, authbind e iptables-redirect. E questo è fantastico che abbiamo così tante opzioni.


2
Grazie per la magnifica risposta! Sembra che questa funzionalità sia apparsa per la prima volta in Linux 4.11 nell'aprile 2017 , quindi non era in circolazione nel 2009 quando ho fatto questa domanda per la prima volta. Ho anche fatto un test rapido e sembra funzionare anche per IPV6, anche se "ipv4" è nel nome sysctl.
Jason Creighton,

15

Altre due semplici possibilità:

Esiste una vecchia soluzione (non alla moda) per "un demone che si lega su una porta bassa e passa il controllo al demone". Si chiama inetd (o xinetd). I contro sono:

  • il tuo demone deve parlare su stdin / stdout (se non controlli il demone - se non hai il sorgente - allora questo è forse uno showtopper, anche se alcuni servizi potrebbero avere un flag di compatibilità inetd)
  • un nuovo processo daemon è biforcuto per ogni connessione
  • è un anello in più nella catena

Professionisti:

  • disponibile su qualsiasi vecchio UNIX
  • una volta che l'amministratore di sistema ha impostato la configurazione, sei pronto per lo sviluppo (quando ricostruisci il tuo demone, potresti perdere le capacità di setcap? E poi dovrai tornare al tuo amministratore ", per favore, signore ... . ")
  • il demone non deve preoccuparsi di quella roba di rete, deve solo parlare su stdin / stdout
  • può configurare per eseguire il tuo demone come utente non root, come richiesto

Un'altra alternativa: un proxy hackerato (netcat o anche qualcosa di più robusto ) dalla porta privilegiata ad una porta arbitraria ad alto numero in cui è possibile eseguire il demone di destinazione. (Netcat non è ovviamente una soluzione di produzione, ma "solo la mia scatola di sviluppo", giusto?). In questo modo potresti continuare a utilizzare una versione di rete del tuo server, sarebbe necessario solo root / sudo per avviare il proxy (all'avvio), non fare affidamento su funzionalità complesse / potenzialmente fragili.


1
Buon consiglio Per qualche ragione non ho nemmeno pensato di usare inetd. Solo che il servizio in questione è basato su UDP, quindi è leggermente più complicato dei servizi TCP. Ho una soluzione che funziona ora con setcap, ma dovrò pensarci.
Jason Creighton,

Per la seconda opzione, probabilmente puoi anche avviare il proxy usando inetd ... (Ma IPTables è probabilmente un sovraccarico inferiore ...)
Gert van den Berg

14

La mia "soluzione alternativa standard" utilizza socat come redirector dello spazio utente:

socat tcp6-listen:80,fork tcp6:8080

Attenzione che questo non si ridimensiona, il fork è costoso ma è il modo in cui funziona socat.


13

Linux supporta funzionalità per supportare permessi più dettagliati rispetto al semplice "questa applicazione viene eseguita come root". Una di queste funzionalità CAP_NET_BIND_SERVICEriguarda l'associazione a una porta privilegiata (<1024).

Sfortunatamente non so come sfruttarlo per eseguire un'applicazione come non root pur continuando a darla CAP_NET_BIND_SERVICE(probabilmente usando setcap, ma c'è sicuramente una soluzione esistente per questo).


Non funziona con script o singoli programmi JVM / mono.
Stefan Steiger,

13

So che questa è una vecchia domanda, ma ora con kernel recenti (> = 4.3) c'è finalmente una buona risposta a questo: le capacità ambientali.

La risposta rapida è prendere una copia dell'ultima versione (non ancora rilasciata) di libcap da git e compilarla. Copia il progs/capshbinario risultante da qualche parte ( /usr/local/binè una buona scelta). Quindi, come root, inizia il tuo programma con

/usr/local/bin/capsh --keep=1 --user='your-service-user-name' \
    --inh='cap_net_bind_service' --addamb='cap_net_bind_service' \ 
    -- -c 'your-program'

In ordine, lo siamo

  • Dichiarando che quando cambiamo utente, vogliamo mantenere i nostri attuali set di capacità
  • Passaggio di utente e gruppo a "nome-utente-di-servizio"
  • Aggiungere il cap_net_bind_service funzionalità ai set ereditati e ambientali
  • Forking bash -c 'your-command'(poiché capshinizia automaticamente bash con gli argomenti dopo --)

C'è molto da fare sotto il cofano qui.

In primo luogo, stiamo eseguendo come root, quindi per impostazione predefinita otteniamo un set completo di funzionalità. Inclusi in questo è la possibilità di passare da uid & gid a setuide setgidsyscalls. Tuttavia, normalmente quando un programma fa questo, perde il suo insieme di capacità - questo è così che il vecchio modo di far cadere radice con setuidfunziona ancora. La --keep=1bandiera indica capshdi emettere la prctl(PR_SET_KEEPCAPS)syscall, che disabilita la caduta di funzionalità quando si cambia utente. L'effettivo cambio di utenti capshavviene con la --userbandiera, che corre setuide setgid.

Il prossimo problema che dobbiamo risolvere è come impostare le capacità in modo da continuare dopo che i execnostri figli. Il sistema di capacità ha sempre avuto un insieme di capacità "ereditate", che è "un insieme di capacità conservate in un execve (2)" [ capacità (7) ]. Anche se sembra che risolva il nostro problema (basta impostare la cap_net_bind_servicecapacità su ereditato, giusto?), Questo in realtà si applica solo ai processi privilegiati - e il nostro processo non è più privilegiato, perché abbiamo già cambiato utente (con il --userflag).

Il nuovo set di funzionalità ambientali risolve questo problema: è "un set di funzionalità che vengono conservate in un execve (2) di un programma che non ha i privilegi". Inserendo cap_net_bind_serviceil set ambientale, quando capshexec è il nostro programma server, il nostro programma erediterà questa capacità e sarà in grado di associare gli ascoltatori a porte basse.

Se sei interessato a saperne di più, la pagina del manuale delle funzionalità spiega questo in dettaglio. Correre capshattraverso straceè anche molto istruttivo!


La --addambbandiera è stata introdotta con libcap 2.26. Il mio sistema aveva 2.25 e ho dovuto compilare dal sorgente.
ngreen

12

TLDR: per "la risposta" (come la vedo io), passa alla sezione >> TLDR << in questa risposta.

OK, l'ho capito (davvero questa volta), la risposta a questa domanda, e questa mia risposta è anche un modo per scusarmi per aver promosso un'altra risposta (sia qui che su Twitter) che pensavo fosse "la migliore ", ma dopo averlo provato, ho scoperto che mi sbagliavo. Impara dai miei errori bambini: non promuovere qualcosa fino a quando non l'hai provato tu stesso!

Ancora una volta, ho esaminato tutte le risposte qui. Ne ho provati alcuni (e ho scelto di non provare altri perché semplicemente non mi piacevano le soluzioni). Ho pensato che la soluzione fosse usare systemdcon le sue Capabilities=e le CapabilitiesBindingSet=impostazioni. Dopo aver lottato con questo per qualche tempo, ho scoperto che questa non è la soluzione perché:

Le funzionalità hanno lo scopo di limitare i processi di root!

Come ha saggiamente affermato l'OP, è sempre meglio evitarlo (se possibile per tutti i tuoi demoni!).

Non è possibile utilizzare le opzioni relative alle funzionalità con User=e Group=nei systemdfile di unità, poiché le funzionalità vengono SEMPRE ripristinate quando execev(o qualunque sia la funzione) viene chiamata. In altre parole, quando si systemdbiforca e rilascia i suoi permanenti, le funzionalità vengono ripristinate. Non c'è modo di aggirare questo, e tutta quella logica di legame nel kernel è di base attorno a uid = 0, non alle capacità. Ciò significa che è improbabile che le capacità siano mai la risposta giusta a questa domanda (almeno in qualunque momento presto). Per inciso, setcapcome altri hanno già detto, non è una soluzione. Non ha funzionato per me, non funziona bene con gli script e questi vengono ripristinati comunque ogni volta che il file cambia.

A mia modesta difesa, ho affermato (nel commento che ho ora eliminato), che il suggerimento iptables di James (che cita anche l'OP), era la "seconda soluzione migliore". :-P

>> << TLDR

La soluzione è combinare systemdcon i iptablescomandi al volo , come questo ( preso da DNSChain ):

[Unit]
Description=dnschain
After=network.target
Wants=namecoin.service

[Service]
ExecStart=/usr/local/bin/dnschain
Environment=DNSCHAIN_SYSD_VER=0.0.1
PermissionsStartOnly=true
ExecStartPre=/sbin/sysctl -w net.ipv4.ip_forward=1
ExecStartPre=-/sbin/iptables -D INPUT -p udp --dport 5333 -j ACCEPT
ExecStartPre=-/sbin/iptables -t nat -D PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 5333
ExecStartPre=/sbin/iptables -A INPUT -p udp --dport 5333 -j ACCEPT
ExecStartPre=/sbin/iptables -t nat -A PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 5333
ExecStopPost=/sbin/iptables -D INPUT -p udp --dport 5333 -j ACCEPT
ExecStopPost=/sbin/iptables -t nat -D PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 5333
User=dns
Group=dns
Restart=always
RestartSec=5
WorkingDirectory=/home/dns
PrivateTmp=true
NoNewPrivileges=true
ReadOnlyDirectories=/etc

# Unfortunately, capabilities are basically worthless because they're designed to restrict root daemons. Instead, we use iptables to listen on privileged ports.
# Capabilities=cap_net_bind_service+pei
# SecureBits=keep-caps

[Install]
WantedBy=multi-user.target

Qui realizziamo quanto segue:

  • Il daemon è in ascolto su 5333, ma le connessioni sono state accettate con successo su 53 grazie a iptables
  • Possiamo includere i comandi nel file unitario stesso, e quindi salviamo il mal di testa delle persone. systemdpulisce le regole del firewall per noi, assicurandoci di rimuoverle quando il demone non è in esecuzione.
  • Non corriamo mai come root e rendiamo impossibile l'escalation dei privilegi (almeno systemdpretende di farlo), presumibilmente anche se il demone è compromesso e impostato uid=0.

iptablespurtroppo è ancora un'utilità piuttosto brutta e difficile da usare. Se il demone è in ascolto eth0:0anziché eth0, ad esempio, i comandi sono leggermente diversi .


2
Nessuno dovrebbe usare alias vecchio stile come eth0:0se non avessero una distribuzione Linux davvero antica . Sono stati deprecati per anni e alla fine andranno via.
Michael Hampton,

1
Penso che intendi OpenVZ. (SolusVM è un pannello di controllo.) E sì, OpenVZ fa un sacco di cose sbagliate, essendo la rete solo una di queste.
Michael Hampton,

1
No, intendo per SolusVM. Da / etc / network / interfaces:# Generated by SolusVM
Greg Slepak

1
Ancora non OpenVZ ... Almeno non lo sto usando, né il mio VPS.
Greg Slepak,

7
Grazie per aver sottolineato la funzionalità di systemd. Tuttavia non è necessario per iptables complessi quando systemd avvia direttamente il binario (non uno script). L'impostazione ha AmbientCapabilities=CAP_NET_BIND_SERVICEfunzionato perfettamente per me in combinazione con User=.
Ruud,

11

systemd è un sostituto di sysvinit che ha un'opzione per avviare un demone con capacità specifiche. Opzioni Capacità =, CapabilityBoundingSet = nella manpage systemd.exec (5) .


2
In precedenza avevo raccomandato questa risposta, ma dopo averlo provato, ora non lo faccio. Vedi la mia risposta per un'alternativa che ancora utilizza systemd.
Greg Slepak,

10

Il reindirizzamento delle porte aveva più senso per noi, ma ci siamo imbattuti in un problema in cui la nostra applicazione avrebbe risolto localmente un URL che doveva anche essere reindirizzato; (ciò significa che sei shindig ).

Ciò consentirà inoltre di essere reindirizzati quando si accede all'URL sul computer locale.

iptables -A PREROUTING -t nat -p tcp --dport 80 -j REDIRECT --to-port 8080
iptables -A OUTPUT -t nat -p tcp --dport 80 -j REDIRECT --to-port 8080

9

Supporti Linux moderni /sbin/sysctl -w net.ipv4.ip_unprivileged_port_start=0.


8

Con systemd, devi solo modificare leggermente il tuo servizio per accettare socket pre-attivati.

È possibile utilizzare successivamente socket di systemd attivato .

Non sono necessarie funzionalità, iptables o altri trucchi.

Questo è il contenuto dei file systemd rilevanti da questo esempio di semplice server http python

File httpd-true.service

[Unit]
Description=Httpd true 

[Service]
ExecStart=/usr/local/bin/httpd-true
User=subsonic

PrivateTmp=yes

File httpd-true.socket

[Unit]
Description=HTTPD true

[Socket]
ListenStream=80

[Install]
WantedBy=default.target

7

All'avvio:

iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8080

Quindi è possibile associare alla porta a cui si inoltra.


non --to-portesiste? man iptablesmenziona solo --to-ports(plurale).
Abdull,

Ho notato alcune differenze e ho saltato in giro
James

Vedi questa risposta per come combinare questo con systemd.
Greg Slepak,


3

C'è anche il 'modo djb'. È possibile utilizzare questo metodo per avviare il processo come root in esecuzione su qualsiasi porta in tcpserver, quindi passerà il controllo del processo all'utente specificato immediatamente dopo l'avvio del processo.

#!/bin/sh

UID=$(id -u username)
GID=$(id -g username)
exec tcpserver -u "${UID}" -g "${GID}" -RHl0 0 port /path/to/binary &

Per maggiori informazioni, consultare: http://thedjbway.b0llix.net/daemontools/uidgid.html


1

Poiché l'OP è solo sviluppo / test, possono essere utili soluzioni non semplici:

setcap può essere utilizzato sull'interprete di uno script per garantire funzionalità agli script. Se setcaps sul binario dell'interprete globale non è accettabile, crea una copia locale del binario (qualsiasi utente può) e ottieni root per setcap su questa copia. Python2 (almeno) funziona correttamente con una copia locale dell'interprete nell'albero di sviluppo degli script. Non è necessario alcun aiuto in modo che l'utente root possa controllare a quali funzionalità gli utenti hanno accesso.

Se è necessario tenere traccia degli aggiornamenti dell'intero sistema all'interprete, utilizzare uno script shell come il seguente per eseguire lo script:

#!/bin/sh
#
#  Watch for updates to the Python2 interpreter

PRG=python_net_raw
PRG_ORIG=/usr/bin/python2.7

cmp $PRG_ORIG $PRG || {
    echo ""
    echo "***** $PRG_ORIG has been updated *****"
    echo "Run the following commands to refresh $PRG:"
    echo ""
    echo "    $ cp $PRG_ORIG $PRG"
    echo "    # setcap cap_net_raw+ep $PRG"
    echo ""
    exit
}

./$PRG $*

1

Ho provato il metodo iptables PREROUTING REDIRECT. Nei kernel precedenti sembra che questo tipo di regola non sia supportato per IPv6 . Ma a quanto pare ora è supportato in ip6tables v1.4.18 e kernel Linux v3.8.

Ho anche scoperto che PREROUTING REDIRECT non funziona per le connessioni avviate all'interno della macchina. Per lavorare per le connessioni dal computer locale, aggiungere anche una regola OUTPUT - vedere il reindirizzamento della porta iptables non funziona per localhost . Ad esempio qualcosa del tipo:

iptables -t nat -I OUTPUT -o lo -p tcp --dport 80 -j REDIRECT --to-port 8080

Ho anche scoperto che PREROUTING REDIRECT influenza anche i pacchetti inoltrati . Cioè, se la macchina inoltra anche pacchetti tra le interfacce (ad esempio se agisce come un punto di accesso Wi-Fi collegato a una rete Ethernet), la regola iptables rileverà anche le connessioni dei client collegati a destinazioni Internet e li reindirizzerà a la macchina. Non è quello che volevo, volevo solo reindirizzare le connessioni dirette alla macchina stessa. Ho scoperto che posso farlo influenzare solo i pacchetti indirizzati alla scatola, aggiungendo -m addrtype --dst-type LOCAL. Ad esempio qualcosa del tipo:

iptables -A PREROUTING -t nat -p tcp --dport 80 -m addrtype --dst-type LOCAL -j REDIRECT --to-port 8080

Un'altra possibilità è utilizzare il port forwarding TCP. Ad esempio usando socat:

socat TCP4-LISTEN:www,reuseaddr,fork TCP4:localhost:8080

Tuttavia, uno svantaggio di questo metodo è che l'applicazione in ascolto sulla porta 8080 non conosce l'indirizzo di origine delle connessioni in entrata (ad es. Per la registrazione o altri scopi di identificazione).


0

Risposta al 2015 / settembre:

ip6tables ora supporta NAT IPV6: http://www.netfilter.org/projects/iptables/files/changes-iptables-1.4.17.txt

Avrai bisogno del kernel 3.7+

Prova:

[09:09:23] root@X:~ ip6tables -t nat -vnL
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination
    0     0 REDIRECT   tcp      eth0   *       ::/0                 ::/0                 tcp dpt:80 redir ports 8080
    0     0 REDIRECT   tcp      eth0   *       ::/0                 ::/0                 tcp dpt:443 redir ports 1443

Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy ACCEPT 6148 packets, 534K bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain POSTROUTING (policy ACCEPT 6148 packets, 534K bytes)
 pkts bytes target     prot opt in     out     source               destination
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.