Aller au contenu

Exposition et sécurité

Principe

Un seul service publie des ports vers Internet : Traefik (80 et 443). Tout le reste écoute sur 127.0.0.1 et n'est joignable que par tunnel SSH.

flowchart LR
    I[Internet] -->|443| T[Traefik]
    T -->|"/"| W[nginx]
    T -->|"/public/ /app/ /api/public/"| M[Metabase]
    S[Poste administrateur] -.->|tunnel SSH| L["127.0.0.1<br/>:3000 :8080 :5432"]
    L -.-> M
    L -.-> A[Airflow]
    L -.-> P[(PostgreSQL)]

Ce qui est public, ce qui ne l'est pas

Chemin Réponse Servi par
/ 200 nginx — la vitrine
/docs/ 200 nginx — cette documentation
/public/dashboard/… 200 Metabase — tableaux de bord partagés
/api/public/… 200 Metabase — données des tableaux de bord
/app/… 200 Metabase — ressources statiques nécessaires à l'affichage
/auth/login 404
/admin 404
/api/card, /api/setting, /api/dashboard/… 404

Le routage Metabase est une liste blanche de chemins, pas un domaine dédié. La console d'administration est servie sur /, chemin qui appartient à nginx : elle est donc structurellement injoignable depuis Internet.

Vérifier après toute modification des étiquettes

Les règles Traefik sont portées par les étiquettes Docker du conteneur Metabase. Modifier le docker-compose.yml ne suffit pas : il faut recréer le conteneur (docker compose up -d --force-recreate metabase), sinon il continue de porter les anciennes étiquettes. Cette erreur a été commise pendant la mise en place et a produit des 404 déroutants.

TLS

Certificat Let's Encrypt obtenu par défi HTTP-01, stocké dans data/letsencrypt/acme.json. Renouvellement automatique 30 jours avant échéance, sans intervention.

# Vérifier le certificat servi
echo | openssl s_client -connect demo.metrika.fr:443 -servername demo.metrika.fr 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates

Tester avant de consommer le quota

Let's Encrypt limite les échecs de validation à 5 par heure et par nom. Pour tout test, basculer ACME_CASERVER sur l'environnement de test dans .env, puis supprimer data/letsencrypt/acme.json avant de repasser en production — un compte de test n'est pas valide en production.

Le compromis assumé sur les tableaux de bord

Les tableaux de bord sont exposés via les liens publics Metabase. Ce choix a des conséquences qu'il faut connaître :

Lien public (retenu) Embarquement signé (écarté)
Backend requis non oui
Durée de validité permanente jeton expirable
Filtrage par visiteur impossible possible
Qui peut voir quiconque a l'URL qui présente un jeton valide

Acceptable ici : les données commerciales sont fictives et l'open data est public par nature. Ce ne le serait pas pour des données client réelles — il faudrait alors l'embarquement signé, donc un composant serveur capable de signer un JWT avec MB_EMBEDDING_SECRET_KEY (ce calcul ne peut pas se faire dans le navigateur, la clé fuiterait).

Les liens se révoquent à tout moment depuis Metabase.

Comptes et secrets

Tous les mots de passe sont générés aléatoirement à l'installation et stockés dans .env — permissions 600, exclu de Git.

Rôle PostgreSQL Portée
warehouse propriétaire de l'entrepôt, lecture et écriture
metabase_reader lecture seule, limité aux schémas de l'entrepôt
metabase, airflow leurs bases de métadonnées respectives
metrika superutilisateur

Metabase se connecte à l'entrepôt avec metabase_reader : il ne peut rien modifier. C'est le principe du moindre privilège appliqué à l'outil qui a le plus de chances d'être exposé.

Messagerie

SMTP OVH (ssl0.ovh.net:587, STARTTLS) avec une boîte dédiée notification@metrika.fr. Le mot de passe est chiffré par Metabase dans sa base de métadonnées via MB_ENCRYPTION_SECRET_KEY.

Authentification du courrier en place

Le domaine metrika.fr publie les trois mécanismes attendus :

Mécanisme Valeur
SPF v=spf1 include:mx.ovh.com ~all — un seul enregistrement
DKIM actif, sélecteurs ovhmo-selector-1 et -2
DMARC v=DMARC1; p=none; rua=mailto:notification@metrika.fr; sp=none; aspf=r

Un point de vigilance permanent : il ne doit jamais y avoir qu'un seul enregistrement SPF. La RFC 7208 impose un résultat permerror en présence de plusieurs, ce qui fait échouer l'évaluation entièrement — pire que l'absence d'enregistrement. L'éditeur de zone OVH range les SPF sous un type distinct de TXT, ce qui les rend invisibles si l'on filtre sur TXT.

~all (softfail) est le bon qualificateur au démarrage. Une fois que les rapports DMARC confirment que tout le courrier légitime passe — comptez deux à quatre semaines — on peut durcir en -all puis passer DMARC en p=quarantine, et enfin p=reject.

Vérifier

host -t TXT metrika.fr dns200.anycast.me | grep -c "v=spf1"   # doit valoir 1
host -t TXT _dmarc.metrika.fr
host -t TXT ovhmo-selector-1._domainkey.metrika.fr