Crenein – GrafIn

Crenein – GrafIn

Crenein Grafin, es una solución que utiliza software libre y un pequeño script escrito por nosostros para equipos Mikrotik.
El objetivo es poder graficar flujos de tráficos basados en reglas de firewall.

Pongamos un ejemplo:

Tenemos una conexión a un proveedor que nos vende en una misma interfaz trafico de CDN e Internet. Para poder discriminar ese tráfico, podríamos crear reglas de firewall que marquen esos paquetes o identifiquen esos flujos.

Lo que haríamos tipicamente en nuestro Mikrotik, es marcar en mangle ese trafico y luego poner una simple queue que tome el packet-mark para poder luego consultar una grafica de consumo.

Ésto tiene algunas limitaciones, por ejemplo:

  • Si el router donde tenemos la conexión al proveedor necesita manejar colas limitando velocidad, ya no podemos crear esta simple-queue que nos permitiría ver la gráfica. Ya que ésta sería coincidente con trafico que debe ser controlada por la queue que limita la velocidad.
  • Otro punto, es el incremento de cpu que puede provocar la queue.
  • También debemos tener en cuenta que si el Mikrotik debe remplazarse, los gráficos se van con él.
  • Y por último y no menos importante, tenemos los graficos de forma desentralizada, lo que nos complica a la hora de comparar unos con otros y acceder a ellos.

Cómo Crenein-GrafIn soluciona estos conflictos?

Simple, el almacenamiento se realiza en una base de datos que puede instalarse en algun servidor central, al igual que el sistema que lee y grafica y los datos.

Para la recolección de información, no necesita la creación de una queue, ya que una tarea programada con un script creado por nosotros, recolecta la información de la regla, la calcula y se la envía a la base de datos remota.

¡Qué bien se ve!

El servidor

Para el servidor necesitamos:

  • Una PC pequeña o máquina virtual. Las dimensiones depende de que tánta información almacenemos.
  • Linux (probado en debian 10).
  • Docker y Docker-Compose.
  • InfluxDB.
  • Grafana.

Instalación y configuración del servidor

Partimos de la base de que la siguiente lista ya fue realizada. Puede consultar como hacer cada item haciendo click sobre ellos para acceder a un tutorial.

Luego de tener docker compose instalado, descargaremos un archivo YAML para crear los contenedores Docker.

git clone https://github.com/facubertran/Crenein_GrafIn.git

Ya clonado el archivo docker-compose.yml, editaremos los usuarios y passwords a nuestro gusto:

Luego, ejecutaremos el archivo con el comando:

cd GrafIn
docker-compose up -d --build

Se verá algo así:

Podemos chequear el estado de nuestros contenedores con el comando:

docker ps

Configurar InfluxDB

El siguiente paso es configurar InfluxDB, ésto lo haremos solo la primera vez y consiste en:

Crear token

Para ello ejecutamos el siguiente comando:

Debemos cambiar “NombreDeMiEmpresa” por el nombre que quieran definir como organización en InfluxDB.

docker exec -ti grafin_influxdb_1 bash -c "influx auth create -o NombreDeMiEmpresa --write-buckets --read-buckets"

Éste comando nos devolverá algo como ésto:

Largo = Token, Corto = Org ID

Debemos guardar el token y el org id ya que lo necesitaremos para configurar el script en nuestro Mikrotik.

Configurar Grafana

Para acceder a Grafana debes poner en tu navegador http://ip_server:3000/

En Grafana debemos configurar el source data con la información de conexión que hayamos definido de la siguiente forma:

El script generador de tareas programadas

Creamos un script que lee las reglas y crea una tarea programada para el envió de los datos al server de forma automática.

Éste script utiliza datos de comentario de la regla de firewall para establecer la configuración de almacenamiento de la información en el server remoto.

Requiere la configuración de algunos parámetros globales:

GraphServerIP -> IP del servidor InfluxDB
GraphServerPort -> Puerto del servidor InfluxDB
GraphOrganization -> Nombre que definimos como organización en influxdb.
GraphOrganizationID -> ID de organización obtenido de influxdb
GraphToken -> Token de influxdb
GraphPollTime -> Cada cuánto tiempo se envía información al server.
GraphHostname -> Nombre que identifique al router (evitar uso de espacios y caracteres)

Instalador del script

/system script
add name=CreneinGrafInSchedulerGenerator source="#Co\
    nfiguration\r\
    \n:global GraphServerIP \"1.2.3.4\";\r\
    \n:global GraphServerPort \"8086\";\r\
    \n:global GraphOrganization \"NombreEmpresa\";\r\
    \n:global GraphOrganizationID \"ID de organizaci\F3n obtenido de influxdb\";\r\
    \n:global GraphToken \"Token influxdb\";\r\
    \n:global GraphPollTime \"01\"; #Minutes 01 10 15 - Intervalo de muestra\r\
    \n:global GraphHostname \"Nombre que identifique al router (evitar uso de espacios y caracteres)\";\r\
    \n#RAW table\r\
    \n:do {\r\
    \n    :foreach a in=[/ip firewall raw find comment~\"CreneinGraph\"] do={\r\
    \n        :local varname (\"\\\"\".[:tostr [:pick [toarray [/ip firewall raw get \$a comment]] 2]].\" \".[:tostr [:pick [toarray [/ip firewall raw get \$a comme\
    nt]] 1]].\"\\\"\");\r\
    \n        :local varnamelog (\"\\\"\".[:tostr [:pick [toarray [/ip firewall raw get \$a comment]] 2]].\" \".[:tostr [:pick [toarray [/ip firewall raw get \$a co\
    mment]] 1]]);\r\
    \n        :local measurement [:pick [toarray [/ip firewall raw get \$a comment]] 1];\r\
    \n        :local bucket [:pick [toarray [/ip firewall raw get \$a comment]] 2];\r\
    \n        :local peak [:pick [toarray [/ip firewall raw get \$a comment]] 3];\r\
    \n        :if ([/ip firewall raw get \$a disabled]) do={\r\
    \n            /system scheduler remove \$varname;\r\
    \n        } else={\r\
    \n            do {\r\
    \n                :do {\r\
    \n                    :local bucketcreate \"{\\\"orgID\\\": \\\"\$GraphOrganizationID\\\",\\\"name\\\": \\\"\$bucket\\\",\\\"retentionRules\\\": [{\\\"type\\\":\
    \_\\\"expire\\\",\\\"everySeconds\\\": 7776000,\\\"shardGroupDurationSeconds\\\": 0}]}\";\r\
    \n                    /tool fetch mode=http \\\r\
    \n                    url=\"http://\$GraphServerIP/api/v2/buckets\" \\\r\
    \n                    port=\$GraphServerPort http-method=post \\\r\
    \n                    http-header-field=\"Content-Type: application/json,Authorization: Token \$GraphToken\" \\\r\
    \n                    http-data=\$bucketcreate;\r\
    \n                } on-error {};\r\
    \n                /system scheduler add interval=(\"00:\".\$GraphPollTime.\":00\") name=\$varname \\\r\
    \n                on-event=\":local measurement \$measurement;:local bucket \$bucket;:local limitpeak \$peak;:global \$varname;\\\r\
    \n                :local CurrentBytes ([ip firewall raw get [find comment~\\\"CreneinGraph\\\" and comment~\\\"\\\$measurement\\\" and comment~\\\"\\\$bucket\\\
    \"] bytes]*8);:if (\\\$\$varname <= \\\$CurrentBytes) do={:local mbps (\\\r\
    \n                (((\\\$CurrentBytes - \\\$\$varname)/(\$GraphPollTime*60))/1024)/1024);:if (\\\$mbps <= (\\\$limitpeak)) do={:local data \\\"\\\$measurement,h\
    ost=\$GraphHostname\\\r\
    \n                \\\\_rate=\\\$mbps\\\";:put \\\$mbps;/tool fetch output=none mode=http url=\\\"http://\$GraphServerIP/api/v2/write\\\?org=\$GraphOrganization&\
    bucket=\\\$bucket&p\\\r\
    \n                recision=s\\\" port=\$GraphServerPort http-method=post http-header-field=\\\"Content-Type: text/plain,Authorization: Token \$GraphToken \\\r\
    \n                \\\" http-data=\\\"\\\$data\\\";:set \\\$\$varname \\\$CurrentBytes} else={:log w\\\r\
    \n                arning \\\"El calculo en Mbps es superior al limite -> \\\$mbps\\\";:set \\\$\$varname \\\$CurrentBytes;}} else={:log warning \$varnamelog (\\\
    \$\\\r\
    \n                \$varname) es > a CurrentBytes\\\";:set \\\$\$varname \\\$CurrentBytes;}\"\r\
    \n            } on-error={}\r\
    \n        }\r\
    \n    }\r\
    \n} on-error={}\r\
    \n#Mange table\r\
    \n:do {\r\
    \n    :foreach a in=[/ip firewall mangle find comment~\"CreneinGraph\"] do={\r\
    \n        :local varname ([:pick [toarray [/ip firewall mangle get \$a comment]] 2].[:pick [toarray [/ip firewall mangle get \$a comment]] 1]);\r\
    \n        :local varnamelog (\"\\\"\".[:tostr [:pick [toarray [/ip firewall raw get \$a comment]] 2]].\" \".[:tostr [:pick [toarray [/ip firewall raw get \$a co\
    mment]] 1]]);\r\
    \n        :local measurement [:pick [toarray [/ip firewall mangle get \$a comment]] 1];\r\
    \n        :local bucket [:pick [toarray [/ip firewall mangle get \$a comment]] 2];\r\
    \n        :local peak [:pick [toarray [/ip firewall mangle get \$a comment]] 3];\r\
    \n        :if ([/ip firewall mangle get \$a disabled]) do={\r\
    \n            /system scheduler remove \$varname;\r\
    \n        } else={\r\
    \n            do {\r\
    \n                :do {\r\
    \n                    :local bucketcreate \"{\\\"orgID\\\": \\\"\$GraphOrganizationID\\\",\\\"name\\\": \\\"\$bucket\\\",\\\"retentionRules\\\": [{\\\"type\\\":\
    \_\\\"expire\\\",\\\"everySeconds\\\": 7776000,\\\"shardGroupDurationSeconds\\\": 0}]}\";\r\
    \n                    /tool fetch mode=http \\\r\
    \n                    url=\"http://\$GraphServerIP/api/v2/buckets\" \\\r\
    \n                    port=\$GraphServerPort http-method=post \\\r\
    \n                    http-header-field=\"Content-Type: application/json,Authorization: Token \$GraphToken\" \\\r\
    \n                    http-data=\$bucketcreate;\r\
    \n                } on-error {};\r\
    \n                /system scheduler add interval=(\"00:\".\$GraphPollTime.\":00\") name=\$varname \\\r\
    \n                on-event=\":local measurement \$measurement;:local bucket \$bucket;:local limitpeak \$peak;:global \$varname;\\\r\
    \n                :local CurrentBytes ([ip firewall mangle get [find comment~\\\"CreneinGraph\\\" and comment~\\\"\\\$measurement\\\" and comment~\\\"\\\$bucket\
    \\\"] bytes]*8);:if (\\\$\$varname <= \\\$CurrentBytes) do={:local mbps (\\\r\
    \n                (((\\\$CurrentBytes - \\\$\$varname)/(\$GraphPollTime*60))/1024)/1024);:if (\\\$mbps <= (\\\$limitpeak)) do={:local data \\\"\\\$measurement,h\
    ost=\$GraphHostname\\\r\
    \n                \\\\_rate=\\\$mbps\\\";:put \\\$mbps;/tool fetch output=none mode=http url=\\\"http://\$GraphServerIP/api/v2/write\\\?org=\$GraphOrganization&\
    bucket=\\\$bucket&p\\\r\
    \n                recision=s\\\" port=\$GraphServerPort http-method=post http-header-field=\\\"Content-Type: text/plain,Authorization: Token \$GraphToken \\\r\
    \n                \\\" http-data=\\\"\\\$data\\\";:set \\\$\$varname \\\$CurrentBytes} else={:log w\\\r\
    \n                arning \\\"El calculo en Mbps es superior al limite -> \\\$mbps\\\";:set \\\$\$varname \\\$CurrentBytes;}} else={:log warning \$varnamelog (\\\
    \$\\\r\
    \n                \$varname) es > a CurrentBytes\\\";:set \\\$\$varname \\\$CurrentBytes;}\"\r\
    \n            } on-error={}\r\
    \n        }\r\
    \n    }\r\
    \n} on-error={}"

Una vez configurado el script generador creamos las reglas que queremos sean monitoreadas.

Monitoreo de reglas

Desarrollamos un script que genera tareas programadas para el envío de datos a InfluxDB.

Éste script analiza las tablas de RAW y Mangle en busca de reglas que tengan como comentario “CreneinGraph”, asi las reconoce como reglas de medición y con otros datos en el comentario crea las tareas programadas y los buckets necesarios para la recolección de datos.

El comentario de cada regla es el que define como se graficará luego en Grafana. Tiene la siguiente estructura:

Veamos la estructura del comentario:

CreneinGraph,Measuremnt,Bucket,LimitPeak

El comentario cuenta de 4 elementos:

  • CreneinGraph (Sirve para identificar que es una regla que debe ser monitoreada).
  • Measurement (Nombre con el que se mostrará la medición en Grafana)
  • Bucket (Espacio en InfluxDB al que se enviaran los datos)
  • LimitPeak (El límite máximo al que puede llegar la medicion en condiciones normales)

Por ejemplo:

CreneinGraph,NetflixFromCache,Patagonia,2000

Organizacion de buckets y measuremnts

Un bucket es un agrupador de measurements. La distribución que se realice depende mucho de como quiere presentar la información.

Por ejemplo, en Crenein, necesitábamos medir el total de trafico por nodo y el trafico obtenido de los caches para esos nodos. Entonces creamos un bucket por cada nodo y un measurement por cada item a medir (todo el trafico y trafico de cache).

Determinación del peak

Es recomendable crear las reglas antes que nada para poder verificar el rate del momento y con eso determinar el rate pico.

El rate pico evita que se envíen valores superiores al permitido, evitando así que la grafica quede inentendible por éstas mediciones con datos erroneos. Esto se dá por ejemplo cuando por algún motivo no se envió info en la ventana de tiempo configurada y acumuló tráfico en más tiempo.

Ejecutar script de creación de tareas programadas

Una vez configurado el script de creacion y las reglas creadas con su comentario en el formato correcto se puede proceder a ejecutar el script de creación.

Notará que el primer status es finished y existen otros 2 failed. Ésto se da porque existen 3 reglas con un mismo bucket, e intenta crear el bucket más de una vez. Siempre que la primera de “finished” el proceso se ejecutó de forma correcta, dejandonos algunas tareas en “System->Scheduler”.

Crear panel en Grafana

Para crear un nuevo panel en Grafana debe:

  • Agregarlo desde el menú
  • Crear el panel
  • Configurar la toma de datos y gráfico

Para poder obtener los datos de InfluxDB y poder representarlo en un gráfico, se deben hacer consultas a InfluxDB. Ésta base de datos utiliza un lenguaje llamado FLUX. En nuestros gráficos se utliza una versión muy simple de la consulta. Pero puede consultar la documentación de InfluxDB para ampliar su capacidad.

Consulta de ejemplo

from(bucket:"NombreDelBucket")
|> range(start: -48h)

Recuerda guardar tu dashboard

¡Ya tienes GrafIn funcionando!

¿Quieres que lo hagamos nosotros?

Whatsapp al +5493725409044

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *