La cultura del «todo gratis» está matando al Open Source.

7 Dic

Antier por la tarde un tweet de un desarrollador de FreePBX me llamó la atención. «Wow… Acabo de escuchar que 3CX está comprando a Elastix…»

De inmediato pregunté a sus creadores si el rumor era cierto. No obtuve respuesta.

Hoy por la mañana Elio Rojano a través de su sitio Sinologic.net ha confirmado lo peor: Elastix ha sido vendido a una empresa cerrada que trabaja bajo Windows y que ahora renombrará Elastix como 3CX for Linux

Todos los detalles pueden encontrarse en el sitio de Elio: https://www.sinologic.net/blog/2016-12/adios-elastix.html

Lo que a mi me preocupa y que la «comunidad» no ha alcanzado a ver es que la cultura del Todo gratis mata lentamente los proyectos FOSS, nos pasó con Trixbox hace unos años, nos pasó con MySQL y existen varios ejemplos más de cómo proyectos se vienen abajo por nada más y nada menos que la plata.

Ya lo decía alguien -que no encuentro la fuente- en mi timeline de Twitter, que aquel que quiera hacer negocio basado en Open Source (OS) debe estar loco o engañandose. Y es cierto todas las comunidades de OS están repletas de esos usuarios que ven una oportunidad de venta sin sacrificio. Lo he repetido una y otra vez hablando de Elastix, siempre hubo usuarios que sólo entraban a los foros a preguntar «¿Cómo hago esto?» «¿Cómo implemento aquello?» «Tengo una empresa de VoIP y me urge resolver esto a mis clientes». En cuanto se les mencionaba el soporte de pago huían (en el mejor de los casos). En el peor decían que era obligación de los creadores dar soporte gratis. Yo lo viví cuando trabajé en 2 de las empresas más fuertes de VoIP de la Ciudad de México: recibí llamadas de gente exigiendo soporte gratuito porque Asterisk es OS.

Esa es la gente que daña a la comunidad. También está la otra cara de la moneda: aquellos que apoyan mediante donaciones, participando en foros de discusión, reportando bugs, creando parches, módulos, etcétera. Lamentablemente son los menos.

Ahora bien como lo comenta Christian en un tweet, habrá que esperar y ver si esto puede ser un paso a las mejoras:

Siempre he sido un escéptico de las versiones enlatadas y sé que esto para los que saben Asterisk puro no representa ninguna tragedia. Sin embargo, como la única distribución creada por latinos para latinos es un fuerte golpe a la comunidad de habla hispana, o una estrategia para solo dejar FreePBX en el mapa. Cualquiera que fuese el resultado, la ventaja de que Asterisk sea un toolkit es que cualquiera con mínimos conocimientos podría crear la nueva distro enlatada para ser el una de las alternativas.

¿Cuantos más 2016, cuántos más?

Generador de contraseñas seguras para Asterisk/Elastix

8 Sep

CapturaDice el dicho que una cadena es tan fuerte como su eslabón más débil, y en muchos de los casos, las contraseñas inseguras que ocupamos en las extensiones de nuestro conmutador son la principal causa por la que nos llevamos desagradables sorpresas como recibir cuentas millonarias porque un atacante ganó acceso a nuestro equipo y curso tráfico no autorizado con alto costo para nosotros.

Como instalar equipos basados en Asterisk es parte del pan de todos los días, optamos por crear un generador de contraseñas seguras que te permita de manera rápida:

  1. Crear un pool de cadenas de texto aptas para ser utilizadas como contraseñas.
  2. Que permita generar la configuración de un sistema con interfaz gráfica (por ejemplo: Elastix) de manera rápida y con la plantilla default creada, descargándola como un CSV listo para importar.

Hemos estado utilizando esta herramienta ya por varios meses, pero hoy la damos a conocer para que todos ustedes puedan aprovecharla. Para usarla, solo deben proporcionar 3 datos:

  1. La lista de extensiones sobre la cual generar las contraseñas. Pueden usar valores individuales (ej. 101, 102, 105, etc) o rangos (ej. 101-200).
  2. Una clave inicial que servirá como semilla para generar las contraseñas seguras.
  3. La longitud deseada para tus contraseñas.

El sistema te regresa una cadena de texto con el alfabeto 0-9a-zA-Z. No utilizamos caracteres especiales porque sabemos que hay algunos sistemas que no te los reconocen, así que preferimos irnos a la segura y desplegar lo universal.

La aplicación es libre y puedes utilizarlas las veces que necesites. La única restricción es que por cuestiones de rendimiento, solo puedes generar lotes de hasta 500 extensiones.

Puedes comenzar a utilizar esta herramienta desde la siguiente liga (nota: se encuentra hospedada en el dominio de Enlaza Comunicaciones).

¡Suerte!

Elastix Addon: EasyVPN

2 Jun

Easy VPN Logo.PNG.Hola, como ya lo venía diciendo en mi cuenta de Twitter, en Enlaza Comunicaciones hemos estado trabajando en un Addon para los usuarios de Elastix. Después de algunas semanas, finalmente ha sido publicado en el sitio de Elastix Addons y ya está disponible para su descarga e instalación desde su Elastix (PDF de instrucciones [4.2MB]).

¿Pero de que va este Addon? En Enlaza tomamos mucho en cuenta la seguridad de los PBX dado que sabemos que al día se reciben cientos de ataques a PBX por los puertos expuestos de SIP, IAX, SSH, entre otros. Es por esta razón que nosotros usamos, siempre que es posible, una conexión a través de OpenVPN tanto para administrar los sistemas a los que les damos soporte como para crear extensiones remotas.

Como la mayoría de ustedes saben y conocen, OpenVPN es una herramienta Open Source que nos permite crear VPNs seguras entre nuestros equipos, esta herramienta es la más popular en ambientes Unix para crear túneles seguros entre nuestras redes. Si bien existen miles de tutoriales en la WEB para crear los túneles y configuraciones de OpenVPN y seguramente existen ya muchos usuarios con esta herramienta instalada en sus PBX, nos decidimos a crear un front-end web intuitivo y fácil de usar para usuarios que prefieran lo visual(WEB) al texto(consola de Linux).

Con el addon EasyVPN podrán:

  • Crear la configuración de la VPN en tan solo 5 sencillos pasos.

  • Crear los certificados de los clientes con un par de clicks .

  • Ver y administrar los clientes conectados al momento.

  • Crear certificados para clientes Windows.
  • Crear certificados para clientes Linux.
  • Crear certificados para teléfonos Yealink.
  • Crear certificados para teléfonos SNOM.

Entre los beneficios de utilizar EasyVPN están:

  • Utilizar sólo un puerto para la comunicación desde el exterior. Ya no será necesario abrir o natear puertos en el firewall para servicios específicos (SIP, IAX, SSH, HTTPS, etc).
  • Toda la comunicación entre los clientes y el servidor estará cifrada, nadie podrá leer los paquetes que se transmitan.
  • Los accesos estarán controlados. El administrador sabrá quien está conectado y desde qué IP.
  • Ideal para troncalizar Asterisk en distintas ubicaciones físicas.
  • Utilizar OpenVPN Connect en iOS y Android para registrar extensiones remotas en smartphones y/o tablets.

Todo el feedback, sugerencias y reportes de bugs serán bien recibidos en el thread oficial «Nuevo Addon para Elastix EasyVPN» en los foros Asterisk México. Así que a probarlo a fondo y de ser posible reportar fallos.

Asterisk vs Elastix vs Trixbox vs AsteriskNow vs FreePBX: Explicando la diferencia

31 Jul

Actualizado el 2016-01-15 para reflejar los cambios más recientes a las distribuciones.

Una de las actividades más importantes en mi vida profesional es la de capacitar sobre el uso de la plataforma Asterisk®, y durante los cursos que doy recibo frecuentemente preguntas como estas:

  • ¿Cuál es la diferencia entre Asterisk y Elastix?
  • Tengo sistemas basados en Trixbox. ¿Puedo migrarlos a Asterisk?
  • ¿Es lo mismo Asterisk que AsteriskNow?

Cuando escucho estas preguntas es fácil darme cuenta que hay mucha diferencia entre los productos que reciben una promoción comercial contra los que son el proyecto base sobre el que se construye el producto comercial, y por eso quiero establecer la diferencia.

 

Asterisk «puro»

Asterisk es software open source, hecho en lenguaje C y creado originalmente por Mark Spencer (actual CTO de Digium, empresa que patrocina la mayor parte del desarrollo de Asterisk). Este software, por si solo, no es una herramienta plug-and-play que posea la capacidad de hacer llamadas, sino que es necesario atravesar por numerosos pasos (descarga, compilación, instalación y configuración) para que pueda realizar labores útiles. Sin embargo, es un elemento base: una plataforma para crear cosas más grandes, para que de allí podamos construir un sin fin de aplicaciones basadas no solamente en voz, sino en la unión con datos y/o cualquier otro sistema de cómputo que necesitamos que interactúe con un teléfono.

Asterisk puede instalarse en cualquier distribución de Linux, por lo que podemos usar Debian, Ubuntu, Mint, CentOS, RedHat, OpenSuse, etc. También se puede instalar en FreeBSD, MacOS y hay algunos ports para Windows, pero solo es en Linux en donde se tiene el soporte completo para su ejecución. Esto quiere decir que puedes tomar prácticamente cualquier PC que tengas y usando Asterisk la puedes convertir en un servidor de comunicaciones totalmente gratuito y open source.

Si quisiéramos hacer una analogía, Asterisk es el motor de un automovil. El motor es la parte base: sin él no se puede andar, pero por si solo no puede hacer gran cosa. Necesita de varias otras partes: asientos, volante, frenos, etc. para poder entregarnos la experiencia completa de poder conducir un vehículo.

Aquellas personas que optamos por especializarnos en Asterisk puro tenemos la opción de tomar 2 certificaciones que están disponibles: el dCAA (Digium Certified Asterisk Administrator) o el dCAP (Digium Certified Asterisk Professional). Ambas certificaciones son avaladas por Digium, y buscan reconocer que la persona que las posea cuenta con los conocimientos necesarios para instalar y configurar un servidor de comunicaciones basado en Asterisk, pero usando solamente la línea de comandos, con la que tenemos el máximo control, pero también la máxima complejidad.

Ventajas: 

  • Tienes total control: puedes hacer lo que quieras y actualizar en cualquier momento.
  • Al compilar, tu conmutador se ajustará a la arquitectura de tu PC.
  • Puedes elegir que módulos quieres compilar y cuales no.
  • Sin limitantes impuestas por interfases gráficas.

Desventajas:

  • Tienes que hacer todo a mano.
  • Programar por línea de comandos puede no ser tan natural para algunas personas.
  • Toma un mayor tiempo de implementación.
  • Puedes caer en muchos escenarios diferentes y enfrentarte con muchos problemas por resolver.

 

 

Para muchas personas, configurar un sistema a partir de línea de comandos puede resultar complicado. Por tal motivo, existen varios grupos, comunidades y empresas que han optado por desarrollar su propio sabor de Asterisk, ofreciendo distribuciones todo-en-uno que simplifican notablemente el proceso de instalación y puesta en marcha de un conmutador IP. Estas son algunas de las más famosas:

 

FreePBX

FreePBX es la interfaz gráfica de facto para Asterisk. Por si sola, FreePBX solamente es una interfaz web, pero hoy en día existe una distribución de FreePBX con la que puedes descargar e instalar un ISO, y en un solo paso instalar Linux (una versión modificada de CentOS) + Asterisk + MySQL + Apache + FreePBX.

FreePBX nos permite simplificar el trabajo de configuración básica de Asterisk. Utiliza PHP y MySQL, y lo que hace es crear una representación más sencilla de comprender para facilitar la creación de usuarios, troncales, extensiones y otros puntos fundamentales de la configuración de Asterisk.

FreePBX es un apoyo importante para la administración de Asterisk por personal no técnico. Originalmente desarrollado y mantenido por Schmooze Com Inc, a principios de 2015 fue adquirido por Sangoma, quien es el actual desarrollador del software.

Ventajas: 

  • Prácticamente es considerada la interfaz web estandard de Asterisk.
  • Mucho tiempo en desarrollo.
  • Amplia comunidad que la soporta.
  • Te ayuda a configurar Asterisk más rápidamente.
  • Prácticamente todas las distribuciones open source disponibles hacen uso de esta interfaz.

Desventajas:

  • No todos los módulos están soportados.
  • Para mayor control tienes que recurrir a la linea de comandos a final de cuentas.
  • La distro de FreePBX utiliza una versión modificada de CentOS, con la que se ha dificultado más la creación de soluciones libres que compitan directamente con las soluciones comerciales de Sangoma.

 

Elastix

Elastix es una distribución creada por Palosanto Solutions, cuya base de operaciones está en Guayaquil, Ecuador. Elastix surgió en el 2006 como una interfaz de tarificación de llamadas para Asterisk (una herramienta para interpretar los registros de llamadas que Asterisk genera), pero rápidamente se convirtió en una suite de comunicaciones que integra varios productos en uno, ya que en un solo ISO es posible instalar en un solo paso no solamente Asterisk, sino una interfaz web de configuración como FreePBX, un sistema de base de datos (MySQL), un sistema de mensajería instantánea (OpenFire), soporte para fax (Hylafax) y un CRM (vtiger) entre otras aplicaciones más que incluye. Hoy en día Elastix es la distribución basada en Asterisk que más seguidores tiene. Al igual que Asterisk, Elastix es un proyecto open source, con lo que es libre y gratuito.

Según el roadmap de Elastix con su próxima versión 4.0, se abandonará el uso de FreePBX para usar su propia interfaz de configuración. En las versiones 2.x e inferiores, la interfaz gráfica está «amarrada» con el uso de FreePBX 2.9 e inferiores, ya que Elastix usa un wrapper (para personalizarlo con su interfaz) y no soporta versiones posteriores del GUI.

Elastix no es un reemplazo de Asterisk, sino que es un conjunto de herramientas que unidas, nos permiten hacer de manera más sencilla las labores más comunes que haríamos utilizando un sistema desde línea de comandos.

Ventajas: 

  • Sistema todo en uno.
  • Soporte incluido para señalizaciones de América Latina (R2 MFC).
  • Amplia comunidad de apoyo.
  • Existen algunos addons desarrollados por la comunidad que te permiten hacer crecer las funcionalidades de Elastix.

Desventajas:

  • Tiempos de desarrollo muy largos. Principalmente desde que decidieron utilizar su propia interfaz web.
  • Instala muchos componentes por default, los quieras usar o no.
  • Su interfaz gráfica es muy lenta y pesada (comparada con FreePBX puro)
  • Algunos componentes no han sido actualizados en mucho tiempo por romper la arquitectura propia de Elastix (ej. FreePBX 2.8, Vtiger 5.2.1)
  • Al tener muchos componentes «extras», también ha sido víctima de errores de seguridad de los mismos.
  • Relativa poca penetración en el mercado angloparlante. Su principal desarrollo ha sido en América Latina.

 

Trixbox

En sus inicios fue conocida como Asterisk@Home, y fue la primera distribución todo en uno que hacía uso de FreePBX + MySQL + PHP + CentOS + Asterisk para levantar un conmutador IP de manera rápida. En el 2006 cambia su nombre a Trixbox y se separa en las versiones CE (Community Edition) y Pro, que es el servicio de paga proporcionado por Fonality (la empresa que compró su desarrollo). Trixbox es más usada en el mercado norteamericano al estar creada originalmente en inglés y tener su base de operaciones en EUA. Sin embargo, al utilizar la misma interfaz de FreePBX, las funcionalidades que ofrece esta plataforma son casi las mismas que el resto de las distribuciones que se basan en ella.

Trixbox hace uso de un fork muy viejo de FreePBX, por lo que muchas de sus funcionalidades están atrasadas, comparadas con el resto de las distribuciones.

Ventajas: 

  • Mucho tiempo en el mercado.
  • La versión Pro te permite administrar tu PBX desde la nube.

Desventajas:

  • Sus componentes son muy viejos.
  • Sin soporte para el mercado de América Latina.
  • Poco desarrollo a la plataforma.

 

AsteriskNow

Es la distribución oficial de Digium, y al igual que las anteriores permite instalar CentOS + Asterisk en un solo paso. La diferencia primordial con las 2 anteriores es que esta es la distribución más ligera de todas, con lo que no se instalan extras (como Hud en Trixbox u Openfire, vtiger e Hylafax en Elastix). El FreePBX viene puro, por lo que puedes utilizar la versión más reciente y no estar amarrado a limitantes del desarrollador de la distribución. También es la distribución que más rápidamente ofrece las nuevas actualizaciones para Asterisk.

Un inconveniente quizá es que al ser mantenida por Digium no se ofrece el soporte precargado para las tarjetas PSTN de sus competidores (como Sangoma), por lo que si necesitas estos drivers tendrás que instalarlos por aparte.

Ventajas: 

  • Ligero.
  • Apoyado por Digium.

Desventajas:

  • Todos los extras deben ser instalados a mano.

 

PBX in a Flash

Tal como las anteriores, es una distro basada en CentOS + Asterisk + FreePBX. La principal ventaja que tiene sobre las otras es que es mantenida por un grupo de entusiastas que además de las funcionalidades de productos terminados como FreePBX, ofrecen su propia colección de mini-herramientas incluidas y fáciles de instalar con Asterisk, además de que no instalan software extra para el cual no tendremos uso (durante la instalación, tu determinas exactamente que quieres poner).

Es usada en mucho menor proporción que las anteriores, y su sistema de versionamiento puede resultar un poco complejo de entender. Sin embargo, tiene la gran ventaja de ser la más personalizable de las anteriores, por la gran cantidad de pequeños addons que es posible ponerle.

Ventajas: 

  • Altamente personalizable.
  • Diferentes versiones te permiten experimentar con diferentes componentes.

Desventajas:

  • Su sistema de versionamiento puede resultar confuso.
  • Requieres conexión a internet durante la instalación.
  • Reducida base de usuarios

 

 

Conclusiones

No importa la distribución que elijamos, todas ellas tienen algo en común: utilizan el software de Asterisk como una base para montar el resto de la experiencia para el usuario. Algunas instalan software extra, algunas te preguntan si lo quieres instalar, otras ni siquiera lo traen. ¿Cuál es la mejor de ellas para utilizar? Todo depende de nuestros hábitos, nuestros gustos y nuestra experiencia previa. Lo mejor que pueden hacer es decargarlas todas y probar una por una, hasta que sepan con cual se sienten más cómodos.

Si al final ninguna de ellas les resulta suficientemente buena, solo recuerden: todas estas distribuciones empezaron desde cero alguna vez, por lo que ustedes mismos podrían armar la suya propia con las herramientas que más les sirvan y desde allí, crear la herramienta más poderosa de comunicaciones que se ajustará completamente a sus necesidades. La decisión la tienen ustedes.

¡Suerte!

Las extensiones internas se bloquean cuando el acceso a internet se cae. ¿Cómo resolverlo?

29 Jul

Este es un problema viejo y aunque la solución puede encontrarse buscando por internet, quise tomarme unos minutos para escribir un breve post que habla del problema y como resolverlo.

Estoy seguro que a varios les ha pasado: tienes tu conmutador configurado perfectamente y todo marcha bien. De pronto, de la nada, tus extensiones internas se caen: no haces ni recibes llamadas. Revisas un poco y te das cuenta de que no tienes internet. ¿Pero para que necesito internet si mis extensiones son internas? ¿Qué tiene que ver una cosa con la otra? Es ahí donde entramos.

El problema radica en la manera en como Asterisk resuelve los dominios de las troncales SIP a donde necesita conectarse. El canal SIP utiliza un método de consulta de DNS síncrono, lo que quiere decir que cuando llega una petición de resolver un DNS (ej. siptrunk.alianza.com) el canal SIP le pregunta al servidor DNS, espera la respuesta, y cuando finalmente la obtiene continú con su siguiente petición SIP. En un mundo ideal esto no es problema, ya que la resolución por DNS es muy rápida y toma unos cuantos milisegundos, por lo que normalmente no la notamos. Pero… ¿qué ocurre cuando por alguna falla en internet, el servidor de DNS al que solemos apuntar se cae o simplemente no podemos acceder a él? Pues la respuesta es que esto provoca un efecto  dominó en Asterisk que ocasiona que todo el canal SIP se caiga.

¿Cómo puede pasar esto?

Imagina el siguiente caso:

  1. Juan quiere hacer una llamada a través de su troncal de pbx.micarrier.com, así que levanta su teléfono y llama al número deseado.
  2. Asterisk recibe la petición de llamada y le manda a su DNS la solicitud de resoler pbx.micarrier.com, pero al utilizar paquetes por UDP, Asterisk no se da cuenta de que el equipo está offiline, así que le da un cierto tiempo de timeout a que el servidor responda.
  3. Mientras que Asterisk espera a recibir la respuesta, Jorge desea hacer una llamada a través de pbx.miotrocarrier.com, pero aún no puede enviar la llamada porque Asterisk está ocupado con la petición anterior.
  4. Para cuando Juan por fin recibe la respuesta de request timeout, Jorge ya lleva buen rato esperando a que su petición apenas comience, así que muy probablemente Jorge reciba un timeout pero a nivel de SIP, porque Asterisk se tardó mucho en responderle por estar ocupado en la petición de Juan.
  5. Si a este escenario le agregan más usuarios y más carriers, el sistema se hace más complejo exponencialmente, ocasionando una serie de retrasos que eventualmebte tiran todo el servidor porque nada responde (todos se quedan esperando a todos y ninguna llamada logra salir).

Esto es un problema a nivel de código de Asterisk: si las peticiones fueran asíncronas la espera de uno no se convertiría en la espera del otro y todos serían felices. Pero como se menciona en los foros de desarrollo de Asterisk, esta es una funcionalidad que requiere mucho tiempo para ser resuelta, y que al menos en la versión 1.8.21.0, persiste.

¿Cómo lo resolvemos?

Hay algunas soluciones:

  • Editar manualmente el /etc/hosts y poner allí todos los dominios y direcciones IP que necesitemos. El problema es que esto no funciona para resolución inversa de DNS, así que tiene fallos. Otro problema es que tendríamos que agregar manualmente cada dominio, lo cual puede consumir mucho tiempo, además de que si el proveedor actualiza su IP, nuestra resolución fallaría.
  • Configurar en Asterisk las IPs fijas de cada carrier. Fácil de hacer pero de igual manera, es manual, así que nos exponemos a los problemas de tener que estar vigilando nuestro PBX constantemente.

La solución que mas predomina es la de instalar localmente (en el mismo servidor de Asterisk) un servicio de cache de DNS, como es el caso de bind. Esto hará que el equipo almacede de manera local la información de consulta frecuente para que en caso de fallos con el internet, Asterisk sobreviva del cache. Y es bastante fácil de hacer.

En CentOS/RedHat lo hacemos fácil:

yum install bind bind-utils bind-libs caching-nameserver

En Debian/Ubuntu, también es sencillo:

apt-get install bind9

En CentOS/RedHat, por default el servicio no arranca automáticamente, así que debemos decirle a Linux que lo arranque cuando el sistema prenda.

chkconfig named on

También aprovechamos y lo arrancamos:

/etc/init.d/named start

Con esto el servicio de named ya está corriendo. Ahora le decimos a Linux que lo utilice. Editamos el archivo /etc/resolv.conf y nos aseguramos que solamente contenga una linea como la que sigue:

nameserver 127.0.0.1

Si usamos Elastix, hacer el cambio también desde la interfaz web. Abrimos el menu de System > Network y editamos la configuración para dejar únicamente un DNS:

DNS en Elastix

De esta manera, obligamos a que use el local. Si el local falla, lo peor que pasa es que perdemos nuestra troncal, pero es mejor recibir una respuesta negativa del DNS a no recibir respuesta.

Podemos validar que todo está corriendo revisando que el puerto 53 esté ocupado por el servicio de named:

[root@pbx ~]# netstat -anpl | grep 53
tcp        0      0 0.0.0.0:9090                0.0.0.0:*                   LISTEN      3953/java
tcp        0      0 127.0.0.1:53                0.0.0.0:*                   LISTEN      4745/named
tcp        0      0 127.0.0.1:953               0.0.0.0:*                   LISTEN      4745/named
udp        0      0 127.0.0.1:53                0.0.0.0:*                               4745/named

Y claro está, no puede faltar la super prueba del ping que muestre que los dominios resuelven correctamente:

[root@elastix ~]# ping asteriskmx.com
PING asteriskmx.com (216.93.172.112) 56(84) bytes of data.
64 bytes from 216.93.172.112.servepath.com (216.93.172.112): icmp_seq=1 ttl=53 time=74.6 ms
64 bytes from 216.93.172.112.servepath.com (216.93.172.112): icmp_seq=2 ttl=53 time=74.5 ms
64 bytes from 216.93.172.112.servepath.com (216.93.172.112): icmp_seq=3 ttl=53 time=74.9 ms

--- asteriskmx.com ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1999ms
rtt min/avg/max/mdev = 74.563/74.723/74.912/0.265 ms

Hecho estos sencillos pasos, tu sistema estará preparado para caidas en el internet y tus troncales SIP no causarán que todo tu sistema se muera.

Solo una nota final: el servicio de named/bind al parecer vacia su cache cuando arranca. Esto quiere decir que si se va la luz y el internet al mismo tiempo, perderás la conectividad y todo se caerá (lo siento, no hay solución universal), pero en caso de que eso pase todo lo que necesitas hacer es detener el servicio y con esto tus DNS fallarán inmediatamente, con lo que te quedarás sin troncales SIP pero no si extensiones locales.

¡Suerte!

Sistema de Atención al Cliente con WebRTC y Elastix-CallCenter.

26 Jul

Hola, en este artículo vamos a crear un sistema de atención a cliente usando las herramientas WebRTC-SIPML5 y Elastix junto con su addon de Call Center. La idea general es generar 0 costos entre el usuario y nuestro centro de atención. Es por esto que vamos a usar WebRTC y Elastix dos herramientas open source las cuales van a interactuar usando nuestra conexión de internet.

Paso 1.

Instalar el soporte de WebRTC para el módulo de Call Center de Elastix que publiqué anteriormente.

Paso 2.

Crear una cola de atención:

  • Crear al menos 2 dispositivos SIP. Menú PBX—>Extensions.
  • Crear una cola de atención. Menú PBX—>Queue.
  • Asignar a la nueva cola de atención uno de los dispositivos SIP como miembro dinámico y usando el prefijo S, por ejemplo, para el dispositivo 1500 quedará de la siguiente manera: S1500,0
  • Seleccionar el ‘ring strategy'(estrategia de timbrado) con ‘fewestcalls'(menos llamadas recibidas).
  • Añadir los Anuncios necesarios.
  • Aplicar los cambios.

Paso 3.

Configurar el Módulo de Call Center de Elastix, para recibir llamadas:

  • Ir al menú ‘Agent Options'(Opciones de agente).
  • Crear al menos una ‘callback extension'(Extension callback). Tiene que coincidir con el dispositivo que se añadió como miembro a la cola de atención.
  • Ir a ‘Ingoing calls'(llamadas entrantes)—> Queues(Colas).
  • Seleccionar la Cola de atención previamente creada.

Paso 4.

Crear nuestra página web que será desde donde nuestros usuarios van a contactarnos(el diseño de la página web queda al gusto y capacidades de cada quien).

Para usar la API de SIPML5 pueden checar el ejemplo online con el que ellos cuentan o su documentación. Les dejo el código con el que llevo trabajando en algunos proyectos y el cual estoy usando en este artículo:

 

<head>
       <title>Navaismo's ElastixCC-WebRTC</title>
    <!-- SIPMl5 API for WEBRTC calls -->
        <script src="js/SIPml-api.js"></script>
        <link href="css/bootstrap.min.css" media="screen" rel="stylesheet"></link>
</head>

<script>

 //Variables
 var mySipStack;
        var mycallSession;
 var myregisterSession;

 // readycallback for INIT
 var readyCallback = function(e){
  console.log("engine is ready");

  //CHeck if the SIPml start
  if(SIPml.isInitialized() == 1){
   console.log("Done to initialize the engine");
   //If the stack is started, create the sip stack
   startSipStack();
  }else{
   //If not started display console msg
   console.log("Failed to initialize the engine");
  }          
 }

 // error callback for INIT
 var errorCallback = function(e){
  console.error('Failed to initialize the engine: ' + e.message);
 }

 //INIT SIPML5 API
 SIPml.init(readyCallback, errorCallback);

 //Here we listen stack messages
 function listenerFunc(e){
  //Log incoming messages
  tsk_utils_log_info('==stack event = ' + e.type);

  switch(e.type){

   //If failed msg or error Log in console & Web Page
   case 'failed_to_start': case 'failed_to_stop':  case 'stopping': case 'stopped': {

    console.log("Failed to connect to SIP SERVER")
    mycallSession = null;
    mySipStack = null;
    myregisterSession = null;

    $("#mysipstatus").html('');
    $("#mysipstatus").html('<i>Disconnected: </i>'+e.description);

    break;
   }

   //If the msg is 'started' now try to Login to Sip server           
          case 'started': {
                  console.log("Trying to Login");

    login();//function to login in sip server

    //Display msg in the web page
    $("#mysipstatus").html('');
    $("#mysipstatus").html('<i>Trying to Connect</i>');

    break;
   }

   //If the msg 'connected' display the register OK in the web page 
   case 'connected':{
    $("#mysipstatus").html('');
    $("#mysipstatus").html('<i>Registered with Sip Server</i>');

    break;
   }

   //If the msg 'Sent request' display that in the web page---Pattience
   case 'sent_request':{

    $("#mysipstatus").html('');
    $("#mysipstatus").html('<i>'+e.description+'</i>');

    break;
   }

   //If the msg 'terminated' display that on the web---error maybe?
   case 'terminated': {
    $("#mysipstatus").html('');
    $("#mysipstatus").html('<i>'+e.description+'</i>');

    break;
   }

   //If the msg 'i_new_call' the browser has an incoming call
   case 'i_new_call': {
     if (mycallSession) {
                          // do not accept the incoming call if we're already 'in call'
                          e.newSession.hangup(); // comment this line for multi-line support
                  }else{

     mycallSession = e.newSession;

     //Change buttons values
                   btnCall.value = 'Answer';
                  btnHangUp.value = 'Reject';
                          btnCall.disabled = false;
                          btnHangUp.disabled = false;

     //Start ringing in the browser
                          startRingTone();

     //Display in the web page who is calling
                   var sRemoteNumber = (mycallSession.getRemoteFriendlyName() || 'unknown');
                   $("#mycallstatus").html("<i>Incoming call from [<b>" + sRemoteNumber + "</b>]</i>");
                   showNotifICall(sRemoteNumber);
    }
    break;
   }

   case 'm_permission_requested':{
                         break;
                 }
              case 'm_permission_accepted':
          case 'm_permission_refused': {
    if(e.type == 'm_permission_refused'){

            btnCall.value = 'Call';
            btnHangUp.value = 'HangUp';
            btnCall.disabled = false;
            btnHangUp.disabled = true;

            mycallSession = null;

            stopRingbackTone();
            stopRingTone();

             $("#mysipstatus").html("<i>" + s_description + "</i>");

                      }
                      break;
                 }
   case 'starting': default: break;
         }            
 }

 //Function to Listen the call session events
 function calllistener(e){
  //Log all events
  tsk_utils_log_info('****call event**** = ' + e.type);

  switch(e.type){

   //Display in the web page that the call is connecting
   case 'connected': case 'connecting': {

        var bConnected = (e.type == 'connected');
                      if (e.session == myregisterSession) {                          
                           $("#mycallstatus").html("<i>" + e.description + "</i>");

                      }else if (e.type == 'connecting') {                          
                           $("#mycallstatus").html("<i>" + e.description + "</i>");

    }else if (e.session == mycallSession) {
                          btnHangUp.value = 'HangUp';

                         if (bConnected) {
                           stopRingbackTone();
                           stopRingTone();
     }
                             }
    break;
                        }

   //Display in the browser teh call is finished
   case 'terminated': case 'terminating': {

    if (e.session == mycallSession) {
                          mycallSession = null;
                         myregisterSession = null;

                          $("#mycallstatus").html("<i>" + e.description + "</i>");
            stopRingbackTone();
            stopRingTone();

                 }else if (e.session == mycallSession) {

            btnCall.value = 'Call';
            btnHangUp.value = 'HangUp';
            btnCall.disabled = false;
            btnHangUp.disabled = true;

            mycallSession = null;

            stopRingbackTone();
            stopRingTone();
                        }
    break;

   }  

   // future use with video
           case 'm_stream_video_local_added': {
                      if (e.session == mycallSession) {

                          }
                      break;
                 }

   //future use with video
                case 'm_stream_video_local_removed': {
                      if (e.session == mycallSession) {

                          }
                      break;
                 }

   //future use with video
         case 'm_stream_video_remote_added':  {
                      if (e.session == mycallSession) {

                          }
                         break;
              }

   //future use with video
          case 'm_stream_video_remote_removed': {
                      if (e.session == mycallSession) {

                          }
                      break;
                 }

   //added media audio todo messaging
                 case 'm_stream_audio_local_added':
   case 'm_stream_audio_local_removed':
          case 'm_stream_audio_remote_added':
          case 'm_stream_audio_remote_removed': {

                  stopRingTone();                   
                         stopRingbackTone();

                      break;
                 }

   //If the remote end send us a request with SIPresponse 18X start to ringing
   case 'i_ao_request':{
                         var iSipResponseCode = e.getSipResponseCode();
                         if (iSipResponseCode == 180 || iSipResponseCode == 183) {
                   startRingbackTone(); //function to start the ring tone
     $("#mycallstatus").html('');
                              $("#mycallstatus").html('<i>Remote ringing...</i>');
     $("#btnHangUp").show();
                         }
    break;
   }

   // If the remote send early media stop the sounds
   case 'm_early_media': {
                  if (e.session == mycallSession){ 
           stopRingTone();                   
                          stopRingbackTone();
     $("#mycallstatus").html('');
     $("#mycallstatus").html('<i>Call Answered</i>');
    }
    break;
   }
                }

 }

 //function to send the SIP Register
 function login(){
  //Show in the console that the browser is trying to register
  console.log("Registering");

  //create the session
         myregisterSession = mySipStack.newSession('register', {
                 events_listener: { events: '*', listener: listenerFunc } // optional: '*' means all events
                });

  //send the register
        myregisterSession.register();
 }

 // function to create the sip stack
 function startSipStack(){
  //show in the console that th browser is trying to create the sip stack
  console.info("attempting to start the SIP STACK");

  //stack options
  mySipStack  = new SIPml.Stack({
          realm: 'asterisk',
          impi: 'usuario',
          impu: 'sip:usuario@myip',
          password: 'mipassword', // optional
          display_name: 'TS', // optional
          websocket_proxy_url: 'ws://miip:10060', // optional
          outbound_proxy_url: 'udp://miip:5060', // optional
          //ice_servers: [{ url: 'stun:stun.l.google.com:19302'}, { url:'turn:user@numb.viagenie.ca', credential:'myPassword'}], // optional
          enable_rtcweb_breaker: true, // optional
          enable_click2call: false, // optional
          events_listener: { events: '*', listener: listenerFunc }, //optional
          sip_headers: [ //optional
              {name: 'User-Agent', value: 'DM_SIPWEB-UA'}, 
              {name: 'Organization', value: 'Digital-Merge'}
          ]
      });
  //If the stack failed show errors in console
  if (mySipStack.start() != 0) {
                 console.info("Failed to start Sip Stack");
             }else{
                 console.info("Started the Sip Stack");
  }

 }

 //Fucntion to call/answer
 function call(){
  var calltype;

  if(mySipStack){
   //create the session to call
          mycallSession = mySipStack.newSession('call-audio', {
                  audio_remote: document.getElementById('audio_remote'),
                  audio_local: document.getElementById('audio_local'),
                  video_remote: document.getElementById('video_remote'),
                  video_local: document.getElementById('video_local'),
                      events_listener: { events: '*', listener: calllistener } // optional: '*' means all events
                 });
   $("#mycallstatus").show();
   //call using the number 80000
          mycallSession.call("80000");
  }else if(!mySipStack){
   alert('Stack not ready!');

  //If the textbox is empty and the button call is ANSWER, then is a incoming call
  }else if(flag =='Answer' && mySipStack && mycallSession){

                        stopRingbackTone();
                        stopRingTone();                   

   //Accept the session call
   mycallSession.accept({
                                audio_remote: document.getElementById('audio_remote'),
                                audio_local: document.getElementById('audio_local'),
                                events_listener: { events: '*', listener: calllistener } // optional: '*' means all events
                        });
  }
 }

 //function to hangup the call
 function hangup(){
  //If exist a call session, hangup and reset button values
  if(mycallSession){
          mycallSession.hangup({events_listener: { events: '*', listener: calllistener }});
                        stopRingbackTone();
                        stopRingTone();                   
                        btnCall.value = 'Call';
                        btnHangUp.value = 'HangUp';
   $("#callnumber").attr('value','');
   $("#mycallstatus").html("Call Terminated")
   $("#btnHangUp").hide();
   //destroy the call session
   mycallSession = null;

  }else{
   $("#callnumber").attr('value','');
  }   

 }

 //Fucntion to send DTMF frames
 function sipSendDTMF(c){
         if(mycallSession && c){
              if(mycallSession.dtmf(c) == 0){
                  try { dtmfTone.play(); } catch(e){ }
              }
         }else{
   var lastn = $("#callnumber").val();

   $("#callnumber").val( lastn + c );
          try { dtmfTone.play(); } catch(e){ }

  }

     }

/**************** fucntion to play sounds *******************/
    function startRingTone() {
        try { ringtone.play(); }
        catch (e) { }
    }

    function stopRingTone() {
        try { ringtone.pause(); }
        catch (e) { }
    }

    function startRingbackTone() {
        try { ringbacktone.play(); }
        catch (e) { }
    }

    function stopRingbackTone() {
        try { ringbacktone.pause(); }
        catch (e) { }
    }

   function showNotifICall(s_number) {
        // permission already asked when we registered
        if (window.webkitNotifications && window.webkitNotifications.checkPermission() == 0) {
            if (oNotifICall) {
                oNotifICall.cancel();
            }
            oNotifICall = window.webkitNotifications.createNotification('images/sipml-34x39.png', 'Incaming call', 'Incoming call from ' + s_number);
            oNotifICall.onclose = function () { oNotifICall = null; };
            oNotifICall.show();
        }
    }

</script>

<div class="col">
            <h2>Llamenos Usando WebRTC</h2>
ll <audio id='audio_remote'></audio>
                        &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
   <button class="btn btn-primary" id="btnCall" onclick="call()" >Click Aquí Para Llamarnos!</button>

 &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
   <button class="btn btn-danger hide" id="btnHangUp" onclick="hangup()" >Colgar Llamada</button>

                        &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp;
   <span class="label hide" id="mycallstatus"></span>

 &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; 
   <span class="label label-inverse" id="mysipstatus">Si ve esta etiquete usted necesita usar Chrome para llamar</span>

                        <p style="font-size: 10px;">WebRTC es una nueva tecnología que usa el Navegador Web para establecer una llamada, en este caso la llamada es de Audio y Usted necesita contar con una diadema(o bien micrófono y bocinas) para llamarnos sin costo alguno.
Esta tecnología por ahora solo es compatible con el navegador Chrome para descargarlo <a href="">de click aqui.</a>
                </div>
                <div class="col"> ll

Si van a usar el ejemplo anterior sólo tienen que editar el siguiente código con los datos de su dispositivo SIP.

//stack options
mySipStack = new SIPml.Stack({
realm: 'asterisk',
impi: 'usuario',
impu: 'sip:usuario@myip',
password: 'mipassword', // optional
display_name: 'TS', // optional
websocket_proxy_url: 'ws://miip:10060', // optional
outbound_proxy_url: 'udp://miip:5060', // optionalk

Y lo más importante es apuntar el número de la llamada hacia nuestra cola de atención previamente creada, eso se logra editando el código(en éste ejemplo la cola creada tiene el número 80000):

//call using the number 80000
mycallSession.call("80000");

Al finalizar tendrán algo como esto:

Paso 5.

Probar el sistema. Video Demo(sin audio por problemas técnicos):

 

Notas.

Es claro que este ejemplo sólo usa un peer SIP, sin embargo estos son algunos hints para crear múltiples usuarios.

  • Crear una página web de atención donde primero verifique los peers registrados.
  • Basados en el punto anterior elegir qué peer registrar.
  • Crear un formulario para que el usuario registre algunos datos básicos como su nombre y pasar estos datos con el peer de registro.
  • Adaptar realtime o bien escribir directamente en la base de datos de asterisk para crear peers ‘dinámicos’.
  • Crear una página web para clientes/usuarios registrados y crear extensiones/peers basados en estos datos, así mismo se pueden crear campañas entrantes con estos datos.

El registro no es necesario realmente, pero de esta forma demuestro que es posible interactuar con asterisk y WebRTC.

WebRTC no necesita de Asterisk para lograr esto, de hecho lo puede hacer Peer to Peer(punto a punto) como lo haría la fantástica aplicación llamada Twelephone, sin embargo este artículo esta diseñado para integrar un sistema de atención online con Elastix y su módulo de call center.

Se puede añadir video a las llamadas ;).

Asterisk Plano también es capaz de esto(y más) usando extensiones directamente o bien una cola de atención. Es solo que estoy de buenas con Elastix. 😀

Para aquellos que no sólo quieran tener soporte en Chrome existe una extensión que permite a los navegadores como Internet Explorer y Opera funcionar con WebRTC se llama webrtc4all.

Recuerden su feedback es bienvenido.

Integrando WebRTC al módulo de call center de Elastix

27 Jun

Hola, quienes me conocen saben que yo prefiero usar Asterisk Plano sobre cualquier cosa. Cuando la gente me dice que quieren usar sí o sí una GUI para administrar su asterisk porque no se quieren meter en líos, recomiendo FreePBX. Son pocas las veces que me veo en la necesidad de usar Elastix o recomendarlo de hecho me puedo considerar hasta cierto punto un ‘hater’ de esa Distro; sin embargo, como dice la canción: «…No todo es blanco, O negro: es gris Todo depende del matiz…«, debo decir que una de las cualidades más atractivas de Elastix es su Módulo de Call Center. El cual permite crear campañas de Salida o de Entrada de manera más que sencilla y que su operación es muy pulcra, sin contar además que, integrar nuestro propio CRM es sumamente sencillo.

Este año vendrá de nueva cuenta el ElastixWorld a México y esta vez si pienso asistir ya que dejar pasar las conferencias de las personalidades que vendrán sería un error y más si tomo en cuenta que asistir al Astricon en Atlanta es prácticamente imposible para mi. Es por esta razón que decidí «contribuir»(si se le puede llamar así a una simple edición de código) un poquito al proyecto de Elastix, no quiero ir solo a recibir información invaluable al ElastixWorld sin haber «contribuido» en algo.

Elegí un tema de «moda»: WebRTC y una herramienta útil como el Módulo de Call Center que combinadas podrían tener mucho éxito-según yo-. Así que me di a la tarea de integrar la fantástica API SIPML5 y el Gateway WebRTC2SIP ambos de Doubango a una instalación de Elastix.

Notas sobre la integración y operación

  • Se requiere una versión de ‘libtool’ más reciente de modo que se instala la version 2.4.2
  • Se requiere instalar el Gateway WebRTC2SIP para su operación ya que la versión de Asterisk no soporta WebRTC. La conexión es ws.
  • Solo funciona correctamente con el explorador Google Chrome.
  • Se modifican algunos archivos(consideren respaldarlos antes) utilizados por Elastix entre ellos:
    • /var/www/html/themes/elastixneo/_common/_menu.tpl
    • /var/www/html/modules/agent_console/index.php
    • /var/www/html/agent_console/themes/default/agent_console.tpl
  • La Integración solo funciona con el tema default ‘Elastixneo’.
  • Debido a mi falta de conocimiento tuve que crear una conexión externa a MySQL para obtener el password del ‘Peer’.
  • Se han creado algunos archivos extras:
    • Conexión a la BD: /var/www/html/libs/astdb.php
    • Javascript para el Teléfono: /var/www/html/modules/agent_console/themes/default/js/ml5.js
    • SIPml5 API: /var/www/html/modules/agent_console/themes/default/js/SIPml-api.js
    • Estilos para botones del Teléfono: /var/www/html/modules/agent_console/themes/default/css/bootstrap.css
    • Sonidos utilizados por el Teléfono: /var/www/html/modules/agent_console/themes/default/sounds
  • No se puede marcar desde el teléfono. Esto lo hice así pensando en que el agente sólo debe de recibir las llamadas de una cola Entrante o una campaña del Dialer (se puede modificar a futuro).
  • Si no se está en llamada y se presiona el botón (CALL/Answer) se enviará la llamada al buzón de voz del Agente.
  • La integración esta hecha para trabajar  solamente en modo ‘Agent CallBack’. Esto se debe a la facilidad de tener el teléfono registrado en ‘Stand-by’ una vez que se accedió a la consola del Agente. 

El gateway WebRTC2SIP corre bajo una sessión ‘screen’ con el comando:

screen -dmS wrtc /usr/local/sbin/webrtc2sip --config=/usr/local/sbin/config.xml

Para ingresar a la sesión usar:

screen -r wrtc

Para salir de la sesión sin detener el gateway usar la combinación de teclas CTRL+A+D. Para detener el Gateway, entrar en la sesión y escribir ‘quit’+ENTER.

Instalación

Descargar el tarball desde este enlace. Y extraer el contenido con el comando:

# tar zxvf Elastix_CC_ML5.tar.gz

Cambiar al directorio creado con:

# cd ccml5

Instalar el gateway webrtc2sip.

Para instalar el Gateway ejecutar el siguiente comando:

# ./install_wrtcgw.sh

Esto instalará el framework de doubango, las dependencias y el gateway, dependiendo de la velocidad del equipo puede tomar desde 30 minutos hasta un par de horas.

Añadir el Teléfono WebRTC a la consola de Agente.

Para integrar el teléfono a la consola de agente ejecutar el siguiente comando:

# ./install_webfiles.sh

Si no se ha instalado el Módulo de Call Center previamente, el script preguntará si desean instalarlo. Basta presionar Y+enter para que así lo haga.

Los imágenes del Módulo ya editado:

Inicio de Sesión Consola de Agente modo ‘Callback login’

Consola de Agente con el Telefóno WebRTC Insertado

Demo

Para configurar y usar el Módulo del Call Center de Elastix pueden usar el manual oficial que esta en este enlace Recuerden que el Teléfono WebRTC solo trabajará correctamente en el modo ‘callback login’ en el manual se refieren a él como modo dinámico.

Yo ya tengo preparado una cola de entrada sin datos y una campaña saliente con un CRM Custom. Y este es el resultado:

Inbound Queue:

Outgoing Campaign:

Feedback: Como nada en esta vida es perfecto cualquier feedback se agradecerá. 😉

Versión mejorada del mensaje “all circuits are busy” de Elastix/FreePBX (v4)

8 Abr

Actualización 2016-01-13: La liga de los archivos de sonido ya está disponible nuevamente.

Hace casi 2 años escribí un artículo sobre como mejorar los mensajes de código de error en llamadas por E1 para Elastix. El tiempo ha pasado y han habido cambios tanto en Asterisk como en Elastix, ocasionando que algunas partes de ese viejo post ya no funcionen, por lo que decidí reescribirlo y mejorarlo.

Este código les permitirá dar mensajes más descriptivos para los códigos de error Q931 que los enlaces digitales arrojan al momento en que una llamada sale mal. Con respecto del post anterior, estas son las mejoras:

  • Existen más códigos de error documentados.
  • Las voces son sintetizadas con acento neutro (para todos los que nos leen en América Latina, esto es más cómodo que el acento español que se tenía antes)
  • Ya solo se requiere meter el código en un único archivo .conf, no es necesario usar el .ael.

Según la versión de Elastix que estemos ocupando, el código debe insertarse en uno de los 2 archivos siguientes:

  • /etc/asterisk/extensions_override_elastix.conf
  • /etc/asterisk/extensions_override_freepbx.conf

Preferentemente usar el extensions_override_elastix.conf, pero si no existe, usar el otro. Coloquen este código al final del archivo:

[macro-outisbusy]
exten => s,1,Progress
exten => s,n,Set(MSG=all-circuits-busy-now&pls-try-call-later) ; Clausula default
exten => s,n,Goto(s-${HANGUPCAUSE},1)
 
; Numero no existe
exten => s-1,1,Set(MSG=no-existe)
; No hay una ruta para llegar a este equipo. Tratar como no existe
exten => s-2,1,Set(MSG=no-existe)
; Celular fuera de area de servicio
exten => s-20,1,Set(MSG=celular-no-disponible&intente-mas-tarde)
; Numero mal marcado
exten => s-28,1,Set(MSG=cannot-complete-as-dialed&check-number-dial-again)
; Numero fuera de servicio
exten => s-27,1,Set(MSG=fuera-servicio&intente-mas-tarde)
; Red fuera de servicio
exten => s-38,1,Set(MSG=fuera-servicio&intente-mas-tarde)
; Falla temporal en la red
exten => s-41,1,Set(MSG=falla-red&intente-nuevamente)
; Congestion por alta cantidad de trafico
exten => s-42,1,Set(MSG=alto-trafico&intente-mas-tarde)
; La central telefonica no dio respuesta
exten => s-102,1,Set(MSG=falla-red&intente-nuevamente)
; Problema de interconexion de carriers
exten => s-127,1,Set(MSG=no-proveedor&intente-mas-tarde)
 
exten => _s-.,n,Playback(${MSG},noanswer) ; Reproducir el mensaje
exten => _s-.n,Macro(hangupcall)

Los nuevos archivos de sonido pueden descargarse desde la liga Sonidos mejorados para Elastix. Recuerden que hay que desempaquetar el .zip y el contenido subirlo a la carpeta /var/lib/asterisk/sounds para que puedan reproducirse sin problemas.

¡Suerte!

Restringe el acceso a carpetas específicas usando Apache

18 Dic

url7[1]Nosotros nunca recomendamos exponer la interfaz web de tu conmutador (entiéndase: FreePBX/Elastix/Trixbox) bajo ningún motivo. Sin embargo, hay casos específicos en los cuales puede existir la necesidad de abrir el puerto HTTP/HTTPS para algunos servicios (ej. un CRM o alguna aplicación in house). Si esto debe de hacerse, es mejor hacerlo teniendo en cuenta algunas funciones básicas de seguridad.

Si aplicáramos lo que vimos en nuestros artículos del modelo de seguridad en Asterisk veremos que no es posible defender a nivel de firewall externo/iptables, ya que no hay una manera de permitir el paso solamente a ciertas carpetas. Para lograrlo, debemos hacer uso de la configuración de Apache la cual nos permite aplicar un mini firewall interno que solo permitirá a ciertas IPs ver ciertas páginas.

Primero, localicemos el archivo /etc/httpd/conf/httpd.conf (en algunos casos cambia de nombre a /etc/apache2/httpd.conf, así que hay que buscarlo con calma). Lo que buscamos es encontrar una directriz que permite el acceso por default a toda nuestra carpeta /var/www/html. El default en Elastix lo encuentran escrito así:

<Directory "/var/www/html">

#
# Possible values for the Options directive are "None", "All",
# or any combination of:
#   Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews
#
# Note that "MultiViews" must be named *explicitly* --- "Options All"
# doesn't give it to you.
#
# The Options directive is both complicated and important.  Please see
# http://httpd.apache.org/docs/2.2/mod/core.html#options
# for more information.
#
    Options Indexes FollowSymLinks

#
# AllowOverride controls what directives may be placed in .htaccess files.
# It can be "All", "None", or any combination of the keywords:
#   Options FileInfo AuthConfig Limit
#
    AllowOverride None

#
# Controls who can get stuff from this server.
#
    Order allow,deny
    Allow from all

</Directory>

Haciendo a un lado los comentarios, quedamos con estas instrucciones:

<Directory "/var/www/html">
SymLinksifOwnerMatch ExecCGI MultiViews
    Options Indexes FollowSymLinks
    AllowOverride None
    Order allow,deny
    Allow from all
</Directory>

Queremos ponerle atención a las lineas de Order allow,denyAllow from all. Explicamos lo que quiere decir:

  • Order allow,deny quiere decir que las instrucciones están declaradas como Permite primero, niega después.
  • Allow from all es una cláusula que permite que todos vean todo (default), lo cual le quita todas las restricciones a los usuarios.

El primer paso consiste en cerrar todo y permitir el paso solamente a direcciones IPs de confianza. Esto lo podemos conseguir con lo siguiente:

<Directory "/var/www/html">
SymLinksifOwnerMatch ExecCGI MultiViews
    Options Indexes FollowSymLinks
    AllowOverride None
    Order deny,allow
    Deny from all
    Allow from 192.168.1.0/24
    Allow from 127
    Allow from 10.200.1.0/24
</Directory>

¿Notan la diferencia? Ahora estamos negando el paso a todo y permitiendo solamente que las redes de confianza 192.168.1.0/24, 10.200.1.0/24 y la de loopback tengan acceso completo al servidor. En otras palabras: solo las redes locales van a poder entrar a la administración web completa.

Para crear ahora carpetas sin restricción (ej. que cualquier usuario interno o externo las pueda ver), las agregamos como directrices nuevas:

<Directory "/var/www/html/vtigercrm" >
   Order deny,allow
   Allow from all
</Directory>

Y ahora estamos permitiendo el acceso completo a esa carpeta en particular (nuestro CRM). Por cada carpeta debemos crear una directiva <Directory> que permita el acceso.

Recuerden reiniciar el servicio de Apache:

/etc/init.d/httpd restart

# Según nuestra distro, el servicio se llama Apache2
/etc/init.d/apache2 restart

De esta manera, si abrimos http://<direccion ip publica>/vtigercrm podremos ver el CRM de nuestro equipo, pero si tratáramos de ver nuestra interfaz de administración con http://<direccion ip publica>/adminhttp://<direccion ip publica>

Advertencia: Siempre asegúrate de probar tu configuración de seguridad antes de ponerla en producción. Nosotros nunca recomendamos exponer el puerto HTTP/HTTPS, ya que aunque al usar esta configuración no permites el paso a otros usuarios, no quiere decir que no haya vulnerabilidades en tu código mostrado por Apache, así que acepta el riesgo de exponer tu equipo así. Lo mejor que puedes hacer es crear un nuevo equipo que hostee tu aplicación y exponer ese, sin que tengas abierto lo mismo en tu conmutador.

¡Suerte!

Día de campo con Elastix: armando la canasta de comunicaciones completa

4 Nov

Los equipos en sus cajas, antes que todo comenzara

Con motivos del Elastix World que se está celebrando en estos momentos en la ciudad de México, quisimos armar un proyecto mixto que habíamos comentado en la oficina desde hace tiempo. Este proyecto consistiría en crear una solución demostrativa llave en mano de un sistema de comunicaciones basado totalmente en soluciones abiertas. Lo que buscábamos era lo siguiente:

  • Crear un sistema que fuera extremadamente portátil, de manera que pudiéramos demostrarlo ante los potenciales clientes para sistemas más robustos
  • El consumo total energético (PBX + switch + teléfonos) debe ser menor de 100W
  • Crear una prueba de concepto de VoIP, que demostrara la convergencia de soluciones con teléfonos análogos, teléfonos IP, teléfonos inalámbricos e interacción con líneas analógicas de la PSTN
  • Demostrar que es posible montar una solución de PBX completo (lineas, tarjetas, PC, switch) por menos de $1,000 USD
El proyecto comenzó con un viaje a nuestro supermercado local para conseguir un contenedor de plástico que nos permitiera la portabilidad así como la posibilidad de contener todo nuestro equipo. Encontramos uno que se adaptaba completamente a lo que buscábamos, y nos costó apenas $5 USD

Nuestra caja. Originalmente era un contenedor para guardar archivos

Para cuando decidimos hacer este proyecto, ya teniamos definido el hardware que queríamos utilizar. Para el PBX ocuparíamos una Acer Veriton VN281G-SD425L, la cual tiene 1 GB de RAM, Atom @ 1.8 Ghz, DD 250GB y Gigabit ethernet, además de ser la PC más pequeña que conocemos en el mercado actualmente. Como este equipo es ultra pequeño, el uso de tarjetas PCI no era una opción, así que le conectamos una Sangoma U100 para dar servicio a 2 puertos FXO:

El µPBX junto con su tarjeta U100

Al Veriton le instalamos Elastix 2.2 RC3 y funcionó de maravilla (tuvimos que usar un DVD externo ya que la unidad en si no trae almacenamiento óptico), así que procedimos a dejarla configurada con 30 extensiones y 2 lineas FXO, así como todos los detalles necesarios del plan de llamadas.
El siguiente paso fue empezar a organizar los componentes para darles una óptima colocación, procurando mantener la instalación lo más limpia y fácil de conectar posible. Decidimos utilizar velcro autoadherible, así no tendríamos que desgastar ninguno de los equipos de manera permanente en caso de que el proyecto no resultara, al tiempo que nos permitir re-organizar los diferentes dispositivos en caso de que lo necesitáramos.
Armar el equipo no fue demasiado complicado dadas las dimensiones de la caja (29 x 30 x 28 cms), pero nos emocionamos tanto armándola que no tomamos fotos durante el proceso. Aquí tenemos el resultado final:

Caja final y armada

Aquí el comparativo con un objeto de uso cotidiano:

 

El último paso fue permitir el paso de los cables de una manera sencilla hacia el exterior (energía, red y PSTN). Para ello hicimos un agujero en uno de los extremos de la caja utilizando un cautín convencional para hacer el perforado del plástico. Estos fueron los resultados:
Y ya por último tenemos la caja con los componentes fijos, los cables que salen libremente por donde deben y los teléfonos conectados (2 IPs, 2 inalámbricos, 1 analógico y 1 cable para la PSTN)

Vista aérea con la caja abierta

Vista frontal conectado

En resumen, nuestra maqueta es capaz de soportar lo siguiente:
  • 4 teléfonos IP con PoE
  • Hasta 8 teléfonos inalámbricos
  • 2 teléfonos análogos
  • 2 lineas análogas (FXO)
  • 30+ softphones
  • 15+ llamadas simultáneas
  • Y es totalmente portable:
Pero… ¿y cuanto cuesta armar una solución así?
  • Acer Veriton: $270 USD
  • Elastix: $0 USD
  • Switch Cisco SDP208: $170 USD
  • Cisco PAP2T:  $69 USD
  • Teléfono Yealink T20P (2): $190 USD
  • Aastra MBU400: $299 USD
  • Sangoma U100: $140 USD
  • Teléfono analógico: $10 USD
  • Barra de contactos, caja y cables: $10 USD
  • Total: $1,158 USD
Algunas notas:
  • Es obvio que nos salimos de nuestro presupuesto de $1,000 USD, pero la MBU400 fue agregado de última hora. Sin ella, el costo del sistema habria sido de $859 USD.
  • En la foto aparece que utilizamos un Cisco SPA504G como segundo teléfono. Esto lo hicimos para demostrar la interactividad entre marcas, pero en los precios optamos por Yealink por ser la opción más económica
En conclusión, este equipo fue fabricado con intenciones de ser un equipo demostrativo multimarca, que pudiéramos transportar fácilmente para demostrar el uso de un IP PBX basado en open source (Elastix) en la mayoría de los escenarios. Logramos mantener un consumo bajo de energía y una excelente prueba de concepto de como podemos meter muchísimas funcionalidades en una pequeña caja, desde teléfonos inalámbricos hasta teléfonos IP, todo alimentado por PoE para reducir el cableado al mínimo. Sabemos que el equipo tiene sus limitantes y que para escenarios más grandes no sería realizable tener algo de este tamaño, pero ese no era el objetivo del proyecto, y terminamos con una maqueta que nos hizo recordar como armábamos los proyectos de electrónica mientras que estábamos en la universidad.
¡Ah! Lo olvidaba: todos los componentes utilizados (excepto la caja de plástico) pueden encontrarlos en nuestra tienda en línea.
¡Suerte!