Un credential broker est un service qui facilite la gestion et l'échange sécurisé des credentials entre différents systèmes dans une infrastructure informatique. Il centralise et sécurise la gestion des credentials (noms d'utilisateur, mots de passe, clés d'API, certificats, etc.), et garantit un accès autorisé et contrôlé à ces ressources.
Les applications et les services peuvent alors demander les identifiants nécessaires au credential broker de manière sécurisée, sans avoir à les stocker ou à les gérer localement.
Pour implémenter Vault Credential Broker, il faut d’abord comprendre le fonctionnement de Boundary et de Vault.
Nous avons déjà creusé l’implémentation et l’utilisation de Boundary dans un précédent article. Revenons en détail sur son fonctionnement.
Boundary permet de créer des sessions entre un utilisateur et un service. C’est une alternative aux bastions et VPN, qui permet d’avoir une granularité fine limitant l’accès à vos ressources. Si vous donnez à vos utilisateurs l’accès à un bastion, ils auront accès à toutes les ressources accessibles par le bastion. Avec Boundary, vous pouvez configurer avec précision quels utilisateurs ont accès à quelles ressources.
Pour expliquer rapidement le fonctionnement de Boundary, il utilise deux composants principaux :
Le schéma ci-dessous explique plus en détail le flow de création d’un tunnel vers une target.
Zoomons maintenant sur le fonctionnement de Vault.
C’est un outil utilisé pour la gestion des secrets. Il offre une granularité fine qui permet de limiter l'accès au sein de vos ressources. Par exemple, vous pouvez avoir une base de données avec plusieurs rôles ayant des droits différents sur les tables.
Vault vous permet d'accéder uniquement aux informations d'identification d'un seul rôle. Vous pouvez ainsi limiter les droits de vos utilisateurs sur la base de données.
Il utilise un coffre-fort, dans lequel vous pouvez stocker de manière sécurisée vos secrets. La gestion de Vault étant path-based, vos secrets sont stockés dans des “chemins”.
La gestion des accès aux secrets est faite avec des policies (politiques). Les policies sont des règles qui donnent des accès en lecture/écriture/etc à certains chemins. Voici un exemple de policy :
# Permet de lire le contenu du chemin postgres/creds/analyst
path "postgres/creds/analyst" {
capabilities = ["read"]
}
Dans cet exemple, on stocke dans le chemin postgres/creds/analyst des identifiants pour accéder à une base de données postgres avec un rôle analyst. Cette policy donne le droit d’accéder à ces identifiants en lecture.
Lorsqu'un utilisateur ou une application souhaite interagir avec Vault pour lire ou modifier le contenu d’un chemin, il doit d’abord s’authentifier pour obtenir un token d’authentification temporaire. Il est possible de configurer plusieurs méthodes d’authentification pour accéder à Vault, mais il faut mapper une policy à chaque méthode d’authentification.
Par exemple, la documentation Vault donne cet exemple de mapping si on veut configurer LDAP en tant que méthode d’authentification :
Dans cet exemple, le flow d’authentification ressemblerait à ceci pour un développeur utilisant une authentification LDAP :
Une fois qu’il est authentifié à Vault, l’utilisateur peut interagir avec Vault en lui présentant son token d'authentification. Vault vérifie les policies associées à ce token pour déterminer si chaque opération est autorisée ou non.
Vault peut aussi être utilisé pour générer des secrets dynamiques et court terme. Pour cela, il suffit de créer un backend, connecté à une target. Cela permet à Vault de créer des sets de credentials temporaires sur cette target. Un utilisateur authentifié peut alors demander la création d’un secret dynamique pour accéder à la target.
Vault Credential Broker est une implémentation de Vault en tant que Credential Broker au sein de Boundary. Cela permet à un client de s’authentifier auprès d’un service avec des credentials temporaires !
Le schéma suivant explique le flow de connexion d’un client Boundary vers une target. Ce schéma commence à devenir complexe, mais il reprend uniquement le schéma de fonctionnement de Boundary vu précédemment. On y ajoute Vault (en bleu) pour générer un secret dynamique pour que l’utilisateur puisse accéder à la target.
Contrairement au cas d’utilisation de Boundary sans Vault, les informations de connexion fournis au client sont maintenant un secret dynamique émis par Vault. Ce secret a l’avantage d’être temporaire et généré à partir de policies qui restreignent les droits du client sur la target.
On a déployé Vault Credential Broker dans un cluster EKS en utilisant Terraform et Helm. Si vous êtes curieux, le code est disponible sur Github.
Pour implémenter Vault Credential Broker, on a suivi un tutoriel de la documentation Hashicorp. Dans ce tutoriel, nous avons une base de données PostgreSQL avec deux rôles : un rôle analyst qui nécessite un accès à une table en écriture pour créer des rapports mensuels, et un rôle dba (database administrator) qui doit avoir tous les droits sur la base de données pour la gérer.
L’objectif de ce tutoriel est de créer deux targets Boundary qui soient accessibles via des identifiants temporaires émis par Vault : une target avec un accès à la base de données avec le rôle analyst, et une autre avec un accès avec le rôle dba.
Ce que nous avons fait à quand même un peu différé du tutoriel, parce que Vault et Boundary y sont lancés en mode dev, et on a voulu tester en mode prod. On a dû faire pas mal de configurations en plus pour y arriver.
Quand on lance Boundary en prod, il n’y a aucune ressource par défaut et il faut donc créer pas mal de choses avant d’avoir son premier utilisateur. On a suivi un tutoriel qui explique comment créer toutes les ressources nécessaires.
Au final, on a déployé :
L’architecture obtenue est la suivante :
Il y a 3 endpoints :
Si vous vous demandez pourquoi on a exposé publiquement l’endpoint boundary controller cluster, alors que le controller et le worker auraient pu communiquer à l’intérieur du cluster, c’était juste pour tester la configuration du cas où le worker et le controller ne seraient pas dans le même cluster. Ici ce n’est pas vraiment utile.
Zoomons sur la configuration de Vault et Boundary. Côté Vault, on crée un “secrets engine” pour le connecter à la base de données PostgreSQL et y créer des secrets dynamiques.
resource "vault_database_secret_backend_connection" "postgres" {
backend = vault_mount.db.path
name = "postgres"
allowed_roles = ["dba", "analyst"]
plugin_name = "postgresql-database-plugin"
postgresql {
connection_url = "postgresql://${data.terraform_remote_state.main.outputs.rds.this.username}:${data.terraform_remote_state.main.outputs.rds.this.password}@${data.terraform_remote_state.main.outputs.rds.this.address}:5432/postgres"
username = "vault"
password = "vault-password"
}
}
On ajoute ensuite deux “secret backend role”, un pour le rôle analyst et un pour le rôle dba. Ils permettent de spécifier comment créer les identifiants dynamiques pour chacun des rôles.
resource "vault_database_secret_backend_role" "dba" {
backend = vault_mount.db.path
name = "dba"
db_name = vault_database_secret_backend_connection.postgres.name
creation_statements = ["CREATE ROLE \"Boundary et Vault : déploiement de Vault Credential Broker\" WITH LOGIN PASSWORD '' VALID UNTIL '' inherit; grant northwind_dba to \"Boundary et Vault : déploiement de Vault Credential Broker\";"]
}
resource "vault_database_secret_backend_role" "analyst" {
backend = vault_mount.db.path
name = "analyst"
db_name = vault_database_secret_backend_connection.postgres.name
creation_statements = ["CREATE ROLE \"Boundary et Vault : déploiement de Vault Credential Broker\" WITH LOGIN PASSWORD '' VALID UNTIL '' inherit; grant northwind_analyst to \"Boundary et Vault : déploiement de Vault Credential Broker\";"]
}
Pour utiliser Vault en tant que Credential Broker pour Boundary, on crée un token Vault. Ce token permettra à Boundary de demander la création de secrets dynamiques à Vault.
resource "vault_token" "boundary" {
no_default_policy = true
policies = ["boundary-controller", "northwind-database"]
renewable = true
period = "20m"
ttl = "24h"
no_parent = true
metadata = {
"purpose" = "boundary"
}
}
Ce token utilise deux policies. La policy boundary-controller
, qui permet à Boundary d'accéder aux informations de son token, mais aussi de le renouveler ou de le révoquer. La policy northwind-database
, qui permet d’obtenir les informations nécessaires pour se connecter à la base de données avec les rôles analyst et dba.
Côté Boundary, on crée un “credential store” pour stocker des identifiants de manière sécurisée. Ici le credential store est créé pour gérer les secrets de Vault. On indique donc l’adresse de Vault et le token qu’on a créé précédemment.
# Vault credential store
resource "boundary_credential_store_vault" "example" {
name = "vault"
description = "Vault credential store"
address = "http://vault.vault.svc.cluster.local:8200"
token = data.terraform_remote_state.vault.outputs.vault_token.client_token
scope_id = boundary_scope.project.id
}
On crée ensuite deux “credential librairies”, une pour le rôle analyst et une pour le rôle dba. Elles fournissent des identifiants pour les sessions et gèrent la création, le renouvellement et la révocation des secrets dynamiques. Pour chaque credential library, on spécifie le chemin Vault des identifiants pour se connecter à la base de données avec le rôle associé.
# Credential libraries
resource "boundary_credential_library_vault" "dba" {
name = "dba"
description = "Northwind DBA credential library"
credential_store_id = boundary_credential_store_vault.example.id
path = "postgres/creds/dba"
}
resource "boundary_credential_library_vault" "analyst" {
name = "analyst"
description = "Northwind DBA credential analyst"
credential_store_id = boundary_credential_store_vault.example.id
path = "postgres/creds/analyst"
}
Finalement, on peut créer les deux targets, en précisant pour chacune quelle credential library utiliser.
# Targets
resource "boundary_target" "northwind_analyst" {
scope_id = boundary_scope.project.id
name = "Northwind Analyst Database"
type = "tcp"
default_port = "5432"
session_connection_limit = 1
host_source_ids = [boundary_host_set.example.id]
brokered_credential_source_ids = [
boundary_credential_library_vault.analyst.id
]
}
resource "boundary_target" "northwind_dba" {
scope_id = boundary_scope.project.id
name = "Northwind DBA Database"
type = "tcp"
default_port = "5432"
session_connection_limit = 1
host_source_ids = [boundary_host_set.example.id]
brokered_credential_source_ids = [
boundary_credential_library_vault.dba.id
]
}
Une fois configurée, l’utilisation est simple !
On s’authentifie en ligne de commandes à Boundary grâce à la commande boundary authenticate
. Pour cela il faut indiquer l’adresse de l’API du controller. On récupère un token qui nous authentifie auprès de Boundary.
Pour se connecter à une target, il faut d’abord récupérer son id. Cette partie n’est pas très user-friendly en CLI, car elle se fait en 3 commandes, mais ce n’est pas trop compliqué.
boundary scopes list
pour récupérer l’id de l’organisation.boundary scopes list -scope-id $ORG_ID
pour récupérer l’id du projet.boundary targets list -scope-id $PROJECT_ID
et on récupère l’id de la target qui nous intéresse.Une fois qu’on a l’id de la target, il suffit d’utiliser la commande boundary connect
en précisant l’id de la target. Il faut aussi préciser la commande à exécuter sur la machine distante. La CLI de Boundary gère directement certains exécutables. Par exemple, pour se connecter à une base de données PostgreSQL, on peut utiliser la commande boundary connect postgres
.
Pour les exécutables qui ne sont pas gérés par Boundary, il faut installer l’exécutable sur la machine locale et utiliser la commande boundary connect -exec <executable>
. C’est un petit bémol par rapport à un tunnel ssh.
Si on ouvre une session par target et qu’on fait une requête à la base de données pour lister les utilisateurs, on observe les utilisateurs qui ont été créés dynamiquement : v-token-to-analyst-xxxx et v-token-to-dba-xxxx. Ces utilisateurs ont des mots de passes temporaires et sont respectivement membres des rôles analyst et dba.
On a vu dans l’exemple de fonctionnement ci-dessus qu’on peut utiliser Vault Credential Broker pour se connecter à une base de données PostgreSQL, en ligne de commandes, avec des identifiants temporaires.
C’est pratique pour des développeurs qui veulent faire du debug, mais est-ce qu’on peut également l’utiliser pour se connecter à la base de données depuis n’importe quel service ? Par exemple depuis un logiciel d’administration, comme pgadmin ? Après vérification, la réponse est oui !
Il suffit d’utiliser la commande boundary connect
sans préciser d’exécutable. Boundary ouvre alors une socket locale et vous renvoie le port sur lequel elle est ouverte, ainsi que les identifiants temporaires.
Par exemple, on a essayé de se connecter à pgadmin avec ces identifiants :
Et ça fonctionne !
Des développeurs pourraient aussi utiliser cette technique pour utiliser la base de données avec une application en développement.
Dans notre exemple, on a configuré une target qui est une base de données PostgreSQL. Il est aussi possible de configurer d’autres types de targets. Il y a d’autres cas d’utilisation qui sont intéressants :
Plus généralement, vous pouvez utiliser Vault Credential Broker avec une target si Vault dispose d’un Secrets Engine qui peut être utilisé avec votre ressource. Vous pouvez trouver la liste des Secrets Engine qui existent dans la documentation.
Avantages :
✅ Plus de sécurité dans l’accès à vos ressources.
✅ Une fois déployée, la gestion est très facile.
✅ La connexion au target pour les clients est facile.
Désavantages :
❌ L'implémentation nécessite une montée en compétences importante pour comprendre les concepts et configurations nécessaires, en particulier pour Boundary. Cela rend l'expérience d'implémentation complexe lorsqu’on ne connaît pas l’outil.
❌ Configurer Boundary en prod est assez long. Il y a pas mal de documentation à faire pour savoir quelles ressources créer. Pour information, on avait commencé à créer toutes les ressources en lignes de commandes et c’était vraiment très long. On a fini par utiliser le provider Boundary de Terraform et ça facilite la vie !
❌ La CLI peut encore être améliorée ! Pour l’instant, la récupération de l’id des targets se fait en 3 commandes. Il faut aussi entrer l’adresse de l’API Boundary dans chaque commande, ce qui est assez pénible.
Vault Credential Broker est un bon outil pour sécuriser votre infrastructure en ayant une granularité fine dans la gestion des accès à vos ressources. Par contre, il faut pas mal de temps pour le prendre en main et le configurer. C’est donc un outil qu’on ne recommanderait pas pour un petit projet, à moins de l’avoir déjà utilisé et de le maîtriser.
Par contre, il peut être très intéressant à déployer sur un projet où il est possible de consacrer du temps à sa configuration.