Connecteur PostgreSQL 🐘

Sauvegarde une base PostgreSQL via pg_dump (format custom binaire) et la restaure via pg_restore. Tout est chiffré côté bridge ; ILYGO ne voit jamais le contenu.

  • Image : ilygo/bridge-postgres:<version_pg> (ex : ilygo/bridge-postgres:16)
  • Sous-commande : ilygo-bridge postgres
  • Outils embarqués : pg_dump, pg_restore, psql (client PostgreSQL de la version correspondante)
  • Versions PostgreSQL : 12 → 16 (alignez le tag d'image sur la version majeure de votre serveur)

Variables d'environnement

En plus des variables communes :

Variable Obligatoire Description
PG_CONN_URL oui DSN libpq, ex : postgres://USER:PASSWORD@HOST:5432/DBNAME

Le DSN peut aussi être surchargé par commande via payload.dsn (avancé).

Prérequis côté base

Créez un rôle de backup dédié en lecture seule (principe de moindre privilège) :

CREATE ROLE ilygo_backup WITH LOGIN PASSWORD 'un-mot-de-passe-fort';
GRANT CONNECT ON DATABASE appdb TO ilygo_backup;
GRANT USAGE ON SCHEMA public TO ilygo_backup;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO ilygo_backup;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO ilygo_backup;

Pour la restauration, le rôle utilisé doit pouvoir créer une base (side_by_side) ou écrire dans la base cible (in_place). Vous pouvez utiliser un second DSN plus privilégié au moment de la restauration, ou donner CREATEDB au rôle.

Déploiement (docker-compose)

services:
  app-db:
    image: postgres:16-alpine
    networks: [app-net]            # base invisible depuis Internet
    # ... votre config existante

  ilygo-bridge:
    image: ilygo/bridge-postgres:16
    restart: unless-stopped
    networks:
      app-net:                     # pour atteindre app-db en interne
      default:                     # pour sortir en HTTPS vers ILYGO
    environment:
      ILYGO_ENDPOINT: https://backup.example/api/v1
      ILYGO_API_KEY: ik_xxxxxxxxxxxxxxxxxxxxxxxx
      ILYGO_PASSPHRASE: <généré-dans-votre-navigateur>      # jamais transmis à ILYGO
      ILYGO_RESTORE_SECRET: <généré-dans-votre-navigateur>  # jamais transmis à ILYGO
      ILYGO_LABEL: prod-app-db
      PG_CONN_URL: postgres://ilygo_backup:secret@app-db:5432/appdb
    # Aucun port exposé.

networks:
  app-net:
    internal: true

Mécanisme de backup

Le bridge exécute, en streaming (pas de fichier temporaire) :

pg_dump --format=custom --no-owner --no-privileges --compress=0 "$PG_CONN_URL"
  • Format custom (-Fc) : archive binaire, restaurable sélectivement avec pg_restore.
  • --compress=0 : la compression est faite par le bridge (zstd) avant chiffrement — compresser dans pg_dump serait redondant et le ciphertext est incompressible.
  • La sortie est découpée en chunks de 16 MiB, chiffrés AES-256-GCM, puis poussés.

Mécanisme de restauration

Déclenché depuis la console (Restaurations) avec un RAT. Le bridge :

  1. Vérifie le token (signé localement).
  2. Récupère le manifest chiffré, dérive la clé depuis ILYGO_PASSPHRASE, déchiffre, décompresse.
  3. Applique selon le mode :

side_by_side (recommandé) — crée une base sœur et y restaure :

DROP DATABASE IF EXISTS "appdb_restored";
CREATE DATABASE "appdb_restored";
pg_restore --no-owner --no-privileges --dbname="postgres://.../appdb_restored" <dump>

Vous comparez appdb_restored à la prod, puis vous promouvez manuellement.

in_place (destructif, exige confirm_destructive) :

pg_restore --no-owner --no-privileges --clean --if-exists --dbname="$PG_CONN_URL" <dump>

Exemple validé en démo : 8 customers / 500 orders / 2000 audit_log restaurés à l'identique dans demoapp_restored.

Réseau & sécurité

  • Le bridge n'écoute aucun port ; il sort en HTTPS vers ILYGO_ENDPOINT.
  • Placez app-db sur un réseau internal: true : seul le bridge (multi-réseaux) peut sortir.
  • En prod, ajoutez sur le service bridge : read_only: true, tmpfs: /tmp, cap_drop: [ALL].

Dépannage

Symptôme Cause probable Solution
connection refused au backup mauvais hôte/port dans PG_CONN_URL, ou bridge pas sur app-net Vérifiez l'alias réseau et le DSN
permission denied for table … rôle de backup sans SELECT Appliquez les GRANT ci-dessus
server version mismatch tag d'image ≠ version serveur Utilisez ilygo/bridge-postgres:<majeure> correspondante
restore DROP DATABASE cannot run inside a transaction (corrigé en v1.1) Mettez à jour l'image
decrypt manifest (wrong passphrase?) ILYGO_PASSPHRASE différent de celui du backup Le backup ne peut être restauré qu'avec la passphrase d'origine

Limites & roadmap

  • v1 : dump logique complet (pg_dump). Convient jusqu'à ~100 Go.
  • Roadmap v1.2 : pg_basebackup + WAL streaming continu → PITR (Point-In-Time Recovery), RPO en secondes, économie de stockage.