main: fix TLS cert autogen and server configuration
This commit is contained in:
parent
5d971a8ea8
commit
f67ce4e6d7
167
lnd.go
167
lnd.go
@ -1,9 +1,16 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"math/big"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
_ "net/http/pprof"
|
_ "net/http/pprof"
|
||||||
@ -33,13 +40,20 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
autogenCertValidity = 10 * 365 * 24 * time.Hour
|
// Make certificate valid for 14 months.
|
||||||
|
autogenCertValidity = 14 /*months*/ * 30 /*days*/ * 24 * time.Hour
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
cfg *config
|
cfg *config
|
||||||
shutdownChannel = make(chan struct{})
|
shutdownChannel = make(chan struct{})
|
||||||
registeredChains = newChainRegistry()
|
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
|
// 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 {
|
if err := rpcServer.Start(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
sCreds, err := credentials.NewServerTLSFromFile(cfg.TLSCertPath,
|
cert, err := tls.LoadX509KeyPair(cfg.TLSCertPath, cfg.TLSKeyPath)
|
||||||
cfg.TLSKeyPath)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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)}
|
opts := []grpc.ServerOption{grpc.Creds(sCreds)}
|
||||||
grpcServer := grpc.NewServer(opts...)
|
grpcServer := grpc.NewServer(opts...)
|
||||||
lnrpc.RegisterLightningServer(grpcServer, rpcServer)
|
lnrpc.RegisterLightningServer(grpcServer, rpcServer)
|
||||||
@ -268,9 +309,14 @@ func lndMain() error {
|
|||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
restEndpoint := fmt.Sprintf(":%d", loadedConfig.RESTPort)
|
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)
|
rpcsLog.Infof("gRPC proxy started at localhost%s", restEndpoint)
|
||||||
http.ListenAndServeTLS(restEndpoint, cfg.TLSCertPath,
|
http.Serve(listener, mux)
|
||||||
cfg.TLSKeyPath, mux)
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// If we're not in simnet mode, We'll wait until we're fully synced to
|
// 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
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// genCertPair generates a key/cert pair to the paths provided.
|
// genCertPair generates a key/cert pair to the paths provided. The
|
||||||
// This function is adapted from https://github.com/btcsuite/btcd
|
// 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 {
|
func genCertPair(certFile, keyFile string) error {
|
||||||
rpcsLog.Infof("Generating TLS certificates...")
|
rpcsLog.Infof("Generating TLS certificates...")
|
||||||
|
|
||||||
org := "lnd autogenerated cert"
|
org := "lnd autogenerated cert"
|
||||||
validUntil := time.Now().Add(autogenCertValidity)
|
now := time.Now()
|
||||||
cert, key, err := btcutil.NewTLSCertPair(org, validUntil, nil)
|
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 {
|
if err != nil {
|
||||||
return err
|
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.
|
// 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
|
return err
|
||||||
}
|
}
|
||||||
if err = ioutil.WriteFile(keyFile, key, 0600); err != nil {
|
if err = ioutil.WriteFile(keyFile, keyBuf.Bytes(), 0600); err != nil {
|
||||||
os.Remove(certFile)
|
os.Remove(certFile)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user