diff --git a/.github/workflows/e2e-polybft-test.yml b/.github/workflows/e2e-polybft-test.yml index 630862c3a6..e7b591724e 100644 --- a/.github/workflows/e2e-polybft-test.yml +++ b/.github/workflows/e2e-polybft-test.yml @@ -26,19 +26,19 @@ jobs: go-version: 1.21.x check-latest: true - name: Generate OpenSSL certificate - run: openssl req -x509 -out localhost.crt -keyout localhost.key -newkey rsa:2048 -nodes -sha256 -subj '/CN=localhost' -extensions EXT -config <(printf "[dn]\nCN=localhost\n[req]\ndistinguished_name = dn\n[EXT]\nsubjectAltName=DNS:localhost\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth") + run: openssl req -x509 -out jsontls.crt -keyout jsontls.key -newkey rsa:2048 -nodes -sha256 -subj '/CN=localhost' -extensions EXT -config <(printf "[dn]\nCN=localhost\n[req]\ndistinguished_name = dn\n[EXT]\nsubjectAltName=DNS:localhost\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth") - name: Copy certificate key - run: sudo cp localhost.key /etc/ssl/private/localhost.key + run: sudo cp jsontls.key /etc/ssl/private/jsontls.key - name: Copy certificate itself - run: sudo cp localhost.crt /usr/local/share/ca-certificates/localhost.crt + run: sudo cp jsontls.crt /usr/local/share/ca-certificates/jsontls.crt - name: Add certificate to trusted list run: sudo update-ca-certificates - name: Update certificate key folder permissions run: sudo chmod -R 755 /etc/ssl/private - name: Update certificate key file permissions - run: sudo chmod 644 /etc/ssl/private/localhost.key + run: sudo chmod 644 /etc/ssl/private/jsontls.key - name: Check certificate key permissions - run: ls -l /etc/ssl/private/localhost.key + run: ls -l /etc/ssl/private/jsontls.key - name: Run tests run: make test-e2e-polybft - name: Run tests failed diff --git a/command/server/config/config.go b/command/server/config/config.go index 177028d9e4..5f70afc062 100644 --- a/command/server/config/config.go +++ b/command/server/config/config.go @@ -32,8 +32,7 @@ type Config struct { JSONRPCBlockRangeLimit uint64 `json:"json_rpc_block_range_limit" yaml:"json_rpc_block_range_limit"` JSONLogFormat bool `json:"json_log_format" yaml:"json_log_format"` CorsAllowedOrigins []string `json:"cors_allowed_origins" yaml:"cors_allowed_origins"` - TLSCertFile string `json:"tls_cert_file" yaml:"tls_cert_file"` - TLSKeyFile string `json:"tls_key_file" yaml:"tls_key_file"` + UseTLS bool `json:"use_tls" yaml:"use_tls"` Relayer bool `json:"relayer" yaml:"relayer"` @@ -146,8 +145,7 @@ func DefaultConfig() *Config { AccessControlAllowOrigins: []string{"*"}, }, LogFilePath: "", - TLSCertFile: "", - TLSKeyFile: "", + UseTLS: false, JSONRPCBatchRequestLimit: DefaultJSONRPCBatchRequestLimit, JSONRPCBlockRangeLimit: DefaultJSONRPCBlockRangeLimit, Relayer: false, diff --git a/command/server/params.go b/command/server/params.go index 5faa467196..7a4a74022d 100644 --- a/command/server/params.go +++ b/command/server/params.go @@ -37,8 +37,7 @@ const ( devFlag = "dev" corsOriginFlag = "access-control-allow-origins" logFileLocationFlag = "log-to" - tlsCertFileLocationFlag = "tls-cert-file" - tlsKeyFileLocationFlag = "tls-key-file" + useTLSFlag = "use-tls" relayerFlag = "relayer" @@ -185,8 +184,7 @@ func (p *serverParams) generateConfig() *server.Config { LogLevel: hclog.LevelFromString(p.rawConfig.LogLevel), JSONLogFormat: p.rawConfig.JSONLogFormat, LogFilePath: p.logFileLocation, - TLSCertFile: p.rawConfig.TLSCertFile, - TLSKeyFile: p.rawConfig.TLSKeyFile, + UseTLS: p.rawConfig.UseTLS, Relayer: p.relayer, MetricsInterval: p.rawConfig.MetricsInterval, diff --git a/command/server/server.go b/command/server/server.go index bca5a60202..ef9b87af82 100644 --- a/command/server/server.go +++ b/command/server/server.go @@ -214,18 +214,11 @@ func setFlags(cmd *cobra.Command) { "write all logs to the file at specified location instead of writing them to console", ) - cmd.Flags().StringVar( - ¶ms.rawConfig.TLSCertFile, - tlsCertFileLocationFlag, - defaultConfig.TLSCertFile, - "path to TLS cert file, if no file is provided then TLS is not used", - ) - - cmd.Flags().StringVar( - ¶ms.rawConfig.TLSKeyFile, - tlsKeyFileLocationFlag, - defaultConfig.TLSKeyFile, - "path to TLS key file, if no file is provided then TLS is not used", + cmd.Flags().BoolVar( + ¶ms.rawConfig.UseTLS, + useTLSFlag, + defaultConfig.UseTLS, + "start json rpc endpoint with tls enabled", ) cmd.Flags().BoolVar( diff --git a/e2e-polybft/e2e/jsonrpc_test.go b/e2e-polybft/e2e/jsonrpc_test.go index 03f22aa30b..c5700ed81b 100644 --- a/e2e-polybft/e2e/jsonrpc_test.go +++ b/e2e-polybft/e2e/jsonrpc_test.go @@ -31,7 +31,7 @@ func TestE2E_JsonRPC(t *testing.T) { framework.WithEpochSize(int(epochSize)), framework.WithPremine(preminedAcct.Address()), framework.WithBurnContract(&polybft.BurnContractInfo{BlockNumber: 0, Address: types.ZeroAddress}), - framework.WithHTTPS("/etc/ssl/certs/localhost.pem", "/etc/ssl/private/localhost.key"), + framework.WithHTTPS(), ) defer cluster.Stop() diff --git a/e2e-polybft/framework/test-cluster.go b/e2e-polybft/framework/test-cluster.go index 9f0e3801df..03ed2aa834 100644 --- a/e2e-polybft/framework/test-cluster.go +++ b/e2e-polybft/framework/test-cluster.go @@ -147,8 +147,7 @@ type TestClusterConfig struct { logsDirOnce sync.Once - TLSCertFile string - TLSKeyFile string + UseTLS bool } func (c *TestClusterConfig) Dir(name string) string { @@ -464,10 +463,9 @@ func WithPredeploy(predeployString string) ClusterOption { } } -func WithHTTPS(certFile string, keyFile string) ClusterOption { +func WithHTTPS() ClusterOption { return func(h *TestClusterConfig) { - h.TLSCertFile = certFile - h.TLSKeyFile = keyFile + h.UseTLS = true } } @@ -813,8 +811,7 @@ func (c *TestCluster) InitTestServer(t *testing.T, config.Relayer = nodeType.IsSet(Relayer) config.NumBlockConfirmations = c.Config.NumBlockConfirmations config.BridgeJSONRPC = bridgeJSONRPC - config.TLSCertFile = c.Config.TLSCertFile - config.TLSKeyFile = c.Config.TLSKeyFile + config.UseTLS = c.Config.UseTLS }) // watch the server for stop signals. It is important to fix the specific diff --git a/e2e-polybft/framework/test-server.go b/e2e-polybft/framework/test-server.go index 0934628d64..c281c6bc0c 100644 --- a/e2e-polybft/framework/test-server.go +++ b/e2e-polybft/framework/test-server.go @@ -39,8 +39,7 @@ type TestServerConfig struct { Relayer bool NumBlockConfirmations uint64 BridgeJSONRPC string - TLSCertFile string - TLSKeyFile string + UseTLS bool } type TestServerConfigCallback func(*TestServerConfig) @@ -67,7 +66,7 @@ func (t *TestServer) GrpcAddr() string { } func (t *TestServer) JSONRPCAddr() string { - if t.config.TLSCertFile != "" && t.config.TLSKeyFile != "" { + if t.config.UseTLS { return fmt.Sprintf("https://localhost:%d", t.config.JSONRPCPort) } else { return fmt.Sprintf("http://%s:%d", hostIP, t.config.JSONRPCPort) @@ -171,10 +170,6 @@ func (t *TestServer) Start() { "--jsonrpc", fmt.Sprintf(":%d", config.JSONRPCPort), // minimal number of child blocks required for the parent block to be considered final "--num-block-confirmations", strconv.FormatUint(config.NumBlockConfirmations, 10), - // TLS certificate file - "--tls-cert-file", config.TLSCertFile, - // TLS key file - "--tls-key-file", config.TLSKeyFile, } if len(config.LogLevel) > 0 { @@ -187,6 +182,10 @@ func (t *TestServer) Start() { args = append(args, "--relayer") } + if config.UseTLS { + args = append(args, "--use-tls") + } + // Start the server stdout := t.clusterConfig.GetStdout(t.config.Name) diff --git a/jsonrpc/jsonrpc.go b/jsonrpc/jsonrpc.go index aa238e0696..7199b8620d 100644 --- a/jsonrpc/jsonrpc.go +++ b/jsonrpc/jsonrpc.go @@ -1,6 +1,7 @@ package jsonrpc import ( + "crypto/tls" "encoding/json" "fmt" "io" @@ -9,6 +10,7 @@ import ( "sync" "time" + "github.com/0xPolygon/polygon-edge/secrets" "github.com/0xPolygon/polygon-edge/versioning" "github.com/gorilla/websocket" "github.com/hashicorp/go-hclog" @@ -50,8 +52,8 @@ type Config struct { ConcurrentRequestsDebug uint64 WebSocketReadLimit uint64 - TLSCertFile string - TLSKeyFile string + UseTLS bool + SecretsManager secrets.SecretsManager } // NewJSONRPC returns the JSONRPC http server @@ -111,12 +113,23 @@ func (j *JSONRPC) setupHTTP() error { ReadHeaderTimeout: 60 * time.Second, } - if j.config.TLSCertFile != "" && j.config.TLSKeyFile != "" { - j.logger.Info("TLS", "cert file", j.config.TLSCertFile) - j.logger.Info("TLS", "key file", j.config.TLSKeyFile) + if j.config.UseTLS { + j.logger.Info("configuring http server with tls...") + + cert, err := loadTLSCertificate(j.config.SecretsManager) + if err != nil { + j.logger.Error("loading tls certificate", "err", err) + + return err + } + + srv.TLSConfig = &tls.Config{ + Certificates: []tls.Certificate{*cert}, + MinVersion: tls.VersionTLS12, + } go func() { - if err := srv.ServeTLS(lis, j.config.TLSCertFile, j.config.TLSKeyFile); err != nil { + if err := srv.ServeTLS(lis, "", ""); err != nil { j.logger.Error("closed https connection", "err", err) } }() @@ -133,6 +146,29 @@ func (j *JSONRPC) setupHTTP() error { return nil } +func loadTLSCertificate(manager secrets.SecretsManager) (*tls.Certificate, error) { + if manager.HasSecret(secrets.JSONTLSCert) && manager.HasSecret(secrets.JSONTLSKey) { + tlsCert, err := manager.GetSecret(secrets.JSONTLSCert) + if err != nil { + return nil, fmt.Errorf("unable to get a tls cert file from Secrets Manager, %w", err) + } + + tlsKey, err := manager.GetSecret(secrets.JSONTLSKey) + if err != nil { + return nil, fmt.Errorf("unable to get a tls key file from Secrets Manager, %w", err) + } + + cert, err := tls.X509KeyPair(tlsCert, tlsKey) + if err != nil { + return nil, fmt.Errorf("unable to create a tls certificate, %w", err) + } + + return &cert, nil + } + + return nil, secrets.ErrSecretNotFound +} + // The middlewareFactory builds a middleware which enables CORS using the provided config. func middlewareFactory(config *Config) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { diff --git a/secrets/local/local.go b/secrets/local/local.go index 2a08d1a36d..ac675d2c7c 100644 --- a/secrets/local/local.go +++ b/secrets/local/local.go @@ -91,6 +91,18 @@ func (l *LocalSecretsManager) Setup() error { secrets.NetworkKeyLocal, ) + // /etc/ssl/certs/jsonrpc.pem + l.secretPathMap[secrets.JSONTLSCert] = filepath.Join( + secrets.JSONTLSFolderLocal, + secrets.JSONTLSCertLocal, + ) + + // /etc/ssl/private/jsonrpc.key + l.secretPathMap[secrets.JSONTLSKey] = filepath.Join( + secrets.JSONTLSFolderLocal, + secrets.JSONTLSKeyLocal, + ) + return nil } diff --git a/secrets/secrets.go b/secrets/secrets.go index 4dcd30205e..5690359b2a 100644 --- a/secrets/secrets.go +++ b/secrets/secrets.go @@ -31,6 +31,12 @@ const ( // NetworkKey is the libp2p private key secret used for networking NetworkKey = "network-key" + + // JSONTLSKey is the tls private key used for json rpc https endpoint + JSONTLSKey = "jsontls-key" + + // JSONTLSCert is the tls certificate used for json rpc https endpoint + JSONTLSCert = "jsontls-pem" ) // Define constant file names for the local StorageManager @@ -38,12 +44,15 @@ const ( ValidatorKeyLocal = "validator.key" ValidatorBLSKeyLocal = "validator-bls.key" NetworkKeyLocal = "libp2p.key" + JSONTLSKeyLocal = "/private/jsontls.key" + JSONTLSCertLocal = "/certs/jsontls.pem" ) // Define constant folder names for the local StorageManager const ( ConsensusFolderLocal = "consensus" NetworkFolderLocal = "libp2p" + JSONTLSFolderLocal = "/etc/ssl" ) var ( diff --git a/server/config.go b/server/config.go index c33743894e..e08292a749 100644 --- a/server/config.go +++ b/server/config.go @@ -42,9 +42,7 @@ type Config struct { LogFilePath string - TLSCertFile string - - TLSKeyFile string + UseTLS bool Relayer bool diff --git a/server/server.go b/server/server.go index 1c42503401..c95522efd4 100644 --- a/server/server.go +++ b/server/server.go @@ -869,8 +869,8 @@ func (s *Server) setupJSONRPC() error { BlockRangeLimit: s.config.JSONRPC.BlockRangeLimit, ConcurrentRequestsDebug: s.config.JSONRPC.ConcurrentRequestsDebug, WebSocketReadLimit: s.config.JSONRPC.WebSocketReadLimit, - TLSCertFile: s.config.TLSCertFile, - TLSKeyFile: s.config.TLSKeyFile, + UseTLS: s.config.UseTLS, + SecretsManager: s.secretsManager, } srv, err := jsonrpc.NewJSONRPC(s.logger, conf)