De l'audimetrie de Traefik libre avec Matomo

De l'audimetrie de Traefik libre avec Matomo

Il y a quelque temps, je vous avais parlé du tracking et des raisons pour lesquelles je tenais à ma vie privée. Dans ma conclusion, j'indiquais que le tracking utilisateur restait tout de même un outil utile pour une entreprise, à condition qu'il soit éthique et respectueux des utilisateurs.

Toutefois, très souvent, je vois que Google Analytics est utilisé par les sites sur lesquels je navigue. Ce dernier est loin (même très loin) d'être respectueux des données de vos utilisateurs. Pire! Vous permettez à Google de connaitre l'activité de votre site de bout en bout et de savoir comment mieux cibler ses publicités (entre autres).

Aujourd'hui, je vous propose donc de voir comment il est possible de faire du tracking utilisateur en faisant preuve de respect pour ce dernier.

Matomo, qu'est-ce que c'est ?

Matomo, c'est une alternative OpenSource à Google Analytics. Disponible sur GitHub, elle vous permet de déployer une application complète avec un dashboard et un système de tracking en JavaScript. Il est aussi possible de se baser sur la lecture des logs du serveur plutôt que le JavaScript.

matomo-org/matomo
Liberating Web Analytics. Star us on Github? +1. Matomo is the leading open alternative to Google Analytics that gives you full control over your data. Matomo lets you easily collect data from webs...

Avoir une solution libre vous permet de connaître ses méandres, mais surtout et le gage de respect pour vos utilisateurs. Le libre porte aujourd'hui la majorité d'internet (dont ce site web).

Matomo vous permet donc d'avoir les statistiques utilisées classiquement comme (de manière non exhaustive) :

  • Les pages visitées
  • Les "User Agent" (OS, Navigateur, langue, etc.)
  • Le taux de transformation depuis les réseaux sociaux
  • Du GeoIp
  • Le taux de rebond
  • La durée des sessions

Basée sur le couple classique, mais efficace PHP + MySql, l'application est légère et intègre aussi des composants visant au respect de la vie privée :

  • Support du header DoNotTrack, qui vous permet d'indiquer que vous ne souhaitez pas que le site web suive votre activité
  • Support des contraintes RGPD (consentement éclairé), il est possible d'activer ou désactiver directement Matomo pour chaque utilisateur
  • Anonymisation ou pseudoanonymisation native des données

Ainsi il est possible d'avoir des statistiques claires et complètes sans être intrusif.

À noter qu'il existe aussi une solution managée directement par Matomo, disponible sur leur site internet.

Matomo - L’alternative à Google Analytics qui protège vos données
Matomo est l’alternative éthique à Google Analytics qui protège vos données et la vie privée de vos clients Une puissante plateforme d’analyse Web avec la propriété de 100 % des données.

Dans le cas que je vais vous présenter aujourd'hui, nous partirons de l'image docker officielle.

Deployer Matomo dans Kubernetes

Comme d'habitude sur ce blog, je vais vous parler du déploiement dans Kubernetes, car c'est ce que j'ai sous la main et c'est devenu de plus en plus courant aujourd'hui.

Pour rappel, mon déploiement n'est pas un déploiement "prod ready" en entreprise, il ne gère pas la haute disponibilité et le scaling est catastrophique vu que la base de données est dans le même pod. Ce billet reste simplement une "démo" de ce qui est possible pour que vous puissiez l'adapter facilement à votre besoin. L'idée étant bien sûr que vous puissiez vous approprier l'outil.

Bien que le déploiement soit dans Kubernetes, il reste facilement transposable sur Docker ou Docker-compose (ou podman, ou rancher, etc.).

Voici donc le déploiement que nous allons faire, en bleu, ce sont les éléments que nous allons créer :

Nous allons donc avoir 3 fichiers yaml :

  • Un "deployment" portant Matomo et MySql
  • Un "service" permettant d'exposer Matomo
  • Un IngressRoute permettant à Traefik de servir Matomo et gérer son certificat TLS

Le déploiement

Nous sommes ici sur quelque chose de très basique :

  • Deux images (Matomo et MySql)
  • Une configuration de la base de données (utilisateur, base par défaut, etc.)
  • Du stockage persistant
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: matomo
  labels:
    app: matomo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: matomo
  template:
    metadata:
      labels:
        app: matomo
    spec:
      containers:
      - name: matomo
        image: matomo:4.1.1-apache
        imagePullPolicy: Always
        ports:
        - containerPort: 80
        env:
        - name: MATOMO_DATABASE_HOST
          value: 127.0.0.1
        - name: MATOMO_DATABASE_USERNAME
          value: "root"
        - name: MATOMO_DATABASE_PASSWORD
          value: "matomo"
        - name: MATOMO_DATABASE_DBNAME
          value: "matomo"
        volumeMounts:
        - name: matomo-storage
          mountPath: /var/www/html/config
      - name: mysql
        volumeMounts:
        - name : mysql-storage
          mountPath: /var/lib/mysql
        image: mysql:8.0.23
        imagePullPolicy: Always
        ports:
        - containerPort: 3306
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: "matomo"
        - name: MYSQL_DATABASE
          value: "matomo"
        resources:
          limits:
            cpu: "0.2"
            memory: "512Mi"
      volumes:
      - name: mysql-storage
        hostPath:
          path: /home/kube/matomo/content/mysql
          type: Directory
      - name: matomo-storage
        hostPath:
          path: /home/kube/matomo/content/matomo
          type: Directory

On retrouve donc tout d'abord le container Matomo, basé sur une image Apache.

Ce dernier a donc comme configuration :

  • Un binding sur le port 80 du pod
  • La configuration de la base de données. Dans mon exemple, je n'ai pas besoin de mettre un mot de passe fort, car la base est dédiée à Matomo est uniquement accessible par ce dernier.
  • Un stockage persistant. Cela permet à Matomo de stocker ses configurations localement (notamment la configuration de la base de données). Cette configuration n'est pas valide pour un déploiement haute disponibilité, vu que j'exploite du stockage local

Ensuite, nous avons un second container qui porte la base de données MySql, avec comme configuration :

  • Les logins et mots de passe sur la base de données
  • Le binding du port 3306 à l'intérieur du pod uniquement, donc uniquement accessible par Matomo
  • Un stockage persistant : Je ne veux pas perdre mes données au moindre redémarrage. Une fois de plus, cette configuration n'est pas valide pour un déploiement en haut disponibilité.

Ensuite, nous avons un service, qui va nous permettre d'exposer Matomo :

kind: Service
apiVersion: v1
metadata:
  labels:
    app: matomo
  name: matomo
spec:
  type: ClusterIP
  ports:
  - port: 80
    name: http
  selector:
    app: matomo

Rien de transcendant ici, je bind simplement le port 80 de mon pod sur une ClusterIP Kubernetes.

Enfin, je déclare un IngressRoute, c'est ce dernier qui permettra à Traefik de router le trafic vers mon pod.

kind: IngressRoute
metadata:
  name: matomo-tls
  namespace: default
spec:
  entryPoints:
    - websecure
  routes:
  - kind: Rule
    match: Host(`matomo.tferdinand.net`)
    services:
    - name: matomo
      port: 80
    middlewares:
      - name: security
  tls:
    certResolver: le
    options:
      name: mytlsoption
      namespace: default
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: matomo
  namespace: default
spec:
  entryPoints:
    - web
  routes:
  - kind: Rule
    match: Host(`matomo.tferdinand.net`)
    services:
    - name: matomo
      port: 80
    middlewares:
      - name: security
      - name: redirectscheme

On retrouve donc ici 2 bloc, un en http et un en https, avec une redirection automatique de l'un vers l'autre.

Ensuite, on peut voir que j'attribue donc le record DNS "matomo.tferdinand.net" à ce dernier.

Enfin, j'applique les mêmes patterns de sécurité que j'avais décrits dans un de mes anciens billets.

Je peux donc maintenant appliquer mes 3 configurations.

Configurer Matomo

Configurer le serveur

En vous connectant sur l'adresse déclarée dans Traefik, vous devriez voir l'assistant d'installation.

Ce dernier va vous guider pas à pas pour :

  • Configurer votre base de données
  • Configurer votre premier utilisateur administrateur
  • Configurer votre premier site

Vous pouvez voir l'ensemble des étapes ci-dessous :

Intégrer le tracker js

Comme vous avez pu le voir, Matomo fonctionne via un tracker JavaScript à intégrer dans votre site Internet.

C'est ce dernier qui va interagir avec le client pour remonter les informations nécessaires.

Pour activer Matomo sur mon site, j'ai donc juste à intégrer ce code. Dans le cas de Ghost, mon blog, j'ai un paramètre prévu pour ça dans "Code injection" > "Site headers".

En vous connectant sur l'interface de Matomo, vous devriez voir arriver du trafic, en prenant en compte que :

  • Les navigateurs envoyant un header DNT (Do Not Track) n'enverront pas d'informations (comme Brave par exemple)
  • Du point de vue RGPD, vous devez obtenir le consentement de l'utilisateur avant de collecter ses informations et déposer un cookie sur son poste. Matomo met à disposition un guide dédié à ce sujet (https://fr.matomo.org/docs/gdpr/).

Pour aller plus loin

Il est aussi possible, comme je l'ai évoqué plus haut, de coupler Matomo à des fichiers de logs.

Il existe en effet un script python livré dans l'image Matomo qui vous permet de faire cette action, en partant de format de logs connus, mais aussi de définir des formats personnalisés.

Pour utiliser ce script, il y a deux prérequis :

  • Python 3.x
  • PHP 7.x

Toutefois, l'image utilisée plus haut est bien basée sur l'image officielle PHP, comme on peut le voir sur le repository GitHub, mais n'a pas Python d'installé.

matomo-org/docker
Official Docker project for Matomo Analytics. Contribute to matomo-org/docker development by creating an account on GitHub.

Si vous souhaitez utiliser ce script, il existe donc deux solutions :

  • Créer une image custom pour ce script avec Python et PHP
  • Repartir de l'image officielle en ajoutant PHP, comme ainsi
FROM matomo:4.1.1-apache
RUN apt update -y 
RUN apt install -y python3
RUN apt-get purge -y --auto-remove && rm -rf /var/lib/apt/lists/*

Le script est ensuite facilement utilisable, comme indiqué dans la documentation officielle :

How to use Log Analytics tool - Analytics Platform - Matomo

Concernant l'authentification, trois solutions sont possibles. Soit vous être dans l'image qui exécute l'application, et le script va pouvoir exploiter directement les fichiers de configurations et donc se connecter directement.

La seconde solution est de créer un jeton qui va vous permettre de vous authentifier et de le passer en paramètre avec le switch --token-auth, ce token peut être créé depuis l'interface de Matomo. Pour ce faire, il suffit de cliquer sur le rouage en haut à droite, puis "Sécurité" et "Créer un nouveau jeton".

Attention, le token ne peut être récupéré qu'une seule fois, donc n'oubliez pas de le stocker dans un environnement sécurisé.

Enfin, la troisième solution, que je déconseille fortement, il est aussi possible de passer directement un login et mot de passe en ligne de commande.

Importer les logs avec un cronjob Kubernetes

Pour ma part, j'ai choisi d'importer les logs en exploitant un cronjob Kubernetes.

En effet, j'ai choisi de ne pas deployer le javascript que je trouve trop intrusif pour vous, mes visiteurs. J'ai donc opté pour l'analyse des logs bruts fournis par Traefik.

J'ai aussi choisi de ne pas utiliser l'image officielle pour ce besoin et j'exploite donc une authentification par jeton.

L'image

L'image docker que j'ai utilisé est assez basique :

FROM python:3.8.7-buster
RUN apt update\
	&& apt install git \
	&& git clone https://github.com/matomo-org/matomo-log-analytics.git \
	&& rm -rf /var/lib/apt/lists/*

Je pars d'une image python 3.8 (car l'importeur n'est compatible qu'avec certaines versions) puis je clone le repository associé.

Vous pouvez récupérer l'image directement créée via dockerhub :

Docker Hub

Le job

J'ai choisi de mettre à disposition mes logs Traefik dans un répertoire que je peux monter quand j'en ai besoin.

Pour en savoir plus sur la configuration des logs, je vous invite à lire mon article à ce sujet :

Extraction des accesslogs Traefik et création de dashboard
Il y a quelques semaines, je vous ai proposé un article expliquant la migrationde Traefik 1 à Traefik 2. Je vous propose cette fois d’aborder un point crucialdans la mise en place d’une application, son monitoring. Cet article explique comment j’ai mis en place mon dashboarding, il n’expliqueen …

J'ai donc ensuite créé un cronjob Kubernetes, qui va me permettre de lire les logs à intervalle régulier pour les intégrer dans Matomo.

apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: matomo-importer
spec:
  schedule: "5 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: matomo-importer
            image: tferdinand/matomo-log-importer:1.0.0
            imagePullPolicy: IfNotPresent
            env:
              - name: PROCESSING_DATE
                value: $(date -d "1 hour ago" "+%d/%b/%Y:%H")
              - name: TOKEN
                value: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
              - name: TRAEFIK_INGRESS_ID
                value: "default-traefik-web-ui-tls-c463f039d72c55f0aca6"
              - name: ID_SITE
                value: 1
            command:
            - /bin/bash
            - -c
            - grep $(TRAEFIK_INGRESS_ID) /tmp/access.log | grep $(PROCESSING_DATE) > /tmp/matomo_$(ID_SITE).log; python matomo-log-analytics/import_logs.py  --url=https://matomo.tferdinand.net --idsite=$(ID_SITE) --debug --token-auth $(TOKEN) --log-format-name=common /tmp/matomo_$(ID_SITE).log
            volumeMounts:
            - name: shared-storage
              mountPath: /tmp/
          restartPolicy: OnFailure
          volumes:
            - name: shared-storage
              hostPath:
                path: /home/kube/share/from_traefik
                type: Directory

Je fais donc tourner cette tâche une fois par heure, pour traiter l'heure précédente.

Le "grep" que l'on voit au début me permet de sortir uniquement les logs pour le service que je veux, l'importateur de log de Matomo ne sachant pas gérer le format des logs traefik de manière plus fine.

ID_SITE me permet d'indiquer l'identifiant du site sur Matomo, par défaut 1.

Ainsi, toutes les heures, je peux suivre la fréquentation du site, le tout sans impacter mes utilisateurs. A noter qu'il est possible de descendre à la minute en ajustant les paramètres.

En conclusion

Matomo est une alternative fiable et plus respectueuse de vos utilisateurs. Il est complètement possible de l'envisager dans un environnement professionnel.

Si vous avez une nécessité de statistiques plus consistante sur votre site, il est en soi mieux pour vos utilisateurs d'utiliser cette solution plutôt que Google Analytics.

De plus, héberger vous-même la solution vous permet aussi de respecter plus facilement les contraintes liées au RGPD. L'ouverture du code vous garantissant aussi qu'aucune exploitation tierce des données n’est faite.

J'insiste aussi sur le fait que tous les sites n'ont pas forcément besoin de tracking complet, pour ma part, une simple exploitation des logs me permet de sortir des données cohérentes.