From e624e536e78fc2694870f73c1c7454e3d938b1a9 Mon Sep 17 00:00:00 2001 From: Jimmy Chen Date: Fri, 28 Nov 2025 00:56:09 +1100 Subject: [PATCH 1/2] Add User-Agent header to validator client HTTP requests Implements identification of Lighthouse validator client in HTTP requests to beacon nodes using User-Agent header with format 'Lighthouse/v'. This helps distributed validator middleware operators identify what their peers are running. Fixes #7963 --- Cargo.lock | 1 + common/eth2/Cargo.toml | 1 + common/eth2/src/lib.rs | 15 ++++++++++++++- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index a1ad2ab5ba7..a18c48d5d9d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3147,6 +3147,7 @@ dependencies = [ "futures", "futures-util", "libp2p-identity", + "lighthouse_version", "mediatype", "multiaddr", "pretty_reqwest_error", diff --git a/common/eth2/Cargo.toml b/common/eth2/Cargo.toml index 7a75bdc80a1..2cd62db0508 100644 --- a/common/eth2/Cargo.toml +++ b/common/eth2/Cargo.toml @@ -20,6 +20,7 @@ ethereum_ssz_derive = { workspace = true } futures = { workspace = true } futures-util = "0.3.8" libp2p-identity = { version = "0.2", features = ["peerid"] } +lighthouse_version = { workspace = true } mediatype = "0.19.13" multiaddr = "0.18.2" pretty_reqwest_error = { workspace = true } diff --git a/common/eth2/src/lib.rs b/common/eth2/src/lib.rs index bcd979daca6..e7ab818d9ac 100644 --- a/common/eth2/src/lib.rs +++ b/common/eth2/src/lib.rs @@ -23,6 +23,7 @@ use educe::Educe; use futures::Stream; use futures_util::StreamExt; use libp2p_identity::PeerId; +use lighthouse_version::VERSION; pub use reqwest; use reqwest::{ Body, IntoUrl, RequestBuilder, Response, @@ -154,8 +155,20 @@ impl fmt::Display for BeaconNodeHttpClient { impl BeaconNodeHttpClient { pub fn new(server: SensitiveUrl, timeouts: Timeouts) -> Self { + let mut headers = HeaderMap::new(); + + // Add User-Agent header to identify the client + if let Ok(user_agent) = HeaderValue::from_str(VERSION) { + headers.insert(reqwest::header::USER_AGENT, user_agent); + } + + let client = reqwest::Client::builder() + .default_headers(headers) + .build() + .unwrap_or_else(|_| reqwest::Client::new()); + Self { - client: reqwest::Client::new(), + client, server, timeouts, } From d9a5a23b26bee4c4393927c1b15dc75fcf7e274a Mon Sep 17 00:00:00 2001 From: dapplion <35266934+dapplion@users.noreply.github.com> Date: Mon, 9 Feb 2026 20:43:08 -0700 Subject: [PATCH 2/2] add user-agent header to all http clients --- Cargo.lock | 2 ++ .../execution_layer/src/engine_api/http.rs | 4 ++-- common/eth2/src/lib.rs | 9 +-------- common/eth2/src/lighthouse_vc/http_client.rs | 15 +++++++++++++-- common/monitoring_api/src/lib.rs | 7 ++++++- validator_client/Cargo.toml | 1 + .../initialized_validators/Cargo.toml | 1 + .../initialized_validators/src/lib.rs | 1 + validator_client/src/lib.rs | 3 ++- 9 files changed, 29 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a18c48d5d9d..86313a2a997 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4678,6 +4678,7 @@ dependencies = [ "bls", "eth2_keystore", "filesystem", + "lighthouse_version", "lockfile", "metrics", "parking_lot", @@ -9754,6 +9755,7 @@ dependencies = [ "hyper 1.8.1", "initialized_validators", "lighthouse_validator_store", + "lighthouse_version", "metrics", "monitoring_api", "parking_lot", diff --git a/beacon_node/execution_layer/src/engine_api/http.rs b/beacon_node/execution_layer/src/engine_api/http.rs index 8f7564ace6b..73719011843 100644 --- a/beacon_node/execution_layer/src/engine_api/http.rs +++ b/beacon_node/execution_layer/src/engine_api/http.rs @@ -613,7 +613,7 @@ impl HttpJsonRpc { execution_timeout_multiplier: Option, ) -> Result { Ok(Self { - client: Client::builder().build()?, + client: Client::builder().user_agent(VERSION).build()?, url, execution_timeout_multiplier: execution_timeout_multiplier.unwrap_or(1), engine_capabilities_cache: Mutex::new(None), @@ -628,7 +628,7 @@ impl HttpJsonRpc { execution_timeout_multiplier: Option, ) -> Result { Ok(Self { - client: Client::builder().build()?, + client: Client::builder().user_agent(VERSION).build()?, url, execution_timeout_multiplier: execution_timeout_multiplier.unwrap_or(1), engine_capabilities_cache: Mutex::new(None), diff --git a/common/eth2/src/lib.rs b/common/eth2/src/lib.rs index e7ab818d9ac..a0644ce494f 100644 --- a/common/eth2/src/lib.rs +++ b/common/eth2/src/lib.rs @@ -155,15 +155,8 @@ impl fmt::Display for BeaconNodeHttpClient { impl BeaconNodeHttpClient { pub fn new(server: SensitiveUrl, timeouts: Timeouts) -> Self { - let mut headers = HeaderMap::new(); - - // Add User-Agent header to identify the client - if let Ok(user_agent) = HeaderValue::from_str(VERSION) { - headers.insert(reqwest::header::USER_AGENT, user_agent); - } - let client = reqwest::Client::builder() - .default_headers(headers) + .user_agent(VERSION) .build() .unwrap_or_else(|_| reqwest::Client::new()); diff --git a/common/eth2/src/lighthouse_vc/http_client.rs b/common/eth2/src/lighthouse_vc/http_client.rs index 8c9d3397a8c..cc51496ba91 100644 --- a/common/eth2/src/lighthouse_vc/http_client.rs +++ b/common/eth2/src/lighthouse_vc/http_client.rs @@ -1,5 +1,6 @@ use super::types::*; use crate::{Error, success_or_error}; +use lighthouse_version::VERSION; use reqwest::{ IntoUrl, header::{HeaderMap, HeaderValue}, @@ -45,8 +46,13 @@ impl Display for AuthorizationHeader { impl ValidatorClientHttpClient { /// Create a new client pre-initialised with an API token. pub fn new(server: SensitiveUrl, secret: String) -> Result { + let client = reqwest::Client::builder() + .user_agent(VERSION) + .build() + .unwrap_or_else(|_| reqwest::Client::new()); + Ok(Self { - client: reqwest::Client::new(), + client, server, api_token: Some(secret.into()), authorization_header: AuthorizationHeader::Bearer, @@ -57,8 +63,13 @@ impl ValidatorClientHttpClient { /// /// A token can be fetched by using `self.get_auth`, and then reading the token from disk. pub fn new_unauthenticated(server: SensitiveUrl) -> Result { + let client = reqwest::Client::builder() + .user_agent(VERSION) + .build() + .unwrap_or_else(|_| reqwest::Client::new()); + Ok(Self { - client: reqwest::Client::new(), + client, server, api_token: None, authorization_header: AuthorizationHeader::Omit, diff --git a/common/monitoring_api/src/lib.rs b/common/monitoring_api/src/lib.rs index 03b93f2faae..0dd9c187d6f 100644 --- a/common/monitoring_api/src/lib.rs +++ b/common/monitoring_api/src/lib.rs @@ -73,8 +73,13 @@ pub struct MonitoringHttpClient { impl MonitoringHttpClient { pub fn new(config: &Config) -> Result { + let client = reqwest::Client::builder() + .user_agent(lighthouse_version::VERSION) + .build() + .unwrap_or_else(|_| reqwest::Client::new()); + Ok(Self { - client: reqwest::Client::new(), + client, db_path: config.db_path.clone(), freezer_db_path: config.freezer_db_path.clone(), update_period: Duration::from_secs( diff --git a/validator_client/Cargo.toml b/validator_client/Cargo.toml index 6990a2f61a7..42c650ca3ab 100644 --- a/validator_client/Cargo.toml +++ b/validator_client/Cargo.toml @@ -23,6 +23,7 @@ graffiti_file = { workspace = true } hyper = { workspace = true } initialized_validators = { workspace = true } lighthouse_validator_store = { workspace = true } +lighthouse_version = { workspace = true } metrics = { workspace = true } monitoring_api = { workspace = true } parking_lot = { workspace = true } diff --git a/validator_client/initialized_validators/Cargo.toml b/validator_client/initialized_validators/Cargo.toml index 8b2ae62aea3..a1860c53efb 100644 --- a/validator_client/initialized_validators/Cargo.toml +++ b/validator_client/initialized_validators/Cargo.toml @@ -10,6 +10,7 @@ bincode = { workspace = true } bls = { workspace = true } eth2_keystore = { workspace = true } filesystem = { workspace = true } +lighthouse_version = { workspace = true } lockfile = { workspace = true } metrics = { workspace = true } parking_lot = { workspace = true } diff --git a/validator_client/initialized_validators/src/lib.rs b/validator_client/initialized_validators/src/lib.rs index 4d61bd4ed81..1172b640528 100644 --- a/validator_client/initialized_validators/src/lib.rs +++ b/validator_client/initialized_validators/src/lib.rs @@ -422,6 +422,7 @@ fn build_web3_signer_client( max_idle_connections: Option, ) -> Result { let builder = Client::builder() + .user_agent(lighthouse_version::VERSION) .timeout(request_timeout) .pool_idle_timeout(keep_alive_timeout) .pool_max_idle_per_host(max_idle_connections.unwrap_or(usize::MAX)); diff --git a/validator_client/src/lib.rs b/validator_client/src/lib.rs index 71bdde10b02..25ca7d7fb2e 100644 --- a/validator_client/src/lib.rs +++ b/validator_client/src/lib.rs @@ -272,7 +272,8 @@ impl ProductionValidatorClient { let url = x.1; let slot_duration = Duration::from_secs(context.eth2_config.spec.seconds_per_slot); - let mut beacon_node_http_client_builder = ClientBuilder::new(); + let mut beacon_node_http_client_builder = + ClientBuilder::new().user_agent(lighthouse_version::VERSION); // Add new custom root certificates if specified. if let Some(certificates) = &config.beacon_nodes_tls_certs {