Skip to content
Open
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
410 changes: 201 additions & 209 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion anchor/client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ fdlimit = "0.3"
global_config = { workspace = true }
http_api = { workspace = true }
http_metrics = { workspace = true }
hyper = { workspace = true }
keygen = { workspace = true }
logging = { workspace = true }
message_receiver = { workspace = true }
Expand Down Expand Up @@ -49,6 +48,7 @@ strum = { workspace = true }
subnet_service = { workspace = true }
task_executor = { workspace = true }
tokio = { workspace = true }
tower-http = { workspace = true }
tracing = { workspace = true }
types = { workspace = true }
validator_metrics = { workspace = true }
Expand Down
15 changes: 13 additions & 2 deletions anchor/client/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -323,8 +323,19 @@ pub struct Node {
help_heading = FLAG_HEADER
)]
pub enable_high_validator_count_metrics: bool,
// TODO: Metrics CORS Origin
// https://github.com/sigp/anchor/issues/249

#[clap(
long,
value_name = "ORIGIN",
help = "Set the value of the Access-Control-Allow-Origin response HTTP header \
for the metrics server. Use * to allow any origin (not recommended in production). \
If no value is supplied, the CORS allowed origin is set to the listen \
address of this server (e.g., http://localhost:5164).",
display_order = 0,
requires = "metrics"
)]
pub metrics_allow_origin: Option<String>,

#[clap(
long,
global = true,
Expand Down
16 changes: 11 additions & 5 deletions anchor/client/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use network_utils::unused_port::{
};
use sensitive_url::SensitiveUrl;
use ssv_types::OperatorId;
use tower_http::cors::AllowOrigin;
use tracing::{error, warn};

use crate::cli::Node;
Expand Down Expand Up @@ -249,12 +250,10 @@ pub fn from_cli(cli_args: &Node, global_config: GlobalConfig) -> Result<Config,
}

if let Some(allow_origin) = &cli_args.http_allow_origin {
// Pre-validate the config value to give feedback to the user on node startup, instead of
// as late as when the first API response is produced.
hyper::header::HeaderValue::from_str(allow_origin)
let header_value = allow_origin
.parse()
.map_err(|_| "Invalid allow-origin value")?;

config.http_api.allow_origin = Some(allow_origin.to_string());
config.http_api.allow_origin = AllowOrigin::exact(header_value);
}

// Prometheus metrics HTTP server
Expand All @@ -271,6 +270,13 @@ pub fn from_cli(cli_args: &Node, global_config: GlobalConfig) -> Result<Config,
config.http_metrics.listen_port = port;
}

if let Some(allow_origin) = &cli_args.metrics_allow_origin {
let header_value = allow_origin
.parse()
.map_err(|_| "Invalid metrics-allow-origin value")?;
config.http_metrics.allow_origin = AllowOrigin::exact(header_value);
}

config.enable_high_validator_count_metrics = cli_args.enable_high_validator_count_metrics;

config.impostor = cli_args.impostor.map(OperatorId);
Expand Down
7 changes: 6 additions & 1 deletion anchor/client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,12 @@ impl Client {
.await
.map_err(|e| format!("Unable to bind to metrics server port: {e}"))?;

let metrics_future = http_metrics::serve(listener, shared_state.clone(), exit);
let metrics_future = http_metrics::serve(
listener,
shared_state.clone(),
config.http_metrics.allow_origin.clone(),
exit,
);

executor.spawn_without_exit(metrics_future, "metrics-http");

Expand Down
1 change: 1 addition & 0 deletions anchor/http_api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ slot_clock = { workspace = true }
ssv_types = { workspace = true }
task_executor = { workspace = true }
tokio = { workspace = true }
tower-http = { workspace = true }
tracing = { workspace = true }
types = { workspace = true }
version = { workspace = true }
8 changes: 4 additions & 4 deletions anchor/http_api/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@

use std::net::{IpAddr, Ipv4Addr};

use serde::{Deserialize, Serialize};
use tower_http::cors::AllowOrigin;

/// Configuration for the HTTP server.
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone)]
pub struct Config {
pub enabled: bool,
pub listen_addr: IpAddr,
pub listen_port: u16,
pub allow_origin: Option<String>,
pub allow_origin: AllowOrigin,
}

impl Default for Config {
Expand All @@ -19,7 +19,7 @@ impl Default for Config {
enabled: false,
listen_addr: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
listen_port: 5062,
allow_origin: None,
allow_origin: AllowOrigin::any(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure if any is a good default.

E.g. Lighthouse defaults to the listen address and port: https://github.com/sigp/lighthouse/blob/f3fd1f210b2f4ed7d208f81f9a09e1edced3bb3d/beacon_node/http_api/src/lib.rs#L340-L344

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah makes sense thanks Daniel.
I suggested any based on following the default behavior of None but it makes more sense to use the listen address and port as lighthouse does

}
}
}
9 changes: 7 additions & 2 deletions anchor/http_api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ mod router;

use std::{net::SocketAddr, path::PathBuf, sync::Arc};

use axum::http::Method;
pub use config::Config;
use database::NetworkState;
use parking_lot::RwLock;
use slot_clock::SlotClock;
use task_executor::TaskExecutor;
use tokio::{net::TcpListener, sync::watch};
use tower_http::cors::CorsLayer;
use tracing::info;
/// A wrapper around all the items required to spawn the HTTP server.
///
Expand Down Expand Up @@ -38,11 +40,14 @@ pub async fn run(config: Config, shared_state: Arc<RwLock<Shared>>) -> Result<()
return Ok(());
}

let cors = CorsLayer::new()
.allow_methods([Method::GET, Method::POST])
.allow_origin(config.allow_origin);

// Generate the axum routes
let router = router::new(shared_state);
let router = router::new(shared_state).layer(cors);

// Set up a listening address

let socket = SocketAddr::new(config.listen_addr, config.listen_port);
let listener = TcpListener::bind(socket).await.map_err(|e| e.to_string())?;

Expand Down
21 changes: 11 additions & 10 deletions anchor/http_metrics/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,9 @@ use axum::{
use libp2p::metrics::Registry;
use parking_lot::RwLock;
use prometheus_client::encoding::text::encode;
use serde::{Deserialize, Serialize};
use slot_clock::{SlotClock, SystemTimeSlotClock};
use tokio::net::TcpListener;
use tower_http::cors::{Any, CorsLayer};
use tower_http::cors::{AllowOrigin, CorsLayer};
use tracing::error;
use types::EthSpec;
use validator_services::duties_service::DutiesService;
Expand All @@ -41,12 +40,12 @@ pub struct Shared<E: EthSpec> {
}

/// Configuration for the HTTP server.
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone)]
pub struct Config {
pub enabled: bool,
pub listen_addr: IpAddr,
pub listen_port: u16,
pub allow_origin: Option<String>,
pub allow_origin: AllowOrigin,
}

impl Default for Config {
Expand All @@ -55,17 +54,18 @@ impl Default for Config {
enabled: false,
listen_addr: IpAddr::V4(Ipv4Addr::LOCALHOST),
listen_port: 5164,
allow_origin: None,
allow_origin: AllowOrigin::any(),
}
}
}

fn create_router<E: EthSpec>(shared_state: Arc<RwLock<Shared<E>>>) -> Router {
fn create_router<E: EthSpec>(
shared_state: Arc<RwLock<Shared<E>>>,
allow_origin: AllowOrigin,
) -> Router {
let cors = CorsLayer::new()
// allow `GET` and `POST` when accessing the resource
.allow_methods([Method::GET, Method::POST])
// allow requests from any origin
.allow_origin(Any);
.allow_origin(allow_origin);

Router::new()
.route("/metrics", get(metrics_handler))
Expand Down Expand Up @@ -148,10 +148,11 @@ async fn metrics_handler<E: EthSpec>(
pub async fn serve<E: EthSpec>(
listener: TcpListener,
shared_state: Arc<RwLock<Shared<E>>>,
allow_origin: AllowOrigin,
shutdown: impl Future<Output = ()> + Send + Sync + 'static,
) {
// Generate the axum routes
let router = create_router(shared_state);
let router = create_router(shared_state, allow_origin);

// Start the http api server
if let Err(e) = axum::serve(listener, router)
Expand Down