main: fix TLS cert autogen and server configuration

This commit is contained in:
Alex 2017-08-14 14:54:06 -06:00 committed by Olaoluwa Osuntokun
parent 5d971a8ea8
commit f67ce4e6d7

167
lnd.go
View File

@ -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
}