This adds a certificate lifetime parameter to our certificate generation and hard codes it to twenty years in some uninteresting places. In the main binary there are a couple of constants but it results in twenty years for the device certificate and 820 days for the HTTPS one. 820 is less than the 825 maximum Apple allows nowadays. This also means we must be prepared for certificates to expire, so I add some handling for that and generate a new certificate when needed. For self signed certificates we regenerate a month ahead of time. For other certificates we leave well enough alone.
This commit is contained in:
@@ -9,7 +9,9 @@ package api
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
@@ -56,10 +58,11 @@ import (
|
||||
var bcryptExpr = regexp.MustCompile(`^\$2[aby]\$\d+\$.{50,}`)
|
||||
|
||||
const (
|
||||
DefaultEventMask = events.AllEvents &^ events.LocalChangeDetected &^ events.RemoteChangeDetected
|
||||
DiskEventMask = events.LocalChangeDetected | events.RemoteChangeDetected
|
||||
EventSubBufferSize = 1000
|
||||
defaultEventTimeout = time.Minute
|
||||
DefaultEventMask = events.AllEvents &^ events.LocalChangeDetected &^ events.RemoteChangeDetected
|
||||
DiskEventMask = events.LocalChangeDetected | events.RemoteChangeDetected
|
||||
EventSubBufferSize = 1000
|
||||
defaultEventTimeout = time.Minute
|
||||
httpsCertLifetimeDays = 820
|
||||
)
|
||||
|
||||
type service struct {
|
||||
@@ -146,6 +149,12 @@ func (s *service) getListener(guiCfg config.GUIConfiguration) (net.Listener, err
|
||||
httpsCertFile := locations.Get(locations.HTTPSCertFile)
|
||||
httpsKeyFile := locations.Get(locations.HTTPSKeyFile)
|
||||
cert, err := tls.LoadX509KeyPair(httpsCertFile, httpsKeyFile)
|
||||
|
||||
// If the certificate has expired or will expire in the next month, fail
|
||||
// it and generate a new one.
|
||||
if err == nil {
|
||||
err = checkExpiry(cert)
|
||||
}
|
||||
if err != nil {
|
||||
l.Infoln("Loading HTTPS certificate:", err)
|
||||
l.Infoln("Creating new HTTPS certificate")
|
||||
@@ -158,7 +167,7 @@ func (s *service) getListener(guiCfg config.GUIConfiguration) (net.Listener, err
|
||||
name = s.tlsDefaultCommonName
|
||||
}
|
||||
|
||||
cert, err = tlsutil.NewCertificate(httpsCertFile, httpsKeyFile, name)
|
||||
cert, err = tlsutil.NewCertificate(httpsCertFile, httpsKeyFile, name, httpsCertLifetimeDays)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -1672,3 +1681,45 @@ func addressIsLocalhost(addr string) bool {
|
||||
return ip.IsLoopback()
|
||||
}
|
||||
}
|
||||
|
||||
func checkExpiry(cert tls.Certificate) error {
|
||||
leaf := cert.Leaf
|
||||
if leaf == nil {
|
||||
// Leaf can be nil or not, depending on how parsed the certificate
|
||||
// was when we got it.
|
||||
if len(cert.Certificate) < 1 {
|
||||
// can't happen
|
||||
return errors.New("no certificate in certificate")
|
||||
}
|
||||
var err error
|
||||
leaf, err = x509.ParseCertificate(cert.Certificate[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if leaf.Subject.String() != leaf.Issuer.String() ||
|
||||
len(leaf.DNSNames) != 0 || len(leaf.IPAddresses) != 0 {
|
||||
// The certificate is not self signed, or has DNS/IP attributes we don't
|
||||
// add, so we leave it alone.
|
||||
return nil
|
||||
}
|
||||
|
||||
if leaf.NotAfter.Before(time.Now()) {
|
||||
return errors.New("certificate has expired")
|
||||
}
|
||||
if leaf.NotAfter.Before(time.Now().Add(30 * 24 * time.Hour)) {
|
||||
return errors.New("certificate will soon expire")
|
||||
}
|
||||
|
||||
// On macOS, check for certificates issued on or after July 1st, 2019,
|
||||
// with a longer validity time than 825 days.
|
||||
cutoff := time.Date(2019, 7, 1, 0, 0, 0, 0, time.UTC)
|
||||
if runtime.GOOS == "darwin" &&
|
||||
leaf.NotBefore.After(cutoff) &&
|
||||
leaf.NotAfter.Sub(leaf.NotBefore) > 825*24*time.Hour {
|
||||
return errors.New("certificate incompatible with macOS 10.15 (Catalina)")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user