Ejecutar comandos desde las reglas de Nginx

Iniciado por WHK, 5 Mayo 2015, 21:02 PM

0 Miembros y 1 Visitante están viendo este tema.

WHK

Hola, estoy usando la última versión de nginx y tengo una duda...

Estoy intentando de crear un sistema de baneo a través de nginx junto a un servicio estadístico de ips baneadas, el tema es que no se como hacerlo.

Lo que hice fue crear una regla muy simple en la configuración de nginx:

server{
...

location / {
if ($request_uri ~ /.ht.*) {
# Ejecutar un .sh que banee la ip por iptables y
# cree un log personalizado entre otras cosas.
return 403;
}
}
}


Se comprende lo que quiero hacer? :P la idea es que cuando alguien intente acceder a archivos no permitidos termine denegando el acceso y a demás baneandose a traves de iptables, ahora, lo que quiero principalmente es generar un log personalizado muy similar al del nginx pero que indique la razón de porque fue baneado, fecha de baneo, regla de solicitud denegada, etc.

Por eso estaba pensando pasar todo a traves de un único archivo .sh pasándole argumentos y que este se encargue de ejecutar el iptables o apf y que genere este log o que llame a un archivo en python y guarde en una base de datos en mongodb los datos de baneo... pero para lograr todo esto necesito saber si es posible ejecutar un comando sobre la linea comentada antes de retornar el 403, también sería genial que desde el mismo archivo sh se comunique por telnet al router y bloquee la ip por hardware a traves del firewall cisco asa.

Hay alguna manera de lanzar esta ejecución de alguna manera?

La segunda alternativa pero no tan efectiva sería que solamente genere un log personalizado pero tendría que tener un servicio que revise el log cada x segundos y vaya baneando las ips que tengan ciertas reglas, pero eso quiere decir que ya no será baneado sobre la primera solicitud sino cuando el servicio recorra el log, a demás al crecer el log el proceso se hará mas pesado y no es la idea.

MinusFour

Lo unico que se me ocurre es que reescribas la url a un script en php, python o perl. No creo que puedas ejecutar iptables a menos que el que corra el script tenga permisos root.

moikano→@

#2
Cita de: MinusFour en  5 Mayo 2015, 21:30 PM
Lo unico que se me ocurre es que reescribas la url a un script en php, python o perl. No creo que puedas ejecutar iptables a menos que el que corra el script tenga permisos root.

Puedes darle permisos a www-data para que ejecute un sh como permisos de root, pero solo un sh. Es un poco peligroso, pero si solo le das permisos de escritura a root no hay peligro.

En el fichero sudoers sería algo así:

www-data   ALL=NOPASSWD:/bin/bash /path/a/tu/script.sh

También puedes usar el permiso sticky bit con:

Mentira, no es el sticky bit lo que hace eso, es el setuid. Se activa de esta forma:

chmod u+s /path/a/tu/script.sh

Eso lo que hace es que se ejecuta el fichero con los permisos del usuario propietario aunque no lo sea.

Aunque lo que no se como se pone en nginx para ejecutar un sh en una regla, pero suena interesante.

Edito: Parece que en nginx puedes ejecutar código de Lua, para ejecutar el bash script:

content_by_lua 'os.execute(" /path/a/tu/script.sh")';

La fuente: http://stackoverflow.com/questions/22891148/nginx-how-to-run-a-shell-script-on-every-request

Suerte.

WHK

Si, de hecho intenté hacer un rewrite hacia un cgi/bash pero si la persona está usando un robot para escanear puede cancelar la solucitud y cancelar la ejecución a menos que se cargue sin ninguna redirección como lo hace el mod rewrite de apache

Intentaré lo que dicen, gracias por los tips.

MinusFour

#4
No estoy seguro de como hace el rewrite nginx, pero yo creo que es posible hacer el rewrite sin una respuesta HTTP 30X de por medio. Si vi el tema de lua pero no estaba seguro si puedes enviar parametros, por ejemplo ip. Y no hace falta mencionar... que esos parametros que estes pasando a tu archivo que tiene permisos root pueden representar un riesgo.

Edit: Nginx al parecer no hace uso de ninguna peticion intermediaria con rewrite a menos que tu le indiques.

WHK

#5
Asi es, nginx para hacer todo eso lo estoy corriendo como root al igual que snort y squid, estoy haciendo un invento que si resulta bien lo transformaré en un servicio, lo bueno es que no usaré un servidor para varios sitios, usaré un servidor por sitio y ese servidor no tendrá credenciales de ningún tipo y solo harán reverse proxy asi que es muy poco probable que me hackeen el servidor a través de nginx a menos que sea un bug explotable de forma remota pero eso ya es mas difícil, para eso está snort y acciones por iptables de bloqueo preventivo pre procesamiento de datos por parte del servidor web y si lo hackean no expongo nada, con suerte la ip del servidor al que le estoy haciendo el reverse proxy aunque de todas maneras pasará a traves de una vpn configurado a traves del royter y no del sistema operativo, ais que si lo hackean da lo mismo, se levanta una instancia anterior del ec2 y se parcha el bug y listo.

Me tinca mas hacer la redirección, lo que estoy intentando es enviar un header 403 y crear un 403 personalizado en .pl el cual se va a encargar de ejecutar todo, lo único que debe hacer es leer las variables nativas de nginx, asi que todos los que  lean un 403 quedarán baneados :P

Ahora tengo que ver como puedo llevar una variable desde el archivo de configuraciones hasta el archivo perl para obtener el vector de ataque por el cual fue bloqueado.

MinusFour

Si usas FCGI, puedes pasar las variables con fastcgi_params.

http://wiki.nginx.org/FcgiExample

El documento hace mencion a PHP FPM, hay un fcgi para perl pero la verdad nunca he tratado con el.

WHK

#7
Bueno, hice funcionar fast cgi wrap pero el binario está compilado para que no se pueda iniciar en modo root :( y el lua está dificil de instalar ya que requiere la instalacion manual de versiones especificas de paquetes y no es bueno si quiero automatizar su instalación.

Lo que hice fue instalar httpd y php y lo hice correr en modo root y desde nginx hice que cuando se produjera un error 403 hiciera un reverse proxy a localhost puerto 81 a la ruta del 403.php :D y php es el encargado de hacer todo, es mas, ahora puedo utilizar mongodb con php directo o migrar a futuro a oracle si es necesario :D y httpd blindado unicamente en localhost.

Me salió muy larga la vuelta que quería hacer pero finalmente fue efectivo, tampoco creo que haya problema de carga con apache ya que las solicitudes se harán unicamente cuando se haga un baneo o algo por el estilo.

Gracias de todas maneras, a futuro buscaré una manera mas facil de hacer todo sin tener que pasar por el httpd y un doble reverse proxy xd

Ahora intentaré hacer lo que dice moikano→@ para no tener que usar todo en modo root sino solo los scripts necesarios.

Saludos.

WHK

#8
Agregué al /etc/sudoers la linea:
apache ALL=NOPASSWD:/bin/bash /var/ban/403.sh

Ahora ejecuto desde php:
Código (php) [Seleccionar]
<plaintext>
<?php 
system
('id');
system('/var/ban/403.sh');
system('/bin/bash /var/ban/403.sh');
system('sudo /var/ban/403.sh');
system('sudo /bin/bash /var/ban/403.sh');
?>


el archivo 403.sh tiene:
#!/bin/bash
id


[root@localhost ban]# ls -la
total 8
drwxr-xr-x.  2 root root   19 may  6 17:21 .
drwxr-xr-x. 22 root root 4096 may  6 17:20 ..
-rwxrwxr-x.  1 root root   15 may  6 17:21 403.sh


Le di permisos de ejecución con chmod +x 403.sh y nada, solo se ejecuta el primero con id de apache:

uid=48(apache) gid=48(apache) groups=48(apache) context=system_u:system_r:httpd_t:s0


Pero el resto nada :( apache está corriendo con permisos de apache y nginx con permisos de nginx, supuse que php al tener permisos de apache podría ejecutar el script de baneo con permisos de root pero nada.

Le acabo de poner:
apache ALL=(ALL) NOPASSWD: ALL
A sudoers para descartar un problema con la ruta o cosas así y tampoco :-/

moikano→@

#9
Estás seguro que el usuario que ejecuta es apache? en debian suele ser www-data sea apache o nginx.

Aparte de eso, prueba a reinciar el servicio, ya que los permisos de sudo se cargan cuando se reinicia la sesión.

El que me suele funcionar a mi es este :

system('sudo /bin/bash /var/ban/403.sh');