Skip to content
This repository has been archived by the owner on Apr 4, 2024. It is now read-only.

rpc, server: add TLS certificate for websocket #600

Merged
merged 3 commits into from
Sep 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ Ref: https://keepachangelog.com/en/1.0.0/

### Bug Fixes

* (rpc, server) [tharsis#600](https://github.com/tharsis/ethermint/pull/600) Add TLS configuration for websocket API
* (rpc) [tharsis#598](https://github.com/tharsis/ethermint/pull/598) Check truncation when creating a `BlockNumber` from `big.Int`
* (evm) [tharsis#597](https://github.com/tharsis/ethermint/pull/597) Check for `uint64` -> `int64` block height overflow on `GetHashFn`
* (evm) [tharsis#579](https://github.com/tharsis/ethermint/pull/579) Update `DeriveChainID` function to handle `v` signature values `< 35`.
Expand Down
34 changes: 24 additions & 10 deletions ethereum/rpc/websockets.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"fmt"
"io/ioutil"
"math/big"
"net"
"net/http"
"sync"

Expand All @@ -25,6 +26,7 @@ import (

rpcfilters "github.com/tharsis/ethermint/ethereum/rpc/namespaces/eth/filters"
"github.com/tharsis/ethermint/ethereum/rpc/types"
"github.com/tharsis/ethermint/server/config"
evmtypes "github.com/tharsis/ethermint/x/evm/types"
)

Expand Down Expand Up @@ -61,19 +63,25 @@ type ErrorMessageJSON struct {
}

type websocketsServer struct {
rpcAddr string // listen address of rest-server
wsAddr string // listen address of ws server
api *pubSubAPI
logger log.Logger
rpcAddr string // listen address of rest-server
wsAddr string // listen address of ws server
certFile string
keyFile string
api *pubSubAPI
logger log.Logger
}

func NewWebsocketsServer(logger log.Logger, tmWSClient *rpcclient.WSClient, rpcAddr, wsAddr string) WebsocketsServer {
func NewWebsocketsServer(logger log.Logger, tmWSClient *rpcclient.WSClient, cfg config.Config) WebsocketsServer {
logger = logger.With("api", "websocket-server")
_, port, _ := net.SplitHostPort(cfg.JSONRPC.Address)

return &websocketsServer{
rpcAddr: rpcAddr,
wsAddr: wsAddr,
api: newPubSubAPI(logger, tmWSClient),
logger: logger,
rpcAddr: "localhost:" + port, // FIXME: this shouldn't be hardcoded to localhost
wsAddr: cfg.JSONRPC.WsAddress,
certFile: cfg.TLS.CertificatePath,
keyFile: cfg.TLS.KeyPath,
api: newPubSubAPI(logger, tmWSClient),
logger: logger,
}
}

Expand All @@ -82,7 +90,13 @@ func (s *websocketsServer) Start() {
ws.Handle("/", s)

go func() {
err := http.ListenAndServe(s.wsAddr, ws)
var err error
if s.certFile == "" || s.keyFile == "" {
err = http.ListenAndServe(s.wsAddr, ws)
} else {
err = http.ListenAndServeTLS(s.wsAddr, s.certFile, s.keyFile, ws)
}

if err != nil {
if err == http.ErrServerClosed {
return
Expand Down
117 changes: 81 additions & 36 deletions server/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package config
import (
"errors"
"fmt"
"path"

"github.com/spf13/viper"

Expand Down Expand Up @@ -31,9 +32,43 @@ const (

var evmTracers = []string{DefaultEVMTracer, "markdown", "struct", "access_list"}

// GetDefaultAPINamespaces returns the default list of JSON-RPC namespaces that should be enabled
func GetDefaultAPINamespaces() []string {
return []string{"eth", "net", "web3"}
// Config defines the server's top level configuration. It includes the default app config
// from the SDK as well as the EVM configuration to enable the JSON-RPC APIs.
type Config struct {
config.Config

EVM EVMConfig `mapstructure:"evm"`
JSONRPC JSONRPCConfig `mapstructure:"json-rpc"`
TLS TLSConfig `mapstructure:"tls"`
}

// EVMConfig defines the application configuration values for the EVM.
type EVMConfig struct {
// Tracer defines vm.Tracer type that the EVM will use if the node is run in
// trace mode. Default: 'json'.
Tracer string `mapstructure:"tracer"`
}

// JSONRPCConfig defines configuration for the EVM RPC server.
type JSONRPCConfig struct {
// Address defines the HTTP server to listen on
Address string `mapstructure:"address"`
// WsAddress defines the WebSocket server to listen on
WsAddress string `mapstructure:"ws-address"`
// API defines a list of JSON-RPC namespaces that should be enabled
API []string `mapstructure:"api"`
// Enable defines if the EVM RPC server should be enabled.
Enable bool `mapstructure:"enable"`
// GasCap is the global gas cap for eth-call variants.
GasCap uint64 `mapstructure:"gas-cap"`
}

// TLSConfig defines the certificate and matching private key for the server.
type TLSConfig struct {
// CertificatePath the file path for the certificate .pem file
CertificatePath string `mapstructure:"certificate-path"`
// KeyPath the file path for the key .pem file
KeyPath string `mapstructure:"key-path"`
}

// AppConfig helps to override default appConfig template and configs.
Expand Down Expand Up @@ -63,6 +98,7 @@ func AppConfig(denom string) (string, interface{}) {
Config: *srvCfg,
EVM: *DefaultEVMConfig(),
JSONRPC: *DefaultJSONRPCConfig(),
TLS: *DefaultTLSConfig(),
}

customAppTemplate := config.DefaultConfigTemplate + DefaultConfigTemplate
Expand All @@ -76,16 +112,10 @@ func DefaultConfig() *Config {
Config: *config.DefaultConfig(),
EVM: *DefaultEVMConfig(),
JSONRPC: *DefaultJSONRPCConfig(),
TLS: *DefaultTLSConfig(),
}
}

// EVMConfig defines the application configuration values for the EVM.
type EVMConfig struct {
// Tracer defines vm.Tracer type that the EVM will use if the node is run in
// trace mode. Default: 'json'.
Tracer string `mapstructure:"tracer"`
}

// DefaultEVMConfig returns the default EVM configuration
func DefaultEVMConfig() *EVMConfig {
return &EVMConfig{
Expand All @@ -102,18 +132,20 @@ func (c EVMConfig) Validate() error {
return nil
}

// JSONRPCConfig defines configuration for the EVM RPC server.
type JSONRPCConfig struct {
// Address defines the HTTP server to listen on
Address string `mapstructure:"address"`
// WsAddress defines the WebSocket server to listen on
WsAddress string `mapstructure:"ws-address"`
// API defines a list of JSON-RPC namespaces that should be enabled
API []string `mapstructure:"api"`
// Enable defines if the EVM RPC server should be enabled.
Enable bool `mapstructure:"enable"`
// GasCap is the global gas cap for eth-call variants.
GasCap uint64 `mapstructure:"gas-cap"`
// GetDefaultAPINamespaces returns the default list of JSON-RPC namespaces that should be enabled
func GetDefaultAPINamespaces() []string {
return []string{"eth", "net", "web3"}
}

// DefaultJSONRPCConfig returns an EVM config with the JSON-RPC API enabled by default
func DefaultJSONRPCConfig() *JSONRPCConfig {
return &JSONRPCConfig{
Enable: true,
API: GetDefaultAPINamespaces(),
Address: DefaultJSONRPCAddress,
WsAddress: DefaultJSONRPCWsAddress,
GasCap: DefaultGasCap,
}
}

// Validate returns an error if the JSON-RPC configuration fields are invalid.
Expand All @@ -135,24 +167,29 @@ func (c JSONRPCConfig) Validate() error {
return nil
}

// DefaultJSONRPCConfig returns an EVM config with the JSON-RPC API enabled by default
func DefaultJSONRPCConfig() *JSONRPCConfig {
return &JSONRPCConfig{
Enable: true,
API: GetDefaultAPINamespaces(),
Address: DefaultJSONRPCAddress,
WsAddress: DefaultJSONRPCWsAddress,
GasCap: DefaultGasCap,
// DefaultTLSConfig returns the default TLS configuration
func DefaultTLSConfig() *TLSConfig {
return &TLSConfig{
CertificatePath: "",
KeyPath: "",
}
}

// Config defines the server's top level configuration. It includes the default app config
// from the SDK as well as the EVM configuration to enable the JSON-RPC APIs.
type Config struct {
config.Config
// Validate returns an error if the TLS certificate and key file extensions are invalid.
func (c TLSConfig) Validate() error {
certExt := path.Ext(c.CertificatePath)

EVM EVMConfig `mapstructure:"evm"`
JSONRPC JSONRPCConfig `mapstructure:"json-rpc"`
if c.CertificatePath != "" && certExt != ".pem" {
return fmt.Errorf("invalid extension %s for certificate path %s, expected '.pem'", certExt, c.CertificatePath)
}

keyExt := path.Ext(c.KeyPath)

if c.KeyPath != "" && keyExt != ".pem" {
return fmt.Errorf("invalid extension %s for key path %s, expected '.pem'", keyExt, c.KeyPath)
}

return nil
}

// GetConfig returns a fully parsed Config object.
Expand All @@ -171,6 +208,10 @@ func GetConfig(v *viper.Viper) Config {
WsAddress: v.GetString("json-rpc.ws-address"),
GasCap: v.GetUint64("json-rpc.gas-cap"),
},
TLS: TLSConfig{
CertificatePath: v.GetString("tls.certificate-path"),
KeyPath: v.GetString("tls.key-path"),
},
}
}

Expand All @@ -193,5 +234,9 @@ func (c Config) ValidateBasic() error {
return sdkerrors.Wrapf(sdkerrors.ErrAppConfig, "invalid json-rpc config value: %s", err.Error())
}

if err := c.TLS.Validate(); err != nil {
return sdkerrors.Wrapf(sdkerrors.ErrAppConfig, "invalid tls config value: %s", err.Error())
}

return c.Config.ValidateBasic()
}
12 changes: 12 additions & 0 deletions server/config/toml.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,16 @@ api = "{{range $index, $elmt := .JSONRPC.API}}{{if $index}},{{$elmt}}{{else}}{{$

# GasCap sets a cap on gas that can be used in eth_call/estimateGas (0=infinite). Default: 25,000,000.
gas-cap = {{ .JSONRPC.GasCap }}

###############################################################################
### TLS Configuration ###
###############################################################################

[tls]

# Certificate path defines the cert.pem file path for the TLS configuration.
certificate-path = "{{ .TLS.CertificatePath }}"

# Key path defines the key.pem file path for the TLS configuration.
key-path = "{{ .TLS.KeyPath }}"
`
6 changes: 6 additions & 0 deletions server/flags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ const (
EVMTracer = "evm.tracer"
)

// TLS flags
const (
TLSCertPath = "tls.certificate-path"
TLSKeyPath = "tls.key-path"
)

// AddTxFlags adds common flags for commands to post tx
func AddTxFlags(cmd *cobra.Command) *cobra.Command {
cmd.PersistentFlags().String(flags.FlagChainID, "testnet", "Specify Chain ID for sending Tx")
Expand Down
4 changes: 1 addition & 3 deletions server/json_rpc.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package server

import (
"net"
"net/http"
"time"

Expand Down Expand Up @@ -73,11 +72,10 @@ func StartJSONRPC(ctx *server.Context, clientCtx client.Context, tmRPCAddr, tmEn
}

ctx.Logger.Info("Starting JSON WebSocket server", "address", config.JSONRPC.WsAddress)
_, port, _ := net.SplitHostPort(config.JSONRPC.Address)

// allocate separate WS connection to Tendermint
tmWsClient = ConnectTmWS(tmRPCAddr, tmEndpoint, ctx.Logger)
wsSrv := rpc.NewWebsocketsServer(ctx.Logger, tmWsClient, "localhost:"+port, config.JSONRPC.WsAddress)
wsSrv := rpc.NewWebsocketsServer(ctx.Logger, tmWsClient, config)
wsSrv.Start()
return httpSrv, httpSrvDone, nil
}
3 changes: 3 additions & 0 deletions server/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,9 @@ which accepts a path for the resulting pprof file.

cmd.Flags().String(srvflags.EVMTracer, config.DefaultEVMTracer, "the EVM tracer type to collect execution traces from the EVM transaction execution (json|struct|access_list|markdown)")

cmd.Flags().String(srvflags.TLSCertPath, "", "the cert.pem file path for the server TLS configuration")
cmd.Flags().String(srvflags.TLSKeyPath, "", "the key.pem file path for the server TLS configuration")

cmd.Flags().Uint64(server.FlagStateSyncSnapshotInterval, 0, "State sync snapshot interval")
cmd.Flags().Uint32(server.FlagStateSyncSnapshotKeepRecent, 2, "State sync snapshot to keep")

Expand Down