Version 1.0.0
Appliquer les migrations de la base
Faire évoluer le schéma de la passerelle sans détruire les clés, et la migration dont on ne revient pas.
Cette page décrit comment amener la base d’une passerelle déjà déployée au schéma courant, et la migration qui ne se défait pas.
Deux points à lire avant toute migration
Section intitulée « Deux points à lire avant toute migration »La liste des migrations en attente
Section intitulée « La liste des migrations en attente »La liste des migrations qui vont s’exécuter ne se lit dans aucun document. Elle
est calculée par le script, sur les fichiers présents dans migrations/ et sur
ce que la base a déjà reçu. Une copie tenue à la main de cette liste vieillit ;
un administrateur qui la suit peut croire poser un renommage de contrainte
quand la chaîne contient une migration irréversible.
export DATABASE_URL="postgresql://<utilisateur>:<mot-de-passe>@<hôte>:<port>/<base>"cd gateway-db./migrate.sh --statusChaque ligne [ ] est une migration qui sera exécutée au prochain appel sans
option. Lisez-les toutes avant de lancer quoi que ce soit.
Une migration s’applique sur une sauvegarde qu’on sait restaurer
Section intitulée « Une migration s’applique sur une sauvegarde qu’on sait restaurer »Prendre une sauvegarde ne suffit pas. Ce qui protège est d’avoir déjà restauré une sauvegarde au moins une fois, à froid, sur cette installation. Tant que l’aller-retour n’a pas été fait, vous avez des fichiers dont personne ne sait s’ils remontent.
La procédure (les deux scripts, l’inventaire à comparer avant et après, l’essai de restauration sur une base de côté) est dans Sauvegarder et restaurer la base de la passerelle. Faites-la avant de revenir ici.
Deux points qu’elle détaille et qui pèsent sur la suite :
- la sauvegarde ne porte pas les secrets qui vivent hors de la base : ni les
clés de vos fournisseurs, dont la base ne connaît que le nom de la variable
d’environnement, ni
ADMIN_API_KEY. Une base restaurée ne fait pas une installation qui repart ; - le fichier est un secret : avant la migration
015, il contient les clés de tous vos porteurs en clair.
Les instantanés gérés par votre hébergeur restent utiles, mais ils ne remplacent pas cet essai : ni leur cadence, ni leur rétention, ni leur contenu exact ne sont vérifiables depuis ici.
Les migrations qui existent aujourd’hui
Section intitulée « Les migrations qui existent aujourd’hui »Elles sont listées, dans l’ordre où elles s’appliquent, par la référence des migrations. Cette page est produite depuis les fichiers eux-mêmes : elle ne peut ni oublier une migration qui existe, ni en décrire une qui n’existe pas. Ce guide n’en tient pas de copie.
Le script trie les noms de fichiers et n’exige aucune contiguïté : un trou dans la numérotation n’est pas une migration perdue.
015 est à sens unique
Section intitulée « 015 est à sens unique »C’est la seule migration de la chaîne dont on ne revient pas.
Avant elle, users.api_key contient la clé de chaque client en clair. La
migration calcule le condensat SHA-256 de chaque clé, en dérive le fragment
affichable (sk-lemniscate-…a3f9), puis supprime la colonne.
Après le DROP COLUMN, aucune requête, aucun écran, aucun accès à la base ne
retrouve une clé. La conversion est sans rupture pour les porteurs : personne
n’a à changer de clé. Ce qui disparaît est votre capacité à relire une clé,
donc à réparer une remise ratée autrement qu’en faisant tourner la clé.
Une sauvegarde antérieure devient alors le seul endroit où ces clés existent en clair. Traitez-la comme un secret : chiffrez-la, sortez-la de la machine, détruisez-la une fois la migration confirmée. La garder « au cas où » rouvre la fuite que la migration ferme.
Le tout tient dans une transaction unique, avec l’inscription au registre : la migration passe entièrement ou pas du tout.
Appliquer
Section intitulée « Appliquer »L’ordre compte. Le schéma et le code ne sont pas compatibles en avant : le code
d’avant lit api_key, qui n’existe plus, et le code neuf ne fonctionne pas sur
l’ancien schéma. Il y a donc une fenêtre pendant laquelle l’authentification
est en panne, de la durée du redéploiement. Prévoyez-la.
-
Sauvegarder, et traiter le fichier comme un secret.
Fenêtre de terminal ./sauvegarder.sh --sortie /var/sauvegardes/avant-migration-$(date +%Y%m%d-%H%M%S).sql.gzGardez l’inventaire que le script affiche : c’est ce que vous comparerez si vous devez revenir en arrière. Le détail est dans Sauvegarder et restaurer la base de la passerelle.
-
Relever ce qui va s’exécuter.
Fenêtre de terminal ./migrate.sh --status -
Compter les comptes avant, pour avoir un nombre à retrouver après.
Fenêtre de terminal psql "$DATABASE_URL" -c 'SELECT count(*) FROM users' -
Appliquer.
Fenêtre de terminal ./migrate.shChaque migration s’exécute dans une transaction unique avec son inscription au registre
schema_migrations: une migration qui échoue ne laisse ni schéma à moitié converti, ni registre faux. -
Vérifier, si
015ou021faisait partie du lot appliqué. Les trois nombres doivent être égaux entre eux et égaux à celui de l’étape 3.Fenêtre de terminal psql "$DATABASE_URL" -c "SELECT count(*) AS cles, count(*) FILTER (WHERE key_hash ~ '^[0-9a-f]{64}$') AS hachees, count(*) FILTER (WHERE key_hint <> '') AS avec_fragment FROM access_keys"Aucune colonne de clé ne doit subsister sur les comptes ; zéro ligne est attendue :
Fenêtre de terminal psql "$DATABASE_URL" -c "SELECT column_name FROM information_schema.columns WHERE table_name = 'users' AND column_name LIKE '%key%'" -
Mettre les rôles de service en service, si
023faisait partie du lot appliqué.Cette migration crée deux rôles PostgreSQL,
lemniscate_gatewayetlemniscate_console, avec, pour chacun, les seuls droits dont son service a besoin. Elle les crée sans mot de passe et sans le droit de se connecter (NOLOGIN) : un secret écrit dans le produit serait un secret publié chez tous ceux qui l’installent. Ouvrez-les avec vos propres secrets :Fenêtre de terminal psql "$DATABASE_URL" -c "ALTER ROLE lemniscate_gateway WITH LOGIN PASSWORD 'votre-secret'"psql "$DATABASE_URL" -c "ALTER ROLE lemniscate_console WITH LOGIN PASSWORD 'un-autre'"Puis remplacez, dans la configuration de chaque service, l’identifiant du compte propriétaire par celui de son rôle. La passerelle prend
lemniscate_gateway, la consolelemniscate_console. -
Redéployer la passerelle et la console, dans un ordre indifférent entre elles, mais après la migration.
-
Contrôler. Authentifier une clé connue contre la passerelle, et ouvrir la liste des comptes dans la console : chaque ligne doit porter son fragment.
Le cas d’une base qui n’a pas de registre
Section intitulée « Le cas d’une base qui n’a pas de registre »migrate.sh tient un registre (schema_migrations) qu’il crée au premier
appel. Une base dont le schéma a été monté autrement que par le script n’a pas
ce registre. Le script, ne trouvant rien d’inscrit, voudrait tout rejouer
depuis 000, ce qui détruirait des données vivantes.
Le jalonnement sert à ce cas, une seule fois :
./migrate.sh --baseline 006 # inscrit 000 à 006 sans les exécuter./migrate.sh # applique 007 et les suivantesEnsuite, ./migrate.sh sans option applique les migrations suivantes.
La valeur 006 n’est pas générique. C’est celle d’une base dont l’histoire est
connue : 001 à 006 y ont été passées à la main, sans registre. --baseline
inscrit sans exécuter : un jalon posé trop loin marque comme appliquées des
migrations qui ne l’ont pas été, et ces migrations ne s’exécutent plus. Sur
015, cela laisse des clés en clair dans une base que le reste du système
croit convertie.
Ne posez un jalon que sur une base dont vous savez, par ailleurs, jusqu’où le schéma est monté.
Le second cas est celui d’une base créée par docker-compose, qui a reçu
init.sql. Ce fichier décrit l’état cible complet du schéma, et un test
d’intégration compare colonne par colonne les deux constructions. La chaîne ne
s’y applique pas telle quelle : 000 y recrée des tables qui existent déjà, et
échoue. Le jalon à poser est celui de la dernière migration du dépôt ;
la référence des migrations le donne, et le
tutoriel Découvrir la passerelle le
prescrit aussi.
Ajouter une migration
Section intitulée « Ajouter une migration »Si vous étendez le schéma vous-même, trois gestes, dans cet ordre :
- créer
migrations/0NN-description-courte.sql; le préfixe à trois chiffres fixe l’ordre d’exécution ; - répercuter le même effet dans
init.sql, qui décrit l’état cible complet ; - lancer la suite d’intégration : le test de schéma échoue si les deux descriptions ont divergé.
La migration 010 ne se retouche pas : son en-tête l’interdit, parce qu’elle
s’exécute sur un schéma qui a encore users.api_key. Une clé publiée après
015 se purge dans une migration neuve, sur le condensat.