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
|
### Connecting a validator client
|
||||||
In order to connect a validator client to a beacon node over TLS, we need to
|
In order to connect a validator client to a beacon node over TLS, the validator
|
||||||
add the certificate to the trust store of our operating system.
|
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.
|
The process for this will vary depending on your operating system.
|
||||||
Below are the instructions for Ubuntu and Arch Linux:
|
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
|
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
|
## Troubleshooting
|
||||||
|
|
||||||
### HTTP API is unavailable or refusing connections
|
### HTTP API is unavailable or refusing connections
|
||||||
|
@ -202,6 +202,33 @@ fn use_long_timeouts_flag() {
|
|||||||
.with_config(|config| assert!(config.use_long_timeouts));
|
.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.
|
// Tests for Graffiti flags.
|
||||||
#[test]
|
#[test]
|
||||||
fn graffiti_flag() {
|
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, \
|
made to the beacon node. This flag is generally not recommended, \
|
||||||
longer timeouts can cause missed duties when fallbacks are used.")
|
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.
|
// This overwrites the graffiti configured in the beacon node.
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("graffiti")
|
Arg::with_name("graffiti")
|
||||||
|
@ -50,6 +50,9 @@ pub struct Config {
|
|||||||
/// If true, enable functionality that monitors the network for attestations or proposals from
|
/// If true, enable functionality that monitors the network for attestations or proposals from
|
||||||
/// any of the validators managed by this client before starting up.
|
/// any of the validators managed by this client before starting up.
|
||||||
pub enable_doppelganger_protection: bool,
|
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 {
|
impl Default for Config {
|
||||||
@ -80,6 +83,7 @@ impl Default for Config {
|
|||||||
http_metrics: <_>::default(),
|
http_metrics: <_>::default(),
|
||||||
monitoring_api: None,
|
monitoring_api: None,
|
||||||
enable_doppelganger_protection: false,
|
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
|
* Http API server
|
||||||
*/
|
*/
|
||||||
|
@ -38,11 +38,15 @@ use eth2::{reqwest::ClientBuilder, BeaconNodeHttpClient, StatusCode, Timeouts};
|
|||||||
use http_api::ApiSecret;
|
use http_api::ApiSecret;
|
||||||
use notifier::spawn_notifier;
|
use notifier::spawn_notifier;
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
|
use reqwest::Certificate;
|
||||||
use slog::{error, info, warn, Logger};
|
use slog::{error, info, warn, Logger};
|
||||||
use slot_clock::SlotClock;
|
use slot_clock::SlotClock;
|
||||||
use slot_clock::SystemTimeSlotClock;
|
use slot_clock::SystemTimeSlotClock;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Read;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
use std::path::Path;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
use sync_committee_service::SyncCommitteeService;
|
use sync_committee_service::SyncCommitteeService;
|
||||||
@ -246,7 +250,17 @@ impl<T: EthSpec> ProductionValidatorClient<T> {
|
|||||||
.map(|(i, url)| {
|
.map(|(i, url)| {
|
||||||
let slot_duration = Duration::from_secs(context.eth2_config.spec.seconds_per_slot);
|
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.
|
// Set default timeout to be the full slot duration.
|
||||||
.timeout(slot_duration)
|
.timeout(slot_duration)
|
||||||
.build()
|
.build()
|
||||||
@ -657,3 +671,12 @@ async fn poll_whilst_waiting_for_genesis<E: EthSpec>(
|
|||||||
sleep(WAITING_FOR_GENESIS_POLL_TIME).await;
|
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