Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions rsworkspace/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 rsworkspace/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ hmac = "=0.13.0"
jsonwebtoken = { version = "=11.0.0", features = ["rust_crypto"] }
nats-jwt-rs = "=0.1.1"
nkeys = { version = "=0.4.5", features = ["xkeys"] }
rustls = { version = "=0.23.37", default-features = false, features = ["aws-lc-rs", "std"] }
Comment thread
coderabbitai[bot] marked this conversation as resolved.
rustls-pemfile = "=2.2.0"
rustls-pki-types = { version = "=1.15.1", default-features = false, features = ["std"] }
rustls-webpki = { version = "=0.103.13", default-features = false, features = ["std", "alloc", "ring"] }
Expand Down
3 changes: 2 additions & 1 deletion rsworkspace/crates/platform/trogon-gateway/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,11 @@ twilight-model = { workspace = true }
trogon-nats = { workspace = true }
trogon-semconv = { workspace = true }
trogon-service-config = { workspace = true }
trogon-std = { workspace = true, features = ["clap", "signal", "telemetry-http"] }
trogon-std = { workspace = true, features = ["clap", "signal", "telemetry-http", "tls"] }
url = "2.5"

[dev-dependencies]
rustls = { workspace = true }
tempfile = { workspace = true }
time = { workspace = true }
tower = "0.5"
Expand Down
4 changes: 4 additions & 0 deletions rsworkspace/crates/platform/trogon-gateway/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ type SourceResult = (&'static str, anyhow::Result<()>);
#[cfg(not(coverage))]
#[tokio::main]
async fn main() -> anyhow::Result<()> {
trogon_std::tls::install_default_crypto_provider()?;

let cli = CliArgs::<cli::Cli>::new().parse_args();
let resolved = config::load_with_overrides(cli.runtime.config.as_deref(), &cli.runtime.nats)?;

Expand Down Expand Up @@ -219,12 +221,14 @@ async fn serve(resolved: config::ResolvedConfig) -> anyhow::Result<()> {
Ok(())
}

#[cfg_attr(coverage, allow(dead_code))]
#[derive(Debug, thiserror::Error)]
enum NotionVerificationTokenCommandError {
#[error("notion integration '{0}' is not configured")]
IntegrationNotConfigured(source_integration_id::SourceIntegrationId),
}

#[cfg_attr(coverage, allow(dead_code))]
async fn notion_verification_token<N, J, W>(
resolved: &config::ResolvedConfig,
integration: &source_integration_id::SourceIntegrationId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ use futures_util::{Sink, SinkExt, Stream, StreamExt};
use serde::Deserialize;
use tokio_tungstenite::tungstenite::{Error as WebSocketError, Message};
use tracing::{info, warn};
#[cfg_attr(coverage, allow(unused_imports))]
use trogon_nats::jetstream::{ClaimCheckPublisher, JetStreamPublisher, ObjectStorePut};

#[cfg_attr(coverage, allow(unused_imports))]
use super::config::{SlackConfig, SlackSocketModeConfig};
use super::constants::RECONNECT_MAX_DELAY;
#[cfg(not(coverage))]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//! Guards the dependency graph that made every TLS handshake panic at runtime.
//!
//! `trogon-gateway` reaches rustls through both provider features: `aws-lc-rs`
//! via the OTLP exporter and `ring` via async-nats and twilight. rustls refuses
//! to pick one, so anything building a TLS client panicked instead of
//! connecting. This fails if the explicit provider install stops happening or a
//! dependency change reintroduces the ambiguity.

#[test]
fn tls_clients_build_once_the_provider_is_installed() {
assert!(trogon_std::tls::install_default_crypto_provider().is_ok());

let _ = rustls::ClientConfig::builder();
}
2 changes: 2 additions & 0 deletions rsworkspace/crates/platform/trogon-std/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ test-support = ["dep:tracing", "dep:tracing-subscriber"]
clap = ["dep:clap"]
uuid = ["dep:uuid"]
signal = ["dep:tokio", "dep:tracing"]
tls = ["dep:rustls"]
telemetry-http = [
"dep:axum",
"dep:opentelemetry",
Expand All @@ -27,6 +28,7 @@ axum = { workspace = true, optional = true }
bytesize = "2.3.1"
clap = { workspace = true, optional = true }
opentelemetry = { workspace = true, optional = true }
rustls = { workspace = true, optional = true }
trogon-semconv = { workspace = true, optional = true }
serde = { workspace = true }
serde_json = { workspace = true }
Expand Down
4 changes: 4 additions & 0 deletions rsworkspace/crates/platform/trogon-std/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ pub mod signal;
#[cfg(feature = "telemetry-http")]
pub mod telemetry;
pub mod time;
#[cfg(feature = "tls")]
pub mod tls;
#[cfg(feature = "uuid")]
pub mod uuid;

Expand All @@ -71,5 +73,7 @@ pub use json::{JsonSerialize, StdJsonSerialize};
pub use log_capture::{CapturedEvent, CapturedEvents};
pub use secret_string::{EmptySecretError, SecretString};
pub use time::{EpochClock, GetElapsed, GetNow, SystemClock};
#[cfg(feature = "tls")]
pub use tls::{CryptoProviderAlreadyInstalledError, install_default_crypto_provider};
#[cfg(feature = "uuid")]
pub use uuid::{NowV7, UuidV7Generator};
31 changes: 31 additions & 0 deletions rsworkspace/crates/platform/trogon-std/src/tls.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//! Process-wide TLS provider selection for rustls.
//!
//! rustls only derives its process-level [`rustls::crypto::CryptoProvider`] from
//! crate features when exactly one provider feature is enabled. A dependency
//! graph that reaches both `aws-lc-rs` and `ring` leaves the choice ambiguous,
//! and rustls panics on the first handshake rather than guessing. Installing the
//! provider explicitly removes the dependency on feature unification, so adding
//! a dependency cannot change which cryptography a binary uses.
//!
//! Call [`install_default_crypto_provider`] before the first TLS handshake,
//! ahead of any client construction or task spawn.

/// A rustls [`rustls::crypto::CryptoProvider`] was already installed for this
/// process, so the caller has no guarantee which cryptography it now uses.
#[derive(Debug, thiserror::Error)]
#[error("a rustls CryptoProvider was already installed for this process")]
pub struct CryptoProviderAlreadyInstalledError;

/// Installs aws-lc-rs as the process-wide rustls
/// [`rustls::crypto::CryptoProvider`].
///
/// Fails rather than accepting whichever provider got there first, so a second
/// installer cannot silently downgrade the process to different cryptography.
pub fn install_default_crypto_provider() -> Result<(), CryptoProviderAlreadyInstalledError> {
rustls::crypto::aws_lc_rs::default_provider()
.install_default()
.map_err(|_| CryptoProviderAlreadyInstalledError)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

#[cfg(test)]
mod tests;
16 changes: 16 additions & 0 deletions rsworkspace/crates/platform/trogon-std/src/tls/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use super::install_default_crypto_provider;
use rustls::crypto::CryptoProvider;

#[test]
fn installs_a_process_wide_provider() {
let _ = install_default_crypto_provider();

assert!(CryptoProvider::get_default().is_some());
}

#[test]
fn refuses_to_replace_an_installed_provider() {
let _ = install_default_crypto_provider();

assert!(install_default_crypto_provider().is_err());
}
Loading