Tous les projets

Janus

Une frontière de secrets auto-hébergée. Les applications s’authentifient auprès de Janus, et Janus seul lit le secret amont dans OpenBao puis l’ajoute au passage. Un monolithe modulaire en Java 25 et Spring Boot 4, avec une console React pour les identités, les connexions et les autorisations. Il tourne en production, et mon autre application en dépend.

Statut
Maintenu, en production
Rôle
Développeur unique, du modèle de sécurité à la production.
Présentation
0:00 / 0:00

Le problème initial

Une clé d’API est copiée dans un projet, commitée par accident, et jamais tournée. Toute application qui détient une clé peut la fuiter. Et rien ne dit quel service a appelé quoi, ni à quelle fréquence.

Solution imaginée

Un proxy authentifié. L’appelant prouve son identité, Janus vérifie l’autorisation, applique le quota, lit le secret dans OpenBao et transmet la requête. Le secret ne repasse jamais dans l’autre sens : ni dans une réponse, ni dans un log, ni dans un message d’erreur. Chaque appel est audité sous un identifiant de corrélation.

Choix importants

Java 25 et Spring Boot 4, le LTS courant. Éprouvé sans enjeu, avant que la production n’impose la montée.

Un monolithe modulaire, pas des microservices. Un seul artefact à déployer. Le besoin ne justifiait pas la complexité.

Une autorisation admet par défaut tout ce qui est sous un slug. Une liste blanche ne serait qu’une seconde copie, plus périmée, de ce que l’amont applique déjà. Le cloisonnement existe pour les clés incapables de dire non elles-mêmes.

Architecture

Votre application

détient une clé d’appelant, jamais celle de l’API

la requête

La clé de l’API n’est lisible qu’ici

Janus

vérifie l’identité, puis l’autorisation, puis le quota

lit la clé

OpenBao

détient la clé de l’API

la même requête, clé ajoutée

L’API tierce

TMDB, et tout ce qui est enregistré

la réponse revient expurgée, sous un identifiant de corrélation

L’application s’authentifie auprès de Janus, pas auprès de l’API. Rien de ce qui revient ne transporte la clé : ni une réponse, ni un journal, ni un message d’erreur.

Un monolithe modulaire, en couches du contrôleur au service puis au dépôt. La classe HTTP ne décide de rien, le service porte la transaction et l’enregistrement d’audit, et les entités font respecter leurs propres invariants. Deux chaînes de sécurité indépendantes : une pour la console, une pour la passerelle. PostgreSQL contient les identités, les connexions, les autorisations et les audits, jamais un secret en clair. Les appels proxifiés tournent sur des threads virtuels, donc la concurrence est bornée par le pool de la base plutôt que par le conteneur de servlets.

Je suis passé à Java 25 et Spring Boot 4 quand rien n’était en jeu, pour que la montée de version n’arrive pas plus tard en urgence. Ça tourne sur un serveur que j’administre, et quand ça casse, c’est moi qui lis les logs.

Ce qui reste

Le projet est maintenu, en production.

Publier de vraies captures de la console

Documenter le modèle de menaces sur une page dédiée

Élargir la couverture sur les chemins d’échange de jetons

Technologies

Java 25 · Spring Boot 4 · Virtual threads · React 19 · TypeScript · Vite · Tailwind CSS · PostgreSQL · OpenBao · Maven · Docker Compose · Traefik · nginx · GitLab CI

Et si nous travaillions ensemble ?

Vous recrutez un alternant full-stack ? Je suis à un e-mail.