19 April 2017

GeoIP en la Elastic Stack

By Mark Walkom

Introducción

Una de las genialidades del Elastic Stack es que puede tomar archivos de registro comunes y transformarlos en un tesoro con grán valor. Desde descubrir cuánto ancho de banda están usando sus usuarios internos hasta ayudar a planificar para el crecimiento del personal y la capacidad de internet para todas sus oficinas. Descubrir qué exploradores acceden a su sitio para guiar si elige la optimización móvil o de escritorio. O ver exactamente desde dónde están iniciando sesión sus usuarios, de modo que los departamentos de marketing y ventas puedan mejorar su participación. Todo es posible con unos pocos pasos sencillos.


Una solicitud común que vemos es cómo tomar direcciones IP o nombres de host, la representación de un host o sistema que usa la gente para acceder a otros sistemas en internet, y convertirlos en un punto de geolocalización (latitud y longitud) de modo que pueda responder los tipos de preguntas que acabamos de hacer. O, en otras palabras, traducciones geoip.


Esta publicación examina los conocimientos básicos para dar los primeros pasos con geoip en Elastic Stack, además de algunos consejos para la resolución de problemas.

¡Mejore!

Utilicemos una línea de registro Apache falsa como datos de muestra, por ejemplo:

104.194.203.69 - - [01/Apr/2017:16:21:15 +0000] "GET /favicon.ico HTTP/1.1" 200 3638 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36"


Desde ahí aprovecharemos la API de Ingest (Ingest API) y Logstash para demostrar cómo puede usar varios métodos para procesar sus datos. Ambos métodos se basarán en pasos muy similares para convertir una dirección IP o un nombre de host en un conjunto de coordenadas geográficas, lo que significa que puede convertir fácilmente una en otra.


Como resumen rápido de lo que sucederá, Ingest API y Logstash toman la información de IP/nombre de host que forma parte de un evento, hacen una búsqueda en una copia almacenada internamente de la base de datos gratuita de MaxMind GeoLite2 y luego crean un número de campos adicionales en el evento con las coordenadas geográficas y otros campos como ciudad, estado y país. Desde allí, podemos graficarlas en un mapa en Kibana.


Comencemos el proceso y veamos cómo es esto. :)

Ingest API

Ingest API se agregó a Elasticsearch 5.0 y es una forma genial de aprovechar los recursos de clúster existentes para procesar documentos, usando cosas como grok para combinar y extraer patrones. Los casos de uso hasta ahora incluyen el envío de registros directamente a Elasticsearch desde Filebeat o el procesamiento de documentos Arxiv.org con el complemento adjunto ingest (el reemplazo para el plugin de mapper-attachment).


Y, si estos no satisfacen sus necesidades, puede incluso diseñar su propio complemento ingest utilizando este modelo, por ejemplo, para proporcionar funcionalidad de procesamiento del lenguaje natural nativamente a Elasticsearch.


Para nuestro caso de uso, necesitamos instalar el complemento geo-ip, ya que no está incluido de manera predeterminada, entonces en todos los nodos de nuestro clúster ejecutaremos:

sudo /usr/share/elasticsearch/bin/elasticsearch-plugin install ingest-geoip

Luego, asegúrese de reiniciar el/los nodo/s para habilitar el complemento.


Ahora, una de las cosas geniales de Ingest API es que puede simular un proceso, básicamente ejecute una prueba de lo que generan las etapas del procesamiento del evento pero sin indexar el evento en Elasticsearch. Esto le permite evaluar su procesador con anticipación y hacer modificaciones adecuadas si es necesario.


Para ejecutar esta simulación, simplemente definimos nuestro proceso y pasamos nuestro documento de muestra así (nota: debemos evitar las comillas del evento con barras invertidas):

POST _ingest/pipeline/_simulate
{
  "pipeline" : {
"processors" : [
    {
      "grok": {
        "field": "message",
        "patterns": ["%{COMBINEDAPACHELOG}"]
      }
    },
    {
      "geoip": {
        "field": "clientip"
      }
    }
  ]
  },
  "docs": [
    {
      "_source": {
        "message": "104.194.203.69 - - [01/Apr/2017:16:21:15 +0000] \"GET /favicon.ico HTTP/1.1\" 200 3638 \"-\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36\""
      }
    }
  ]
}

Si está familiarizado con Logstash, podrá ver que estamos usando un patrón grok predefinido llamado %{COMBINEDAPACHELOG}. Allí dentro hay un campo %{IPORHOST:clientip} que usamos luego en el procesador geoip para obtener la información que deseamos.


La salida que obtenemos de esta llamada es la siguiente:

{
  "docs": [
    {
      "doc": {
        "_type": "_type",
        "_id": "_id",
        "_index": "_index",
        "_source": {
          "request": "/favicon.ico",
          "agent": """"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36"""",
          "geoip": {
            "continent_name": "Oceania",
            "city_name": "Alexandria",
            "country_iso_code": "AU",
            "region_name": "New South Wales",
            "location": {
              "lon": 151.2,
              "lat": -33.9167
            }
          },
          "auth": "-",
          "ident": "-",
          "verb": "GET",
          "message": """104.194.203.69 - - [01/Apr/2017:16:21:15 +0000] "GET /favicon.ico HTTP/1.1" 200 3638 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36"""",
          "referrer": """"-"""",
          "response": "200",
          "bytes": "3638",
          "clientip": "104.194.203.69",
          "httpversion": "1.1",
          "timestamp": "01/Apr/2017:16:21:15 +0000"
        },
        "_ingest": {
          "timestamp": "2017-04-11T04:19:00.518Z"
        }
      }
    }
  ]
}

A continuación, podemos guardar este proceso en Elasticsearch, configurar una plantilla para asegurarnos de que los valores de geoip se traten de manera adecuada y luego usar Filebeat para enviar los datos directamente a nuestro clúster para la indexación y el almacenamiento, con nuestra información geoip agregada automáticamente.

Logstash

Al igual que con ingest API, en Logstash nuestro punto de partida es el filtro geoip. Esto viene como parte del paquete predeterminado de Logstash, de modo que no tenemos que hacer otra cosa más que quedarnos en nuestra configuración.


Una configuración básica para procesar el evento sería así:

input { stdin {} }
filter {
  grok { match => { "message" => "%{COMBINEDAPACHELOG}" } }
  geoip { source => "clientip" }
}
output { stdout { codec => "rubydebug" } }

Nuevamente estamos usando un un patrón grok predefinido y, como es lógico, los patrones grok para Ingest API son los mismos que para el filtro grok en Logstash. Esto nos permite reutilizar la funcionalidad fácilmente.


Después de procesar los datos de muestra con nuestra configuración, obtenemos lo siguiente:

{
        "request" => "/favicon.ico",
          "agent" => "\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36\"",
          "geoip" => {
              "timezone" => "Australia/Sydney",
                    "ip" => "104.194.203.69",
              "latitude" => -33.9167,
        "continent_code" => "OC",
             "city_name" => "Alexandria",
         "country_code2" => "AU",
          "country_name" => "Australia",
         "country_code3" => "AU",
           "region_name" => "New South Wales",
              "location" => [
            [0] 151.2,
            [1] -33.9167
        ],
           "postal_code" => "1435",
             "longitude" => 151.2,
           "region_code" => "NSW"
    },
           "auth" => "-",
          "ident" => "-",
           "verb" => "GET",
        "message" => "104.194.203.69 - - [01/Apr/2017:16:21:15 +0000] \"GET /favicon.ico HTTP/1.1\" 200 3638 \"-\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36\"",
       "referrer" => "\"-\"",
     "@timestamp" => 2017-04-14T00:40:18.814Z,
       "response" => "200",
          "bytes" => "3638",
       "clientip" => "104.194.203.69",
       "@version" => "1",
           "host" => "bender.local",
    "httpversion" => "1.1",
      "timestamp" => "01/Apr/2017:16:21:15 +0000"
}

Ahora, observe que la ubicación realmente es un conjunto de coordenadas y que mantiene la latitud y la longitud como campos separados, en caso de que sea necesario. ¡Eso es!


Como queremos almacenar los datos en Elasticsearch, para poder analizarlos con Kibana, asegúrese de cambiar la salida para usar el complemento de salida de elasticsearch.

Mapear para mapas

Tanto Logstash como Filebeat pueden administrar automáticamente la plantilla que necesitan para asegurar que un campo geoip que se envía a Elasticsearch está correctamente gestionado como un tipo de dato de geo_point.


Suponiendo que estamos usando el patrón de nombre de índice predeterminado de Logstash o Filebeat, enviar nuestro evento de muestra de Logstash a Elasticsearch significa que también aplicamos la plantilla predeterminada que se envía con Logstash o Filebeat. Las partes de interés específicas para nosotros son las siguientes:

"geoip"  : {
  "dynamic": true,
  "properties" : {
    "ip": { "type": "ip" },
    "location" : { "type" : "geo_point" },
    "latitude" : { "type" : "half_float" },
    "longitude" : { "type" : "half_float" }
  }
}

Esto significa es que cualquier campo llamado geoip.location, que es la forma en que nos referimos a estos campos anidados, que se envía a Elasticsearch se maperá automáticamente como un geo_point.


Si está usando Ingest API directamente, necesitará asegurarse de que tiene una plantilla existente local. La opción más fácil aquí es copiar las plantillas anteriores de Logstash o Filebeat de los enlaces y modificarlas como corresponde.


Los lectores más observadores habrán notado que las salidas de datos geoip desde Ingest API y Logstash tenían un formato diferente. Elasticsearch acepta varios formatos geoip tal como también lo permite el estándar GeoJSON. Puede ver los distintos formatos permitidos en la documentación.

Usemos los puntos

Ahora que hemos procesado datos dentro de Elasticsearch, elaboremos unos gráficos. Una vez que haya agregado un patrón de índice a Kibana, podemos asegurarnos de que el campo está definido como un punto geográfico. Un campo mapeado correctamente se verá así con la configuración de patrón de índice:


Y así en Discover:


Ahora que está todo confirmado y completo, visite a la sección Visualización y cree un nuevo mapa de mosaicos, utilizando el patrón de índice que acaba de definir. Una vez que elige el patrón, y suponiendo que sus puntos geográficos están mapeados correctamente, Kibana completará automáticamente los ajustes de visualización, como qué campo agregar, y mostrará el mapa casi al instante, el cual se verá más o menos así:


En nuestro ejemplo solo tenemos un punto de datos pero con más puede hacer zoom y vista panorámica, y Kibana redistribuirá los datos agregados según sea necesario, e incluso puede crear un cuadro delimitador geográfico o un filtro para obtener los datos específicos que necesita.


Este es un ejemplo de un mapa más completo. Estos datos son 100 % reales y se obtuvieron de personas que visitan nuestro blog, por ejemplo, usted, aquí y ahora.

¿Qué ocurre si no puedo crear un mapa?

A veces las cosas no suceden como las planeamos. Ha indexado sus datos en Elasticsearch y ahora quiere usarlos en Kibana pero cuando intenta crear un nuevo mapa de mosaicos no hay campos para seleccionar. Le genera una profunda tristeza.


Este es un problema que pueden tener los usuarios y puede estar causado por unos pocos antipatrones comunes.

Mover cosas a campos personalizados

De vez en cuando, vemos configuraciones como esta:

geoip {
  source => "src_ip"
  target => "geoip"
  add_field => [ "[geoip][coordinates]", "%{[geoip][longitude]}" ]
  add_field => [ "[geoip][coordinates]", "%{[geoip][latitude]}"  ]
}
mutate {
  convert => [ "[geoip][coordinates]", "float"]
}

El filtro geoip ya crea un campo de ubicación, como vimos en los ejemplos anteriores, usando add_field simplemente duplica los datos y no coincidirá con la plantilla predeterminada en Elasticsearch. Esto significa que los datos probablemente se mapearán dinámicamente en un conjunto numérico y no podrá usar un mapa en Kibana sin modificaciones adicionales.

Otra variación es cambiar el valor objetivo del predeterminado: geoip. Si hace esto, deberá actualizar el mapeo/la plantilla de índices para solucionar el asunto. Más sobre esto a continuación.

Nombres de índices personalizados

Si utiliza un nombre/patrón de índice personalizado en la salida de elasticsearch o cambia el campo objetivo predeterminado para que se completen automáticamente los datos geoip, necesitará copiar la plantilla predeterminada de Logstash/Filebeat que mencionamos anteriormente y asegurarse de que coincida con estos cambios. Para hacerlo, puede copiar el archivo que usa Logstash/Filebeat y luego consultar el cambio uno, o tomar la plantilla de Elasticsearch APIs y modificarla.


Un consejo rápido si toma esta vía es que busque y actualice el valor "template": "logstash-*" en la plantilla o el archivo.


Finalmente, podría ser más fácil usar un nombre de índice de logstash-customvalue-YYYY.MM.DD (en donde customvalue es un valor de su elección), en lugar de cambiar todo el patrón. De esta manera, la plantilla predeterminada todavía se podrá aplicar.

_geoip_lookup_failure

Si aparece esto, entonces Logstash no ha podido encontrar el campo que dijo que debería contener una IP o un nombre de host para mapear. Verifique si su patrón grok está extrayendo el campo correctamente y si el filtro de geoip lo menciona correctamente.


El uso de una sección stdout { codec => rubydebug } en su salida de Logstash puede ayudarle a ver qué está sucediendo y qué no.

Geolocalización incorrecta

Es posible que la IP coincida con una ubicación incorrecta. Tenga en cuenta que la base de datos gratuita de Maxmind que se usa es “similar pero menos precisa que las bases de datos GeoIP2 de MaxMind ” y “la geolocalización IP es esencialmente imprecisa. Las ubicaciones generalmente se encuentran cerca del centro de la población.” Consulte el sitio de MaxMind para obtener más información.


Existen bases de datos disponibles en el mercado que pueden ser más precisas. Para usarlas, deberá definir la ruta de la base de datos en su configuración de Logstash o database_file para Ingest API, para indicar el archivo de base de datos obtenido.

No se han encontrado resultados

Verifique que su selector de hora (en el extremo superior derecho de la pantalla) indique la hora correcta en la que se encuentran sus datos. Si es correcta, diríjase a Discover y asegúrese de que los eventos en la ventana de la hora definitivamente tengan un campo geoip.


Si aún no lo logra, vuelva a revisar los pasos desde el comienzo de este blog, especialmente los mapeos.

Veo puntos de datos pero no veo mapas.

Kibana usa el servicio de Elastic Tile Map para proporcionar los mosaicos que componen el mapa real.  Sin embargo, algunas organizaciones pueden bloquear el acceso a este servicio con cortafuegos o servidores proxy, y esto interrumpirá la capacidad de mostrar su región geográfica favorita en el explorador. Se sabe que complementos de explorador como Privacy Badger también han dejado de cargar los mosaicos, así que vuelva a revisarlos, intente quizás con una pestaña de incógnito.


Si no puede evitar las políticas de seguridad, es posible que tenga que considerar establecer su propio servicio de mapa de mosaicos, o dirigir a Kibana a un servicio WMS compatible alternativo, como se menciona en la documentación.

Conclusión

Como podemos ver, la extracción de geoip es muy valiosa en muchos casos de uso distintos. Y con solo unos pasos, hemos demostrado dos métodos simples para obtener esta información de un evento y mostrarla con Kibana.


No hemos analizado la capacidad de usar mapas personalizados en detalle pero si desea un poco de inspiración u orientación, consulte alguna de estas publicaciones de blog: Kibana y Custom Tile Server for NHL Data o Earthquake data with the Elastic Stack.


Por último, no piense demasiado sobre su configuración. Deje la predeterminada para los nombres de campo e índices en donde pueda, y si tiene dudas, consulte la documentación.

Si los problemas persisten o tiene más preguntas, visite los foros de nuestra comunidad para obtener ayuda adicional de nuestros extraordinarios e increíbles técnicos de la comunidad de Logstash y el resto del equipo de Elastic.