Skip to content

Commit

Permalink
feat: Add option to use SSLKEYLOGFILE env var (#153)
Browse files Browse the repository at this point in the history
This adds support for the SSLKEYLOGFILE environment variable which is
a common standard used by browsers to log the TLS keys used for
connections.  It allows traffic inspectors like tcpdump and wireshark
to use the logged keys and decrypt the traffic.  Using this is a first
step to allowing us to observe our protocol on the wire.

The support is still hidden behind a commandline option so that it can
not be accidentally enabled by only having an environment variable
set.
  • Loading branch information
flub authored Feb 20, 2023
1 parent 19e2b05 commit d64d12d
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 6 deletions.
5 changes: 4 additions & 1 deletion src/get.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,16 @@ pub struct Options {
pub addr: SocketAddr,
/// The peer id to expect
pub peer_id: Option<PeerId>,
/// Whether to log the SSL keys when `SSLKEYLOGFILE` environment variable is set.
pub keylog: bool,
}

impl Default for Options {
fn default() -> Self {
Options {
addr: "127.0.0.1:4433".parse().unwrap(),
peer_id: None,
keylog: false,
}
}
}
Expand All @@ -48,7 +51,7 @@ impl Default for Options {
async fn setup(opts: Options) -> Result<quinn::Connection> {
let keypair = Keypair::generate();

let tls_client_config = tls::make_client_config(&keypair, opts.peer_id)?;
let tls_client_config = tls::make_client_config(&keypair, opts.peer_id, opts.keylog)?;
let mut client_config = quinn::ClientConfig::new(Arc::new(tls_client_config));
let mut endpoint = quinn::Endpoint::client("0.0.0.0:0".parse().unwrap())?;
let mut transport_config = quinn::TransportConfig::default();
Expand Down
4 changes: 4 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ mod tests {
let opts = get::Options {
addr,
peer_id: Some(peer_id),
keylog: true,
};
let content = &content;
let name = &name;
Expand Down Expand Up @@ -224,6 +225,7 @@ mod tests {
let opts = get::Options {
addr: provider.listen_addr(),
peer_id: Some(provider.peer_id()),
keylog: true,
};

let i = AtomicUsize::new(0);
Expand Down Expand Up @@ -332,6 +334,7 @@ mod tests {
get::Options {
addr: provider_addr,
peer_id: None,
keylog: true,
},
|| async move { Ok(()) },
|_collection| async move { Ok(()) },
Expand Down Expand Up @@ -380,6 +383,7 @@ mod tests {
get::Options {
addr: provider_addr,
peer_id: None,
keylog: true,
},
|| async move { Ok(()) },
|_collection| async move { Ok(()) },
Expand Down
26 changes: 23 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ enum Commands {
/// If this path is provided and it exists, the private key is read from this file and used, if it does not exist the private key will be persisted to this location.
#[clap(long)]
key: Option<PathBuf>,
/// Log SSL pre-master key to file in SSLKEYLOGFILE environment variable.
#[clap(long)]
keylog: bool,
},
/// Fetch some data by hash.
#[clap(about = "Fetch the data from the hash")]
Expand All @@ -57,6 +60,9 @@ enum Commands {
/// Optional path to a new directory in which to save the file(s). If none is specified writes the data to STDOUT.
#[clap(long, short)]
out: Option<PathBuf>,
/// Log SSL pre-master key to file in SSLKEYLOGFILE environment variable.
#[clap(long)]
keylog: bool,
},
/// Fetches some data from a ticket,
///
Expand All @@ -72,6 +78,9 @@ enum Commands {
out: Option<PathBuf>,
/// Ticket containing everything to retrieve a hash from provider.
ticket: Ticket,
/// Log SSL pre-master key to file in SSLKEYLOGFILE environment variable.
#[clap(long)]
keylog: bool,
},
}

Expand Down Expand Up @@ -205,9 +214,11 @@ async fn main() -> Result<()> {
token,
addr,
out,
keylog,
} => {
let mut opts = get::Options {
peer_id: Some(peer),
keylog,
..Default::default()
};
if let Some(addr) = addr {
Expand All @@ -226,7 +237,11 @@ async fn main() -> Result<()> {
}
}
}
Commands::GetTicket { out, ticket } => {
Commands::GetTicket {
out,
ticket,
keylog,
} => {
let Ticket {
hash,
peer,
Expand All @@ -236,6 +251,7 @@ async fn main() -> Result<()> {
let opts = get::Options {
addr,
peer_id: Some(peer),
keylog,
};
tokio::select! {
biased;
Expand All @@ -253,10 +269,11 @@ async fn main() -> Result<()> {
addr,
auth_token,
key,
keylog,
} => {
tokio::select! {
biased;
res = provide_interactive(path, addr, auth_token, key) => {
res = provide_interactive(path, addr, auth_token, key, keylog) => {
res
}
_ = tokio::signal::ctrl_c() => {
Expand All @@ -273,6 +290,7 @@ async fn provide_interactive(
addr: Option<SocketAddr>,
auth_token: Option<String>,
key: Option<PathBuf>,
keylog: bool,
) -> Result<()> {
let out_writer = OutWriter::new();
let keypair = get_keypair(key).await?;
Expand Down Expand Up @@ -314,7 +332,9 @@ async fn provide_interactive(
println!("- {}: {} bytes", path.display(), size);
}
println!();
let mut builder = provider::Provider::builder(db).keypair(keypair);
let mut builder = provider::Provider::builder(db)
.keypair(keypair)
.keylog(keylog);
if let Some(addr) = addr {
builder = builder.bind_addr(addr);
}
Expand Down
14 changes: 13 additions & 1 deletion src/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ pub struct Builder {
keypair: Keypair,
auth_token: AuthToken,
db: Database,
keylog: bool,
}

#[derive(Debug)]
Expand All @@ -90,6 +91,7 @@ impl Builder {
keypair: Keypair::generate(),
auth_token: AuthToken::generate(),
db,
keylog: false,
}
}

Expand All @@ -113,13 +115,23 @@ impl Builder {
self
}

/// Whether to log the SSL pre-master key.
///
/// If `true` and the `SSLKEYLOGFILE` environment variable is the path to a file this
/// file will be used to log the SSL pre-master key. This is useful to inspect captured
/// traffic.
pub fn keylog(mut self, keylog: bool) -> Self {
self.keylog = keylog;
self
}

/// Spawns the [`Provider`] in a tokio task.
///
/// This will create the underlying network server and spawn a tokio task accepting
/// connections. The returned [`Provider`] can be used to control the task as well as
/// get information about it.
pub fn spawn(self) -> Result<Provider> {
let tls_server_config = tls::make_server_config(&self.keypair)?;
let tls_server_config = tls::make_server_config(&self.keypair, self.keylog)?;
let mut server_config = quinn::ServerConfig::with_crypto(Arc::new(tls_server_config));
let mut transport_config = quinn::TransportConfig::default();
transport_config
Expand Down
17 changes: 16 additions & 1 deletion src/tls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,14 @@ impl FromStr for PeerId {
}

/// Create a TLS client configuration.
///
/// If *keylog* is `true` this will enable logging of the pre-master key to the file in the
/// `SSLKEYLOGFILE` environment variable. This can be used to inspect the traffic for
/// debugging purposes.
pub fn make_client_config(
keypair: &Keypair,
remote_peer_id: Option<PeerId>,
keylog: bool,
) -> Result<rustls::ClientConfig, certificate::GenError> {
let (certificate, private_key) = certificate::generate(keypair)?;

Expand All @@ -159,13 +164,21 @@ pub fn make_client_config(
.with_single_cert(vec![certificate], private_key)
.expect("Client cert key DER is valid; qed");
crypto.alpn_protocols = vec![P2P_ALPN.to_vec()];
if keylog {
crypto.key_log = Arc::new(rustls::KeyLogFile::new());
}

Ok(crypto)
}

/// Create a TLS server configuration.
///
/// If *keylog* is `true` this will enable logging of the pre-master key to the file in the
/// `SSLKEYLOGFILE` environment variable. This can be used to inspect the traffic for
/// debugging purposes.
pub fn make_server_config(
keypair: &Keypair,
keylog: bool,
) -> Result<rustls::ServerConfig, certificate::GenError> {
let (certificate, private_key) = certificate::generate(keypair)?;

Expand All @@ -178,7 +191,9 @@ pub fn make_server_config(
.with_single_cert(vec![certificate], private_key)
.expect("Server cert key DER is valid; qed");
crypto.alpn_protocols = vec![P2P_ALPN.to_vec()];

if keylog {
crypto.key_log = Arc::new(rustls::KeyLogFile::new());
}
Ok(crypto)
}

Expand Down

0 comments on commit d64d12d

Please sign in to comment.