diff --git a/src/config.rs b/src/config.rs index 7e54a1b..8c33863 100644 --- a/src/config.rs +++ b/src/config.rs @@ -22,7 +22,10 @@ pub struct Config { /// Path to the JWT secret for the primary execution engine. #[arg(long, value_name = "PATH")] pub ee_jwt_secret: String, - /// Path to TOML file JWT secrets for the non-canonical CL clients. + /// Path to the JWT secret for the controlling consensus client. + #[arg(long, value_name = "PATH")] + pub controller_jwt_secret: PathBuf, + /// Path to TOML file of JWT secrets for the non-controlling consensus clients. /// /// See docs for TOML file format. #[arg(long, value_name = "PATH")] diff --git a/src/jwt.rs b/src/jwt.rs index 60d62e3..994e480 100644 --- a/src/jwt.rs +++ b/src/jwt.rs @@ -34,6 +34,14 @@ fn verify_parsed_token(token: UnverifiedToken, secret: &Secret) -> Result Result { + std::fs::read_to_string(path) + .map_err(|e| e.to_string()) + .and_then(|hex_secret| hex::decode(hex_secret).map_err(|e| e.to_string())) + .and_then(|byte_secret| Secret::new_from_slice(&byte_secret).map_err(|e| e.to_string())) + .map_err(|e| format!("Invalid JWT secret at path {}: {e}", path.display())) +} + impl KeyCollection { pub fn verify(&self, token: &str) -> Result { let parsed_token = UnverifiedToken::parse_unverified(token).map_err(convert_err)?; diff --git a/src/main.rs b/src/main.rs index fa6add1..18ad715 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ use crate::{ config::Config, - jwt::KeyCollection, + jwt::{jwt_secret_from_path, verify_single_token, KeyCollection, Secret}, multiplexer::Multiplexer, transition_config::handle_transition_config, types::{ @@ -59,9 +59,11 @@ async fn main() { let body_limit_mb = config.body_limit_mb; let listen_address = config.listen_address; let listen_port = config.listen_port; + let controller_jwt_secret = jwt_secret_from_path(&config.controller_jwt_secret).unwrap(); let client_jwt_collection = KeyCollection::load(&config.client_jwt_secrets).unwrap(); let multiplexer = Multiplexer::::new(config, executor, log).unwrap(); let app_state = Arc::new(AppState { + controller_jwt_secret, client_jwt_collection, multiplexer, }); @@ -82,6 +84,7 @@ async fn main() { } struct AppState { + controller_jwt_secret: Secret, client_jwt_collection: KeyCollection, multiplexer: Multiplexer, } @@ -169,9 +172,24 @@ async fn process_client_request( async fn handle_controller_json_rpc( State(state): State>, + TypedHeader(jwt_token_str): TypedHeader>, maybe_request: Result, JsonRejection>, ) -> Result, Json> { + let jwt_secret = &state.controller_jwt_secret; let multiplexer = &state.multiplexer; + + // Check JWT auth. + if let Err(e) = verify_single_token(jwt_token_str.token(), jwt_secret) { + tracing::warn!( + error = ?e, + "Controller JWT auth failed" + ); + return Err(Json(ErrorResponse::parse_error_generic( + serde_json::json!(0), + e, + ))); + } + let Json(request) = maybe_request .map_err(|e| ErrorResponse::parse_error_generic(serde_json::json!(0), e.body_text()))?;