diff --git a/configuration/configuration.go b/configuration/configuration.go index 59c90fde5..46f7a01f0 100644 --- a/configuration/configuration.go +++ b/configuration/configuration.go @@ -95,6 +95,19 @@ type Configuration struct { // Specifies the CA certs for client authentication // A file may contain multiple CA certificates encoded as PEM ClientCAs []string `yaml:"clientcas,omitempty"` + + // LetsEncrypt is used to configuration setting up TLS through + // Let's Encrypt instead of manually specifying certificate and + // key. If a TLS certificate is specified, the Let's Encrypt + // section will not be used. + LetsEncrypt struct { + // CacheFile specifies cache file to use for lets encrypt + // certificates and keys. + CacheFile string `yaml:"cachefile,omitempty"` + + // Email is the email to use during Let's Encrypt registration + Email string `yaml:"email,omitempty"` + } `yaml:"letsencrypt,omitempty"` } `yaml:"tls,omitempty"` // Headers is a set of headers to include in HTTP responses. A common diff --git a/configuration/configuration_test.go b/configuration/configuration_test.go index f406a727f..86f048ad9 100644 --- a/configuration/configuration_test.go +++ b/configuration/configuration_test.go @@ -73,6 +73,10 @@ var configStruct = Configuration{ Certificate string `yaml:"certificate,omitempty"` Key string `yaml:"key,omitempty"` ClientCAs []string `yaml:"clientcas,omitempty"` + LetsEncrypt struct { + CacheFile string `yaml:"cachefile,omitempty"` + Email string `yaml:"email,omitempty"` + } `yaml:"letsencrypt,omitempty"` } `yaml:"tls,omitempty"` Headers http.Header `yaml:"headers,omitempty"` Debug struct { @@ -83,6 +87,10 @@ var configStruct = Configuration{ Certificate string `yaml:"certificate,omitempty"` Key string `yaml:"key,omitempty"` ClientCAs []string `yaml:"clientcas,omitempty"` + LetsEncrypt struct { + CacheFile string `yaml:"cachefile,omitempty"` + Email string `yaml:"email,omitempty"` + } `yaml:"letsencrypt,omitempty"` }{ ClientCAs: []string{"/path/to/ca.pem"}, }, diff --git a/registry/registry.go b/registry/registry.go index aec6a030f..559f724c3 100644 --- a/registry/registry.go +++ b/registry/registry.go @@ -9,6 +9,8 @@ import ( "os" "time" + "rsc.io/letsencrypt" + log "github.com/Sirupsen/logrus" "github.com/Sirupsen/logrus/formatters/logstash" "github.com/bugsnag/bugsnag-go" @@ -111,11 +113,10 @@ func (registry *Registry) ListenAndServe() error { return err } - if config.HTTP.TLS.Certificate != "" { + if config.HTTP.TLS.Certificate != "" || config.HTTP.TLS.LetsEncrypt.CacheFile != "" { tlsConf := &tls.Config{ ClientAuth: tls.NoClientCert, NextProtos: []string{"http/1.1"}, - Certificates: make([]tls.Certificate, 1), MinVersion: tls.VersionTLS10, PreferServerCipherSuites: true, CipherSuites: []uint16{ @@ -130,9 +131,26 @@ func (registry *Registry) ListenAndServe() error { }, } - tlsConf.Certificates[0], err = tls.LoadX509KeyPair(config.HTTP.TLS.Certificate, config.HTTP.TLS.Key) - if err != nil { - return err + if config.HTTP.TLS.LetsEncrypt.CacheFile != "" { + if config.HTTP.TLS.Certificate != "" { + return fmt.Errorf("cannot specify both certificate and Let's Encrypt") + } + var m letsencrypt.Manager + if err := m.CacheFile(config.HTTP.TLS.LetsEncrypt.CacheFile); err != nil { + return err + } + if !m.Registered() { + if err := m.Register(config.HTTP.TLS.LetsEncrypt.Email, nil); err != nil { + return err + } + } + tlsConf.GetCertificate = m.GetCertificate + } else { + tlsConf.Certificates = make([]tls.Certificate, 1) + tlsConf.Certificates[0], err = tls.LoadX509KeyPair(config.HTTP.TLS.Certificate, config.HTTP.TLS.Key) + if err != nil { + return err + } } if len(config.HTTP.TLS.ClientCAs) != 0 {