¿Hay alguna manera para que los procesos no root se unan a puertos "privilegiados" en Linux?

389

Es muy molesto tener esta limitación en mi cuadro de desarrollo, cuando no habrá ningún usuario que no sea yo.

Soy consciente de las soluciones estándar , pero ninguna de ellas hace exactamente lo que quiero:

  1. authbind (la versión en las pruebas de Debian, 1.0, solo admite IPv4)
  2. Uso del objetivo REDIRECT de iptables para redirigir un puerto bajo a un puerto alto (la tabla "nat" aún no está implementada para ip6tables, la versión IPv6 de iptables)
  3. sudo (Ejecutar como root es lo que estoy tratando de evitar)
  4. SELinux (o similar). (Este es solo mi cuadro de desarrollo, no quiero introducir mucha complejidad adicional).

¿Existe alguna sysctlvariable simple para permitir que los procesos no root se unan a puertos "privilegiados" (puertos menores de 1024) en Linux, o simplemente no tengo suerte?

EDITAR: en algunos casos, puede usar capacidades para hacer esto.

Jason Creighton
fuente
55
En mi experiencia, una razón para intentar esto es escribir un servidor web en lugar de usar Apache (o lighttpd).
S.Lott
He añadido la materia setcap a mi respuesta a la casi idéntica stackoverflow.com/questions/277991/...
Paul Tomblin
15
Porque en este caso, estaba usando IPv6, por lo que algunas de las soluciones "habituales" (authbind e iptables REDIRECT) no funcionaron para mí.
Jason Creighton
1
serverfault.com/questions/112795/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
1
Hay algunas formas de hacerlo. Consulte ¿ Permitir que el proceso no root se una al puerto 80 y 443? en Super Usuario.
jww

Respuestas:

392

Bien, gracias a las personas que señalaron el sistema de CAP_NET_BIND_SERVICEcapacidades y la capacidad. Si tiene un kernel reciente, de hecho es posible usarlo para iniciar un servicio como no raíz pero enlazar puertos bajos. La respuesta corta es que haces:

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

Y luego, en cualquier momento que programse ejecute, tendrá la CAP_NET_BIND_SERVICEcapacidad. setcapestá en el paquete debian libcap2-bin.

Ahora para las advertencias:

  1. Necesitará al menos un núcleo 2.6.24
  2. Esto no funcionará si su archivo es un script. (es decir, utiliza una línea #! para iniciar un intérprete). En este caso, hasta donde yo entiendo, tendrías que aplicar la capacidad al ejecutable del intérprete, lo que por supuesto es una pesadilla de seguridad, ya que cualquier programa que use ese intérprete tendrá la capacidad. No pude encontrar ninguna manera limpia y fácil de solucionar este problema.
  3. Linux deshabilitará LD_LIBRARY_PATH en cualquiera programque tenga privilegios elevados como setcapo suid. Entonces, si programusa los suyos .../lib/, es posible que deba buscar otra opción, como el reenvío de puertos.

Recursos:

Nota: RHEL primero agregó esto en v6 .

Jason Creighton
fuente
3
Solución parcial para los scripts: cree una copia del intérprete (por ejemplo, bash), dele las capacidades pero restrinja el acceso a la copia a aquellos usuarios que lo necesiten. Por supuesto, se debe confiar en esos usuarios, pero podrían cambiar el script de todos modos.
Erich Kitzmueller
3
Además del paquete de Debian (binario) mencionado anteriormente, el sitio del desarrollador es friedhoff.org/posixfilecaps.html documentos / presentaciones asociadas / etc ...
RandomNickName42
1
en suse SLES 11.1 Tuve que agregar el kernel param file_caps = 1 a grub menu.lst para que esto funcione.
Shay
2
Sí @ C.Ross ya que tendría que aplicarse /usr/bin/javay luego abriría la capacidad a cualquier aplicación Java que se ejecute en el sistema. Las capacidades demasiado malas tampoco se pueden establecer por usuario.
joeytwiddle
55
¿La configuración de setcap persiste en los reinicios; Si no, ¿hay un lugar estándar para poner esta regla para que se ejecute durante el inicio del sistema? ¿Tiene /etc/security/capability.confDebian / Ubuntu alguna ayuda?
joeytwiddle
34

Puede hacer una redirección de puerto. Esto es lo que hago para un servidor de políticas Silverlight que se ejecuta en un cuadro de Linux

iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 943 -j REDIRECT --to-port 1300
FlappySocks
fuente
55
Desafortunadamente, esto solo funcionará para conexiones enrutadas, es decir, no desde la máquina local.
zbyszek
@joeytwiddle Creo que está en el script que ejecuta (inicia) su servidor, pero podría estar equivocado. También me gustaría saber: P
Camilo Martin
@CamiloMartin Mirando de nuevo, la documentación de Debian que estaba vinculada a la pregunta recomienda colocarla en su script de firewall o crear una en /etc/network/if-up.d/firewall.
joeytwiddle
8
@zbyszek Esto puede funcionar para conexiones de máquinas locales, con una regla adicional de iptables: stackoverflow.com/a/31795603/1356953
00500005
En núcleos más antiguos parece que esto no era compatible con IPv6 . Pero aparentemente es compatible con ip6tables v1.4.18 y Linux kernel v3.8.
Craig McQueen
31

La forma estándar es hacerlos "setuid" para que se inicien como root, y luego descarten ese privilegio de root tan pronto como se unan al puerto pero antes de que comiencen a aceptar conexiones a él. Puede ver buenos ejemplos de eso en el código fuente de Apache e INN. Me dicen que Lighttpd es otro buen ejemplo.

Otro ejemplo es Postfix, que utiliza múltiples demonios que se comunican a través de tuberías, y solo uno o dos de ellos (que hacen muy poco, excepto aceptar o emitir bytes) se ejecutan como root y el resto se ejecuta con un privilegio menor.

Paul Tomblin
fuente
1
Curiosamente, esto no funciona en versiones recientes de Linux (tal vez solo Ubuntu) sin el conjunto CAP_SETUID. Entonces, si necesita setuid, tendrá que configurar esta capacidad de todos modos.
Matt
Dejar caer root privs es la forma correcta de hacer esto. Aunque no es necesario establecer setuid si tiene init / upstart / systemd iniciar el servicio.
Michael Hampton
2
Esto es difícil si el programa está escrito en un lenguaje interpretado o en un intérprete de código de bytes como C # (Mono), Java, Python. (Aparentemente, Perl lo ha hecho a través de binfmt_miscsu bandera 'C'; no estoy seguro de los demás.)
Craig McQueen
muy poco, excepto emitir bytes, eso no está haciendo muy poco.
Ryan
Si establece un setuid binario, se ejecutará como un proceso raíz. La pregunta, sin embargo, pregunta cómo lograrlo sin ejecutar su binario como proceso raíz. Esta solución es la respuesta a una pregunta diferente, a saber: "¿Cómo puede un usuario no privilegiado que un proceso se vincule a un puerto privilegiado", pero esta no es la pregunta planteada, en mi humilde opinión?
Michael Beer
24

O parchear su núcleo y eliminar el cheque.

(Opción de último recurso, no recomendado).

En net/ipv4/af_inet.c, elimine las dos líneas que leen

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

y el kernel ya no verificará los puertos privilegiados.

Joshua
fuente
22
Una muy mala idea por una multitud de razones.
Adam Lassek el
24
Esta es una mala idea. Pero en realidad funcionaría. Y seguro me hizo reír.
Oto Brglez
23
Por supuesto que es una mala idea. Por eso dije último recurso. El punto de código abierto es que si no funciona de la manera que desea puede cambiarlo.
Joshua
18
No estoy seguro de por qué te rechazaron. A los escritores de malware no les importa en qué puerto escuchan, y están más que felices de abrir un puerto superior a 1024. Me parece que todos los puertos deberían requerir privilegios, o ningún puerto debería requerir privilegios. Y requerir que la raíz abra un puerto por debajo de 1024 solo significa que tiene una aplicación de alto riesgo ejecutándose como raíz. Eso me parece una idea realmente tonta. Tal vez me estoy perdiendo algo aquí ...
jww
99
En el pasado, el puerto <1024 se usaba en los protocolos UNIX a UNIX para probar que el código que se ejecutaba en el otro extremo se ejecutaba como root. Esto funcionó razonablemente bien para un conjunto de servidores UNIX con seguridad común.
Joshua
22

Puede configurar un túnel SSH local, por ejemplo, si desea que el puerto 80 llegue a su aplicación vinculada a 3000:

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

Esto tiene la ventaja de trabajar con servidores de script y ser muy simple.

Gabriel Burt
fuente
1
Mi favorito personal Muy fácil de encender y apagar y no requiere cambios en todo el sistema. ¡Gran idea!
Dan Passaro el
Esto es increíble. Con mucho, la respuesta más simple.
michaelsnowden
1
Esperaría que esto sea moderadamente caro y no sea una buena idea en cualquier situación de rendimiento, pero no puedo estar seguro sin probarlo. El cifrado SSH cuesta una cantidad distinta de cero.
lahwran
3
Esto podría hacerse con netcat sin gastos generales de ssh:sudo nc -l 80 | nc localhost 3000
Bugster
1
¿Está cifrando + descifrando solo para mover los datos a otro puerto en la misma máquina? Además, esto no funciona para el tráfico UDP.
Daniel F
19

Actualización 2017:

Use authbind


Mucho mejor que CAP_NET_BIND_SERVICE o un kernel personalizado.

  • CAP_NET_BIND_SERVICE otorga confianza al binario pero no proporciona control sobre el acceso por puerto.
  • Authbind otorga confianza al usuario / grupo y proporciona control sobre el acceso por puerto, y es compatible con IPv4 e IPv6 (últimamente se ha agregado compatibilidad con IPv6).

    1. Instalar en pc: apt-get install authbind

    2. Configure el acceso a los puertos relevantes, por ejemplo, 80 y 443 para todos los usuarios y grupos:

      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. Ejecute su comando vía authbind
      (opcionalmente especificando --deepu otros argumentos, vea la página del manual):

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

      p.ej

      authbind --deep java -jar SomeServer.jar
      

Como seguimiento a la fabulosa (no recomendada de Joshua a menos que sepas lo que haces) recomendación para hackear el kernel:

Lo publiqué por primera vez aquí .

Simple. Con un núcleo normal o antiguo, no lo hace.
Como señalaron otros, iptables puede reenviar un puerto.
Como también señalaron otros, CAP_NET_BIND_SERVICE también puede hacer el trabajo.
Por supuesto, CAP_NET_BIND_SERVICE fallará si inicia su programa desde un script, a menos que configure el límite en el intérprete de shell, lo cual no tiene sentido, podría ejecutar su servicio como root ...
por ejemplo, para Java, debe aplicarlo a la JVM JAVA

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

Obviamente, eso significa que cualquier programa Java puede enlazar puertos del sistema.
Dito para mono / .NET.

También estoy bastante seguro de que xinetd no es la mejor de las ideas.
Pero dado que ambos métodos son hacks, ¿por qué no simplemente levantar el límite levantando la restricción?
Nadie dijo que tenía que ejecutar un núcleo normal, por lo que puede ejecutar el suyo.

Simplemente descargue la fuente del último kernel (o el mismo que tiene actualmente). Luego, vas a:

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

Ahí buscas esta línea

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

y cambiarlo a

#define PROT_SOCK 0

si no desea tener una situación ssh insegura, modifíquela a esto: #define PROT_SOCK 24

En general, usaría la configuración más baja que necesite, por ejemplo, 79 para http, o 24 cuando use SMTP en el puerto 25.

Eso ya es todo.
Compile el núcleo e instálelo.
Reiniciar.
Terminado: ese límite estúpido se ha ido, y eso también funciona para los scripts.

Así es como compila un núcleo:

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 ..

En pocas palabras, use iptables si desea mantenerse seguro, compile el kernel si desea asegurarse de que esta restricción nunca lo moleste nuevamente.

Stefan Steiger
fuente
¿Alguna razón para el voto negativo? ¿Odia la raíz o las instrucciones de compilación del núcleo no funcionaron?
Stefan Steiger
44
modificar el kernel hace que las futuras actualizaciones sean mucho más dolorosas. No haría esto, porque sé que sería demasiado vago para seguir actualizando mi kernel regularmente. Es bueno que sea posible en teoría, pero no es una opción viable en muchas circunstancias.
kritzikratzi
Quizás una forma más segura de hacerlo es cambiar chmod 777 / etc / authbind / byport / 80 por chmod 544 / etc / authbind / byport / 23 que chown login: group / etc / authbind / byport / 23
Jérôme B
18

Las capacidades de archivo no son ideales, porque pueden romperse después de una actualización del paquete.

La solución ideal, en mi humilde opinión, debería ser la capacidad de crear un shell con un CAP_NET_BIND_SERVICEconjunto heredable .

Aquí hay una forma algo complicada de hacer esto:

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

capshLa utilidad se puede encontrar en el paquete libcap2-bin en las distribuciones Debian / Ubuntu. Esto es lo que pasa:

  • sgcambia la identificación efectiva del grupo a la del usuario daemon. Esto es necesario porque capshdeja GID sin cambios y definitivamente no lo queremos.
  • Establece el bit 'mantener capacidades en cambio UID'.
  • Cambia UID a $DAEMONUSER
  • Descarta todas las mayúsculas (en este momento todas las mayúsculas siguen presentes debido a --keep=1), excepto heredablescap_net_bind_service
  • Ejecuta su comando ('-' es un separador)

El resultado es un proceso con usuario y grupo especificados, y cap_net_bind_serviceprivilegios.

Como ejemplo, una línea del ejabberdscript de inicio:

sg $EJABBERDUSER "capsh --keep=1 --uid=`id -u $EJABBERDUSER` --caps='cap_net_bind_service+pei' -- $EJABBERD --noshell -detached"
Cyberax
fuente
Y en realidad NO funciona. Las mayúsculas no se conservan sobre el interruptor de ID de usuario. Funcionará si desea ejecutarse como root, pero con todas las capacidades descartadas.
Cyberax
Si lo intento, me sale "no se puede ejecutar el archivo binario". De hecho, no puedo capshhacer otra cosa que "no puedo ejecutar el archivo binario" cuando uso las opciones --o ==. Me pregunto qué me estoy perdiendo.
Craig McQueen
@CraigMcQueen, todo después --se pasa a /bin/bash, por lo que es posible que desee probar -c 'your command'. Por desgracia, parece que estoy experimentando el mismo problema que @Cyberax, porque obtengo un "permiso denegado" cuando intento bind.
Amir
Para que esto funcione sin establecer capacidades de archivo (o ejecutar como root), que tendría que utilizar las capacidades ambientales que se describen en esta respuesta Unix.SE . También es posible usar --giden lugar de sg(o --userqué conjuntos --uid, --gidy --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

Por alguna razón, nadie menciona acerca de reducir sysctl net.ipv4.ip_unprivileged_port_start al valor que necesita. Ejemplo: necesitamos vincular nuestra aplicación al puerto 443.

sysctl net.ipv4.ip_unprivileged_port_start=443

Algunos pueden decir que existe un posible problema de seguridad: los usuarios no privilegiados ahora pueden unirse a los otros puertos privilegiados (444-1024). Pero puede resolver este problema fácilmente con iptables, bloqueando otros puertos:

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

Comparación con otros métodos. Este método:

  • desde algún punto (IMO) es aún más seguro que configurar CAP_NET_BIND_SERVICE / setuid, ya que una aplicación no se establece en absoluto, incluso en parte (las capacidades sí lo son). Por ejemplo, para capturar un núcleo de una aplicación habilitada para capacidades, deberá cambiar sysctl fs.suid_dumpable (lo que conduce a otros posibles problemas de seguridad). Además, cuando se configura CAP / suid, el directorio / proc / PID es propiedad de root, por lo que su usuario no root no tendrá información / control completo del proceso en ejecución, por ejemplo, el usuario no podrá (en el caso común) determinar qué conexiones pertenecen a la aplicación a través de / proc / PID / fd / (netstat -aptn | grep PID).
  • tiene una desventaja de seguridad: aunque su aplicación (o cualquier aplicación que use los puertos 443-1024) está inactiva por alguna razón, otra aplicación podría tomar el puerto. Pero este problema también podría aplicarse a CAP / suid (en caso de que lo configure en intérprete, por ejemplo, java / nodejs) y iptables-redirect. Utilice el método systemd-socket para excluir este problema. Utilice el método authbind para permitir solo enlaces de usuario especiales.
  • no requiere configurar CAP / suid cada vez que implementa una nueva versión de la aplicación.
  • no requiere soporte / modificación de la aplicación, como el método systemd-socket.
  • no requiere la reconstrucción del kernel (si la versión en ejecución admite esta configuración sysctl)
  • no hace LD_PRELOAD como el método authbind / privbind, esto podría afectar el rendimiento, la seguridad y el comportamiento (¿no es cierto?) En el resto, authbind es un método realmente flexible y seguro.
  • realiza el método REDIRECT / DNAT de iptables en exceso, ya que no requiere traducción de direcciones, seguimiento del estado de conexión, etc. Esto solo se nota en sistemas de alta carga.

Dependiendo de la situación, elegiría entre sysctl, CAP, authbind e iptables-redirect. Y esto es genial porque tenemos tantas opciones.

urusha
fuente
2
¡Gracias por la gran respuesta! Parece que esta funcionalidad apareció por primera vez en Linux 4.11 en abril de 2017 , por lo que no existía en 2009 cuando hice esta pregunta por primera vez. También hice una prueba rápida, y parece que también funciona para IPV6, aunque "ipv4" está en el nombre sysctl.
Jason Creighton
15

Otras dos posibilidades simples:

Hay una solución antigua (pasada de moda) para "un demonio que se une a un puerto bajo y controla las manos de su demonio". Se llama inetd (o xinetd). Los contras son:

  • su demonio necesita hablar sobre stdin / stdout (si no controla el demonio, si no tiene la fuente, entonces esto es quizás un showtopper, aunque algunos servicios pueden tener un indicador de compatibilidad inetd)
  • Se bifurca un nuevo proceso de demonio para cada conexión
  • es un eslabón extra en la cadena

Pros:

  • disponible en cualquier UNIX antiguo
  • una vez que su administrador del sistema haya configurado la configuración, puede continuar con su desarrollo (cuando reconstruye su demonio, ¿podría perder las capacidades de setcap? Y luego tendrá que volver a su administrador "por favor señor ... ")
  • daemon no tiene que preocuparse por esas cosas de redes, solo tiene que hablar sobre stdin / stdout
  • puede configurar para ejecutar su daemon como usuario no root, según lo solicitado

Otra alternativa: un proxy pirateado (netcat o incluso algo más robusto ) desde el puerto privilegiado a algún puerto arbitrario de alto número donde puede ejecutar su demonio de destino. (Netcat obviamente no es una solución de producción, sino "solo mi caja de desarrollo", ¿verdad?). De esta manera, podría continuar utilizando una versión de su servidor con capacidad de red, solo necesitaría root / sudo para iniciar el proxy (en el arranque), no dependería de capacidades complejas / potencialmente frágiles.

Martin Carpenter
fuente
1
Buena sugerencia. Por alguna razón, ni siquiera pensé en usar inetd. Excepto que el servicio en cuestión está basado en UDP, por lo que es un poco más complicado que los servicios TCP. Ahora mismo tengo una solución que funciona con setcap, pero tendré que pensarlo un poco.
Jason Creighton el
Para la segunda opción, es probable que incluso pueda iniciar el proxy usando inetd ... (Pero IPTables es probablemente una sobrecarga más baja ...)
Gert van den Berg
14

Mi "solución estándar" usa socat como redirector de espacio de usuario:

socat tcp6-listen:80,fork tcp6:8080

Tenga en cuenta que esto no escalará, la bifurcación es costosa pero es la forma en que funciona socat.

Astro
fuente
13

Linux admite capacidades para admitir permisos más específicos que simplemente "esta aplicación se ejecuta como root". Una de esas capacidades es la CAP_NET_BIND_SERVICEque se trata de vincular a un puerto privilegiado (<1024).

Desafortunadamente, no sé cómo explotar eso para ejecutar una aplicación como no root mientras la sigo dando CAP_NET_BIND_SERVICE(probablemente usando setcap, pero seguramente habrá una solución existente para esto).

Joachim Sauer
fuente
No funcionará para scripts o programas individuales JVM / mono.
Stefan Steiger
13

Sé que esta es una vieja pregunta, pero ahora con núcleos recientes (> = 4.3) finalmente hay una buena respuesta a esto: capacidades ambientales.

La respuesta rápida es tomar una copia de la última versión (aún no publicada) de libcap de git y compilarla. Copie el progs/capshbinario resultante en alguna parte ( /usr/local/bines una buena opción). Luego, como root, inicie su programa 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'

En orden, somos

  • Declarar que cuando cambiamos de usuario, queremos mantener nuestros conjuntos de capacidades actuales
  • Cambiar usuario y grupo a 'su-nombre-usuario-servicio'
  • Agregar la cap_net_bind_servicecapacidad a los conjuntos heredados y ambientales
  • Bifurcación bash -c 'your-command'(ya que capshautomáticamente comienza a golpear con los argumentos después --)

Aquí pasan muchas cosas debajo del capó.

En primer lugar, nos estamos ejecutando como root, por lo que, de forma predeterminada, obtenemos un conjunto completo de capacidades. Se incluye en esto la capacidad de cambiar uid & gid con las llamadas al sistema setuidy setgid. Sin embargo, normalmente cuando un programa hace esto, pierde su conjunto de capacidades, esto es para que la antigua forma de eliminar la raíz setuidaún funcione. El --keep=1indicador le indica capshque emita la prctl(PR_SET_KEEPCAPS)llamada al sistema, que deshabilita la pérdida de capacidades al cambiar de usuario. El cambio real de usuarios por capshocurre con la --userbandera, que se ejecuta setuidy setgid.

El siguiente problema que debemos resolver es cómo establecer capacidades de una manera que continúe después de que execnuestros hijos. El sistema de capacidades siempre ha tenido un conjunto de capacidades 'heredado', que es "un conjunto de capacidades preservadas en un execve (2)" [ capacidades (7) ]. Si bien esto suena como que resuelve nuestro problema (solo establece la cap_net_bind_servicecapacidad de heredado, ¿verdad?), Esto en realidad solo se aplica a procesos privilegiados, y nuestro proceso ya no es privilegiado, porque ya cambiamos de usuario (con la --userbandera).

El nuevo conjunto de capacidades ambientales soluciona este problema: es "un conjunto de capacidades que se conservan en un execve (2) de un programa que no tiene privilegios". Al poner cap_net_bind_serviceel entorno, cuando el capshejecutivo es nuestro programa de servidor, nuestro programa heredará esta capacidad y podrá vincular a los oyentes a puertos bajos.

Si está interesado en obtener más información, la página del manual de capacidades explica esto con gran detalle. Correr capsha través stracetambién es muy informativo!

KJ Tsanaktsidis
fuente
La --addambbandera se introdujo con libcap 2.26. Mi sistema tenía 2.25 y tuve que construir desde la fuente.
ngreen
12

TLDR: Para "la respuesta" (tal como la veo), salte a la parte >> TLDR << en esta respuesta.

De acuerdo, lo he descubierto (de verdad esta vez), la respuesta a esta pregunta, y esta respuesta mía también es una forma de disculparme por promover otra respuesta (tanto aquí como en Twitter) que pensé que era "la mejor ", pero después de intentarlo, descubrí que me había equivocado al respecto. Aprenda de mi error, niños: ¡no promocionen algo hasta que lo hayan intentado ustedes mismos!

Nuevamente, revisé todas las respuestas aquí. He probado algunos de ellos (y elegí no probar otros porque simplemente no me gustaron las soluciones). Pensé que la solución era utilizar systemdcon sus Capabilities=y CapabilitiesBindingSet=ajustes. Después de luchar con esto por un tiempo, descubrí que esta no es la solución porque:

¡Las capacidades están destinadas a restringir los procesos raíz!

Como el OP declaró sabiamente, siempre es mejor evitar eso (¡para todos tus demonios si es posible!).

No puede usar las opciones relacionadas con Capacidades con User=y Group=en systemdarchivos de unidad, porque las capacidades SIEMPRE se restablecen cuando execev(o como se llame la función). En otras palabras, cuando se systemdbifurca y cae sus permisos, las capacidades se restablecen. No hay forma de evitar esto, y toda esa lógica de enlace en el núcleo es básica en torno a uid = 0, no a las capacidades. Esto significa que es poco probable que Capacidades sea la respuesta correcta a esta pregunta (al menos en el corto plazo). Por cierto, setcapcomo otros han mencionado, no es una solución. No funcionó para mí, no funciona bien con los scripts, y estos se restablecen de todos modos cada vez que cambia el archivo.

En mi exigua defensa, dije (en el comentario que eliminé ahora), que la sugerencia de iptables de James (que también menciona el OP) era la "segunda mejor solución". :-PAGS

>> TLDR <<

La solución es combinarla systemdcon iptablescomandos sobre la marcha , como este ( tomado de 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

Aquí logramos lo siguiente:

  • El daemon escucha en 5333, pero las conexiones se aceptan con éxito en 53 gracias a iptables
  • Podemos incluir los comandos en el archivo de la unidad, y así ahorramos dolores de cabeza a las personas. systemdlimpia las reglas del firewall para nosotros, asegurándonos de eliminarlas cuando el demonio no se está ejecutando.
  • Nunca corremos como root, y hacemos imposible la escalada de privilegios (al menos lo systemdafirma), supuestamente incluso si el demonio se ve comprometido y se establece uid=0.

iptablessigue siendo, desafortunadamente, una utilidad bastante fea y difícil de usar. Si el demonio está escuchando en eth0:0lugar de eth0, por ejemplo, los comandos son ligeramente diferentes .

Greg Slepak
fuente
2
Nadie debería usar alias de estilo antiguo como eth0:0nunca más a menos que tengan una distribución de Linux realmente antigua . Han quedado en desuso durante años y eventualmente desaparecerán.
Michael Hampton
1
Creo que te refieres a OpenVZ. (SolusVM es un panel de control). Y sí, OpenVZ hace muchas cosas mal, la red es solo una de ellas.
Michael Hampton
1
No, me refiero a SolusVM. Desde / etc / network / interfaces:# Generated by SolusVM
Greg Slepak
1
Todavía no es OpenVZ ... Al menos no lo estoy usando, ni mi VPS.
Greg Slepak
77
Gracias por señalar la función de capacidades de systemd. Sin embargo, no hay necesidad de iptables complejas cuando systemd inicia el binario directamente (no un script). La configuración AmbientCapabilities=CAP_NET_BIND_SERVICEfuncionó perfectamente bien para mí en combinación con User=.
Ruud
11

systemd es un reemplazo de sysvinit que tiene la opción de lanzar un demonio con capacidades específicas. Opciones Capacidades =, CapabilityBoundingSet = en la página de manual systemd.exec (5) .

zbyszek
fuente
2
Anteriormente recomendé esta respuesta, pero después de probarla, ahora no. Vea mi respuesta para una alternativa que todavía usa systemd.
Greg Slepak
10

La redirección de puertos tenía más sentido para nosotros, pero nos encontramos con un problema en el que nuestra aplicación resolvería una URL localmente que también necesitaba ser redirigida; (eso significa que eres shindig ).

Esto también le permitirá ser redirigido al acceder a la url en la máquina local.

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
00500005
fuente
9

Soportes modernos de Linux /sbin/sysctl -w net.ipv4.ip_unprivileged_port_start=0.

Gene McCulley
fuente
8

Con systemd, solo necesita modificar ligeramente su servicio para aceptar sockets preactivados.

Más tarde puede usar systemd socket active .

No se necesitan capacidades, iptables u otros trucos.

Este es el contenido de los archivos systemd relevantes de este ejemplo de servidor http python simple

Expediente httpd-true.service

[Unit]
Description=Httpd true 

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

PrivateTmp=yes

Expediente httpd-true.socket

[Unit]
Description=HTTPD true

[Socket]
ListenStream=80

[Install]
WantedBy=default.target
j123b567
fuente
7

Al inicio:

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

Luego puede unirse al puerto al que desea reenviar.

James
fuente
no --to-portexiste? man iptablessolo menciona --to-ports(plural).
Abdull
He notado algunas diferencias y he estado saltando alrededor
James
Vea esta respuesta para saber cómo combinar esto con systemd.
Greg Slepak
3

Use la utilidad privbind : permite que una aplicación sin privilegios se una a puertos reservados.

Alexander Davydov
fuente
3

También existe la 'forma djb'. Puede usar este método para iniciar su proceso como root que se ejecuta en cualquier puerto bajo tcpserver, luego le dará el control del proceso al usuario que especifique inmediatamente después de que comience el proceso.

#!/bin/sh

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

Para obtener más información, consulte: http://thedjbway.b0llix.net/daemontools/uidgid.html

mti2935
fuente
1

Dado que el OP es solo desarrollo / prueba, las soluciones menos que elegantes pueden ser útiles:

setcap se puede usar en el intérprete de un script para otorgar capacidades a los scripts. Si setcaps en el binario del intérprete global no es aceptable, haga una copia local del binario (cualquier usuario puede hacerlo) y obtenga root para setcap en esta copia. Python2 (al menos) funciona correctamente con una copia local del intérprete en su árbol de desarrollo de scripts. No se necesita suid para que el usuario root pueda controlar a qué capacidades tienen acceso los usuarios.

Si necesita realizar un seguimiento de las actualizaciones de todo el sistema para el intérprete, utilice un script de shell como el siguiente para ejecutar su 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 $*
duanev
fuente
1

Probé el método de REDUCCIÓN DE PREROUTING de iptables. En núcleos anteriores parece que este tipo de regla no era compatible con IPv6 . Pero aparentemente ahora es compatible con ip6tables v1.4.18 y Linux kernel v3.8.

También descubrí que PREROUTING REDIRECT no funciona para conexiones iniciadas dentro de la máquina. Para trabajar para conexiones desde la máquina local, agregue también una regla de SALIDA - vea la redirección de puerto de iptables que no funciona para localhost . Por ejemplo, algo como:

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

También descubrí que PREROUTING REDIRECT también afecta los paquetes reenviados . Es decir, si la máquina también reenvía paquetes entre interfaces (por ejemplo, si actúa como un punto de acceso Wi-Fi conectado a una red Ethernet), entonces la regla de iptables también detectará las conexiones de los clientes conectados a los destinos de Internet y los redirigirá a la máquina. Eso no era lo que quería: solo quería redirigir las conexiones que se dirigían a la máquina misma. Descubrí que puedo hacer que solo afecte los paquetes dirigidos a la caja, agregando -m addrtype --dst-type LOCAL. Por ejemplo, algo como:

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

Otra posibilidad es utilizar el reenvío de puertos TCP. Por ejemplo, usando socat:

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

Sin embargo, una desventaja de ese método es que la aplicación que está escuchando en el puerto 8080 no conoce la dirección de origen de las conexiones entrantes (por ejemplo, para el registro u otros fines de identificación).

Craig McQueen
fuente
0

Respuesta en 2015 / septiembre:

ip6tables ahora es compatible con IPV6 NAT: http://www.netfilter.org/projects/iptables/files/changes-iptables-1.4.17.txt

Necesitarás el kernel 3.7+

Prueba:

[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
HVNSweeting
fuente