From 14d89f304ad022bb61158d498869d63bc5109e7a Mon Sep 17 00:00:00 2001 From: Adnan Maolood Date: Thu, 14 Jan 2021 20:42:12 -0500 Subject: [PATCH] Move cert.go to a subpackage --- cert.go => certificate/certificate.go | 98 ++++++++++++--------------- server.go | 7 +- 2 files changed, 49 insertions(+), 56 deletions(-) rename cert.go => certificate/certificate.go (69%) diff --git a/cert.go b/certificate/certificate.go similarity index 69% rename from cert.go rename to certificate/certificate.go index 0c897dd..185ac4d 100644 --- a/cert.go +++ b/certificate/certificate.go @@ -1,4 +1,5 @@ -package gemini +// Package certificate provides utility functions for TLS certificates. +package certificate import ( "crypto" @@ -19,27 +20,23 @@ import ( "time" ) -// CertificateDir maps certificate scopes to certificates. -type CertificateStore map[string]tls.Certificate - -// CertificateDir represents a certificate store optionally loaded from a directory. -// The zero value of CertificateDir is an empty store ready to use. +// Dir represents a directory of certificates. +// The zero value of Dir is an empty directory ready to use. // -// CertificateDir is safe for concurrent use by multiple goroutines. -type CertificateDir struct { - CertificateStore - dir bool - path string - mu sync.RWMutex +// Dir is safe for concurrent use by multiple goroutines. +type Dir struct { + certs map[string]tls.Certificate + path *string + mu sync.RWMutex } -// Add adds a certificate for the given scope to the store. +// Add adds a certificate for the given scope to the directory. // It tries to parse the certificate if it is not already parsed. -func (c *CertificateDir) Add(scope string, cert tls.Certificate) { - c.mu.Lock() - defer c.mu.Unlock() - if c.CertificateStore == nil { - c.CertificateStore = CertificateStore{} +func (d *Dir) Add(scope string, cert tls.Certificate) error { + d.mu.Lock() + defer d.mu.Unlock() + if d.certs == nil { + d.certs = map[string]tls.Certificate{} } // Parse certificate if not already parsed if cert.Leaf == nil { @@ -48,30 +45,26 @@ func (c *CertificateDir) Add(scope string, cert tls.Certificate) { cert.Leaf = parsed } } - c.CertificateStore[scope] = cert -} -// Write writes the provided certificate to the certificate directory. -func (c *CertificateDir) Write(scope string, cert tls.Certificate) error { - c.mu.RLock() - defer c.mu.RUnlock() - if c.dir { + if d.path != nil { // Escape slash character scope = strings.ReplaceAll(scope, "/", ":") - certPath := filepath.Join(c.path, scope+".crt") - keyPath := filepath.Join(c.path, scope+".key") - if err := WriteCertificate(cert, certPath, keyPath); err != nil { + certPath := filepath.Join(*d.path, scope+".crt") + keyPath := filepath.Join(*d.path, scope+".key") + if err := Write(cert, certPath, keyPath); err != nil { return err } } + + d.certs[scope] = cert return nil } -// Lookup returns the certificate for the given scope. -func (c *CertificateDir) Lookup(scope string) (tls.Certificate, bool) { - c.mu.RLock() - defer c.mu.RUnlock() - cert, ok := c.CertificateStore[scope] +// Lookup returns the certificate for the provided scope. +func (d *Dir) Lookup(scope string) (tls.Certificate, bool) { + d.mu.RLock() + defer d.mu.RUnlock() + cert, ok := d.certs[scope] return cert, ok } @@ -81,7 +74,7 @@ func (c *CertificateDir) Lookup(scope string) (tls.Certificate, bool) { // For example, the hostname "localhost" would have the corresponding files // localhost.crt (certificate) and localhost.key (private key). // New certificates will be written to this directory. -func (c *CertificateDir) Load(path string) error { +func (d *Dir) Load(path string) error { matches, err := filepath.Glob(filepath.Join(path, "*.crt")) if err != nil { return err @@ -95,31 +88,30 @@ func (c *CertificateDir) Load(path string) error { scope := strings.TrimSuffix(filepath.Base(crtPath), ".crt") // Unescape slash character scope = strings.ReplaceAll(scope, ":", "/") - c.Add(scope, cert) + d.Add(scope, cert) } - c.SetDir(path) + d.SetPath(path) return nil } -// SetDir sets the directory that new certificates will be written to. -func (c *CertificateDir) SetDir(path string) { - c.mu.Lock() - defer c.mu.Unlock() - c.dir = true - c.path = path +// SetPath sets the directory that new certificates will be written to. +func (d *Dir) SetPath(path string) { + d.mu.Lock() + defer d.mu.Unlock() + d.path = &path } -// CertificateOptions configures the creation of a certificate. -type CertificateOptions struct { - // Subject Alternate Name values. - // Should contain the IP addresses that the certificate is valid for. - IPAddresses []net.IP - +// CreateOptions configures the creation of a TLS certificate. +type CreateOptions struct { // Subject Alternate Name values. // Should contain the DNS names that this certificate is valid for. // E.g. example.com, *.example.com DNSNames []string + // Subject Alternate Name values. + // Should contain the IP addresses that the certificate is valid for. + IPAddresses []net.IP + // Subject specifies the certificate Subject. // // Subject.CommonName can contain the DNS name that this certificate @@ -136,8 +128,8 @@ type CertificateOptions struct { Ed25519 bool } -// CreateCertificate creates a new TLS certificate. -func CreateCertificate(options CertificateOptions) (tls.Certificate, error) { +// Create creates a new TLS certificate. +func Create(options CreateOptions) (tls.Certificate, error) { crt, priv, err := newX509KeyPair(options) if err != nil { return tls.Certificate{}, err @@ -150,7 +142,7 @@ func CreateCertificate(options CertificateOptions) (tls.Certificate, error) { } // newX509KeyPair creates and returns a new certificate and private key. -func newX509KeyPair(options CertificateOptions) (*x509.Certificate, crypto.PrivateKey, error) { +func newX509KeyPair(options CreateOptions) (*x509.Certificate, crypto.PrivateKey, error) { var pub crypto.PublicKey var priv crypto.PrivateKey if options.Ed25519 { @@ -206,9 +198,9 @@ func newX509KeyPair(options CertificateOptions) (*x509.Certificate, crypto.Priva return cert, priv, nil } -// WriteCertificate writes the provided certificate and private key +// Write writes the provided certificate and its private key // to certPath and keyPath respectively. -func WriteCertificate(cert tls.Certificate, certPath, keyPath string) error { +func Write(cert tls.Certificate, certPath, keyPath string) error { certOut, err := os.OpenFile(certPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) if err != nil { return err diff --git a/server.go b/server.go index 8223cbf..97f6ed3 100644 --- a/server.go +++ b/server.go @@ -7,6 +7,8 @@ import ( "net" "strings" "time" + + "git.sr.ht/~adnano/go-gemini/certificate" ) // Server is a Gemini server. @@ -23,7 +25,7 @@ type Server struct { WriteTimeout time.Duration // Certificates contains the certificates used by the server. - Certificates CertificateDir + Certificates certificate.Dir // CreateCertificate, if not nil, will be called to create a new certificate // if the current one is expired or missing. @@ -157,8 +159,7 @@ func (s *Server) getCertificateFor(hostname string) (*tls.Certificate, error) { if s.CreateCertificate != nil { cert, err := s.CreateCertificate(hostname) if err == nil { - s.Certificates.Add(hostname, cert) - if err := s.Certificates.Write(hostname, cert); err != nil { + if err := s.Certificates.Add(hostname, cert); err != nil { s.logf("gemini: Failed to write new certificate for %s: %s", hostname, err) } }