From d127bd10a6caa3b7d5eced3693ec1ef3cb9623fb Mon Sep 17 00:00:00 2001 From: Leo Nash Date: Sat, 6 Dec 2025 01:47:59 +0000 Subject: [PATCH 1/2] Add option to verify JWT tokens in the HTTP Authorization header --- rust/Cargo.lock | 1 + rust/auth-impls/src/lib.rs | 4 ++- rust/server/Cargo.toml | 1 + rust/server/src/main.rs | 55 +++++++++++++++++++++--------- rust/server/src/util/config.rs | 3 +- rust/server/vss-server-config.toml | 1 + 6 files changed, 46 insertions(+), 19 deletions(-) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 6c37e11..4e32722 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -1981,6 +1981,7 @@ name = "vss-server" version = "0.1.0" dependencies = [ "api", + "auth-impls", "bytes", "http-body-util", "hyper 1.4.1", diff --git a/rust/auth-impls/src/lib.rs b/rust/auth-impls/src/lib.rs index 86c6853..2bc0fb3 100644 --- a/rust/auth-impls/src/lib.rs +++ b/rust/auth-impls/src/lib.rs @@ -14,10 +14,12 @@ use api::auth::{AuthResponse, Authorizer}; use api::error::VssError; use async_trait::async_trait; -use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation}; +use jsonwebtoken::{decode, Algorithm, Validation}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; +pub use jsonwebtoken::DecodingKey; + /// A JWT based authorizer, only allows requests with verified 'JsonWebToken' signed by the given /// issuer key. /// diff --git a/rust/server/Cargo.toml b/rust/server/Cargo.toml index 2a0e6f1..6c66812 100644 --- a/rust/server/Cargo.toml +++ b/rust/server/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] api = { path = "../api" } +auth-impls = { path = "../auth-impls" } impls = { path = "../impls" } hyper = { version = "1", default-features = false, features = ["server", "http1"] } diff --git a/rust/server/src/main.rs b/rust/server/src/main.rs index 38fdccd..88a5dc0 100644 --- a/rust/server/src/main.rs +++ b/rust/server/src/main.rs @@ -20,11 +20,14 @@ use hyper_util::rt::TokioIo; use crate::vss_service::VssService; use api::auth::{Authorizer, NoopAuthorizer}; use api::kv_store::KvStore; +use auth_impls::{DecodingKey, JWTAuthorizer}; use impls::postgres_store::{Certificate, PostgresPlaintextBackend, PostgresTlsBackend}; use std::sync::Arc; -pub(crate) mod util; -pub(crate) mod vss_service; +mod util; +mod vss_service; + +use util::config::{Config, ServerConfig}; fn main() { let args: Vec = std::env::args().collect(); @@ -33,22 +36,21 @@ fn main() { std::process::exit(1); } - let config = match util::config::load_config(&args[1]) { - Ok(cfg) => cfg, - Err(e) => { - eprintln!("Failed to load configuration: {}", e); - std::process::exit(1); - }, - }; - - let addr: SocketAddr = - match format!("{}:{}", config.server_config.host, config.server_config.port).parse() { - Ok(addr) => addr, + let Config { server_config: ServerConfig { host, port, rsa_pub_file_path }, postgresql_config } = + match util::config::load_config(&args[1]) { + Ok(cfg) => cfg, Err(e) => { - eprintln!("Invalid host/port configuration: {}", e); + eprintln!("Failed to load configuration: {}", e); std::process::exit(1); }, }; + let addr: SocketAddr = match format!("{}:{}", host, port).parse() { + Ok(addr) => addr, + Err(e) => { + eprintln!("Invalid host/port configuration: {}", e); + std::process::exit(1); + }, + }; let runtime = match tokio::runtime::Builder::new_multi_thread().enable_all().build() { Ok(runtime) => Arc::new(runtime), @@ -66,9 +68,27 @@ fn main() { std::process::exit(-1); }, }; - let authorizer: Arc = Arc::new(NoopAuthorizer {}); - let postgresql_config = - config.postgresql_config.expect("PostgreSQLConfig must be defined in config file."); + + let authorizer: Arc = if let Some(file_path) = rsa_pub_file_path { + let rsa_pub_file = match std::fs::read(file_path) { + Ok(pem) => pem, + Err(e) => { + println!("Failed to read RSA public key file: {}", e); + std::process::exit(-1); + }, + }; + let rsa_public_key = match DecodingKey::from_rsa_pem(&rsa_pub_file) { + Ok(pem) => pem, + Err(e) => { + println!("Failed to parse RSA public key file: {}", e); + std::process::exit(-1); + }, + }; + Arc::new(JWTAuthorizer::new(rsa_public_key).await) + } else { + Arc::new(NoopAuthorizer {}) + }; + let endpoint = postgresql_config.to_postgresql_endpoint(); let db_name = postgresql_config.database; let store: Arc = if let Some(tls_config) = postgresql_config.tls { @@ -109,6 +129,7 @@ fn main() { Arc::new(postgres_plaintext_backend) }; println!("Connected to PostgreSQL backend with DSN: {}/{}", endpoint, db_name); + let rest_svc_listener = TcpListener::bind(&addr).await.expect("Failed to bind listening port"); println!("Listening for incoming connections on {}", addr); diff --git a/rust/server/src/util/config.rs b/rust/server/src/util/config.rs index 801d1bd..9a79248 100644 --- a/rust/server/src/util/config.rs +++ b/rust/server/src/util/config.rs @@ -3,13 +3,14 @@ use serde::Deserialize; #[derive(Deserialize)] pub(crate) struct Config { pub(crate) server_config: ServerConfig, - pub(crate) postgresql_config: Option, + pub(crate) postgresql_config: PostgreSQLConfig, } #[derive(Deserialize)] pub(crate) struct ServerConfig { pub(crate) host: String, pub(crate) port: u16, + pub(crate) rsa_pub_file_path: Option, } #[derive(Deserialize)] diff --git a/rust/server/vss-server-config.toml b/rust/server/vss-server-config.toml index 8c3d9c0..76a4da4 100644 --- a/rust/server/vss-server-config.toml +++ b/rust/server/vss-server-config.toml @@ -1,6 +1,7 @@ [server_config] host = "127.0.0.1" port = 8080 +# rsa_pub_file_path = "rsa_public_key.pem" # Uncomment to verify JWT tokens in the HTTP Authorization header [postgresql_config] username = "postgres" # Optional in TOML, can be overridden by env var `VSS_POSTGRESQL_USERNAME` From 748532c48b0bb2eaa9e433b5de205a45f00c633c Mon Sep 17 00:00:00 2001 From: Leo Nash Date: Mon, 8 Dec 2025 16:50:18 +0000 Subject: [PATCH 2/2] fixup: Switch back to `Option` --- rust/server/src/main.rs | 2 ++ rust/server/src/util/config.rs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/rust/server/src/main.rs b/rust/server/src/main.rs index 88a5dc0..9f08b4c 100644 --- a/rust/server/src/main.rs +++ b/rust/server/src/main.rs @@ -89,6 +89,8 @@ fn main() { Arc::new(NoopAuthorizer {}) }; + let postgresql_config = + postgresql_config.expect("PostgreSQLConfig must be defined in config file."); let endpoint = postgresql_config.to_postgresql_endpoint(); let db_name = postgresql_config.database; let store: Arc = if let Some(tls_config) = postgresql_config.tls { diff --git a/rust/server/src/util/config.rs b/rust/server/src/util/config.rs index 9a79248..4462c5a 100644 --- a/rust/server/src/util/config.rs +++ b/rust/server/src/util/config.rs @@ -3,7 +3,7 @@ use serde::Deserialize; #[derive(Deserialize)] pub(crate) struct Config { pub(crate) server_config: ServerConfig, - pub(crate) postgresql_config: PostgreSQLConfig, + pub(crate) postgresql_config: Option, } #[derive(Deserialize)]