← Tous les articlesDonnées

Exploiter la réplication logique en toute sécurité sur une plateforme Postgres partagée

La réplication logique est une fonction généreuse à offrir à un client. Elle lui remet le flux de chaque changement d’une base et, dans l’autre sens, permet à un serveur qu’il désigne de nous envoyer des changements. Sur une plateforme partagée, chacune de ces possibilités peut aussi nuire à quelqu’un d’autre si les valeurs par défaut sont mauvaises. Voici ce que nous avons fait sur l’instance Postgres dédiée de Canner, et une chose que nous n’avons apprise que parce que nous avons testé.

La réplication physique est fermée

Un accès autorisé à répliquer ne l’est pas seulement pour la réplication logique. Avec les bons paramètres de connexion, la même permission peut ouvrir une connexion de réplication physique, qui diffuse toute l’instance, chaque base qu’elle contient. Les clients atteignent Postgres par un répartiteur de connexions et, pendant nos essais, nous avons constaté qu’un accès de réplication pouvait ouvrir une copie de toute l’instance à travers PgBouncer. Nous l’avons trouvé en tentant de casser notre propre configuration, ce qui est le but des essais.

Le correctif se trouve dans pg_hba : la réplication physique n’est permise à aucun accès client. Reste la réplication logique, limitée à une base par conception. La documentation le dit sans détour : l’accès ne peut pas ouvrir une copie de tout le serveur.

Un accès de réplication qui ne peut presque rien faire

L’accès de réplication que nous remettons est limité à une seule base. Il peut lire cette base et ouvrir des connexions de réplication, rien de plus. Il ne peut pas écrire, ne peut pas créer d’objets et ne peut pas atteindre vos autres bases. Vous le créez avec :

canner db roles <nom> create feed --replication

Le destinataire se connecte par la même adresse et la même liste d’adresses autorisées que l’accès externe, qui doit être activé pour cette base : le point d’accès est donc TLS uniquement et restreint aux adresses que vous indiquez.

Un plafond de WAL par slot

Un slot de réplication force Postgres à conserver du WAL jusqu’à ce que le destinataire l’ait lu. Si un destinataire disparaît, s’arrête ou est tout simplement lent, ce WAL s’accumule sur notre disque. Un destinataire bloqué finirait par remplir le disque d’un serveur qui appartient à un client, et ce WAL compte aussi dans son stockage groupé.

Chaque slot a donc une limite de WAL qu’il peut retenir : 2 Go par défaut, modifiable avec canner db replication <nom> cap 4096. Au-delà, Postgres invalide le slot au lieu de laisser le disque se remplir. C’est le bon échec, mais il a un coût : le destinataire doit être reconfiguré à partir de zéro.

C’est pourquoi la limite doit être supérieure à votre plus grosse transaction. Le WAL d’une transaction ouverte ne peut pas être libéré avant sa fin : un seul gros traitement par lots peut donc pousser un slot en bonne santé au-delà d’une limite dimensionnée pour le trafic moyen. Chaque slot indique ce qu’il retient et son état de santé, et vous recevez une alerte quand un slot prend du retard, reste inactif une journée ou est invalidé.

Entrant : appliquer les changements d’un autre serveur

L’autre sens est plus risqué, car un serveur distant nous dicte ce qu’il faut écrire. Une base de l’instance dédiée peut suivre un serveur Postgres externe, ce qui permet de migrer sans interruption. Trois décisions comptent.

L’application se fait sans droits de superutilisateur. Les changements sont appliqués par un rôle par base, sans privilège de superutilisateur, avec les droits du propriétaire de vos tables. La raison, ce sont les déclencheurs. Si l’abonnement appartenait à un superutilisateur, chaque déclencheur de vos tables s’exécuterait en superutilisateur pendant l’application des lignes répliquées, et un déclencheur est du code arbitraire écrit par les clients. Avec les droits du propriétaire des tables, un déclencheur s’exécute avec vos droits et rien de plus.

L’adresse distante est vérifiée. Canner se connecte à un hôte que vous désignez, ce qui correspond au schéma classique de la falsification de requête côté serveur (SSRF). L’adresse doit être publique; les adresses privées, de bouclage et de métadonnées infonuagiques sont refusées. La connexion est chiffrée sauf si vous indiquez autrement avec sslmode. Le mot de passe est saisi dans un champ masqué et remis à Postgres, et Canner ne le conserve pas.

Le processus d’application a un search_path vide. Les changements répliqués s’exécutent avec un search_path vide : rien n’est résolu par accident. Le piège est de votre côté, et il surprend : les fonctions de déclencheur doivent utiliser des noms qualifiés par le schéma. public.audit fonctionne; audit n’est pas résolu, et l’abonnement affiche En échec tant que la fonction n’est pas corrigée.

Ce que vous exécutez réellement

canner db subscriptions <nom> add ancien --from-env SRC_URL --publication app_pub

Créez d’abord les tables avec les mêmes noms et colonnes, et une publication sur l’autre serveur. Les lignes existantes sont copiées, puis les changements arrivent en continu. Si un changement ne peut pas être appliqué (table manquante, ligne en conflit), vous recevez une alerte et il réessaie de lui-même une fois la cause corrigée. Jusqu’à cinq abonnements par base.

Limites à connaître

  • La réplication logique fait partie de l’instance dédiée (9 $ CA par mois par projet), et son activation redémarre l’instance.
  • Le serveur source doit être joignable à une adresse publique. Une source sur un réseau privé ne fonctionnera pas.
  • Les séquences et les changements de schéma ne sont pas transportés par la réplication logique elle-même; le guide de migration explique quoi faire à la bascule.
  • Canner n’a ni répliques en lecture ni basculement automatique. La réplication sortante sert vos propres destinataires, pas un serveur de relève géré.

La référence complète se trouve dans la documentation Postgres, et le guide de migration décrit une bascule. Si vous pensez que nous avons oublié un scénario d’attaque, nous voulons le savoir.

À propos de l’auteur

Colin Shand est le fondateur de Canner, une plateforme de déploiement canadienne exploitée depuis le Québec. Il écrit sur l’infrastructure souveraine, l’écosystème des startups canadiennes et la création en toute indépendance.

Essayez Canner.

Déposez un projet, obtenez une URL en direct sur une infrastructure canadienne en environ 30 secondes. Forfait gratuit disponible.