jueves, 28 de marzo de 2013

Agregando tareas propietarias a cámaras Ubiquiti

(english version)

Luego de utilizar cámaras IP Axis durante bastantes años (con resultados no tan satisfactorios en el largo plazo), comencé a considerar alternativas. Así es como pasaron por mis manos distintas marcas y modelos que no alcanzaban a cubrir las mínimas espectativas que pretendía cubrir para dejar definitivamente de lado las cámaras Axis. Parece una broma de mal gusto que la mayoría de estas cámaras cuyo firmware suele estar basado en Linux, poseen una interfaz demasiado orientada a navegadores como Internet Explorer, mientras que la inspección desde un navegador en Linux suele verse mal, con las pantallas deformadas, las opciones de menú inutilizables, entre otros defectos.

Eventualmente comencé a realizar algunas pruebas con una cámara Ubiquiti AirCam mini. Desde un principio parecía poseer las características necesarias para cubrir el uso pretendido. Además me interesaron algunas funcionalidades extras, tales como que posee un agente de SNMP, e inclusive posee un servidor de SSH, el cual permite ingresar a la cámara en modo consola.

Al momento de configurar el servicio de dns dinámico me encontré con un problema: aparentemente está preparado para funcionar solo con el servicio de dyndns.org, que lamentablemente dejó de poseer cuentas gratuitas. El manual de la cámara dice lo contrario, en cuanto a que afirma soportar una amplia gama de servicios de dns dinámico, pero lo cierto es que el archivo de log de la conexión invalida esta afirmación. Al configurar el servicio de dns dinámico con una cuenta de no-ip.com, el log de la cámara muestra que el cliente de dns dinámico sigue intentando conectarse al sitio http://members.dyndns.org. Indagando sobre el tema, encontré una forma de agregar tareas o servicios a la cámara sin necesidad de rehacer el firmware (aunque esta última también es una opción válida, ya que ubiquiti provee del código fuente del firmware).

Investigando el entorno

Accediendo por SSH a la cámara, nos encontramos con un sistema de archivos típico de una distribución Linux en un sistema embedido (la referencia de los binarios a busybox, por poner un ejemplo). Indagando un poco en el directorio /etc/rc.d, nos encontramos con el script rc.sysinit encargado de configurar parámetros del kernel de la cámara, y el script rc, encargado de levantar los distintos servicios que corren en ella.

Este último script hace uso de funciones definidas en el archivo /usr/etc/rc.d/rc.funcs, particularmente la función rc_start(), donde podemos encontrar referencias a scripts externos de nombre /etc/persistent/rc.prestart y /etc/persistent/rc.poststart. Tales como su nombre lo indica, estos scripts son ejecutados (si existen) en la etapa previa y posterior del arranque de los servicios, respectivamente. El directorio /etc/persistent permite almacenar otros scripts y archivos propios, y solo debemos acordarnos de grabar la configuración luego de realizado los cambios, para que las modificaciones no se pierdan en el siguiente reinicio de la cámara.

Entre los servicios soportados por la cámara que no están arrancado desde un comienzo, se encuentra el demonio crond, que permite ejecutar tareas de manera periódica. Vamos a aprovecharnos de ese servicio para ejecutar un script de manera periódica, que se fijará si el IP público de nuestra conexión se ha modificado, y en ese caso actualizará la información en los registros de no-ip.com.


Generando los archivos necesarios

Tal como se mencionó anteriormente, deberemos generar el archivo de nombre /etc/persistent/rc.poststart. El contenido de ese archivo es el siguiente:


#!/usr/bin/sh
mkdir /etc/crontabs
cp -a /etc/persistent/no-ip.crontab /etc/crontabs/root
echo "root" > /etc/crontabs/cron.update
crond


Este script crea el directorio /etc/crontabs (que es consultado por el demonio crond), copia un archivo (cuyo contenido se muestra en breve) a ese directorio con el nombre del usuario que correrá la tarea periódica (root en nuestro caso), y genera un archivo de nombre cron.update cuyo contenido es el nombre del usuario que ha visto modificada su entrada de cron (esto permite avisarle al demonio crond que relea los archivos de configuración). Como último paso, lanza el servicio de cron al ejecutar el demonio crond. El script rc.poststart debe tener permisos de ejecución.

El archivo /etc/persistent/no-ip.crontab posee una sola línea, y es la siguiente:

*/5 * * * * /etc/persistent/no-ip.client >/dev/null 2>&1

Esta línea le indica a crond, que ejecute el script /etc/persistent/no-ip.client (cuyo contenido se muestra en breve) cada 5 minutos, y que descarte la salida por pantalla cuando lo ejecute.

Por último, el script /etc/persistent/no-ip.client ejecutado periódicamente por crond, contiene lo siguiente:


#!/usr/bin/sh

DDNSUSER="UsuarioNOIP"
DDNSPASS="PasswordNOIP"
DDNSHOST="HostnameNOIP.no-ip.org"

IPADDRESS=`wget http://ipecho.net/plain -O - -q ; echo`
if [ "${IPADDRESS}" = "" ]; then
exit 1
fi

IPSAVED=""
if [ -f /tmp/ip.publico ]; then
IPSAVED=`cat /tmp/ip.publico`
fi

if [ "${IPADDRESS}" != "${IPSAVED}" ]; then
echo ${IPADDRESS} > /tmp/ip.publico
logger Se detecta IP publico ${IPADDRESS}
wget -q -O /dev/null "http://${DDNSUSER}:${DDNSPASS}@dynupdate.no-ip.com/nic/update?hostname=${DDNSHOST}&myip=${IPADDRESS}" true
fi


Este script posee definida tres variables:
DDNSUSER: usuario con que definimos la entrada del host en no-ip.com
DDNSPASS: contraseña del mismo
DDNSHOST: nombre del host definido en no-ip.com

El script obtiene el IP público de la conexión con la ayuda del sitio http://ipecho.net, y lo almacena en la variable IPADDRESS. Además compara este valor con la dirección IP que posee almacenada en el archivo /tmp/ip.publico donde almacenó el valor de la dirección pública en el último cambio (luego del arranque de la cámara, este archivo no existe por lo que la actualización en no-ip.com se ejecuta por lo menos una vez). Además genera una entrada en el log, informando del cambio de IP público detectado. Este script también debe poseer permisos de ejecución.

Una vez que generamos los archivos necesarios, podemos grabar estas modificaciones con el comando:

cfgmtd -w -p /etc/

Este comando graba las modificaciones a la memoria flash.

Luego podemos reiniciar el equipo (con el comando reboot, por ejemplo), y controlar si las modificaciones se mantienen.


Controlando los cambios

Si las modificaciones se hicieron sin errores, veremos que:


  1. Los archivos agregados en /etc/persistent se encuentran allí
  2. El proceso crond está corriendo.
  3. Aparecen mensajes de crond en el log del sistema, tales como:
Mar 28 14:27:19 192.168.1.20 crond[292]: time disparity of 586527 minutes detected
Mar 28 14:30:01 192.168.1.20 crond[292]: crond: USER root pid 314 cmd /etc/persistent/no-ip.client >/dev/null 2>&1
Mar 28 14:30:02 192.168.1.20 root: Se detecta IP publico 1.2.3.4
Mar 28 14:35:01 192.168.1.20 crond[292]: crond: USER root pid 323 cmd /etc/persistent/no-ip.client >/dev/null 2>&1

El primer mensaje es un aviso por parte de crond, respecto a que hubo un corrimiento importante de la hora. Eso se debe a que la cámara no posee reloj, y arranca con una fecha y hora errónea, hasta que sincroniza con los relojes de Internet vía ntp.

En la primera ejecución del script, vemos que la dirección IP es detectada correctamente, lo que implica que se produjo la actualización en los registros de no-ip. El valor de esta dirección IP debería estar correctamente almacenada en el archivo /tmp/ip.publico

Quedará para otra ocasión la revisión de los scripts de la página de gestión de la cámara, para detectar la causa por la cual pareciera que el único servicio de DNS dinámico que contempla es el de dyndns.org. De todas formas, si el aparente bug se debiera a una mala interpretación de mi parte, no deja de ser útil que se conozcan mecanismos que permiten la modificación o el agregado de funcionalidades a la cámara.

Referencias:
Adding FTP To The Ubiquiti AirCam Mini
Integrate No-IP


2 comentarios:

  1. muy bueno. gran trabajo---me intereso esto, pero en si lo quiero aplicar para reinicios diarios de un CPE Ubiquiti... crees poder ayudarme...

    Gracisa

    ResponderEliminar
    Respuestas
    1. No se a qué modelo te referirás, pero en general los Aps Ubiquiti tienen servicio de SSH. Es cuestión de crear un script que se conecte por SSH y haga el reboot. Yo hice algún script de ese estilo para el modelo Nanostation.

      Eliminar