Comment configurer Keycloak avec Docker et PostgreSQL

Banner

Dans ce blog, on va apprendre comment faire tourner Keycloak dans un conteneur Docker, en utilisant une base de données PostgreSQL dédiée qui tourne également dans un conteneur Docker.

Cette configuration est principalement conçue pour être utilisée dans un environnement de développement, mais elle constitue un bon point de départ pour un environnement de production utilisant une architecture de microservices.

Pour utiliser cette configuration sur votre propre machine, vous aurez besoin de Docker et de Docker Compose. Consultez la documentation pour avoir un guide sur la manière d'installer Docker sur votre système d'exploitation préféré https://docs.docker.com/get-docker/.

Commençons par configurer la base de données PostgreSQL.

Configuration de PostgreSQL avec Docker

Dans notre configuration, on utilise PostgreSQL comme base de données pour que Keycloak puisse persister des données comme les utilisateurs, les clients ou les domaines, et remplacer la base de données H2 fournie par défaut.

On exécute notre instance PostgreSQL dans un conteneur Docker en utilisant l'image PostgreSQL officielle fournie sur Docker Hub.

On utilise Docker Compose pour faciliter notre configuration multi-conteneur en définissant l'instance PostgreSQL dans un fichier docker-compose.yml.

version: "3.9"
services:
  postgres:
    container_name: postgres_blog
    image: "postgres:13.2"
    volumes:
      - ./db-data:/var/lib/postgresql/data/
      - ./sql:/docker-entrypoint-initdb.d/:ro
    env_file:
     - ./database.dev.env
    networks:
      - backend
    ports:
      - "5432:5432"
  pgadmin:
    container_name: pgadmin_blog
    image: "dpage/pgadmin4:5.1"
    env_file:
      - ./database.dev.env
    ports:
      - "5050:80"
    networks:
      - backend

Plusieurs éléments sont à prendre en compte ici. Expliquons-les un par un.

On commence par définir des volumes pour monter des données dans le conteneur, et pour persister des données sur l'hôte.

Ici, on fournit à la fois un chemin source et un chemin cible, ce qui fait de ces volumes des montages de type bind. Ce type de volume convient à un environnement de développement, mais il est recommandé d'utiliser des volumes nommés ou de copier les fichiers directement dans le conteneur pour un environnement de production, sauf si vous souhaitez avoir un contrôle total sur votre système de fichiers et apporter des modifications en dehors de Docker.

Le premier montage de type bind est utilisé pour persister les données même si le conteneur est arrêté. Le deuxième monte les fichiers pour initialiser les bases de données Keycloak et d'application.

Les deux premiers scripts ne sont nécessaires que si vous souhaitez stocker des données liées à l'application dans la même base de données que celle de Keycloak.

#!/bin/bash

psql -U dev -tc "SELECT 1 FROM pg_database WHERE datname = 'app'" \
| grep -q 1 || psql -U dev -c "CREATE DATABASE app"

Contrairement aux bases de données de type MySQL, PostgreSQL n'a pas d'instruction CREATE DATABASE IF EXIST.

Pour contourner cette limitation, on utilise un script bash pour exécuter une commande psql qui crée la base de données si elle n'existe pas. Ici, le double pipe signifie que la commande CREATE DATABASE psql ne sera exécutée que si la commande grep ne réussit pas.

\c app;

DROP TABLE IF EXISTS Member;

CREATE TABLE Member (
    id VARCHAR(50) NOT NULL PRIMARY KEY,
    username VARCHAR(50) NOT NULL,
    email VARCHAR(50) NOT NULL,
    createdAt TIMESTAMP DEFAULT NOW()
);

DROP TABLE IF EXISTS Post;

CREATE TABLE Post (
    id SERIAL NOT NULL PRIMARY KEY,
    title VARCHAR(50) NOT NULL,
    scientificName VARCHAR(50) NOT NULL,
    family VARCHAR(32) NOT NULL,
    rating NUMERIC(3,2) DEFAULT 0.5,
    authorId VARCHAR(50),
    createdAt TIMESTAMP DEFAULT NOW(),
    CONSTRAINT fk_member FOREIGN KEY(authorId) REFERENCES Member(id)
    ON DELETE SET NULL ON UPDATE CASCADE
);

Le deuxième script est plus simple. Il initialise simplement le schéma de la base de données de l'application.

#!/bin/bash

psql -U dev -tc "SELECT 1 FROM pg_database WHERE datname = 'keycloak'" \
| grep -q 1 || psql -U dev -c "CREATE DATABASE keycloak"

psql -U dev -c "CREATE USER keycloak WITH PASSWORD 'kc'"

Dans le dernier script, on crée la base de données Keycloak de manière similaire à la base de données de l'application.

On crée également un nouvel utilisateur qui sera utilisé par Keycloak pour accéder à la base de données. Il est bon d'utiliser une pratique de sécurité recommandée en définissant un utilisateur dédié à un usage spécifique, au lieu d'utiliser le superutilisateur par défaut.

Le fichier .env est utilisé pour définir les variables d'environnement répertoriées sur la page Docker Hub pour indiquer l'utilisateur à créer par défaut, tel que défini dans le script précédent :

POSTGRES_USER="dev"
POSTGRES_PASSWORD="pwd"

On définit aussi un service pgAdmin. Il fournit une interface utilisateur facile à utiliser et évite de devoir accéder directement au conteneur pour interroger la base de données en utilisant la ligne de commande.

On doit ajouter deux nouvelles variables d'environnement pour définir la connexion par défaut pour accéder à l'interface utilisateur.

POSTGRES_USER="dev"
POSTGRES_PASSWORD="pwd"
PGADMIN_DEFAULT_EMAIL="user@domain.local"
PGADMIN_DEFAULT_PASSWORD="admin"

Configuration de Keycloak avec Docker

Keycloak est une solution open-source de gestion des identités développée par Red Hat. C'est une alternative robuste aux produits SaaS tels qu'Auth0 ou aux services cloud tels que Firebase ou AWS Cognito, et il répond à presque tous nos besoins en matière d'autorisation ou d'authentification. Pour les besoins spécifiques non couverts par l'implémentation par défaut, Keycloak propose de nombreuses interfaces de fournisseur de services pour intégrer vos propres fournisseurs personnalisés.

Si vous voulez en savoir plus sur Keycloak, consultez la documentation officielle https://www.keycloak.org/

La première étape consiste à compléter notre fichier docker-compose en ajoutant le service Keycloak.

version: "3.9"
services:
  postgres:
    container_name: postgres_blog
    image: "postgres:13.2"
    env_file:
      - ./database.dev.env
    networks:
      - backend
    volumes:
      - ./db-data:/var/lib/postgresql/data/
      - ./sql:/docker-entrypoint-initdb.d/:ro
    ports:
      - "127.0.0.1:5432:5432"
  pgadmin:
    container_name: pgadmin_blog
    image: "dpage/pgadmin4:5.1"
    env_file:
      - ./database.dev.env
    ports:
      - "127.0.0.1:5050:80"
    networks:
      - backend
  keycloak:
    container_name: keycloak_blog
    image: "jboss/keycloak:15.0.2"
    depends_on:
      - "postgres"
    env_file:
      - ./keycloak.dev.env
    ports:
      - "127.0.0.1:8180:8080"
      - "127.0.0.1:8787:8787" # debug port
    networks:
      - backend

networks:
  backend:
    name: backend
    driver: bridge

Il n'est pas nécessaire de définir de nouveaux volumes ici, on doit simplement mapper le port 8080 pour accéder à Keycloak depuis l'hôte, ainsi que le port de débogage, au cas où on voudrait attacher un débogueur distant depuis notre IDE, par exemple.

De manière similaire à la configuration PostgreSQL, on utilise un fichier .env pour définir les variables d'environnement nécessaires.

KEYCLOAK_USER=admin
KEYCLOAK_PASSWORD=password
DEBUG=true
DEBUG_PORT='*:8787'
DB_VENDOR=POSTGRES
DB_ADDR=postgres
DB_PORT=5432
DB_DATABASE=keycloak
DB_USER=keycloak
DB_PASSWORD=kc
TZ=Europe/Paris

Toutes les variables d'environnement disponibles sont répertoriées sur la page Docker Hub de l'image Keycloak https://hub.docker.com/r/jboss/keycloak/.

On crée également un nouveau script pour créer à la fois l'utilisateur et la base de données Keycloak.

On définit tous les services sur le même réseau afin que chaque conteneur puisse communiquer entre eux.

Ici, on utilise le mode bridge car la communication se fait uniquement sur le même hôte.

Enfin, on spécifie que le service Keycloak dépend du service PostgreSQL, sinon on obtiendrait une erreur de connexion à la base de données pendant la phase de démarrage du conteneur Keycloak.

Conclusion

Maintenant qu'on a créé toutes les déclarations nécessaires, il est temps de lancer notre configuration.

Docker-compose fournit une seule commande qui initialise tous les services

docker-compose up -d

On peut vérifier que tous les conteneurs sont en cours d'exécution une fois le démarrage terminé

docker ps

On peut maintenant accéder au Back Office Keycloak via le port mappé dans le fichier docker-compose.yml et en utilisant la connexion précédemment définie dans notre fichier .env.

keycloak_login.PNG

Une fois connectés, on peut personnaliser notre domaine en créant des utilisateurs, en définissant des clients et en effectuant de nombreuses autres actions. Je vous laisse consulter la documentation officielle de Keycloak pour avoir une idée de toutes les fonctionnalités que Keycloak offre.

keycloak_home.PNG

On peut également accéder à la base de données PostgreSQL en utilisant pgAdmin :

pgAdmin_login.PNG

On doit ensuite configurer la connexion à la base de données PostgreSQL :

pgAdmin_connection.PNG

Et enfin, on peut accéder au schéma de notre application :

pgAdmin_query.PNG

Le repository qui contient tous les fichiers de configuration est disponible ici : https://github.com/Mozenn/setup-keycloak-with-docker-and-postgresql

Ressources