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

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
Contents
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 mainimport(
"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.go
il 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 mainimport(
"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.crtRunning 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 Server
y 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 mainimport (
"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.crtRunning 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