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.
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
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
Jonathan Blanchard