From aa984c88435bf8f8e3d41a44a9cce5fdca6df689 Mon Sep 17 00:00:00 2001 From: Ilkka Kaakkola Date: Tue, 22 Jan 2019 21:24:47 +0200 Subject: [PATCH 1/2] support multiple server certificates and keys https://github.com/aakso/ssh-inscribe/issues/1 --- .gitignore | 1 + pkg/server/config.go | 58 +++++++++++++++++++++++++++++++++++++++++--- pkg/server/server.go | 37 +++++++++++++++++++++++++--- 3 files changed, 89 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 2e62d653..8d35a300 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ *~ .DS_Store build +.vscode diff --git a/pkg/server/config.go b/pkg/server/config.go index 60f05c8a..6badf573 100644 --- a/pkg/server/config.go +++ b/pkg/server/config.go @@ -1,11 +1,19 @@ package server import ( + "crypto/tls" "path" "github.com/aakso/ssh-inscribe/pkg/globals" + + "github.com/pkg/errors" ) +type CertificateConfig struct { + Certificates []tls.Certificate + CertificateMap map[string]*tls.Certificate +} + type AuthBackend struct { Type string Config string @@ -16,6 +24,9 @@ type Config struct { Listen string TLSCertFile string `yaml:"TLSCertFile"` TLSKeyFile string `yaml:"TLSKeyFile"` + TLSCertFiles []string `yaml:"TLSCertFiles"` + TLSKeyFiles []string `yaml:"TLSKeyFiles"` + TLSCertNames []string `yaml:"TLSCertNames"` AuthBackends []AuthBackend `yaml:"authBackends"` DefaultAuthBackends []string `yaml:"defaultAuthBackends"` MaxCertLifetime string `yaml:"maxCertLifetime"` @@ -28,9 +39,11 @@ type Config struct { } var Defaults *Config = &Config{ - Listen: ":8540", - TLSCertFile: "", - TLSKeyFile: "", + Listen: ":8540", + TLSCertFile: "", + TLSKeyFile: "", + TLSCertFiles: []string{}, + TLSKeyFiles: []string{}, AuthBackends: []AuthBackend{ AuthBackend{ Type: "authfile", @@ -47,3 +60,42 @@ var Defaults *Config = &Config{ CertSigningKeyFingerprint: "", TokenSigningKey: "", } + +func (c Config) GetCertificateMap() (cc CertificateConfig, err error) { + cc = CertificateConfig{ + Certificates: []tls.Certificate{}, + CertificateMap: make(map[string]*tls.Certificate), + } + + // Single certificate has no name configuration + if c.TLSCertFile != "" && c.TLSKeyFile != "" { + if len(c.TLSCertFiles) > 0 { + return cc, errors.New("Unsupported configuration, either set TLSCertFile or TLSCertFiles, not both") + } + + var certificate, err = tls.LoadX509KeyPair(c.TLSCertFile, c.TLSKeyFile) + if err != nil { + return cc, err + } + cc.Certificates = append(cc.Certificates, certificate) + return cc, nil + } + + if len(c.TLSCertFiles) > 0 && len(c.TLSKeyFiles) > 0 && len(c.TLSCertNames) > 0 { + if (len(c.TLSCertFiles) != len(c.TLSKeyFiles)) || (len(c.TLSCertFiles) != len(c.TLSCertNames)) { + return cc, errors.New("TLSCertFiles, TLSKeyFiles and TLSCertNames must contain same number of elements") + } + + cc.Certificates = make([]tls.Certificate, len(c.TLSCertFiles)) + for index, cert := range c.TLSCertFiles { + var certificate, err = tls.LoadX509KeyPair(cert, c.TLSKeyFiles[index]) + if err != nil { + return cc, err + } + cc.Certificates[index] = certificate + cc.CertificateMap[c.TLSCertNames[index]] = &cc.Certificates[index] + } + return cc, nil + } + return cc, nil +} diff --git a/pkg/server/server.go b/pkg/server/server.go index 41e632ab..875716a8 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -1,6 +1,7 @@ package server import ( + "crypto/tls" "fmt" "io/ioutil" "net/http" @@ -32,13 +33,41 @@ func (s *Server) Start() error { var err error log := Log.WithField("server_version", globals.Version()) s.web.Logger.SetOutput(ioutil.Discard) - certFile, keyFile := s.config.TLSCertFile, s.config.TLSKeyFile - if certFile != "" && keyFile != "" { - log.WithField("listen", fmt.Sprintf("https://%s", s.config.Listen)).Info("server starting") - err = s.web.StartTLS(s.config.Listen, certFile, keyFile) + + cc, err := s.config.GetCertificateMap() + if err != nil { + return errors.Wrap(err, "invalid certificate configuration") + } + + if len(cc.Certificates) > 0 { + // Configure TLSServer before starting + tlsServer := s.web.TLSServer + tlsServer.TLSConfig = new(tls.Config) + + if len(cc.Certificates) == 1 { + tlsServer.TLSConfig.Certificates = make([]tls.Certificate, 1) + tlsServer.TLSConfig.Certificates[0] = cc.Certificates[0] + } else { + tlsServer.TLSConfig.NameToCertificate = cc.CertificateMap + tlsServer.TLSConfig.Certificates = cc.Certificates + } + + tlsServer.Addr = s.config.Listen + if !s.web.DisableHTTP2 { + tlsServer.TLSConfig.NextProtos = append(tlsServer.TLSConfig.NextProtos, "h2") + } + log.WithField("listen", fmt.Sprintf("https://%s", s.config.Listen)).WithField( + "certificates", fmt.Sprintf("%d", len(cc.Certificates))).Info("server starting") + + err = s.web.StartServer(tlsServer) + if err != nil { + return errors.Wrap(err, "cannot start server") + } + log.WithField("listen", fmt.Sprintf("https://%s", s.config.Listen)).Info("server terminated") } else { log.WithField("listen", fmt.Sprintf("http://%s", s.config.Listen)).Warn("server starting without TLS") err = s.web.Start(s.config.Listen) + log.WithField("listen", fmt.Sprintf("http://%s", s.config.Listen)).Info("server terminated") } if err != nil { return errors.Wrap(err, "cannot start server") From 2a180eca9a2032aced03ea178ef915c9a7e897fa Mon Sep 17 00:00:00 2001 From: Ilkka Kaakkola Date: Wed, 23 Jan 2019 10:56:54 +0200 Subject: [PATCH 2/2] removed unnecessary logging --- pkg/server/server.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/pkg/server/server.go b/pkg/server/server.go index 875716a8..394a9f1b 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -63,11 +63,9 @@ func (s *Server) Start() error { if err != nil { return errors.Wrap(err, "cannot start server") } - log.WithField("listen", fmt.Sprintf("https://%s", s.config.Listen)).Info("server terminated") } else { log.WithField("listen", fmt.Sprintf("http://%s", s.config.Listen)).Warn("server starting without TLS") err = s.web.Start(s.config.Listen) - log.WithField("listen", fmt.Sprintf("http://%s", s.config.Listen)).Info("server terminated") } if err != nil { return errors.Wrap(err, "cannot start server")