From f67ce4e6d704857eeb313df27c6b44228028b975 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 14 Aug 2017 14:54:06 -0600 Subject: [PATCH] main: fix TLS cert autogen and server configuration --- lnd.go | 167 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 156 insertions(+), 11 deletions(-) diff --git a/lnd.go b/lnd.go index 93b27e34..18bcf313 100644 --- a/lnd.go +++ b/lnd.go @@ -1,9 +1,16 @@ package main import ( + "bytes" "crypto/rand" + "crypto/rsa" + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" "fmt" "io/ioutil" + "math/big" "net" "net/http" _ "net/http/pprof" @@ -33,13 +40,20 @@ import ( ) const ( - autogenCertValidity = 10 * 365 * 24 * time.Hour + // Make certificate valid for 14 months. + autogenCertValidity = 14 /*months*/ * 30 /*days*/ * 24 * time.Hour ) var ( cfg *config shutdownChannel = make(chan struct{}) registeredChains = newChainRegistry() + + // End of ASN.1 time. + endOfTime = time.Date(2049, 12, 31, 23, 59, 59, 0, time.UTC) + + // Max serial number. + serialNumberLimit = new(big.Int).Lsh(big.NewInt(1), 128) ) // lndMain is the true entry point for lnd. This function is required since @@ -229,11 +243,38 @@ func lndMain() error { if err := rpcServer.Start(); err != nil { return err } - sCreds, err := credentials.NewServerTLSFromFile(cfg.TLSCertPath, - cfg.TLSKeyPath) + cert, err := tls.LoadX509KeyPair(cfg.TLSCertPath, cfg.TLSKeyPath) if err != nil { return err } + tlsConf := &tls.Config{ + Certificates: []tls.Certificate{cert}, + /* + * 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 + * - Are available in the Go 1.7.6 standard library (more are + * available in 1.8.3 and will be added after lnd no longer + * supports 1.7, including suites that support CBC mode) + * + * The cipher suites are ordered from strongest to weakest + * primitives, but the client's preference order has more + * effect during negotiation. + **/ + // TODO(aakselrod): add more cipher suites when 1.7 isn't + // supported. + CipherSuites: []uint16{ + tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + tls.TLS_RSA_WITH_AES_256_GCM_SHA384, + tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + tls.TLS_RSA_WITH_AES_128_GCM_SHA256, + }, + MinVersion: tls.VersionTLS12, + } + sCreds := credentials.NewTLS(tlsConf) opts := []grpc.ServerOption{grpc.Creds(sCreds)} grpcServer := grpc.NewServer(opts...) lnrpc.RegisterLightningServer(grpcServer, rpcServer) @@ -268,9 +309,14 @@ func lndMain() error { } go func() { restEndpoint := fmt.Sprintf(":%d", loadedConfig.RESTPort) + listener, err := tls.Listen("tcp", restEndpoint, tlsConf) + if err != nil { + ltndLog.Errorf("gRPC proxy unable to listen on "+ + "localhost%s", restEndpoint) + return + } rpcsLog.Infof("gRPC proxy started at localhost%s", restEndpoint) - http.ListenAndServeTLS(restEndpoint, cfg.TLSCertPath, - cfg.TLSKeyPath, mux) + http.Serve(listener, mux) }() // If we're not in simnet mode, We'll wait until we're fully synced to @@ -379,23 +425,122 @@ func fileExists(name string) bool { return true } -// genCertPair generates a key/cert pair to the paths provided. -// This function is adapted from https://github.com/btcsuite/btcd +// 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 +// desired hostnames for the service. For production/public use, consider a +// real PKI. +// +// This function is adapted from https://github.com/btcsuite/btcd and +// https://github.com/btcsuite/btcutil func genCertPair(certFile, keyFile string) error { rpcsLog.Infof("Generating TLS certificates...") org := "lnd autogenerated cert" - validUntil := time.Now().Add(autogenCertValidity) - cert, key, err := btcutil.NewTLSCertPair(org, validUntil, nil) + now := time.Now() + validUntil := now.Add(autogenCertValidity) + + // Check that the certificate validity isn't past the ASN.1 end of time. + if validUntil.After(endOfTime) { + validUntil = endOfTime + } + + // Generate a serial number that's below the serialNumberLimit. + serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) + if err != nil { + return fmt.Errorf("failed to generate serial number: %s", err) + } + + // Collect the host's IP addresses, including loopback, in a slice. + ipAddresses := []net.IP{net.ParseIP("127.0.0.1"), net.ParseIP("::1")} + + // 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) { + return + } + } + ipAddresses = append(ipAddresses, ipAddr) + } + + // Add all the interface IPs that aren't already in the slice. + addrs, err := net.InterfaceAddrs() + if err != nil { + return err + } + for _, a := range addrs { + ipAddr, _, err := net.ParseCIDR(a.String()) + if err == nil { + addIP(ipAddr) + } + } + + // Collect the host's names into a slice. + host, err := os.Hostname() + if err != nil { + return err + } + dnsNames := []string{host} + if host != "localhost" { + dnsNames = append(dnsNames, "localhost") + } + + // Generate a private key for the certificate. + priv, err := rsa.GenerateKey(rand.Reader, 4096) if err != nil { return err } + // Construct the certificate template. + template := x509.Certificate{ + SerialNumber: serialNumber, + Subject: pkix.Name{ + Organization: []string{org}, + CommonName: host, + }, + NotBefore: now.Add(-time.Hour * 24), + NotAfter: validUntil, + + KeyUsage: x509.KeyUsageKeyEncipherment | + x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, + IsCA: true, // so can sign self. + BasicConstraintsValid: true, + + DNSNames: dnsNames, + IPAddresses: ipAddresses, + + // This signature algorithm is most likely to be compatible + // with clients using less-common TLS libraries like BoringSSL. + SignatureAlgorithm: x509.SHA256WithRSA, + } + + derBytes, err := x509.CreateCertificate(rand.Reader, &template, + &template, &priv.PublicKey, priv) + if err != nil { + return fmt.Errorf("failed to create certificate: %v", err) + } + + certBuf := &bytes.Buffer{} + err = pem.Encode(certBuf, &pem.Block{Type: "CERTIFICATE", + Bytes: derBytes}) + if err != nil { + return fmt.Errorf("failed to encode certificate: %v", err) + } + + keybytes := x509.MarshalPKCS1PrivateKey(priv) + keyBuf := &bytes.Buffer{} + err = pem.Encode(keyBuf, &pem.Block{Type: "RSA PRIVATE KEY", + Bytes: keybytes}) + if err != nil { + return fmt.Errorf("failed to encode private key: %v", err) + } + // Write cert and key files. - if err = ioutil.WriteFile(certFile, cert, 0644); err != nil { + if err = ioutil.WriteFile(certFile, certBuf.Bytes(), 0644); err != nil { return err } - if err = ioutil.WriteFile(keyFile, key, 0600); err != nil { + if err = ioutil.WriteFile(keyFile, keyBuf.Bytes(), 0600); err != nil { os.Remove(certFile) return err }