diff --git a/bin/reth/src/args/rpc_server_args.rs b/bin/reth/src/args/rpc_server_args.rs
index 567e00d1f2a5..175c73b1ded0 100644
--- a/bin/reth/src/args/rpc_server_args.rs
+++ b/bin/reth/src/args/rpc_server_args.rs
@@ -116,10 +116,23 @@ pub struct RpcServerArgs {
#[arg(long = "authrpc.port", default_value_t = constants::DEFAULT_AUTH_PORT)]
pub auth_port: u16,
- /// Path to a JWT secret to use for authenticated RPC endpoints
+ /// Path to a JWT secret to use for the authenticated engine-API RPC server.
+ ///
+ /// This will enforce JWT authentication for all requests coming from the consensus layer.
+ ///
+ /// If no path is provided, a secret will be generated and stored in the datadir under
+ /// `
//jwt.hex`. For mainnet this would be `~/.reth/mainnet/jwt.hex` by default.
#[arg(long = "authrpc.jwtsecret", value_name = "PATH", global = true, required = false)]
pub auth_jwtsecret: Option,
+ /// Hex encoded JWT secret to authenticate the regular RPC server(s), see `--http.api` and
+ /// `--ws.api`.
+ ///
+ /// This is __not__ used for the authenticated engine-API RPC server, see
+ /// `--authrpc.jwtsecret`.
+ #[arg(long = "rpc.jwtsecret", value_name = "HEX", global = true, required = false)]
+ pub rpc_jwtsecret: Option,
+
/// Set the maximum RPC request payload size for both HTTP and WS in megabytes.
#[arg(long, default_value_t = RPC_DEFAULT_MAX_REQUEST_SIZE_MB)]
pub rpc_max_request_size: u32,
@@ -397,7 +410,7 @@ impl RethRpcConfig for RpcServerArgs {
}
fn rpc_server_config(&self) -> RpcServerConfig {
- let mut config = RpcServerConfig::default();
+ let mut config = RpcServerConfig::default().with_jwt_secret(self.rpc_secret_key());
if self.http {
let socket_address = SocketAddr::new(self.http_addr, self.http_port);
@@ -427,7 +440,7 @@ impl RethRpcConfig for RpcServerArgs {
Ok(AuthServerConfig::builder(jwt_secret).socket_addr(address).build())
}
- fn jwt_secret(&self, default_jwt_path: PathBuf) -> Result {
+ fn auth_jwt_secret(&self, default_jwt_path: PathBuf) -> Result {
match self.auth_jwtsecret.as_ref() {
Some(fpath) => {
debug!(target: "reth::cli", user_path=?fpath, "Reading JWT auth secret file");
@@ -444,6 +457,10 @@ impl RethRpcConfig for RpcServerArgs {
}
}
}
+
+ fn rpc_secret_key(&self) -> Option {
+ self.rpc_jwtsecret.clone()
+ }
}
/// clap value parser for [RpcModuleSelection].
diff --git a/bin/reth/src/cli/config.rs b/bin/reth/src/cli/config.rs
index 01eb395e9f67..a567d39a2e11 100644
--- a/bin/reth/src/cli/config.rs
+++ b/bin/reth/src/cli/config.rs
@@ -63,7 +63,12 @@ pub trait RethRpcConfig {
///
/// The `default_jwt_path` provided as an argument will be used as the default location for the
/// jwt secret in case the `auth_jwtsecret` argument is not provided.
- fn jwt_secret(&self, default_jwt_path: PathBuf) -> Result;
+ fn auth_jwt_secret(&self, default_jwt_path: PathBuf) -> Result;
+
+ /// Returns the configured jwt secret key for the regular rpc servers, if any.
+ ///
+ /// Note: this is not used for the auth server (engine API).
+ fn rpc_secret_key(&self) -> Option;
}
/// A trait that provides payload builder settings.
diff --git a/bin/reth/src/dirs.rs b/bin/reth/src/dirs.rs
index c93e8234ba4d..c7bf9dc80398 100644
--- a/bin/reth/src/dirs.rs
+++ b/bin/reth/src/dirs.rs
@@ -260,26 +260,36 @@ impl ChainPath {
}
/// Returns the path to the db directory for this chain.
+ ///
+ /// `//db`
pub fn db_path(&self) -> PathBuf {
self.0.join("db").into()
}
/// Returns the path to the reth p2p secret key for this chain.
+ ///
+ /// `//discovery-secret`
pub fn p2p_secret_path(&self) -> PathBuf {
self.0.join("discovery-secret").into()
}
/// Returns the path to the known peers file for this chain.
+ ///
+ /// `//known-peers.json`
pub fn known_peers_path(&self) -> PathBuf {
self.0.join("known-peers.json").into()
}
/// Returns the path to the config file for this chain.
+ ///
+ /// `//reth.toml`
pub fn config_path(&self) -> PathBuf {
self.0.join("reth.toml").into()
}
/// Returns the path to the jwtsecret file for this chain.
+ ///
+ /// `//jwt.hex`
pub fn jwt_path(&self) -> PathBuf {
self.0.join("jwt.hex").into()
}
diff --git a/bin/reth/src/node/mod.rs b/bin/reth/src/node/mod.rs
index 6c7002cc8a7c..0dc54d31611c 100644
--- a/bin/reth/src/node/mod.rs
+++ b/bin/reth/src/node/mod.rs
@@ -521,7 +521,7 @@ impl NodeCommand {
// extract the jwt secret from the args if possible
let default_jwt_path = data_dir.jwt_path();
- let jwt_secret = self.rpc.jwt_secret(default_jwt_path)?;
+ let jwt_secret = self.rpc.auth_jwt_secret(default_jwt_path)?;
// adjust rpc port numbers based on instance number
self.adjust_instance_ports();
diff --git a/crates/rpc/rpc-builder/src/lib.rs b/crates/rpc/rpc-builder/src/lib.rs
index cf6ffe875819..7108c755f038 100644
--- a/crates/rpc/rpc-builder/src/lib.rs
+++ b/crates/rpc/rpc-builder/src/lib.rs
@@ -1266,9 +1266,9 @@ impl RpcServerConfig {
self
}
- /// Configures the JWT secret for authentication
- pub fn with_jwt_secret(mut self, secret: JwtSecret) -> Self {
- self.jwt_secret = Some(secret);
+ /// Configures the JWT secret for authentication.
+ pub fn with_jwt_secret(mut self, secret: Option) -> Self {
+ self.jwt_secret = secret;
self
}
@@ -1336,7 +1336,7 @@ impl RpcServerConfig {
}
.cloned();
- let secret = self.jwt_secret.take();
+ let secret = self.jwt_secret.clone();
// we merge this into one server using the http setup
self.ws_server_config.take();
@@ -1369,7 +1369,7 @@ impl RpcServerConfig {
builder,
ws_socket_addr,
self.ws_cors_domains.take(),
- self.jwt_secret.take(),
+ self.jwt_secret.clone(),
ServerKind::WS(ws_socket_addr),
metrics.clone(),
)
@@ -1384,7 +1384,7 @@ impl RpcServerConfig {
builder,
http_socket_addr,
self.http_cors_domains.take(),
- self.jwt_secret.take(),
+ self.jwt_secret.clone(),
ServerKind::Http(http_socket_addr),
metrics.clone(),
)
@@ -1708,14 +1708,14 @@ impl WsHttpServerKind {
builder: ServerBuilder,
socket_addr: SocketAddr,
cors_domains: Option,
- auth_secret: Option,
+ jwt_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()))?;
- if let Some(secret) = auth_secret {
+ if let Some(secret) = jwt_secret {
// stack cors and auth layers
let middleware = tower::ServiceBuilder::new()
.layer(cors)
@@ -1742,7 +1742,7 @@ impl WsHttpServerKind {
let server = WsHttpServerKind::WithCors(server);
Ok((server, local_addr))
}
- } else if let Some(secret) = auth_secret {
+ } else if let Some(secret) = jwt_secret {
// jwt auth layered service
let middleware = tower::ServiceBuilder::new()
.layer(AuthLayer::new(JwtAuthValidator::new(secret.clone())));
diff --git a/crates/rpc/rpc/src/layers/jwt_secret.rs b/crates/rpc/rpc/src/layers/jwt_secret.rs
index af5d92c3812f..61bb3149f10b 100644
--- a/crates/rpc/rpc/src/layers/jwt_secret.rs
+++ b/crates/rpc/rpc/src/layers/jwt_secret.rs
@@ -8,6 +8,7 @@ use reth_primitives::{
use serde::{Deserialize, Serialize};
use std::{
path::Path,
+ str::FromStr,
time::{Duration, SystemTime, UNIX_EPOCH},
};
use thiserror::Error;
@@ -101,15 +102,7 @@ impl JwtSecret {
fs::write(fpath, hex)?;
Ok(secret)
}
-}
-impl std::fmt::Debug for JwtSecret {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- f.debug_tuple("JwtSecretHash").field(&"{{}}").finish()
- }
-}
-
-impl JwtSecret {
/// Validates a JWT token along the following rules:
/// - The JWT signature is valid.
/// - The JWT is signed with the `HMAC + SHA256 (HS256)` algorithm.
@@ -169,6 +162,20 @@ impl JwtSecret {
}
}
+impl std::fmt::Debug for JwtSecret {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_tuple("JwtSecretHash").field(&"{{}}").finish()
+ }
+}
+
+impl FromStr for JwtSecret {
+ type Err = JwtError;
+
+ fn from_str(s: &str) -> Result {
+ JwtSecret::from_hex(s)
+ }
+}
+
/// Claims in JWT are used to represent a set of information about an entity.
/// Claims are essentially key-value pairs that are encoded as JSON objects and included in the
/// payload of a JWT. They are used to transmit information such as the identity of the entity, the