Files
tkmind_go/crates/goose-server/src/tls.rs
T
Rodolfo Olivieri caef9f6466 feat: add optional native-tls support as alternative to rustls (#8037)
Signed-off-by: Rodolfo Olivieri <rolivier@redhat.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-03-25 20:46:50 +00:00

102 lines
3.3 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! TLS configuration for the goose server.
//!
//! Two TLS backends are supported for the HTTPS listener via `axum-server`:
//!
//! - **`rustls-tls`** (enabled by default) – uses `axum-server/tls-rustls` with
//! the `aws-lc-rs` crypto provider.
//! - **`native-tls`** – uses `axum-server/tls-openssl`, which links against the
//! platform's OpenSSL (or a compatible fork such as LibreSSL / BoringSSL).
//! On Linux this *is* the platform-native TLS stack; on macOS/Windows the
//! `native-tls` crate used by the HTTP *client* delegates to Security.framework
//! / SChannel respectively, but `axum-server` does not offer those backends so
//! the server listener always uses OpenSSL when this feature is active.
use anyhow::Result;
use rcgen::{CertificateParams, DnType, KeyPair, SanType};
#[cfg(feature = "rustls-tls")]
pub type TlsConfig = axum_server::tls_rustls::RustlsConfig;
#[cfg(feature = "native-tls")]
pub type TlsConfig = axum_server::tls_openssl::OpenSSLConfig;
pub struct TlsSetup {
pub config: TlsConfig,
pub fingerprint: String,
}
fn generate_self_signed_cert() -> Result<(rcgen::Certificate, KeyPair)> {
let mut params = CertificateParams::default();
params
.distinguished_name
.push(DnType::CommonName, "goosed localhost");
params.subject_alt_names = vec![
SanType::IpAddress(std::net::IpAddr::V4(std::net::Ipv4Addr::LOCALHOST)),
SanType::DnsName("localhost".try_into()?),
];
let key_pair = KeyPair::generate()?;
let cert = params.self_signed(&key_pair)?;
Ok((cert, key_pair))
}
fn sha256_fingerprint(der: &[u8]) -> String {
#[cfg(feature = "rustls-tls")]
{
let sha256 = aws_lc_rs::digest::digest(&aws_lc_rs::digest::SHA256, der);
sha256
.as_ref()
.iter()
.map(|b| format!("{b:02X}"))
.collect::<Vec<_>>()
.join(":")
}
#[cfg(feature = "native-tls")]
{
use openssl::hash::MessageDigest;
let digest =
openssl::hash::hash(MessageDigest::sha256(), der).expect("SHA-256 hash failed");
digest
.iter()
.map(|b| format!("{b:02X}"))
.collect::<Vec<_>>()
.join(":")
}
}
/// Generate a self-signed TLS certificate for localhost (127.0.0.1) and
/// return a [`TlsSetup`] containing the server config and the SHA-256
/// fingerprint of the generated certificate (colon-separated hex).
///
/// The fingerprint is printed to stdout so the parent process (e.g. Electron)
/// can pin it and reject connections from any other certificate.
pub async fn self_signed_config() -> Result<TlsSetup> {
#[cfg(feature = "rustls-tls")]
let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
let (cert, key_pair) = generate_self_signed_cert()?;
let fingerprint = sha256_fingerprint(cert.der());
println!("GOOSED_CERT_FINGERPRINT={fingerprint}");
let cert_pem = cert.pem();
let key_pem = key_pair.serialize_pem();
#[cfg(feature = "rustls-tls")]
let config = axum_server::tls_rustls::RustlsConfig::from_pem(
cert_pem.into_bytes(),
key_pem.into_bytes(),
)
.await?;
#[cfg(feature = "native-tls")]
let config =
axum_server::tls_openssl::OpenSSLConfig::from_pem(cert_pem.as_bytes(), key_pem.as_bytes())?;
Ok(TlsSetup {
config,
fingerprint,
})
}