Eymoutiers, le 20 octobre 2025
Nouvel épisode de notre série dédiée à l’industrialisation de PostgreSQL : après le déploiement d’instances Postgres à grande échelle et la gestion homogène des sauvegardes Postgres avec pglift, nous allons cette fois nous pencher sur la question de la sécurité.
Les bases de données sont les cibles ultimes des attaques informatiques
Les bases de données contiennent les informations les plus précieuses d’une organisation : données personnelles, informations financières, secrets commerciaux, dossiers médicaux, etc. Elles représentent donc le point névralgique et un bastion à protéger au cœur de chaque système d’information.
PostgreSQL propose bien sûr un large panel de mesures internes pour la sécurité :
un controle des accès très fin avec le fichier pg_hba.conf
, le chiffrement
TLS des communications, un système de roles/permissions sophitiqué ou encore
les règles RLS pour filtrer les lectures lignes par lignes.
Mais qu’en est-il de l’environnement dans lequel PostgreSQL évolue ? En particulier, comment protéger l’instance PostgreSQL depuis l’extérieur ?
SELinux est le standard de l’industrie
SELinux
( pour “Security-Enhanced Linux”) répond à cette question en ajoutant
une couche de sécurité supplémentaire au-delà du système de permissions
traditionnel. Développé initialement par la NSA, SELinux applique des politiques
de sécurité strictes qui décrivent très précisément quels processus peuvent
accéder à quelles ressources (fichiers, ports réseau, etc.). Chaque processus
est ainsi isolé et confiné dans un contexte précis. Concrètement, si un
attaquant prend le control d’un service fonctionnant avec les privilèges root,
SELinux va contenir l’attaque en empêchant le processus compromis d’accéder à
des ressources non autorisées.
Ce qui est exactement le cas de Postgres ! Par définition, PostgreSQL est
habituellement lancé avec l’utilisateur root
et accède à de nombreux emplacements
de fichiers. Dès lors, la définition d’une politique SELinux pour le service est
complexe et devient réellement ardue lorsque l’on y ajoute les autres briques
logicielles de la stack Postgres, notamment pgBackrest, Patroni, etc.
Voir notre article ci-dessous pour un exemple concret:
https://blog.dalibo.com/2014/10/06/Confiner_PostgreSQL_avec_SELinux.html
Ansible augmente la surface d’attaque
La difficulté augmente également lorsque l’on passe à l’échelle supérieure et que l’on veut piloter un parc d’instances avec un outil d’orchestration comme Ansible.
Pour gérer chaque instance PostgreSQL, le nœud de contrôle Ansible va se connecter en SSH à chaque serveur et devra acquérir les privilèges superutilisateur pour certaines opérations, notamment pour manipuler le service Postgres.
Par exemple:
- name: Enabling postgresql services
become: true
ansible.builtin.service:
name: postgresql
state: started
enabled: true
Ici la ligne become: true
permet de devenir root
le temps de réaliser la tâche
pour vérifier l’état du service et de l’activer et démarrer si nécessaire.
Cette simple ligne semble anodine, mais implique que l’utilisateur lançant le playbook
peut acquérir les droits superutilisateur à tout moment sur un nœud distant.
Concrètement, cela signifie que l’utilisateur pilotant les playbooks Ansible est
de facto root
sur l’ensemble du parc d’instances Postgres.
C’est en cela qu’Ansible peut élargir la surface d’attaque vers PostgreSQL. En effet, le nœud de contrôle est une cible de choix pour les attaquants : plutôt que d’attaquer directement les bases de données qui sont généralement les éléments les plus protégés, ils tenteront d’accéder indirectement aux instances en passant par des points d’entrées moins bien sécurisés et plus facile d’accès.
Bien sûr, il est possible de restreindre les commandes accessibles via des solutions d’élévation
de privilèges comme sudo
, doas
ou d’autres mécanismes similaires, notamment en configurant
leurs fichiers de contrôle (par exemple /etc/sudoers
pour sudo
). Cependant, cela ajoute une
couche supplémentaire de complexité et augmente le risque d’erreur de configuration.
Séparer les rôles pour mieux confiner Postgres
La solution consiste à revenir à la racine du problème. Dans le cycle de vie d’une instance Postgres, 2 types d’intervenant sont impliqués.
- Un rôle “sysadmin” chargé de préparer la machine qui hébergera PostgreSQL en installant tous les paquets logiciels nécessaires.
- Un rôle “dba” chargé de créer les instances Postgres et garantir leur bon fonctionnement.
Pour chacun de ces deux rôles, on assigne un et un seul utilisateur système.
Le rôle “sysadmin” aura les droits de l’utilisateur root
, tandis que
le rôle dba se limitera à des droits restreints nécessaires à la gestion des instances
PostgreSQL. Dans les exemples qui suivent, nous utiliserons l’utilisateur postgres
.
Concrètement cela signifie que l’on veut interdire aux DBA d’acquérir les
privilèges root via sudo
et lui interdire d’utiliser become: true
dans ses playbooks Ansible !
Les services utilisateur de Systemd : un trésor caché
Mais cela semble impossible puisque le service Postgres est lancé en tant
que root
via une commande du type sudo systemctl start postgresql
?
C’est ici qu’entre en jeu le “mode user” de systemd, un mode de fonctionnement
largement méconnu et sous-estimé. En effet, la commande systemctl --user
permet à chaque utilisateur de démarrer/arrêter/activer/désactiver ses propres
services.
Dans le cas de PostgreSQL, on fera généralement tourner le service avec
l’utilisateur système postgres
mais il est possible d’utiliser n’importe
quel autre utilisateur. On stockera les données (PGDATA) dans le dossier $HOME
de l’utilisateur plutot que dans le dossier traditionnel /var/lib/postgres
.
Enfin il est nécessaire d’activer la fonction lingering
pour l’utilisateur,
qui permet au service de continuer à tourner, même après la déconnexion de
l’utilisateur. Cette fonction est activée par la commande ci-dessous:
loginctl enable-linger <utilisateur>
pglift : la sécurité par défaut
En tant qu’outil de déploiement, pglift
tire pleinement partie de systemd et
de ce mode utilisateur.
Lorsque systemd est activé, pglift
utilise le mode utilisateur par défaut.
Concrètement la commande pglift instance create
initialise une nouvelle instance
et crée une unité de service systemd pour l’utilisateur postgres
.
Désormais, l’utilisateur postgres
peut manipuler son instance sans utiliser
la commande sudo
. De même, il peut créer une instance avec un playbook
Ansible sans utiliser become: true
.
Par exemple :
- name: Create and configure my postgresql instances
hosts: localhost
tasks:
- name: Create production instance
dalibo.pglift.instance:
name: prod
state: started
port: ""
settings:
max_connections: 100
shared_buffers: 1GB
unix_socket_directories: /tmp
shared_preload_libraries: pg_stat_statements, passwordcheck
Atout majeur de ce confinement du service postgres : cela rend l’activation de
SELinux complètement trivial ! Puisque le processus PostgreSQL tourne dans
le contexte de l’utilisateur système postgres
, les accès aux ressources sont
déjà encadrés par les droits d’accès standard.
En d’autres termes, si un attaquant arrive à compromettre une instance
PostgreSQL créée avec pglift, il ne pourra pas “sortir” du rôle postgres
et n’aura pas accès à des ressources non-autorisées.
Cette approche permet également d’isoler plusieurs instances PostgreSQL exécutées sous des comptes utilisateurs distincts. Chaque instance dispose de son propre répertoire de données, accessible uniquement à l’utilisateur qui l’exécute. Ainsi, les données ne sont ni accessibles ni modifiables par d’autres comptes, y compris l’utilisateur postgres. Ce cloisonnement, basé sur les permissions classiques du système de fichiers, assure une séparation stricte et efficace des environnements basée sur des mécanismes éprouvés.
Etablir une politique de sécurité à grande échelle
En résumé, pglift vous permet facilement et par défaut de:
- Séparer clairement les rôles “sysadmin” et “dba”
- Activer SELinux sans configuration complexe
- Interdire
sudo
à l’utilisateurpostgres
- Éradiquer la clause
become: true
des playbooks PostgreSQL
Il existe évidement d’autres mesures de sécurité à mettre en place pour compléter l’arsenal de protection de vos bases de données. On peut notamment citer :
- Imposer les accès nominatifs avec un annuaire externe et ldap2pg
- Anonymiser les données sur les environnements secondaires (dev, testing) avec PostgreSQL Anonymizer
- Tracer toutes les interactions des utilisateurs avec pgaudit
- Compartimenter les processus et les ressources de PostgreSQL grace espaces de noms (“namespaces”) de Linux
De vastes sujets, que nous arborderons peut-être à l’occasion d’un prochain article !
Crédit Photo: Pew Nguyen