Les certificats sont toujours une douleur dans l’environnement de production !
La sécurité est l’une des parties les plus importantes de toute application de nos jours, en particulier le fait que la plupart des applications s’exécutent sur un fournisseur de cloud public donne une priorité plus élevée à la partie sécurité. L’une des façons dont les applications sont utilisées pour sécuriser les communications est le certificat. Le certificat est l’un des concepts qui n’est pas aussi simple que d’autres parties du développement logiciel. Tout d’abord, vous devez comprendre comment un certificat joue dans la partie sécurité pour savoir comment l’intégrer dans la sécurité de votre application. De plus, vous devez savoir comment générer/émettre un nouveau certificat pour votre application.
Malheureusement, la génération de certificats n’est pas un travail ponctuel et a une date d’expiration. Cela signifie donc qu’un nouveau certificat doit être remplacé par le certificat actuel avant la date d’expiration. Dans la plupart des cas, les informations de certificat sont utilisées dans les configurations d’une application déployée en production. Par conséquent, vous devez générer un nouveau certificat et redéployer votre application en production. Cela crée des difficultés pour les équipes logicielles pour voir comment elles peuvent gérer ce problème et justifier le temps d’arrêt de la production s’il n’y a pas de mécanisme de mise à jour continue. Le manque de connaissances et de documentation dans les projets rend souvent cette opération très risquée. Par conséquent, il y a de fortes chances que, même après un nouveau certificat, quelque chose échoue de manière inattendue lors de la production en raison d’une mauvaise configuration. Dans cet article, nous allons voir comment résoudre ce problème sans temps d’arrêt en production tout en utilisant un seul serveur et sans aucun changement au niveau de l’application.
Comment mettre à jour le certificat sur le serveur avec zéro temps d’arrêt
Cet exemple montre un serveur basé sur Java avec le Spring-Boot largement utilisé, cependant, la configuration peut également être appliquée à d’autres serveurs.
Dépendances Maven
Les dépendances Maven suivantes seront utilisées pour les exemples :
Client
Serveur
Spring Boot a un serveur Tomcat intégré qui ne prend pas en charge la configuration qui sera démontrée dans cet exemple. Le serveur Tomcat sera remplacé par Jetty qui a la capacité d’accepter un objet de configuration SSL personnalisé.
Configuration du serveur
Dans ce didacticiel, nous allons démontrer la mise à jour des certificats de serveur via un appel HTTP. Il est également possible de modifier les certificats lorsque les magasins de clés reçoivent des mises à jour sur le système de fichiers avec un écouteur de fichiers, voir ici pour une implémentation de référence : FilesBasedSslUpdateService
Commençons par l’implémentation REST en ajoutant un contrôleur de repos de base pour accéder au serveur :
Le serveur est actuellement accessible sur le port 8080 avec le protocole HTTP. Pour le sécuriser, nous devons ajuster la configuration du serveur. Cela peut être fait en créant une instance de ConfigurableServletWebServerFactory
. Il faut un SslContextFactory
et un port personnalisé. Dans ce cas, 8443
sera utilisé.
SslContextFactory
de Jetty sera construit avec l’extrait suivant :
La SSLFactory utilisera d’abord le identity.jks
, qui contient la paire de clés du serveur, pour créer un KeyManager. Il utilisera également le truststore.jks
, qui contient des certificats de confiance, pour créer un TrustManager. Par défaut, il n’est pas possible de mettre à jour les certificats du serveur au moment de l’exécution sans arrêter le serveur et le redémarrer. Cependant, cette fonction peut être activée en utilisant le withSwappableIdentityMaterial
et withSwappableTrustMaterial
. Ces deux options envelopperont le KeyManager et le TrustManager réels avec un wrapper du même type. Cette classe wrapper spéciale a la capacité d’échanger le KeyManager et le TrustManager sous-jacents avec un nouveau chaque fois que nécessaire. La plupart des serveurs ainsi que les clients HTTP utilisent la configuration SSL de base, telle que SSLContext, SSLSocketFactory, SSLServerSocketFactory et SSLEngine qui ne contiendra que la référence au plus haut niveau de KeyManager et TrustManager, et il est donc possible d’échanger à chaud le un interne.
L’étape suivante consiste à rendre KeyManager, TrustManager et SSLSession disponibles pour un service d’administration qui a la responsabilité de prendre le nouveau matériel SSL et de l’échanger avec la configuration SSL existante.
Les SwappableSslService
prendra un nouveau KeyManager et un nouveau TrustManager. Il l’injectera dans la configuration SSL existante que nous avons créée dans l’extrait précédent et l’ancien sera remplacé. En nettoyant la session SSL, toutes les nouvelles demandes utiliseront les nouveaux certificats. L’étape suivante serait un point d’entrée pour obtenir de nouveaux certificats, il pourrait s’agir d’un écouteur de fichiers ou simplement d’un contrôleur de repos. Dans cet exemple, nous utiliserons un contrôleur de repos de base qui accepte une SSLUpdateRequest. Nous cachons ce point de terminaison derrière le chemin d’administration.
Ce contrôleur d’administration de base a maintenant la capacité de recevoir un nouveau keyStore
en tant que nouvelle identité de serveur et nouvelle trustStore
sous forme de tableau d’octets. Il est capable de traduire ces tableaux d’octets en un objet KeyStore et il le transmettra au SwappableSslService
. La configuration du serveur est terminée et lorsqu’elle est accessible via le navigateur à https://localhost:8443/api/hello
nous obtiendrons le certificat de serveur comme indiqué ci-dessous. Il expirera le 28 avril 2021, nous devons donc le mettre à jour !
Configuration des clients
Le client peut être n’importe quel type d’application capable d’envoyer une requête de publication HTTP au contrôleur d’administration. Il attend une requête JSON qui a la structure du SSLUpdateRequest
comme montré précédemment. Dans notre cas, nous utiliserons le client HTTP par défaut qui est disponible à partir de Java 11. Le client a également besoin de ses propres certificats pour pouvoir communiquer avec le serveur, car le serveur exige que tout client s’authentifie en fonction de ses certificats. Ceci est également appelé authentification mutuelle, alias, SSL bidirectionnel. Si un client n’est pas approuvé par le serveur, il ne peut pas communiquer avec lui. Le matériel SSL d’administration sera chargé et il sera fourni au client HTTP. Ensuite, le nouveau serveur identity.jks
et truststore
sera chargé et il sera envoyé au point de terminaison admin du serveur.
Après avoir exécuté le client, il affichera un 200 comme code d’état, ce qui signifie que je l’ai fait et il semble que tout s’est bien passé ! Lorsque nous actualisons la page de notre navigateur, nous verrons que le certificat du serveur a changé, qu’il a toujours le Root-CA signé mais que le nouveau certificat est valable encore 10 ans. Il expirera le 26 janvier 2031 !
Si vous n’avez pas regardé la vidéo qui était au début, il y a une démo dans la vidéo dans laquelle vous pouvez voir comment fonctionne cette solution.
Avantages de cette solution
Cette solution aide à découpler la mise en œuvre du certificat de la couche application et qui peut être contrôlée séparément du pipeline de développement réel. Cela signifie que chaque fois que des modifications doivent être apportées à la partie du certificat, vous ne modifiez que cette partie sans attendre une version pour ajouter cette modification. Plus important encore, avec cette solution, il n’y aura pas de temps d’arrêt et les utilisateurs finaux ne remarqueront pas le remplacement du certificat. Ce sera une énorme réussite pour les applications où vous n’avez pas de créneaux flexibles pour ce genre de changements.
Bien que cette solution ait été implémentée avec Java, vous devriez pouvoir implémenter ce concept dans n’importe quel langage de programmation.
Comme d’habitude, vous trouverez les sources sur GitHub. Vous pouvez l’essayer et nous attendons vos commentaires avec impatience !