Blog

Seguridad de servidores – Parte I – Conceptos básicos, NGINX, Jaula

Vamos a comenzar una serie de post sobre seguridad de servidores. Antes de empezar, vamos a definir una serie de conceptos básicos:

Servidores

Un servidor no es más que un dispositivo que responde a solicitudes de un cliente, normalmente es un ordenador que contiene información para ser compartida con muchos sistemas de clientes. Por ejemplo, páginas Web, documentos, bases de datos, imágenes, archivos de audio y video, etc.

Podemos y debemos pedir autenticación de usuario para verificar si tiene permiso para realizar una operación particular. Estos servidores deben contar con una lista central de cuentas de usuarios y autorizaciones, o permisos (para realizar operaciones y de acceso a datos) otorgados a cada usuario. Por ejemplo, si quiere cargar datos a un servidor FTP, se le puede dar permiso para escribir en su carpeta personal, pero no para leer otros archivos del sitio.

Concretando: Un servidor es un equipo, normalmente un ordenador, que ejecuta procesos en segundo plano, llamados servicios o demonios. Ëstos escuchan peticiones de los clientes y envían la respuesta. La comunicación se realiza siguiendo un determinado protocolo, que dependerá del servicio que estemos desplegando.

También suele llamarse servidor al software que se encarga de escuchar y responder a las llamadas de los clientes. Por ejemplo, Apache es un software que se encarga de gestionar las peticiones de acceso a una página Web, es decir, nos provee el servicio Web. Apache es, por tanto, un servidor Web.

Atendiendo al servicio que proveen, veamos diferentes tipos de servidores:

  • Servidor Web: Es un software que se mantiene a la escucha y devuelve las páginas webs solicitadas por los clientes.
  • Servidor Proxy: Es un software que permite que, dentro de una red local, se acceda a servicios que se encuentran fuera de la misma. El ejemplo típico es un servidor proxy para permitir la salida a Internet de determinados equipos de nuestra red local. No sólo se utiliza para redirigir el servicio web, puede ser utilizado para redirigir cualquier servicio. Cuando la función es la inversa, es decir, que desde el exterior de nuestra red se pueda acceder a un servicio interno, se llama proxy inverso.
  • Servidor FTP: Permite acceder a archivos que se encuentran en el servidor. El acceso puede ser de sólo lectura o lectura y escritura.
  • Servidor SSH: Nos permite iniciar sesiones de usario de manera remota a un equipo, en el que se está ejecutando.
  • Servidor DNS: Dado un nombre de máquina, resuelve la dirección IP a la que corresponde.
  • Servidor de correo: Es el servicio encargado de recibir y entregar los correos de los clientes.

Cada uno de estos servidores, utiliza sus protocolos de comunicación. Por el simple hecho de tenerlos en funcionamiento, estamos dejando una “puerta” abierta en el equipo que se ejecutan. Debemos proteger esta puerta de entrada contra posibles ataques.

Para la fortificación de sistemas, debemos seguir unos principios básicos:

  • Gestión de riesgos: Tenemos que realizar un análisis para estimar la magnitud de los riesgos que está expuesto el servidor. Es decir, debemos conocer los riesgos, para poder prevenir, impedir, y controlar.
  • Mínimo punto de exposición: Cada servicio que queramos proporcionar, debe (o debería) estar en un equipo separado. Además, cada equipo debe contener el software estrictamente necesario para realizar su función. Si es un servidor Web, no debería ser a la vez el servidor de base de datos o de correo, por ejemplo. Cada servidor debe tener su propio papel , y no debe contener más que lo imprescindible para realizar su función.
  • Mínimo privilegio posible: En un entorno multiusuario, cada programa se ejecuta con unos privilegios determinados. Para la administración del equipo, existe un usuario administrador (root en los sistemas UNIX-Like), que dispone de privilegios para acceder a cualquier parte el sistema. Bien, los servidores deben ejecutarse con un usuario específico, que deberá poseer los privilegios justos para realizar su función. Si un atacante consigue entrar en nuestro sistema aprovechando una vulnerabilidad de nuestro servidor, podrá obtener los privilegios bajo los que se esté ejecutando, pero no el control total del sistema.
  • Defensa en profundidad: Debemos implantar todas las medidas de seguridad posibles. Para hacer esto hay que tener en cuenta que, si una medida de seguridad no permite que el servicio responda en un tiempo razonable, no estaremos ofrenciéndolo. Esa medida no es válida. Además, hay que tener presente que, algunas medidas anulan a otras. En ese caso, habrá que plantearse cuál de las dos hay que implantar.

En el caso de una gran corporación, la utilización de diferentes equipos para la ejecución de los diferentes servicios, será viable. ¿Qué ocurre si esto no es así? ¿Y si sólo disponemos de un equipo? En este caso, la división de los diferentes servicios puede hacerse mediante virtualización. Creamos una máquina virtual que será la encargada de ejectuar un servicio determinado.

Debemos tener en cuenta que, si vamos a levantar un servidor Web, probablemente necesitemos más servicios. Por ejemplo, necesitaremos también un servidor de correo electrónico. Además, seguramente necesitemos realizar inicios de sesión en remoto para poder administrar el equipo o subir determinados archivos. A esto le añadimos la necesidad de tener un servidor de base de datos, para poder desplegar las aplicaciones Web.

Esto hace que, en muchos casos, en un mismo equipo, estén corriendo varios servicios a la vez. Debemos proteger los mismos y, siempre que sea posible, separarlos mediante virtualización o técnicas de “enjaulado”.

Veamos tipos de ataques básicos a los que nos enfrentaremos:

  • Malware: Es un software malicioso que se ejecuta en nuestro equipo. Este software puede estar diseñado para realizar diferentes acciones, sin que nos percatemos. Estas acciones pueden ser de diversa índole, por ejemplo, permitir acceso a nuestro equipo, enviar correos con malware, enviar información almacenada, etc.
  • DDoS: Son ataques de denegación de servicio. Saturan con peticiones falsas el servicio, de forma que impiden atender las peticiones legítimas. Para hacer este tipo de ataques, normalmente, se utilizan equipos infectados con malware que realizan las peticiones sin que el usuario se de cuenta que está formando parte de una BotNet (una red de equipos “zombies” que se “despiertan” cuando el atacante les indica y realizan las peticiones masivas al objetivo).
  • Fuerza bruta: Se intenta acceder a un servicio probando múltiples contraseñas y usuarios, hasta que se consigue.

¿Cómo podemos protegernos de estos ataques?

Para el caso del Malware, debemos utilizar antivirus y detectores de rootkits. Para los sistemas GNU/Linux, disponemos de múltiples opciones, por ejemplo, chkrootkit, rkhunter, unhide, tiger o lynis. Estas herramientas nos permiten detectar rootkits y nos informan de posibles vulnerabilidades en nuestro sistema (binarios con el bit SUID activado y configuración permisiva para el usuario root de determitados servicios, por ejemplo). Además, podemos utilizar SpamAssanssin y ClamAV, sobre todo, para el servidor de correo.

En el caso de los ataques DDoS, podemos mitigarlos utilizando scripts que limiten el número de conexiones por IP o mediante la limitación de conexiones, si el servicio lo requiere. Una herramienta interesante es DDoS Deflate, que bloquea IP’s si exceden un determinado número de conexiones.

Fuerza bruta,  en el caso de servicios como SSH, es interesante utilizar SSH-Keys, que proporcionan una solución elegante para ataques de fuerza bruta. Si no queda más remedio que utilizar la autencación clásica de usuario/contraseña y para proteger más servicios que requieren esta autenticación, disponemos de herramientas como Fail2Ban o SSHGuard. Estas herramientas realizan una comprobación de los intentos de autencación fallidos y, a un número de fallos en un período determinado, banean la IP.

Además de esto, es conveniente bloquear los escaneos de puertos. Normalmente, antes de ser atacados, nuestro equipo será escaneado para comprobar qué puertos están abiertos y qué servicios están activados. Siempre que sea posible, es una buena idea cambiar los puertos por defecto, por ejemplo, en conexiones SSH. Una herramienta que nos permite bloquear los escaneos de puertos a IPs que no se encuentren en una lista blanca de IPs permitidas, es PortSentry. No siempre nos interesa bloquear todas las consultas, que también es posible realizarlo mediante cortafuegos, de ahí la conveniencia de utilizar PortSentry.

Por supuesto, debemos huir de repositiorios no oficiales a la hora de instalar aplicaciones.

Una vez vistos estos conceptos básicos, veamos un ejemplo de enjaulado de un servidor web. En este caso, vamos a hacerlo con NGiNX.

NGiNX es un servidor web, proxy inverso de alto rendimiento y un proxy para protocolos de correo electrónico. Su desarrollo comenzó en el 2002 por Igor Sysoev para solventar un problema llamado C10K (los servidores no soportaban más de 10000 conexiones a la vez).

Entre sus ventajas más importantes es su estabilidad, bajo consumo de recursos y su sencilla configuración. Es utilizado por Github, Wikipedia, WordPress y muchos más.

Vamos a instalar los paquetes necesarios:

Arch

# pacman -Syu nginx

Debian/ Ubuntu

# apt-get install nginx-full

Jaula en Debian

#!/bin/bash –verbose
#######################################
# Empezamos
#######################################
# Creamos la estructura de directorios
#######################################

mkdir -vp /srv/nginx/dev
mkdir -vp /srv/nginx/etc/nginx/
mkdir -vp /srv/nginx/usr/{lib,sbin,bin}
mkdir -vp /srv/nginx/usr/share/nginx
mkdir -vp /srv/nginx/var/{log,lib}/nginx
mkdir -vp /srv/nginx/srv/http
mkdir -vp /srv/nginx/{run,tmp}

cd /srv/nginx/
ln -s usr/lib lib
cd lib
ln -s ../lib i386-linux-gnu
ln -s i386-linux-gnu i686
ln -s i386-linux-gnu/i686 cmov

[[ `uname -m` == “x86_64” ]] && {
ln -s usr/lib lib64 ;
cd usr ;
ln -s lib lib64 ;
}

########################################
#Creamos los dispositivos
########################################

mknod -m 0666 /srv/nginx/dev/null c 1 3
mknod -m 0666 /srv/nginx/dev/random c 1 8
mknod -m 0444 /srv/nginx/dev/urandom c 1 9

########################################
#Copiamos los binarios
########################################

cp -vr /usr/share/nginx/* /srv/nginx/usr/share/nginx
cp -v /usr/sbin/nginx /srv/nginx/usr/sbin/
cp -vrp /var/lib/nginx /srv/nginx/var/lib/

touch /srv/nginx/etc/shells

#########################################
#Copiamos la bibliotecas necesarias
#########################################
cp -v $(ldd /usr/sbin/nginx |grep /usr/lib|sed -r ‘s/(.+) (\/.*) (.*)/\2/’) /srv/nginx/usr/lib

cp -v /lib/i386-linux-gnu/i686/cmov/libnsl* /srv/nginx/lib

cp -v $(ldd /usr/sbin/nginx |grep /lib/ | grep -v /usr/lib|sed -r ‘s/(.+)(\/lib\/.*) .*/\2/’) /srv/nginx/lib

cp -v /lib/libnss_* /srv/nginx/lib
cp -v /lib/i386-linux-gnu/libnss* /srv/nginx/lib

cp -rfvL /etc/{services,localtime,nsswitch.conf,protocols,hosts,ld.so.cache,ld.so.conf,resolv.conf,host.conf,nginx} /srv/nginx/etc

#########################################
#Creamos los usuarios
#########################################

echo -e “www-data:x:33:\nnogroup:x:65534:” > /srv/nginx/etc/group
echo -e “www-data:x:33:33:www-data:/var/www:/bin/sh\nnobody:x:65534:65534:nobody:/nonexistent:/bin/sh” > /srv/nginx/etc/passwd
echo -e “www-data:*:15836:0:99999:7:::\nnobody:*:15836:0:99999:7:::”  > /srv/nginx/etc/shadow
echo -e “www-data:*::\nnogroup:*::” > /srv/nginx/etc/gshadow

############################################################
#Para poder utilizar puertos hasta el 1024 dentro del chroot
############################################################

setcap ‘cap_net_bind_service=+ep’ /srv/nginx/usr/sbin/nginx

############################################################
#Estableciendo permisos y montando los dispositivos
############################################################

chmod ugo+rw /srv/nginx/tmp
chmod ugo+rw /srv/nginx/run
chown www-data:www-data /srv/nginx/var/log/nginx
mount -t tmpfs none /srv/nginx/run -o ‘noexec,size=1M’
mount -t tmpfs none /srv/nginx/tmp -o ‘noexec,size=100M’

Jaula en Archlinux

#!/bin/bash –verbose
#######################################
# Empezamos
#######################################
# Creamos la estructura de directorios
#######################################

mkdir -p /srv/nginx/dev
mkdir -p /srv/nginx/etc/nginx
mkdir -p /srv/nginx/usr/{lib,bin}
mkdir -p /srv/nginx/usr/share/nginx
mkdir -p /srv/nginx/var/{log,lib}/nginx
mkdir -p /srv/nginx/srv/http
mkdir -p /srv/nginx/{run,tmp}
cd /srv/nginx/
ln -s usr/lib lib
[[ `uname -m` == “x86_64” ]] && {
ln -s usr/lib lib64
cd usr
ln -s lib lib64
}

########################################
#Creamos los dispositivos
########################################
mknod -m 0666 /srv/nginx/dev/null c 1 3
mknod -m 0666 /srv/nginx/dev/random c 1 8
mknod -m 0444 /srv/nginx/dev/urandom c 1 9

########################################
#Copiamos los binarios
########################################
cp -r /usr/share/nginx/* /srv/nginx/usr/share/nginx
cp /usr/bin/nginx /srv/nginx/usr/bin/
cp -rp /var/lib/nginx /srv/nginx/var/lib/

touch /srv/nginx/etc/shells

#########################################
#Copiamos la bibliotecas necesarias
#########################################
cp $(ldd /usr/bin/nginx |grep /usr/lib|sed -r ‘s/(.+) (\/.*) (.*)/\2/’) /srv/nginx/usr/lib
cp /lib/ld-linux-x86-64.so.2 /srv/nginx/lib
cp /usr/lib/libnss_* /srv/nginx/usr/lib
cp -rfvL /etc/{services,localtime,nsswitch.conf,nscd.conf,protocols,hosts,ld.so.cache,ld.so.conf,resolv.conf,host.conf,nginx} /srv/nginx/etc

#########################################
#Creamos los usuarios
#########################################
echo -e “http:x:33: \nnobody:x:99:” > /srv/nginx/etc/group
echo -e “http:x:33:33:http:/:/bin/false \nnobody:x:99:99:nobody:/:/bin/false” > /srv/nginx/etc/passwd
echo -e “http:::\nnobody:::” > /srv/nginx/etc/gshadow

############################################################
#Para poder utilizar puertos hasta el 1024 dentro del chroot
############################################################
setcap ‘cap_net_bind_service=+ep’ /srv/nginx/usr/bin/nginx

############################################################
#Estableciendo permisos y montando los dispositivos
############################################################
chmod ugo+rw /srv/nginx/tmp
chmod ugo+rw /srv/nginx/run

mount -t tmpfs none /srv/nginx/run -o ‘noexec,size=1M’
mount -t tmpfs none /srv/nginx/tmp -o ‘noexec,size=100M’
chown http:log /srv/nginx/var/log/nginx

El problema que tendremos es que, para actualizar NGiNX, no basta con actualizar los paquetes instalados, hay que volver a copiarlos dentro de la jaula. Las aplicaciones deberán ser copiadas también dentro de ésta, para que sean accesibles.

Cómo podemos ver, no dejamos ningún shell dentro de ella, con lo que impedimos que se pueda abrir una sesión interactiva. Además, sólo están los usuarios con los que se ejecuta NGiNX, con lo que también impedimos una escalada de privilegios.

Para que todo esto funcione, hay que modificar, en el caso de ArchLinux, el servicio de SystemD que levanta NGiNX y, en el caso de Debian, el script que realiza esta tarea:

Debian

Editamos /etc/init.d/nginx, y debe quedar como se muestra en el ejemplo:

Captura de pantalla de 2013-05-13 19_03_05

Arch

Creamos /usr/lib/systemd/nginx-jail.service , y tiene que contener estás líneas:

Captura de pantalla de 2013-05-13 19_03_58

En los siguientes posts iremos viendo el resto de herramientas, así como un ejemplo también con Apache.

Espero que os haya gustado, happy hacking!!

Mª José Montes – mjose@highsec.es – @MMontesDiaz

  1. no
    no12-12-2013

    Hola, he ejecutado el script de creación de jaula en debian wheezy 64 bits y tira los siguiente errores:
    ln: opción inválida — ‘l’
    Pruebe `ln –help’ para más información.
    scriptnginx.sh: línea 40: cd: usr: No existe el fichero o el directorio
    ln: fallo al crear el enlace simbólico «lib64»: El fichero ya existe
    cp: no se puede efectuar `stat’ sobre «/lib/i386-linux-gnu/i686/cmov/libnsl*»: No existe el fichero o el directorio
    cp: no se puede efectuar `stat’ sobre «/lib/i386-linux-gnu/libnss*»: No existe el fichero o el directorio

    Y el script de arranque luego no se atreve a levantar.

    Gracias

  2. guido
    guido02-02-2014

    Mis felicitaciones muy grande post. Felicidades de nuevo y saludos.

Leave a Reply

*

    No Twitter Messages