Merge pull request #4648 from halseth/option-no-tls
RPC: add --no-rest-tls option
This commit is contained in:
commit
b525c04432
@ -210,6 +210,7 @@ type Config struct {
|
||||
ExternalIPs []net.Addr
|
||||
DisableListen bool `long:"nolisten" description:"Disable listening for incoming peer connections"`
|
||||
DisableRest bool `long:"norest" description:"Disable REST API"`
|
||||
DisableRestTLS bool `long:"no-rest-tls" description:"Disable TLS for REST connections"`
|
||||
NAT bool `long:"nat" description:"Toggle NAT traversal support (using either UPnP or NAT-PMP) to automatically advertise your external IP address to the network -- NOTE this does not support devices behind multiple NATs"`
|
||||
MinBackoff time.Duration `long:"minbackoff" description:"Shortest backoff when reconnecting to persistent peers. Valid time units are {s, m, h}."`
|
||||
MaxBackoff time.Duration `long:"maxbackoff" description:"Longest backoff when reconnecting to persistent peers. Valid time units are {s, m, h}."`
|
||||
@ -1175,9 +1176,10 @@ func ValidateConfig(cfg Config, usageMessage string) (*Config, error) {
|
||||
|
||||
// For each of the RPC listeners (REST+gRPC), we'll ensure that users
|
||||
// have specified a safe combo for authentication. If not, we'll bail
|
||||
// out with an error.
|
||||
// out with an error. Since we don't allow disabling TLS for gRPC
|
||||
// connections we pass in tlsActive=true.
|
||||
err = lncfg.EnforceSafeAuthentication(
|
||||
cfg.RPCListeners, !cfg.NoMacaroons,
|
||||
cfg.RPCListeners, !cfg.NoMacaroons, true,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -1188,7 +1190,7 @@ func ValidateConfig(cfg Config, usageMessage string) (*Config, error) {
|
||||
cfg.RESTListeners = nil
|
||||
} else {
|
||||
err = lncfg.EnforceSafeAuthentication(
|
||||
cfg.RESTListeners, !cfg.NoMacaroons,
|
||||
cfg.RESTListeners, !cfg.NoMacaroons, !cfg.DisableRestTLS,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -48,11 +48,13 @@ func NormalizeAddresses(addrs []string, defaultPort string,
|
||||
}
|
||||
|
||||
// EnforceSafeAuthentication enforces "safe" authentication taking into account
|
||||
// the interfaces that the RPC servers are listening on, and if macaroons are
|
||||
// activated or not. To protect users from using dangerous config combinations,
|
||||
// we'll prevent disabling authentication if the server is listening on a public
|
||||
// interface.
|
||||
func EnforceSafeAuthentication(addrs []net.Addr, macaroonsActive bool) error {
|
||||
// the interfaces that the RPC servers are listening on, and if macaroons and
|
||||
// TLS is activated or not. To protect users from using dangerous config
|
||||
// combinations, we'll prevent disabling authentication if the server is
|
||||
// listening on a public interface.
|
||||
func EnforceSafeAuthentication(addrs []net.Addr, macaroonsActive,
|
||||
tlsActive bool) error {
|
||||
|
||||
// We'll now examine all addresses that this RPC server is listening
|
||||
// on. If it's a localhost address or a private address, we'll skip it,
|
||||
// otherwise, we'll return an error if macaroons are inactive.
|
||||
@ -62,10 +64,17 @@ func EnforceSafeAuthentication(addrs []net.Addr, macaroonsActive bool) error {
|
||||
}
|
||||
|
||||
if !macaroonsActive {
|
||||
return fmt.Errorf("Detected RPC server listening on "+
|
||||
return fmt.Errorf("detected RPC server listening on "+
|
||||
"publicly reachable interface %v with "+
|
||||
"authentication disabled! Refusing to start "+
|
||||
"with --no-macaroons specified.", addr)
|
||||
"with --no-macaroons specified", addr)
|
||||
}
|
||||
|
||||
if !tlsActive {
|
||||
return fmt.Errorf("detected RPC server listening on "+
|
||||
"publicly reachable interface %v with "+
|
||||
"encryption disabled! Refusing to start "+
|
||||
"with --notls specified", addr)
|
||||
}
|
||||
}
|
||||
|
||||
|
97
lnd.go
97
lnd.go
@ -270,7 +270,7 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error {
|
||||
defer cleanUp()
|
||||
|
||||
// Only process macaroons if --no-macaroons isn't set.
|
||||
tlsCfg, restCreds, restProxyDest, cleanUp, err := getTLSConfig(cfg)
|
||||
serverOpts, restDialOpts, restListen, cleanUp, err := getTLSConfig(cfg)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("unable to load TLS credentials: %v", err)
|
||||
ltndLog.Error(err)
|
||||
@ -279,19 +279,20 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error {
|
||||
|
||||
defer cleanUp()
|
||||
|
||||
serverCreds := credentials.NewTLS(tlsCfg)
|
||||
serverOpts := []grpc.ServerOption{grpc.Creds(serverCreds)}
|
||||
// We use the first RPC listener as the destination for our REST proxy.
|
||||
// If the listener is set to listen on all interfaces, we replace it
|
||||
// with localhost, as we cannot dial it directly.
|
||||
restProxyDest := cfg.RPCListeners[0].String()
|
||||
switch {
|
||||
case strings.Contains(restProxyDest, "0.0.0.0"):
|
||||
restProxyDest = strings.Replace(
|
||||
restProxyDest, "0.0.0.0", "127.0.0.1", 1,
|
||||
)
|
||||
|
||||
// For our REST dial options, we'll still use TLS, but also increase
|
||||
// the max message size that we'll decode to allow clients to hit
|
||||
// endpoints which return more data such as the DescribeGraph call.
|
||||
// We set this to 200MiB atm. Should be the same value as maxMsgRecvSize
|
||||
// in cmd/lncli/main.go.
|
||||
restDialOpts := []grpc.DialOption{
|
||||
grpc.WithTransportCredentials(*restCreds),
|
||||
grpc.WithDefaultCallOptions(
|
||||
grpc.MaxCallRecvMsgSize(1 * 1024 * 1024 * 200),
|
||||
),
|
||||
case strings.Contains(restProxyDest, "[::]"):
|
||||
restProxyDest = strings.Replace(
|
||||
restProxyDest, "[::]", "[::1]", 1,
|
||||
)
|
||||
}
|
||||
|
||||
// Before starting the wallet, we'll create and start our Neutrino
|
||||
@ -380,7 +381,7 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error {
|
||||
if !cfg.NoSeedBackup {
|
||||
params, shutdown, err := waitForWalletPassword(
|
||||
cfg, cfg.RESTListeners, serverOpts, restDialOpts,
|
||||
restProxyDest, tlsCfg, walletUnlockerListeners,
|
||||
restProxyDest, restListen, walletUnlockerListeners,
|
||||
)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("unable to set up wallet password "+
|
||||
@ -730,7 +731,7 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error {
|
||||
rpcServer, err := newRPCServer(
|
||||
cfg, server, macaroonService, cfg.SubRPCServers, serverOpts,
|
||||
restDialOpts, restProxyDest, atplManager, server.invoices,
|
||||
tower, tlsCfg, rpcListeners, chainedAcceptor,
|
||||
tower, restListen, rpcListeners, chainedAcceptor,
|
||||
)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("unable to create RPC server: %v", err)
|
||||
@ -832,8 +833,8 @@ func Main(cfg *Config, lisCfg ListenerCfg, shutdownChan <-chan struct{}) error {
|
||||
|
||||
// getTLSConfig returns a TLS configuration for the gRPC server and credentials
|
||||
// and a proxy destination for the REST reverse proxy.
|
||||
func getTLSConfig(cfg *Config) (*tls.Config, *credentials.TransportCredentials,
|
||||
string, func(), error) {
|
||||
func getTLSConfig(cfg *Config) ([]grpc.ServerOption, []grpc.DialOption,
|
||||
func(net.Addr) (net.Listener, error), func(), error) {
|
||||
|
||||
// Ensure we create TLS key and certificate if they don't exist.
|
||||
if !fileExists(cfg.TLSCertPath) && !fileExists(cfg.TLSKeyPath) {
|
||||
@ -844,7 +845,7 @@ func getTLSConfig(cfg *Config) (*tls.Config, *credentials.TransportCredentials,
|
||||
cfg.TLSDisableAutofill, cert.DefaultAutogenValidity,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, "", nil, err
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
rpcsLog.Infof("Done generating TLS certificates")
|
||||
}
|
||||
@ -853,7 +854,7 @@ func getTLSConfig(cfg *Config) (*tls.Config, *credentials.TransportCredentials,
|
||||
cfg.TLSCertPath, cfg.TLSKeyPath,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, "", nil, err
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
|
||||
// We check whether the certifcate we have on disk match the IPs and
|
||||
@ -867,7 +868,7 @@ func getTLSConfig(cfg *Config) (*tls.Config, *credentials.TransportCredentials,
|
||||
cfg.TLSExtraDomains, cfg.TLSDisableAutofill,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, "", nil, err
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
@ -879,12 +880,12 @@ func getTLSConfig(cfg *Config) (*tls.Config, *credentials.TransportCredentials,
|
||||
|
||||
err := os.Remove(cfg.TLSCertPath)
|
||||
if err != nil {
|
||||
return nil, nil, "", nil, err
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
|
||||
err = os.Remove(cfg.TLSKeyPath)
|
||||
if err != nil {
|
||||
return nil, nil, "", nil, err
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
|
||||
rpcsLog.Infof("Renewing TLS certificates...")
|
||||
@ -894,7 +895,7 @@ func getTLSConfig(cfg *Config) (*tls.Config, *credentials.TransportCredentials,
|
||||
cfg.TLSDisableAutofill, cert.DefaultAutogenValidity,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, "", nil, err
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
rpcsLog.Infof("Done renewing TLS certificates")
|
||||
|
||||
@ -903,7 +904,7 @@ func getTLSConfig(cfg *Config) (*tls.Config, *credentials.TransportCredentials,
|
||||
cfg.TLSCertPath, cfg.TLSKeyPath,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, "", nil, err
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
@ -911,20 +912,7 @@ func getTLSConfig(cfg *Config) (*tls.Config, *credentials.TransportCredentials,
|
||||
|
||||
restCreds, err := credentials.NewClientTLSFromFile(cfg.TLSCertPath, "")
|
||||
if err != nil {
|
||||
return nil, nil, "", nil, err
|
||||
}
|
||||
|
||||
restProxyDest := cfg.RPCListeners[0].String()
|
||||
switch {
|
||||
case strings.Contains(restProxyDest, "0.0.0.0"):
|
||||
restProxyDest = strings.Replace(
|
||||
restProxyDest, "0.0.0.0", "127.0.0.1", 1,
|
||||
)
|
||||
|
||||
case strings.Contains(restProxyDest, "[::]"):
|
||||
restProxyDest = strings.Replace(
|
||||
restProxyDest, "[::]", "[::1]", 1,
|
||||
)
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
|
||||
// If Let's Encrypt is enabled, instantiate autocert to request/renew
|
||||
@ -984,7 +972,34 @@ func getTLSConfig(cfg *Config) (*tls.Config, *credentials.TransportCredentials,
|
||||
tlsCfg.GetCertificate = getCertificate
|
||||
}
|
||||
|
||||
return tlsCfg, &restCreds, restProxyDest, cleanUp, nil
|
||||
serverCreds := credentials.NewTLS(tlsCfg)
|
||||
serverOpts := []grpc.ServerOption{grpc.Creds(serverCreds)}
|
||||
|
||||
// For our REST dial options, we'll still use TLS, but also increase
|
||||
// the max message size that we'll decode to allow clients to hit
|
||||
// endpoints which return more data such as the DescribeGraph call.
|
||||
// We set this to 200MiB atm. Should be the same value as maxMsgRecvSize
|
||||
// in cmd/lncli/main.go.
|
||||
restDialOpts := []grpc.DialOption{
|
||||
grpc.WithTransportCredentials(restCreds),
|
||||
grpc.WithDefaultCallOptions(
|
||||
grpc.MaxCallRecvMsgSize(1 * 1024 * 1024 * 200),
|
||||
),
|
||||
}
|
||||
|
||||
// Return a function closure that can be used to listen on a given
|
||||
// address with the current TLS config.
|
||||
restListen := func(addr net.Addr) (net.Listener, error) {
|
||||
// For restListen we will call ListenOnAddress if TLS is
|
||||
// disabled.
|
||||
if cfg.DisableRestTLS {
|
||||
return lncfg.ListenOnAddress(addr)
|
||||
}
|
||||
|
||||
return lncfg.TLSListenOnAddress(addr, tlsCfg)
|
||||
}
|
||||
|
||||
return serverOpts, restDialOpts, restListen, cleanUp, nil
|
||||
}
|
||||
|
||||
// fileExists reports whether the named file or directory exists.
|
||||
@ -1109,7 +1124,7 @@ type WalletUnlockParams struct {
|
||||
// the user to this RPC server.
|
||||
func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr,
|
||||
serverOpts []grpc.ServerOption, restDialOpts []grpc.DialOption,
|
||||
restProxyDest string, tlsConf *tls.Config,
|
||||
restProxyDest string, restListen func(net.Addr) (net.Listener, error),
|
||||
getListeners rpcListeners) (*WalletUnlockParams, func(), error) {
|
||||
|
||||
chainConfig := cfg.Bitcoin
|
||||
@ -1188,7 +1203,7 @@ func waitForWalletPassword(cfg *Config, restEndpoints []net.Addr,
|
||||
srv := &http.Server{Handler: allowCORS(mux, cfg.RestCORS)}
|
||||
|
||||
for _, restEndpoint := range restEndpoints {
|
||||
lis, err := lncfg.TLSListenOnAddress(restEndpoint, tlsConf)
|
||||
lis, err := restListen(restEndpoint)
|
||||
if err != nil {
|
||||
ltndLog.Errorf("Password gRPC proxy unable to listen "+
|
||||
"on %s", restEndpoint)
|
||||
|
16
rpcserver.go
16
rpcserver.go
@ -3,12 +3,12 @@ package lnd
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"net"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"sort"
|
||||
@ -512,9 +512,10 @@ type rpcServer struct {
|
||||
// restProxyDest is the address to forward REST requests to.
|
||||
restProxyDest string
|
||||
|
||||
// tlsCfg is the TLS config that allows the REST server proxy to
|
||||
// connect to the main gRPC server to proxy all incoming requests.
|
||||
tlsCfg *tls.Config
|
||||
// restListen is a function closure that allows the REST server proxy to
|
||||
// connect to the main gRPC server to proxy all incoming requests,
|
||||
// applying the current TLS configuration, if any.
|
||||
restListen func(net.Addr) (net.Listener, error)
|
||||
|
||||
// routerBackend contains the backend implementation of the router
|
||||
// rpc sub server.
|
||||
@ -551,7 +552,8 @@ func newRPCServer(cfg *Config, s *server, macService *macaroons.Service,
|
||||
subServerCgs *subRPCServerConfigs, serverOpts []grpc.ServerOption,
|
||||
restDialOpts []grpc.DialOption, restProxyDest string,
|
||||
atpl *autopilot.Manager, invoiceRegistry *invoices.InvoiceRegistry,
|
||||
tower *watchtower.Standalone, tlsCfg *tls.Config,
|
||||
tower *watchtower.Standalone,
|
||||
restListen func(net.Addr) (net.Listener, error),
|
||||
getListeners rpcListeners,
|
||||
chanPredicate *chanacceptor.ChainedAcceptor) (*rpcServer, error) {
|
||||
|
||||
@ -754,7 +756,7 @@ func newRPCServer(cfg *Config, s *server, macService *macaroons.Service,
|
||||
listenerCleanUp: []func(){cleanup},
|
||||
restProxyDest: restProxyDest,
|
||||
subServers: subServers,
|
||||
tlsCfg: tlsCfg,
|
||||
restListen: restListen,
|
||||
grpcServer: grpcServer,
|
||||
server: s,
|
||||
routerBackend: routerBackend,
|
||||
@ -901,7 +903,7 @@ func (r *rpcServer) Start() error {
|
||||
// Now spin up a network listener for each requested port and start a
|
||||
// goroutine that serves REST with the created mux there.
|
||||
for _, restEndpoint := range r.cfg.RESTListeners {
|
||||
lis, err := lncfg.TLSListenOnAddress(restEndpoint, r.tlsCfg)
|
||||
lis, err := r.restListen(restEndpoint)
|
||||
if err != nil {
|
||||
ltndLog.Errorf("gRPC proxy unable to listen on %s",
|
||||
restEndpoint)
|
||||
|
@ -176,6 +176,9 @@
|
||||
; Disable REST API.
|
||||
; norest=true
|
||||
|
||||
; Disable TLS for the REST API.
|
||||
; no-rest-tls=true
|
||||
|
||||
; Shortest backoff when reconnecting to persistent peers. Valid time units are
|
||||
; {s, m, h}.
|
||||
; minbackoff=1s
|
||||
|
Loading…
Reference in New Issue
Block a user