Illustration représentant la sécurité d'un serveur PostgreSQL SSL dans Docker. Un cadenas avec un effet de bouclier énergétique symbolise le chiffrement des données.

Docker : configurer un serveur PostgreSQL sécurisé avec SSL

Avec l’augmentation des cyberattaques et des violations de données, la sécurité des données est devenue une priorité pour de nombreuses entreprises. L’utilisation de protocoles de sécurité comme SSL (Secure Sockets Layer) pour chiffrer les données en transit est un moyen efficace de protéger les informations sensibles contre les attaques.
Les bases de données, comme PostgreSQL, sont particulièrement concernées par la sécurité des données, car elles stockent souvent des informations nécessitant une protection renforcée. Durant les phases de développement ou de validation, il est souvent nécessaire de tester les applications avec des bases de données dont les communications sont chiffrées. Dans cet article, nous allons découvrir comment configurer un serveur PostgreSQL avec SSL dans Docker afin de simplifier le processus de test de ces applications.

Deux solutions pour configurer un conteneur PostgreSQL SSL

La configuration d’un serveur PostgreSQL est toujours plus simple lorsque nous pouvons intervenir manuellement sur le serveur via un terminal. Cependant, que ce soit pour configurer son environnement de développement ou pour simplifier le déploiement d’un serveur PostgreSQL, il est souvent plus pratique d’utiliser Docker.

Généralement, les images Docker officielles nous permettent de configurer le conteneur en utilisant des variables d’environnement. Il arrive malheureusement que certaines fonctionnalités, comme la configuration SSL, soient plus complexes à mettre en place. Dans ce cas, nous pouvons :

  • Soit exploiter les options de configuration de l’image Docker,
  • Soit créer notre propre image Docker à partir de l’image officielle.

Configurer un conteneur PostgreSQL SSL avec les options de configuration

En lisant la documentation de l’image Docker PostgreSQL, nous découvrons que l’image officielle n’expose pas d’options de configuration pour activer directement SSL. La documentation nous indique également que l’image officielle utilise un script d’initialisation pour configurer le serveur PostgreSQL.

Nous pouvons donc envisager d’exploiter cette fonctionnalité pour configurer le serveur PostgreSQL avec SSL. Pour cela, nous devons :

  1. Créer un script d’initialisation personnalisé pour configurer le serveur PostgreSQL avec SSL.
  2. Créer les certificats SSL nécessaires pour chiffrer les communications.
  3. Créer le conteneur Docker en montant le script d’initialisation et les certificats SSL.

Créer un script d’initialisation personnalisé

Description de la solution

L’option des scripts d’initialisation permet de personnaliser le comportement de création de la base de données lors du premier démarrage du conteneur. Nous allons donc créer un script d’initialisation personnalisé pour configurer le serveur PostgreSQL avec SSL. Pour cela, de nombreuses stratégies d’automatisation sont possibles. Nous nous concentrerons sur une solution offrant le plus de flexibilité.

L’objectif est de permettre à l’utilisateur de :

  • Configurer finement les options SSL du serveur PostgreSQL.
  • Fournir ses propres certificats SSL pour chiffrer les communications.

Créer le script d’initialisation

Le script d’initialisation conforme à notre solution pourrait contenir les instructions suivantes :

#!/usr/bin/env bash

set -e


# Initialize SSL directory
mkdir /var/lib/postgresql/data/ssl/

# Override the default configuration using additional configuration files
if [ ! -f /var/lib/postgresql/data/postgresql.conf ]; then
    echo "[WARNING] No such file '/var/lib/postgresql/data/postgresql.conf'" >&2
elif [ ! -d /opt/postgresql.conf.d ]; then
    echo "[WARNING] No such directory '/opt/postgresql.conf.d'" >&2
else
    sed -r "s@^#include_dir.*\$@include_dir = '/opt/postgresql.conf.d/'@" -i /var/lib/postgresql/data/postgresql.conf
fi


# Copy SSL files to protected directory and override permissions
if [ -d /opt/ssl-in ]; then
    cp -r /opt/ssl-in/* /var/lib/postgresql/data/ssl/
    find /var/lib/postgresql/data/ssl/ -type f -exec chmod 600 {} \;
else
    echo "[WARNING] No such directory '/opt/ssl-in'" >&2
fi

Ce script d’initialisation crée un répertoire ssl dans lequel seront stockés les certificats SSL exploitables par le serveur PostgreSQL. L’utilisateur peut fournir ses propres certificats SSL grâce à un point de montage dans le répertoire /opt/ssl-in. Le script d’initialisation permet également de surcharger la configuration par défaut en permettant à l’utilisateur de monter ses propres fichiers de configuration dans le répertoire /opt/postgresql.conf.d.

Créer les certificats SSL et la configuration du serveur PostgreSQL

La configuration SSL du serveur PostgreSQL nécessite un certificat SSL pour le serveur et une clé privée. Le client psql nécessite quant à lui la chaîne de certificats de l’autorité de certification (CA) qui a signé le certificat du serveur.

Créer les certificats SSL

Pour créer ces certificats SSL, vous pouvez utiliser un outil comme OpenSSL. Je vous invite à consulter mon article sur la création de certificats SSL autogérés. Ici, nous utiliserons des certificats SSL auto-signés pour simplifier la configuration.

Créer la configuration SSL du serveur PostgreSQL

La seconde étape consiste à configurer le serveur PostgreSQL pour utiliser les certificats SSL. Pour cela, nous devons créer un fichier de configuration contenant les directives SSL appropriées. Ce fichier sera monté dans le conteneur Docker dans le répertoire /opt/postgresql.conf.d.

# ssl.conf
ssl = on
ssl_ca_file = '/var/lib/postgresql/data/ssl/server.crt'
ssl_cert_file = '/var/lib/postgresql/data/ssl/server.crt'
ssl_crl_file = ''
ssl_key_file = '/var/lib/postgresql/data/ssl/server.key'
ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers
ssl_prefer_server_ciphers = on

Comme vous pouvez le constater, ce fichier configure le serveur PostgreSQL pour activer les connexions chiffrées et utiliser un certificat SSL auto-signé stocké dans le répertoire /var/lib/postgresql/data/ssl.

Créer le conteneur PostgreSQL en mode SSL

Les premières étapes de configuration étant terminées, nous pouvons maintenant créer le conteneur PostgreSQL en mode SSL. Pour cela, nous devons :

  • Créer l’arborescence de fichiers à monter dans le conteneur Docker.
  • Créer le conteneur Docker en montant les fichiers nécessaires.

Créer l’arborescence de fichiers à monter

L’arborescence de fichiers découpe de manière logique les fichiers présentés précédemment afin de simplifier le montage dans le conteneur Docker. Voici un exemple de cette arborescence :

.
└── docker
    ├── conf.d
    │   └── ssl.conf
    ├── initdb.d
    │   └── configure-ssl.bash
    └── ssl
        ├── server.crt
        └── server.key

Exécuter le conteneur Docker PostgreSQL SSL

La dernière étape consiste à exécuter le conteneur Docker PostgreSQL en montant les fichiers nécessaires. Voici la commande Docker compatible avec notre arborescence de fichiers :

$ docker run --name postgres-ssl -p 5432:5432 \
    -v $(pwd)/docker/conf.d:/opt/postgresql.conf.d \
    -v $(pwd)/docker/initdb.d:/docker-entrypoint-initdb.d \
    -v $(pwd)/docker/ssl:/opt/ssl-in \
    -e POSTGRES_PASSWORD=mysecretpassword \
    postgres:latest
The files belonging to this database system will be owned by user "postgres".
This user must also own the server process.
...
server started

/usr/local/bin/docker-entrypoint.sh: ignoring /docker-entrypoint-initdb.d/configure-ssl.bash # <1>

...
2024-12-19 09:34:50.821 UTC [1] LOG:  database system is ready to accept connections
1

Notre script d’initialisation a été exécuté avec succès, aucun message de warning ou d’erreur n’a été affiché.

Créer une image Docker PostgreSQL SSL personnalisée

Problèmes de la première solution

La documentation de l’image officielle PostgreSQL indique que les fichiers montés dans le répertoire /docker-entrypoint-initdb.d/ sont exécutés uniquement lors de l’initialisation de la base de données. Cela signifie que notre script d’initialisation ne s’exécute qu’une seule fois, au premier démarrage du conteneur. Si nous devons modifier les certificats SSL, nous devrons recréer le conteneur Docker.

Pour des tests ponctuels, cette solution est suffisante. Cependant, pour des besoins de développement ou de validation plus avancés nécessitant la persistance des données, il est préférable de créer une image Docker personnalisée. Cela permettra de reconfigurer le serveur PostgreSQL en redémarrant simplement le conteneur Docker.

Créer une image Docker PostgreSQL personnalisée

Pour créer une image Docker PostgreSQL capable de reconfigurer le serveur en mode SSL à chaque démarrage, nous allons intégrer le mécanisme précédent directement dans une image Docker personnalisée.

Création d’un nouvel entrypoint

Le nouvel entrypoint doit permettre d’exécuter un ou plusieurs scripts d’initialisation avant d’appeler l’entrypoint d’origine. Cela nous permettra de copier les fichiers SSL à chaque redémarrage du conteneur (volume existant ou non).

# /docker-before-entrypoint.sh
#!/usr/bin/env bash


# Source original entrypoint script
. /usr/local/bin/docker-entrypoint.sh

if ! _is_sourced; then
  # Source all sh files in /docker-entrypoint-init.d/ directory
  while read -r file; do
    echo "[INFO] Source file: '$file'"

    set -e
    . "$file"
  done < <(ls /docker-entrypoint-init.d/*.sh 2>/dev/null || true)

  # Execute original entrypoint
  _main "$@"
fi

Cet entrypoint exécute tous les scripts présents dans le répertoire /docker-entrypoint-init.d/ avant d’appeler l’entrypoint d’origine.

Ajout du script extends-conf.sh

Le script extends-conf.sh va étendre la configuration du serveur PostgreSQL en chargeant les fichiers de configuration additionnels depuis le répertoire /opt/postgresql.conf.d/.

# /extends-conf.sh
#!/usr/bin/env bash

set -e


if [ ! -f /var/lib/postgresql/data/postgresql.conf ]; then
  echo "[WARNING] No such file '/var/lib/postgresql/data/postgresql.conf'" >&2
else
  sed -r "s@^#include_dir.*\$@include_dir = '/opt/postgresql.conf.d/'@" \
    -i /var/lib/postgresql/data/postgresql.conf
fi

Création du Dockerfile

Maintenant que nos scripts sont prêts, nous pouvons créer notre Dockerfile :

# /Dockerfile
ARG TAG=latest

FROM postgres:${TAG}

RUN mkdir /docker-entrypoint-init.d/ /opt/postgresql.conf.d \
    && chmod 777 /docker-entrypoint-init.d/ \
    && chmod 755 /opt/postgresql.conf.d \
    && chown postgres:postgres /opt/postgresql.conf.d

COPY ./docker-before-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-before-entrypoint.sh

COPY ./extends-conf.sh /docker-entrypoint-initdb.d/ # <1>
RUN chmod +x /docker-entrypoint-initdb.d/extends-conf.sh

ENTRYPOINT [ "docker-before-entrypoint.sh" ]
CMD [ "postgres" ]
1

Attention, cette directive impose à l’utilisateur de monter individuellement chaque fichier plutôt que le répertoire complet.

Création de l’image Docker PostgreSQL personnalisée

Toutes nos ressources sont prêtes pour construire notre image Docker PostgreSQL SSL. Voici la commande Docker pour construire l’image :

$ docker build -t lavoiedudev/postgres:latest .

Exécuter le conteneur Docker PostgreSQL personnalisé

Avant d’exécuter notre propre image postgres, nous devons adapter les fichiers à monter dans le conteneur Docker.

Script de pré-configuration SSL

Nous allons renommer le script docker/initdb.d/configure-ssl.bash en docker/init.d/configure-ssl.sh et supprimer les instructions inutiles. Le nouveau script est le suivant :

# /docker/init.d/configure-ssl.sh
#!/usr/bin/env sh

set -e


# Initialize SSL directory
mkdir /etc/postgresql/ssl/


# Copy SSL files to protected directory and override permissions
if [ -d /opt/ssl-in ]; then
    cp -r /opt/ssl-in/* /etc/postgresql/ssl/

    find /etc/postgresql/ssl/ -type f -exec chown postgres:postgres {} \;
    find /etc/postgresql/ssl/ -type f -exec chmod 600 {} \;
else
    echo "[WARNING] No such directory '/opt/ssl-in'" >&2
fi

Ce script ne traite maintenant que la copie des fichiers SSL dans le répertoire /etc/postgresql/ssl/. Cela implique de modifier le fichier de configuration ssl.conf pour utiliser le nouveau répertoire :

 # /docker/conf.d/ssl.conf
 ssl = on
-ssl_ca_file = '/var/lib/postgresql/data/ssl/server.crt'
-ssl_cert_file = '/var/lib/postgresql/data/ssl/server.crt'
+ssl_ca_file = '/etc/postgresql/ssl/server.crt'
+ssl_cert_file = '/etc/postgresql/ssl/server.crt'
 ssl_crl_file = ''
-ssl_key_file = '/var/lib/postgresql/data/ssl/server.key'
+ssl_key_file = '/etc/postgresql/ssl/server.key'
 ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers
 ssl_prefer_server_ciphers = on

Exécuter notre image PostgreSQL en mode SSL

Toutes les modifications nécessaires ont été apportées. Nous pouvons exécuter notre conteneur postgres en mode SSL en utilisant la commande suivante :

$ docker run --name postgres-ssl -p 5432:5432 \
    -v $(pwd)/docker/conf.d:/opt/postgresql.conf.d \
    -v $(pwd)/docker/init.d:/docker-entrypoint-init.d \
    -v $(pwd)/docker/ssl:/opt/ssl-in \
    -e POSTGRES_PASSWORD=mysecretpassword \
    lavoiedudev/postgres:latest
[INFO] Source file: '/docker-entrypoint-init.d/configure-ssl.sh'
...
server started

/usr/local/bin/docker-entrypoint.sh: running /docker-entrypoint-initdb.d/extends-conf.sh

2024-12-19 11:44:42.276 UTC [58] LOG:  received fast shutdown request
...
2024-12-19 11:44:43.432 UTC [1] LOG:  database system is ready to accept connections

Comme vous pouvez le constater, notre script d’initialisation a été exécuté avec succès. Le serveur PostgreSQL est maintenant configuré pour accepter les connexions chiffrées.

Tester la configuration SSL du serveur PostgreSQL

Avant de terminer, nous allons tester la configuration SSL du serveur PostgreSQL. Pour cela, nous allons utiliser le client psql du conteneur pour nous connecter au serveur PostgreSQL en mode SSL.

$ docker container exec -u postgres -it postgres-ssl bash
$ psql "sslmode=verify-full host=localhost port=5432 sslrootcert=/etc/postgresql/ssl/server.crt"
psql (17.2 (Debian 17.2-1.pgdg120+1))
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, compression: off, ALPN: postgresql)
Type "help" for help.

postgres=# \q

La connexion a été établie avec succès en mode SSL. Nous pouvons donc valider que notre serveur PostgreSQL est correctement configuré pour utiliser SSL.

Conclusion

Dans cet article, nous avons exploré deux méthodes pour configurer un serveur PostgreSQL avec SSL dans Docker :

  • Méthode 1 : utiliser les options de configuration de l’image Docker officielle.
  • Méthode 2 : créer une image Docker personnalisée plus flexible.

Nous avons également vu comment configurer le serveur PostgreSQL pour utiliser nos propres certificats SSL. Enfin, nous avons validé que les connexions sont correctement chiffrées avec le client psql.

Ainsi, en suivant les étapes décrites dans cet article, vous pourrez facilement configurer PostgreSQL avec SSL dans Docker. Cela vous permettra de mettre en place un environnement de test ou de développement sécurisé pour tester les communications chiffrées entre vos applications et votre base de données.

Merci de votre lecture, n’hésitez pas à partager cet article et à laisser vos questions ou vos suggestions en commentaire.
Pour aller plus loin, consultez mon article sur la création de certificats SSL auto-signés.

Laisser un commentaire