Recopilando los LOGs de Ubiquiti UniFi en Elasticsearch y visualizándolos en Grafana
Y seguimos con más cosas raras… hoy de primero una de recopilar Logs al entorno de Ubiquiti Unifi, de nuestros APs, Switches… de segundo parseamos, tratamos los Logs con Logstash y de postre lo almacenamos en Elasticsearch, y ya la guinda del pastel será visualizarlo con Grafana. Todo ello para controlar en tiempo real qué sucede en nuestra infraestructura de comunicaciones, quién intenta acceder al Wisfis…
Bueno pues lo dicho, si tenemos unos Ubiquiti por ahí prestando algún servicio y queremos conocer qué está pasando ahí, quién comunica con quién… a mí me dio más la curiosidad por la parte WiFi, ver intentos de conexiones, salud de los clientes… cada uno visualizará lo que más le interese. Así que empezaremos indicando a la consola de UniFi que almacene los logs en un syslog, a nuestro Logstash le mandará los logs y os dejaré un fichero de ejemplo para filtrar y poder separar en campos interesantes los datos recopilados, ese mismo fichero de configuración indicará que se almacene en un índice de Elasticsearch la información ya separada en distintos campos. Y acabaremos con Kibana o Grafana visualizando en un Dashboard la info que nos interese y en el formato que nos interese.
Primero obviamente tenemos que tener la parte de Elastic Stack instalada, lo que viene siendo Logstash, Elasticsearch y Kibana; Luego ya habilitamos el remote syslog desde la consola de UniFi Controller > “Settings” > “System Settings” > “Controller Configuration” > “Remote Logging”, y con habilitar únicamente la parte de Syslog nos vale, sin Netconsole. Indicamos la dirección IP de nuestro Logstash y un puerto aleatorio no usado donde mandará los Logs.
input { udp { type => "UniFi" port => "1601" tags => ["UniFi"] } } filter { if [type] == "UniFi" { grok { match => { "message" => [ "^<%{POSINT:syslog_pri}>%{SYSLOGTIMESTAMP:syslog_timestamp} %{WORD:UniFi_MAC},%{GREEDYDATA:UniFi_Firmware}: %{PROG:Proceso_nombre}: \[%{NUMBER:Proceso_pid}\] \[%{DATA:Proceso_tarea}\] DNS request timed out; \[STA: %{COMMONMAC:Cliente_MAC}\]\[QUERY: %{GREEDYDATA:Cliente_consulta}\] \[DNS_SERVER :%{IPV4}\] \[TXN_ID %{DATA:Txn_id}\] \[SRCPORT %{NUMBER:Cliente_puerto_origen}\]", "^<%{POSINT:syslog_pri}>%{SYSLOGTIMESTAMP:syslog_timestamp} %{WORD:UniFi_MAC},%{GREEDYDATA:UniFi_Firmware}: %{PROG:Proceso_nombre}: \[%{NUMBER:Proceso_pid}\] \[%{DATA:Proceso_tarea}\] %{GREEDYDATA:Mensaje}", "^<%{POSINT:syslog_pri}>%{SYSLOGTIMESTAMP:syslog_timestamp} %{WORD:UniFi_MAC},%{GREEDYDATA:UniFi_Firmware}: : %{PROG:Proceso_nombre}\[%{NUMBER:Proceso_pid}\]: wireless_agg_stats.log_sta_anomalies\(\): bssid=%{COMMONMAC:BSSID_MAC} radio=%{WORD:BSSID_radio} vap=%{WORD:vap} sta=%{COMMONMAC:Cliente_MAC} satisfaction_now=%{NUMBER:Cliente_satisfaccion} anomalies=%{GREEDYDATA:Cliente_anomalia}", "^<%{POSINT:syslog_pri}>%{SYSLOGTIMESTAMP:syslog_timestamp} %{WORD:UniFi_MAC},%{GREEDYDATA:UniFi_Firmware}: : %{PROG:Proceso_nombre}\[%{NUMBER:Proceso_pid}\]: \[%{DATA:Proceso_tarea}\].stahtd_dump_event\(\): \{\"query_%{NUMBER:Consulta_id}\":\"%{HOSTNAME:Consulta_FQDN}\",\"mac\":\"%{COMMONMAC:Cliente_MAC}\",\"message_type\":\"%{DATA:Mensaje_tipo}\",\"query_server_%{NUMBER:Consulta_id}\":\"%{IPV4:IP_servidor}\",\"assoc_status\":\"%{NUMBER:Asociacion_estado}\",\"vap\":\"%{WORD:vap}\",\"event_type\":\"%{DATA:Evento_tipo}\"\}", "^<%{POSINT:syslog_pri}>%{SYSLOGTIMESTAMP:syslog_timestamp} %{WORD:UniFi_MAC},%{GREEDYDATA:UniFi_Firmware}: : %{PROG:Proceso_nombre}\[%{NUMBER:Proceso_pid}\]: \[%{DATA:Proceso_tarea}\].stahtd_dump_event\(\): \{\"dns_resp_seen\":\"%{WORD:DNS_resp_seen}\",\"message_type\":\"%{DATA:Mensaje_tipo}\",\"mac\":\"%{COMMONMAC:Cliente_MAC}\",\"vap\":\"%{WORD:vap}\",\"assoc_status\":\"%{NUMBER:Asociacion_estado}\",\"event_type\":\"%{DATA:Evento_tipo}\",\"event_id\":\"%{NUMBER:Evento_id}\",\"arp_reply_gw_seen\":\"%{WORD:ARP_resp_seen}\",\"auth_ts\":\"%{NUMBER:Auth_ts}\"\}", "^<%{POSINT:syslog_pri}>%{SYSLOGTIMESTAMP:syslog_timestamp} %{WORD:UniFi_MAC},%{GREEDYDATA:UniFi_Firmware}: : %{PROG:Proceso_nombre}\[%{NUMBER:Proceso_pid}\]: \[%{DATA:Proceso_tarea}\].stahtd_dump_event\(\): \{\"query_server_%{NUMBER:Consulta_id}\":\"%{IPV4}\",\"query_%{NUMBER:Consulta_id}\":\"%{HOSTNAME:Consulta_FQDN}\",\"mac\":\"%{COMMONMAC:Cliente_MAC}\",\"message_type\":\"%{DATA:Mensaje_tipo}\",\"assoc_status\":\"%{NUMBER:Asociacion_estado}\",\"vap\":\"%{WORD:vap}\",\"event_type\":\"%{DATA:Evento_tipo}\"\}", "^<%{POSINT:syslog_pri}>%{SYSLOGTIMESTAMP:syslog_timestamp} %{WORD:UniFi_MAC},%{GREEDYDATA:UniFi_Firmware}: : %{PROG:Proceso_nombre}\[%{NUMBER:Proceso_pid}\]: \[%{DATA:Proceso_tarea}\].stahtd_dump_event\(\): \{\"query_server_%{NUMBER:Consulta_id}\":\"%{IPV4}\",\"mac\":\"%{COMMONMAC:Cliente_MAC}\",\"message_type\":\"%{DATA:Mensaje_tipo}\",\"vap\":\"%{WORD:vap}\",\"%{DATA:Evento_tipo}\",\"query_%{NUMBER:Consulta_id}\":\"%{HOSTNAME:Consulta_FQDN}\",\"assoc_status\":\"%{NUMBER:Asociacion_estado}\"\}", "^<%{POSINT:syslog_pri}>%{SYSLOGTIMESTAMP:syslog_timestamp} %{WORD:UniFi_MAC},%{GREEDYDATA:UniFi_Firmware}: : %{PROG:Proceso_nombre}\[%{NUMBER:Proceso_pid}\]: \[%{DATA:Proceso_tarea}\].stahtd_dump_event\(\): \{\"mac\":\"%{COMMONMAC:Cliente_MAC}\",\"message_type\":\"%{DATA:Mensaje_tipo}\",\"assoc_status\":\"%{NUMBER:Asociacion_estado}\",\"vap\":\"%{WORD:vap}\",\"event_type\":\"%{DATA:Evento_tipo}\":\"%{NUMBER:Evento_id}\"\}", "^<%{POSINT:syslog_pri}>%{SYSLOGTIMESTAMP:syslog_timestamp} %{WORD:UniFi_MAC},%{GREEDYDATA:UniFi_Firmware}: : %{PROG:Proceso_nombre}\[%{NUMBER:Proceso_pid}\]: \[%{DATA:Proceso_tarea}\].stahtd_dump_event\(\): \{\"mac\":\"%{COMMONMAC:Cliente_MAC}\",\"message_type\":\"%{DATA:Mensaje_tipo}\",\"vap\":\"%{WORD:vap}\",\"query_server_%{NUMBER:Consulta_id}\":\"%{IPV4}\",\"event_type\":\"%{DATA:Evento_tipo}\",\"assoc_status\":\"%{NUMBER:Asociacion_estado}\",\"query_%{NUMBER:Consulta_id}\":\"%{HOSTNAME:Consulta_FQDN}\"\}", "^<%{POSINT:syslog_pri}>%{SYSLOGTIMESTAMP:syslog_timestamp} %{WORD:UniFi_MAC},%{GREEDYDATA:UniFi_Firmware}: : %{PROG:Proceso_nombre}\[%{NUMBER:Proceso_pid}\]: \[%{DATA:Proceso_tarea}\].stahtd_dump_event\(\): \{\"dns_resp_seen\":\"%{WORD:DNS_resp_seen}\",\"mac\":\"%{COMMONMAC:Cliente_MAC}\",\"message_type\":\"%{DATA:Mensaje_tipo}\",\"assoc_status\":\"%{NUMBER:Asociacion_estado}\",\"vap\":\"%{WORD:vap}\",\"event_type\":\"%{DATA:Evento_tipo}\",\"event_id\":\"%{NUMBER:Evento_id}\",\"auth_ts\":\"%{NUMBER:Auth_ts}\"\}", "^<%{POSINT:syslog_pri}>%{SYSLOGTIMESTAMP:syslog_timestamp} %{WORD:UniFi_MAC},%{GREEDYDATA:UniFi_Firmware}: : %{PROG:Proceso_nombre}\[%{NUMBER:Proceso_pid}\]: \[%{DATA:Proceso_tarea}\].stahtd_dump_event\(\): \{\"dns_resp_seen\":\"%{WORD:DNS_resp_seen}\",\"message_type\":\"%{DATA:Mensaje_tipo}\",\"mac\":\"%{COMMONMAC:Cliente_MAC}\",\"ip_assign_type\":\"%{DATA:Tipo_asignacion_ip}\",\"vap\":\"%{WORD:vap}\",\"assoc_status\":\"%{NUMBER:Asociacion_estado}\",\"event_type\":\"%{DATA:Evento_tipo}\",\"auth_ts\":\"%{NUMBER:Auth_ts}\",\"wpa_auth_delta\":\"%{NUMBER:WPA_auth_delta}\",\"assoc_delta\":\"%{NUMBER:Assoc_delta}\",\"auth_delta\":\"%{NUMBER:Auth_delta}\",\"event_id\":\"%{NUMBER:Evento_id}\"\}", "^<%{POSINT:syslog_pri}>%{SYSLOGTIMESTAMP:syslog_timestamp} %{WORD:UniFi_MAC},%{GREEDYDATA:UniFi_Firmware}: %{PROG:Proceso_nombre}: %{WORD:vap}: STA %{COMMONMAC:Cliente_MAC} %{DATA:Proceso_tarea}: %{GREEDYDATA:Mensaje}", "^<%{POSINT:syslog_pri}>%{SYSLOGTIMESTAMP:syslog_timestamp} %{WORD:UniFi_MAC},%{GREEDYDATA:UniFi_Firmware}: : %{PROG:Proceso_nombre}\[%{NUMBER:Proceso_pid}\]: wevent.ubnt_custom_event\(\): %{DATA:Evento_tipo} %{WORD:vap}: %{COMMONMAC:Cliente_MAC} / %{IPV4:Cliente_ip}", "^<%{POSINT:syslog_pri}>%{SYSLOGTIMESTAMP:syslog_timestamp} %{WORD:UniFi_MAC},%{GREEDYDATA:UniFi_Firmware}: : %{PROG:Proceso_nombre}\[%{NUMBER:Proceso_pid}\]: wevent.ubnt_custom_event\(\): %{DATA:Evento_tipo} %{WORD:vap}: %{COMMONMAC:Cliente_MAC} / %{NUMBER:Evento_id}", "^<%{POSINT:syslog_pri}>%{SYSLOGTIMESTAMP:syslog_timestamp} %{WORD:UniFi_MAC},%{GREEDYDATA:UniFi_Firmware}: : %{PROG:Proceso_nombre}\[%{NUMBER:Proceso_pid}\]: %{GREEDYDATA:Mensaje}", "^<%{POSINT:syslog_pri}>%{SYSLOGTIMESTAMP:syslog_timestamp} %{WORD:UniFi_MAC},%{GREEDYDATA:UniFi_Firmware}: %{PROG:Proceso_nombre}: %{DATA:Proceso_tarea}\[%{NUMBER:Proceso_pid}\]: %{GREEDYDATA:Tarea}: %{GREEDYDATA:Mensaje}", "^<%{POSINT:syslog_pri}>%{SYSLOGTIMESTAMP:syslog_timestamp} %{WORD:UniFi_MAC},%{GREEDYDATA:UniFi_Firmware}: %{PROG:Proceso_nombre}: %{DATA:Proceso_tarea} %{COMMONMAC:Cliente_MAC} %{WORD:vap} \(%{GREEDYDATA:Mensaje}\)", "^<%{POSINT:syslog_pri}>%{SYSLOGTIMESTAMP:syslog_timestamp} %{WORD:UniFi_MAC},%{GREEDYDATA:UniFi_Firmware}: %{UNIXPATH:Proceso_nombre}\[%{NUMBER:Proceso_pid}\]: %{GREEDYDATA:Mensaje}", "^<%{POSINT:syslog_pri}>%{SYSLOGTIMESTAMP:syslog_timestamp} %{WORD:UniFi_MAC},%{GREEDYDATA:UniFi_Firmware}: %{PROG:Proceso_nombre}: \[%{NUMBER:Proceso_pid}\] %{GREEDYDATA:Mensaje}", "^<%{POSINT:syslog_pri}>%{SYSLOGTIMESTAMP:syslog_timestamp} %{WORD:UniFi_MAC},%{GREEDYDATA:UniFi_Firmware}: %{PROG:Proceso_nombre}: %{DATA:Proceso_tarea}: %{GREEDYDATA:Mensaje}" ] } } mutate { convert => { "Cliente_satisfaccion" => "integer" } } } } output { if ([type]=="UniFi"){ elasticsearch { index => "unifi-%{+YYYY.MM.dd}" hosts => "DIRECCION_IP_ELASTICSEARCH:9200" } } }
Continuamos, necesitamos crear un fichero en Logstash para ingestar, transformar y enviar los datos. Primero escucharemos en el puerto TCP indicado en UniFi que vimos en el paso anterior; segundo, tratar los logs que recibe y separarlos en distintos campos para ser por último almacenados en nuestro Elasticsearch. Así que creamos por ejemplo el fichero de configuración ‘/etc/logstash/conf.d/unifi.conf’, Seguiremos con los filtros, yo hice mis grok como pude, pido perdón a los sabios del tema, pero totalmente válido para recopilar los Logs del Ubiquiti UniFi con versión 6.2.x. Al final, en el output le daremos salida a nuestro Elasticsearch, recordar poner username & password que lo mío es un LAB y me encantan las worst practices… Y de verdad perdonar los verdaderos gurús mis groks, sigo en 1ro de filtros.
Una vez creado el fichero de configuración, acordaros de reiniciar el servicio de Logstash para recargar la nueva config. Después como siempre, iremos a Kibana y una vez los datos estén entrando ya podremos ir a “Management” > “Stack management” > “Kibana” > “Index Patterns” > “Create index pattern” para crear el patrón del índice, lo dicho, como habitualmente (en este caso y sin las comillas) ‘unifi-*’ y tendremos los datos ya en Elasticsearch almacenados de manera correcta. Ahora podríamos conectarnos desde “Discover” a nuestro índice de Ubiquiti UniFi visualizar que está recogiendo datos.
Y acabamos como siempre en Grafana! Tras crear el índice en Kibana, ahora en nuestro amado Grafana deberíamos crear un “Data Source” que apunte contra nuestro Elasticsearch y el índice de UniFi. Luego ya es dejar volar la imaginación, hacer un Dashboard con distintos Paneles, con distintos datos a visualizar, gráficas con satisfacción/calidad de la conexión de los clientes, un mapamundi con las conexiones entrantes/salientes, uno de estilo Sankey para ver IPs clientes/APs, en formato columnas, en tablas para ver datos concretos de por ejemplo los accesos correctos, incorrectos, las conexiones…
Como siempre, intentando que pueda inspirar o ayudar, si lo mejoráis me lo pasáis 😉 Con esto podremos ver qué pasa en nuestro(s) dispositivos de Ubiquiti, en los Puntos de Acceso, en los Switches, en tiempo real, poniendo un refresco automático cada 10 segundos queda muy impresionante, también nos servirá para analizar el resumen de las últimas 24h, o conocer cuando algo extraño sucede… como el vecino intentando conectarse.. Como siempre, gracias a todos y muchísimo más a los que movéis este tipo de contenidos en redes sociales jeje ¡Abrazos!