martes, 16 de agosto de 2016

Montar una VPN en tu servidor usando un container de Docker

Hola de nuevo,

hoy vamos a aprender a montar una VPN en nuestro servidor para poder usarla desde nuestros móviles, nuestros portátiles, etc. Lo primero que tenemos que saber es qué es una VPN y para qué sirve, así que vamos a ello: VPN viene de las siglas en inglés de "Virtual private network", red privada virtual en español y básicamente es una red privada que se extiende a través de una red pública, como internet. Quizá con un ejemplo gráfico se entienda mejor:

By Ludovic.ferre (talk · contribs) - Own work, GFDL, https://commons.wikimedia.org/w/index.php?curid=10101288

Imaginemos que tengo un empresa con varias sedes físicas separadas. Hace muchos años para conectar dichas sedes en una única red se usaban líneas dedicadas punto-a-punto, se conectaban físicamente las sedes con "un cable" que las unía físicamente. Como os podéis imaginar esto es muy muy caro y sólo estaba al alcance de empresas muy fuertes, como por ejemplo los bancos.
Una VPN viene a solucionar mediante software este problema, creando un enlace entre las sedes que las une entre ellas a través de internet, y por tanto las sedes sólo necesitan una conexión a internet que es mucho más barata que la conexión punto-a-punto. Como nuestros datos privados viajarán por Internet que es una red insegura per se, lo que hacemos es encriptar los datos de tal manera que sólo nosotros podamos entenderlos. Cualquier nodo de internet que vea pasar nuestros datos VPN sólo verá una ristra de bits sin sentido. Con esto conseguimos que nuestras sedes estén todas en la misma red y puedan compartir recursos de una forma fiable y barata.

Y entonces te preguntarás... ¿para qué quiero yo una VPN? Pues puedes ser que tengas una empresa con varias sedes y ya has dado con la solución ;-) .... o puede ser que simplemente estés un día en un Mc Donald's, un Burguer King, un centro comercial, una cafetería, un hotel, etc y te quieras conectar con tu móvil, tu portátil, tu tablet o lo que sea a la conexión Wifi del lugar. Si haces esto y NO usas una VPN lo que puedes conseguir es que te roben tus datos personales que viajan por una red insegura como es una Wifi pública. Quizá pienses que tú no tienes nada en el ordenador o en el móvil que quieras proteger, bueno, yo creo que sí lo tienes. Te pueden robar contraseñas (o sesiones) de servicios como Google, Facebook, etc También te pueden robar datos sensibles bancarios si te conectas a tu banco a través de internet, te pueden robar fotos, vídeos, etc que tengas alojadas en la nube.Pueden saber las webs que visitas, los videos que ves, las imágenes que estás viendo ahora mismo por intenet. Pueden suplantar tu identidad para publicar cosas en tu nombre o hacerse pasar por ti. En fin, creo que está claro que es una muy mala idea usar una Wifi pública sin la protección adecuada.

Pues bien, para evitar todos estos inconvenientes podemos usar una VPN que te conecte virtualmente con otro lugar para que cuando navegues a través de la Wifi Pública tus datos viajen encriptados hasta ese otro lugar y nadie en dicha Wifi te pueda robar nada. Es que como si estuvieras usando internet desde ese otro lugar. Para hacer esto puedes usar un servidor que tengas contratado en internet, un ordenador que tengas en casa, etc.

Vista la justificación de usar una VPN para el común de los mortales, veamos lo que necesitamos para llevar a cabo esta tarea:

- Un servidor conectado a internet, en este tutorial usaré un servidor con Ubuntu 16.04 LTS alojado en OVH.
- Un dispositivo móvil que haga de cliente de la VPN como puede ser un móvil con Android, con iOS, etc.
- Tiempo y ganas.

Para este tutorial voy a montar la VPN mediante Docker, y vamos a ver por encima qué es Docker y porque lo podemos usar para esto. De la Wikipedia: "Docker es un proyecto de código abierto que automatiza el despliegue de aplicaciones dentro de contenedores de software, proporcionando una capa adicional de abstracción y automatización de Virtualización a nivel de sistema operativo en Linux" ¡¡¡En cristiano Doc!!! Bien, Docker nos servirá en este proyecto para montar una VPN encapsulando todo un servidor VPN en un contenedor. Básicamente es como si virtualizáramos un servidor de VPN dentro de nuestro servidor. Los que conozcan la virtualización sabrán de sus ventajas, pero en este caso la ventaja es que no tenemos que tocar la configuración de nuestro servidor, ni modificar la configuración de la red, ni pelearnos con librerías, etc. Si actualizamos, cambiamos a otro sabor de Linux cambiamos de servidor, etc nuestro contenedor Docker funcionará igualmente en otro servidor.

Para instalar Docker en Ubuntu, lo mejor es que sigáis las instrucciones de la propia página de Docker, aunque aquí voy a resumir los pasos que yo seguí. Por supuesto, todo esto lo haremos desde una terminal de Linux con permisos de root.

Lo primero, actualizar nuestro sistema:

1
2
# apt-get update
# apt-get dist-upgrade

Lo siguiente es añadir la clave de firma del repositorio de Docker a nuestro sistema:


1
# apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D

Lo siguiente será añadir el repositorio de Docker. Para ello editamos el fichero con nuestro editor favorito, en mi caso con el vi:


1
# vi /etc/apt/sources.list.d/docker.list

Y añadimos el siguiente contenido al archivo ( en mi caso para Ubuntu 16.04 LTS):


deb https://apt.dockerproject.org/repo ubuntu-xenial main

Por último instalamos Docker desde los repositorios:


1
2
# apt-get update
# apt-get install docker-engine

Y por último arrancamos el servicio de Docker y ejecutamos el contenedor de ejemplo:


1
2
# service docker start
# docker run hello-world

Y si todo va bien nos debería aparecer algo así en la consola:


# docker run hello-world

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 # docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker Hub account:
 https://hub.docker.com

For more examples and ideas, visit:
 https://docs.docker.com/engine/userguide/

Desafortunadamente, en mi caso la cosa no fue bien, me daba un error:


docker: failed to register layer: devmapper

El problema en mi caso estaba en el kernel, ya que los servidores dedicados de OVH arrancan por defecto con un kernel desde la red, y claro, el kernel que te ponen los de OVH era un 3.15 que no viene con devicemapper (una funcionalidad que usa Docker) compilado, y por tanto docker no funciona bien.

La solución fue entrar en el panel de control de OVH, entrar en la configuración del servidor dedicado y poner que arranque desde la red pero con el kernel 4.4:


Después de esto, reinicias el servidor y entonces Docker ya funciona correctamente.
Ahora tenemos que preparar nuestro "container" con el servidor de VPN, para ello ejecutamos lo siguiente:


# OVPN_DATA="ovpn-data"
# docker volume create --name $OVPN_DATA
# docker run -v $OVPN_DATA:/etc/openvpn --log-driver=none --rm kylemanna/openvpn ovpn_genconfig -u udp://vpn.ejemplo.com:1194


Lo que hacemos al principio es establecer una variable con el nombre que queremos que tenga nuestro container Docker, así es más fácil copiar y pegar los comandos desde aquí sin cambiar nada. En nuestro caso, le hemos puesto "ovpn_data" como nombre. Luego lo que hacemos es crear un volumen con ese nombre que contendrá nuestros datos. Por último ejecutamos, ahora sí, la imagen del servidor VPN(kylemanna/openvpn) que ya está preparada en los servidores de docker y en esa imagen ejecutamos el script "ovpn_genconfig" que básicamente genera la configuración básica de servidor y la crea en /etc/openvpn que es el volumen que hemos creado previamente. En esta última orden es importante apreciar que tenemos que sustituir el nombre de dominio "vpn.ejemplo.com" por el nuestro propio que apunte a nuestro servidor, o si no tenemos un nombre, pues la IP directamente (suponiendo que sea un IP estática).

Ahora lo que vamos a hacer es generar el PKI de la autoridad de certificación. Al ejecutar el último comando nos pedirá una clave, tenemos que poner una clave muy segura ya que de ella dependerá la seguridad de nuestra VPN:


# docker run -v $OVPN_DATA:/etc/openvpn --rm kylemanna/openvpn touch /etc/openvpn/vars
# docker run -v $OVPN_DATA:/etc/openvpn --log-driver=none --rm -it kylemanna/openvpn ovpn_initpki

Ya tenemos nuestro servidor de VPN preparado, ahora vamos a crear un servicio en systemd para que se ejecute automáticamente el servidor VPN de docker cuando arranquemos nuestro servidor. Para ello creamos un nuevo fichero con nuestro editor favorito:


# vi /lib/systemd/system/docker-openvpn.service

Y dentro del fichero copiamos el siguiente contenido:


[Unit]
Description=Docker container for OpenVPN server
Requires=docker.socket
After=network.target docker.socket

[Service]
Environment="IMG=kylemanna/openvpn:latest"
Environment="NAME=ovpn"
User=user
Group=group
RestartSec=10
Restart=always
# Clean-up bad state if still hanging around
ExecStartPre=-/usr/bin/docker rm -f ovpn

# Attempt to pull new image for security updates
ExecStartPre=-/usr/bin/docker pull $IMG
ExecStart=/usr/bin/docker run -v ovpn-data:/etc/openvpn --rm -p 1194:1194/udp --name $NAME --cap-add=NET_ADMIN kylemanna/openvpn
ExecStop=/usr/bin/docker stop -t 2 kylemanna/openvpn

[Install]
WantedBy=multi-user.target


Lo que hemos hecho es crear un servicio que ejecute nuestro servidor docker VPN. Tendremos que cambiar las variables User y Group por nuestro usuario y grupo que los podemos obtener ejecutando "whoami" y "id -gn" respectivamente.

Ahora nos queda registrar el servicio en el sistema haciendo que lo arranque cada vez que reiniciemos la máquina, esto lo hacemos con el siguiente comando:


# systemctl enable docker-openvpn.service

Como podemos ver, estamos ejecutando nuestro servidor VPN escuchando por el puerto 1194, por tanto, si tenemos un firewall en nuestro servidor físico, tendremos que abrir dicho puerto:

# iptables -A INPUT -p udp --dport 1194 -j ACCEPT

Ahora ejecutamos dicho servicio:

# systemctl start docker-openvpn.service

Y si todo ha ido bien al ejecutar este comando:

# docker ps

Veremos que nuestro container se está ejecutando correctamente:

# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                    NAMES
4a2e1ebe211d        kylemanna/openvpn   "ovpn_run"          2 days ago          Up 2 days           0.0.0.0:1194->1194/udp   ovpn

Ahora tenemos que generar los certificados y ficheros de configuración necesarios para cada cliente que queramos que se conecte a nuestra VPN. Para ello, tendremos que sustituir NOMBRECLIENTE por el nombre que nosotros queramos, como puede ser "movil" (sin acento), "portatilasus", etc.

Con este comando creamos el certificado del cliente. Nos pedirá la contraseña de nuestra CA, la que hemos puesto antes, la ponemos sin equivocarnos. Ahí va el comando (cambiar NOMBRECLIENTE por lo que queramos):

# docker run -v $OVPN_DATA:/etc/openvpn --log-driver=none --rm -it kylemanna/openvpn easyrsa build-client-full NOMBREDECLIENTE nopass

Y por último creamos el fichero .opvn con toda la configuración del cliente que luego importaremos en nuestro móvil, tablet, etc:

# docker run -v $OVPN_DATA:/etc/openvpn --log-driver=none --rm kylemanna/openvpn ovpn_getclient NOMBREDECLIENTE > NOMBREDECLIENTE.ovpn

El fichero que nos cree aquí (por ejemplo movil.ovpn) tenemos que copiarlo de forma segura a nuestro dispositivo cliente, por ejemplo con scp. Yo lo que hice fue copiarlo al ordenador de casa con scp y luego al móvil con el cable USB. Si transfieres este fichero de forma insegura (email, dropbox, whatsapp, etc) corres el riesgo de comprometer la seguridad de tu VPN.

Ya con este fichero .ovpn lo que haces es abrir el cliente de OpenVPN, yo en mi caso, para Android uso este:

OpenVPN for Android
https://play.google.com/store/apps/details?id=de.blinkt.openvpn

En este cliente de VPN, nada más entrar, le das al botón (+), le dices IMPORTAR y eliges el fichero .ovpn que hemos creado, y con esto ya debería conectarse sin problemas a tu servidor VPN de docker. Si esto no te funciona, porque no te muestra el fichero, hazlo desde tu gestor de archivos e intentas abrir el fichero, cuando te pregunte con qué app quieres abrirlo le dices con "OpenVPN for Android" y ya está.

Espero que os haya gustado este tutorial y que lo uséis cada vez que os conectéis a una red insegura.
Hasta pronto!!!

Documentación:
https://en.wikipedia.org/wiki/Virtual_private_network

13 comentarios:

  1. Muy interesante! Tendré que verlo antes de conectarme otra vez a cualquier WIFI público.

    ResponderEliminar
    Respuestas
    1. Gracias Jose. También hay otras soluciones como por ejemplo la app "Hola"que te conecta a una VPN gratuita. Eso sí, la seguridad de esta red está en manos de los dueños de dicha app. Gracias por leerme!

      Eliminar
    2. No, hola esta distribuida entre los propios usuarios. En otras palabras, cuando te instalas el cliente, haces de nodo a la vez.

      Eliminar
  2. Hola, gracias por compartir!
    ¿ Podrías ampliar lo siguiente ?...
    ... los servidores dedicados de OVH arrancan por defecto con un kernel desde la red...
    ¿Qué utilidad tiene seleccionar el kernel desde red?
    ¿Porqué no usar la clásica elección de kernel desde el arranque?
    Gracias!

    ResponderEliminar
    Respuestas
    1. Hola Susana, pues por lo visto los de OVH compilan su propio kernel para optimizarlo para sus servidores. Siempre puedes arrancar desde disco claro. ¡Gracias por leerme!

      Eliminar
  3. En mi experiencia -tengo una VPN contra una raspberry con raspbian en mi casa- es que cuando te intentas conectar desde la wifi del hotel, el puerto 1194 UDP, puerto por defecto de OpenVPN, está cerrado...

    Así que lo configuré en el 443 TCP, con lo que te aseguras que siempre estará accesible tu vpn.

    ResponderEliminar
    Respuestas
    1. Muchas gracias por tu comentario Javier, efectivamente puede ocurrir que el puerto que estés usando esté cerrado, siempre puedes cambiarlo claro. Yo lo que hago en esos casos es que me conecto por SSH al servidor (a través de la red 3G), cambio el puerto y luego ya me conecto por el nuevo puerto, o al menos lo intento porque también puede ser que esté cerrado. Gracias de nuevo!!!

      Eliminar
  4. perfecto, con instrucciones de copiar y pegar, las que me gustan ;)

    ResponderEliminar
  5. Hola, un par de comentarios :)

    La imagen de openvpn esta basada en alpine, asi q para el data container te sale mejor usar alpine o openvpn (ambas opciones tendrán coste 0 siendo q harás pull igualmente de openvpn en el segundo paso). Si no, estas haciendo un pull innecesario de busybox.

    Además, desde docker 1.2 existe el argumento `--restart always` q te puede evitar la configuracion de systemd.

    Gracias x el post, saludos!

    ResponderEliminar
    Respuestas
    1. Muchas gracias por tu comentario constructivo Alex, efectivamente te puedes ahorrar unos megas si usas cualquiera de las dos opciones, pero bueno, tampoco es tanto ;-)

      Y efectivamente, desde docker 1.2 puedes hacer que el propio servicio de docker cargue los containers que necesites con --restart, eso sí, te dicen que si tu container depende de otros servicios aparte de docker, que lo sigas haciendo con systemd, pero sí, en este caso se podría hacer como tú propones.

      Gracias de nuevo!!!

      Eliminar
  6. Buenos dias,

    Un manual extraordinario, muchas gracias por compartirlo, resulta facilisimo montarlo.
    Una pregunta, he estado revisando el proyecto, ya que el direccionamiento IP que utiliza por defecto, no es valido para mi configuración de red.
    He estado revisando y hay un metodo para cambiar a IP fija a los clientes, pero no funciona.
    Hay alguna manera, que en vez de otorgar una ip en /30, lo haga en /24, sin aplicar ruteo?

    Un Saludo

    ResponderEliminar
    Respuestas
    1. Buenas tardes,

      siempre puedes modificar la configuración del OpenVPN accediendo a los ficheros de configuración.
      Para ello, tienes que usar "docker ps" para ver el nombre de tu contenedor, y luego "docker inspect" seguido del nombre del contenedor. En "Mount Point" te viene la ruta física del volumen "/etc/openvpn". Entras en esa ruta física con el servicio de docker-openvpn apagado y modificas el fichero openvpn.conf según tus necesidades.

      Un saludo.

      Eliminar