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 con números bajos sin tener que elevar el proceso a root:
Opción 1:Usar CAP_NET_BIND_SERVICE
para otorgar acceso de puerto de número bajo a un proceso:
Con esto, puede otorgar acceso permanente a un binario específico para enlazar a puertos con números bajos a través del setcap
comando:
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/binary
podrá vincularse a puertos con números bajos. Tenga en cuenta que debe usar setcap
en el propio binario en lugar de un enlace simbólico.
Opción 2:Usar authbind
para otorgar acceso único, con un control más preciso de usuarios/grupos/puertos:
El authbind
(página man) existe precisamente para esto.
-
Instalar
authbind
utilizando su administrador de paquetes favorito. -
Configúrelo para otorgar acceso a los puertos relevantes, p. 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
-
Ahora ejecuta tu comando a través de
authbind
(opcionalmente especificando--deep
u otros argumentos, consulte la página del manual):authbind --deep /path/to/binary command line args
Por ejemplo
authbind --deep java -jar SomeServer.jar
Hay ventajas y desventajas para los dos 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 anteriores solo admitían IPv4 (ya que originalmente escribí esto, se lanzaron versiones más nuevas con compatibilidad con IPv6).
Dale Hagglund está en el clavo. Así que solo 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 enlace el socket de escucha;
- tener otro programa pequeño, simple, fácilmente auditable que elimine privilegios, generado por el primer programa;
- tener la carne del servicio, en un tercero separado ejecutar bajo una cuenta que no sea de superusuario y la cadena cargada 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 que se lee 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 de alto riesgo. Las partes que se abren, bind()
y listen()
, e incluso (hasta cierto punto) la parte que accepts()
, no son de alto riesgo y se pueden ejecutar bajo la égida del superusuario. No usan ni actúan (con la 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 antiguo "superservidor de red" inetd
Haz 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 eliminación de privilegios en dos programas separados, pequeños y fácilmente auditables, pero separa el código del servicio principal en un programa separado, exec()
editado en un proceso de servicio que genera con un descriptor de archivo abierto para el socket.
La dificultad de auditar no es un gran problema, ya que uno solo tiene que auditar un programa. inetd
El principal problema de no es auditar tanto, sino que no proporciona un control de servicio de tiempo de ejecución simple y detallado, 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. También se puede utilizar el conjunto de herramientas daemontools-encore de Bruce Guenter, en gran parte equivalente.
El programa para abrir el descriptor de archivo de socket y enlazar con el puerto local privilegiado es tcpserver
, de UCSPI-TCP. Hace tanto el listen()
y el accept()
.
tcpserver
luego genera un programa de servicio que elimina los privilegios de root (porque el protocolo que se sirve implica comenzar como superusuario y luego "iniciar sesión", como es el caso, por ejemplo, con un demonio FTP o SSH) o setuidgid
que es un programa autónomo pequeño y fácilmente auditable que solo elimina privilegios y luego encadena cargas al programa de servicio propiamente dicho (ninguna parte del cual se ejecuta con privilegios de superusuario, como es el caso de, digamos, qmail-smtpd
).
Un servicio run
el script 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
comer
Mi paquete nosh está diseñado para hacer esto. Tiene un pequeño setuidgid
utilidad, al igual que los demás. Una pequeña diferencia es que se puede usar con systemd
-estilo de servicios "LISTEN_FDS" así como con servicios UCSPI-TCP, por lo que el tradicional tcpserver
el programa es reemplazado por dos programas separados:tcp-socket-listen
y tcp-socket-accept
.
Nuevamente, las utilidades de un solo propósito se generan y se cargan en cadena entre sí. Una peculiaridad interesante del diseño es que uno puede eliminar los privilegios de superusuario después de listen()
pero antes incluso accept()
. Aquí hay un run
secuencia de comandos 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 en cadena independientes del servicio fdmove
, clearenv
, envdir
, softlimit
, tcp-socket-listen
y setuidgid
. Por el punto que sh
se inicia, el socket está abierto y vinculado al smtp
puerto, y el proceso ya no tiene privilegios de superusuario.
s6, s6-redes y execline
Los paquetes de red s6 y s6 de Laurent Bercot fueron diseñados para hacer esto en conjunto. Los comandos son estructuralmente muy similares a los de daemontools
y UCSPI-TCP.
run
los scripts serían muy parecidos, excepto por la sustitución de s6-tcpserver
para tcpserver
y s6-setuidgid
para setuidgid
. Sin embargo, también se puede optar por hacer uso del 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 usa 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
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 chpst
y tcpsvd
esta vez, pero hacen lo mismo, y el código de alto riesgo que lee, procesa y escribe cosas enviadas a través de la red por clientes que no son de confianza todavía está en un programa separado.
Aquí está el ejemplo de M. Pape de ejecutar fnord
en un run
guión:
#!/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 e inicio de servicios que se puede encontrar en algunas distribuciones de Linux, pretende hacer lo que inetd
puede hacer. Sin embargo, no utiliza un conjunto de pequeños programas autónomos. Uno tiene que auditar systemd
en su totalidad, por desgracia.
Con systemd
uno crea archivos de configuración para definir un socket que systemd
escucha y un servicio que systemd
empieza. El archivo de "unidad" del servicio tiene configuraciones que permiten un gran control sobre el proceso del servicio, incluido el usuario con el que se ejecuta.
Con ese usuario configurado como no superusuario, systemd
hace todo el trabajo de abrir el socket, vincularlo a un puerto y llamar a listen()
(y, si es necesario, accept()
) en el proceso #1 como superusuario, y el proceso de servicio que genera se ejecuta sin privilegios de superusuario.
Tengo un enfoque bastante diferente. Quería usar el puerto 80 para un servidor node.js. No pude hacerlo porque se instaló Node.js para un usuario que no es sudo. Traté de usar enlaces simbólicos, pero no funcionó para mí.
Luego me di cuenta de que puedo reenviar 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 usar para hacer esto. Aquí están los comandos:
host local/bucle invertido
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 a los puertos inferiores directamente, sino darles acceso mediante el reenvío de puertos.