Umami web analytics

Si ofreces un servicio online, quieres conocer a tu audiencia. Interactuar con ellos, saber qué contenido consideran más interesante. Esto, claro está, no es gratis. Quien diga lo contrario, te miente.

Umami es un proyecto open source que nos ofrece la posibilidad de alojar en nuestros propios servidores una solución de web analytics.

*Nota: Esta no pretende ser una guía exhaustiva sino una presentación de una alternativa. Se muestra eso sí el proceso de instalación y fotos para visualizar su funcionamiento. En la web del proyecto se puede encontrar una demo

Antes de empezar debemos resolver una pregunta.

¿qué ventajas nos ofrece esto respecto a otras alternativas?

  • Tiempo de carga: Al tener un diseño sencillo y un único objetivo a cumplir, no sobrecarga nuestra página.
  • Dependencia: No estamos sujetos a las condiciones de uso de otra entidad, a los cambios que pueda hacer en busca exclusiva de su beneficio.
  • Evitamos la recopilación de datos de nuestros clientes por terceras partes.
  • Adaptabilidad: Si lo necesitas, siempre puedes mejorar el código y adaptarlo a tus necesidades.

Pss: si te interesan las ventajas pero umami no te acaba de convencer, puedes echarle un ojo a ackee

Podemos tener todo esto listo en apenas 30 minutos. Lo sé, lo sé, no es un click y ya. ¿Pero donde esta la gracia en eso?

Requisitos previos:

  • Docker instalado en tu sistema.
  • Conocimientos intermedios/avanzados de gnu/linux
  • Capacidad para configurar un servidor web.

A ver, que me enrollo mucho, porque solo necesitamos tres lineas

git clone https://github.com/mikecao/umami.git
cd umami
sudo docker-compose up -d 

Y ya esta, finito, nada más que hacer… Bueno más o menos.

Con esto ya podríamos tener las analytics de cualquier página que funcione en el mismo servidor, pues efectivamente puede reportar los datos a localhost. Sin embargo ¿qué pasa si el servicio web se encuentra en un servidor distinto al de umami?

Pues nada, simplemente configuramos un dominio o subdomino.

Añadir una web a umami

Página principal
Página principal: Demasiado vacía

Una vez añadida la web, clickamos en el icono de código y nos muestra nuestro código de rastreo ( ¡Muahaha! Big brother cuidado que te haremos la competencia… Bueno, not really)

Obtenemos y copiamos el código de rastreo en nuestra web

Lo ideal es colocar este código en una zona de la web que siempre sea cargada por el usuario. Por ejemplo el footer.

Ya tenemos nuestra web en la lista

Ahh por cierto… ¡cambia la contraseña!

Docker y UFW pueden ser amigos

Si has intentado hacer funcionar docker junto a un firewall, te habrás dado cuenta de que todo falla estrepisomante. Docker juega con iptables y no le gusta que nadie se meta en medio.

Así pues vamos a ver como podemos configurar docker desde el principio hasta arreglar nuestros problemas con el firewall (UFW). Realmente este es el punto importante del post así que puedes ir directo si es lo que buscas

Nuevamente esto lo realizaremos en un servidor debian, pero a parte de este paso concreto en el que añadimos los repositorios concretos y usamos apt para la instalación, los demás pasos son iguales.

En cualquier caso, la guía ofcial es un buen punto de referencia.

Nota*: Este post pretende ser una referencia rápida para asegurarnos de que disponemos de un firewall a la para que podemos usar docker en nuestro servidor.

Instalación

Lo primero es asegurarnos de tener los paquetes necesarios para poder añadir el repositorio

$ sudo apt-get update

$ sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg-agent \
    software-properties-common

Una vez hecho eso añadimos la clave GPG oficial (os recomiendo comprobarlo en la web de docker, añadir repositorios externos puede abrir la puerta al malware en nuestro sistema. No os fies de un cualquiera en internet)

$ curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -
Ejecutando el siguiente comando podremos comprobar  que la clave del repositorio coincide con la indicada en la guía oficial de Docker (9DC8 5822 9FC7 DD38 854A E2D8 8D81 803C 0EBF CD88), y que el servidor es auténtico
$ sudo apt-key fingerprint 0EBFCD88


pub   4096R/0EBFCD88 2017-02-22
      Key fingerprint = 9DC8 5822 9FC7 DD38 854A  E2D8 8D81 803C 0EBF CD88
uid                  Docker Release (CE deb) <docker@docker.com>
sub   4096R/F273FCD8 2017-02-22

Una vez hecho eso añadimos finalmente el repositorio e instalamos docker engine

$ sudo add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/debian \
   $(lsb_release -cs) \
   stable"

$ sudo apt-get update
$ sudo apt-get install docker-ce docker-ce-cli containerd.io

En distros con systemd habilitamos docker para iniciarse con el sistema

$ sudo systemctl enable docker

Convenciendo a Docker y a UFW para llevarse bien.

En nuestro caso como firewall usamos UFW, para asegurarnos de que no existan conflictos y todos los puertos que nadie fuera de nuestra red debe acceder queden efectivamente bloqueados.

Hasta donde yo se hay otras dos soluciones a este problema:

  • Evitar que Docker añada reglas iptables
  • Añadir reglas concretas en el firewall para permitir que una red concreta pueda accederse desde el exterior

Sin embargo, la primera acaba con la función de administración de red de Docker, y la segunda requiere añadir manualmente una regla cada vez que creamos una nueva red.

Sin embargo, con la siguiente aproximación, no encontramos ninguno de estos problemas. Bastaría con añadir lo siguiente al final del archivo /etc/ufw/after.rules (antes de COMMIT) 
# BEGIN UFW AND DOCKER

:ufw-user-forward - [0:0]
:ufw-docker-logging-deny - [0:0]
:DOCKER-USER - [0:0]
-A DOCKER-USER -j ufw-user-forward

-A DOCKER-USER -j RETURN -s 10.0.0.0/8
-A DOCKER-USER -j RETURN -s 172.16.0.0/12
-A DOCKER-USER -j RETURN -s 192.168.0.0/16

-A DOCKER-USER -p udp -m udp --sport 53 --dport 1024:65535 -j RETURN

-A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16
-A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8
-A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12
-A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 192.168.0.0/16
-A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 10.0.0.0/8
-A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 172.16.0.0/12

-A DOCKER-USER -j RETURN

-A ufw-docker-logging-deny -m limit --limit 3/min --limit-burst 10 -j LOG --log-prefix "[UFW DOCKER BLOCK] "
-A ufw-docker-logging-deny -j DROP

Si estás interesado, he encontrado un post (en inglés) que explica esta configuración más en detalle.

Bueno, pues eso, en una burbuja a prueba de paquetes malvados, vivieron felices y comieron perdices…

WordPress-fpm nginx reverse proxy

Aviso para navegantes: ¿muy dificil no puede ser verdad? Bueno, la cuestión es ¿cúantos fallos puedes cometer?

Usando de ejemplo wordpress-fpm vamos a ver cómo configurar un programa fpm corriendo en un contenedor y usando nginx como reverse proxy.

En cuánto a por qué fpm, la idea es consumir menos recursos. Además, cusando nginx como proxy, tener una instancia de apache corriendo solo para docker, no es de mi agrado.

Aclarar que antes de seguir este tutorial debes tener docker instalado y configurado para evitar colisiones con el firewall.

En este caso contaremos con NGINX funcionando en el servidor como reverse proxy para múltiples apps. NGINX NO estará funcionando en ningún container.

(Si vas a levantar múltiples programas/webs/apps que usen mysql, puede ser más conveniente levantar una sola instancia y conectar los demás contenedores a ella)

version: '3.1'

services:

  db-blog:
    image: mariadb
    restart: unless-stopped
    environment:
      MYSQL_DATABASE: blogdb
      MYSQL_USER: MySqlU
      MYSQL_PASSWORD: contraseña
      MYSQL_ROOT_PASSWORD: contraseña
    volumes:
      - ./db-data:/var/lib/mysql


  wordpress:
    image: wordpress:5.4.2-php7.2-fpm
    restart: unless-stopped
    ports:
      - "127.0.0.1:8082:9000"
    depends_on: 
      - db-blog

    environment:
      WORDPRESS_DB_HOST: db-blog
      WORDPRESS_DB_USER: MySqlU
      WORDPRESS_DB_NAME: blogdb
      WORDPRESS_DB_PASSWORD: contraseña
    
    user: www-data

    volumes:
      - /var/www/html/blog/wp-content:/var/www/html/blog/wp-content
    links: 
      - db-blog


volumes:
  wordpress:
  db-data:

Debemos tener en cuenta que si cambiamos el directorio local de /var/www/html a otro en la carpeta de un usuario, por ejemplo, los permisos pueden ser problemáticos. Pues aunque concedamos permisos al directorio concreto nginx necesita acceso a los directorios superiores, para funcionar correctamente.

Actualizar wordpress gracias a docker

Quiero llamar aún más la atención sobre este punto:

 volumes:    
  - /var/www/html/blog/wp-content:/var/www/html/blog/wp-content

es muy importante añadir el wp-content, pues queremos delegar en docker las actualizaciones de wordpress. De esta forma, al recrear el contenedor con la nueva versión, actualizará todo lo necesario. Y simplemente al acceder al panel de administración de la web nos notificará de que es necesario actualizar la base de datos.

En caso contrario, nos encontraremos con problemas al intentar actualizar desde el panel de wordpress, pues este no cuenta con permisos para hacerlo.

Realmente esa es la parte complicada. Lo siguiente sería configurar nginx como reverse proxy.

Un ejemplo del archivo de configuración de nginx sería:

server {

    
        # Add index.php to the list if you are using PHP
        index index.html index.htm index.php;

        server_name example.es;
        root /var/www/html/example.es;

        access_log /var/log/nginx/example-access.log;
        error_log /var/log/nginxexample-error.log;


    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Robots-Tag none;
    add_header X-Download-Options noopen;
    add_header X-Permitted-Cross-Domain-Policies none;
    add_header Referrer-Policy no-referrer;



location / {
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass 127.0.0.1:8082;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }


    listen [::]:443 ssl; 
    listen 443 ssl; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot



    ssl_certificate /etc/letsencrypt/live/example.es/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/example.es/privkey.pem; # managed by Certbot
}


server {
    if ($host = example.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


        listen 80;

        server_name example.com;
    return 404; # managed by Certbot


}