Asterisk: baneando IPs “peligrosas” de por paises

Hoy he encontrado una página muy interesante donde se reportan bloques de IP que pueden ser utilizados para realizar ataques. A la hora de securizar nuestro Asterisk, tener estos bloques de IPs denegados, nos ayudará mucho a mitigar malas experiencias, como por ejemplo, fraudes en llamadas.

Mi consejo es siempre banear todas las IPs excepto aquellas que queramos utilizar, conociendo a nuestros clientes, sin embargo, en algunos casos esto no puede ser, y hay que tener nuestro Asterisk abierto a todo el mundo.

¿A todo el mundo? Bueno, a casi todo el mundo. En este post explicaré como poner en funcionamiento un script que he encontrado junto a fail2ban (herramienta que no queremos que se deshabilite).

Este script que he encontrado en http://www.lowendguide.com/ se nutre de la base de datos de http://www.ipdeny.com, donde se pueden encontrar “algunas” (muchas) IPs que no son deseadas ni deseables, y desde las cuales se realizan mucho ataques. Estas están almacenadas por países.

Script:

Este script lo que hace es simplemente conectarse a la página, bajarse un listado de IPs de los países que queramos, y proceder a añadirlas en el IPTables de nuestra máquina. Sí, efectivamente, deberemos tener instalado IPTables en nuestro servidor para poder utilizar dicho script.

Una vez creado este script, el cual tenéis pegado en el siguiente apartado, lo que haremos es crear un fichero, con permisos de ejecución en nuestra máquina e insertar el siguiente código en bash:

#!/bin/bash
### Block all traffic from AFGHANISTAN (af) and CHINA (CN). Use ISO code ###
ISO="af cn"
 
### Set PATH ###
IPT=/sbin/iptables
WGET=/usr/bin/wget
EGREP=/bin/egrep
 
### No editing below ###
SPAMLIST="countrydrop"
ZONEROOT="/root/iptables"
DLROOT="http://www.ipdeny.com/ipblocks/data/countries"
 
cleanOldRules(){
$IPT -F
$IPT -X
$IPT -t nat -F
$IPT -t nat -X
$IPT -t mangle -F
$IPT -t mangle -X
$IPT -P INPUT ACCEPT
$IPT -P OUTPUT ACCEPT
$IPT -P FORWARD ACCEPT
}
 
# create a dir
[ ! -d $ZONEROOT ] && /bin/mkdir -p $ZONEROOT
 
# clean old rules
cleanOldRules
 
# create a new iptables list
$IPT -N $SPAMLIST
 
for c in $ISO
do
 # local zone file
 tDB=$ZONEROOT/$c.zone
 
 # get fresh zone file
 $WGET -O $tDB $DLROOT/$c.zone
 
 # country specific log message
 SPAMDROPMSG="$c Country Drop"
 
 # get 
 BADIPS=$(egrep -v "^#|^$" $tDB)
 for ipblock in $BADIPS
 do
 $IPT -A $SPAMLIST -s $ipblock -j LOG --log-prefix "$SPAMDROPMSG"
 $IPT -A $SPAMLIST -s $ipblock -j DROP
 done
done
 
# Drop everything 
$IPT -I INPUT -j $SPAMLIST
$IPT -I OUTPUT -j $SPAMLIST
$IPT -I FORWARD -j $SPAMLIST
 
# call your other iptable script
# /path/to/other/iptables.sh
 
exit 0

Si os fijáis bien, en la segunda línea del script se puede indicar entre las comillas los países que queremos que se baneen.:

### Block all traffic from AFGHANISTAN (af) and CHINA (CN). Use ISO code ###
ISO="af cn"

Si queremos banear las no deseadas de España, pondremos: “es” entre las comillas.

Una vez realizado esto, ejecutamos el script y a esperar. ¡Ojo! Este script siempre borra todo lo que tengáis en el IPTables, dado que ejecuta una función definida dentro del mismo llamada “cleanOldRules” la cual hace un flush de todas las reglas existentes.

Una vez realizado esto, y para mantener nuestro Fail2Ban trabajando como es de recibo, lo que os aconsejo es que procedáis a realizar un:

/etc/init.d/iptables reload

Con esto volveremos a cargar las chains de reglas del IPtables.

Depués de utilizar este script, y hacer un Word count de IPtables, pues me han aparecido un todal de 57692 reglas que banean (casi nada, y eso que comenté la parte de LOG…)

root@VOIP:/etc/asterisk# iptables -nL | wc -l
57692
root@VOIP:/etc/asterisk#

Mi consejo es que para ver que hace iptables de ahora en adelante, utilicéis un head (y a poder ser que os creéis un alias en la consola http://www.thegeekstuff.com/2010/04/unix-bash-alias-examples/). Las reglas se añaden en una cadena llamada “countrydrop”:

root@VOIP:/etc/asterisk# iptables -nL | head -n 20

Chain INPUT (policy ACCEPT)
target prot opt source destination
fail2ban-ASTERISK-UDP all -- 0.0.0.0/0 0.0.0.0/0
fail2ban-ASTERISK-TCP all -- 0.0.0.0/0 0.0.0.0/0
fail2ban-ssh tcp -- 0.0.0.0/0 0.0.0.0/0
countrydrop all -- 0.0.0.0/0 0.0.0.0/0

Chain FORWARD (policy ACCEPT)
target prot opt source destination
countrydrop all -- 0.0.0.0/0 0.0.0.0/0

Chain OUTPUT (policy ACCEPT)
target prot opt source destination
countrydrop all -- 0.0.0.0/0 0.0.0.0/0

Chain countrydrop (3 references)
target prot opt source destination
DROP all -- 27.116.56.0/22 0.0.0.0/0
DROP all -- 43.231.131.0/24 0.0.0.0/0
DROP all -- 43.249.40.0/22 0.0.0.0/0

Luego para no tener que repetir el proceso cada vez que se reinicie la máquina, haré un:

root@VOIP:/etc/asterisk# /etc/init.d/iptables-persistent save

¡Espero que este post sea de utilidad!

 

Referencias:

http://www.lowendguide.com/3/networking/block-an-entire-country-with-iptables-2/

http://www.ipdeny.com/ipblocks/data/countries

http://jonathanmanning.com/2012/01/17/how-to-iptables-firewall-configuration-for-sipvoip-on-centos-rackspace-cloud/

Kamailio – Identificador de llamadas salientes E.164 a través de dbaliases

Nuestro problema/reto a intentar solventar hoy es cómo identificar las llamadas salientes a través de un DID que tengamos asignado para nuestro SIP End Point en el dbaliases. Hasta ahora, lo que nos ha ocupado sobre esta tabla era muy sencillo: entra un número con un formato E.164 y queremos reenviar esto a nuestro cliente, con lo cual traducíamos de un número tal y como 34930000000 a robert.voip001.netvoip.no-ip.org.

Pero, ¿y cuando robert.voip001.netvoip.no-ip.org llama al exterior, nosotros debemos de asignarlo a este número? ¿Cómo hacerlo para que no salga anónimo o el número 1000? (en caso de que sea 1000 ese el SIP Username). Pues vamos a ello.

Lo primero que vamos a hacer es cargar un módulo que nos permita interactuar a nuestras variables con nuestras tablas mysql.

loadmodule "avpops.so"

Una vez cargado el módulo, procedemos a asignar los valores que necesitamos para que funcione:

# ----- avpops params ----
modparam("avpops","db_url","mysql://kamailio:p4ssw0rd3t3st1ng!@localhost/kamailio")
modparam("avpops","avp_table","dbaliases")

Y ahora comenzamos con nuestra lógica de llamadas, es decir, tocando el route[PSTN] para que las llamadas exteriores se cursen de modo adecuado. Lo que queremos es que cuando se llame al exterior, se consulte la tabla de dbaliases y se reescriba el identificador de llamada saliente.

Adjunto toda mi rutina de route[PSTN] para poder hacer esto:

route[PSTN] {
#!ifdef WITH_PSTN
 # check if PSTN GW IP is defined
 if (strempty($sel(cfg_get.pstn.gw_ip))) {
 xlog("SCRIPT: PSTN rotuing enabled but pstn.gw_ip not defined\n");
 return;
 }

 # Dejamos que se llamen a los números que están marcados correctamente
 # en caso contrario, le decimos que tienen un mal formato y rechazamos.
 # El 067 lo metemos para que salga identificado sin callerID
 if(!($rU=~"^(\+|00)[1-9][0-9]{6,20}$") && !($rU=~"^(6|7|8|9)[0-9][0-9]{5,20}$") && !($rU=~"^(067)(6|7|8|9)[0-9][0-9]{5,20}$")){
 sl_send_reply("408", "Not Allowed - Bad Format Number.");
 xlog("*****DEBUGTONI: ERROR IN FORMAT NUMBER\n");
 exit;
 }

 # Con este prefijo no dejamos que se llamen a números de tarificacion especiales
 # de este modo nos ahorramos problemas en invoicing
 if(($rU=~"^(8|9)[0][3-7]{6,20}$") || ($rU=~"^(067)(8|9)[0][3-7]{6,20}$")){
 sl_send_reply("409", "Not Allowed - Special Number.");
 xlog("*****DEBUGTONI: SPECIAL NUMBER NOT ALLOWED\n");
 exit;
 }

 # Solo permitir llamadas desde nuestros dominios
 if(from_uri!=myself) {
 sl_send_reply("403", "Not Allowed");
 exit;
 }

 if (strempty($sel(cfg_get.pstn.gw_port))) {
 $ru = "sip:" + $rU + "@" + $sel(cfg_get.pstn.gw_ip);
 } else {
 $ru = "sip:" + $rU + "@" + $sel(cfg_get.pstn.gw_ip) + ":"
 + $sel(cfg_get.pstn.gw_port);
 }
 xlog ("*****DEBUGTONI: Usuario llamando a $rU desde $fu con un tag $tu \n");
 $var(a) = $(fu{uri.user});
 xlog ("*****DEBUGTONI: User llamando con caller ID: $var(a)\n");

 # Aquí ponemos el caller ID a través del cual debemos de mostrar 
 # al destino. Para ello utilizamos el P-Asserted-Identity, el 
 # P-Preferred-Identity y modificamos el from, dado que muchos sistemas
 # lo utilizan para ello.
 if (avp_db_query("select alias_username from dbaliases where username='$fU' and domain='$fd'","$avp(callerid)")){
 xlog("*****DEBUGTONI: Sobreescribimos el uri user del from a $avp(callerid)\n");

 #Llamada normal , es decir, sin tratamiento de privacidad.
 if(!($rU=~"^(067)[0-9]{5,20}$")){
 $fu= "sip:"+$avp(callerid)+"@"+$(fu{uri.domain});
 }

 #Llamada con numero oculto, para lo cual se ha prefijado el
 # código 067 delante del numero marcado
 else{
 $var(calledNum)=$(rU{s.strip,3});
 $rU= $var(calledNum);
 $tu="sip:"+$var(calledNum)+"@"+$(tu{uri.domain})+";"+$(tu{uri.params});
 $fu="sip:anonymous@anonymous.invalid";
 }

 $var(pidentity_a)="tel:"+$avp(callerid)+"\n";
 $var(pidentity_p)="\"ToniKamailio <"+$avp(callerid)+">\" <sip:"+$avp(callerid)+"@"+$(fu{uri.domain})+">\n";

 append_hf("P-Asserted-Identity: $var(pidentity_a)","Call-ID");
 append_hf("P-Preferred-Identity: $var(pidentity_p)","Call-ID");

 # Si se ha prefijado el 067 le insertamos privacidad
 if(($rU=~"^(067)[0-9]{5,20}$")){
 xlog ("*****DEBUGTONI: User ANONIMAMENTE a llamando a $rU and this is to: $tu\n");
 append_hf("Privacy: id\n","Call-ID");
 }
 }
 else{
 sl_send_reply("428", "Use Identity Header.");
 exit;
 }

 route(RELAY);
 exit;
#!endif

 return;
}

Si nos fijamos bien, lo que he tenido que realizar es cambiar el $fu (fromUri) y añadir dos campos en el header de SIP (P-Asserted-Identity y P-Preferred-Identity). Depende de cual sea vuestro carrier, con el P-Assert-Identity debería de ser suficiente, pero en mi caso he tenido que rehacer el fromUri.

Como veréis, además de esto, he añadido una subrutina/subcódigo para que prefijando con 067 el número llamante salga como oculto. Echadle un vistazo al RFC 3325 el cual trata sobre Asserted Identity (desgraciadamente, el RFC no lo cumple nadie… Pero bueno,  es un punto de referencia muy importante y ayuda a ver qué estamos haciendo).

Cuidado, para que no fallen las llamadas iniciadas por nuestros SIP End Points, hemos cambiado el orden en el enrutado general, para que miren antes si este DID E.164 está en el location table (es una llamada intra dominio) y en caso contrario que lo envie a través de PSTN:

 # MODIFIED: Primero miramos si está en location table, y de ser así, procedemos a llamar.

 # user location service
 route(LOCATION);

 # dispatch destinations to PSTN
 route(PSTN);

Por otro lado, y para evitar sustos y funcionamientos erroneos, hemos modificado parte del enrutamiento de route[LOCATION], para que las llamadas salientes sólo sean de cuando uno de nuestros SIP End Points no encuentre el número, y evitar que terceros utilicen nuestra ruta PSTN si son ajenos a nuestros dominios SIP:

 # MODIFIED: Comentado para que vaya hacia fuera en caso
 # de no estar en location busque en PSTN, siempre y cuando la llamada
 # sea originada desde nuestro dominio.
 if (uri==myself){
    route(PSTN);
 }
 else{
    send_reply("404", "Not Found");
 }
 exit;

Por otro lado, las llamadas ocultas, tal y como se ha configurado, sólo funcionan para llamadas salientes, pero vamos, que simplemente es necesario aplicar las mismas políticas n el route[LOCATION] para que funcionen de modo interno.

¡Espero que os sea de utilidad! La verdad es que me lo he pasado muy bien siguiendo el flujo de llamadas de Kamailio en su archivo de configuración, y es un ejercicio que recomiendo.

Resources:

http://kamailio.org/docs/modules/3.1.x/modules/avpops.html#id2532762

http://www.kamailio.org/docs/modules/3.1.x/modules_k/textops.html

http://www.kamailio.org/wiki/cookbooks/devel/transformations

http://www.kamailio.org/wiki/cookbooks/3.2.x/transformations

http://kamailio.org/dokuwiki/doku.php/pseudovariables:3.1.x

http://lists.sip-router.org/pipermail/users/2009-July/023972.html

http://en.wikipedia.org/wiki/E.164

GAPS – Creando scripts de autoprovision para GXP

Para automatizar la provisión de GXP he creado un pequeño script en Python donde se genera a partir de una plantilla común para todos los dispositivos un archivo de configuración.

Para configurar los terminales GXP a través de http o tftp u otros protocolos, es necesario crear un archivo de texto plano, donde se definen los valores de las variables para dicha extensión (SIP Username, password, dominio, proxy,…) el cual luego debe de encriptarse a través de un binario que Grandstream tiene disponible en su página web.

El binario para encriptar se puede encontrar en la siguiente URL:

http://www.grandstream.com/support/tools

Nosotros, lo vamos a poner en funcionamiento dentro de una máquina UNIX. Lo que he realizado, básicamente es una página web, donde escaneo las cajas de terminales que llegan a la empresa, y donde indicando la empresa y la extensión, se autoprovisionan los terminales.

Este es el script creado para tal efecto, los parámetros a pasarle son que en apartado de help se indican:

#!/usr/bin/python


###########################################
# Imports
###########################################
import sys
import os

###########################################
# Functions
###########################################
def createFile(mac,name,sipUser,sipPass,domain,userWeb,adminWeb):

 #Creamos fichero
 fileCfg="/var/www/prov/gxp/config/plaintext/cfg"+mac+"_plaintext"
 fileCfgEnc="/var/www/prov/gxp/config/cfg"+mac

 cmd="cp /var/www/prov/gxp/config/plantilla_gxp "+fileCfg
 h = os.popen(cmd)
 ret = h.read()

 passwordParams="\n##########################\n"
 passwordParams=passwordParams+"# PASSWORD PARAMS\n"
 passwordParams=passwordParams+"##########################\n"
 passwordParams=passwordParams+"P2="+adminWeb+"\n"
 passwordParams=passwordParams+"P196="+userWeb+"\n"

 sipParams="\n##########################\n";
 sipParams=sipParams+"# SIP CONFIGURATION EXT\n"
 sipParams=sipParams+"##########################\n"
 sipParams=sipParams+"P3="+name+"\n";
 sipParams=sipParams+"P35="+sipUser+"\n"
 sipParams=sipParams+"P36="+sipUser+"\n"
 sipParams=sipParams+"P34="+sipPass+"\n"
 sipParams=sipParams+"P47="+domain+"\n"

 cmd="echo \""+sipParams+ "\" >> "+fileCfg
 h = os.popen(cmd)
 ret = h.read()

 cmd="echo \""+passwordParams+ "\" >> "+fileCfg
 h = os.popen(cmd)
 ret = h.read()
 print ret

 #Encriptamos fichero
 cmd="/usr/local/src/GS_CFG_GEN/bin/encode.sh "+mac+" "+fileCfg+" "+fileCfgEnc
 h = os.popen(cmd)
 ret = h.read()
 print ret
 return(1)

def help():

 print "--------------------------------------------------------\n"
 print " configureGXP script generator http files\n"
 print "--------------------------------------------------------\n"
 print "./configureGXP.py configure mac name sipUser sipPass sipDomain userWeb adminWeb"
 print "--------------------------------------------------------\n"

 return(1)


###########################################
# Main
###########################################

if len(sys.argv) <= 1:
 
 help()

elif len(sys.argv) > 1:

 action=sys.argv[1]

 if action=='configure' and len(sys.argv)==9:

 mac=sys.argv[2]
 mac=mac.lower()
 name=sys.argv[3]
 sipUser=sys.argv[4]
 sipPass=sys.argv[5]
 sipDomain=sys.argv[6]
 userWeb=sys.argv[7]
 adminWeb=sys.argv[8]

 res=createFile(mac,name,sipUser,sipPass,sipDomain,userWeb,adminWeb)

 else:
 help()

else:
 help()

¿Cómo funciona? ¿Qué hace el script?

Paso 1: Toma los argumentos que se le pasan (macTerminal,usuarioSIP, passwordSIP, dominioSIP,contraseñaWebTeléfonoParaUsuario y contraseñaWebTeléfonoParaAdministrador).

Paso 2: Crea un fichero en texto plano con la configuración con unas variables comunes en todos los terminales a partir de una plantilla.

Nombre fichero: cfgMACTERMINAL_plaintext

Os recomiendo leer la siguiente entrada (http://www.voip-info.org/wiki/view/Grandstream+Configuration+Tool)

Paso 3: Añade las varibles concretas para este terminal haciendo “echos” y appendings.

Paso 4: Encripta el fichero y lo inserta en una carpetas accesible en nuestro servidor WEB para que los terminales se lo puedan bajar sin problema

Localización: /var/www/prov/gxp/config/cfgMACTERMINAL

Con esto, ya hemos realizado nuestro escript para autoprovisionar Grandstreams. Recuerda configurar GAPS como es debido para que los terminales busquen el archivo siguiente:

http://ServidorProvisionEjemplo.net/gxp/config/cfgMACTERMINAL

GAPS – Sistema de provisión para Grandstream

A continuación voy a hablaros sobre mi experiencia con GAPS (Grandstream Automatic Provisioning System). Este es un sistema que Grandstream ofrece a sus clientes para poder mecanizar y automatizar la provision de estos terminales.

Por defecto, todos los teléfonos, cuando se conectan a la red, establecen una consulta contra el servidor de Grandstream, comunicandole la MAC que tienen, y preguntando si tienen “algo para ellos”.

Si hemos registrado la MAC del teléfono en este sistema (https://fm.grandstream.com) y hemos creado un perfil, el teléfono comenzará a buscar su archivo de configuración donde se le haya indicado, es decir, lo que se hace es reedirigir la petición hacia un server que indiquemos donde se encuentra la configuración para este dispositivo.

Podemos tener diferentes perfiles para diferentes clientes, GAPS lo permite, lo cual es muy útil por si acaso tenemos diferentes servidores de provisión.

Resumiendo, ¿cómo funciona GAPS?

Paso 1: Conecto el teléfono a la red

Paso 2: El teléfono pregunta a GAPS vía http si tiene alguna configuración para el. En caso afirmativo, procede a paso 3.

Paso 3: Teléfono se conecta a donde el servidor GAPS le ha indicado y se baja el firmware y la configuración para él.

Paso 4: El telefono se reiniciará, y las configuraciones tomarán efecto.

Una vez entendido cómo funciona GAPS, procedo a crear otro post para explicar los detalles técnicos y la implementación realizada.

Aprovecho para enviar un saludo a los chic@s de Avanzada7 los cuales creo que están haciendo una magnifica labor en el campo de VoIP en España, y sin los cuales, no me hubiera interesado por este y otros sistemas.

Fuentes:

http://www.voipeamos.es/?p=97

 

Snom715 – Registrando contra Asterisk vía VPN

El el post anterior hemos visto cómo levantar una VPN de nuestro Snom 715 a nuestro servidor VPN. Si nosotros tenemos nuestro servidor SIP trabajando en la misma dirección IP que openVPN, no hay ningún tipo de problema, y la llamada se ejecuta bajo encriptación.

El escenario se me plantea ahora mismo es el siguiente: tenemos el servidor SIP en otra máquina donde no corre nuestro servidor openVPN, y queremos ejecutar una llamada vía el túnel OpenVPN. Por higiene y seguridad, creo que es más que sensato, tener los diferentes servicios en diferentes máquinas.

Tenemos diferentes opciones:

Opción 1: Crear otro tunel openVPN entre el servidor SIP y el servidor openVPN, y que este último haga de concentrador. Para esto, es necesario que nuestro registro DNS de la cuenta SIP resuelva contra esta IP privada del servidor SIP (la que se le asigne openVPN). Esto no es muy sencillo, y añade dificultades y registros extras en caso de que queramos hacerlo así. Por este motivo, utilizaremos otra opción.

Opción 2 (elegida en este post): Pasar parte del tráfico del teléfono por la conexión VPN. Es decir, todo lo redirigido hacia la máquina SIP que vaya a través del servidor VPN añadiendo una ruta en la configuración de openVPN. Más sencillo en configuración y más seguro. Es la que vamos a explicar cómo realizar.

Paso 1: Modificar el fichero server.conf de nuestro servidor openVPN.

Nuestro servidor ficticio es el 91.127.0.100, y vamos a reedirigir todo el tráfico destinado hacia esta red a través del tunel openVPN del teléfono. También aprovechamos y hacemos que los DNS vayan a través de este tunel (no necesario).

push "route 91.127.0.0 255.255.255.0"
push "route 91.127.1.0 255.255.255.0"

push "dhcp-option DNS 91.127.1.225"
push "dhcp-option DNS 91.127.1.5"
push "dhcp-option DNS 91.127.1.6"

Paso 2: Una vez realizado esto, reiniciamos el servidor openVPN:

root@dmzserver:/home/toni# /etc/init.d/openvpn restart

Paso 3: Reiniciamos nuestro terminal.

Y ya debería de funcionar correctamente. En caso de que no llegue la petición, deberemos de asegurarnos de que nuestro servidor dmz (donde corre el servicio openVPN) tenga habilitado el bit de forwading para poder reenviar las peticiones:

echo "1" > /proc/sys/net/ipv4/ip_forward

Y en el iptables que se permita hacer NAT de estas IPs del tunnel y masquerade (es decir, reedirigir y hacer traducción de IPs:

iptables -I FORWARD -s 10.9.8.0/24 -j ACCEPT
iptables -t nat -A POSTROUTING -s 10.9.8.0/24 -o eth0 -j MASQUERADE

Fuentes:

https://community.openvpn.net/openvpn/ticket/291

https://forum.snom.com/index.php?showtopic=8728

https://community.openvpn.net/openvpn/ticket/291

OpenVPN – Snom 715 – VoIP

En la empresa hemos recibido un Snom 715, y me ha venido en gusto ponerme a trastear con OpenVPN y Snom 715 e ir probando este por diferentes redes. Lo primero que haré es configurar-lo contra un servidor OpenVPN y proceder a realizar pruebas de audio y calidad en casa de mis diferentes compañeros.

Fase 1: Instalando OpenVPN en un server

Lo primero que vamos a hacer es comenzar con la instalación de un servidor OpenVPN para securizar nuestras comunicaciones contra nuestros Asterisk. Este servidor tiene dos tipos de tuneles: UDP y TCP. En nuestro caso, y por la naturaleza de las conexiones de telefonía, optaremos por las primeras: UDP.

root@dmzserver:/home/toni# apt-get install openvpn

Sencillo, ¿no?

Fase 2: Preparamos el servidor

Aquí, una vez instalado el servidor de OpenVPN vamos a proceder a detallar como generar las diferentes claves y certificados para nuestro servidor.

Ficheros en nuestro servidor OpenVPN:

  • Clave privada servidor (para desencriptar lo que me llegue).
  • Clave pública servidor  (para que el resto de agentes me encripten todo aquello que me tengan que enviar).
  • Certificado servidor (para firmar las claves públicas).
  • Certificado de Autoridad de Certificación (para dar confianza al sistema).

Para hacerlo, necesitamos copiar los ejemplos del easy-rsa de nuestro server en nuestra carpeta de OpenVPN:

root@dmzserver:/home/toni#cd /usr/share/doc/openvpn/examples/

root@dmzserver:/usr/share/doc/openvpn/examples# sudo cp -R easy-rsa/ /etc/openvpn/

root@dmzserver:/home/toni#cd /etc/openvpn/easy-rsa/2.0 

root@dmzserver:/etc/openvpn/easy-rsa/2.0# mkdir keys

Modificamos lo siguiente para generar nuestra autoridad certificadora con nuestros parámetros:

root@dmzserver:/etc/openvpn/easy-rsa/2.0# vi vars

export KEY_DIR="/etc/openvpn/easy-rsa/2.0/keys/"

export KEY_COUNTRY="ES"
export KEY_PROVINCE="BCN"
export KEY_CITY="Barcelona"
export KEY_ORG="TONIEMPRESA"
export KEY_EMAIL="tibanezlujan@gmail.com"
root@dmzserver:/etc/openvpn/easy-rsa/2.0# source ./vars
NOTE: If you run ./clean-all, I will be doing a rm -rf on /etc/openvpn/easy-rsa/2.0/keys/
root@dmzserver:/etc/openvpn/easy-rsa/2.0# 
root@dmzserver:/etc/openvpn/easy-rsa/2.0# ./clean-all
root@dmzserver:/etc/openvpn/easy-rsa/2.0# ./build-dh 
Generating DH parameters, 1024 bit long safe prime, generator 2
This is going to take a long time
.+.............................+......+.....................................................................+...+....+................
                                  [...]
......................+.......................................+.......+.................+....+.........................................++*++*++*
root@dmzserver:/etc/openvpn/easy-rsa/2.0#

Generamos certificado para autoridad CERTIFICADORA

root@dmzserver:/etc/openvpn/easy-rsa/2.0# ./pkitool --initca
Using CA Common Name: TONIEMPRESA CA
Generating a 1024 bit RSA private key
.....++++++
...++++++
writing new private key to 'ca.key'
-----
root@dmzserver:/etc/openvpn/easy-rsa/2.0#

Generamos certificado y claves para el server

root@dmzserver:/etc/openvpn/easy-rsa/2.0# ./pkitool --server openvpnServer
Generating a 1024 bit RSA private key
...................++++++
................++++++
writing new private key to 'openvpnServer.key'
-----
Using configuration from /etc/openvpn/easy-rsa/2.0/openssl-1.0.0.cnf
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
countryName :PRINTABLE:'ES'
stateOrProvinceName :PRINTABLE:'BCN'
localityName :PRINTABLE:'Barcelona'
organizationName :PRINTABLE:'TONIEMPRESA'
commonName :PRINTABLE:'openvpnServer'
emailAddress :IA5STRING:'tibanezlujan@gmail.com'
Certificate is to be certified until Nov 14 14:45:16 2024 GMT (3650 days)
Write out database with 1 new entries
Data Base Updated
root@dmzserver:/etc/openvpn/easy-rsa/2.0#

Miramos que los ficheros se hayan creado normalmente:

root@dmzserver:/etc/openvpn/easy-rsa/2.0# ls -la keys/
total 52
drwx------ 2 root root 4096 Nov 17 15:45 .
drwxr-xr-x 4 root root 4096 Nov 17 15:47 ..
-rw-r--r-- 1 root root 3896 Nov 17 15:45 01.pem
-rw-r--r-- 1 root root 1233 Nov 17 15:44 ca.crt
-rw------- 1 root root 916 Nov 17 15:44 ca.key
-rw-r--r-- 1 root root 245 Nov 17 15:44 dh1024.pem
-rw-r--r-- 1 root root 112 Nov 17 15:45 index.txt
-rw-r--r-- 1 root root 21 Nov 17 15:45 index.txt.attr
-rw-r--r-- 1 root root 0 Nov 17 15:44 index.txt.old
-rw-r--r-- 1 root root 3896 Nov 17 15:45 openvpnServer.crt
-rw-r--r-- 1 root root 680 Nov 17 15:45 openvpnServer.csr
-rw------- 1 root root 916 Nov 17 15:45 openvpnServer.key
-rw-r--r-- 1 root root 3 Nov 17 15:45 serial
-rw-r--r-- 1 root root 3 Nov 17 15:44 serial.old
root@dmzserver:/etc/openvpn/easy-rsa/2.0#

Creamos server.conf para que se pueda ejecutar el daemon de openVPN en el servidor y aceptar las conexiones.

root@dmzserver:/etc/openvpn# touch /var/log/openvpn.log

root@dmzserver:/etc/openvpn# vim server.conf

port 1194
proto udp
dev tun

ca /etc/openvpn/easy-rsa/2.0/keys/ca.crt # generated keys
cert /etc/openvpn/easy-rsa/2.0/keys/openvpnServer.crt
key /etc/openvpn/easy-rsa/2.0/keys/openvpnServer.key # keep secret
dh /etc/openvpn/easy-rsa/2.0/keys/dh1024.pem

server 10.9.8.0 255.255.255.0 # internal tun0 connection IP
ifconfig-pool-persist ipp.txt

keepalive 10 120

comp-lzo # Compression - must be turned on at both end
persist-key
persist-tun
status /var/log/openvpn.log
verb 3 # verbose mode
client-to-client

Permitimos forwading:

root@dmzserver:/etc/openvpn# echo 1 > /proc/sys/net/ipv4/ip_forward

Iniciamos servicio:

root@dmzserver:/etc/openvpn# /etc/init.d/openvpn start
Starting virtual private network daemon: server.
root@dmzserver:/etc/openvpn# /etc/init.d/openvpn status
VPN 'server' is running.
root@smzserver:/etc/openvpn#

Fase 3: Preparar certificados para teléfonos

Limpiamos los archivos de configuración para crear nuevas claves:

root@dmzserver:/etc/openvpn/easy-rsa/2.0# source ./vars
NOTE: If you run ./clean-all, I will be doing a rm -rf on /etc/openvpn/easy-rsa/2.0/keys
root@dmzserver:/etc/openvpn/easy-rsa/2.0#

Con la MAC del teléfono, o con cualquier otra palabra única e inequívoca de que se trata de este terminal, procedemos a hacer un build de las claves.

root@dmzserver:/etc/openvpn/easy-rsa/2.0# ./build-key 000413750BEF

Generating a 1024 bit RSA private key ..................................................................++++++ .....++++++ writing new private key to '000413750BEF.key' ----- You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [ES]: State or Province Name (full name) [BCN]: Locality Name (eg, city) [Barcelona]: Organization Name (eg, company) [TONITEST]: Organizational Unit Name (eg, section) []: Common Name (eg, your name or your server's hostname) [000413750BEF]: Name []: Email Address [tibanezlujan@gmail.com]: Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []: An optional company name []: Using configuration from /etc/openvpn/easy-rsa/2.0/openssl-1.0.0.cnf Check that the request matches the signature Signature ok 
The Subject's Distinguished Name is as follows 
countryName :PRINTABLE:'ES' 
stateOrProvinceName :PRINTABLE:'BCN' 
localityName :PRINTABLE:'Barcelona' 
organizationName :PRINTABLE:'TONITEST' 
commonName :PRINTABLE:'000413750BEF' 
emailAddress :IA5STRING:'tibanezlujan@gmail.com' 
Certificate is to be certified until Nov 14 15:00:08 2024 GMT (3650 days) Sign the certificate? [y/n]:y 
1 out of 1 certificate requests certified, commit? [y/n]y 
Write out database with 1 new entries Data Base Updated 

root@dmzserver:/etc/openvpn/easy-rsa/2.0#

Creamos una carpeta para alojar todas las claves generadas para este cliente, y las copiamos allí:

root@dmzserver:/etc/openvpn/easy-rsa/2.0# mkdir keys/000413750BEF

root@dmzserver:/etc/openvpn/easy-rsa/2.0# cp keys/000413750BEF.* keys/000413750BEF
root@dmzserver:/etc/openvpn/easy-rsa/2.0# cp keys/000413750BEF.* keys/000413750BEF

Finalmente creamos un archivo exactamente que se llame vpn.cnf, exactamente así, y añadimos los archivos que hemos pasado con sus rutas. Así mismo, hemos de mover también el certificado de la autoridad pública, tal y como hemos visto en la sentencia anterior:

root@dmzserver:/etc/openvpn/easy-rsa/2.0

client
dev tun 
proto udp 
remote dmzserver.netvoip.no-ip.org 1194 
resolv-retry infinite 
nobind 
persist-key 
persist-tun 

ca /openvpn/ca.crt 
cert /openvpn/000413750BEF.crt 
key /openvpn/000413750BEF.key 

ns-cert-type 
server comp-lzo 
verb 0

Es importante que todos los archivos se encuentren en la misma carpeta, a pesar de que las rutas relativas en el archivo haga referencia a una carpeta llamada openvpn. Estos ficheros los ordenará internamente Snom715.

Finalmente, creamos el .tar para que nuestro teléfono se lo baje, y lo dejamos en una carpeta de un servidor web:

root@dmzserver:/home/toni# tar -cvf /var/www/provision_vpn_snom/000413750BEF.tar *
root@dmzserver:/etc/openvpn/easy-rsa/2.0/keys# chown www-data:www-data /var/www/provision_vpn_snom/000413750BEF.tar

Fase 4: Configurar teléfono

Activar el servicio de VPN en la página web del teléfono lo siguiente:

Configuración >> Avanzado >> QoS/Security

OpenVPN_Snom715

En este caso, se puede apreciar como nuestro servidor Web tiene autenticación de usuario y nombre, la cual le indicamos a través de user:pass dentro de la URL.

Una vez aplicados cambios, se nos pedirá reiniciar el terminal, y esto ya debería de funcionar, para ver esto, podemos hacer lo siguiente en el servidor:

root@dmzserver:/etc/openvpn/easy-rsa/2.0/keys# ngrep port 1194
interface: eth0 (91.z.x.0/255.255.255.0)
filter: (ip or ip6) and ( port 1194 )
#
U 91.z.x.x:1194 -> 91.z.y.237:57491
 2....Wk..X.wa...?...........bf.|..L3.....@..Y...>'.Q..H!......{.....=...H....Op..aZ4.'{..h...Y.V<<..o.~*..F....J4.-.=.z...~.. 
#
U 91.z.y.237:57491 -> 91.z.x.x1194
 2U<..#........i......v.~.ql8~.p.>ykR^d;4H7M'q../.~V..../..w..9.......G..'...:..V.?...C.T....sIb..-.(..Xc.......W.....W.;.f... 
#
U 91.z.x.x:1194 -> 91.z.y.237:57491
 2T...../.."^uPoN.#....X...=.qw..jF::2n..8e(sPr.`GFd!.X(...u...P...!....8L>.. ..w!.).55%'.-%.........J3a.*H........9C=.p.|"e.. 
#
U 91.z.y.237:57491 -> 91.z.x.x:1194
 2..6..l.B..h.:-.(...J4w..)..[Z....X.+2.....y...#..5.%.....](.]...M..C.B.u.T....J./._...>......&r.cb.!...;P~..5..0..^)Yz..D..! 
#
U 91.z.x.x:1194 -> 91.z.y.237:57491
 2.....t..]4..._d._H.........."6...H.M...)...(.[..+X)x.|..c.)..b+..l......J.....=>r.

y después de ngrepear puerto de openVPN y ver tráfico en ambos sentidos, mirar el log:

root@dmzserver:/var/www/prov_openvpn# tailf /var/log/openvpn.log 
OpenVPN CLIENT LIST
Updated,Tue Nov 18 10:36:09 2014
Common Name,Real Address,Bytes Received,Bytes Sent,Connected Since
000413750BEF,91.z.y.237:57491,566693,588002,Mon Nov 17 18:33:03 2014
ROUTING TABLE
Virtual Address,Common Name,Real Address,Last Ref
10.9.8.10,000413750BEF,91.z.y.237:57491,Tue Nov 18 10:36:08 2014
GLOBAL STATS
Max bcast/mcast queue length,0
END
root@dmzserver:/var/www/prov_openvpn# tailf /var/log/openvpn-log.log 
Tue Nov 18 10:37:35 2014 us=540371 PO_CTL rwflags=0x0000 ev=6 arg=0x7f26543c6064
Tue Nov 18 10:37:35 2014 us=540389 I/O WAIT Tr|Tw|Sr|SW [10/0]
Tue Nov 18 10:37:35 2014 us=540407 PO_WAIT[0,0] fd=5 rev=0x00000004 rwflags=0x0002 arg=0x7f26543c6130 
Tue Nov 18 10:37:35 2014 us=540421 event_wait returned 1
Tue Nov 18 10:37:35 2014 us=540434 I/O WAIT status=0x0002
Tue Nov 18 10:37:35 2014 us=540472 000413750BEF/91.z.y.237:57491 UDPv4 WRITE [53] to [AF_INET]91.z.y.237:57491: P_DATA_V1 kid=2 DATA 0e11c9bd c692aa8f 1c860ffc 77fdc175 eb601e31 9733083b 8cc2d68e 029fc69[more...]
Tue Nov 18 10:37:35 2014 us=540618 000413750BEF/91.z.y.237:57491 UDPv4 write returned 53
Tue Nov 18 10:37:35 2014 us=540646 PO_CTL rwflags=0x0001 ev=5 arg=0x7f26543c6130
Tue Nov 18 10:37:35 2014 us=540660 PO_CTL rwflags=0x0001 ev=6 arg=0x7f26543c6064

Fuentes consultadas

https://openvpn.net/index.php/open-source/documentation/howto.html

https://wiki.debian.org/OpenVPN

http://wiki.snom.com/Networking/Virtual_Private_Network_(VPN)

http://www.redeszone.net/redes/openvpn/

http://geekland.hol.es/crear-y-configurar-servidor-openvpn/

Presentación Olle Johanson

Para leer con mucho cariño, de nuestro más que querido Olle! Siempre es bueno poder seguir,a pesar de perderse el espectáculo en directo, algunas buenas diapositivas de este mentor de la voz ip, y en general de las comunicaciones en tiempo real.