client: default skip hostname verification; add --verify-hostname to enable

This commit is contained in:
jasonwitty 2025-08-22 22:39:06 -07:00
parent d0455611d5
commit fab1e5a104
2 changed files with 21 additions and 4 deletions

View File

@ -21,6 +21,7 @@ pub(crate) struct ParsedArgs {
dry_run: bool, // hidden test helper: skip connecting
metrics_interval_ms: Option<u64>,
processes_interval_ms: Option<u64>,
verify_hostname: bool,
}
pub(crate) fn parse_args<I: IntoIterator<Item = String>>(args: I) -> Result<ParsedArgs, String> {
@ -34,14 +35,21 @@ pub(crate) fn parse_args<I: IntoIterator<Item = String>>(args: I) -> Result<Pars
let mut dry_run = false;
let mut metrics_interval_ms: Option<u64> = None;
let mut processes_interval_ms: Option<u64> = None;
let mut verify_hostname = false;
while let Some(arg) = it.next() {
match arg.as_str() {
"-h" | "--help" => {
return Err(format!("Usage: {prog} [--tls-ca CERT_PEM|-t CERT_PEM] [--profile NAME|-P NAME] [--save] [--demo] [--metrics-interval-ms N] [--processes-interval-ms N] [ws://HOST:PORT/ws]\n"));
return Err(format!("Usage: {prog} [--tls-ca CERT_PEM|-t CERT_PEM] [--verify-hostname] [--profile NAME|-P NAME] [--save] [--demo] [--metrics-interval-ms N] [--processes-interval-ms N] [ws://HOST:PORT/ws]\n"));
}
"--tls-ca" | "-t" => {
tls_ca = it.next();
}
"--verify-hostname" => {
// opt-in hostname (SAN) verification
// default behavior is to skip it for easier home network usage
// (still pins the provided certificate)
verify_hostname = true;
}
"--profile" | "-P" => {
profile = it.next();
}
@ -89,7 +97,7 @@ pub(crate) fn parse_args<I: IntoIterator<Item = String>>(args: I) -> Result<Pars
if url.is_none() {
url = Some(arg);
} else {
return Err(format!("Unexpected argument. Usage: {prog} [--tls-ca CERT_PEM|-t CERT_PEM] [--profile NAME|-P NAME] [--save] [--demo] [ws://HOST:PORT/ws]"));
return Err(format!("Unexpected argument. Usage: {prog} [--tls-ca CERT_PEM|-t CERT_PEM] [--verify-hostname] [--profile NAME|-P NAME] [--save] [--demo] [ws://HOST:PORT/ws]"));
}
}
}
@ -103,6 +111,7 @@ pub(crate) fn parse_args<I: IntoIterator<Item = String>>(args: I) -> Result<Pars
dry_run,
metrics_interval_ms,
processes_interval_ms,
verify_hostname,
})
}
@ -118,6 +127,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
if parsed.demo || matches!(parsed.profile.as_deref(), Some("demo")) {
return run_demo_mode(parsed.tls_ca.as_deref()).await;
}
if parsed.verify_hostname {
// Set env var consumed by ws::connect logic
std::env::set_var("SOCKTOP_VERIFY_NAME", "1");
}
let profiles_file = load_profiles();
let req = ProfileRequest {
profile_name: parsed.profile.clone(),

View File

@ -56,8 +56,12 @@ async fn connect_with_ca(url: &str, ca_path: &str) -> Result<WsStream, Box<dyn s
let cfg = Arc::new(cfg);
let req = url.into_client_request()?;
let (ws, _) =
connect_async_tls_with_config(req, None, true, Some(Connector::Rustls(cfg))).await?;
// Default: skip hostname/SAN verification (cert still pinned). Enable with --verify-hostname flag
let verify_domain = std::env::var("SOCKTOP_VERIFY_NAME").ok().as_deref() == Some("1");
if !verify_domain {
eprintln!("socktop: hostname verification disabled (default). Use --verify-hostname to enable.");
}
let (ws, _) = connect_async_tls_with_config(req, None, verify_domain, Some(Connector::Rustls(cfg))).await?;
Ok(ws)
}