Sécurité & Authentification

6 min de lecture

Sécurité & Authentification

PgArachne s’appuie entièrement sur le système de permission de PostgreSQL. Il ne réinvente pas les listes de contrôle d’accès (ACL). PgArachne prend en charge quatre méthodes d’authentification. Les méthodes 1 à 3 utilisent SET LOCAL ROLE pour changer d’identité ; la méthode 4 (identifiants directs) se connecte directement en tant que l’utilisateur, sans changement de rôle nécessaire.

Cela s’applique aux endpoints JSON-RPC et MCP. L’endpoint SSE est la seule exception — il authentifie l’appelant mais ne change pas de rôle et ne vérifie pas les permissions par canal, car les canaux LISTEN/NOTIFY de PostgreSQL ne sont pas des objets de base de données avec leurs propres GRANTs à faire respecter. Voir la page SSE pour ce que cela signifie en pratique.

1. Connexion interactive (JWT)

Les utilisateurs s’authentifient en utilisant leur vrai nom d’utilisateur et mot de passe PostgreSQL par authentification HTTP Basic sur POST /{prefix}/{database}/token. En cas de succès, ils reçoivent un JWT de courte durée. Les requêtes suivantes portent ce jeton dans l’en-tête Authorization: Bearer <token>, et PgArachne bascule le rôle actif vers cet utilisateur pour la durée de chaque requête.

curl -X POST http://localhost:8080/db/my_database/token -u demo_user:user_password
→ {"token":"eyJhbGciOi...","token_type":"Bearer","expires_in":28800}

Les identifiants voyagent dans l’en-tête Authorization, jamais dans un corps JSON, et la connexion a sa propre URL, de sorte qu’un reverse proxy peut la limiter en débit sans lire les corps de requête. La réponse n’est jamais mise en cache.

Nécessite JWT_SECRET. S’il n’est pas configuré, l’endpoint renvoie HTTP 404 (-32601) et les clients utilisent à la place les identifiants directs (méthode 4) ou les tokens API (méthode 2).

2. Comptes de service (Tokens API)

Pour les systèmes automatisés ou les scripts, vous pouvez utiliser des tokens API longue durée.

  • Les jetons sont stockés dans la table pgarachne.api_tokens.
  • Chaque jeton est mappé à un utilisateur/rôle de base de données spécifique.
  • Envoyez le jeton via l’en-tête Authorization: Bearer <token>.

La création de tokens API nécessite pgarachne_admin. Utilisez pgarachne.add_api_token(...) avec un rôle membre de pgarachne_admin.

3. Fournisseur d’identité externe (JWT personnalisé)

Si vous authentifiez les utilisateurs en dehors de PgArachne (par exemple via un service d’authentification externe), vous pouvez émettre les JWT dans ce service et les envoyer directement à PgArachne.

  • Format de l’en-tête : Authorization: Bearer <jwt>.
  • Signature : HMAC uniquement (HS256/HS384/HS512) avec le même JWT_SECRET que celui configuré dans PgArachne.
  • Claims requis : db_role (chaîne non vide) et db_name (chaîne qui doit correspondre au segment de chemin :database).
  • Claim requis : exp (timestamp Unix) pour l’expiration du token.

Nécessite JWT_SECRET ; sans lui, les valeurs Bearer sont uniquement vérifiées comme tokens API.

Exemple minimal de payload :

{
  "db_role": "demo_user",
  "db_name": "my_database",
  "exp": 1767225600
}

Note : les algorithmes JWT asymétriques (par exemple RS256/ES256) ne sont pas pris en charge.

4. Identifiants directs de base de données (Basic Auth)

L’option la plus simple : envoyez le nom d’utilisateur et le mot de passe PostgreSQL avec chaque requête via l’authentification HTTP Basic standard. PgArachne ouvre un pool de connexions dédié authentifié directement en tant que cet utilisateur — SET LOCAL ROLE n’est pas effectué.

Authorization: Basic <base64(username:password)>

Exemple avec curl :

curl -X POST http://localhost:8080/db/my_database/jsonrpc \
  -u demo_user:user_password \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"api.hello_world","params":{},"id":1}'
Quand utiliser les identifiants directs :
  • Prototypage rapide et développement local — aucune gestion de jetons requise.
  • Services internes qui gèrent déjà les identifiants de manière sécurisée.
  • Scénarios où émettre un JWT au préalable ajoute une complexité inutile.

Différences clés par rapport aux méthodes basées sur les jetons :

  • Aucun GRANT demo_user TO pgarachne n’est requis — PgArachne ne change pas de rôle.
  • La sécurité au niveau des lignes (Row-Level Security) et les permissions GRANT de PostgreSQL sont appliquées comme d’habitude car la connexion s’exécute sous l’identité de l’utilisateur réel.
  • Les pools de connexions sont conservés par utilisateur pour l’efficacité, avec une durée de vie maximale de 5 minutes. Après un changement de mot de passe, les anciennes connexions expirent dans cette fenêtre.
  • Pour que les clés d’idempotence fonctionnent avec les identifiants directs, accordez à l’utilisateur EXECUTE ON FUNCTION pgarachne.save_idempotency_key(text, text).

Privilèges Proxy : requis uniquement pour les méthodes 1 à 3

Pour l’authentification par JWT et par jeton API, PgArachne se connecte en tant qu’utilisateur système défini dans DB_USER (ex : pgarachne) et change d’identité via SET LOCAL ROLE. L’utilisateur système doit être membre de chaque rôle cible.

-- Required for JWT / API-token auth only:
GRANT demo_user TO pgarachne;

Ce grant n’est pas nécessaire lors de l’utilisation des identifiants directs (méthode 4).

Tableau comparatif des méthodes d’authentification

MéthodeEn-têteSET LOCAL ROLEGRANT … TO pgarachneIdéal pour
JWT (/token)Bearer <jwt>✅ OuiRequisSessions utilisateur
Jeton APIBearer <token>✅ OuiRequisServices automatisés
JWT externeBearer <jwt>✅ OuiRequisIntégration IdP externe
Identifiants directsBasic <b64>❌ NonNon requisDéveloppement, services internes

Quelles fonctions peuvent être appelées

PgArachne n’a pas de liste blanche de schémas qui lui soit propre — ce qu’un rôle peut appeler est entièrement déterminé par les privilèges PostgreSQL. Une fonction est appelable (et listée par capabilities, par MCP tools/list et par l’export OpenAPI) lorsqu’elle prend un unique paramètre jsonb, que le rôle dispose de EXECUTE sur elle et que le rôle dispose de USAGE sur son schéma. Les fonctions de pg_catalog, information_schema, pgarachne ainsi que celles appartenant à des extensions sont omises de la liste pour la garder lisible, mais les mêmes privilèges s’y appliquent.

Les valeurs par défaut de PostgreSQL sont permissives

Par défaut, PostgreSQL accorde EXECUTE sur chaque nouvelle fonction à PUBLIC, et chaque rôle dispose de USAGE sur le schéma public. Une fonction utilitaire public.something(jsonb) est donc appelable via l’API par n’importe quel rôle authentifié, sauf si vous révoquez ce privilège. Configuration recommandée :

-- New functions are not executable by everyone (run as the role that creates them):
ALTER DEFAULT PRIVILEGES REVOKE EXECUTE ON FUNCTIONS FROM PUBLIC;

-- Keep API functions in a dedicated schema and grant explicitly:
GRANT USAGE ON SCHEMA api TO demo_user;
GRANT EXECUTE ON FUNCTION api.hello_world(jsonb) TO demo_user;

Protection contre la force brute

LOGIN_RATE_LIMIT et LOGIN_RATE_LIMIT_PER_IP s’appliquent à toutes les méthodes d’authentification, sur tous les endpoints (JSON-RPC, MCP, SSE, OpenAPI) :

  • /token : chaque tentative est décomptée des budgets par (IP, nom d’utilisateur) et par IP.
  • Identifiants directs (Basic Auth) : les tentatives échouées sont décomptées des budgets par (IP, nom d’utilisateur) et par IP. Les requêtes réussies ne sont pas comptées : un client légitime qui envoie ses identifiants à chaque requête n’est donc jamais limité.
  • Jetons Bearer (JWT ou token API) : les tentatives échouées sont décomptées du budget par IP.

Une fois un budget épuisé, les tentatives suivantes sont rejetées avec HTTP 429 jusqu’à la fin de la fenêtre (LOGIN_RATE_WINDOW) — sans même vérifier les identifiants.