feat(relay): write development certificate
The gateway and Windows client now pin a relay certificate, but local relay runs generated an ephemeral self-signed certificate only in memory. That made the development trust flow awkward because there was no stable DER artifact to feed into the new CLIs. Add `--dev-cert-der-out` to write the generated development certificate before the relay binds its endpoint. The file is DER-encoded and parent directories are created when needed. This keeps the production certificate/key path explicit future work while making the current pinned-trust flow usable. Test Plan: - cargo fmt --check - cargo test --workspace - cargo clippy --workspace --all-targets -- -D warnings - git diff --check Refs: PLAN.md relay/client trust bootstrap
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
use std::{net::SocketAddr, sync::Arc};
|
||||
use std::{fs, net::SocketAddr, path::Path, sync::Arc};
|
||||
|
||||
use anyhow::{Context, Result, anyhow};
|
||||
use bytes::Bytes;
|
||||
@@ -59,7 +59,10 @@ struct PeerSession {
|
||||
|
||||
impl RelayServer {
|
||||
pub fn bind(config: &RelayConfig) -> Result<Self> {
|
||||
let server_config = development_server_config()?;
|
||||
let (server_config, certificate) = development_server_config_with_certificate()?;
|
||||
if let Some(path) = config.dev_cert_der_out() {
|
||||
write_development_certificate(path, &certificate)?;
|
||||
}
|
||||
let endpoint = Endpoint::server(server_config, config.listen().socket_addr())
|
||||
.context("failed to bind QUIC relay endpoint")?;
|
||||
|
||||
@@ -440,10 +443,22 @@ async fn leave_peer(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn development_server_config() -> Result<ServerConfig> {
|
||||
let (server_config, _) = development_server_config_with_certificate()?;
|
||||
fn write_development_certificate(path: &Path, certificate: &CertificateDer<'_>) -> Result<()> {
|
||||
if let Some(parent) = path
|
||||
.parent()
|
||||
.filter(|parent| !parent.as_os_str().is_empty())
|
||||
{
|
||||
fs::create_dir_all(parent).with_context(|| {
|
||||
format!(
|
||||
"failed to create certificate directory {}",
|
||||
parent.display()
|
||||
)
|
||||
})?;
|
||||
}
|
||||
fs::write(path, certificate.as_ref())
|
||||
.with_context(|| format!("failed to write development certificate {}", path.display()))?;
|
||||
|
||||
Ok(server_config)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn development_server_config_with_certificate() -> Result<(ServerConfig, CertificateDer<'static>)> {
|
||||
@@ -477,7 +492,7 @@ fn development_server_config_with_certificate() -> Result<(ServerConfig, Certifi
|
||||
mod tests {
|
||||
use std::{
|
||||
net::{IpAddr, Ipv4Addr, SocketAddr},
|
||||
time::Duration,
|
||||
time::{Duration, SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
|
||||
use bytes::Bytes;
|
||||
@@ -558,6 +573,25 @@ mod tests {
|
||||
assert_eq!(rooms.lock().await.room_count(), 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn writes_development_certificate_when_configured() {
|
||||
let cert_path = unique_temp_cert_path();
|
||||
let config = RelayConfig::with_dev_cert_der_out(
|
||||
ListenEndpoint::new(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0)),
|
||||
DEFAULT_MAX_CLIENTS_PER_ROOM,
|
||||
Some(cert_path.clone()),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let server = RelayServer::bind(&config).unwrap();
|
||||
let cert = std::fs::read(&cert_path).unwrap();
|
||||
|
||||
assert!(!cert.is_empty());
|
||||
|
||||
server.shutdown("test complete").await;
|
||||
std::fs::remove_file(cert_path).unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn forwards_ethernet_datagrams_between_joined_peers() {
|
||||
let (server, certificate) = bind_test_server(DEFAULT_MAX_CLIENTS_PER_ROOM);
|
||||
@@ -709,4 +743,16 @@ mod tests {
|
||||
frame.extend_from_slice(b"payload");
|
||||
frame
|
||||
}
|
||||
|
||||
fn unique_temp_cert_path() -> std::path::PathBuf {
|
||||
let nanos = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
|
||||
std::env::temp_dir().join(format!(
|
||||
"lanparty-relay-dev-cert-{}-{nanos}.der",
|
||||
std::process::id()
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user