diff --git a/cmd/restapi/apis/remote_db_api.go b/cmd/restapi/apis/remote_db_api.go index cddf85ebe78e8adadf12a7483dc6559df629b345..59f526d41a66f9e7eb6498d16497c4620e58ae96 100644 --- a/cmd/restapi/apis/remote_db_api.go +++ b/cmd/restapi/apis/remote_db_api.go @@ -26,7 +26,7 @@ func (e *Env) GetDB(c *gin.Context) { func (e *Env) PostDB(c *gin.Context) { newAddr := c.Query("host") + ":" + c.Query("port") - kv, back, err := ethdb.NewRemote().Path(newAddr).Open() + kv, back, err := ethdb.NewRemote().Path(newAddr).Open("") if err != nil { c.Error(err) //nolint:errcheck return diff --git a/cmd/restapi/rest/serve_rest.go b/cmd/restapi/rest/serve_rest.go index da521e2d7b76a06115a9a6089fc76dd3544cd616..de2dcafef1db4352fdb9a9968c161a2091832343 100644 --- a/cmd/restapi/rest/serve_rest.go +++ b/cmd/restapi/rest/serve_rest.go @@ -36,7 +36,7 @@ func ServeREST(ctx context.Context, restHost, rpcHost string, chaindata string) var back ethdb.Backend var err error if rpcHost != "" { - kv, back, err = ethdb.NewRemote().Path(rpcHost).Open() + kv, back, err = ethdb.NewRemote().Path(rpcHost).Open("") db = ethdb.NewObjectDatabase(kv) } else if chaindata != "" { database, errOpen := ethdb.Open(chaindata) diff --git a/cmd/rpcdaemon/cli/config.go b/cmd/rpcdaemon/cli/config.go index 642a1a1d6784cc8df30726b85d0733b6013aa1b3..999fc60fdc580e739a4c60f51a56591d8530e8a6 100644 --- a/cmd/rpcdaemon/cli/config.go +++ b/cmd/rpcdaemon/cli/config.go @@ -17,6 +17,7 @@ type Flags struct { PrivateApiAddr string Chaindata string HttpListenAddress string + TLSCertfile string HttpPort int HttpCORSDomain []string HttpVirtualHost []string @@ -48,6 +49,7 @@ func RootCommand() (*cobra.Command, *Flags) { rootCmd.PersistentFlags().StringVar(&cfg.PrivateApiAddr, "private.api.addr", "127.0.0.1:9090", "private api network address, for example: 127.0.0.1:9090, empty string means not to start the listener. do not expose to public network. serves remote database interface") rootCmd.PersistentFlags().StringVar(&cfg.Chaindata, "chaindata", "", "path to the database") rootCmd.PersistentFlags().StringVar(&cfg.HttpListenAddress, "http.addr", node.DefaultHTTPHost, "HTTP-RPC server listening interface") + rootCmd.PersistentFlags().StringVar(&cfg.TLSCertfile, "tls.cert", "", "certificate for client side TLS handshake") rootCmd.PersistentFlags().IntVar(&cfg.HttpPort, "http.port", node.DefaultHTTPPort, "HTTP-RPC server listening port") rootCmd.PersistentFlags().StringSliceVar(&cfg.HttpCORSDomain, "http.corsdomain", []string{}, "Comma separated list of domains from which to accept cross origin requests (browser enforced)") rootCmd.PersistentFlags().StringSliceVar(&cfg.HttpVirtualHost, "http.vhosts", node.DefaultConfig.HTTPVirtualHosts, "Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard.") @@ -64,7 +66,7 @@ func OpenDB(cfg Flags) (ethdb.KV, ethdb.Backend, error) { var txPool ethdb.Backend var err error if cfg.PrivateApiAddr != "" { - db, txPool, err = ethdb.NewRemote().Path(cfg.PrivateApiAddr).Open() + db, txPool, err = ethdb.NewRemote().Path(cfg.PrivateApiAddr).Open(cfg.TLSCertfile) if err != nil { return nil, nil, fmt.Errorf("could not connect to remoteDb: %w", err) } diff --git a/cmd/state/commands/gas_limits.go b/cmd/state/commands/gas_limits.go index 7cd05ababf4f9e28c4ad79b2ee04b5a952b87396..97fafde77715080731ec3a5c17c691667871cd0a 100644 --- a/cmd/state/commands/gas_limits.go +++ b/cmd/state/commands/gas_limits.go @@ -2,6 +2,7 @@ package commands import ( "fmt" + "github.com/ledgerwatch/turbo-geth/common/dbutils" "github.com/ledgerwatch/turbo-geth/cmd/state/stateless" @@ -27,7 +28,7 @@ var gasLimitsCmd = &cobra.Command{ } }).MustOpen() - remoteDB, _, err := ethdb.NewRemote().Path(privateApiAddr).Open() + remoteDB, _, err := ethdb.NewRemote().Path(privateApiAddr).Open("") if err != nil { return err } diff --git a/cmd/state/commands/state_growth.go b/cmd/state/commands/state_growth.go index b69c3f834f8e3a3d02828bb441c1c2d0c83ca259..aef248e224273d1da11e92b7ad4660638d21fb9d 100644 --- a/cmd/state/commands/state_growth.go +++ b/cmd/state/commands/state_growth.go @@ -2,11 +2,12 @@ package commands import ( "fmt" - "github.com/ledgerwatch/turbo-geth/common/dbutils" "os" "path" "time" + "github.com/ledgerwatch/turbo-geth/common/dbutils" + "github.com/ledgerwatch/turbo-geth/cmd/state/stateless" "github.com/ledgerwatch/turbo-geth/ethdb" "github.com/spf13/cobra" @@ -26,7 +27,7 @@ func init() { } }).MustOpen() - remoteDB, _, err := ethdb.NewRemote().Path(privateApiAddr).Open() + remoteDB, _, err := ethdb.NewRemote().Path(privateApiAddr).Open("") if err != nil { return err } diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 559336eeb3c9e08ae808191c6a8cd4b981c52392..dac37a588f3ae9e5104222b69108fb4c341fad8f 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -547,6 +547,20 @@ var ( Usage: "API's offered over the HTTP-RPC interface", Value: "", } + NoTLSFlag = cli.BoolTFlag{ + Name: "notls", + Usage: "Disable TLS handshake", + } + TLSCertFlag = cli.StringFlag{ + Name: "tls.cert", + Usage: "Specify certification file", + Value: "", + } + TLSKeyFlag = cli.StringFlag{ + Name: "tls.key", + Usage: "Specify key file", + Value: "", + } GraphQLEnabledFlag = cli.BoolFlag{ Name: "graphql", Usage: "Enable GraphQL on the HTTP-RPC server. Note that GraphQL can only be started if an HTTP server is started as well.", @@ -971,6 +985,20 @@ func splitAndTrim(input string) (ret []string) { // read-only interface to the databae func setPrivateApi(ctx *cli.Context, cfg *node.Config) { cfg.PrivateApiAddr = ctx.GlobalString(PrivateApiAddr.Name) + if !ctx.GlobalBool(NoTLSFlag.Name) { + certFile := ctx.GlobalString(TLSCertFlag.Name) + keyFile := ctx.GlobalString(TLSKeyFlag.Name) + if certFile == "" { + log.Warn("Could not establish TLS grpc: missing certificate") + return + } else if keyFile == "" { + log.Warn("Could not establish TLS grpc: missing key file") + return + } + cfg.TLSConnection = true + cfg.TLSCertFile = certFile + cfg.TLSKeyFile = keyFile + } } // setIPC creates an IPC path configuration from the set command line flags, diff --git a/eth/backend.go b/eth/backend.go index 64a6137a0a6afc3886feec05ca87ea3a0eee007a..ba1378df817cc5aeb6d338552c36c637010cdb80 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -28,6 +28,7 @@ import ( "sync/atomic" ethereum "github.com/ledgerwatch/turbo-geth" + "google.golang.org/grpc/credentials" "github.com/ledgerwatch/turbo-geth/accounts" "github.com/ledgerwatch/turbo-geth/common" @@ -226,7 +227,16 @@ func New(stack *node.Node, config *Config) (*Ethereum, error) { eth.txPool = core.NewTxPool(config.TxPool, chainConfig, chainDb, txCacher) if stack.Config().PrivateApiAddr != "" { - remotedbserver.StartGrpc(chainDb.KV(), eth, stack.Config().PrivateApiAddr) + if stack.Config().TLSConnection { + var creds credentials.TransportCredentials + creds, err = credentials.NewServerTLSFromFile(stack.Config().TLSCertFile, stack.Config().TLSKeyFile) + if err != nil { + return nil, err + } + remotedbserver.StartGrpc(chainDb.KV(), eth, stack.Config().PrivateApiAddr, &creds) + } else { + remotedbserver.StartGrpc(chainDb.KV(), eth, stack.Config().PrivateApiAddr, nil) + } } checkpoint := config.Checkpoint diff --git a/ethdb/kv_remote.go b/ethdb/kv_remote.go index 493418d98856e81ce2fb835723fccf365ce8c741..0a91febf9d8065da92eebbecef28526c8bd34655 100644 --- a/ethdb/kv_remote.go +++ b/ethdb/kv_remote.go @@ -13,6 +13,7 @@ import ( "github.com/ledgerwatch/turbo-geth/log" "google.golang.org/grpc" "google.golang.org/grpc/backoff" + "google.golang.org/grpc/credentials" "google.golang.org/grpc/test/bufconn" ) @@ -85,10 +86,24 @@ func (opts remoteOpts) InMem(listener *bufconn.Listener) remoteOpts { return opts } -func (opts remoteOpts) Open() (KV, Backend, error) { - var dialOpts = []grpc.DialOption{ - grpc.WithConnectParams(grpc.ConnectParams{Backoff: backoff.DefaultConfig}), - grpc.WithInsecure(), +func (opts remoteOpts) Open(certFile string) (KV, Backend, error) { + var dialOpts []grpc.DialOption + if certFile == "" { + dialOpts = []grpc.DialOption{ + grpc.WithConnectParams(grpc.ConnectParams{Backoff: backoff.DefaultConfig}), + grpc.WithInsecure(), + } + } else { + creds, err := credentials.NewClientTLSFromFile(certFile, "") + + if err != nil { + return nil, nil, err + } + + dialOpts = []grpc.DialOption{ + grpc.WithConnectParams(grpc.ConnectParams{Backoff: backoff.DefaultConfig}), + grpc.WithTransportCredentials(creds), + } } if opts.inMemConn != nil { @@ -129,7 +144,7 @@ func (opts remoteOpts) Open() (KV, Backend, error) { } func (opts remoteOpts) MustOpen() (KV, Backend) { - db, txPool, err := opts.Open() + db, txPool, err := opts.Open("") if err != nil { panic(err) } diff --git a/ethdb/remote/remotedbserver/server.go b/ethdb/remote/remotedbserver/server.go index 601ded39259deee5490f58752de91bda9344e497..c8f9519359aa5c31476e48567bdf0468a791be7c 100644 --- a/ethdb/remote/remotedbserver/server.go +++ b/ethdb/remote/remotedbserver/server.go @@ -10,6 +10,7 @@ import ( grpc_recovery "github.com/grpc-ecosystem/go-grpc-middleware/recovery" grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus" "google.golang.org/grpc" + "google.golang.org/grpc/credentials" "github.com/ledgerwatch/turbo-geth/common" "github.com/ledgerwatch/turbo-geth/core" @@ -27,7 +28,7 @@ type KvServer struct { kv ethdb.KV } -func StartGrpc(kv ethdb.KV, eth core.Backend, addr string) { +func StartGrpc(kv ethdb.KV, eth core.Backend, addr string, creds *credentials.TransportCredentials) { log.Info("Starting private RPC server", "on", addr) lis, err := net.Listen("tcp", addr) if err != nil { @@ -48,15 +49,27 @@ func StartGrpc(kv ethdb.KV, eth core.Backend, addr string) { } streamInterceptors = append(streamInterceptors, grpc_recovery.StreamServerInterceptor()) unaryInterceptors = append(unaryInterceptors, grpc_recovery.UnaryServerInterceptor()) - - grpcServer := grpc.NewServer( - grpc.NumStreamWorkers(20), // reduce amount of goroutines - grpc.WriteBufferSize(1024), // reduce buffers to save mem - grpc.ReadBufferSize(1024), - grpc.MaxConcurrentStreams(40), // to force clients reduce concurency level - grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(streamInterceptors...)), - grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(unaryInterceptors...)), - ) + var grpcServer *grpc.Server + if creds == nil { + grpcServer = grpc.NewServer( + grpc.NumStreamWorkers(20), // reduce amount of goroutines + grpc.WriteBufferSize(1024), // reduce buffers to save mem + grpc.ReadBufferSize(1024), + grpc.MaxConcurrentStreams(40), // to force clients reduce concurency level + grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(streamInterceptors...)), + grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(unaryInterceptors...)), + ) + } else { + grpcServer = grpc.NewServer( + grpc.NumStreamWorkers(20), // reduce amount of goroutines + grpc.WriteBufferSize(1024), // reduce buffers to save mem + grpc.ReadBufferSize(1024), + grpc.MaxConcurrentStreams(40), // to force clients reduce concurency level + grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(streamInterceptors...)), + grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(unaryInterceptors...)), + grpc.Creds(*creds), + ) + } remote.RegisterKVServer(grpcServer, kvSrv) remote.RegisterDBServer(grpcServer, dbSrv) remote.RegisterETHBACKENDServer(grpcServer, ethBackendSrv) diff --git a/node/config.go b/node/config.go index 012ed4487ffb198d922cd232c202d74af8d61e9d..5344ab2bd49a67117b20349dbf93b4b5dc7cf08b 100644 --- a/node/config.go +++ b/node/config.go @@ -189,6 +189,10 @@ type Config struct { staticNodesWarning bool trustedNodesWarning bool oldGethResourceWarning bool + + TLSConnection bool + TLSCertFile string + TLSKeyFile string } // IPCEndpoint resolves an IPC endpoint based on a configured value, taking into diff --git a/turbo/cli/default_flags.go b/turbo/cli/default_flags.go index 0040483726607189d9e8f301d3bb26e8a1f4c7c7..20bedc8968b726d91eed8719bbf036d41015e297 100644 --- a/turbo/cli/default_flags.go +++ b/turbo/cli/default_flags.go @@ -26,6 +26,9 @@ var DefaultFlags = []cli.Flag{ utils.HddFlag, utils.DatabaseFlag, utils.LMDBMapSizeFlag, + utils.NoTLSFlag, + utils.TLSCertFlag, + utils.TLSKeyFlag, utils.PrivateApiAddr, utils.ListenPortFlag, utils.NATFlag,