diff --git a/config.example.toml b/config.example.toml index ae69c3ff..89d472c1 100644 --- a/config.example.toml +++ b/config.example.toml @@ -148,6 +148,13 @@ url = "http://0xa119589bb33ef52acbb8116832bec2b58fca590fe5c85eac5d3230b44d5bc09f # Docker image to use for the Signer module. # OPTIONAL, DEFAULT: ghcr.io/commit-boost/signer:latest # docker_image = "ghcr.io/commit-boost/signer:latest" +# Host to bind the Signer API server to +# OPTIONAL, DEFAULT: 127.0.0.1 +host = "127.0.0.1" +# Port to listen for Signer API calls on +# OPTIONAL, DEFAULT: 20000 +port = 20000 + # For Remote signer: # [signer.remote] # URL of the Web3Signer instance diff --git a/crates/cli/src/docker_init.rs b/crates/cli/src/docker_init.rs index 2f8802ee..7f418e97 100644 --- a/crates/cli/src/docker_init.rs +++ b/crates/cli/src/docker_init.rs @@ -14,11 +14,11 @@ use cb_common::{ PBS_MODULE_NAME, PROXY_DIR_DEFAULT, PROXY_DIR_ENV, PROXY_DIR_KEYS_DEFAULT, PROXY_DIR_KEYS_ENV, PROXY_DIR_SECRETS_DEFAULT, PROXY_DIR_SECRETS_ENV, SIGNER_DEFAULT, SIGNER_DIR_KEYS_DEFAULT, SIGNER_DIR_KEYS_ENV, SIGNER_DIR_SECRETS_DEFAULT, - SIGNER_DIR_SECRETS_ENV, SIGNER_KEYS_ENV, SIGNER_MODULE_NAME, SIGNER_PORT_ENV, + SIGNER_DIR_SECRETS_ENV, SIGNER_ENDPOINT_ENV, SIGNER_KEYS_ENV, SIGNER_MODULE_NAME, SIGNER_URL_ENV, }, pbs::{BUILDER_API_PATH, GET_STATUS_PATH}, - signer::{ProxyStore, SignerLoader}, + signer::{ProxyStore, SignerLoader, DEFAULT_SIGNER_PORT}, types::ModuleId, utils::random_jwt_secret, }; @@ -73,7 +73,7 @@ pub async fn handle_docker_init(config_path: PathBuf, output_dir: PathBuf) -> Re let mut targets = Vec::new(); // address for signer API communication - let signer_port = 20000; + let signer_port = cb_config.signer.as_ref().map(|s| s.port).unwrap_or(DEFAULT_SIGNER_PORT); let signer_server = if let Some(SignerConfig { inner: SignerType::Remote { url }, .. }) = &cb_config.signer { url.to_string() @@ -333,10 +333,17 @@ pub async fn handle_docker_init(config_path: PathBuf, output_dir: PathBuf) -> Re let mut signer_envs = IndexMap::from([ get_env_val(CONFIG_ENV, CONFIG_DEFAULT), get_env_same(JWTS_ENV), - get_env_uval(SIGNER_PORT_ENV, signer_port as u64), ]); - let mut ports = vec![]; + // Bind the signer API to 0.0.0.0 + let container_endpoint = + SocketAddr::from((Ipv4Addr::UNSPECIFIED, signer_config.port)); + let (key, val) = get_env_val(SIGNER_ENDPOINT_ENV, &container_endpoint.to_string()); + signer_envs.insert(key, val); + + let host_endpoint = SocketAddr::from((signer_config.host, signer_config.port)); + let mut ports = vec![format!("{}:{}", host_endpoint, signer_config.port)]; + warnings.push(format!("cb_signer has an exported port on {}", signer_config.port)); if let Some((key, val)) = chain_spec_env.clone() { signer_envs.insert(key, val); @@ -458,13 +465,20 @@ pub async fn handle_docker_init(config_path: PathBuf, output_dir: PathBuf) -> Re let mut signer_envs = IndexMap::from([ get_env_val(CONFIG_ENV, CONFIG_DEFAULT), get_env_same(JWTS_ENV), - get_env_uval(SIGNER_PORT_ENV, signer_port as u64), get_env_val(DIRK_CERT_ENV, DIRK_CERT_DEFAULT), get_env_val(DIRK_KEY_ENV, DIRK_KEY_DEFAULT), get_env_val(DIRK_DIR_SECRETS_ENV, DIRK_DIR_SECRETS_DEFAULT), ]); - let mut ports = vec![]; + // Bind the signer API to 0.0.0.0 + let container_endpoint = + SocketAddr::from((Ipv4Addr::UNSPECIFIED, signer_config.port)); + let (key, val) = get_env_val(SIGNER_ENDPOINT_ENV, &container_endpoint.to_string()); + signer_envs.insert(key, val); + + let host_endpoint = SocketAddr::from((signer_config.host, signer_config.port)); + let mut ports = vec![format!("{}:{}", host_endpoint, signer_config.port)]; + warnings.push(format!("cb_signer has an exported port on {}", signer_config.port)); if let Some((key, val)) = chain_spec_env.clone() { signer_envs.insert(key, val); diff --git a/crates/common/src/config/constants.rs b/crates/common/src/config/constants.rs index 31580cd8..3f93ce27 100644 --- a/crates/common/src/config/constants.rs +++ b/crates/common/src/config/constants.rs @@ -33,7 +33,7 @@ pub const SIGNER_IMAGE_DEFAULT: &str = "ghcr.io/commit-boost/signer:latest"; pub const SIGNER_MODULE_NAME: &str = "signer"; /// Where the signer module should open the server -pub const SIGNER_PORT_ENV: &str = "CB_SIGNER_PORT"; +pub const SIGNER_ENDPOINT_ENV: &str = "CB_SIGNER_ENDPOINT"; /// Comma separated list module_id=jwt_secret pub const JWTS_ENV: &str = "CB_JWTS"; diff --git a/crates/common/src/config/signer.rs b/crates/common/src/config/signer.rs index 9df6b948..5618f3ae 100644 --- a/crates/common/src/config/signer.rs +++ b/crates/common/src/config/signer.rs @@ -1,4 +1,8 @@ -use std::{collections::HashMap, path::PathBuf}; +use std::{ + collections::HashMap, + net::{Ipv4Addr, SocketAddr}, + path::PathBuf, +}; use eyre::{bail, OptionExt, Result}; use serde::{Deserialize, Serialize}; @@ -6,18 +10,25 @@ use tonic::transport::{Certificate, Identity}; use url::Url; use super::{ - constants::SIGNER_IMAGE_DEFAULT, load_jwt_secrets, utils::load_env_var, CommitBoostConfig, - SIGNER_PORT_ENV, + load_jwt_secrets, load_optional_env_var, utils::load_env_var, CommitBoostConfig, + SIGNER_ENDPOINT_ENV, SIGNER_IMAGE_DEFAULT, }; use crate::{ config::{DIRK_CA_CERT_ENV, DIRK_CERT_ENV, DIRK_DIR_SECRETS_ENV, DIRK_KEY_ENV}, - signer::{ProxyStore, SignerLoader}, + signer::{ProxyStore, SignerLoader, DEFAULT_SIGNER_PORT}, types::{Chain, ModuleId}, + utils::{default_host, default_u16}, }; #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(rename_all = "snake_case")] pub struct SignerConfig { + /// Host address to listen for signer API calls on + #[serde(default = "default_host")] + pub host: Ipv4Addr, + /// Port to listen for signer API calls on + #[serde(default = "default_u16::")] + pub port: u16, /// Docker image of the module #[serde(default = "default_signer")] pub docker_image: String, @@ -87,7 +98,7 @@ pub struct StartSignerConfig { pub chain: Chain, pub loader: Option, pub store: Option, - pub server_port: u16, + pub endpoint: SocketAddr, pub jwts: HashMap, pub dirk: Option, } @@ -97,15 +108,22 @@ impl StartSignerConfig { let config = CommitBoostConfig::from_env_path()?; let jwts = load_jwt_secrets()?; - let server_port = load_env_var(SIGNER_PORT_ENV)?.parse()?; - let signer = config.signer.ok_or_eyre("Signer config is missing")?.inner; + let signer_config = config.signer.ok_or_eyre("Signer config is missing")?; + + // Load the server endpoint first from the env var if present, otherwise the + // config + let endpoint = if let Some(endpoint) = load_optional_env_var(SIGNER_ENDPOINT_ENV) { + endpoint.parse()? + } else { + SocketAddr::from((signer_config.host, signer_config.port)) + }; - match signer { + match signer_config.inner { SignerType::Local { loader, store, .. } => Ok(StartSignerConfig { chain: config.chain, loader: Some(loader), - server_port, + endpoint, jwts, store, dirk: None, @@ -133,7 +151,7 @@ impl StartSignerConfig { Ok(StartSignerConfig { chain: config.chain, - server_port, + endpoint, jwts, loader: None, store, diff --git a/crates/common/src/signer/constants.rs b/crates/common/src/signer/constants.rs new file mode 100644 index 00000000..aa834f91 --- /dev/null +++ b/crates/common/src/signer/constants.rs @@ -0,0 +1 @@ +pub const DEFAULT_SIGNER_PORT: u16 = 20000; diff --git a/crates/common/src/signer/mod.rs b/crates/common/src/signer/mod.rs index e0a164a7..b6dce29d 100644 --- a/crates/common/src/signer/mod.rs +++ b/crates/common/src/signer/mod.rs @@ -1,8 +1,10 @@ +mod constants; mod loader; mod schemes; mod store; mod types; +pub use constants::*; pub use loader::*; pub use schemes::*; pub use store::*; diff --git a/crates/signer/src/service.rs b/crates/signer/src/service.rs index 28a1d934..a965f057 100644 --- a/crates/signer/src/service.rs +++ b/crates/signer/src/service.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, net::SocketAddr, sync::Arc}; +use std::{collections::HashMap, sync::Arc}; use axum::{ extract::{Request, State}, @@ -67,7 +67,7 @@ impl SigningService { let loaded_consensus = state.manager.read().await.available_consensus_signers(); let loaded_proxies = state.manager.read().await.available_proxy_signers(); - info!(version = COMMIT_BOOST_VERSION, commit_hash = COMMIT_BOOST_COMMIT, modules =? module_ids, port =? config.server_port, loaded_consensus, loaded_proxies, "Starting signing service"); + info!(version = COMMIT_BOOST_VERSION, commit_hash = COMMIT_BOOST_COMMIT, modules =? module_ids, endpoint =? config.endpoint, loaded_consensus, loaded_proxies, "Starting signing service"); SigningService::init_metrics(config.chain)?; @@ -81,8 +81,7 @@ impl SigningService { .route_layer(middleware::from_fn(log_request)) .route(STATUS_PATH, get(handle_status)); - let address = SocketAddr::from(([0, 0, 0, 0], config.server_port)); - let listener = TcpListener::bind(address).await?; + let listener = TcpListener::bind(config.endpoint).await?; axum::serve(listener, app).await.wrap_err("signer server exited") } diff --git a/docs/docs/get_started/building.md b/docs/docs/get_started/building.md index a00b36cf..589b325c 100644 --- a/docs/docs/get_started/building.md +++ b/docs/docs/get_started/building.md @@ -121,6 +121,8 @@ url = "https://0xafa4c6985aa049fb79dd37010438cfebeb0f2bd42b115b89dd678dab0670c1d enabled = true [signer] +port = 20000 + [signer.local.loader] format = "lighthouse" keys_path = "/tmp/keys" @@ -155,20 +157,19 @@ This will create a binary in `./target/release/commit-boost-signer`. To verify i The signer needs the following environment variables set: - `CB_CONFIG` = path of your config file. - `CB_JWTS` = a dummy key-value pair of [JWT](https://en.wikipedia.org/wiki/JSON_Web_Token) values for various services. Since we don't need them for the sake of just testing the binary, we can use something like `"test_jwts=dummy"`. -- `CB_SIGNER_PORT` = the network port to listen for signer requests on. Default is `20000`. Set these values, create the `keys` and `secrets` directories listed in the configuration file, and run the binary: ``` mkdir -p /tmp/keys && mkdir -p /tmp/secrets -CB_CONFIG=cb-config.toml CB_JWTS="test_jwts=dummy" CB_SIGNER_PORT=20000 ./target/release/commit-boost-signer +CB_CONFIG=cb-config.toml CB_JWTS="test_jwts=dummy" ./target/release/commit-boost-signer ``` You should see output like this: ``` -2025-05-07T21:43:46.385535Z WARN Proxy store not configured. Proxies keys and delegations will not be persisted -2025-05-07T21:43:46.393507Z INFO Starting signing service version="0.7.0" commit_hash="58082edb1213596667afe8c3950cd997ab85f4f3" modules=["test_jwts"] port=20000 loaded_consensus=0 loaded_proxies=0 -2025-05-07T21:43:46.393574Z WARN No metrics server configured +2025-06-03T04:57:19.815702Z WARN Proxy store not configured. Proxies keys and delegations will not be persisted +2025-06-03T04:57:19.818193Z INFO Starting signing service version="0.8.0-rc.1" commit_hash="3eed5268f07803c55cca7d7e2e14a7017098f797" modules=["test"] endpoint=127.0.0.1:20000 loaded_consensus=0 loaded_proxies=0 +2025-06-03T04:57:19.818229Z WARN No metrics server configured ``` If you do, then the binary works. \ No newline at end of file diff --git a/docs/docs/get_started/configuration.md b/docs/docs/get_started/configuration.md index 4e642205..efe9da3f 100644 --- a/docs/docs/get_started/configuration.md +++ b/docs/docs/get_started/configuration.md @@ -39,6 +39,13 @@ Commit-Boost supports both local and remote signers. The signer module is respon To start a local signer module, you need to include its parameters in the config file ```toml +[pbs] +... +with_signer = true + +[signer] +port = 20000 + [signer.local.loader] format = "lighthouse" keys_path = "/path/to/keys" @@ -64,7 +71,13 @@ We currently support Lighthouse, Prysm, Teku and Lodestar's keystores so it's ea #### Config: ```toml + [pbs] + ... + with_signer = true + [signer] + port = 20000 + [signer.local.loader] format = "lighthouse" keys_path = "keys" @@ -87,7 +100,13 @@ We currently support Lighthouse, Prysm, Teku and Lodestar's keystores so it's ea #### Config: ```toml + [pbs] + ... + with_signer = true + [signer] + port = 20000 + [signer.local.loader] format = "prysm" keys_path = "wallet/direct/accounts/all-accounts.keystore.json" @@ -110,7 +129,13 @@ We currently support Lighthouse, Prysm, Teku and Lodestar's keystores so it's ea #### Config: ```toml + [pbs] + ... + with_signer = true + [signer] + port = 20000 + [signer.local.loader] format = "teku" keys_path = "keys" @@ -132,7 +157,13 @@ We currently support Lighthouse, Prysm, Teku and Lodestar's keystores so it's ea #### Config: ```toml + [pbs] + ... + with_signer = true + [signer] + port = 20000 + [signer.local.loader] format = "lodestar" keys_path = "keys" @@ -299,6 +330,8 @@ port = 18550 url = "" [signer] +port = 20000 + [signer.loader] format = "lighthouse" keys_path = "/path/to/keys" diff --git a/docs/docs/get_started/running/binary.md b/docs/docs/get_started/running/binary.md index ef5b8393..385e7a0c 100644 --- a/docs/docs/get_started/running/binary.md +++ b/docs/docs/get_started/running/binary.md @@ -22,11 +22,11 @@ Modules need some environment variables to work correctly. ### PBS Module - `CB_BUILDER_URLS`: optional, comma-separated list of urls to `events` modules where to post builder events. -- `CB_PBS_ENDPOINT`: optional, override the endpoint where the PBS module will open the port for the beacon node. +- `CB_PBS_ENDPOINT`: optional, override to specify the `IP:port` endpoint where the PBS module will open the port for the beacon node. - `CB_MUX_PATH_{ID}`: optional, override where to load mux validator keys for mux with `id=\{ID\}`. ### Signer Module -- `CB_SIGNER_PORT`: required, port to open the signer server on. +- `CB_SIGNER_ENDPOINT`: optional, override to specify the `IP:port` endpoint to bind the signer server to. - For loading keys we currently support: - `CB_SIGNER_LOADER_FILE`: path to a `.json` with plaintext keys (for testing purposes only). - `CB_SIGNER_LOADER_FORMAT`, `CB_SIGNER_LOADER_KEYS_DIR` and `CB_SIGNER_LOADER_SECRETS_DIR`: paths to the `keys` and `secrets` directories or files (ERC-2335 style keystores, see [Signer config](../configuration/#signer-module) for more info).