我有一个简单的https服务器,提供了一个简单的页面,例如(简洁起见,没有错误处理):
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提供内容。我希望能够进行证书续订并更新服务器中的证书,而无需停机。
我尝试运行goroutine更新tlsConf:
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?
TLSConfig
有:您可以使用tls.Config的GetCertificate成员而不是填充Certificates。首先,定义一个封装证书和重载功能的数据结构(SIGHUP在此示例中,在接收到信号时):
tls.Config
GetCertificate
Certificates
SIGHUP
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 } }
然后,在您的服务器代码中,使用:
kpr, err := NewKeypairReloader(*tlsCertPath, *tlsKeyPath) if err != nil { log.Fatal(err) } srv.TLSConfig.GetCertificate = kpr.GetCertificateFunc()
我最近在RobustIRC中实现了这种模式。