Allow custom certificates when connecting to BN (#2703)
## Issue Addressed Resolves #2262 ## Proposed Changes Add a new CLI flag `--beacon-nodes-tls-certs` which allows the user to specify a path to a certificate file (or a list of files, separated by commas). The VC will then use these certificates (in addition to the existing certificates in the OS trust store) when connecting to a beacon node over HTTPS. ## Additional Info This only supports certificates in PEM format.
This commit is contained in:
parent
05040e68ec
commit
7c23e2142a
@ -163,8 +163,10 @@ curl -X GET "https://localhost:5052/eth/v1/node/version" -H "accept: applicatio
|
||||
|
||||
```
|
||||
### Connecting a validator client
|
||||
In order to connect a validator client to a beacon node over TLS, we need to
|
||||
add the certificate to the trust store of our operating system.
|
||||
In order to connect a validator client to a beacon node over TLS, the validator
|
||||
client needs to be aware of the certificate.
|
||||
There are two ways to do this:
|
||||
#### Option 1: Add the certificate to the operating system trust store
|
||||
The process for this will vary depending on your operating system.
|
||||
Below are the instructions for Ubuntu and Arch Linux:
|
||||
|
||||
@ -185,6 +187,13 @@ Now the validator client can be connected to the beacon node by running:
|
||||
lighthouse vc --beacon-nodes https://localhost:5052
|
||||
```
|
||||
|
||||
#### Option 2: Specify the certificate via CLI
|
||||
You can also specify any custom certificates via the validator client CLI like
|
||||
so:
|
||||
```bash
|
||||
lighthouse vc --beacon-nodes https://localhost:5052 --beacon-nodes-tls-certs cert.pem
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### HTTP API is unavailable or refusing connections
|
||||
|
@ -202,6 +202,33 @@ fn use_long_timeouts_flag() {
|
||||
.with_config(|config| assert!(config.use_long_timeouts));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn beacon_nodes_tls_certs_flag() {
|
||||
let dir = TempDir::new().expect("Unable to create temporary directory");
|
||||
CommandLineTest::new()
|
||||
.flag(
|
||||
"beacon-nodes-tls-certs",
|
||||
Some(
|
||||
vec![
|
||||
dir.path().join("certificate.crt").to_str().unwrap(),
|
||||
dir.path().join("certificate2.crt").to_str().unwrap(),
|
||||
]
|
||||
.join(",")
|
||||
.as_str(),
|
||||
),
|
||||
)
|
||||
.run()
|
||||
.with_config(|config| {
|
||||
assert_eq!(
|
||||
config.beacon_nodes_tls_certs,
|
||||
Some(vec![
|
||||
dir.path().join("certificate.crt"),
|
||||
dir.path().join("certificate2.crt")
|
||||
])
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
// Tests for Graffiti flags.
|
||||
#[test]
|
||||
fn graffiti_flag() {
|
||||
|
@ -101,6 +101,16 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
|
||||
made to the beacon node. This flag is generally not recommended, \
|
||||
longer timeouts can cause missed duties when fallbacks are used.")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("beacon-nodes-tls-certs")
|
||||
.long("beacon-nodes-tls-certs")
|
||||
.value_name("CERTIFICATE-FILES")
|
||||
.takes_value(true)
|
||||
.help("Comma-separated paths to custom TLS certificates to use when connecting \
|
||||
to a beacon node. These certificates must be in PEM format and are used \
|
||||
in addition to the OS trust store. Commas must only be used as a \
|
||||
delimiter, and must not be part of the certificate path.")
|
||||
)
|
||||
// This overwrites the graffiti configured in the beacon node.
|
||||
.arg(
|
||||
Arg::with_name("graffiti")
|
||||
|
@ -50,6 +50,9 @@ pub struct Config {
|
||||
/// If true, enable functionality that monitors the network for attestations or proposals from
|
||||
/// any of the validators managed by this client before starting up.
|
||||
pub enable_doppelganger_protection: bool,
|
||||
/// A list of custom certificates that the validator client will additionally use when
|
||||
/// connecting to a beacon node over SSL/TLS.
|
||||
pub beacon_nodes_tls_certs: Option<Vec<PathBuf>>,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
@ -80,6 +83,7 @@ impl Default for Config {
|
||||
http_metrics: <_>::default(),
|
||||
monitoring_api: None,
|
||||
enable_doppelganger_protection: false,
|
||||
beacon_nodes_tls_certs: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -193,6 +197,10 @@ impl Config {
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(tls_certs) = parse_optional::<String>(cli_args, "beacon-nodes-tls-certs")? {
|
||||
config.beacon_nodes_tls_certs = Some(tls_certs.split(',').map(PathBuf::from).collect());
|
||||
}
|
||||
|
||||
/*
|
||||
* Http API server
|
||||
*/
|
||||
|
@ -38,11 +38,15 @@ use eth2::{reqwest::ClientBuilder, BeaconNodeHttpClient, StatusCode, Timeouts};
|
||||
use http_api::ApiSecret;
|
||||
use notifier::spawn_notifier;
|
||||
use parking_lot::RwLock;
|
||||
use reqwest::Certificate;
|
||||
use slog::{error, info, warn, Logger};
|
||||
use slot_clock::SlotClock;
|
||||
use slot_clock::SystemTimeSlotClock;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::marker::PhantomData;
|
||||
use std::net::SocketAddr;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use sync_committee_service::SyncCommitteeService;
|
||||
@ -246,7 +250,17 @@ impl<T: EthSpec> ProductionValidatorClient<T> {
|
||||
.map(|(i, url)| {
|
||||
let slot_duration = Duration::from_secs(context.eth2_config.spec.seconds_per_slot);
|
||||
|
||||
let beacon_node_http_client = ClientBuilder::new()
|
||||
let mut beacon_node_http_client_builder = ClientBuilder::new();
|
||||
|
||||
// Add new custom root certificates if specified.
|
||||
if let Some(certificates) = &config.beacon_nodes_tls_certs {
|
||||
for cert in certificates {
|
||||
beacon_node_http_client_builder = beacon_node_http_client_builder
|
||||
.add_root_certificate(load_pem_certificate(cert)?);
|
||||
}
|
||||
}
|
||||
|
||||
let beacon_node_http_client = beacon_node_http_client_builder
|
||||
// Set default timeout to be the full slot duration.
|
||||
.timeout(slot_duration)
|
||||
.build()
|
||||
@ -657,3 +671,12 @@ async fn poll_whilst_waiting_for_genesis<E: EthSpec>(
|
||||
sleep(WAITING_FOR_GENESIS_POLL_TIME).await;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_pem_certificate<P: AsRef<Path>>(pem_path: P) -> Result<Certificate, String> {
|
||||
let mut buf = Vec::new();
|
||||
File::open(&pem_path)
|
||||
.map_err(|e| format!("Unable to open certificate path: {}", e))?
|
||||
.read_to_end(&mut buf)
|
||||
.map_err(|e| format!("Unable to read certificate file: {}", e))?;
|
||||
Certificate::from_pem(&buf).map_err(|e| format!("Unable to parse certificate: {}", e))
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user