Graylog: Obtener y manipular los logs de Apache en una infraestructura de docker bajo traefik

Buenas a todo@s. En este post vamos a hacer que nuestro graylog, reciba los logs de apache montado en docker y bajo un web proxy que será traefik. A primera vista parece sencillo, pero hay que hacer un par de cosillas para que toda la info llegue correcta y podamos manipularla.

Pasos previos

Graylog

Antes que nada, tenemos que tener montado nuestro graylog. Este irá sobre docker y con traefik y lo desplegaremos con docker-compose.

mkdir graylog && cd graylog
vim docker-compose.yml
version: '3'

services:
  mongodb:
    container_name: graylog_mongodb
    image: mongo:3
    volumes:
      - mongodb:/data/db:Z
    networks:
      - backend
    labels:
      - "traefik.enable=false"

  elasticsearch:
    container_name: graylog_elasticsearch
    image: docker.elastic.co/elasticsearch/elasticsearch:5.6.10
    volumes:
      - elasticsearch:/usr/share/elasticsearch/data:Z
    environment:
      - http.host=0.0.0.0
      - xpack.security.enabled=false
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
      - cluster.name=graylog
      - TZ=Europe/Madrid
    ulimits:
      memlock:
        soft: -1
        hard: -1
    networks:
      - backend
    labels:
      - "traefik.enable=false"

  graylog:
    container_name: graylog_server
    image: graylog/graylog:2.4
    environment:
      GRAYLOG_PASSWORD_SECRET: pc5segCzHPNlVn5eTIU0mrQtSiueSQTXIq0XOe0ZSarEqJ3yw6VY58INFNrATDnaUzVdquh6ugY0HHHHCqstARbr8gV3zyrI
      GRAYLOG_ROOT_PASSWORD_SHA2:faddf971d18f29934eeec767f66c1026bb3ad6a927501d9783ed009c7829abe5GRAYLOG_WEB_ENDPOINT_URI: 'https://graylog.example.com/api'
      GRAYLOG_ELASTICSEARCH_HOSTS: 'http://elasticsearch:9200'
      GRAYLOG_ROOT_TIMEZONE: 'Europe/Madrid'
      GRAYLOG_SERVER_JAVA_OPTS: '-Xms512m -Xmx512m -XX:NewRatio=1 -XX:MaxMetaspaceSize=128m -server -XX:+ResizeTLAB -XX:+UseConcMarkSweepGC -XX:+CMSConcurrentMTEnabled -XX:+CMSClassUnloadingEnabled -XX:+UseParNewGC -XX:-OmitStackTraceInFastThrow'
      ES_MEMORY: 1g
      TZ: 'Europe/Madrid'
    volumes:
      - conf:/usr/share/graylog:Z
      - journal:/usr/share/graylog/data/journal:Z
      - ./confs/GeoLite2-City.mmdb:/etc/graylog/server/GeoLite2-City.mmdb
    depends_on:
      - mongodb
      - elasticsearch
    links:
      - mongodb:mongo
    ports:
      - 1514:1514/tcp
      - 1514:1514/udp
    networks:
      - backend
      - traefik
    labels:
      - "traefik.backend=graylog"
      - "traefik.frontend.rule=Host:graylog.example.com"
      - "traefik.docker.network=traefik"
      - "traefik.port=9000"

  volumes:
    mongodb:
    elasticsearch:
    conf:
    journal:

  networks:
    backend:
    traefik:
      external:
      name: traefik

Ahora añadiremos la base de datos de geolite2

mkdir confs && cd confs
wget http://geolite.maxmind.com/download/geoip/database/GeoLite2-City.tar.gz &&  tar -zxvf GeoLite2-City.tar.gz GeoLite2-City_20180911/GeoLite2-City.mmdb && mv GeoLite2-City_*/GeoLite2-City.mmdb . && rm -fr GeoLite2-City.tar.gz GeoLite2-City_*

Una vez con esto levantar el stack

docker-compose up -d

Traefik

Ahora tenemos que hacer que Traefik pase la IP del host que accede a apache. Esto se hace con el parámetro forwardedHeaers , de lo contrario, en los logs de apache solo se verán peticiones desde la IP de traefik.

vim traefik.toml
[entryPoints]
  [entryPoints.http]
    address = ":80"
    [entryPoints.http.forwardedHeaders]
      trustedIPs = ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]

  [entryPoints.https]
    address = ":443"
    [entryPoints.https.forwardedHeaders]
      trustedIPs = ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]

Apache

Y por último, queda configurar apache para que en los logs interprete bien la IP externa. Para ello, hay que habilitar un módulo y configurar los logs.

Habilitar el módulo:

a2enmod remoteip

Editar el apache.conf  o httpd.conf  y des-comentar:

LoadModule remoteip_module modules/mod_remoteip.so

Y cambiar el formato el log. Hay que cambiar el %h  por el %a

#LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%a %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined

Y reiniciar apache

Configuración

Apache

Hay que configurar apache para mandar los logs a graylog. Si se ha montado con docker. Basta con añadir en el docker-compose en la sección de apache lo siguiente:

logging:
  driver: syslog
  options:
    syslog-address: "udp://graylog.example.com:1514"
    tag: "apache"

Si no es así, hay que mirar cual es la opción que mas nos conviene. Graylog tiene una herramienta que es el sidecar para enviar logs.

Graylog

En graylog, hay que hacer unas primeras configuraciones para que funcione la geolocalización

Ir a configuración y habilitar la geo-location

Y poner en orden correcto el Message Processors Configuration. Lo importante es que el GeoIP Resolver esté al final

Ahora toca crear el input para recibir los logs. Para esto ir a la sección de Inputs y crear uno. En nuestro caso va a ser del tipo Syslog UDP .

Ahora le añadiremos un extractor de Apache. Esto puede que haya que adaptarlo cada uno a sus logs

Importar el extractor

El que vamos a utilizar es este pero un poco cambiado. Solo hay que copiar y pegarlo

{
  "extractors": [
    {
      "title": "Apache Time",
      "extractor_type": "regex",
      "converters": [],
      "order": 0,
      "cursor_strategy": "copy",
      "source_field": "message",
      "target_field": "apache_time",
      "extractor_config": {
        "regex_value": "\\:(\\d+\\:\\d+:\\d+)\\s"
      },
      "condition_type": "none",
      "condition_value": ""
    },
    {
      "title": "Apache user agent",
      "extractor_type": "split_and_index",
      "converters": [],
      "order": 0,
      "cursor_strategy": "copy",
      "source_field": "message",
      "target_field": "Apache_client_UserAgent",
      "extractor_config": {
        "index": 6,
        "split_by": "\""
      },
      "condition_type": "none",
      "condition_value": ""
    },
    {
      "title": "Apache_date",
      "extractor_type": "regex",
      "converters": [],
      "order": 0,
      "cursor_strategy": "copy",
      "source_field": "message",
      "target_field": "Apache_date",
      "extractor_config": {
        "regex_value": "\\[(\\d+\\/\\w+\\/\\d+)"
      },
      "condition_type": "none",
      "condition_value": ""
    },
    {
      "title": "Apache Client IP",
      "extractor_type": "split_and_index",
      "converters": [],
      "order": 0,
      "cursor_strategy": "copy",
      "source_field": "message",
      "target_field": "Apache_client_IP",
      "extractor_config": {
        "index": 2,
        "split_by": " "
      },
      "condition_type": "regex",
      "condition_value": "(?<![0-9])(?:(?:[0-1]?[0-9]{1,2}|2[0-4][0-9]|25[0-5])[.](?:[0-1]?[0-9]{1,2}|2[0-4][0-9]|25[0-5])[.](?:[0-1]?[0-9]{1,2}|2[0-4][0-9]|25[0-5])[.](?:[0-1]?[0-9]{1,2}|2[0-4][0-9]|25[0-5]))(?![0-9])"
    },
    {
      "title": "Apache http request",
      "extractor_type": "split_and_index",
      "converters": [],
      "order": 0,
      "cursor_strategy": "copy",
      "source_field": "message",
      "target_field": "Apache_http_request",
      "extractor_config": {
        "index": 2,
        "split_by": "\""
      },
      "condition_type": "none",
      "condition_value": ""
    },
    {
      "title": "Apache referrer",
      "extractor_type": "split_and_index",
      "converters": [],
      "order": 0,
      "cursor_strategy": "copy",
      "source_field": "message",
      "target_field": "Apache_referrer",
      "extractor_config": {
        "index": 4,
        "split_by": "\""
      },
      "condition_type": "none",
      "condition_value": ""
    },
    {
      "title": "Whois",
      "extractor_type": "lookup_table",
      "converters": [],
      "order": 0,
      "cursor_strategy": "copy",
      "source_field": "Apache_client_IP",
      "target_field": "Whois",
      "extractor_config": {
        "lookup_table_name": "whois"
      },
      "condition_type": "none",
      "condition_value": ""
    },
    {
      "title": "Apache http status code",
      "extractor_type": "regex",
      "converters": [],
      "order": 0,
      "cursor_strategy": "copy",
      "source_field": "message",
      "target_field": "Apache_http_status",
      "extractor_config": {
        "regex_value": "HTTP[^\"]*\" (200|301|302|304|404|500)"
      },
      "condition_type": "none",
      "condition_value": ""
    }
  ],
  "version": "2.4.6"
}

Y una vez hecho esto, los logs de apache ya empezarán a tener mas campos y podremos manipularlos de una manera mas fácil

Con esto, ya se podrá empezar a crear dashboards e ir añadiendo lo que nos interese.

 

One comment

  1. Hola Isma!

    Soy Quique, cuánto tiempo! ¿Sería muy complicado esto mismo aplicado a Nginx? Tenemos un compose con Nginx y Tomcat 8 y nos gustaría gestionar esos logs.

    Una alegría leerte. Un abrazo,
    Quique

Leave a Reply

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