Skip to content
Draft
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
7 changes: 7 additions & 0 deletions config.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,13 @@ jwt_auth_fail_timeout_seconds = 300
# [signer.remote]
# URL of the Web3Signer instance
# url = "https://remote.signer.url"
# Path to the client certificate for client authentication
# OPTIONAL
# cert_path = "/path/to/client.crt"
# Path to the client key for client authentication
# OPTIONAL
# key_path = "/path/to/client.key"

# For Dirk signer:
# [signer.dirk]
# Path to the client certificate to authenticate with Dirk
Expand Down
4 changes: 3 additions & 1 deletion crates/cli/src/docker_init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ pub async fn handle_docker_init(config_path: PathBuf, output_dir: PathBuf) -> Re
// address for signer API communication
let signer_port = cb_config.signer.as_ref().map(|s| s.port).unwrap_or(SIGNER_PORT_DEFAULT);
let signer_server =
if let Some(SignerConfig { inner: SignerType::Remote { url }, .. }) = &cb_config.signer {
if let Some(SignerConfig { inner: SignerType::Remote { url, client_auth: _ }, .. }) =
&cb_config.signer
{
url.to_string()
} else {
format!("http://cb_signer:{signer_port}")
Expand Down
29 changes: 23 additions & 6 deletions crates/common/src/commit/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ use std::time::{Duration, Instant};

use alloy::primitives::Address;
use eyre::WrapErr;
use reqwest::header::{AUTHORIZATION, HeaderMap, HeaderValue};
use reqwest::{
Identity,
header::{AUTHORIZATION, HeaderMap, HeaderValue},
};
use serde::Deserialize;
use url::Url;

Expand All @@ -16,6 +19,7 @@ use super::{
};
use crate::{
DEFAULT_REQUEST_TIMEOUT,
config::ClientAuthConfig,
constants::SIGNER_JWT_EXPIRATION,
signer::EcdsaSignature,
types::{BlsPublicKey, BlsSignature, Jwt, ModuleId},
Expand All @@ -35,7 +39,12 @@ pub struct SignerClient {

impl SignerClient {
/// Create a new SignerClient
pub fn new(signer_server_url: Url, jwt_secret: Jwt, module_id: ModuleId) -> eyre::Result<Self> {
pub fn new(
signer_server_url: Url,
jwt_secret: Jwt,
module_id: ModuleId,
client_auth: Option<ClientAuthConfig>,
) -> eyre::Result<Self> {
let jwt = create_jwt(&module_id, &jwt_secret)?;

let mut auth_value =
Expand All @@ -45,10 +54,18 @@ impl SignerClient {
let mut headers = HeaderMap::new();
headers.insert(AUTHORIZATION, auth_value);

let client = reqwest::Client::builder()
.timeout(DEFAULT_REQUEST_TIMEOUT)
.default_headers(headers)
.build()?;
let mut client =
reqwest::Client::builder().timeout(DEFAULT_REQUEST_TIMEOUT).default_headers(headers);

if let Some(ClientAuthConfig { cert_path, key_path }) = client_auth {
let cert = std::fs::read_to_string(cert_path)?;
let key = std::fs::read_to_string(key_path)?;
let buffer = format!("{cert}\n{key}");
let identity = Identity::from_pem(buffer.as_bytes())?;
client = client.identity(identity);
}

let client = client.build()?;

Ok(Self {
url: signer_server_url,
Expand Down
13 changes: 12 additions & 1 deletion crates/common/src/config/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use toml::Table;
use crate::{
commit::client::SignerClient,
config::{
SignerConfig, SignerType,
constants::{CONFIG_ENV, MODULE_ID_ENV, MODULE_JWT_ENV, SIGNER_URL_ENV},
load_env_var,
utils::load_file_from_env,
Expand Down Expand Up @@ -79,6 +80,7 @@ pub fn load_commit_module_config<T: DeserializeOwned>() -> Result<StartCommitMod
struct StubConfig<U> {
chain: Chain,
modules: Vec<ThisModule<U>>,
signer: Option<SignerConfig>,
}

// load module config including the extra data (if any)
Expand All @@ -101,7 +103,16 @@ pub fn load_commit_module_config<T: DeserializeOwned>() -> Result<StartCommitMod
.find(|m| m.static_config.id == module_id)
.wrap_err(format!("failed to find module for {module_id}"))?;

let signer_client = SignerClient::new(signer_server_url, module_jwt, module_id)?;
let client_auth = if let Some(signer) = cb_config.signer {
match signer.inner {
SignerType::Remote { url: _, client_auth } => client_auth,
_ => None,
}
} else {
None
};

let signer_client = SignerClient::new(signer_server_url, module_jwt, module_id, client_auth)?;

Ok(StartCommitModuleConfig {
id: module_config.static_config.id,
Expand Down
14 changes: 12 additions & 2 deletions crates/common/src/config/pbs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use crate::{
commit::client::SignerClient,
config::{
CONFIG_ENV, MODULE_JWT_ENV, MuxKeysLoader, PBS_MODULE_NAME, PbsMuxes, SIGNER_URL_ENV,
load_env_var, load_file_from_env,
SignerConfig, SignerType, load_env_var, load_file_from_env,
},
pbs::{
DEFAULT_PBS_PORT, DEFAULT_REGISTRY_REFRESH_SECONDS, DefaultTimeout, LATE_IN_SLOT_TIME_MS,
Expand Down Expand Up @@ -323,6 +323,7 @@ pub async fn load_pbs_custom_config<T: DeserializeOwned>() -> Result<(PbsModuleC
relays: Vec<RelayConfig>,
pbs: CustomPbsConfig<U>,
muxes: Option<PbsMuxes>,
signer: Option<SignerConfig>,
}

// load module config including the extra data (if any)
Expand Down Expand Up @@ -375,13 +376,22 @@ pub async fn load_pbs_custom_config<T: DeserializeOwned>() -> Result<(PbsModuleC
let all_relays = all_relays.into_values().collect();

let signer_client = if cb_config.pbs.static_config.with_signer {
// if custom pbs requires a signer client, load jwt
// if custom pbs requires a signer client, load jwt and client auth info
let module_jwt = Jwt(load_env_var(MODULE_JWT_ENV)?);
let signer_server_url = load_env_var(SIGNER_URL_ENV)?.parse()?;
let client_auth = if let Some(signer) = cb_config.signer {
match signer.inner {
SignerType::Remote { url: _, client_auth } => client_auth,
_ => None,
}
} else {
None
};
Some(SignerClient::new(
signer_server_url,
module_jwt,
ModuleId(PBS_MODULE_NAME.to_string()),
client_auth,
)?)
} else {
None
Expand Down
13 changes: 13 additions & 0 deletions crates/common/src/config/signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,16 @@ pub struct DirkHostConfig {
pub wallets: Vec<String>,
}

/// Client authentication configuration for remote signers
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "snake_case")]
pub struct ClientAuthConfig {
/// Path to the client certificate
pub cert_path: PathBuf,
/// Path to the client key
pub key_path: PathBuf,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "snake_case")]
pub enum SignerType {
Expand All @@ -97,6 +107,9 @@ pub enum SignerType {
Remote {
/// Complete URL of the base API endpoint
url: Url,
/// Client authentication configuration
#[serde(flatten)]
client_auth: Option<ClientAuthConfig>,
},
/// Dirk remote signer module
Dirk {
Expand Down
9 changes: 9 additions & 0 deletions docs/docs/get_started/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,15 @@ Web3Signer implements the same API as Commit-Boost, so there's no need to set up
url = "https://remote.signer.url"
```

Optionally, you can also provide a client certificate and corresponding private key if the remote signer requires client authentication:

```toml
[signer.remote]
url = "https://remote.signer.url"
cert_path = "/path/to/client.crt"
key_path = "/path/to/client.key"
```

#### Dirk

Dirk is a distributed key management system that can be used to sign transactions. In this case the Signer module is needed as an intermediary between the modules and Dirk. The following parameters are needed:
Expand Down
Loading