¿Cómo ejecutar un servidor en el puerto 80 como usuario normal en Linux?

311

Busqué en Google una solución durante bastante tiempo, pero no pude encontrar una respuesta.

Estoy en Ubuntu Linux y quiero ejecutar un servidor en el puerto 80, pero debido al mecanismo de seguridad de Ubuntu, aparece el siguiente error:

java.net.BindException: Permiso denegado: 80

Creo que debería ser lo suficientemente simple como deshabilitar este mecanismo de seguridad para que el puerto 80 esté disponible para todos los usuarios o para asignar los privilegios necesarios al usuario actual para acceder al puerto 80.

Deepak Mittal
fuente
3
¿Cuál es el problema para ejecutar el servidor en otro puerto que no tiene privilegios? Está pensando en algo tan duro como deshabilitar el mecanismo de seguridad sin proporcionar al menos una razón muy seria para ejecutar un servidor en ese puerto. ¿El servidor está codificado para unirse al puerto 80? Si es así, tíralo a la basura.
Anónimo
44
No puedes Los puertos por debajo de 1024 tienen privilegios y solo el root puede abrir sockets de escucha en ellos. Lo apropiado es eliminar los permisos después de abrirlo.
Falcon Momot
2
"Anónimo": los usuarios en el mundo han sido entrenados para buscar ciertos servicios en ciertos puertos. En algunos casos, se ha estandarizado. Por ejemplo, HTTP en el puerto 80 y HTTPS en el puerto 443. Es un poco difícil cambiar los usuarios y estándares del mundo.
1
@FalconMomot Si los puertos por debajo de 1024 tienen privilegios, ¿por qué el servidor Apache no requiere que uno ingrese una contraseña para obtener privilegios de root en el puerto 80, y cómo lo hacen de esa manera? Creo que el interlocutor quiere saber cómo hacer las cosas de esa manera (si técnicamente se usa como raíz o no). No dudes en decirme si me equivoco, Deepak Mittal.
Shule

Respuestas:

355

Respuesta corta: no puedes. Los puertos por debajo de 1024 solo se pueden abrir por root. Según el comentario, bueno, puede hacerlo , usando CAP_NET_BIND_SERVICE , pero ese enfoque, aplicado a java bin hará que cualquier programa java se ejecute con esta configuración, lo que es indeseable, si no es un riesgo de seguridad.

La respuesta larga: puede redirigir las conexiones en el puerto 80 a algún otro puerto que pueda abrir como usuario normal.

Ejecutar como root:

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

Como los dispositivos de bucle invertido (como localhost) no usan las reglas de enrutamiento previo, si necesita usar localhost, etc., agregue esta regla también ( gracias @Francesco ):

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

NOTA: La solución anterior no es adecuada para sistemas multiusuario, ya que cualquier usuario puede abrir el puerto 8080 (o cualquier otro puerto alto que decida usar), interceptando así el tráfico. (Créditos a CesarB ).

EDITAR: según la pregunta de comentario - para eliminar la regla anterior:

# iptables -t nat --line-numbers -n -L

Esto generará algo como:

Chain PREROUTING (policy ACCEPT)
num  target     prot opt source               destination         
1    REDIRECT   tcp  --  0.0.0.0/0            0.0.0.0/0           tcp dpt:8080 redir ports 8088
2    REDIRECT   tcp  --  0.0.0.0/0            0.0.0.0/0           tcp dpt:80 redir ports 8080

La regla que le interesa es nr. 2, para eliminarlo:

# iptables -t nat -D PREROUTING 2
Soleado
fuente
13
@Sunny: los votos a favor no son para el arma más rápida del oeste, sino para las mejores respuestas. El suyo es el mejor hasta ahora (solo mencioné iptables; en realidad proporcionó la línea de comando completa). Lo único que tiene el mío no es la advertencia de que otros usuarios también pueden unirse al puerto 8080.
CesarB
3
Tenga en cuenta que esto no funciona con IPv6.
Emmanuel Bourg el
3
¿Alguien podría explicar cómo puedo eliminar esta regla más tarde cuando lo desee? Después de ejecutarlo, funciona, pero aparentemente no es una "regla" porque no aparece cuando lo hago sudo iptables --list. Sé lo que es y hace iptables, pero nunca lo he usado antes.
Codificador
44
Gracias por su respuesta ... cada vez que reinicio Ubuntu, esta regla desaparece y tengo que ejecutarla nuevamente. ¿Hay alguna manera de guardarlo para siempre?
Coderji
44
@Coderji: consulte la sección "guardar" en la documentación de la comunidad: help.ubuntu.com/community/IptablesHowTo
Sunny
79

Usa authbind .

Incluso funciona con Java si habilita la pila solo de Java IPv4. Yo suelo:

authbind --deep $JAVA_HOME/bin/java -Djava.net.preferIPv4Stack=true …
geocar
fuente
44
Si el servidor es Tomcat, puede usar authbind automáticamente configurando AUTHBIND=yes/ etc / default / tomcat6
Emmanuel Bourg el
No pude hacer que esto funcione en el servidor Ubuntu con el paquete Java predeterminado ...
Ashley
Yo tampoco, alguna solución conocida?
marca el
10
Tenga en cuenta que debe configurar authbindpara permitir que esto suceda. Desde la página de manual: "/ etc / authbind / byport / port se prueba. Si este archivo es accesible para la ejecución del usuario que realiza la llamada, de acuerdo con access (2), se autoriza el enlace al puerto". , Por ejemplo para el puerto 80, sudo touch /etc/authbind/byport/80 ; sudo chmod 777 /etc/authbind/byport/80. La instalación inicial de authbindgeneralmente no tiene ninguna autorización preconfigurada.
Jason C
44
¡Oh, nonononononono, nunca jamás chmod 777 nada!
Johannes
57

Si su sistema lo admite, tal vez podría usar capacidades. Vea las capacidades de man, la que necesita sería CAP_NET_BIND_SERVICE.

En las versiones más recientes de Debian / Ubuntu puedes ejecutar:

sudo apt-get install libcap2-bin 
sudo setcap 'cap_net_bind_service=+ep' /path/to/program
Otto Kekäläinen
fuente
esto funciona para nodejs: setcap 'cap_net_bind_service = + ep' / usr / bin / nodejs
JasonS
55
Esta. Me pregunto por qué esta respuesta no tiene más votos a favor. Mucho más fácil que la opción iptables en mi humilde opinión.
Dominik R
55
Esta es la respuesta correcta y más eficiente, todas las otras respuestas causan un impacto en el rendimiento o simplemente dudoso / inseguro.
OneOfOne
41

Otra solución es hacer que su aplicación sea setuid para que pueda enlazarse con el puerto 80. Como root, haga lo siguiente

chown root ./myapp
chmod +S ./myapp

Tenga en cuenta que hacer esto, a menos que se haga de manera absolutamente correcta, lo expondrá a posibles agujeros de seguridad, porque su aplicación estará hablando con la red y se ejecutará con privilegios de root completos. Si toma esta solución, debe mirar el código fuente de Apache o Lighttpd o algo similar, donde usan los privilegios de root para abrir el puerto, pero luego renuncian inmediatamente a esos privilegios y "se convierten" en un usuario con menos privilegios para que un secuestrador no puede hacerse cargo de toda su computadora.

Actualización: Como se ve en esta pregunta , parece que los núcleos de Linux desde 2.6.24 tienen una nueva capacidad que le permite marcar un ejecutable (pero no un script, por supuesto) que tiene la CAP_NET_BIND_SERVICEcapacidad " ". Si instala el paquete debian "libcap2-bin", puede hacerlo emitiendo el comando

setcap 'cap_net_bind_service=+ep' /path/to/program
Paul Tomblin
fuente
66
Es lo mismo que ejecutar como root, a menos que la aplicación sepa cómo quitar privilegios.
CesarB
14
Esto es peligroso. Esto significa que cualquier solicitud se ejecutará como root. Es por una razón que incluso apache comienza como root para enlazar, y luego deja caer los privilegios a otro usuario.
Soleado
99
Paul: iptables no es tan peligroso, porque incluso si la aplicación se ve comprometida, no expondrá el sistema a los ataques, al menos no con privilegios de root. Ejecutar la aplicación como root es otra historia.
Soleado
1
El peligro para iptables es solo si este es un sistema multiusuario, como dijo CesarB, ya que cualquiera puede unirse al 8080 e interceptar el tráfico.
Soleado
1
lol, me encanta cómo la respuesta aceptada tiene un -15
Evan Teran
40

Simplemente uso Nginx en frente. También puede ejecutarse en localhost.

  • apt-get install nginx

.. o ..

  • pkg_add -r nginx

.. o lo que mejor se adapte a su sistema operativo.

Todo lo que necesita en nginx.conf, si se ejecuta en localhost, es:

servidor {
        escucha 80;
        nombre_servidor some.domain.org;
        ubicación / {
            proxy_set_header Host $ host;
            proxy_set_header X-Real-IP $ remote_addr;
            proxy_set_header X-Fordered-For $ proxy_add_x_forwards_for;
            proxy_pass http://127.0.0.1:8081;
        }
}
CosmicB
fuente
Realmente me gusta esta solución.
thomasfedb
1
Esta es (una de) la solución sugerida por los propios JFrog
stolsvik
36

Enfoque propuesto por Sunny y CesarB:

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

funciona bien pero tiene un pequeño inconveniente: no impide que el usuario se conecte directamente al puerto 8080 en lugar de 80.

Considere el siguiente escenario cuando esto puede ser un problema.

Digamos que tenemos un servidor que acepta conexiones HTTP en el puerto 8080 y conexiones HTTPS en el puerto 8181.

Utilizamos iptables para establecer las siguientes redirecciones:

80  ---> 8080
443 ---> 8181

Ahora, supongamos que nuestro servidor decide redirigir al usuario desde una página HTTP a una página HTTPS. A menos que reescribamos cuidadosamente la respuesta, se redirigirá a https://host:8181/. En este punto, estamos jodidos:

  • Algunos usuarios marcarían la https://host:8181/URL y deberíamos mantener esta URL para evitar romper sus marcadores.
  • Otros usuarios no podrán conectarse porque sus servidores proxy no admiten puertos SSL no estándar.

Yo uso el siguiente enfoque:

iptables -t mangle -A PREROUTING -p tcp --dport 80 -j MARK --set-mark 1
iptables -t mangle -A PREROUTING -p tcp --dport 443 -j MARK --set-mark 1
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080
iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 8181
iptables -I INPUT -m state --state NEW -m tcp -p tcp --dport 8080 -m mark --mark 1 -j ACCEPT
iptables -I INPUT -m state --state NEW -m tcp -p tcp --dport 8181 -m mark --mark 1 -j ACCEPT

Combinado con la regla de RECHAZO predeterminada en la cadena de ENTRADA, este enfoque evita que los usuarios se conecten directamente a los puertos 8080, 8181

Misha
fuente
1
Esto es bueno, pero ¿por qué no vincular el demonio a localhost: 8080 en lugar de 0.0.0.0:8080? Quiero hacer eso, pero necesito las iptables para eso.
Amala
Funciona, realmente genial.
xtian
29

Tradicionalmente en Unix, solo la raíz puede unirse a puertos bajos (<1024).

La forma más sencilla de evitar esto es ejecutar su servidor en un puerto alto (por ejemplo, 8080) y usar una regla simple de iptables para reenviar las conexiones del puerto 80 al puerto 8080. Tenga en cuenta que con esto pierde la protección adicional del puertos bajos; cualquier usuario en su máquina puede unirse al puerto 8080.

CesarB
fuente
23

Si su sistema lo admite, tal vez podría usar capacidades. Mira man capabilities, el que necesitas sería CAP_NET_BIND_SERVICE. No, nunca los he usado yo mismo y no sé si realmente funcionan :-)

WMR
fuente
1
funcionan, utilizo CAP_NET_RAW para ejecutar herramientas como tcpdump como usuario normal.
Justin
12

¡Use un proxy inverso (nginx, apache + mod_proxy) o un proxy inverso de almacenamiento en caché (Squid, Varnish) frente a los servidores de su aplicación!

Con un proxy inverso puedes lograr muchas cosas interesantes como:

  • Balanceo de carga
  • Reiniciar sus servidores de aplicaciones con usuarios que reciben una página de error elegante
  • Acelera las cosas con caché
  • Configuraciones detalladas que normalmente hace con un proxy inverso y no con un servidor de aplicaciones
Giovanni Toraldo
fuente
6

Puede usar el programa redir:

sudo redir --lport=80 --laddr=192.168.0.101 --cport 9990 --caddr=127.0.0.1
usuario340994
fuente
4

Usa sudo.

Configure sudo para que el usuario normal pueda ejecutar los comandos apropiados:

/etc/init.d/httpd start

O

apachectl stop

O

/usr/local/apache2/apachectl restart

O

/myapp/myappbin start

(O cualquier otro comando / script que use para iniciar / detener su servidor web / aplicación en particular)

Ahora no
fuente
8
Esto tiene el mismo problema: ejecutar un servicio como root es una mala práctica.
Kevin Panko
4

la respuesta de sunny es correcta, pero puede enfrentar problemas adicionales ya que la interfaz de bucle invertido no usa la tabla PREROUTING,

entonces las reglas de iptables para agregar son dos:

iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080
iptables -t nat -I OUTPUT -p tcp -d 127.0.0.1 --dport 80 -j REDIRECT --to-ports 8080
Francesco
fuente
3

Con Linux, tiene otras dos opciones:

Ambas extensiones del kernel de Linux permiten otorgar derechos de acceso en un nivel muy fino. Esto le permitiría otorgar este proceso para abrir el puerto 80, pero no heredaría ninguno de los otros derechos de root.

Por lo que escuché, la seguridad es mucho más simple de usar, pero SELinux es más seguro.

Aaron Digulla
fuente
2

Una solución es usar iptables para realizar PAT en los paquetes para el puerto 80. Puede usar esto para enrutar los paquetes al puerto local 8080, por ejemplo. Asegúrese de ajustar los paquetes salientes nuevamente al puerto 80.

En mi experiencia, las características de permisos específicos de Linux no se compilan en núcleos estándar debido a problemas de seguridad.


fuente
2

Si está intentando hacer esto para que un comando ejecutado por el usuario pueda usar el puerto 80, entonces sus únicas soluciones son los trucos de iptables o la configuración del setuid-to-root ejecutable.

La forma en que algo como Apache hace esto (se une al puerto 80, pero se ejecuta como alguien que no sea root) es ejecutarlo como root, vincularse al puerto y luego cambiar la propiedad del proceso al usuario no privilegiado después del puerto está arreglado. Si la aplicación que está escribiendo puede ejecutarse desde la raíz, puede hacer que cambie de propietario al usuario no privado después de configurar los puertos. Pero si esto es solo para que un usuario promedio lo ejecute desde la línea de comandos, entonces tendrá que usar una de las otras soluciones.

rjray
fuente
2

Cuando tengo varias aplicaciones de servicio web (scripts de python, motores tomcat, ...) que no quiero ejecutar como root, generalmente configuro un servidor web apache frente a ellos. Apache escucha el puerto 80 y Tomcat escucha el 8080.

En apache: s config:

ProxyPass /webapp http://localhost:8080/webapp
ProxyPassReverse /webapp http://localhost:8080/webapp

Consulte la documentación de mod-proxy para obtener más información: http://httpd.apache.org/docs/2.2/mod/mod_proxy.html


fuente
1
El inconveniente de esta solución es la pérdida de la IP entrante en los registros de Tomcat.
Emmanuel Bourg el
2

Creo que la mejor solución es sgid su aplicación y tan pronto como tenga su puerto vinculado, debería abandonar los privilegios al cambiar a otro usuario.

Anxy Dicy
fuente
1

Algunos sistemas host no permiten usar el módulo NAT, 'iptables' no resuelve el problema en este caso.

¿Qué tal xinetd?

En mi caso (ubuntu 10.04)

# apt-get install xinetd
# touch /etc/xinetd.d/my_redirect
# vim /etc/xinetd.d/my_redirect

Pegue la configuración:

service my_redirector_80
{
 disable = no
 socket_type = stream
 protocol = tcp
 user = root
 wait = no
 port = 80
 redirect = localhost 8080
 type = UNLISTED
}

Entonces:

# service xinetd restart

http://docs.codehaus.org/display/JETTY/port80 explica mejor.

Thomas
fuente
1
Esto es básicamente equivalente a poner un proxy inverso frente al servidor, pero es bastante ineficiente en comparación con soluciones dedicadas como HAproxy, Pound o Varnish que entienden el protocolo HTTP y pueden agregar encabezados útiles como X-Forward-For.
Emmanuel Bourg
-1

Por otro lado, FreeBSD y Solaris (¿alguien recuerda eso ?) Le permiten hacer esto (enlazar a puertos bajos) sin escalada de privilegios (es decir, usar programas para cambiar a root). Como especificó Linux, solo publico esto como un aviso para otros que puedan encontrar esta pregunta.

Daniel C. Sobral
fuente
-1

Nigromancia

Sencillo. 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 establezca 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 así: #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.

Dilema
fuente
¿Alguien podría explicar los votos negativos? ¿La compilación del núcleo no funciona? ¿O es solo porque después, ya no puedes hacer una actualización del núcleo desde los repositorios normales?
Dilema el
Probé esta solución ( sudo setcap cap_net_bind_service=+ep /path-to-java/java) pero siempre obtengo java: error while loading shared libraries: libjli.so: cannot open shared object file: No such file or directorysi Java tiene mayúsculas. vea también la primera parte de unix.stackexchange.com/a/16670/55508
Daniel Alder
1
Resuelto el error anterior añadiendo <javahome>/lib/amd64/jli/a ld.so.confy en funcionamientosudo ldconfig
Daniel Alder