don't sign X.509 certs (#34896)

This get rid of 3rd party components rcgen in the path of private key access to make the code more secure.
This commit is contained in:
Lijun Wang 2024-01-28 16:17:46 -08:00 committed by GitHub
parent b9815da6cc
commit 8fde8d26c7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 128 additions and 332 deletions

69
Cargo.lock generated
View File

@ -580,12 +580,6 @@ version = "0.21.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
[[package]]
name = "base64ct"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "874f8444adcb4952a8bc51305c8be95c8ec8237bb0d2e78d2e039f771f8828a0"
[[package]]
name = "bincode"
version = "1.3.3"
@ -1273,12 +1267,6 @@ dependencies = [
"web-sys",
]
[[package]]
name = "const-oid"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3"
[[package]]
name = "const_format"
version = "0.2.32"
@ -1581,15 +1569,6 @@ version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57"
[[package]]
name = "der"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c"
dependencies = [
"const-oid",
]
[[package]]
name = "der-parser"
version = "8.1.0"
@ -3900,17 +3879,6 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkcs8"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0"
dependencies = [
"der",
"spki",
"zeroize",
]
[[package]]
name = "pkg-config"
version = "0.3.22"
@ -4419,18 +4387,6 @@ dependencies = [
name = "rbpf-cli"
version = "1.18.0"
[[package]]
name = "rcgen"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffbe84efe2f38dea12e9bfc1f65377fdf03e53a18cb3b995faedf7934c7e785b"
dependencies = [
"pem",
"ring 0.16.20",
"time",
"yasna",
]
[[package]]
name = "rdrand"
version = "0.4.0"
@ -5895,7 +5851,6 @@ dependencies = [
"rand 0.8.5",
"rand_chacha 0.3.1",
"rayon",
"rcgen",
"solana-logger",
"solana-measure",
"solana-metrics",
@ -5934,7 +5889,6 @@ dependencies = [
"rand_chacha 0.3.1",
"raptorq",
"rayon",
"rcgen",
"rolling-file",
"rustc_version 0.4.0",
"rustls",
@ -6833,7 +6787,6 @@ dependencies = [
"log",
"quinn",
"quinn-proto",
"rcgen",
"rustls",
"solana-connection-cache",
"solana-logger",
@ -7325,11 +7278,9 @@ dependencies = [
"nix 0.26.4",
"pem",
"percentage",
"pkcs8",
"quinn",
"quinn-proto",
"rand 0.8.5",
"rcgen",
"rustls",
"solana-logger",
"solana-metrics",
@ -7519,7 +7470,6 @@ dependencies = [
"rand 0.8.5",
"rand_chacha 0.3.1",
"rayon",
"rcgen",
"rustls",
"solana-entry",
"solana-gossip",
@ -7851,16 +7801,6 @@ version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "511254be0c5bcf062b019a6c89c01a664aa359ded62f78aa72c6fc137c0590e5"
[[package]]
name = "spki"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27"
dependencies = [
"base64ct",
"der",
]
[[package]]
name = "spl-associated-token-account"
version = "2.3.0"
@ -9389,15 +9329,6 @@ dependencies = [
"linked-hash-map",
]
[[package]]
name = "yasna"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "346d34a236c9d3e5f3b9b74563f238f955bbd05fa0b8b4efa53c130c43982f4c"
dependencies = [
"time",
]
[[package]]
name = "zerocopy"
version = "0.7.31"

View File

@ -263,7 +263,6 @@ pbkdf2 = { version = "0.11.0", default-features = false }
pem = "1.1.1"
percentage = "0.1.0"
pickledb = { version = "0.5.1", default-features = false }
pkcs8 = "0.8.0"
predicates = "2.1"
pretty-hex = "0.3.0"
prio-graph = "0.2.1"
@ -282,7 +281,6 @@ rand = "0.8.5"
rand_chacha = "0.3.1"
raptorq = "1.8.0"
rayon = "1.8.1"
rcgen = "0.10.0"
reed-solomon-erasure = "6.0.0"
regex = "1.10.3"
reqwest = { version = "0.11.23", default-features = false }

View File

@ -91,9 +91,7 @@ impl ConnectionCache {
config.update_client_endpoint(client_endpoint);
}
if let Some(cert_info) = cert_info {
config
.update_client_certificate(cert_info.0, cert_info.1)
.unwrap();
config.update_client_certificate(cert_info.0, cert_info.1);
}
if let Some(stake_info) = stake_info {
config.set_staked_nodes(stake_info.0, stake_info.1);
@ -241,19 +239,18 @@ mod tests {
},
};
fn server_args() -> (UdpSocket, Arc<AtomicBool>, Keypair, IpAddr) {
fn server_args() -> (UdpSocket, Arc<AtomicBool>, Keypair) {
(
UdpSocket::bind("127.0.0.1:0").unwrap(),
Arc::new(AtomicBool::new(false)),
Keypair::new(),
"127.0.0.1".parse().unwrap(),
)
}
#[test]
fn test_connection_with_specified_client_endpoint() {
// Start a response receiver:
let (response_recv_socket, response_recv_exit, keypair2, response_recv_ip) = server_args();
let (response_recv_socket, response_recv_exit, keypair2) = server_args();
let (sender2, _receiver2) = unbounded();
let staked_nodes = Arc::new(RwLock::new(StakedNodes::default()));
@ -266,7 +263,6 @@ mod tests {
"quic_streamer_test",
response_recv_socket,
&keypair2,
response_recv_ip,
sender2,
response_recv_exit.clone(),
1,

View File

@ -19,7 +19,6 @@ indicatif = { workspace = true, optional = true }
log = { workspace = true }
rand = { workspace = true }
rayon = { workspace = true }
rcgen = { workspace = true }
solana-measure = { workspace = true }
solana-metrics = { workspace = true }
solana-sdk = { workspace = true }

View File

@ -412,9 +412,6 @@ pub enum ConnectionPoolError {
#[derive(Error, Debug)]
pub enum ClientError {
#[error("Certificate error: {0}")]
CertificateError(#[from] rcgen::RcgenError),
#[error("IO error: {0:?}")]
IoError(#[from] std::io::Error),
}

View File

@ -36,7 +36,6 @@ quinn = { workspace = true }
rand = { workspace = true }
rand_chacha = { workspace = true }
rayon = { workspace = true }
rcgen = { workspace = true }
rolling-file = { workspace = true }
rustls = { workspace = true }
serde = { workspace = true }

View File

@ -9,20 +9,17 @@ use {
EndpointConfig, IdleTimeout, ReadError, ReadToEndError, RecvStream, SendStream,
ServerConfig, TokioRuntime, TransportConfig, VarInt, WriteError,
},
rcgen::RcgenError,
rustls::{Certificate, PrivateKey},
serde_bytes::ByteBuf,
solana_quic_client::nonblocking::quic_client::SkipServerVerification,
solana_runtime::bank_forks::BankForks,
solana_sdk::{packet::PACKET_DATA_SIZE, pubkey::Pubkey, signature::Keypair},
solana_streamer::{
quic::SkipClientVerification, tls_certificates::new_self_signed_tls_certificate,
},
solana_streamer::{quic::SkipClientVerification, tls_certificates::new_dummy_x509_certificate},
std::{
cmp::Reverse,
collections::{hash_map::Entry, HashMap},
io::{Cursor, Error as IoError},
net::{IpAddr, SocketAddr, UdpSocket},
net::{SocketAddr, UdpSocket},
sync::{
atomic::{AtomicBool, AtomicU64, Ordering},
Arc, RwLock,
@ -88,8 +85,6 @@ pub struct RemoteRequest {
#[derive(Error, Debug)]
#[allow(clippy::enum_variant_names)]
pub(crate) enum Error {
#[error(transparent)]
CertificateError(#[from] RcgenError),
#[error("Channel Send Error")]
ChannelSendError,
#[error(transparent)]
@ -123,11 +118,10 @@ pub(crate) fn new_quic_endpoint(
runtime: &tokio::runtime::Handle,
keypair: &Keypair,
socket: UdpSocket,
address: IpAddr,
remote_request_sender: Sender<RemoteRequest>,
bank_forks: Arc<RwLock<BankForks>>,
) -> Result<(Endpoint, AsyncSender<LocalRequest>, AsyncTryJoinHandle), Error> {
let (cert, key) = new_self_signed_tls_certificate(keypair, address)?;
let (cert, key) = new_dummy_x509_certificate(keypair);
let server_config = new_server_config(cert.clone(), key.clone())?;
let client_config = new_client_config(cert, key)?;
let mut endpoint = {
@ -809,7 +803,6 @@ async fn report_metrics_task(name: &'static str, stats: Arc<RepairQuicStats>) {
fn record_error(err: &Error, stats: &RepairQuicStats) {
match err {
Error::CertificateError(_) => (),
Error::ChannelSendError => (),
Error::ConnectError(ConnectError::EndpointStopping) => {
add_metric!(stats.connect_error_other)
@ -1065,7 +1058,6 @@ mod tests {
runtime.handle(),
keypair,
socket,
IpAddr::V4(Ipv4Addr::LOCALHOST),
remote_request_sender,
bank_forks.clone(),
)

View File

@ -19,7 +19,7 @@ use {
},
bytes::Bytes,
crossbeam_channel::{unbounded, Receiver},
solana_client::connection_cache::{ConnectionCache, Protocol},
solana_client::connection_cache::ConnectionCache,
solana_gossip::cluster_info::ClusterInfo,
solana_ledger::{
blockstore::Blockstore, blockstore_processor::TransactionStatusSender,
@ -156,11 +156,6 @@ impl Tpu {
"quic_streamer_tpu",
transactions_quic_sockets,
keypair,
cluster_info
.my_contact_info()
.tpu(Protocol::QUIC)
.expect("Operator must spin up node with valid (QUIC) TPU address")
.ip(),
packet_sender,
exit.clone(),
MAX_QUIC_CONNECTIONS_PER_PEER,
@ -180,11 +175,6 @@ impl Tpu {
"quic_streamer_tpu_forwards",
transactions_forwards_quic_sockets,
keypair,
cluster_info
.my_contact_info()
.tpu_forwards(Protocol::QUIC)
.expect("Operator must spin up node with valid (QUIC) TPU-forwards address")
.ip(),
forwarded_packet_sender,
exit.clone(),
MAX_QUIC_CONNECTIONS_PER_PEER,

View File

@ -1193,10 +1193,6 @@ impl Validator {
.unwrap_or_else(|| current_runtime_handle.as_ref().unwrap()),
&identity_keypair,
node.sockets.tvu_quic,
node.info
.tvu(Protocol::QUIC)
.map_err(|err| format!("Invalid QUIC TVU address: {err:?}"))?
.ip(),
turbine_quic_endpoint_sender,
bank_forks.clone(),
)
@ -1226,10 +1222,6 @@ impl Validator {
.unwrap_or_else(|| current_runtime_handle.as_ref().unwrap()),
&identity_keypair,
node.sockets.serve_repair_quic,
node.info
.serve_repair(Protocol::QUIC)
.map_err(|err| format!("Invalid QUIC serve-repair address: {err:?}"))?
.ip(),
repair_quic_endpoint_sender,
bank_forks.clone(),
)

View File

@ -546,12 +546,6 @@ version = "0.21.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
[[package]]
name = "base64ct"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71acf5509fc522cce1b100ac0121c635129bfd4d91cdf036bcc9b9935f97ccf5"
[[package]]
name = "bincode"
version = "1.3.3"
@ -1074,12 +1068,6 @@ dependencies = [
"web-sys",
]
[[package]]
name = "const-oid"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3"
[[package]]
name = "constant_time_eq"
version = "0.3.0"
@ -1284,15 +1272,6 @@ version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57"
[[package]]
name = "der"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c"
dependencies = [
"const-oid",
]
[[package]]
name = "der-parser"
version = "8.1.0"
@ -3497,17 +3476,6 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkcs8"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0"
dependencies = [
"der",
"spki",
"zeroize",
]
[[package]]
name = "pkg-config"
version = "0.3.17"
@ -3908,18 +3876,6 @@ dependencies = [
"crossbeam-utils",
]
[[package]]
name = "rcgen"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffbe84efe2f38dea12e9bfc1f65377fdf03e53a18cb3b995faedf7934c7e785b"
dependencies = [
"pem",
"ring 0.16.20",
"time",
"yasna",
]
[[package]]
name = "redox_syscall"
version = "0.1.56"
@ -4953,7 +4909,6 @@ dependencies = [
"log",
"rand 0.8.5",
"rayon",
"rcgen",
"solana-measure",
"solana-metrics",
"solana-sdk",
@ -4987,7 +4942,6 @@ dependencies = [
"rand 0.8.5",
"rand_chacha 0.3.1",
"rayon",
"rcgen",
"rolling-file",
"rustc_version",
"rustls",
@ -5564,7 +5518,6 @@ dependencies = [
"log",
"quinn",
"quinn-proto",
"rcgen",
"rustls",
"solana-connection-cache",
"solana-measure",
@ -6369,11 +6322,9 @@ dependencies = [
"nix",
"pem",
"percentage",
"pkcs8",
"quinn",
"quinn-proto",
"rand 0.8.5",
"rcgen",
"rustls",
"solana-metrics",
"solana-perf",
@ -6498,7 +6449,6 @@ dependencies = [
"rand 0.8.5",
"rand_chacha 0.3.1",
"rayon",
"rcgen",
"rustls",
"solana-entry",
"solana-gossip",
@ -6756,16 +6706,6 @@ version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c530c2b0d0bf8b69304b39fe2001993e267461948b890cd037d8ad4293fa1a0d"
[[package]]
name = "spki"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27"
dependencies = [
"base64ct",
"der",
]
[[package]]
name = "spl-associated-token-account"
version = "2.3.0"
@ -8198,15 +8138,6 @@ dependencies = [
"libc",
]
[[package]]
name = "yasna"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "346d34a236c9d3e5f3b9b74563f238f955bbd05fa0b8b4efa53c130c43982f4c"
dependencies = [
"time",
]
[[package]]
name = "zerocopy"
version = "0.7.31"

View File

@ -18,7 +18,6 @@ lazy_static = { workspace = true }
log = { workspace = true }
quinn = { workspace = true }
quinn-proto = { workspace = true }
rcgen = { workspace = true }
rustls = { workspace = true, features = ["dangerous_configuration"] }
solana-connection-cache = { workspace = true }
solana-measure = { workspace = true }

View File

@ -15,7 +15,6 @@ use {
quic_client::QuicClientConnection as BlockingQuicClientConnection,
},
quinn::Endpoint,
rcgen::RcgenError,
solana_connection_cache::{
connection_cache::{
BaseClientConnection, ClientError, ConnectionCache, ConnectionManager, ConnectionPool,
@ -30,21 +29,14 @@ use {
solana_streamer::{
nonblocking::quic::{compute_max_allowed_uni_streams, ConnectionPeerType},
streamer::StakedNodes,
tls_certificates::new_self_signed_tls_certificate,
tls_certificates::new_dummy_x509_certificate,
},
std::{
net::{IpAddr, Ipv4Addr, SocketAddr},
net::{IpAddr, SocketAddr},
sync::{Arc, RwLock},
},
thiserror::Error,
};
#[derive(Error, Debug)]
pub enum QuicClientError {
#[error("Certificate error: {0}")]
CertificateError(#[from] RcgenError),
}
pub struct QuicPool {
connections: Vec<Arc<Quic>>,
endpoint: Arc<QuicLazyInitializedEndpoint>,
@ -93,7 +85,6 @@ pub struct QuicConfig {
// The optional specified endpoint for the quic based client connections
// If not specified, the connection cache will create as needed.
client_endpoint: Option<Endpoint>,
addr: IpAddr,
}
impl Clone for QuicConfig {
@ -104,15 +95,13 @@ impl Clone for QuicConfig {
maybe_staked_nodes: self.maybe_staked_nodes.clone(),
maybe_client_pubkey: self.maybe_client_pubkey,
client_endpoint: self.client_endpoint.clone(),
addr: self.addr,
}
}
}
impl NewConnectionConfig for QuicConfig {
fn new() -> Result<Self, ClientError> {
let addr = IpAddr::V4(Ipv4Addr::UNSPECIFIED);
let (cert, priv_key) = new_self_signed_tls_certificate(&Keypair::new(), addr)?;
let (cert, priv_key) = new_dummy_x509_certificate(&Keypair::new());
Ok(Self {
client_certificate: RwLock::new(Arc::new(QuicClientCertificate {
certificate: cert,
@ -121,7 +110,6 @@ impl NewConnectionConfig for QuicConfig {
maybe_staked_nodes: None,
maybe_client_pubkey: None,
client_endpoint: None,
addr,
})
}
}
@ -150,13 +138,8 @@ impl QuicConfig {
compute_max_allowed_uni_streams(client_type, total_stake)
}
pub fn update_client_certificate(
&mut self,
keypair: &Keypair,
ipaddr: IpAddr,
) -> Result<(), RcgenError> {
let (cert, priv_key) = new_self_signed_tls_certificate(keypair, ipaddr)?;
self.addr = ipaddr;
pub fn update_client_certificate(&mut self, keypair: &Keypair, _ipaddr: IpAddr) {
let (cert, priv_key) = new_dummy_x509_certificate(keypair);
let mut cert_guard = self.client_certificate.write().unwrap();
@ -164,11 +147,10 @@ impl QuicConfig {
certificate: cert,
key: priv_key,
});
Ok(())
}
pub fn update_keypair(&self, keypair: &Keypair) -> Result<(), RcgenError> {
let (cert, priv_key) = new_self_signed_tls_certificate(keypair, self.addr)?;
pub fn update_keypair(&self, keypair: &Keypair) {
let (cert, priv_key) = new_dummy_x509_certificate(keypair);
let mut cert_guard = self.client_certificate.write().unwrap();
@ -176,7 +158,6 @@ impl QuicConfig {
certificate: cert,
key: priv_key,
});
Ok(())
}
pub fn set_staked_nodes(
@ -243,7 +224,7 @@ impl ConnectionManager for QuicConnectionManager {
}
fn update_key(&self, key: &Keypair) -> Result<(), Box<dyn std::error::Error>> {
self.connection_config.update_keypair(key)?;
self.connection_config.update_keypair(key);
Ok(())
}
}
@ -264,7 +245,7 @@ pub fn new_quic_connection_cache(
connection_pool_size: usize,
) -> Result<QuicConnectionCache, ClientError> {
let mut config = QuicConfig::new()?;
config.update_client_certificate(keypair, ipaddr)?;
config.update_client_certificate(keypair, ipaddr);
config.set_staked_nodes(staked_nodes, &keypair.pubkey());
let connection_manager = QuicConnectionManager::new_with_connection_config(config);
ConnectionCache::new(name, connection_manager, connection_pool_size)

View File

@ -27,7 +27,7 @@ use {
transport::Result as TransportResult,
},
solana_streamer::{
nonblocking::quic::ALPN_TPU_PROTOCOL_ID, tls_certificates::new_self_signed_tls_certificate,
nonblocking::quic::ALPN_TPU_PROTOCOL_ID, tls_certificates::new_dummy_x509_certificate,
},
std::{
net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket},
@ -148,9 +148,7 @@ impl QuicLazyInitializedEndpoint {
impl Default for QuicLazyInitializedEndpoint {
fn default() -> Self {
let (cert, priv_key) =
new_self_signed_tls_certificate(&Keypair::new(), IpAddr::V4(Ipv4Addr::UNSPECIFIED))
.expect("Failed to create QUIC client certificate");
let (cert, priv_key) = new_dummy_x509_certificate(&Keypair::new());
Self::new(
Arc::new(QuicClientCertificate {
certificate: cert,

View File

@ -11,10 +11,10 @@ mod tests {
solana_sdk::{net::DEFAULT_TPU_COALESCE, packet::PACKET_DATA_SIZE, signature::Keypair},
solana_streamer::{
nonblocking::quic::DEFAULT_WAIT_FOR_CHUNK_TIMEOUT, quic::SpawnServerResult,
streamer::StakedNodes, tls_certificates::new_self_signed_tls_certificate,
streamer::StakedNodes, tls_certificates::new_dummy_x509_certificate,
},
std::{
net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket},
net::{SocketAddr, UdpSocket},
sync::{
atomic::{AtomicBool, Ordering},
Arc, RwLock,
@ -49,12 +49,11 @@ mod tests {
assert!(total_packets > 0);
}
fn server_args() -> (UdpSocket, Arc<AtomicBool>, Keypair, IpAddr) {
fn server_args() -> (UdpSocket, Arc<AtomicBool>, Keypair) {
(
UdpSocket::bind("127.0.0.1:0").unwrap(),
Arc::new(AtomicBool::new(false)),
Keypair::new(),
"127.0.0.1".parse().unwrap(),
)
}
@ -67,7 +66,7 @@ mod tests {
solana_logger::setup();
let (sender, receiver) = unbounded();
let staked_nodes = Arc::new(RwLock::new(StakedNodes::default()));
let (s, exit, keypair, ip) = server_args();
let (s, exit, keypair) = server_args();
let SpawnServerResult {
endpoint: _,
thread: t,
@ -76,7 +75,6 @@ mod tests {
"quic_streamer_test",
s.try_clone().unwrap(),
&keypair,
ip,
sender,
exit.clone(),
1,
@ -151,12 +149,11 @@ mod tests {
solana_logger::setup();
let (sender, receiver) = unbounded();
let staked_nodes = Arc::new(RwLock::new(StakedNodes::default()));
let (s, exit, keypair, ip) = server_args();
let (s, exit, keypair) = server_args();
let (_, _, t) = solana_streamer::nonblocking::quic::spawn_server(
"quic_streamer_test",
s.try_clone().unwrap(),
&keypair,
ip,
sender,
exit.clone(),
1,
@ -209,7 +206,7 @@ mod tests {
// Request Receiver
let (sender, receiver) = unbounded();
let staked_nodes = Arc::new(RwLock::new(StakedNodes::default()));
let (request_recv_socket, request_recv_exit, keypair, request_recv_ip) = server_args();
let (request_recv_socket, request_recv_exit, keypair) = server_args();
let SpawnServerResult {
endpoint: request_recv_endpoint,
thread: request_recv_thread,
@ -218,7 +215,6 @@ mod tests {
"quic_streamer_test",
request_recv_socket.try_clone().unwrap(),
&keypair,
request_recv_ip,
sender,
request_recv_exit.clone(),
1,
@ -232,7 +228,7 @@ mod tests {
drop(request_recv_endpoint);
// Response Receiver:
let (response_recv_socket, response_recv_exit, keypair2, response_recv_ip) = server_args();
let (response_recv_socket, response_recv_exit, keypair2) = server_args();
let (sender2, receiver2) = unbounded();
let addr = response_recv_socket.local_addr().unwrap().ip();
@ -246,7 +242,6 @@ mod tests {
"quic_streamer_test",
response_recv_socket,
&keypair2,
response_recv_ip,
sender2,
response_recv_exit.clone(),
1,
@ -264,9 +259,7 @@ mod tests {
let tpu_addr = SocketAddr::new(addr, port);
let connection_cache_stats = Arc::new(ConnectionCacheStats::default());
let (cert, priv_key) =
new_self_signed_tls_certificate(&Keypair::new(), IpAddr::V4(Ipv4Addr::UNSPECIFIED))
.expect("Failed to initialize QUIC client certificates");
let (cert, priv_key) = new_dummy_x509_certificate(&Keypair::new());
let client_certificate = Arc::new(QuicClientCertificate {
certificate: cert,
key: priv_key,
@ -286,9 +279,7 @@ mod tests {
info!("Received requests!");
// Response sender
let (cert, priv_key) =
new_self_signed_tls_certificate(&Keypair::new(), IpAddr::V4(Ipv4Addr::LOCALHOST))
.expect("Failed to initialize QUIC client certificates");
let (cert, priv_key) = new_dummy_x509_certificate(&Keypair::new());
let client_certificate2 = Arc::new(QuicClientCertificate {
certificate: cert,

View File

@ -22,11 +22,9 @@ log = { workspace = true }
nix = { workspace = true }
pem = { workspace = true }
percentage = { workspace = true }
pkcs8 = { workspace = true, features = ["alloc"] }
quinn = { workspace = true }
quinn-proto = { workspace = true }
rand = { workspace = true }
rcgen = { workspace = true }
rustls = { workspace = true, features = ["dangerous_configuration"] }
solana-metrics = { workspace = true }
solana-perf = { workspace = true }

View File

@ -100,7 +100,6 @@ pub fn spawn_server(
name: &'static str,
sock: UdpSocket,
keypair: &Keypair,
gossip_host: IpAddr,
packet_sender: Sender<PacketBatch>,
exit: Arc<AtomicBool>,
max_connections_per_peer: usize,
@ -111,7 +110,7 @@ pub fn spawn_server(
coalesce: Duration,
) -> Result<(Endpoint, Arc<StreamStats>, JoinHandle<()>), QuicServerError> {
info!("Start {name} quic server on {sock:?}");
let (config, _cert) = configure_server(keypair, gossip_host)?;
let (config, _cert) = configure_server(keypair)?;
let endpoint = Endpoint::new(
EndpointConfig::default(),
@ -1145,7 +1144,7 @@ pub mod test {
crate::{
nonblocking::quic::compute_max_allowed_uni_streams,
quic::{MAX_STAKED_CONNECTIONS, MAX_UNSTAKED_CONNECTIONS},
tls_certificates::new_self_signed_tls_certificate,
tls_certificates::new_dummy_x509_certificate,
},
assert_matches::assert_matches,
async_channel::unbounded as async_unbounded,
@ -1157,7 +1156,7 @@ pub mod test {
signature::Keypair,
signer::Signer,
},
std::{collections::HashMap, net::Ipv4Addr},
std::collections::HashMap,
tokio::time::sleep,
};
@ -1184,9 +1183,7 @@ pub mod test {
}
pub fn get_client_config(keypair: &Keypair) -> ClientConfig {
let ipaddr = IpAddr::V4(Ipv4Addr::LOCALHOST);
let (cert, key) = new_self_signed_tls_certificate(keypair, ipaddr)
.expect("Failed to generate client certificate");
let (cert, key) = new_dummy_x509_certificate(keypair);
let mut crypto = rustls::ClientConfig::builder()
.with_safe_defaults()
@ -1222,14 +1219,12 @@ pub mod test {
let exit = Arc::new(AtomicBool::new(false));
let (sender, receiver) = unbounded();
let keypair = Keypair::new();
let ip = "127.0.0.1".parse().unwrap();
let server_address = s.local_addr().unwrap();
let staked_nodes = Arc::new(RwLock::new(option_staked_nodes.unwrap_or_default()));
let (_, stats, t) = spawn_server(
"quic_streamer_test",
s,
&keypair,
ip,
sender,
exit.clone(),
max_connections_per_peer,
@ -1658,14 +1653,12 @@ pub mod test {
let exit = Arc::new(AtomicBool::new(false));
let (sender, _) = unbounded();
let keypair = Keypair::new();
let ip = "127.0.0.1".parse().unwrap();
let server_address = s.local_addr().unwrap();
let staked_nodes = Arc::new(RwLock::new(StakedNodes::default()));
let (_, _, t) = spawn_server(
"quic_streamer_test",
s,
&keypair,
ip,
sender,
exit.clone(),
1,
@ -1689,14 +1682,12 @@ pub mod test {
let exit = Arc::new(AtomicBool::new(false));
let (sender, receiver) = unbounded();
let keypair = Keypair::new();
let ip = "127.0.0.1".parse().unwrap();
let server_address = s.local_addr().unwrap();
let staked_nodes = Arc::new(RwLock::new(StakedNodes::default()));
let (_, stats, t) = spawn_server(
"quic_streamer_test",
s,
&keypair,
ip,
sender,
exit.clone(),
2,

View File

@ -1,7 +1,7 @@
use {
crate::{
nonblocking::quic::ALPN_TPU_PROTOCOL_ID, streamer::StakedNodes,
tls_certificates::new_self_signed_tls_certificate,
tls_certificates::new_dummy_x509_certificate,
},
crossbeam_channel::Sender,
pem::Pem,
@ -14,7 +14,7 @@ use {
signature::Keypair,
},
std::{
net::{IpAddr, UdpSocket},
net::UdpSocket,
sync::{
atomic::{AtomicBool, AtomicUsize, Ordering},
Arc, RwLock,
@ -61,9 +61,8 @@ impl rustls::server::ClientCertVerifier for SkipClientVerification {
#[allow(clippy::field_reassign_with_default)] // https://github.com/rust-lang/rust-clippy/issues/6527
pub(crate) fn configure_server(
identity_keypair: &Keypair,
gossip_host: IpAddr,
) -> Result<(ServerConfig, String), QuicServerError> {
let (cert, priv_key) = new_self_signed_tls_certificate(identity_keypair, gossip_host)?;
let (cert, priv_key) = new_dummy_x509_certificate(identity_keypair);
let cert_chain_pem_parts = vec![Pem {
tag: "CERTIFICATE".to_string(),
contents: cert.0.clone(),
@ -113,20 +112,17 @@ fn rt() -> Runtime {
pub enum QuicServerError {
#[error("Endpoint creation failed: {0}")]
EndpointFailed(std::io::Error),
#[error("Certificate error: {0}")]
CertificateError(#[from] rcgen::RcgenError),
#[error("TLS error: {0}")]
TlsError(#[from] rustls::Error),
}
pub struct EndpointKeyUpdater {
endpoint: Endpoint,
gossip_host: IpAddr,
}
impl NotifyKeyUpdate for EndpointKeyUpdater {
fn update_key(&self, key: &Keypair) -> Result<(), Box<dyn std::error::Error>> {
let (config, _) = configure_server(key, self.gossip_host)?;
let (config, _) = configure_server(key)?;
self.endpoint.set_server_config(Some(config));
Ok(())
}
@ -438,7 +434,6 @@ pub fn spawn_server(
name: &'static str,
sock: UdpSocket,
keypair: &Keypair,
gossip_host: IpAddr,
packet_sender: Sender<PacketBatch>,
exit: Arc<AtomicBool>,
max_connections_per_peer: usize,
@ -455,7 +450,6 @@ pub fn spawn_server(
name,
sock,
keypair,
gossip_host,
packet_sender,
exit,
max_connections_per_peer,
@ -476,7 +470,6 @@ pub fn spawn_server(
.unwrap();
let updater = EndpointKeyUpdater {
endpoint: endpoint.clone(),
gossip_host,
};
Ok(SpawnServerResult {
endpoint,
@ -505,7 +498,6 @@ mod test {
let exit = Arc::new(AtomicBool::new(false));
let (sender, receiver) = unbounded();
let keypair = Keypair::new();
let ip = "127.0.0.1".parse().unwrap();
let server_address = s.local_addr().unwrap();
let staked_nodes = Arc::new(RwLock::new(StakedNodes::default()));
let SpawnServerResult {
@ -516,7 +508,6 @@ mod test {
"quic_streamer_test",
s,
&keypair,
ip,
sender,
exit.clone(),
1,
@ -565,7 +556,6 @@ mod test {
let exit = Arc::new(AtomicBool::new(false));
let (sender, receiver) = unbounded();
let keypair = Keypair::new();
let ip = "127.0.0.1".parse().unwrap();
let server_address = s.local_addr().unwrap();
let staked_nodes = Arc::new(RwLock::new(StakedNodes::default()));
let SpawnServerResult {
@ -576,7 +566,6 @@ mod test {
"quic_streamer_test",
s,
&keypair,
ip,
sender,
exit.clone(),
2,
@ -612,7 +601,6 @@ mod test {
let exit = Arc::new(AtomicBool::new(false));
let (sender, _) = unbounded();
let keypair = Keypair::new();
let ip = "127.0.0.1".parse().unwrap();
let server_address = s.local_addr().unwrap();
let staked_nodes = Arc::new(RwLock::new(StakedNodes::default()));
let SpawnServerResult {
@ -623,7 +611,6 @@ mod test {
"quic_streamer_test",
s,
&keypair,
ip,
sender,
exit.clone(),
1,

View File

@ -1,58 +1,99 @@
use {
pkcs8::{der::Document, AlgorithmIdentifier, ObjectIdentifier},
rcgen::{CertificateParams, DistinguishedName, DnType, RcgenError, SanType},
solana_sdk::{pubkey::Pubkey, signature::Keypair},
std::net::IpAddr,
solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer},
x509_parser::{prelude::*, public_key::PublicKey},
};
pub fn new_self_signed_tls_certificate(
keypair: &Keypair,
san: IpAddr,
) -> Result<(rustls::Certificate, rustls::PrivateKey), RcgenError> {
// TODO(terorie): Is it safe to sign the TLS cert with the identity private key?
// Unfortunately, rcgen does not accept a "raw" Ed25519 key.
pub fn new_dummy_x509_certificate(keypair: &Keypair) -> (rustls::Certificate, rustls::PrivateKey) {
// Unfortunately, rustls does not accept a "raw" Ed25519 key.
// We have to convert it to DER and pass it to the library.
// Convert private key into PKCS#8 v1 object.
// RFC 8410, Section 7: Private Key Format
// https://datatracker.ietf.org/doc/html/rfc8410#section-
// https://www.rfc-editor.org/rfc/rfc8410#section-7
//
// The hardcoded prefix decodes to the following ASN.1 structure:
//
// PrivateKeyInfo SEQUENCE (3 elem)
// version Version INTEGER 0
// privateKeyAlgorithm AlgorithmIdentifier SEQUENCE (1 elem)
// algorithm OBJECT IDENTIFIER 1.3.101.112 curveEd25519 (EdDSA 25519 signature algorithm)
// privateKey PrivateKey OCTET STRING (34 byte)
const PKCS8_PREFIX: [u8; 16] = [
0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x04, 0x22, 0x04,
0x20,
];
let mut key_pkcs8_der = Vec::<u8>::with_capacity(PKCS8_PREFIX.len() + 32);
key_pkcs8_der.extend_from_slice(&PKCS8_PREFIX);
key_pkcs8_der.extend_from_slice(keypair.secret().as_bytes());
// from https://datatracker.ietf.org/doc/html/rfc8410#section-3
const ED25519_IDENTIFIER: [u32; 4] = [1, 3, 101, 112];
let mut private_key = Vec::<u8>::with_capacity(34);
private_key.extend_from_slice(&[0x04, 0x20]); // ASN.1 OCTET STRING
private_key.extend_from_slice(keypair.secret().as_bytes());
let key_pkcs8 = pkcs8::PrivateKeyInfo {
algorithm: AlgorithmIdentifier {
oid: ObjectIdentifier::from_arcs(&ED25519_IDENTIFIER).expect("Failed to convert OID"),
parameters: None,
},
private_key: &private_key,
public_key: None,
};
let key_pkcs8_der = key_pkcs8
.to_der()
.expect("Failed to convert keypair to DER")
.to_der();
// Create a dummy certificate. Only the SubjectPublicKeyInfo field
// is relevant to the peer-to-peer protocols. The signature of the
// X.509 certificate is deliberately invalid. (Peer authenticity is
// checked in the TLS 1.3 CertificateVerify)
// See https://www.itu.int/rec/T-REC-X.509-201910-I/en for detailed definitions.
let rcgen_keypair = rcgen::KeyPair::from_der(&key_pkcs8_der)?;
let mut cert_der = Vec::<u8>::with_capacity(0xf4);
// Certificate SEQUENCE (3 elem)
// tbsCertificate TBSCertificate SEQUENCE (8 elem)
// version [0] (1 elem)
// INTEGER 2
// serialNumber CertificateSerialNumber INTEGER (62 bit)
// signature AlgorithmIdentifier SEQUENCE (1 elem)
// algorithm OBJECT IDENTIFIER 1.3.101.112 curveEd25519 (EdDSA 25519 signature algorithm)
// issuer Name SEQUENCE (1 elem)
// RelativeDistinguishedName SET (1 elem)
// AttributeTypeAndValue SEQUENCE (2 elem)
// type AttributeType OBJECT IDENTIFIER 2.5.4.3 commonName (X.520 DN component)
// value AttributeValue [?] UTF8String Solana
// validity Validity SEQUENCE (2 elem)
// notBefore Time UTCTime 1970-01-01 00:00:00 UTC
// notAfter Time GeneralizedTime 4096-01-01 00:00:00 UTC
// subject Name SEQUENCE (0 elem)
// subjectPublicKeyInfo SubjectPublicKeyInfo SEQUENCE (2 elem)
// algorithm AlgorithmIdentifier SEQUENCE (1 elem)
// algorithm OBJECT IDENTIFIER 1.3.101.112 curveEd25519 (EdDSA 25519 signature algorithm)
// subjectPublicKey BIT STRING (256 bit)
cert_der.extend_from_slice(&[
0x30, 0x81, 0xf6, 0x30, 0x81, 0xa9, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x08, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x30, 0x16,
0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x0b, 0x53, 0x6f, 0x6c, 0x61,
0x6e, 0x61, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x30, 0x20, 0x17, 0x0d, 0x37, 0x30, 0x30, 0x31,
0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x18, 0x0f, 0x34, 0x30, 0x39, 0x36,
0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x00, 0x30, 0x2a,
0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x03, 0x21, 0x00,
]);
cert_der.extend_from_slice(&keypair.pubkey().to_bytes());
// extensions [3] (1 elem)
// Extensions SEQUENCE (2 elem)
// Extension SEQUENCE (3 elem)
// extnID OBJECT IDENTIFIER 2.5.29.17 subjectAltName (X.509 extension)
// critical BOOLEAN true
// extnValue OCTET STRING (13 byte) encapsulating
// SEQUENCE (1 elem)
// [2] (9 byte) localhost
// Extension SEQUENCE (3 elem)
// extnID OBJECT IDENTIFIER 2.5.29.19 basicConstraints (X.509 extension)
// critical BOOLEAN true
// extnValue OCTET STRING (2 byte) encapsulating
// SEQUENCE (0 elem)
// signatureAlgorithm AlgorithmIdentifier SEQUENCE (1 elem)
// algorithm OBJECT IDENTIFIER 1.3.101.112 curveEd25519 (EdDSA 25519 signature algorithm)
// signature BIT STRING (512 bit)
cert_der.extend_from_slice(&[
0xa3, 0x29, 0x30, 0x27, 0x30, 0x17, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x01, 0x01, 0xff, 0x04,
0x0d, 0x30, 0x0b, 0x82, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, 0x30,
0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, 0x30, 0x05,
0x06, 0x03, 0x2b, 0x65, 0x70, 0x03, 0x41, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
]);
let mut cert_params = CertificateParams::default();
cert_params.subject_alt_names = vec![SanType::IpAddress(san)];
cert_params.alg = &rcgen::PKCS_ED25519;
cert_params.key_pair = Some(rcgen_keypair);
cert_params.distinguished_name = DistinguishedName::new();
cert_params
.distinguished_name
.push(DnType::CommonName, "Solana node");
let cert = rcgen::Certificate::from_params(cert_params)?;
let cert_der = cert.serialize_der().unwrap();
let priv_key = cert.serialize_private_key_der();
let priv_key = rustls::PrivateKey(priv_key);
Ok((rustls::Certificate(cert_der), priv_key))
(
rustls::Certificate(cert_der),
rustls::PrivateKey(key_pkcs8_der),
)
}
pub fn get_pubkey_from_tls_certificate(der_cert: &rustls::Certificate) -> Option<Pubkey> {
@ -65,22 +106,16 @@ pub fn get_pubkey_from_tls_certificate(der_cert: &rustls::Certificate) -> Option
#[cfg(test)]
mod tests {
use {super::*, solana_sdk::signer::Signer, std::net::Ipv4Addr};
use {super::*, solana_sdk::signer::Signer};
#[test]
fn test_generate_tls_certificate() {
let keypair = Keypair::new();
if let Ok((cert, _)) =
new_self_signed_tls_certificate(&keypair, IpAddr::V4(Ipv4Addr::LOCALHOST))
{
if let Some(pubkey) = get_pubkey_from_tls_certificate(&cert) {
assert_eq!(pubkey, keypair.pubkey());
} else {
panic!("Failed to get certificate pubkey");
}
let (cert, _) = new_dummy_x509_certificate(&keypair);
if let Some(pubkey) = get_pubkey_from_tls_certificate(&cert) {
assert_eq!(pubkey, keypair.pubkey());
} else {
panic!("Failed to generate certificates");
panic!("Failed to get certificate pubkey");
}
}
}

View File

@ -21,7 +21,6 @@ quinn = { workspace = true }
rand = { workspace = true }
rand_chacha = { workspace = true }
rayon = { workspace = true }
rcgen = { workspace = true }
rustls = { workspace = true }
solana-entry = { workspace = true }
solana-gossip = { workspace = true }

View File

@ -8,19 +8,16 @@ use {
EndpointConfig, IdleTimeout, SendDatagramError, ServerConfig, TokioRuntime,
TransportConfig, VarInt,
},
rcgen::RcgenError,
rustls::{Certificate, PrivateKey},
solana_quic_client::nonblocking::quic_client::SkipServerVerification,
solana_runtime::bank_forks::BankForks,
solana_sdk::{pubkey::Pubkey, signature::Keypair},
solana_streamer::{
quic::SkipClientVerification, tls_certificates::new_self_signed_tls_certificate,
},
solana_streamer::{quic::SkipClientVerification, tls_certificates::new_dummy_x509_certificate},
std::{
cmp::Reverse,
collections::{hash_map::Entry, HashMap},
io::Error as IoError,
net::{IpAddr, SocketAddr, UdpSocket},
net::{SocketAddr, UdpSocket},
sync::{
atomic::{AtomicBool, AtomicU64, Ordering},
Arc, RwLock,
@ -67,8 +64,6 @@ pub type AsyncTryJoinHandle = TryJoin<JoinHandle<()>, JoinHandle<()>>;
#[derive(Error, Debug)]
pub enum Error {
#[error(transparent)]
CertificateError(#[from] RcgenError),
#[error("Channel Send Error")]
ChannelSendError,
#[error(transparent)]
@ -96,7 +91,6 @@ pub fn new_quic_endpoint(
runtime: &tokio::runtime::Handle,
keypair: &Keypair,
socket: UdpSocket,
address: IpAddr,
sender: Sender<(Pubkey, SocketAddr, Bytes)>,
bank_forks: Arc<RwLock<BankForks>>,
) -> Result<
@ -107,7 +101,7 @@ pub fn new_quic_endpoint(
),
Error,
> {
let (cert, key) = new_self_signed_tls_certificate(keypair, address)?;
let (cert, key) = new_dummy_x509_certificate(keypair);
let server_config = new_server_config(cert.clone(), key.clone())?;
let client_config = new_client_config(cert, key)?;
let mut endpoint = {
@ -650,7 +644,6 @@ async fn report_metrics_task(name: &'static str, stats: Arc<TurbineQuicStats>) {
fn record_error(err: &Error, stats: &TurbineQuicStats) {
match err {
Error::CertificateError(_) => (),
Error::ChannelSendError => (),
Error::ConnectError(ConnectError::EndpointStopping) => {
add_metric!(stats.connect_error_other)
@ -838,7 +831,6 @@ mod tests {
runtime.handle(),
keypair,
socket,
IpAddr::V4(Ipv4Addr::LOCALHOST),
sender,
bank_forks.clone(),
)