Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add --rpc.jwtsecret arg #5271

Merged
merged 1 commit into from
Nov 3, 2023
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
23 changes: 20 additions & 3 deletions bin/reth/src/args/rpc_server_args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
/// `<DIR>/<CHAIN_ID>/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<PathBuf>,

/// 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<JwtSecret>,

/// 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,
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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<JwtSecret, JwtError> {
fn auth_jwt_secret(&self, default_jwt_path: PathBuf) -> Result<JwtSecret, JwtError> {
match self.auth_jwtsecret.as_ref() {
Some(fpath) => {
debug!(target: "reth::cli", user_path=?fpath, "Reading JWT auth secret file");
Expand All @@ -444,6 +457,10 @@ impl RethRpcConfig for RpcServerArgs {
}
}
}

fn rpc_secret_key(&self) -> Option<JwtSecret> {
self.rpc_jwtsecret.clone()
}
}

/// clap value parser for [RpcModuleSelection].
Expand Down
7 changes: 6 additions & 1 deletion bin/reth/src/cli/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<JwtSecret, JwtError>;
fn auth_jwt_secret(&self, default_jwt_path: PathBuf) -> Result<JwtSecret, JwtError>;

/// 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<JwtSecret>;
}

/// A trait that provides payload builder settings.
Expand Down
10 changes: 10 additions & 0 deletions bin/reth/src/dirs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,26 +260,36 @@ impl<D> ChainPath<D> {
}

/// Returns the path to the db directory for this chain.
///
/// `<DIR>/<CHAIN_ID>/db`
pub fn db_path(&self) -> PathBuf {
self.0.join("db").into()
}

/// Returns the path to the reth p2p secret key for this chain.
///
/// `<DIR>/<CHAIN_ID>/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.
///
/// `<DIR>/<CHAIN_ID>/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.
///
/// `<DIR>/<CHAIN_ID>/reth.toml`
pub fn config_path(&self) -> PathBuf {
self.0.join("reth.toml").into()
}

/// Returns the path to the jwtsecret file for this chain.
///
/// `<DIR>/<CHAIN_ID>/jwt.hex`
pub fn jwt_path(&self) -> PathBuf {
self.0.join("jwt.hex").into()
}
Expand Down
2 changes: 1 addition & 1 deletion bin/reth/src/node/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -521,7 +521,7 @@ impl<Ext: RethCliExt> NodeCommand<Ext> {

// 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();
Expand Down
18 changes: 9 additions & 9 deletions crates/rpc/rpc-builder/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<JwtSecret>) -> Self {
self.jwt_secret = secret;
self
}

Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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(),
)
Expand All @@ -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(),
)
Expand Down Expand Up @@ -1708,14 +1708,14 @@ impl WsHttpServerKind {
builder: ServerBuilder,
socket_addr: SocketAddr,
cors_domains: Option<String>,
auth_secret: Option<JwtSecret>,
jwt_secret: Option<JwtSecret>,
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)
Expand All @@ -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())));
Expand Down
23 changes: 15 additions & 8 deletions crates/rpc/rpc/src/layers/jwt_secret.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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<Self, Self::Err> {
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
Expand Down
Loading