Skip to content

Commit

Permalink
refactor: update to newest quinn and rustls (#70)
Browse files Browse the repository at this point in the history
  • Loading branch information
M0dEx authored Jun 6, 2024
1 parent 1ee801d commit 90c674b
Show file tree
Hide file tree
Showing 8 changed files with 413 additions and 321 deletions.
586 changes: 322 additions & 264 deletions Cargo.lock

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "quincy"
version = "0.9.3"
version = "0.10.0"
authors = ["Jakub Kubík <[email protected]>"]
license = "MIT"
description = "QUIC-based VPN"
Expand Down Expand Up @@ -35,13 +35,13 @@ panic = "abort"

[dependencies]
# Quinn
quinn = "^0.10.1"
quinn = "^0.11.1"

# Interfaces and networking
tun2 = { version = "^1.0.0", features = ["async"] }
socket2 = "^0.5.2"
bytes = "^1.4"
etherparse = "^0.14.0"
etherparse = "^0.15.0"
ipnet = "^2.7"
libc = "^0.2.147"

Expand All @@ -63,8 +63,8 @@ serde = { version = "^1.0", features = ["derive"] }
serde_json = "^1.0"

# TLS
rustls = "^0.21.11"
rustls-pemfile = "^1.0"
rustls = { version = "^0.23.0", default-features = false, features = ["ring"] }
rustls-pemfile = "^2.0"

# Authentication
argon2 = "^0.5.0"
Expand All @@ -90,5 +90,5 @@ tracing-subscriber = { version = "^0.3.17", features = ["env-filter", "ansi"] }
nu-ansi-term = "^0.50.0"

[dev-dependencies]
rstest = "^0.18.0"
rstest = "^0.21.0"
tracing-test = { version = "^0.2.4", features = ["no-env-filter"] }
8 changes: 6 additions & 2 deletions src/auth/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ impl AuthServer {

let message = timeout(self.auth_timeout, auth_stream.recv_message()).await??;

match message {
let auth_result = match message {
AuthMessage::Authenticate(payload) => {
let (username, address) = self
.authenticator
Expand All @@ -68,6 +68,10 @@ impl AuthServer {
Ok((username, address))
}
_ => Err(anyhow!("authentication failed")),
}
}?;

auth_stream.close()?;

Ok(auth_result)
}
}
6 changes: 4 additions & 2 deletions src/auth/stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,10 @@ impl AuthStream {
serde_json::from_slice(&buf).context("failed to parse AuthMessage JSON")
}

pub async fn close(mut self) -> Result<()> {
self.send_stream.finish().await?;
/// Closes the authentication stream.
pub fn close(mut self) -> Result<()> {
// Ignore the result of finish() since we're closing the stream anyway
_ = self.send_stream.finish();

Ok(())
}
Expand Down
68 changes: 45 additions & 23 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@ use figment::{
providers::{Env, Format, Toml},
Figment,
};
use quinn::{EndpointConfig, TransportConfig};
use rustls::{Certificate, RootCertStore};
use quinn::{
crypto::rustls::{QuicClientConfig, QuicServerConfig},
EndpointConfig, TransportConfig,
};
use rustls::crypto::ring::cipher_suite::TLS13_AES_128_GCM_SHA256;
use rustls::pki_types::CertificateDer;
use rustls::RootCertStore;
use serde::de::DeserializeOwned;
use serde::Deserialize;
use std::net::{IpAddr, Ipv4Addr};
Expand All @@ -13,7 +18,7 @@ use std::sync::Arc;
use std::time::Duration;

use crate::constants::{
QUIC_MTU_OVERHEAD, QUINCY_CIPHER_SUITES, TLS_ALPN_PROTOCOLS, TLS_PROTOCOL_VERSIONS,
CRYPTO_PROVIDER, QUIC_MTU_OVERHEAD, TLS_ALPN_PROTOCOLS, TLS_PROTOCOL_VERSIONS,
};
use crate::utils::certificates::{load_certificates_from_file, load_private_key_from_file};
use tracing::error;
Expand Down Expand Up @@ -197,7 +202,7 @@ impl ClientConfig {
/// ### Returns
/// - `quinn::ClientConfig` - the Quinn client configuration
pub fn as_quinn_client_config(&self) -> Result<quinn::ClientConfig> {
let trusted_certificates: Vec<Certificate> = self
let trusted_certificates: Vec<CertificateDer> = self
.authentication
.trusted_certificates
.iter()
Expand All @@ -214,19 +219,27 @@ impl ClientConfig {
let mut cert_store = RootCertStore::empty();

for certificate in trusted_certificates {
cert_store.add(&certificate)?;
cert_store.add(certificate)?;
}

let mut rustls_config = rustls::ClientConfig::builder()
.with_cipher_suites(QUINCY_CIPHER_SUITES)
.with_safe_default_kx_groups()
.with_protocol_versions(TLS_PROTOCOL_VERSIONS)?
.with_root_certificates(cert_store)
.with_no_client_auth();
let mut rustls_config =
rustls::ClientConfig::builder_with_provider(CRYPTO_PROVIDER.clone())
.with_protocol_versions(TLS_PROTOCOL_VERSIONS)?
.with_root_certificates(cert_store)
.with_no_client_auth();

rustls_config.alpn_protocols.clone_from(&TLS_ALPN_PROTOCOLS);

rustls_config.alpn_protocols = TLS_ALPN_PROTOCOLS.clone();
let quic_client_config = QuicClientConfig::with_initial(
rustls_config.into(),
TLS13_AES_128_GCM_SHA256
.tls13()
.expect("QUIC initial suite is a valid TLS 1.3 suite")
.quic_suite()
.expect("QUIC initial suite is a valid QUIC suite"),
)?;

let mut quinn_config = quinn::ClientConfig::new(Arc::new(rustls_config));
let mut quinn_config = quinn::ClientConfig::new(Arc::new(quic_client_config));
let mut transport_config = TransportConfig::default();

transport_config.max_idle_timeout(Some(self.connection.connection_timeout.try_into()?));
Expand Down Expand Up @@ -254,16 +267,25 @@ impl ServerConfig {
let key = load_private_key_from_file(&certificate_key_path)?;
let certs = load_certificates_from_file(&certificate_file_path)?;

let mut rustls_config = rustls::ServerConfig::builder()
.with_cipher_suites(QUINCY_CIPHER_SUITES)
.with_safe_default_kx_groups()
.with_protocol_versions(TLS_PROTOCOL_VERSIONS)?
.with_no_client_auth()
.with_single_cert(certs, key)?;

rustls_config.alpn_protocols = TLS_ALPN_PROTOCOLS.clone();

let mut quinn_config = quinn::ServerConfig::with_crypto(Arc::new(rustls_config));
let mut rustls_config =
rustls::ServerConfig::builder_with_provider(CRYPTO_PROVIDER.clone())
.with_protocol_versions(TLS_PROTOCOL_VERSIONS)?
.with_no_client_auth()
.with_single_cert(certs, key.into())?;

rustls_config.alpn_protocols.clone_from(&TLS_ALPN_PROTOCOLS);
rustls_config.max_early_data_size = 0;

let quic_server_config = QuicServerConfig::with_initial(
rustls_config.into(),
TLS13_AES_128_GCM_SHA256
.tls13()
.expect("QUIC initial suite is a valid TLS 1.3 suite")
.quic_suite()
.expect("QUIC initial suite is a valid QUIC suite"),
)?;

let mut quinn_config = quinn::ServerConfig::with_crypto(Arc::new(quic_server_config));
let mut transport_config = TransportConfig::default();

transport_config.max_idle_timeout(Some(self.connection.connection_timeout.try_into()?));
Expand Down
22 changes: 16 additions & 6 deletions src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ use std::sync::Arc;

use once_cell::sync::Lazy;
use quinn::Runtime;
use rustls::crypto::{ring, CryptoProvider};
use rustls::CipherSuite;

/// Represents the maximum MTU overhead for QUIC, since the QUIC header is variable in size.
pub const QUIC_MTU_OVERHEAD: u16 = 50;
Expand All @@ -15,12 +17,6 @@ pub const PACKET_BUFFER_SIZE: usize = 4;
/// Packet channel size used for communication between the TUN interface and QUIC tunnels.
pub const PACKET_CHANNEL_SIZE: usize = 1024 * 1024;

/// Represents the supported TLS cipher suites for Quincy.
pub static QUINCY_CIPHER_SUITES: &[rustls::SupportedCipherSuite] = &[
rustls::cipher_suite::TLS13_AES_256_GCM_SHA384,
rustls::cipher_suite::TLS13_CHACHA20_POLY1305_SHA256,
];

/// Represents the supported TLS protocol versions for Quincy.
pub static TLS_PROTOCOL_VERSIONS: &[&rustls::SupportedProtocolVersion] = &[&rustls::version::TLS13];

Expand All @@ -29,3 +25,17 @@ pub static TLS_ALPN_PROTOCOLS: Lazy<Vec<Vec<u8>>> = Lazy::new(|| vec![b"quincy".

/// Represents the async runtime used by Quinn.
pub static QUINN_RUNTIME: Lazy<Arc<dyn Runtime>> = Lazy::new(|| Arc::new(quinn::TokioRuntime));

/// Represents the crypto provider used by Quincy.
pub static CRYPTO_PROVIDER: Lazy<Arc<CryptoProvider>> = Lazy::new(|| {
let mut provider = ring::default_provider();

provider.cipher_suites.retain(|suite| {
matches!(
suite.suite(),
CipherSuite::TLS13_AES_256_GCM_SHA384 | CipherSuite::TLS13_CHACHA20_POLY1305_SHA256
)
});

Arc::new(provider)
});
8 changes: 3 additions & 5 deletions src/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ impl QuincyServer {
/// Handles incoming connections by spawning a new QuincyConnection instance for them.
///
/// ### Arguments
/// - `ingress_queue` - the queue to send data to the TUN interface
/// - `endpoint` - the QUIC endpoint
/// - `auth_server` - the authentication server to use for authenticating clients
/// - `ingress_queue` - the queue for sending data to the TUN interface
async fn handle_connections(
&self,
auth_server: AuthServer,
Expand Down Expand Up @@ -185,9 +185,6 @@ impl QuincyServer {
}

/// Creates a Quinn QUIC endpoint that clients can connect to.
///
/// ### Arguments
/// - `quinn_config` - the Quinn server configuration to use
fn create_quinn_endpoint(&self) -> Result<Endpoint> {
let quinn_config = self.config.as_quinn_server_config()?;

Expand Down Expand Up @@ -250,6 +247,7 @@ impl QuincyServer {
/// - `connection_queues` - the queues for sending data to the QUIC connections
/// - `tun_write` - the write half of the TUN interface
/// - `ingress_queue` - the queue for sending data to the TUN interface
/// - `isolate_clients` - whether to isolate clients from each other
async fn process_inbound_traffic(
connection_queues: ConnectionQueues,
tun_write: impl InterfaceWrite,
Expand Down
24 changes: 11 additions & 13 deletions src/utils/certificates.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use anyhow::anyhow;
use anyhow::Result;
use rustls::{Certificate, PrivateKey};
use rustls::pki_types::{CertificateDer, PrivatePkcs8KeyDer};
use std::fs::File;
use std::io::BufReader;
use std::path::Path;
Expand All @@ -11,14 +11,14 @@ use std::path::Path;
/// - `path` - Path to the file containing the certificates.
///
/// ### Returns
/// - `Vec<Certificate>` - A list of loaded certificates.
pub fn load_certificates_from_file(path: &Path) -> Result<Vec<Certificate>> {
/// - `Vec<CertificateDer>` - A list of loaded certificates.
pub fn load_certificates_from_file(path: &Path) -> Result<Vec<CertificateDer<'static>>> {
let file = File::open(path)?;
let mut reader = BufReader::new(file);

let certificates_bytes = rustls_pemfile::certs(&mut reader)?;
let certs: Result<Vec<CertificateDer>, _> = rustls_pemfile::certs(&mut reader).collect();

Ok(certificates_bytes.into_iter().map(Certificate).collect())
Ok(certs?)
}

/// Loads a private key from a file.
Expand All @@ -27,15 +27,13 @@ pub fn load_certificates_from_file(path: &Path) -> Result<Vec<Certificate>> {
/// - `path` - Path to the file containing the private key.
///
/// ### Returns
/// - `PrivateKey` - The loaded private key.
pub fn load_private_key_from_file(path: &Path) -> Result<PrivateKey> {
/// - `PrivatePkcs8KeyDer` - The loaded private key.
pub fn load_private_key_from_file(path: &Path) -> Result<PrivatePkcs8KeyDer<'static>> {
let file = File::open(path)?;
let mut reader = BufReader::new(file);

let private_key_bytes = rustls_pemfile::pkcs8_private_keys(&mut reader)?
.first()
.ok_or_else(|| anyhow!("No private key found in the file: {path:?}"))?
.clone();

Ok(PrivateKey(private_key_bytes))
Ok(rustls_pemfile::pkcs8_private_keys(&mut reader)
.last()
.ok_or(anyhow!("No private key found in file"))??
.clone_key())
}

0 comments on commit 90c674b

Please sign in to comment.