¿Permitir que el proceso no root se una al puerto 80 y 443?

104

¿Es posible ajustar un parámetro del kernel para permitir que un programa userland se una al puerto 80 y 443?

La razón por la que pregunto es que creo que es una tontería permitir que un proceso privilegiado abra un socket y escuche. Cualquier cosa que abra un socket y escuche es de alto riesgo, y las aplicaciones de alto riesgo no deberían ejecutarse como root.

Prefiero tratar de averiguar qué proceso no privilegiado está escuchando en el puerto 80 en lugar de tratar de eliminar el malware que se ha enterrado con privilegios de root.

jww
fuente
1
Consulte serverfault.com/questions/268099 y stackoverflow.com/questions/413807 . La respuesta corta es no.
Sami Laine
10
La respuesta larga es sí ... así que la respuesta corta debería ser sí también.
BT
44
La respuesta corta es sí.
Jason C

Respuestas:

163

No estoy seguro de a qué se refieren las otras respuestas y comentarios aquí. Esto es posible con bastante facilidad. Hay dos opciones, ambas que permiten el acceso a puertos de bajo número sin tener que elevar el proceso a la raíz:

Opción 1: se utiliza CAP_NET_BIND_SERVICEpara otorgar acceso de puerto de bajo número a un proceso:

Con esto puede otorgar acceso permanente a un binario específico para enlazar a puertos de bajo número a través del setcapcomando:

sudo setcap CAP_NET_BIND_SERVICE=+eip /path/to/binary

Para obtener más detalles sobre la parte e / i / p, consulte cap_from_text.

Después de hacer esto, /path/to/binarypodrá vincularse a puertos de números bajos. Tenga en cuenta que debe usar setcapel binario en lugar de un enlace simbólico.

Opción 2: use authbindpara otorgar acceso de una sola vez, con un control más fino de usuario / grupo / puerto

La herramienta authbind( página de manual ) existe precisamente para esto.

  1. Instale authbindusando su administrador de paquetes favorito.

  2. Configúrelo para otorgar acceso a los puertos relevantes, por ejemplo, para permitir 80 y 443 de 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. Ahora 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
    

Hay ventajas y desventajas de los dos anteriores. La opción 1 otorga confianza al binario pero no proporciona control sobre el acceso por puerto. La opción 2 otorga confianza al usuario / grupo y proporciona control sobre el acceso por puerto pero, AFAIK, solo admite IPv4.

Jason C
fuente
¿Realmente necesita rwxpermiso?
mate
Para revertir la operación en la Opción 1, ¿volvería a ejecutar el comando usando -pintead of +eip?
eugene1832
55
Tenga en cuenta que, con setcap, si sobrescribe el ejecutable al que le otorga privilegios (por ejemplo, hacer una reconstrucción), pierde su estado de puerto privilegiado y debe otorgarle privilegios nuevamente: |
rogerdpack
1
Algo con lo que tuve que jugar; Estaba tratando de ejecutar un servicio sysv, que ejecuta un ejecutable ruby ​​que usa ruby. Debe otorgar el setcappermiso sobre el ejecutable ruby ​​específico de la versión , por ejemplo/usr/bin/ruby1.9.1
Christian Rondeau el
3
Tengo mis dudas de que chmodusar 777 byportarchivos sea la mejor idea. He visto dar permisos que van desde 500hasta 744. Me quedaría con el más restrictivo que funcione para ti.
Pere
28

Dale Hagglund es perfecto. Así que voy a decir lo mismo pero de una manera diferente, con algunos detalles y ejemplos. ☺

Lo correcto en los mundos de Unix y Linux es:

  • tener un programa pequeño, simple y fácilmente auditable que se ejecute como superusuario y se una a la toma de escucha;
  • tener otro programa pequeño, simple, fácilmente auditable, que elimine privilegios, generado por el primer programa;
  • para que la carne del servicio, en un tercer programa separado , se ejecute bajo una cuenta que no sea de superusuario y se cargue en cadena por el segundo programa, esperando simplemente heredar un descriptor de archivo abierto para el socket.

Tienes una idea equivocada de dónde está el alto riesgo. El alto riesgo está en leer desde la red y actuar sobre lo que se lee, no en los simples actos de abrir un socket, vincularlo a un puerto y llamar listen(). La parte de un servicio que hace la comunicación real es el alto riesgo. Las partes que se abren, bind()y listen(), e incluso (en una medida) la parte que accepts(), no son los de alto riesgo y se pueden ejecutar bajo los auspicios de la superusuario. No utilizan ni actúan sobre (a excepción de las direcciones IP de origen en el accept()caso) datos que están bajo el control de extraños no confiables a través de la red.

Hay muchas maneras de hacer esto.

inetd

Como dice Dale Hagglund, el viejo "servidor de red" inetdhace esto. La cuenta bajo la cual se ejecuta el proceso de servicio es una de las columnas en inetd.conf. No separa la parte de escucha y la parte de privilegios de caída en dos programas separados, pequeños y fácilmente auditables, pero separa el código de servicio principal en un programa separado, exec()editado en un proceso de servicio que genera con un descriptor de archivo abierto para el zócalo

La dificultad de auditar no es un gran problema, ya que uno solo tiene que auditar un programa. inetdEl principal problema no es auditar tanto, sino que no proporciona un control de servicio de tiempo de ejecución simple, en comparación con las herramientas más recientes.

UCSPI-TCP y daemontools

Los paquetes UCSPI-TCP y daemontools de Daniel J. Bernstein fueron diseñados para hacer esto en conjunto. Alternativamente, se puede usar el conjunto de herramientas daemontools-encore en gran medida equivalente de Bruce Guenter .

El programa para abrir el descriptor del archivo de socket y enlazar al puerto local privilegiado es tcpserver, desde UCSPI-TCP. Hace tanto el listen()como el accept().

tcpserverluego genera un programa de servicio que elimina los privilegios de raíz (porque el protocolo que se está sirviendo implica comenzar como superusuario y luego "iniciar sesión", como es el caso, por ejemplo, de un demonio FTP o SSH) o setuidgidque es un programa autónomo pequeño y fácilmente auditable que solo elimina privilegios y luego encadena las cargas al programa de servicio propiamente dicho (ninguna parte se ejecuta con privilegios de superusuario, como es el caso, por ejemplo qmail-smtpd).

Un runscript de servicio sería, por ejemplo, (este para dummyidentd para proporcionar un servicio IDENT nulo):

#!/bin/sh -e
exec 2>&1
exec \
tcpserver 0 113 \
setuidgid nobody \
dummyidentd.pl

comida

Mi paquete nosh está diseñado para hacer esto. Tiene una pequeña setuidgidutilidad, al igual que las demás. Una ligera diferencia es que se puede usar con los systemdservicios "LISTEN_FDS" de estilo, así como con los servicios UCSPI-TCP, por lo que el tcpserverprograma tradicional se reemplaza por dos programas separados: tcp-socket-listeny tcp-socket-accept.

Nuevamente, las utilidades de un solo propósito se generan y se encadenan entre sí. Una peculiaridad interesante del diseño es que uno puede abandonar los privilegios de superusuario después listen()pero incluso antes accept(). Aquí hay un runscript para qmail-smtpdeso que hace exactamente eso:

#!/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 \
'

Los programas que se ejecutan bajo los auspicios del superusuario son las pequeñas herramientas de la cadena de carga de servicio agnóstica fdmove, clearenv, envdir, softlimit, tcp-socket-listen, y setuidgid. En el momento en que shse inicia, el socket está abierto y vinculado al smtppuerto, y el proceso ya no tiene privilegios de superusuario.

s6, s6-networking y execline

Los paquetes de redes s6 y s6 de Laurent Bercot fueron diseñados para hacer esto en conjunto. Los comandos son estructuralmente muy similares a los de daemontoolsUCSPI-TCP.

runlos guiones serían muy parecidos, excepto por la sustitución de s6-tcpserverfor tcpservery s6-setuidgidfor setuidgid. Sin embargo, uno también podría optar por utilizar el conjunto de herramientas execlinas de M. Bercot al mismo tiempo.

Aquí hay un ejemplo de un servicio FTP, ligeramente modificado del original de Wayne Marshall , que utiliza execline, s6, s6-networking y el programa de servidor FTP de 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

El ipsvd de Gerrit Pape es otro conjunto de herramientas que se ejecuta en la misma línea que ucspi-tcp y s6-networking. Las herramientas son chpsty tcpsvdesta vez, pero hacen lo mismo, y el código de alto riesgo que hace la lectura, el procesamiento y la escritura de cosas enviadas a través de la red por clientes no confiables todavía está en un programa separado.

Aquí está el ejemplo de M. Pape de ejecutar fnorden un runscript:

#!/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, el nuevo sistema de supervisión de servicios e init que se puede encontrar en algunas distribuciones de Linux está destinado a hacer lo que inetdpuede hacer . Sin embargo, no utiliza un conjunto de pequeños programas independientes. Uno tiene que auditar systemden su totalidad, desafortunadamente.

Con systemduno crea archivos de configuración para definir un socket que systemdescucha y un servicio que se systemdinicia. El archivo de "unidad" de servicio tiene configuraciones que le permiten a uno tener un gran control sobre el proceso de servicio, incluido el usuario con el que se ejecuta.

Con ese usuario configurado para no ser un superusuario, systemdrealiza todo el trabajo de abrir el socket, vincularlo a un puerto y llamar listen()(y, si es necesario accept()) , en el proceso n. ° 1 como superusuario, y el proceso de servicio que genera carreras sin privilegios de superusuario.

JdeBP
fuente
2
Gracias por el cumplido. Esta es una gran colección de consejos concretos. +1.
Dale Hagglund
11

Tengo un enfoque bastante diferente. Quería usar el puerto 80 para un servidor node.js. No pude hacerlo porque Node.js se instaló para un usuario que no es sudo. Traté de usar enlaces simbólicos, pero no funcionó para mí.

Entonces llegué a saber que puedo reenviar conexiones de un puerto a otro puerto. Así que inicié el servidor en el puerto 3000 y configuré un puerto hacia adelante desde el puerto 80 al puerto 3000.

Este enlace proporciona los comandos reales que se pueden usar para hacer esto. Aquí están los comandos:

localhost / loopback

sudo iptables -t nat -I OUTPUT -p tcp -d 127.0.0.1 --dport 80 -j REDIRECT --to-ports 3000

externo

sudo iptables -t nat -I PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 3000

He usado el segundo comando y funcionó para mí. Así que creo que este es un término medio para no permitir que el proceso de usuario acceda directamente a los puertos inferiores, sino que les dé acceso mediante el reenvío de puertos.

novato
fuente
66
+1 por pensar fuera de la caja
Richard Wiseman
4

Sus instintos son completamente correctos: es una mala idea tener un gran programa complejo ejecutado como root, porque su complejidad hace que sea difícil confiar en ellos.

Pero, también es una mala idea permitir que los usuarios regulares se unan a puertos privilegiados, porque tales puertos generalmente representan servicios importantes del sistema.

El enfoque estándar para resolver esta aparente contradicción es la separación de privilegios . La idea básica es separar su programa en dos (o más) partes, cada una de las cuales hace una parte bien definida de la aplicación general, y que se comunican mediante interfaces limitadas simples.

En el ejemplo que da, desea separar su programa en dos partes. Uno que se ejecuta como root y se abre y se une al socket privilegiado, y luego lo pasa de alguna manera a la otra parte, que se ejecuta como un usuario normal.

Estas dos formas principales de lograr esta separación.

  1. Un único programa que comienza como root. Lo primero que hace es crear el socket necesario, de la manera más simple y limitada posible. Luego, elimina los privilegios, es decir, se convierte en un proceso de modo de usuario normal y realiza el resto del trabajo. Dejar los privilegios correctamente es complicado, así que tómese el tiempo para estudiar la forma correcta de hacerlo.

  2. Un par de programas que se comunican a través de un par de socket creado por un proceso padre. Un programa de controlador no privilegiado recibe argumentos iniciales y quizás realiza una validación básica de argumentos. Crea un par de sockets conectados a través de socketpair (), y luego bifurca y ejecuta otros dos programas que harán el trabajo real y se comunicarán a través del par de sockets. Uno de estos tiene privilegios y creará el socket del servidor, y cualquier otra operación privilegiada, y el otro hará la ejecución de aplicaciones más compleja y, por lo tanto, menos confiable.

[1] http://en.m.wikipedia.org/wiki/Privilege_separation

Dale Hagglund
fuente
Lo que su propuesta no se considera mejor práctica. Puede mirar inetd, que puede escuchar en un socket privilegiado y luego pasar ese socket a un programa no privilegiado.
Dale Hagglund
3

La solución más simple: eliminar todos los puertos privilegiados en Linux

Funciona en ubuntu / debian:

#save configuration permanently
echo 'net.ipv4.ip_unprivileged_port_start=0' > /etc/sysctl.d/50-unprivileged-ports.conf
#apply conf
sysctl --system

(funciona bien para VirtualBox con cuenta no root)

¡Ahora, tenga cuidado con la seguridad porque todos los usuarios pueden vincular todos los puertos!

soleuu
fuente
Eso es inteligente. Un pequeño inconveniente: la configuración abre 80 y 443, pero también abre todos los demás puertos. Es posible que no se deseen permisos relajantes en otros puertos.
jww