From bfe10376f391ae7bd132679d7471f63672fec86d Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Thu, 14 Nov 2019 09:34:47 +0100 Subject: [PATCH] lnd: prepare TLS code for extraction --- lnd.go | 96 +++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 65 insertions(+), 31 deletions(-) diff --git a/lnd.go b/lnd.go index f1eb5902..2d9264aa 100644 --- a/lnd.go +++ b/lnd.go @@ -57,8 +57,10 @@ import ( ) const ( - // Make certificate valid for 14 months. - autogenCertValidity = 14 /*months*/ * 30 /*days*/ * 24 * time.Hour + // defaultAutogenCertValidity is the default validity of a self-signed + // certificate. The value corresponds to 14 months + // (14 months * 30 days * 24 hours). + defaultAutogenCertValidity = 14 * 30 * 24 * time.Hour ) var ( @@ -77,7 +79,8 @@ var ( serialNumberLimit = new(big.Int).Lsh(big.NewInt(1), 128) /* - * These cipher suites fit the following criteria: + * tlsCipherSuites is the list of cipher suites we accept for TLS + * connections. These cipher suites fit the following criteria: * - Don't use outdated algorithms like SHA-1 and 3DES * - Don't use ECB mode or other insecure symmetric methods * - Included in the TLS v1.2 suite @@ -661,28 +664,28 @@ func getTLSConfig(tlsCertPath string, tlsKeyPath string, tlsExtraIPs, tlsExtraDomains []string, rpcListeners []net.Addr) (*tls.Config, *credentials.TransportCredentials, string, error) { - // Ensure we create TLS key and certificate if they don't exist + // Ensure we create TLS key and certificate if they don't exist. if !fileExists(tlsCertPath) && !fileExists(tlsKeyPath) { + rpcsLog.Infof("Generating TLS certificates...") err := genCertPair( - tlsCertPath, tlsKeyPath, tlsExtraIPs, tlsExtraDomains, + "lnd autogenerated cert", tlsCertPath, tlsKeyPath, + tlsExtraIPs, tlsExtraDomains, + defaultAutogenCertValidity, ) if err != nil { return nil, nil, "", err } + rpcsLog.Infof("Done generating TLS certificates") } - certData, err := tls.LoadX509KeyPair(tlsCertPath, tlsKeyPath) + certData, parsedCert, err := loadCert(tlsCertPath, tlsKeyPath) if err != nil { return nil, nil, "", err } - cert, err := x509.ParseCertificate(certData.Certificate[0]) - if err != nil { - return nil, nil, "", err - } - - // If the certificate expired, delete it and the TLS key and generate a new pair - if time.Now().After(cert.NotAfter) { + // If the certificate expired, delete it and the TLS key and generate a + // new pair. + if time.Now().After(parsedCert.NotAfter) { ltndLog.Info("TLS certificate is expired, generating a new one") err := os.Remove(tlsCertPath) @@ -695,21 +698,19 @@ func getTLSConfig(tlsCertPath string, tlsKeyPath string, tlsExtraIPs, return nil, nil, "", err } + rpcsLog.Infof("Generating TLS certificates...") err = genCertPair( - tlsCertPath, tlsKeyPath, tlsExtraIPs, tlsExtraDomains, + "lnd autogenerated cert", tlsCertPath, tlsKeyPath, + tlsExtraIPs, tlsExtraDomains, + defaultAutogenCertValidity, ) if err != nil { return nil, nil, "", err } - - } - - tlsCfg := &tls.Config{ - Certificates: []tls.Certificate{certData}, - CipherSuites: tlsCipherSuites, - MinVersion: tls.VersionTLS12, + rpcsLog.Infof("Done generating TLS certificates") } + tlsCfg := tlsConfFromCert(certData) restCreds, err := credentials.NewClientTLSFromFile(tlsCertPath, "") if err != nil { return nil, nil, "", err @@ -742,6 +743,41 @@ func fileExists(name string) bool { return true } +// loadCert loads a certificate and its corresponding private key from the PEM +// files indicated and returns the certificate in the two formats it is most +// commonly used. +func loadCert(certPath, keyPath string) (tls.Certificate, *x509.Certificate, + error) { + + // The certData returned here is just a wrapper around the PEM blocks + // loaded from the file. The PEM is not yet fully parsed but a basic + // check is performed that the certificate and private key actually + // belong together. + certData, err := tls.LoadX509KeyPair(certPath, keyPath) + if err != nil { + return tls.Certificate{}, nil, err + } + + // Now parse the the PEM block of the certificate into its x509 data + // structure so it can be examined in more detail. + x509Cert, err := x509.ParseCertificate(certData.Certificate[0]) + if err != nil { + return tls.Certificate{}, nil, err + } + + return certData, x509Cert, nil +} + +// tLSConfFromCert returns the default TLS configuration used for a server, +// using the given certificate as identity. +func tlsConfFromCert(certData tls.Certificate) *tls.Config { + return &tls.Config{ + Certificates: []tls.Certificate{certData}, + CipherSuites: tlsCipherSuites, + MinVersion: tls.VersionTLS12, + } +} + // genCertPair generates a key/cert pair to the paths provided. The // auto-generated certificates should *not* be used in production for public // access as they're self-signed and don't necessarily contain all of the @@ -750,14 +786,11 @@ func fileExists(name string) bool { // // This function is adapted from https://github.com/btcsuite/btcd and // https://github.com/btcsuite/btcutil -func genCertPair(certFile, keyFile string, tlsExtraIPs, - tlsExtraDomains []string) error { +func genCertPair(org, certFile, keyFile string, tlsExtraIPs, + tlsExtraDomains []string, certValidity time.Duration) error { - rpcsLog.Infof("Generating TLS certificates...") - - org := "lnd autogenerated cert" now := time.Now() - validUntil := now.Add(autogenCertValidity) + validUntil := now.Add(certValidity) // Check that the certificate validity isn't past the ASN.1 end of time. if validUntil.After(endOfTime) { @@ -776,7 +809,7 @@ func genCertPair(certFile, keyFile string, tlsExtraIPs, // addIP appends an IP address only if it isn't already in the slice. addIP := func(ipAddr net.IP) { for _, ip := range ipAddresses { - if bytes.Equal(ip, ipAddr) { + if ip.Equal(ipAddr) { return } } @@ -806,8 +839,10 @@ func genCertPair(certFile, keyFile string, tlsExtraIPs, // Collect the host's names into a slice. host, err := os.Hostname() if err != nil { - rpcsLog.Errorf("Failed getting hostname, falling back to "+ - "localhost: %v", err) + // Nothing much we can do here, other than falling back to + // localhost as fallback. A hostname can still be provided with + // the tlsExtraDomain parameter if the problem persists on a + // system. host = "localhost" } @@ -879,7 +914,6 @@ func genCertPair(certFile, keyFile string, tlsExtraIPs, return err } - rpcsLog.Infof("Done generating TLS certificates") return nil }