Usuario del software
2014-02-02 05:48:05 +0000 2014-02-02 05:48:05 +0000
117

¿Permitir que los procesos que no son root se enlacen al puerto 80 y 443?

¿Es posible ajustar un parámetro del kernel para permitir que un programa de tierra de usuario 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 sin privilegios está escuchando en el puerto 80 en lugar de tratar de eliminar el malware que se metió con privilegios de root.

Respuestas [5]

176
2015-03-21 21:12:41 +0000

No estoy seguro de a qué se refieren las otras respuestas y comentarios aquí. Esto es posible con bastante facilidad. Hay dos opciones, ambas permiten el acceso a puertos de baja numeración sin tener que elevar el proceso a root:

Opción 1: Usar CAP_NET_BIND_SERVICE para conceder acceso a puertos de baja numeración a un proceso:

Con esto puede conceder acceso permanente a un binario específico para enlazar con puertos de baja numeración a través del comando setcap:

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

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

Después de hacer esto, /path/to/binary será capaz de enlazar con puertos de baja numeración. Tenga en cuenta que debe utilizar setcap en el propio binario en lugar de un enlace simbólico.

Opción 2: Usar authbind para conceder acceso de una sola vez, con un control más fino de usuario/grupo/puerto:

La herramienta authbind página man ) existe precisamente para esto.

  1. Instale authbind utilizando su gestor de paquetes favorito.

  2. Configúralo para que permita el acceso a los puertos pertinentes, por ejemplo, para permitir 80 y 443 de todos los usuarios y grupos:

  3. Ahora ejecute su comando a través de authbind (opcionalmente especificando --deep u otros argumentos, vea la página man):


Hay ventajas y desventajas en las dos opciones anteriores. La opción 1 otorga confianza al binario pero no proporciona ningún control sobre el acceso por puerto. La opción 2 otorga confianza al usuario/grupo y proporciona control sobre el acceso por puerto, pero las versiones más antiguas sólo soportan IPv4 (desde que escribí esto originalmente, se publicaron versiones más nuevas con soporte para IPv6).

176
29
2014-02-02 16:21:39 +0000

Dale Hagglund da en el clavo. Así que voy a decir lo mismo pero de una manera diferente, con algunos detalles y ejemplos. ☺

Lo correcto en el mundo Unix y Linux es:

  • tener un programa pequeño, sencillo, fácilmente auditable, que se ejecute como superusuario y enlace el socket de escucha;
  • tener otro programa pequeño, sencillo, fácilmente auditable, que baje privilegios, engendrado por el primer programa;
  • tener la carne del servicio, en un tercer programa separado, ejecutado bajo una cuenta de no superusuario y cargado 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 de la red y actuar sobre lo leído no en los simples actos de abrir un socket, vincularlo a un puerto, y llamar a listen(). Es la parte de un servicio que hace la comunicación real que es el alto riesgo. Las partes que abren, bind(), y listen(), e incluso (hasta cierto punto) la parte que accepts(), no son el alto riesgo y pueden ejecutarse bajo la égida del superusuario. No utilizan ni actúan sobre (con la excepción de las direcciones IP de origen en el caso de accept()) 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 "superservidor de red" inetd hace 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 la escucha y la parte de la caída de privilegios en dos programas separados, pequeños y fácilmente auditables, pero sí separa el código principal del servicio en un programa separado, exec()ed en un proceso de servicio que genera con un descriptor de archivo abierto para el socket.

La dificultad de la auditoría no es un gran problema, ya que uno sólo tiene que auditar el programa. El mayor problema de inetd no es tanto la auditoría, sino que no proporciona un control de servicios en tiempo de ejecución sencillo y detallado, en comparación con otras 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. También se puede utilizar el conjunto de herramientas daemontools-encore de Bruce Guenter, en gran medida equivalente.

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

tcpserver entonces genera un programa de servicio que deja caer los privilegios de root por sí mismo (porque el protocolo que se está sirviendo implica comenzar como el superusuario y luego "iniciar sesión", como es el caso de, por ejemplo, un FTP o un demonio SSH) o setuidgid que es un pequeño programa autocontenido y fácilmente auditable que únicamente pierde privilegios y luego carga en cadena al programa de servicio propiamente dicho (ninguna parte del cual se ejecuta con privilegios de superusuario, como es el caso de, por ejemplo, qmail-smtpd ).

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

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

nosh

Mi paquete nosh está diseñado para hacer esto. Tiene una pequeña utilidad setuidgid, igual que las otras. Una ligera diferencia es que es utilizable con servicios "LISTEN_FDS" del estilo de systemd así como con servicios UCSPI-TCP, por lo que el programa tradicional tcpserver es reemplazado por dos programas separados: tcp-socket-listen y tcp-socket-accept.

De nuevo, las utilidades de propósito único generan y cargan en cadena unas con otras. Una peculiaridad interesante del diseño es que uno puede dejar los privilegios de superusuario después de listen() pero antes incluso de accept(). Aquí hay un script de run para qmail-smtpd que de hecho 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 la égida del superusuario son las pequeñas herramientas de carga de cadenas agnósticas fdmove, clearenv, envdir, softlimit y tcp-socket-listen. En el momento en que se inicia setuidgid, el socket está abierto y vinculado al puerto sh, y el proceso ya no tiene privilegios de superusuario.

s6, s6-networking, y execline

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

daemontools serían muy parecidos, excepto por la sustitución de run por s6-tcpserver y tcpserver por s6-setuidgid. Sin embargo, también se podría optar por utilizar el conjunto de herramientas execline 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 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 setuidgid y chpst esta vez, pero hacen lo mismo, y el código de alto riesgo que hace la lectura, el procesamiento y la escritura de cosas enviadas por la red por clientes no confiables sigue estando en un programa separado.

Aquí está el ejemplo de M. Pape de ejecutar tcpsvd en un script fnord:

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

run

systemd , el nuevo sistema de supervisión de servicios e init que puede encontrarse en algunas distribuciones de Linux, pretende hacer lo que systemd puede hacer . Sin embargo, no utiliza un conjunto de pequeños programas autocontenidos. Uno tiene que auditar inetd en su totalidad, desafortunadamente.

Con systemd uno crea archivos de configuración para definir un socket en el que systemd escucha, y un servicio que systemd inicia. El archivo de la "unidad" de servicio tiene ajustes que permiten un gran control sobre el proceso de servicio, incluyendo el usuario con el que se ejecuta.

Con ese usuario configurado para ser un no-superusuario, systemd hace todo el trabajo de abrir el socket, vincularlo a un puerto, y llamar a systemd (y, si es necesario, a listen()) en el proceso #1 como superusuario, y el proceso de servicio que genera se ejecuta sin privilegios de superusuario.

29
17
2018-06-27 07:00:56 +0000

Tengo un enfoque bastante diferente. Quería utilizar el puerto 80 para un servidor node.js. No pude hacerlo ya que Node.js fue instalado para un usuario no-sudo. Intenté usar symlinks, pero no me funcionó.

Entonces me enteré de que puedo reenviar las conexiones de un puerto a otro puerto. Así que inicié el servidor en el puerto 3000 y configuré un reenvío de puerto desde el puerto 80 al puerto 3000. Este enlace proporciona los comandos reales que se pueden utilizar 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

external

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

He utilizado el segundo comando y me ha funcionado. Así que creo que este es un punto intermedio para no permitir que el proceso del usuario acceda a los puertos inferiores directamente, pero dándoles acceso usando el reenvío de puertos.

17
4
2014-02-02 06:49:22 +0000

Tus instintos son totalmente 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 a los usuarios normales enlazar con puertos privilegiados, porque dichos puertos suelen representar importantes servicios 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 simples interfaces limitadas.

En el ejemplo que das, quieres separar tu programa en dos partes. Una que se ejecuta como root y abre y se enlaza al socket privilegiado, y luego se lo pasa de alguna manera a la otra parte, que se ejecuta como un usuario normal.

Estas son las dos formas principales de lograr esta separación.

  1. Un solo programa que se inicia como root. Lo primero que hace es crear el socket necesario, de la manera más simple y limitada posible. Luego, baja los privilegios, es decir, se convierte en un proceso en modo de usuario normal, y hace todo el resto del trabajo. La caída de privilegios correctamente es complicada, así que tómate el tiempo para estudiar la forma correcta de hacerlo.

  2. Un par de programas que se comunican a través de un par de sockets creado por un proceso padre. Un programa controlador sin privilegios recibe los argumentos iniciales y quizás hace alguna 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 ellos es privilegiado y creará el socket del servidor, y cualquier otra operación privilegiada, y el otro hará la ejecución de la aplicación más compleja y por lo tanto menos confiable.

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

4
3
2019-09-13 07:38:46 +0000

¡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 una cuenta no-root)

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

3