Skip to content

Commit

Permalink
tests(iroh-net): expose run_relay_server and option to skip cert ve…
Browse files Browse the repository at this point in the history
…rification to tests (#2145)

## Description

* Add a `test-utils` feature to `iroh-net`. If enabled, expose the
`test_utils` crate with `fn run_relay_server` publicly.
* Add option to `iroh_net::MagicEndpointBuilder` to skip relay server
SSL certificate verification. The option is behind a `#[cfg(any(test,
feature = "test-utils"))]`
* Add the same option to `iroh::NodeBuilder`, behind the same `cfg`
option
* Add a test to `iroh` which uses `run_relay_server` and then downloads
a blob by connecting to a node with only node id and relay URL (no
direct addresses)


## Change checklist

- [x] Self-review.
- [x] Documentation updates if relevant.
- [x] Tests if relevant.
  • Loading branch information
Frando committed Apr 8, 2024
1 parent aeb04d8 commit fb4703a
Show file tree
Hide file tree
Showing 14 changed files with 250 additions and 27 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions iroh-net/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ duct = "0.13.6"
default = ["metrics"]
iroh-relay = ["clap", "toml", "rustls-pemfile", "regex", "serde_with", "tracing-subscriber"]
metrics = ["iroh-metrics/metrics"]
test-utils = []

[[bin]]
name = "iroh-relay"
Expand Down
4 changes: 2 additions & 2 deletions iroh-net/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,5 @@ pub use iroh_base::key;

pub use iroh_base::key::NodeId;

#[cfg(test)]
pub(crate) mod test_utils;
#[cfg(any(test, feature = "test-utils"))]
pub mod test_utils;
19 changes: 19 additions & 0 deletions iroh-net/src/magic_endpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ pub struct MagicEndpointBuilder {
/// Path for known peers. See [`MagicEndpointBuilder::peers_data_path`].
peers_path: Option<PathBuf>,
dns_resolver: Option<DnsResolver>,
#[cfg(any(test, feature = "test-utils"))]
insecure_skip_relay_cert_verify: bool,
}

impl Default for MagicEndpointBuilder {
Expand All @@ -55,6 +57,8 @@ impl Default for MagicEndpointBuilder {
discovery: Default::default(),
peers_path: None,
dns_resolver: None,
#[cfg(any(test, feature = "test-utils"))]
insecure_skip_relay_cert_verify: false,
}
}
}
Expand Down Expand Up @@ -84,6 +88,15 @@ impl MagicEndpointBuilder {
self
}

/// Skip verification of SSL certificates from relay servers
///
/// May only be used in tests.
#[cfg(any(test, feature = "test-utils"))]
pub fn insecure_skip_relay_cert_verify(mut self, skip_verify: bool) -> Self {
self.insecure_skip_relay_cert_verify = skip_verify;
self
}

/// Sets the relay servers to assist in establishing connectivity.
///
/// relay servers are used to discover other peers by [`PublicKey`] and also help
Expand Down Expand Up @@ -192,6 +205,8 @@ impl MagicEndpointBuilder {
nodes_path: self.peers_path,
discovery: self.discovery,
dns_resolver,
#[cfg(any(test, feature = "test-utils"))]
insecure_skip_relay_cert_verify: self.insecure_skip_relay_cert_verify,
};
MagicEndpoint::bind(Some(server_config), msock_opts, self.keylog).await
}
Expand Down Expand Up @@ -670,6 +685,7 @@ mod tests {
.secret_key(server_secret_key)
.alpns(vec![TEST_ALPN.to_vec()])
.relay_mode(RelayMode::Custom(relay_map))
.insecure_skip_relay_cert_verify(true)
.bind(0)
.await
.unwrap();
Expand Down Expand Up @@ -704,6 +720,7 @@ mod tests {
let ep = MagicEndpoint::builder()
.alpns(vec![TEST_ALPN.to_vec()])
.relay_mode(RelayMode::Custom(relay_map))
.insecure_skip_relay_cert_verify(true)
.bind(0)
.await
.unwrap();
Expand Down Expand Up @@ -813,6 +830,7 @@ mod tests {
tokio::spawn(
async move {
let ep = MagicEndpoint::builder()
.insecure_skip_relay_cert_verify(true)
.secret_key(server_secret_key)
.alpns(vec![TEST_ALPN.to_vec()])
.relay_mode(RelayMode::Custom(relay_map))
Expand Down Expand Up @@ -857,6 +875,7 @@ mod tests {
info!("client binding");
let ep = MagicEndpoint::builder()
.alpns(vec![TEST_ALPN.to_vec()])
.insecure_skip_relay_cert_verify(true)
.relay_mode(RelayMode::Custom(relay_map))
.secret_key(client_secret_key)
.bind(0)
Expand Down
18 changes: 18 additions & 0 deletions iroh-net/src/magicsock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,12 @@ pub struct Options {
/// You can use [`crate::dns::default_resolver`] for a resolver that uses the system's DNS
/// configuration.
pub dns_resolver: DnsResolver,

/// Skip verification of SSL certificates from relay servers
///
/// May only be used in tests.
#[cfg(any(test, feature = "test-utils"))]
pub insecure_skip_relay_cert_verify: bool,
}

impl Default for Options {
Expand All @@ -130,6 +136,8 @@ impl Default for Options {
nodes_path: None,
discovery: None,
dns_resolver: crate::dns::default_resolver().clone(),
#[cfg(any(test, feature = "test-utils"))]
insecure_skip_relay_cert_verify: false,
}
}
}
Expand Down Expand Up @@ -220,6 +228,12 @@ struct Inner {

/// Indicates the update endpoint state.
endpoints_update_state: EndpointUpdateState,

/// Skip verification of SSL certificates from relay servers
///
/// May only be used in tests.
#[cfg(any(test, feature = "test-utils"))]
insecure_skip_relay_cert_verify: bool,
}

impl Inner {
Expand Down Expand Up @@ -1150,6 +1164,8 @@ impl MagicSock {
discovery,
nodes_path,
dns_resolver,
#[cfg(any(test, feature = "test-utils"))]
insecure_skip_relay_cert_verify,
} = opts;

let nodes_path = match nodes_path {
Expand Down Expand Up @@ -1230,6 +1246,8 @@ impl MagicSock {
pending_call_me_maybes: Default::default(),
endpoints_update_state: EndpointUpdateState::new(),
dns_resolver,
#[cfg(any(test, feature = "test-utils"))]
insecure_skip_relay_cert_verify,
});

let mut actor_tasks = JoinSet::default();
Expand Down
11 changes: 8 additions & 3 deletions iroh-net/src/magicsock/relay_actor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -479,14 +479,19 @@ impl RelayActor {
let url1 = url.clone();

// building a client dials the relay
let (dc, dc_receiver) = relay::http::ClientBuilder::new(url1.clone())
let builder = relay::http::ClientBuilder::new(url1.clone())
.address_family_selector(move || {
let ipv6_reported = ipv6_reported.clone();
Box::pin(async move { ipv6_reported.load(Ordering::Relaxed) })
})
.can_ack_pings(true)
.is_preferred(my_relay.as_ref() == Some(&url1))
.build(self.conn.secret_key.clone(), self.conn.dns_resolver.clone());
.is_preferred(my_relay.as_ref() == Some(&url1));

#[cfg(any(test, feature = "test-utils"))]
let builder = builder.insecure_skip_cert_verify(self.conn.insecure_skip_relay_cert_verify);

let (dc, dc_receiver) =
builder.build(self.conn.secret_key.clone(), self.conn.dns_resolver.clone());

let (s, r) = mpsc::channel(64);

Expand Down
4 changes: 2 additions & 2 deletions iroh-net/src/relay/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pub use self::server::{Server, ServerBuilder, TlsAcceptor, TlsConfig};

pub(crate) const HTTP_UPGRADE_PROTOCOL: &str = "iroh derp http";

#[cfg(test)]
#[cfg(any(test, feature = "test-utils"))]
pub(crate) fn make_tls_config() -> TlsConfig {
let subject_alt_names = vec!["localhost".to_string()];

Expand Down Expand Up @@ -128,7 +128,7 @@ mod tests {
JoinHandle<()>,
Client,
) {
let client = ClientBuilder::new(server_url);
let client = ClientBuilder::new(server_url).insecure_skip_cert_verify(true);
let dns_resolver = crate::dns::default_resolver();
let (client, mut client_reader) = client.build(key.clone(), dns_resolver.clone());
let public_key = key.public();
Expand Down
29 changes: 23 additions & 6 deletions iroh-net/src/relay/http/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,9 @@ pub struct ClientBuilder {
server_public_key: Option<PublicKey>,
/// Server url.
url: RelayUrl,
/// Allow self-signed certificates from relay servers
#[cfg(any(test, feature = "test-utils"))]
insecure_skip_cert_verify: bool,
}

impl std::fmt::Debug for ClientBuilder {
Expand All @@ -223,6 +226,8 @@ impl ClientBuilder {
is_prober: false,
server_public_key: None,
url: url.into(),
#[cfg(any(test, feature = "test-utils"))]
insecure_skip_cert_verify: false,
}
}

Expand Down Expand Up @@ -265,6 +270,15 @@ impl ClientBuilder {
self
}

/// Skip the verification of the relay server's SSL certificates.
///
/// May only be used in tests.
#[cfg(any(test, feature = "test-utils"))]
pub fn insecure_skip_cert_verify(mut self, skip: bool) -> Self {
self.insecure_skip_cert_verify = skip;
self
}

/// Build the [`Client`]
pub fn build(self, key: SecretKey, dns_resolver: DnsResolver) -> (Client, ClientReceiver) {
// TODO: review TLS config
Expand All @@ -280,10 +294,13 @@ impl ClientBuilder {
.with_safe_defaults()
.with_root_certificates(roots)
.with_no_client_auth();
#[cfg(test)]
config
.dangerous()
.set_certificate_verifier(Arc::new(NoCertVerifier));
#[cfg(any(test, feature = "test-utils"))]
if self.insecure_skip_cert_verify {
warn!("Insecure config: SSL certificates from relay servers will be trusted without verification");
config
.dangerous()
.set_certificate_verifier(Arc::new(NoCertVerifier));
}

config.resumption = Resumption::default();

Expand Down Expand Up @@ -904,10 +921,10 @@ fn downcast_upgrade(
}

/// Used to allow self signed certificates in tests
#[cfg(test)]
#[cfg(any(test, feature = "test-utils"))]
struct NoCertVerifier;

#[cfg(test)]
#[cfg(any(test, feature = "test-utils"))]
impl rustls::client::ServerCertVerifier for NoCertVerifier {
fn verify_server_cert(
&self,
Expand Down
24 changes: 14 additions & 10 deletions iroh-net/src/stun.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,41 +149,44 @@ pub fn parse_response(b: &[u8]) -> Result<(TransactionId, SocketAddr), Error> {
Err(Error::MalformedAttrs)
}

#[cfg(test)]
pub mod test {
#[cfg(any(test, feature = "test-utils"))]
pub(crate) mod test {
use std::{
net::{IpAddr, Ipv4Addr},
net::{IpAddr, SocketAddr},
sync::Arc,
};

use crate::{
relay::{RelayMap, RelayNode, RelayUrl},
test_utils::CleanupDropGuard,
};

use super::*;
use anyhow::Result;
use tokio::{
net,
sync::{oneshot, Mutex},
};
use tracing::{debug, trace};

#[cfg(test)]
use crate::relay::{RelayMap, RelayNode, RelayUrl};
use crate::test_utils::CleanupDropGuard;

use super::*;

// (read_ipv4, read_ipv5)
#[derive(Debug, Default, Clone)]
pub struct StunStats(Arc<Mutex<(usize, usize)>>);

impl StunStats {
#[cfg(test)]
pub async fn total(&self) -> usize {
let s = self.0.lock().await;
s.0 + s.1
}
}

#[cfg(test)]
pub fn relay_map_of(stun: impl Iterator<Item = SocketAddr>) -> RelayMap {
relay_map_of_opts(stun.map(|addr| (addr, true)))
}

#[cfg(test)]
pub fn relay_map_of_opts(stun: impl Iterator<Item = (SocketAddr, bool)>) -> RelayMap {
let nodes = stun.map(|(addr, stun_only)| {
let host = addr.ip();
Expand All @@ -202,8 +205,9 @@ pub mod test {
/// Sets up a simple STUN server binding to `0.0.0.0:0`.
///
/// See [`serve`] for more details.
#[cfg(test)]
pub(crate) async fn serve_v4() -> Result<(SocketAddr, StunStats, CleanupDropGuard)> {
serve(Ipv4Addr::UNSPECIFIED.into()).await
serve(std::net::Ipv4Addr::UNSPECIFIED.into()).await
}

/// Sets up a simple STUN server.
Expand Down
4 changes: 2 additions & 2 deletions iroh-net/src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ use crate::relay::{RelayMap, RelayNode, RelayUrl};
// sender.
#[derive(Debug)]
#[allow(dead_code)]
pub(crate) struct CleanupDropGuard(pub(crate) oneshot::Sender<()>);
pub struct CleanupDropGuard(pub(crate) oneshot::Sender<()>);

/// Runs a relay server with STUN enabled suitable for tests.
///
/// The returned `Url` is the url of the relay server in the returned [`RelayMap`], it
/// is always `Some` as that is how the [`MagicEndpoint::connect`] API expects it.
///
/// [`MagicEndpoint::connect`]: crate::magic_endpoint::MagicEndpoint
pub(crate) async fn run_relay_server() -> Result<(RelayMap, RelayUrl, CleanupDropGuard)> {
pub async fn run_relay_server() -> Result<(RelayMap, RelayUrl, CleanupDropGuard)> {
let server_key = SecretKey::generate();
let me = server_key.public().fmt_short();
let tls_config = crate::relay::http::make_tls_config();
Expand Down
2 changes: 2 additions & 0 deletions iroh/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,14 @@ metrics = ["iroh-metrics", "iroh-bytes/metrics"]
fs-store = ["iroh-bytes/fs-store"]
test = []
examples = ["dep:clap", "dep:indicatif"]
test-utils = ["iroh-net/test-utils"]

[dev-dependencies]
anyhow = { version = "1" }
bytes = "1"
console-subscriber = "0.2"
genawaiter = { version = "0.99", features = ["futures03"] }
iroh = { path = ".", features = ["test-utils"] }
iroh-test = { path = "../iroh-test" }
proptest = "1.2.0"
rand_chacha = "0.3.1"
Expand Down

0 comments on commit fb4703a

Please sign in to comment.