SIG & Carto Guide

PostGIS hébergé en production : déployer, sécuriser, alimenter

Du poste local au serveur distant : ce qui change vraiment quand votre base géospatiale s'ouvre sur le réseau

Mattieu Pottier 12 min de lecture
Schéma d'un serveur PostGIS hébergé sécurisé, accédé à distance depuis QGIS via tunnel SSH

Vous connaissez la scène : reseau_v2_FINAL_vrai.shp sur un bureau, une version concurrente chez le collègue, et une journée de terrain écrasée parce que deux personnes ont enregistré en même temps.

Tant que votre patrimoine géographique — chaque vanne, chaque canalisation, chaque parcelle — vit dans des fichiers qui circulent, ce drame est inévitable.

La sortie, c'est une base PostGIS partagée et hébergée — l'option qui s'impose dès que la donnée doit être écrite à plusieurs. Mais installer PostGIS sur votre poste et l'exposer sur un serveur accessible depuis Internet sont deux métiers différents — et c'est là que la majorité des bases partent en production avec le port 5432 grand ouvert, un seul compte superuser pour toute l'équipe, et aucune couche de chiffrement.
Un PostGIS local et un PostGIS hébergé sont deux objets différents : la frontière entre les deux tient dans trois couches de configuration.

Cet article les parcourt dans l'ordre — réseau, connexion, droits — puis montre comment votre équipe travaille en direct dessus depuis QGIS, et comment alimenter la base à distance sans jamais l'exposer.

Côté métier — ce que ça change

Une base PostGIS hébergée, c'est une seule source de vérité géographique pour toute l'entreprise, au lieu d'un éparpillement de fichiers .shp qui circulent par mail.

  • Vos équipes éditent la même donnée à jour, en même temps, depuis QGIS — fini les versions qui se contredisent.
  • Si le terrain a du réseau, la saisie sur site arrive en base à la seconde, visible au bureau immédiatement — sans export ni consolidation au retour.
  • Chaque collaborateur a exactement les droits qu'il lui faut : qui consulte, qui édite, qui administre. Le tout sans licence propriétaire.
  • Le risque n'est pas technique mais opérationnel : une base mal exposée est une fuite de données métier. Les trois couches ci-dessous existent pour fermer ce risque.

Local ou hébergé : pourquoi ce n'est pas la même base

Dès que la base part sur un serveur joignable depuis Internet, le décor change : un PostgreSQL exposé sans précaution est repéré par les scanners automatiques en quelques minutes, et chaque compte faible devient une porte d'entrée vers vos données métier.

L'erreur classique n'est pas de mal configurer une couche — c'est d'oublier qu'il y en a trois, indépendantes, à traiter chacune pour elle-même. Une base peut être chiffrée et quand même ouverte au monde entier ; elle peut filtrer les IP et laisser un compte tout-puissant sans mot de passe. La sécurité d'un PostGIS hébergé est la conjonction des trois, jamais une seule.

surface d'attaque L'ensemble des points par lesquels un attaquant peut tenter d'entrer. Un PostGIS en local n'en a quasiment aucune ; le même PostGIS exposé sur Internet en ouvre une nouvelle à chaque port, compte et service accessible. Tout l'enjeu de la mise en production est de la réduire au strict nécessaire.
🌐

Réseau

Qui peut atteindre le serveur ? Firewall, tunnel SSH ou VPN — jamais le port 5432 nu sur Internet.

🔒

Connexion

Comment la session est-elle chiffrée et authentifiée ? SSL/TLS côté serveur, méthode scram-sha-256 dans pg_hba.conf.

🗝️

Droits

Que peut faire chaque compte une fois connecté ? Rôles PostgreSQL, du lecteur seul à l'administrateur.

C'est le plan de défense, et aussi le plan de cet article. On les traite dans l'ordre où une connexion les rencontre : d'abord atteindre le serveur, puis ouvrir une session chiffrée et authentifiée, enfin agir sur la donnée selon ses droits.

Déployer PostGIS : Docker ou installation native

Avant de sécuriser quoi que ce soit, il faut un PostGIS qui tourne. Deux voies, toutes deux défendables — le bon choix dépend de votre rapport à l'exploitation, pas d'une supériorité technique abstraite.

Installation native (paquets système)

  • Intégration directe au système : systemd, logs, sauvegardes via les outils de l'OS
  • Mises à jour suivies par le gestionnaire de paquets de la distribution
  • Moins de couches : un dépannage plus direct quand quelque chose casse
  • Reproduire l'environnement ailleurs demande de rejouer l'installation à la main

Conteneur Docker

  • Environnement reproductible à l'identique : la même image partout
  • Version de PostGIS figée et explicite dans le compose, indépendante de l'OS hôte
  • Isolation du reste du serveur, montée de version par simple changement de tag
  • Une couche de plus à comprendre : volumes, réseau Docker, persistance des données

La vraie question n'est pas « lequel est le meilleur » mais « qui exploite la base, et avec quelles habitudes ». Une équipe déjà à l'aise avec Docker gagnera en reproductibilité ; un serveur géré par un administrateur système classique sera plus à l'aise en natif. Le point non négociable est ailleurs : le volume de données doit être persistant et sauvegardé avant toute ouverture au réseau — un conteneur dont le volume n'est pas monté à l'extérieur perd toute sa donnée à la première recréation.

Socle minimal avant d'exposer la base

  • PostgreSQL et PostGIS dans des versions encore supportées
  • Extension PostGIS activée : CREATE EXTENSION postgis;
  • Volume de données persistant (hors conteneur si Docker)
  • Sauvegarde automatique planifiée (pg_dump quotidien minimum) et testée en restauration
  • Accès réseau encore fermé à ce stade — on ouvre après avoir sécurisé

Sécuriser l'accès : réseau, chiffrement, authentification

C'est le cœur du sujet. Trois mécanismes distincts, dans l'ordre qu'une connexion entrante traverse.

Réseau : ne jamais exposer le 5432 nu

Le réflexe à proscrire absolument, c'est d'ouvrir le port 5432 directement sur Internet « le temps de tester ». Un PostgreSQL accessible publiquement est scanné en continu, et la seule barrière devient alors la robustesse de vos mots de passe — pari perdant.

Recommandé

Accès par tunnel SSH

  • Port 5432 fermé au monde, ouvert sur localhost seulement
  • Le client se connecte à 127.0.0.1 sur un port local redirigé
  • Authentification SSH (clé) + authentification PostgreSQL : deux barrières
  • Aucune surface PostgreSQL exposée publiquement
VS

À éviter

Port 5432 ouvert sur Internet

  • Scanné en quelques minutes par les bots
  • Une seule barrière : le mot de passe PostgreSQL
  • Brute force permanent dans les logs
  • Surface d'attaque maximale pour zéro bénéfice

Le tunnel SSH est la voie par défaut pour une petite équipe : aucune infrastructure à monter, juste une commande. Côté poste de travail, on ouvre un tunnel qui redirige un port local vers le 5432 du serveur :

Bash
# Rediriger le port local 5433 vers le 5432 du serveur, via SSH
ssh -N -L 5433:localhost:5432 mattp@mon-serveur.example.com

# La base est désormais joignable en local sur 127.0.0.1:5433
# QGIS, psql, ogr2ogr ciblent 127.0.0.1:5433 — jamais l'IP publique du serveur

Pour une équipe plus outillée, un VPN (WireGuard par exemple) place tous les postes sur un réseau privé d'où la base est joignable — plus confortable au quotidien, mais c'est une infrastructure à administrer. Le firewall du serveur, lui, reste configuré pour n'accepter le 5432 que depuis localhost ou le sous-réseau VPN, jamais depuis 0.0.0.0/0.

Chiffrer la connexion : SSL/TLS côté PostgreSQL

Le tunnel SSH chiffre déjà le trafic ; le VPN aussi. Mais dès qu'une connexion arrive en TCP direct (cas VPN, ou client sans tunnel), il faut que PostgreSQL impose lui-même le chiffrement. Cela s'active dans postgresql.conf :

INI
# postgresql.conf — activer SSL/TLS
ssl = on
ssl_cert_file = '/etc/postgresql/ssl/server.crt'
ssl_key_file  = '/etc/postgresql/ssl/server.key'

Le certificat peut être auto-signé pour un usage interne, ou émis par une autorité reconnue si des clients tiers se connectent. Côté client, on force la vérification du chiffrement avec le paramètre sslmode — on y revient dans la configuration QGIS plus bas.

pg_hba.conf : qui se connecte, d'où, comment

C'est le fichier qui décide, pour chaque tentative de connexion, quelle règle d'authentification s'applique. Un point essentiel à comprendre : pg_hba.conf ne fait pas écouter le serveur sur le réseau et n'ouvre aucun firewall — il décide seulement de la méthode d'authentification une fois que le client a atteint PostgreSQL. C'est la dernière barrière, pas la première.

Chaque ligne suit le même ordre de champs : TYPE DATABASE USER ADDRESS METHOD. Voici une configuration commentée, lisible ligne à ligne :

INI
# pg_hba.conf
# TYPE      DATABASE   USER          ADDRESS           METHOD

# Connexions locales sur le serveur (socket Unix) — admin système
local       all        postgres                        peer

# Connexions via le tunnel SSH (le client arrive en 127.0.0.1)
host        gis_db     +sig_lecteur  127.0.0.1/32      scram-sha-256
host        gis_db     +sig_editeur  127.0.0.1/32      scram-sha-256

# Connexions TCP chiffrées depuis le sous-réseau VPN (SSL obligatoire)
hostssl     gis_db     +sig_editeur  10.8.0.0/24       scram-sha-256

# Tout le reste est refusé explicitement
host        all        all           0.0.0.0/0         reject

Trois principes structurent ce fichier. D'abord, scram-sha-256 est la méthode d'authentification par mot de passe à retenirmd5 est obsolète, et trust (aucun mot de passe) ne doit jamais apparaître sur une ligne réseau.

Ensuite, une IP spécifique se place toujours avant un sous-réseau plus large, car PostgreSQL applique la première ligne qui correspond et s'arrête. Enfin, hostssl n'a de sens que si SSL est activé côté serveur — sinon la connexion est rejetée.

L'erreur qui ouvre tout

La ligne host all all 0.0.0.0/0 trust autorise n'importe qui, depuis n'importe où, sans mot de passe, sur toutes les bases. On la croise dans des tutoriels d'installation rapide ou des pg_hba.conf jamais nettoyés. Sur un serveur exposé, c'est l'équivalent d'une porte d'entrée retirée de ses gonds. Vérifiez qu'elle n'existe nulle part dans votre fichier.

Les rôles PostgreSQL : qui a le droit de faire quoi

La couche réseau et le chiffrement décident qui peut ouvrir une session. Les rôles décident ce qu'on peut faire une fois la session ouverte. C'est ici que se joue la différence entre un consultant qui lit la donnée, un technicien qui l'édite, et l'administrateur qui gère la structure.

Une architecture de rôles pour un projet SIG

Le bon modèle s'appuie sur des rôles de groupe (sans droit de connexion, ils ne servent qu'à porter des permissions) auxquels on rattache des comptes nominatifs (un par personne, avec droit de connexion). On ne connecte jamais une application ou un utilisateur avec le compte postgres superuser.

Dans PostgreSQL, CREATE ROLE crée par défaut un rôle sans connexion — idéal pour un groupe — tandis que CREATE USER est un raccourci qui ajoute le droit LOGIN. Voici le script complet, réutilisable :

SQL
-- 1. Rôles de groupe (NOLOGIN par défaut) : ils portent les permissions
CREATE ROLE sig_lecteur;
CREATE ROLE sig_editeur;
CREATE ROLE sig_admin;

-- 2. Révoquer l'accès public par défaut sur le schéma
REVOKE ALL ON SCHEMA public FROM PUBLIC;

-- 3. Droits du lecteur : voir le schéma et lire les tables
GRANT USAGE ON SCHEMA public TO sig_lecteur;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO sig_lecteur;

-- 4. L'éditeur hérite de la lecture et ajoute l'écriture
GRANT sig_lecteur TO sig_editeur;
GRANT INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO sig_editeur;
GRANT USAGE ON ALL SEQUENCES IN SCHEMA public TO sig_editeur;

-- 5. L'admin gère la structure (création de tables, etc.)
GRANT sig_editeur TO sig_admin;
GRANT CREATE ON SCHEMA public TO sig_admin;

-- 6. Comptes nominatifs (LOGIN) rattachés aux groupes
CREATE USER marie LOGIN PASSWORD 'à_changer_par_un_vrai_secret';
CREATE USER paul  LOGIN PASSWORD 'à_changer_par_un_vrai_secret';
GRANT sig_lecteur TO marie;   -- Marie consulte
GRANT sig_editeur TO paul;    -- Paul édite

Le piège des droits sur les tables futures

Le script ci-dessus donne les droits sur les tables qui existent au moment du GRANT. Toute table créée ensuite échappe à ces permissions — un lecteur ne verra pas les nouvelles couches. La parade est ALTER DEFAULT PRIVILEGES, mais avec une subtilité qui piège beaucoup de monde :

SQL
-- Les futures tables créées PAR sig_admin seront lisibles par sig_lecteur
ALTER DEFAULT PRIVILEGES FOR ROLE sig_admin IN SCHEMA public
  GRANT SELECT ON TABLES TO sig_lecteur;

ALTER DEFAULT PRIVILEGES FOR ROLE sig_admin IN SCHEMA public
  GRANT INSERT, UPDATE, DELETE ON TABLES TO sig_editeur;
La subtilité ALTER DEFAULT PRIVILEGES

Les privilèges par défaut ne s'appliquent qu'aux objets créés par le rôle nommé dans FOR ROLE, pas par n'importe quel membre du groupe. Si Marie et Paul créent eux-mêmes des tables, ces tables n'hériteront pas des règles définies pour sig_admin. Le moyen sûr de tenir cette promesse : centraliser la création de tables sur un seul rôle (ici sig_admin) et passer par lui pour toute évolution de structure.

Cette matrice résume qui peut quoi :

Matrice des droits par rôle SIG
Actionsig_lecteursig_editeursig_admin
Lire les tables (SELECT)✅ Oui✅ Oui✅ Oui
Modifier les données (INSERT/UPDATE/DELETE)❌ Non✅ Oui✅ Oui
Créer ou modifier des tables (DDL)❌ Non❌ Non✅ Oui

QGIS connecté en direct à la base hébergée

C'est l'usage qui concerne le plus de monde, et la vraie raison d'héberger PostGIS : que chaque géomaticien édite la donnée distante depuis son poste, sans passer par des exports. QGIS lit et écrit nativement dans PostGIS — encore faut-il connecter proprement et comprendre ce que QGIS fait des droits que vous avez posés.

Configurer la connexion : pg_service.conf et gestionnaire d'authentification

Plutôt que de saisir hôte, port et base dans chaque projet QGIS — où ils finiraient stockés en clair dans le fichier .qgs — on centralise les paramètres dans un fichier service pg_service.conf. QGIS, comme tout client libpq, sait l'utiliser. Sur Linux et macOS, il vit dans ~/.pg_service.conf ; sur Windows, dans %APPDATA%\postgresql\.pg_service.conf.

INI
# ~/.pg_service.conf  (Windows : %APPDATA%\postgresql\.pg_service.conf)
[gis-prod]
host=127.0.0.1
port=5433
dbname=gis_db
sslmode=require

On note ici host=127.0.0.1 et port=5433 : c'est l'extrémité locale du tunnel SSH ouvert plus haut. Dans QGIS, on crée une nouvelle connexion PostGIS et on renseigne simplement le nom du service (gis-prod) — les autres champs sont tirés du fichier.

Reste le mot de passe. Le pg_service.conf peut contenir une ligne password=, mais elle y serait en clair : à éviter. La bonne pratique est de laisser QGIS demander les identifiants et les stocker dans son gestionnaire d'authentification, une base qgis-auth.db chiffrée par un mot de passe maître. Distinction importante : ce gestionnaire transporte et protège les identifiants sur le poste — il ne définit aucun droit. Les droits, eux, vivent uniquement dans les rôles PostgreSQL.

pg_service.conf Fichier de configuration libpq qui associe un nom de service à un ensemble de paramètres de connexion (hôte, port, base, mode SSL). Le client ne référence plus que le nom du service, jamais les détails — pratique et plus sûr.

Travailler à plusieurs sur la même donnée

Une fois connectés, plusieurs utilisateurs éditent la même couche simultanément. PostgreSQL gère nativement la concurrence par ses transactions et ses verrous de ligne : deux personnes peuvent modifier deux entités différentes sans se gêner, et une modification n'est visible des autres qu'une fois validée. C'est précisément ce qu'un échange de fichiers .shp ne permet pas.

Et si une équipe de terrain dispose de réseau, un client connecté — QGIS sur portable, QField sur tablette — lit et écrit directement dans la base hébergée. La donnée saisie sur site est en base immédiatement, sans export à préparer ni consolidation au retour au bureau. C'est l'un des gains les plus concrets de l'hébergement, à condition d'avoir une connexion stable sur le terrain.

Brider les utilisateurs QGIS par les rôles PostgreSQL

Voici le point que beaucoup ignorent : QGIS ne décide rien des droits, il reflète ceux de PostgreSQL. Un utilisateur connecté avec un rôle en lecture seule ne peut même pas activer le mode édition dans QGIS — le bouton crayon reste grisé. Connecté avec un rôle éditeur, il édite les tables sur lesquelles il a INSERT/UPDATE/DELETE, et rien d'autre.

Le point de contrôle est donc unique et centralisé : la base. Vous n'avez pas à configurer des permissions dans QGIS poste par poste — vous gérez les rôles dans PostgreSQL, et chaque poste QGIS hérite automatiquement du bon périmètre selon le compte utilisé pour se connecter.

Une croyance tenace circule : « QGIS contournerait les droits PostgreSQL », parce qu'un utilisateur censé être en lecture seule arrive parfois à éditer. C'est faux. Dans tous les cas observés, QGIS s'est connecté avec un autre rôle que celui qu'on croyait — souvent un compte mis en cache lors d'une connexion précédente. Si le crayon n'est pas grisé alors qu'il devrait l'être, ce n'est pas QGIS qui triche : c'est le mauvais rôle qui est utilisé.

Pour en avoir le cœur net, activez log_statement = 'all' dans postgresql.conf : chaque requête apparaît dans les logs avec le rôle qui l'a émise. Vous verrez immédiatement qui écrit vraiment.

Bonne pratique — un compte nominatif par personne

Donnez à chaque utilisateur son propre compte PostgreSQL (marie, paul…), rattaché au bon groupe. Jamais de compte partagé. C'est ce qui permet de tracer qui a modifié quoi, de retirer un accès individuel sans impacter les autres, et de faire évoluer les droits d'une personne sans toucher au reste de l'équipe.

Alimenter la base à distance

Éditer dans QGIS couvre le quotidien. Reste l'alimentation en masse : charger un lot de données, pousser une préparation faite en local, automatiser un import récurrent.

ogr2ogr à travers le tunnel : le couteau suisse

ogr2ogr (de la bibliothèque GDAL) convertit et charge à peu près n'importe quel format géospatial vers PostGIS. À travers le tunnel SSH, on cible simplement 127.0.0.1 sur le port local — la commande est identique à un usage local :

Bash
# Charger un Shapefile dans la base hébergée, via le tunnel (port local 5433)
ogr2ogr -f "PostgreSQL" \
  PG:"host=127.0.0.1 port=5433 dbname=gis_db user=paul" \
  reseau_eau.shp \
  -nln reseau.canalisations \
  -t_srs "EPSG:2154" \
  -lco GEOMETRY_NAME=geom \
  -nlt PROMOTE_TO_MULTI

Chaque option compte : -nln nomme la table de destination (schéma inclus), -t_srs reprojette vers Lambert-93, -lco GEOMETRY_NAME=geom fixe le nom de la colonne géométrique, et -nlt PROMOTE_TO_MULTI évite les erreurs de type mixte polygone/multipolygone. Pour ajouter à une table existante au lieu de la recréer, on ajoute -append.

Local vers hébergé : import manuel ou réplication

Un cas fréquent : préparer la donnée tranquillement sur une base locale, puis la pousser en production une fois validée. Deux approches, selon la fréquence.

1

Transfert manuel ponctuel

  • pg_dump de la table locale, puis pg_restore vers la base hébergée
  • Ou ogr2ogr directement de PostgreSQL local vers PostgreSQL distant
  • Simple, sous contrôle, idéal pour une mise à jour occasionnelle
2

Réplication logique continue

  • PostgreSQL réplique en continu certaines tables d'une base vers une autre
  • Adapté à un flux régulier local → production
  • Plus lourd à mettre en place, à réserver aux besoins réellement continus

Le transfert PostgreSQL vers PostgreSQL avec ogr2ogr reprend la même logique que l'import de fichier — la source devient une chaîne PG: :

Bash
# Copier une table d'une base locale vers la base hébergée (via tunnel)
# Ordre ogr2ogr : destination d'abord, source ensuite
ogr2ogr -f "PostgreSQL" \
  PG:"host=127.0.0.1 port=5433 dbname=gis_db user=paul" \
  PG:"host=localhost port=5432 dbname=gis_local user=mattp" \
  "canalisations" \
  -nln reseau.canalisations \
  -nlt PROMOTE_TO_MULTI
Hors périmètre

Le workflow déconnecté complet — embarquer la donnée sur le terrain dans un GeoPackage et synchroniser au retour — relève d'une autre logique (mobilité, travail hors réseau) et fera l'objet d'un autre article. Ici, on reste côté desktop et serveur.

Charges récurrentes : scripts planifiés

Automatiser l'alimentation

Pour un import qui revient (intégration nocturne d'un export métier, par exemple), on encapsule la commande ogr2ogr dans un script déclenché par cron. Pour des transformations plus riches — jointures, nettoyage, contrôles qualité avant chargement — on bascule sur un vrai pipeline ETL. Le sujet est traité en détail dans ETL géospatial : préparer ses données avant QGIS, qui prolonge naturellement cet article côté préparation de données.

Ce qu'il faut retenir

Sécuriser un PostGIS hébergé n'est pas une affaire de réglage unique mais d'empilement maîtrisé : le réseau (jamais le 5432 nu — tunnel SSH ou VPN), la connexion (SSL et scram-sha-256 dans pg_hba.conf), et les droits (des rôles de groupe lecteur/éditeur/admin, des comptes nominatifs). Une fois ces trois couches posées, QGIS devient un client direct de votre base, chaque poste héritant automatiquement du bon périmètre — car les droits vivent dans PostgreSQL, jamais dans le client. L'alimentation à distance, elle, passe par le même tunnel sans rien exposer. La grille de mise en production tient dans la checklist ci-dessous : suivez-la dans l'ordre, n'ouvrez le réseau qu'en dernier.

Checklist de mise en production PostGIS hébergé

  • Volume de données persistant et sauvegarde testée
  • Port 5432 fermé au monde — accès par tunnel SSH ou VPN uniquement
  • SSL activé côté serveur (postgresql.conf)
  • pg_hba.conf nettoyé : scram-sha-256, aucune ligne trust en réseau, reject final
  • Rôles de groupe lecteur/éditeur/admin créés, accès PUBLIC révoqué
  • Comptes nominatifs rattachés aux groupes — aucun usage du superuser
  • ALTER DEFAULT PRIVILEGES posé sur le rôle qui crée les tables
  • Connexion QGIS via pg_service.conf + gestionnaire d'authentification
  • Vérifié : le rôle lecteur ne peut pas éditer dans QGIS

Le prochain article s'attaque à la couche suivante : diffuser cette donnée sur le web de façon sécurisée — exposer des tuiles et des API depuis PostGIS sans rouvrir ce qu'on vient de fermer.