github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/httpserver/tls.go (about) 1 // Copyright 2018 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package httpserver 5 6 import ( 7 "crypto/tls" 8 "net" 9 "strings" 10 11 "github.com/juju/errors" 12 "github.com/juju/utils" 13 "golang.org/x/crypto/acme" 14 "golang.org/x/crypto/acme/autocert" 15 16 "github.com/juju/juju/state" 17 ) 18 19 // NewTLSConfig returns the TLS configuration for the HTTP server to use 20 // based on controller configuration stored in the state database. 21 func NewTLSConfig(st *state.State, getCertificate func() *tls.Certificate) (*tls.Config, error) { 22 controllerConfig, err := st.ControllerConfig() 23 if err != nil { 24 return nil, errors.Trace(err) 25 } 26 return newTLSConfig( 27 controllerConfig.AutocertDNSName(), 28 controllerConfig.AutocertURL(), 29 st.AutocertCache(), 30 getCertificate, 31 ), nil 32 } 33 34 func newTLSConfig( 35 autocertDNSName, autocertURL string, 36 autocertCache autocert.Cache, 37 getLocalCertificate func() *tls.Certificate, 38 ) *tls.Config { 39 // localCertificate calls getLocalCertificate, returning the result 40 // and reporting whether it should be used to serve a connection 41 // addressed to the given server name. 42 localCertificate := func(serverName string) (*tls.Certificate, bool) { 43 cert := getLocalCertificate() 44 if net.ParseIP(serverName) != nil { 45 // IP address connections always use the local certificate. 46 return cert, true 47 } 48 if !strings.Contains(serverName, ".") { 49 // If the server name doesn't contain a period there's no 50 // way we can obtain a certificate for it. 51 // This applies to the common case where "juju-apiserver" is 52 // used as the server name. 53 return cert, true 54 } 55 // Perhaps the server name is explicitly mentioned by the server certificate. 56 for _, name := range cert.Leaf.DNSNames { 57 if name == serverName { 58 return cert, true 59 } 60 } 61 return cert, false 62 } 63 64 tlsConfig := utils.SecureTLSConfig() 65 if autocertDNSName == "" { 66 // No official DNS name, no certificate. 67 tlsConfig.GetCertificate = func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) { 68 cert, _ := localCertificate(clientHello.ServerName) 69 return cert, nil 70 } 71 return tlsConfig 72 } 73 m := autocert.Manager{ 74 Prompt: autocert.AcceptTOS, 75 Cache: autocertCache, 76 HostPolicy: autocert.HostWhitelist(autocertDNSName), 77 } 78 if autocertURL != "" { 79 m.Client = &acme.Client{ 80 DirectoryURL: autocertURL, 81 } 82 } 83 tlsConfig.GetCertificate = func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) { 84 logger.Infof("getting certificate for server name %q", clientHello.ServerName) 85 // Get the locally created certificate and whether it's appropriate 86 // for the SNI name. If not, we'll try to get an acme cert and 87 // fall back to the local certificate if that fails. 88 cert, shouldUse := localCertificate(clientHello.ServerName) 89 if shouldUse { 90 return cert, nil 91 } 92 acmeCert, err := m.GetCertificate(clientHello) 93 if err == nil { 94 return acmeCert, nil 95 } 96 logger.Errorf("cannot get autocert certificate for %q: %v", clientHello.ServerName, err) 97 return cert, nil 98 } 99 tlsConfig.NextProtos = []string{ 100 "h2", "http/1.1", // Enable HTTP/2. 101 acme.ALPNProto, // Enable TLS-ALPN ACME challenges. 102 } 103 return tlsConfig 104 }