有没有办法在网络/ http服务器中更新TLS证书而没有任何停机时间? [英] Is there a way to update the TLS certificates in a net/http server without any downtime?

查看:176
本文介绍了有没有办法在网络/ http服务器中更新TLS证书而没有任何停机时间?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个简单的https服务器,像这样提供一个简单的页面(为简洁起见,不需要错误处理):

I have a simple https server serving a simple page like so (no error handling for brevity):

package main

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

func main() {
    mux := http.NewServeMux()

    mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
        fmt.Fprintf(w, "hello!")
    })

    xcert, _ := tls.LoadX509KeyPair("cert1.crt", "key1.pem")

    tlsConf := &tls.Config{
        Certificates: []tls.Certificate{xcert},
    }

    srv := &http.Server{
        Addr:      ":https",
        Handler:   mux,
        TLSConfig: tlsConf,
    }

    srv.ListenAndServeTLS("", "")
}

我想使用让我们加密 TLS证书以通过https提供内容。我希望能够在没有任何停机时间的情况下执行证书续订并更新服务器中的证书。

I want to use a Let's Encrypt TLS certificate to serve the content over https. I would like to be able to do certificate renewals and update the certificate in the server without any downtime.

我尝试运行goroutine来更新 tlsConf

I tried running a goroutine to update the tlsConf:

go func(c *tls.Config) {
        xcert, _ := tls.LoadX509KeyPair("cert2.crt", "key2.pem")

        select {
        case <-time.After(3 * time.Minute):
            c.Certificates = []tls.Certificate{xcert}
            c.BuildNameToCertificate()
            fmt.Println("cert switched!")
        }

    }(tlsConf)

然而,这不起作用,因为服务器没有读入配置。有没有办法要求服务器重新加载 TLSConfig

However, that doesn't work because the server does not "read in" the changed config. Is there anyway to ask the server to reload the TLSConfig?

推荐答案

有:您可以使用 tls.Config GetCertificate 成员,而不是填充证书。首先,定义一个封装证书和重载功能的数据结构(在本例中接收 SIGHUP 信号):

There is: you can use tls.Config’s GetCertificate member instead of populating Certificates. First, define a data structure that encapsulates the certificate and reload functionality (on receiving the SIGHUP signal in this example):

type keypairReloader struct {
        certMu   sync.RWMutex
        cert     *tls.Certificate
        certPath string
        keyPath  string
}

func NewKeypairReloader(certPath, keyPath string) (*keypairReloader, error) { 
        result := &keypairReloader{
                certPath: certPath,
                keyPath:  keyPath,
        }
        cert, err := tls.LoadX509KeyPair(certPath, keyPath)
        if err != nil {
                return nil, err
        }
        result.cert = &cert
        go func() {
                c := make(chan os.Signal, 1)
                signal.Notify(c, syscall.SIGHUP)
                for range c {
                        log.Printf("Received SIGHUP, reloading TLS certificate and key from %q and %q", *tlsCertPath, *tlsKeyPath)
                        if err := result.maybeReload(); err != nil {
                                log.Printf("Keeping old TLS certificate because the new one could not be loaded: %v", err)
                        }
                }
        }()
        return result, nil
}

func (kpr *keypairReloader) maybeReload() error { 
        newCert, err := tls.LoadX509KeyPair(kpr.certPath, kpr.keyPath)
        if err != nil {
                return err
        }
        kpr.certMu.Lock()
        defer kpr.certMu.Unlock()
        kpr.cert = &newCert
        return nil
}

func (kpr *keypairReloader) GetCertificateFunc() func(*tls.ClientHelloInfo) (*tls.Certificate, error) { 
        return func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
                kpr.certMu.RLock()
                defer kpr.certMu.RUnlock()
                return kpr.cert, nil
        }
}

然后,在你的服务器代码使用:

Then, in your server code, use:

kpr, err := NewKeypairReloader(*tlsCertPath, *tlsKeyPath)
if err != nil {
    log.Fatal(err)
}
srv.TLSConfig.GetCertificate = kpr.GetCertificateFunc()

我最近在RobustIRC中实现了这种模式

I recently implemented this pattern in RobustIRC.

这篇关于有没有办法在网络/ http服务器中更新TLS证书而没有任何停机时间?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆