DEV Community

Dinh Doan Van Bien
Dinh Doan Van Bien

Posted on

Partie 7 — Sécurité et test de charge

Partie 7 sur 7 — Supabase en auto-hébergement : retour d'expérience

Version française de Part 7 — Security and the load test.

Nous avons un cluster à deux projets qui fonctionne. Deux questions se posent maintenant : est-il réellement sécurisé, et qu'est-ce qu'il faut pour le faire craquer ?


Les couches de sécurité

La sécurité ici repose sur la défense en profondeur. Plusieurs couches, chacune ajoutant de la résistance pour un attaquant. Aucune couche n'est suffisante à elle seule.

ufw et fail2ban

La couche externe. Seuls les ports 22, 80 et 443 sont ouverts. fail2ban bannit les adresses IP après cinq tentatives SSH échouées. Cela arrête les scanners automatisés et les attaques par force brute.

Kong : authentification par clé et limitation de débit

Chaque requête à l'API doit inclure un en-tête apikey valide. Sans lui, Kong renvoie un 401 avant que la requête n'atteigne aucun service backend. GoTrue, PostgREST, Realtime, Storage : aucun d'eux ne voit le trafic non authentifié.

Limitation de débit : 30 requêtes par minute par consommateur par défaut. Cela limite les dégâts des tentatives de credential stuffing et protège GoTrue contre son utilisation comme plateforme d'inscription en masse.

Kong est configuré via un fichier YAML (kong.yml) qui réside sur le serveur et n'est jamais commité dans git. Les sections importantes :

plugins:
  - name: key-auth
    config:
      key_names: ["apikey"]

  - name: rate-limiting
    config:
      minute: 30
      policy: local
Enter fullscreen mode Exit fullscreen mode

Studio protégé par une authentification basique

Le tableau de bord Studio donne un accès administrateur complet à votre base de données. Il ne doit pas être accessible publiquement. Nous avons configuré cela dans la partie 4 : le middleware d'authentification basique de Traefik protège la route Studio, et le hash du mot de passe est intégré dans les labels du service. Consultez la partie 4 pour la commande htpasswd -nB et la règle d'échappement $$.

Falco : détection d'intrusion à l'exécution

ufw et Kong traitent les menaces externes. Falco surveille ce qui se passe à l'intérieur des conteneurs en cours d'exécution.

Falco est un outil de sécurité basé sur eBPF qui surveille les événements du noyau Linux : accès aux fichiers, exécution de processus, connexions réseau, changements de privilèges. Il tourne sur l'hôte, donc un conteneur compromis ne peut pas le désactiver. Les événements qui correspondent à des règles déclenchent des alertes.

Installez-le :

curl -fsSL https://falco.org/repo/falcosecurity-packages.asc \
  | gpg --dearmor -o /usr/share/keyrings/falco-archive-keyring.gpg

echo "deb [signed-by=/usr/share/keyrings/falco-archive-keyring.gpg] \
  https://download.falco.org/packages/deb stable main" \
  | tee /etc/apt/sources.list.d/falcosecurity.list

apt update && apt install falco -y
systemctl enable falco-modern-bpf
systemctl start falco-modern-bpf
Enter fullscreen mode Exit fullscreen mode

Les règles personnalisées se placent dans /etc/falco/rules.d/. Pour notre cluster, des règles utiles incluent les alertes sur les connexions sortantes inattendues depuis les conteneurs et sur les commandes psql directes contenant des instructions destructives.

Le piège des alertes. La directive program_output redirige les alertes vers un script. Vous pouvez vérifier qu'elle fonctionne en regardant le journal Falco pendant que vous déclenchez un événement : journalctl -u falco-modern-bpf -f. Si vous voyez des événements là mais que votre script de traitement n'est jamais appelé, program_output est cassé dans votre version. La solution de contournement fiable consiste à créer un service systemd séparé qui suit le journal directement :

# /etc/systemd/system/falco-alerter.service
[Unit]
Description=Falco alert handler
After=falco-modern-bpf.service

[Service]
ExecStart=/bin/bash -c 'journalctl -f -u falco-modern-bpf -o cat | /root/supabase-vps-cluster/scripts/falco-alert.sh'
Restart=always
Enter fullscreen mode Exit fullscreen mode

Le gestionnaire d'alertes enregistre les événements dans /var/log/falco-alerts.log avec un délai de cinq minutes par règle.

Bruit attendu. Les vérifications d'état nginx de Kong s'exécutent toutes les dix secondes. Chacune lance un sous-processus shell et lit /etc/passwd. Falco le signale comme « Shell spawned in container » et « Sensitive file read in container ». C'est un comportement normal, pas une attaque. Le mécanisme de délai maintient le journal lisible. Après vingt-quatre heures d'observation, vous pouvez affiner les règles pour mettre en liste blanche le processus Kong spécifique.


L'architecture de sécurité

Internet
    |
  ufw (ports 22/80/443 only)
    |
  Traefik (TLS, security headers)
    |
    +-- Studio (Traefik basic auth)
    +-- Kong (key-auth + rate limiting)
              |
              +-- internal services (not publicly reachable)

Host layer:
  SSH key-only auth, fail2ban
  Falco eBPF watching all container syscalls
  Vault on localhost only, UI disabled
  No published Postgres port
Enter fullscreen mode Exit fullscreen mode

Le test de charge

Je voulais une réponse concrète à la question : quelle est la limite réelle de ce serveur ?

J'ai utilisé Grafana Cloud k6 pour les tests. Avant d'en lancer un, il vous faut :

  • k6 installé en local (brew install k6 sur macOS, ou téléchargement depuis k6.io pour les autres plateformes)
  • Un compte Grafana Cloud (gratuit sur grafana.com/products/cloud). L'offre gratuite permet jusqu'à 50 utilisateurs virtuels simultanés et des tests d'une durée d'environ dix minutes
  • La clé anon et la clé service role de votre projet (depuis Vault, ou depuis le fichier .env si vous n'avez pas encore configuré Vault)

Trois tests.

Test 1 : ré-authentification à chaque requête

Chaque utilisateur virtuel se connecte à chaque itération. Le test monte progressivement à 50 utilisateurs virtuels simultanés.

Le serveur a craqué aux alentours de 30 à 50 utilisateurs virtuels.

À 50 utilisateurs virtuels, le CPU de la base de données a atteint 100 % et y est resté. GoTrue a commencé à renvoyer des timeouts 504. Le problème est bcrypt. Le hachage de mot de passe est intentionnellement coûteux en CPU : chaque connexion nécessite une vérification bcrypt dans GoTrue et un aller-retour vers la base de données. Avec 50 utilisateurs qui se ré-authentifient toutes les quelques secondes, le serveur à 2 vCPU était saturé par le seul travail cryptographique.

Service Au repos Pendant le test
GoTrue 0 % CPU 51 % CPU
PostgreSQL 0 % CPU 100 % CPU

Cela semble alarmant. Mais aucune application réelle ne fonctionne ainsi. Les jetons JWT sont valables une heure. Vous vous authentifiez une fois, utilisez le jeton, le rafraîchissez à expiration. Vous ne vous ré-authentifiez pas à chaque appel API.

Test 2 : JWT en cache, CRUD pur

Chaque utilisateur virtuel se connecte une seule fois pendant la phase de configuration et met le jeton en cache pour toute la durée du test. Ensuite, il exécute en boucle des opérations d'insertion, lecture, suppression sans ré-authentification.

Aucun point de rupture à 50 utilisateurs virtuels. L'offre gratuite de Grafana Cloud a atteint sa limite de durée de test (environ cinq minutes) avant que le serveur ne montre le moindre signe de stress.

Service Au repos Pendant le test
GoTrue 0 % CPU 0,02 % CPU
PostgreSQL 0 % CPU 9 % CPU
PostgREST 0 % CPU 13 % CPU

Le CPU de la base de données est passé de 100 % à 9 %. Le seul changement était la mise en cache du JWT. PostgREST est désormais le service avec le CPU le plus élevé et deviendrait éventuellement le goulot d'étranglement à des nombres d'utilisateurs virtuels plus élevés, mais nous n'avons pas atteint ce plafond.

Test 3 : sessions réalistes

Trois profils d'utilisateurs en parallèle, avec des temps de réflexion aléatoires entre les actions :

  • 70 % d'utilisateurs occasionnels (temps de réflexion de 10 à 30 secondes, principalement des lectures)
  • 20 % d'utilisateurs actifs (temps de réflexion de 5 à 15 secondes, usage mixte)
  • 10 % d'utilisateurs intensifs (temps de réflexion de 2 à 8 secondes, davantage d'écritures)

Chaque utilisateur se connecte une fois par session. Des périodes d'inactivité aléatoires simulent le changement d'onglet ou une pause.

Le test s'est déroulé jusqu'à son terme complet : 10 minutes 30 secondes, zéro erreur.

Service Au repos Pic pendant le test
GoTrue 0 % CPU 0,02 % CPU
PostgreSQL 0 % CPU 0,67 % CPU
PostgREST 0 % CPU 1,19 % CPU

Le serveur était essentiellement inactif.

Un seuil a tout de même été franchi : la latence de lecture au 95e percentile a dépassé 300 ms. Ce n'était pas le serveur. Le test s'exécutait depuis la région Ohio de Grafana Cloud vers notre serveur en Allemagne. Le temps d'aller-retour de base est de 100 à 130 ms. Le cluster était en bonne santé tout au long du test : la latence venait du réseau, pas de l'application.

Ce que ces chiffres signifient

Avec 50 utilisateurs simultanés et des temps de réflexion réalistes, il y a de 3 à 8 requêtes de base de données qui s'exécutent réellement à tout moment. Les utilisateurs ne cliquent pas en continu : ils lisent quelque chose, rédigent une réponse, réfléchissent à ce qu'ils vont faire ensuite. Le temps de réflexion change complètement le calcul.

Le CX22 convient parfaitement à un projet personnel avec de vrais utilisateurs. Le seul scénario qui le sature est un test de ré-authentification en boucle continue, ce qu'aucune application réelle ne fait.


Ce que le service géré apporte

Après avoir traversé tout cela, j'ai une vision bien plus claire de ce que Supabase vous offre sur son offre gratuite.

J'ai passé plusieurs soirées sur la configuration, le paramétrage et le débogage. J'ai eu droit à l'incident Vault. J'ai débogué des problèmes de routage Traefik, des crashs de Realtime, des portées de variables d'environnement incorrectes et le comportement des vérifications d'état dans Docker Swarm. J'ai mis en place un système de monitoring pour savoir ce que fait le serveur. Je suis responsable des mises à jour, des sauvegardes et des incidents.

Supabase fait tout cela pour deux projets gratuits. L'infrastructure qui sous-tend un seul projet gratuit est plus complexe que tout ce qui est présenté dans cette série. L'équipe maintient GoTrue, PostgREST, Realtime et le reste à jour et en fonctionnement, en continu, à grande échelle.

Le plan Pro, avec sa base de données à récupération jusqu'à un instant précis, ses sauvegardes automatisées, son pooling de connexions via PgBouncer et ses garanties de disponibilité, est un prix juste pour ce qu'il élimine de votre quotidien. Je le comprends maintenant, parce que j'ai vu concrètement ce qu'il élimine.

Auto-hébergez si vous voulez apprendre. Utilisez le service géré si vous voulez construire.


La série complète

  1. Pourquoi auto-héberger
  2. Le serveur
  3. Traefik et SSL
  4. La première instance Supabase
  5. Vault
  6. Deux instances
  7. Sécurité et test de charge, vous êtes ici

Top comments (0)