From d2136f8253d21ed3c7080223664c440b14e7af01 Mon Sep 17 00:00:00 2001 From: Darex <97831554+1010adigupta@users.noreply.github.com> Date: Thu, 2 Nov 2023 19:05:09 +0530 Subject: [PATCH] Authenticated json rpc endpoint (#5250) Co-authored-by: Matthias Seitz --- crates/rpc/rpc-builder/src/auth.rs | 2 +- crates/rpc/rpc-builder/src/lib.rs | 73 +++++++++++++++++++++++++++--- 2 files changed, 67 insertions(+), 8 deletions(-) diff --git a/crates/rpc/rpc-builder/src/auth.rs b/crates/rpc/rpc-builder/src/auth.rs index f3a1ba660820..396b2a9fad87 100644 --- a/crates/rpc/rpc-builder/src/auth.rs +++ b/crates/rpc/rpc-builder/src/auth.rs @@ -128,7 +128,7 @@ where pub struct AuthServerConfig { /// Where the server should listen. pub(crate) socket_addr: SocketAddr, - /// The secrete for the auth layer of the server. + /// The secret for the auth layer of the server. pub(crate) secret: JwtSecret, /// Configs for JSON-RPC Http. pub(crate) server_config: ServerBuilder, diff --git a/crates/rpc/rpc-builder/src/lib.rs b/crates/rpc/rpc-builder/src/lib.rs index 7105a4de030a..ada2b22910ef 100644 --- a/crates/rpc/rpc-builder/src/lib.rs +++ b/crates/rpc/rpc-builder/src/lib.rs @@ -117,9 +117,9 @@ use reth_rpc::{ cache::{cache_new_blocks_task, EthStateCache}, gas_oracle::GasPriceOracle, }, - AdminApi, BlockingTaskGuard, BlockingTaskPool, DebugApi, EngineEthApi, EthApi, EthFilter, - EthPubSub, EthSubscriptionIdProvider, NetApi, OtterscanApi, RPCApi, RethApi, TraceApi, - TxPoolApi, Web3Api, + AdminApi, AuthLayer, BlockingTaskGuard, BlockingTaskPool, DebugApi, EngineEthApi, EthApi, + EthFilter, EthPubSub, EthSubscriptionIdProvider, JwtAuthValidator, JwtSecret, NetApi, + OtterscanApi, RPCApi, RethApi, TraceApi, TxPoolApi, Web3Api, }; use reth_rpc_api::{servers::*, EngineApiServer}; use reth_tasks::{TaskSpawner, TokioTaskExecutor}; @@ -1141,6 +1141,8 @@ pub struct RpcServerConfig { ipc_server_config: Option, /// The Endpoint where to launch the ipc server ipc_endpoint: Option, + /// JWT secret for authentication + jwt_secret: Option, } impl fmt::Debug for RpcServerConfig { @@ -1153,6 +1155,7 @@ impl fmt::Debug for RpcServerConfig { .field("ws_addr", &self.ws_addr) .field("ipc_server_config", &self.ipc_server_config) .field("ipc_endpoint", &self.ipc_endpoint.as_ref().map(|endpoint| endpoint.path())) + .field("jwt_secret", &self.jwt_secret) .finish() } } @@ -1264,6 +1267,12 @@ impl RpcServerConfig { self } + /// Configures the JWT secret for authentication + pub fn with_jwt_secret(mut self, secret: JwtSecret) -> Self { + self.jwt_secret = Some(secret); + self + } + /// Returns true if any server is configured. /// /// If no server is configured, no server will be be launched on [RpcServerConfig::start]. @@ -1328,6 +1337,8 @@ impl RpcServerConfig { } .cloned(); + let secret = self.jwt_secret.take(); + // we merge this into one server using the http setup self.ws_server_config.take(); @@ -1336,6 +1347,7 @@ impl RpcServerConfig { builder, http_socket_addr, cors, + secret, ServerKind::WsHttp(http_socket_addr), metrics.clone(), ) @@ -1358,6 +1370,7 @@ impl RpcServerConfig { builder, ws_socket_addr, self.ws_cors_domains.take(), + self.jwt_secret.take(), ServerKind::WS(ws_socket_addr), metrics.clone(), ) @@ -1372,6 +1385,7 @@ impl RpcServerConfig { builder, http_socket_addr, self.http_cors_domains.take(), + self.jwt_secret.take(), ServerKind::Http(http_socket_addr), metrics.clone(), ) @@ -1667,6 +1681,12 @@ enum WsHttpServerKind { Plain(Server), /// Http server with cors WithCors(Server, RpcServerMetrics>), + /// Http server with auth + WithAuth(Server, Identity>, RpcServerMetrics>), + /// Http server with cors and auth + WithCorsAuth( + Server, Stack>, RpcServerMetrics>, + ), } // === impl WsHttpServerKind === @@ -1677,30 +1697,69 @@ impl WsHttpServerKind { match self { WsHttpServerKind::Plain(server) => server.start(module), WsHttpServerKind::WithCors(server) => server.start(module), + WsHttpServerKind::WithAuth(server) => server.start(module), + WsHttpServerKind::WithCorsAuth(server) => server.start(module), } } - /// Builds + /// Builds the server according to the given config parameters. + /// + /// Returns the address of the started server. async fn build( builder: ServerBuilder, socket_addr: SocketAddr, cors_domains: Option, + auth_secret: Option, server_kind: ServerKind, metrics: RpcServerMetrics, ) -> Result<(Self, SocketAddr), RpcError> { if let Some(cors) = cors_domains.as_deref().map(cors::create_cors_layer) { let cors = cors.map_err(|err| RpcError::Custom(err.to_string()))?; - let middleware = tower::ServiceBuilder::new().layer(cors); + + if let Some(secret) = auth_secret { + // stack cors and auth layers + let middleware = tower::ServiceBuilder::new() + .layer(cors) + .layer(AuthLayer::new(JwtAuthValidator::new(secret.clone()))); + + let server = builder + .set_middleware(middleware) + .set_logger(metrics) + .build(socket_addr) + .await + .map_err(|err| RpcError::from_jsonrpsee_error(err, server_kind))?; + let local_addr = server.local_addr()?; + let server = WsHttpServerKind::WithCorsAuth(server); + Ok((server, local_addr)) + } else { + let middleware = tower::ServiceBuilder::new().layer(cors); + let server = builder + .set_middleware(middleware) + .set_logger(metrics) + .build(socket_addr) + .await + .map_err(|err| RpcError::from_jsonrpsee_error(err, server_kind))?; + let local_addr = server.local_addr()?; + let server = WsHttpServerKind::WithCors(server); + Ok((server, local_addr)) + } + } else if let Some(secret) = auth_secret { + // jwt auth layered service + let middleware = tower::ServiceBuilder::new() + .layer(AuthLayer::new(JwtAuthValidator::new(secret.clone()))); let server = builder .set_middleware(middleware) .set_logger(metrics) .build(socket_addr) .await - .map_err(|err| RpcError::from_jsonrpsee_error(err, server_kind))?; + .map_err(|err| { + RpcError::from_jsonrpsee_error(err, ServerKind::Auth(socket_addr)) + })?; let local_addr = server.local_addr()?; - let server = WsHttpServerKind::WithCors(server); + let server = WsHttpServerKind::WithAuth(server); Ok((server, local_addr)) } else { + // plain server without any middleware let server = builder .set_logger(metrics) .build(socket_addr)