Support client certificates (#319)

This commit is contained in:
Gregory Schier
2025-12-10 13:54:22 -08:00
committed by GitHub
parent ef1ba9b834
commit c4b559f34b
39 changed files with 1131 additions and 236 deletions

View File

@@ -8,7 +8,7 @@ publish = false
[dependencies]
futures-util = "0.3.31"
log = { workspace = true }
md5 = "0.7.0"
md5 = "0.8.0"
reqwest_cookie_store = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
@@ -17,6 +17,7 @@ thiserror = { workspace = true }
tokio = { workspace = true, features = ["macros", "time", "test-util"] }
tokio-tungstenite = { version = "0.26.2", default-features = false, features = ["rustls-tls-native-roots", "connect"] }
yaak-http = { workspace = true }
yaak-tls = { workspace = true }
yaak-models = { workspace = true }
yaak-plugins = { workspace = true }
yaak-templates = { workspace = true }

View File

@@ -23,6 +23,7 @@ use yaak_plugins::events::{
use yaak_plugins::manager::PluginManager;
use yaak_plugins::template_callback::PluginTemplateCallback;
use yaak_templates::{RenderErrorBehavior, RenderOptions};
use yaak_tls::find_client_certificate;
#[tauri::command]
pub(crate) async fn upsert_request<R: Runtime>(
@@ -196,6 +197,7 @@ pub(crate) async fn connect<R: Runtime>(
environment_id,
)?;
let workspace = app_handle.db().get_workspace(&unrendered_request.workspace_id)?;
let settings = app_handle.db().get_settings();
let (resolved_request, auth_context_id) =
resolve_websocket_request(&window, &unrendered_request)?;
let request = render_websocket_request(
@@ -363,6 +365,8 @@ pub(crate) async fn connect<R: Runtime>(
}
}
let client_cert = find_client_certificate(url.as_str(), &settings.client_certificates);
let response = match ws_manager
.connect(
&connection.id,
@@ -370,6 +374,7 @@ pub(crate) async fn connect<R: Runtime>(
headers,
receive_tx,
workspace.setting_validate_certificates,
client_cert,
)
.await
{

View File

@@ -1,3 +1,4 @@
use crate::error::Result;
use log::info;
use std::sync::Arc;
use tauri::http::HeaderMap;
@@ -9,6 +10,7 @@ use tokio_tungstenite::tungstenite::protocol::WebSocketConfig;
use tokio_tungstenite::{
Connector, MaybeTlsStream, WebSocketStream, connect_async_tls_with_config,
};
use yaak_tls::{ClientCertificateConfig, get_tls_config};
// Enabling ALPN breaks websocket requests
const WITH_ALPN: bool = false;
@@ -17,9 +19,10 @@ pub(crate) async fn ws_connect(
url: &str,
headers: HeaderMap<HeaderValue>,
validate_certificates: bool,
) -> crate::error::Result<(WebSocketStream<MaybeTlsStream<TcpStream>>, Response)> {
client_cert: Option<ClientCertificateConfig>,
) -> Result<(WebSocketStream<MaybeTlsStream<TcpStream>>, Response)> {
info!("Connecting to WS {url}");
let tls_config = yaak_http::tls::get_config(validate_certificates, WITH_ALPN);
let tls_config = get_tls_config(validate_certificates, WITH_ALPN, client_cert.clone())?;
let mut req = url.into_client_request()?;
let req_headers = req.headers_mut();
@@ -36,5 +39,12 @@ pub(crate) async fn ws_connect(
Some(Connector::Rustls(Arc::new(tls_config))),
)
.await?;
info!(
"Connected to WS {url} validate_certificates={} client_cert={}",
validate_certificates,
client_cert.is_some()
);
Ok((stream, response))
}

View File

@@ -16,6 +16,9 @@ pub enum Error {
#[error(transparent)]
TemplateError(#[from] yaak_templates::error::Error),
#[error(transparent)]
TlsError(#[from] yaak_tls::error::Error),
#[error("WebSocket error: {0}")]
GenericError(String),
}

View File

@@ -12,6 +12,7 @@ use tokio_tungstenite::tungstenite::Message;
use tokio_tungstenite::tungstenite::handshake::client::Response;
use tokio_tungstenite::tungstenite::http::{HeaderMap, HeaderValue};
use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
use yaak_tls::ClientCertificateConfig;
#[derive(Clone)]
pub struct WebsocketManager {
@@ -35,10 +36,12 @@ impl WebsocketManager {
headers: HeaderMap<HeaderValue>,
receive_tx: mpsc::Sender<Message>,
validate_certificates: bool,
client_cert: Option<ClientCertificateConfig>,
) -> Result<Response> {
let tx = receive_tx.clone();
let (stream, response) = ws_connect(url, headers, validate_certificates).await?;
let (stream, response) =
ws_connect(url, headers, validate_certificates, client_cert).await?;
let (write, mut read) = stream.split();
self.connections.lock().await.insert(id.to_string(), write);