Analisi dei log del Webserver con Kibana ed Elasticsearch per la SEO

Ciao a tuttə. Quest’oggi vorrei iniziare a proporre un tema che sta sicuramente a cuore a molti: come fare una analisi dei log del proprio webserver senza impazzire.

Specifico iniziare perché l’argomento è veramente ampio e non ho la pretesa di sviscerarlo in quest’unico articolo.

Prima di iniziare vorrei darvi qualche informazione sul mio ambiente di lavoro così che possiate regolarvi se decideste di riprodurre il presente tutorial.

Ambiente di Lavoro

Io attualmente lavoro con un Mac Intel (i7 e 16 GB di RAM), la scelta più logica per avere Elasticsearch e Kibana sulla propria macchina senza sporcare tutto è quella di usare Docker Desktop.

Va da se che potrete adattare la configurazione presente ad ambienti sia Windows che Linux: la maggior parte del tutorial resta valida.

Cosa vogliamo fare?

Allora… recentemente ho seguito un evento ufficiale su Knime; il bravissimo relatore ha portato queste slide (che vi invito a visionare qui) che mi hanno solleticato non poco la fantasia 🙂 .

Knime è un potente tool per la data science… grazie all’installazione del pacchetto per la Web Analytics diventa un potente ausilio per noi poveri SEO sempre a caccia di insight.

Quindi, prima di andare oltre, vi consiglio di leggere e provare almeno la prima parte delle slide.

L’obiettivo è replicare queste analisi in un ambiente composto da Elasticsearch e Kibana.

Va notato che, aggiungendo ulteriori componenti sviluppati sempre da Elastic, come Filebeat e Logtash, è possibile effettuare queste ed altre analisi in tempo reale su log di vari tipi di servizi (Apache, nginx, MySQL, Redis, etc.).

Noi utilizzeremo tutto sommato l’un percento delle enormi potenzialità messe a disposizione dallo stack ELK (ELK è l’acronimo di Elasticsearch, Logtash e Kibana); tuttavia vedrete che la semplicità delle query in KQL permette di fare analisi veloci sui log di un sito.

E adesso andiamo ad iniziare.

Preparazione di Docker e Installazione di Elasticsearch e Kibana

Iniziate scaricando e installando Docker per la vostra piattaforma: la pagina ufficiale dedicata all’installazione è scritta bene ed è ricca di informazioni dettagliate per le varie piattaforme.

Se siete utenti della mela vi basterà scaricare il pacchetto e installarlo come si fa con altri software.

Ci sono alcuni fine tuning da fare per utilizzare bene Elasticsearch (che è esoso di risorse) in Docker.

  • Se avete Docker Desktop dedicategli almeno 4 GB di RAM e almeno 20 GB di spazio per l’HD (io ho anche assegnato 2 core)
  • Se usate Linux settate vm.max_map_count ad almeno 262144 (istruzioni dettagliate si trovano in rete e alla pagina ufficiale dell’installazione di Elasticsearch con Docker, di cui la prima parte del tutorial è una semplice applicazione)

Alla sopradetta pagina si presenta un file docker-compose.yml che è spettacolare 🙂 … fa veramente tutto da solo.

Però quel file setta un cluster con tre nodi… la cosa per il mio utilizzo è un po’ inutile e così l’ho semplificato un po’.

Di seguito il docker-compose.yml utilizzato in questo tutorial.

version: "2.2"

services:
  setup:
    image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION}
    volumes:
      - certs:/usr/share/elasticsearch/config/certs
    user: "0"
    command: >
      bash -c '
        if [ x${ELASTIC_PASSWORD} == x ]; then
          echo "Set the ELASTIC_PASSWORD environment variable in the .env file";
          exit 1;
        elif [ x${KIBANA_PASSWORD} == x ]; then
          echo "Set the KIBANA_PASSWORD environment variable in the .env file";
          exit 1;
        fi;
        if [ ! -f config/certs/ca.zip ]; then
          echo "Creating CA";
          bin/elasticsearch-certutil ca --silent --pem -out config/certs/ca.zip;
          unzip config/certs/ca.zip -d config/certs;
        fi;
        if [ ! -f config/certs/certs.zip ]; then
          echo "Creating certs";
          echo -ne \
          "instances:\n"\
          "  - name: elasticsearch\n"\
          "    dns:\n"\
          "      - elasticsearch\n"\
          "      - localhost\n"\
          "    ip:\n"\
          "      - 127.0.0.1\n"\
          > config/certs/instances.yml;
          bin/elasticsearch-certutil cert --silent --pem -out config/certs/certs.zip --in config/certs/instances.yml --ca-cert config/certs/ca/ca.crt --ca-key config/certs/ca/ca.key;
          unzip config/certs/certs.zip -d config/certs;
        fi;
        echo "Setting file permissions"
        chown -R root:root config/certs;
        find . -type d -exec chmod 750 \{\} \;;
        find . -type f -exec chmod 640 \{\} \;;
        echo "Waiting for Elasticsearch availability";
        until curl -s --cacert config/certs/ca/ca.crt https://elasticsearch:9200 | grep -q "missing authentication credentials"; do sleep 30; done;
        echo "Setting kibana_system password";
        until curl -s -X POST --cacert config/certs/ca/ca.crt -u "elastic:${ELASTIC_PASSWORD}" -H "Content-Type: application/json" https://elasticsearch:9200/_security/user/kibana_system/_password -d "{\"password\":\"${KIBANA_PASSWORD}\"}" | grep -q "^{}"; do sleep 10; done;
        echo "All done!";
      '
    healthcheck:
      test: ["CMD-SHELL", "[ -f config/certs/elasticsearch/elasticsearch.crt ]"]
      interval: 1s
      timeout: 5s
      retries: 120

  elasticsearch:
    depends_on:
      setup:
        condition: service_healthy
    image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION}
    volumes:
      - certs:/usr/share/elasticsearch/config/certs
      - elasticsearchdata:/usr/share/elasticsearch/data
    ports:
      - ${ES_PORT}:9200
    environment:
      - node.name=elasticsearch
      - cluster.name=${CLUSTER_NAME}
      - cluster.initial_master_nodes=elasticsearch
      - ELASTIC_PASSWORD=${ELASTIC_PASSWORD}
      - bootstrap.memory_lock=true
      - xpack.security.enabled=true
      - xpack.security.http.ssl.enabled=true
      - xpack.security.http.ssl.key=certs/elasticsearch/elasticsearch.key
      - xpack.security.http.ssl.certificate=certs/elasticsearch/elasticsearch.crt
      - xpack.security.http.ssl.certificate_authorities=certs/ca/ca.crt
      - xpack.security.http.ssl.verification_mode=certificate
      - xpack.security.transport.ssl.enabled=true
      - xpack.security.transport.ssl.key=certs/elasticsearch/elasticsearch.key
      - xpack.security.transport.ssl.certificate=certs/elasticsearch/elasticsearch.crt
      - xpack.security.transport.ssl.certificate_authorities=certs/ca/ca.crt
      - xpack.security.transport.ssl.verification_mode=certificate
      - xpack.license.self_generated.type=${LICENSE}
    mem_limit: ${MEM_LIMIT}
    ulimits:
      memlock:
        soft: -1
        hard: -1
    healthcheck:
      test:
        [
          "CMD-SHELL",
          "curl -s --cacert config/certs/ca/ca.crt https://localhost:9200 | grep -q 'missing authentication credentials'",
        ]
      interval: 10s
      timeout: 10s
      retries: 120

  kibana:
    depends_on:
      elasticsearch:
        condition: service_healthy
    image: docker.elastic.co/kibana/kibana:${STACK_VERSION}
    volumes:
      - certs:/usr/share/kibana/config/certs
      - kibanadata:/usr/share/kibana/data
    ports:
      - ${KIBANA_PORT}:5601
    environment:
      - SERVERNAME=kibana
      - ELASTICSEARCH_HOSTS=https://elasticsearch:9200
      - ELASTICSEARCH_USERNAME=kibana_system
      - ELASTICSEARCH_PASSWORD=${KIBANA_PASSWORD}
      - ELASTICSEARCH_SSL_CERTIFICATEAUTHORITIES=config/certs/ca/ca.crt
    mem_limit: ${MEM_LIMIT}
    healthcheck:
      test:
        [
          "CMD-SHELL",
          "curl -s -I http://localhost:5601 | grep -q 'HTTP/1.1 302 Found'",
        ]
      interval: 10s
      timeout: 10s
      retries: 120

volumes:
  certs:
    driver: local
  elasticsearchdata:
    driver: local
  kibanadata:
    driver: local

Oltre al docker-compose.yml la pagina sopradetta fa creare anche un file .env contenente i settaggi da passare a Docker e quindi di seguito il .env utilizzato per questo tutorial.

# Password for the 'elastic' user (at least 6 characters)
ELASTIC_PASSWORD=CHANGEME

# Password for the 'kibana_system' user (at least 6 characters)
KIBANA_PASSWORD=CHANGEME

# Version of Elastic products
STACK_VERSION=8.4.2

# Set the cluster name
CLUSTER_NAME=docker-stackelk

# Set to 'basic' or 'trial' to automatically start the 30-day trial
LICENSE=basic
#LICENSE=trial

# Port to expose Elasticsearch HTTP API to the host
ES_PORT=9200
#ES_PORT=127.0.0.1:9200

# Port to expose Kibana to the host
KIBANA_PORT=5601
#KIBANA_PORT=80

# Increase or decrease based on the available host memory (in bytes)
MEM_LIMIT=1073741824

# Project namespace (defaults to the current folder name if not set)
COMPOSE_PROJECT_NAME=stackelk

A questo punto il lavoro da fare è veramente semplice , con un comando, grazie alla potenza di Docker, avremo tutto pronto per noi.

Creiamo una cartella (anche sul desktop va benissimo) e copiamoci dentro i due file precedenti: il docker-compose.yml e il .env; apriamo il teminale, entriamo nella cartella contenente i file e diamo il comando

docker-compose up -d

A questo punto diamogli un po’ di tempo (possiamo seguire l’evoluzione del processo dal terminale) e poi andiamo su http://localhost:5601.

Primo login su Kibana e caricamento dei dati

Se tutto è andato per il verso giusto alla pagina sopradetta dovrebbe spuntervi una schermata di login; le credenziali sono:

Username: elastic

Password: quella inserita nel file .env (se non l’avete cambiata è CHANGEME 😉 )

E adesso facciamo una breve digressione sui log del webserver e su come prepararli.

Preparazione dei dati

I log di cui stiamo parlando sono quelli che avevo disponibili, i file access.log nel formato COMBINED di Apache.

Il valore aggiunto portato dallo strumento che stiamo usando è che ha template per i più svariati formati di log e quindi è necessario poco lavoro per adattarlo al vostro caso specifico, casomai ce ne fosse bisogno (il formato COMBINED è piùttosto diffuso e generato spesso anche da NGINX).

Caricare i dati in Kibana

Cliccate su http://localhost:5601/app/home#/tutorial_directory/fileDataViz (dopo esservi loggati) per accedere all’uploader di Kibana e uppate il vostro file.

Alla finestra Import Data andate su advanced e modificate la pipeline con la seguente (per me ha funzionato così ma potrebbero essere necessari degli adattamenti).

{
  "description": "Ingest pipeline created by text structure finder",
  "processors": [
    {
      "grok": {
        "field": "message",
        "patterns": [
          "%{COMBINEDAPACHELOG}"
        ]
      }
    },
    {
      "date": {
        "field": "timestamp",
        "formats": [
          "dd/MMM/yyyy:HH:mm:ss XX"
        ]
      }
    },
    {
      "convert": {
        "field": "bytes",
        "type": "long",
        "ignore_missing": true
      }
    },
    {
      "convert": {
        "field": "httpversion",
        "type": "double",
        "ignore_missing": true
      }
    },
    {
      "convert": {
        "field": "response",
        "type": "long",
        "ignore_missing": true
      }
    },
    {
        "geoip": {
          "description": "Add 'source.geo' GeoIP data for 'clientip'",
          "field": "clientip",
          "target_field": "source.geo"
        }
      },
    {
      "remove": {
        "field": "timestamp"
      }
    }
  ]
}

In particolare attiro la vostra attenzione sul campo geoip, necessario per georeferenziare gli IP presenti nei log, che va sicuramente aggiunto.

Tra Dashboard e Discover

All’indirizzo http://localhost:5601/app/kibana_overview#/ potrete accedere alle potenti funzioni di analisi offerte dalla combinazione di Elasticsearch e Kibana.

Dashboard

Cliccando su Dashboard dal link soprastante si accede allo spazio di lavoro nel quale è possibile creare dei cruscotti (automatizzati se si usasse anche il resto dello stack ELK).

Io non mi sento particolarmente ferrato in data analytics ma un consiglio che posso darvi è quello di replicare l’excel risultante dalle slide di Knime.

Questo strumento potrebbe essere un ottimo modo per avere sott’occhio i principali parametri di un sito.

Discover

Discover è più per i “puristi” 😉 permette di analizzare i dati grezzi anche tramite le query nel potente linguaggio KQL (che è anche veramente semplice grazie all’autocompletamento offerto da Kibana).

Per quanto riguarda il KQL (che sto studiando anch’io) vi posso rimandare ad un buonissima risorsa online di Elastic.

Cosa andare a cercare?

Va da se che alcuni KPI possiamo identificarli sicuramente:

  • Quante volte si è acceduto ad una risorsa online? (probabilmente le pagine più visitate saranno la home e il robots.txt, per evidenti motivi; attenzione perché strani accessi potrebbero denotare anche problemi di sicurezza)
  • Quanti accessi per nazione? E a che risorsa?
  • Quanto traffico si è scambiato con una nazione? (o con una città, visto che abbiamo attivato la geolocalizzazione degli ip 😉 )
  • Che versione del protocollo si è usato? (attenzione perché accessi con la versione uno dell’HTTP son sospette)
  • Accessi da googlebot (si trova facilmente la stringa dello user agent online)
  • etc.

Conclusioni

Devo ammettere che, nonostante un setup iniziale forse un po’ troppo complicato da mettere in piedi, questa soluzione si sta dimostrando di grande usilio nel permettermi di osservare in dettaglio i comportamenti dei visitatori sul mio sito.


Pubblicato

in

,

da

Commenti

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.