From 23d89c09419310b84a43e30e1033cd87ff15625f Mon Sep 17 00:00:00 2001 From: Giulio rebuffo <giulio.rebuffo@gmail.com> Date: Fri, 11 Sep 2020 22:17:37 +0200 Subject: [PATCH] Added TLS handshake to RPCDaemon (#1089) * added eth_getStorageAt * used uint32 * now its 256 * incarnation * added TLS handshake * code minified * lint * minified client * Update flags.go Co-authored-by: Alexey Akhunov <akhounov@gmail.com> --- cmd/restapi/apis/remote_db_api.go | 2 +- cmd/restapi/rest/serve_rest.go | 2 +- cmd/rpcdaemon/cli/config.go | 4 +++- cmd/state/commands/gas_limits.go | 3 ++- cmd/state/commands/state_growth.go | 5 ++-- cmd/utils/flags.go | 28 +++++++++++++++++++++++ eth/backend.go | 12 +++++++++- ethdb/kv_remote.go | 25 ++++++++++++++++---- ethdb/remote/remotedbserver/server.go | 33 +++++++++++++++++++-------- node/config.go | 4 ++++ turbo/cli/default_flags.go | 3 +++ 11 files changed, 99 insertions(+), 22 deletions(-) diff --git a/cmd/restapi/apis/remote_db_api.go b/cmd/restapi/apis/remote_db_api.go index cddf85ebe7..59f526d41a 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 da521e2d7b..de2dcafef1 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 642a1a1d67..999fc60fdc 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 7cd05ababf..97fafde777 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 b69c3f834f..aef248e224 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 559336eeb3..dac37a588f 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 64a6137a0a..ba1378df81 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 493418d988..0a91febf9d 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 601ded3925..c8f9519359 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 012ed4487f..5344ab2bd4 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 0040483726..20bedc8968 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, -- GitLab