allow dangerously disabling certificate on non-localhost (#5694)

* allow dangerously disabling certificate on non-localhost

* add pr
This commit is contained in:
Aram Drevekenin
2026-10-08 11:26:00 +02:00
committed by GitHub
parent 328be6919c
commit 9dacbc1982
42 changed files with 388 additions and 30 deletions
+4
View File
@@ -1034,6 +1034,9 @@ pub fn start_client(
let has_certificate =
config_options.web_server_cert.is_some() && config_options.web_server_key.is_some();
let enforce_https_for_localhost = config_options.enforce_https_for_localhost.unwrap_or(false);
let dangerously_allow_web_serving_without_a_certificate = config_options
.dangerously_allow_web_serving_without_a_certificate
.unwrap_or(false);
let terminal_teardown = TerminalTeardown {
include_kitty_exit: !explicitly_disable_kitty_keyboard_protocol,
@@ -1504,6 +1507,7 @@ pub fn start_client(
web_server_port,
has_certificate,
enforce_https_for_localhost,
dangerously_allow_web_serving_without_a_certificate,
);
match spawn_web_server(&cli_args) {
Ok(_) => {
+16 -3
View File
@@ -101,13 +101,26 @@ pub fn start_web_client(
let web_server_key = custom_server_key.or_else(|| config.options.web_server_key.clone());
let has_https_certificate = web_server_cert.is_some() && web_server_key.is_some();
if let Err(e) = should_use_https(
match should_use_https(
web_server_ip,
has_https_certificate,
config.options.enforce_https_for_localhost.unwrap_or(false),
config
.options
.dangerously_allow_web_serving_without_a_certificate
.unwrap_or(false),
) {
eprintln!("{}", e);
std::process::exit(2);
Ok(false) if !web_server_ip.is_loopback() => {
eprintln!(
"WARNING: serving the web client on {} over plain HTTP without an SSL certificate (dangerously_allow_web_serving_without_a_certificate is enabled). Traffic, including login tokens, is not encrypted.",
web_server_ip
);
},
Ok(_) => {},
Err(e) => {
eprintln!("{}", e);
std::process::exit(2);
},
};
let (runtime, listener, tls_config) = if run_daemonized {
daemonize_web_server(
+90 -5
View File
@@ -24,18 +24,19 @@ pub fn should_use_https(
ip: IpAddr,
has_certificate: bool,
enforce_https_for_localhost: bool,
dangerously_allow_web_serving_without_a_certificate: bool,
) -> Result<bool, String> {
let is_loopback = match ip {
IpAddr::V4(ipv4) => ipv4.is_loopback(),
IpAddr::V6(ipv6) => ipv6.is_loopback(),
};
if is_loopback && !enforce_https_for_localhost {
Ok(has_certificate)
} else if is_loopback {
Err(format!("Cannot bind without an SSL certificate."))
} else if has_certificate {
if has_certificate {
Ok(true)
} else if is_loopback && enforce_https_for_localhost {
Err(format!("Cannot bind without an SSL certificate."))
} else if is_loopback || dangerously_allow_web_serving_without_a_certificate {
Ok(false)
} else {
Err(format!(
"Cannot bind to non-loopback IP: {} without an SSL certificate.",
@@ -74,3 +75,87 @@ pub fn terminal_init_messages() -> Vec<&'static str> {
enable_mouse_mode,
]
}
#[cfg(test)]
mod should_use_https_tests {
use super::should_use_https;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
fn localhost() -> IpAddr {
IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))
}
fn remote_v4() -> IpAddr {
IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))
}
fn remote_v6() -> IpAddr {
IpAddr::V6(Ipv6Addr::UNSPECIFIED)
}
#[test]
fn non_loopback_ipv4_without_certificate_is_refused_by_default() {
assert!(should_use_https(remote_v4(), false, false, false).is_err());
assert!(should_use_https(remote_v4(), false, true, false).is_err());
}
#[test]
fn non_loopback_ipv6_without_certificate_is_refused_by_default() {
assert!(should_use_https(remote_v6(), false, false, false).is_err());
}
#[test]
fn non_loopback_with_certificate_uses_https() {
assert_eq!(should_use_https(remote_v4(), true, false, false), Ok(true));
assert_eq!(should_use_https(remote_v6(), true, false, false), Ok(true));
}
#[test]
fn loopback_without_certificate_uses_http() {
assert_eq!(
should_use_https(localhost(), false, false, false),
Ok(false)
);
assert_eq!(
should_use_https(IpAddr::V6(Ipv6Addr::LOCALHOST), false, false, false),
Ok(false)
);
}
#[test]
fn loopback_with_certificate_uses_https() {
assert_eq!(should_use_https(localhost(), true, false, false), Ok(true));
}
#[test]
fn loopback_with_enforced_https_and_certificate_uses_https() {
assert_eq!(should_use_https(localhost(), true, true, false), Ok(true));
}
#[test]
fn loopback_with_enforced_https_without_certificate_is_refused() {
assert!(should_use_https(localhost(), false, true, false).is_err());
}
#[test]
fn non_loopback_without_certificate_uses_http_when_dangerously_allowed() {
assert_eq!(should_use_https(remote_v4(), false, false, true), Ok(false));
assert_eq!(should_use_https(remote_v6(), false, false, true), Ok(false));
}
#[test]
fn non_loopback_with_certificate_still_uses_https_when_dangerously_allowed() {
assert_eq!(should_use_https(remote_v4(), true, false, true), Ok(true));
}
#[test]
fn enforced_https_for_localhost_wins_over_dangerous_allowance() {
assert!(should_use_https(localhost(), false, true, true).is_err());
}
#[test]
fn dangerous_allowance_does_not_change_loopback_behavior() {
assert_eq!(should_use_https(localhost(), false, false, true), Ok(false));
assert_eq!(should_use_https(localhost(), true, false, true), Ok(true));
}
}