Mettre à jour dynamiquement les certificats TLS dans un serveur Golang sans temps d’arrêt


  • Français


  • Transport Layer Security (TLS) est un protocole cryptographique basé sur SSLv3 conçu pour chiffrer et déchiffrer le trafic entre deux sites. En d’autres termes, TLS garantit que vous visitez le site que vous vouliez visiter et empêche quiconque entre vous et le site Web de voir les données échangées. Ceci est réalisé grâce à l’échange mutuel de certificats numériques : un privé qui existe sur le serveur Web et un public généralement distribué avec les navigateurs Web.

    Dans les environnements de production, tous les serveurs fonctionnent en toute sécurité, mais les certificats de serveur peuvent expirer après un certain temps. Il est alors de la responsabilité du serveur de valider, régénérer et réutiliser les certificats nouvellement générés sans aucun temps d’arrêt. Dans cet article, je montre comment les certificats TLS sont mis à jour dynamiquement à l’aide d’un serveur HTTPS dans Go.

    Voici les prérequis pour suivre ce tutoriel :

    • Une compréhension de base des modèles de travail client-serveur
    • Une connaissance de base de Golang

    Configuration d’un serveur HTTP

    Avant de discuter de la mise à jour dynamique des certificats sur un serveur HTTPS, je vais fournir un exemple simple de serveur HTTP. Dans ce cas, je n’ai besoin que du http.ListenAndServe fonction pour démarrer un serveur HTTP et http.HandleFunc pour enregistrer un gestionnaire de réponse pour un point de terminaison particulier.

    Pour commencer, configurez le serveur HTTP :

    package main

    import(
            "net/http"
            "fmt"
            "log"
    )

    func main() {

            mux := http.NewServeMux()
            mux.HandleFunc("https://opensource.com/", func( res http.ResponseWriter, req *http.Request ) {
                    fmt.Fprint( res, "Running HTTP Server!!" )
            })
           
            srv := &http.Server{
                    Addr: fmt.Sprintf(":%d", 8080),
                    Handler:           mux,
            }

            // run server on port "8080"
            log.Fatal(srv.ListenAndServe())
    }

    Dans l’exemple ci-dessus, lorsque j’exécute la commande go run server.goil démarrera un serveur HTTP sur le port 8080. En visitant le http://localhost:8080 URL dans votre navigateur, vous pourrez voir un Hello World! message à l’écran.

    La srv.ListenAndServe() call utilise la configuration de serveur HTTP standard de Go. Cependant, vous pouvez personnaliser un serveur à l’aide d’un Server type de structure.

    Pour démarrer un serveur HTTPS, appelez le srv.ListenAndServeTLS(certFile, keyFile) méthode avec une certaine configuration, tout comme la srv.ListenAndServe() méthode. LaListenAndServe et ListenAndServeTLS sont disponibles à la fois sur le package HTTP et sur le Server structure.

    La ListenAndServeTLS la méthode est comme la ListenAndServe méthode, sauf qu’il démarrera un serveur HTTPS.

    func ListenAndServeTLS(certFile string, keyFile string) error

    Comme vous pouvez le voir dans la signature de la méthode ci-dessus, la seule différence entre cette méthode et la ListenAndServe méthode est le complément certFile et keyFile arguments. Ce sont les chemins vers le Fichier de certificat SSL et Clé privée fichier, respectivement.

    Génération d’une clé privée et d’un certificat SSL

    Suivez ces étapes pour générer une clé racine et un certificat :

    1. Créez la clé racine :

    openssl genrsa -des3 -out rootCA.key 4096

    2. Créez et auto-signez le certificat racine :

    openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 1024 -out rootCA.crt

    Ensuite, suivez ces étapes pour générer un certificat (pour chaque serveur) :

    1. Créez la clé de certificat :

     openssl genrsa -out localhost.key 2048

    2. Créez la demande de signature de certificat (CSR). Le CSR est l’endroit où vous spécifiez les détails du certificat que vous souhaitez générer. Le propriétaire de la clé racine traitera cette demande pour générer le certificat. Lors de la création du CSR, il est important de préciser le Common Name fournir l’adresse IP ou le nom de domaine du service, sinon le certificat ne pourra pas être vérifié.

     openssl req -new -key localhost.key -out localhost.csr

    3. Générez le certificat à l’aide du CSR et de la clé TSL avec la clé CA Root :

      openssl x509 -req -in localhost.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out localhost.crt -days 500 -sha256

    Enfin, suivez les mêmes étapes pour générer des certificats pour chaque serveur afin de générer des certificats pour les clients.

    Configuration d’un serveur HTTPS

    Maintenant que vous disposez à la fois des fichiers de clé privée et de certificat, vous pouvez modifier votre ancien programme Go et utiliser le ListenAndServeTLS méthode à la place.

    package main

    import(
            "net/http"
            "fmt"
            "log"
    )

    func main() {

            mux := http.NewServeMux()
            mux.HandleFunc("https://opensource.com/", func( res http.ResponseWriter, req *http.Request ) {
                    fmt.Fprint( res, "Running HTTPS Server!!" )
            })
           
            srv := &http.Server{
                    Addr: fmt.Sprintf(":%d", 8443),
                    Handler:           mux,
            }

            // run server on port "8443"
            log.Fatal(srv.ListenAndServeTLS("localhost.crt", "localhost.key"))
    }

    Lorsque vous exécutez le programme ci-dessus, il démarre un serveur HTTPS en utilisant localhost.crt comme le certFile et locahost.key comme le keyFile depuis le répertoire courant contenant le fichier programme. En visitant le https://localhost:8443 URL via navigateur ou interface de ligne de commande (CLI), vous pouvez voir le résultat ci-dessous :

    $ curl https://localhost:8443/ --cacert rootCA.crt --key client.key --cert client.crt

    Running HTTPS Server!!

    C’est ça! C’est ce que la plupart des gens doivent faire pour lancer un serveur HTTPS. Go gère le comportement et les fonctionnalités par défaut de la communication TLS.

    Configuration d’un serveur HTTPS pour la mise à jour automatique des certificats

    Lors de l’exécution du serveur HTTPS ci-dessus, vous avez passé certFile et keyFile au ListenAndServeTLS fonction. Cependant, s’il y a un changement dans certFile et keyFile en raison de l’expiration du certificat, le serveur doit être redémarré pour prendre ces effets. Pour surmonter cela, vous pouvez utiliser le TLSConfig de la net/http forfait.

    La TLSConfig structure fournie par le crypto/tls package configure les paramètres TLS du Servery compris les certificats de serveur, entre autres. Tous les domaines de la TLSConfig structure sont facultatives. Par conséquent, l’attribution d’une valeur de structure vide à la TLSConfig champ n’est pas différent de l’attribution d’un nil évaluer. Cependant, le GetCertificate champ peut être très utile.

    type Config struct {
       GetCertificate func(*ClientHelloInfo) (*Certificate, error)
    }

    La GetCertificate domaine de la TLSConfig structure renvoie un certificat basé sur le ClientHelloInfo .

    je dois mettre en oeuvre GetCertificate fonction de fermeture qui utilise tls.LoadX509KeyPair(certFile string, keyFile string) ou tls.X509KeyPair(certFile []byte, keyFile []byte) fonction pour obtenir les certificats.

    Maintenant, je vais créer un Server structure qui utilise TLSConfig valeur du champ. Voici un exemple de format de code :

    package main

    import (
            "crypto/tls"
            "fmt"
            "log"
            "net/http"
    )

    func main() {

            mux := http.NewServeMux()
            mux.HandleFunc("https://opensource.com/", func( res http.ResponseWriter, req *http.Request ) {
                    fmt.Fprint( res, "Running HTTPS Server!!\n" )
            })
           
            srv := &http.Server{
                    Addr: fmt.Sprintf(":%d", 8443),
                    Handler:           mux,
                    TLSConfig: &tls.Config{
                            GetCertificate: func(*tls.ClientHelloInfo) (*tls.Certificate, error) {
                                                   // Always get latest localhost.crt and localhost.key
                                                   // ex: keeping certificates file somewhere in global location where created certificates updated and this closure function can refer that
                                    cert, err := tls.LoadX509KeyPair("localhost.crt", "localhost.key")
                                    if err != nil {
                                            return nil, err
                                    }
                                    return &cert, nil
                            },
                    },
            }

            // run server on port "8443"
            log.Fatal(srv.ListenAndServeTLS("", ""))
    }

    Dans le programme ci-dessus, j’ai implémenté le GetCertificate fonction de fermeture, qui renvoie un objet cert de type Certificate en utilisant le LoadX509KeyPair fonctionner avec le certificat et les fichiers privés créés précédemment. Il renvoie également une erreur, alors manipulez-le avec soin.

    Depuis que je préconfigure l’instance de serveur srv avec une configuration TLS, je n’ai pas besoin de fournir certFile et keyFile valeurs d’argument à la srv.ListenAndServeTLS appel de fonction. Lorsque je lance ce serveur, il fonctionnera comme avant. Mais cette fois, j’ai extrait toute la configuration du serveur de l’invocateur.

    $ curl https://localhost:8443/ --cacert rootCA.crt --key client.key --cert client.crt

    Running HTTPS Server!!

    Dernières pensées

    Quelques conseils supplémentaires en guise de conclusion : pour lire le fichier de certificat et de clé à l’intérieur du GetCertificate fonction, le serveur HTTPS doit fonctionner comme une goroutine. Il y a aussi StackOverflow thread qui fournit d’autres moyens de configuration

    Source

    Houssen Moshinaly

    Pour contacter personnellement le taulier :

    Laisser un commentaire

    Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

    Copy code