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
+1
View File
@@ -28,6 +28,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
* feat: let a plugin give its layout space back while it has nothing to draw (https://github.com/zellij-org/zellij/pull/5590)
* fix: occasional resurrection layout serialization corruption (https://github.com/zellij-org/zellij/pull/5690)
* fix: support horizontal scroll for programs that request it (https://github.com/zellij-org/zellij/pull/4860)
* feat: allow config to dangerously disable https certificate enforcement on non-localhost (https://github.com/zellij-org/zellij/pull/5694)
## [0.45.1] - 2026-08-28
* fix: nested-session detection over SSH (https://github.com/zellij-org/zellij/pull/5522)
@@ -807,6 +807,14 @@ pub fn describe(key: SettingKey) -> SettingInfo {
"false",
Everyone,
),
SettingKey::DangerouslyAllowWebServingWithoutACertificate => info(
"Allow HTTP without certificate",
Web,
"DANGEROUS: serve unencrypted HTTP on non-localhost addresses",
Toggle,
"false",
Everyone,
),
SettingKey::ClientAsyncWorkerTasks => info(
"Web worker tasks",
Web,
@@ -952,6 +960,7 @@ pub fn section(key: SettingKey) -> &'static str {
| WebServerCert
| WebServerKey
| EnforceHttpsForLocalhost
| DangerouslyAllowWebServingWithoutACertificate
| ClientAsyncWorkerTasks => "Server",
WebClientFont
| WebClientFontSize
+12 -1
View File
@@ -648,12 +648,23 @@ load_plugins {
// (127.0.0.0/8)
// (Requires restart)
//
// Note: https is ALWAYS enforced when bound to non-local interfaces
// Note: https is enforced when bound to non-local interfaces, unless
// dangerously_allow_web_serving_without_a_certificate is set
//
// Default: false
//
// enforce_https_for_localhost true
// DANGEROUS: allow the web server to serve plain, unencrypted HTTP when bound to a
// non-local interface without web_server_cert and web_server_key.
// Anyone on the network can read and tamper with the traffic, including login tokens.
// Only use behind a trusted TLS-terminating proxy or on a fully trusted network.
// Does not override enforce_https_for_localhost.
// Default: false
// (Requires restart)
//
// dangerously_allow_web_serving_without_a_certificate true
// The port the web server should listen on when it starts
// Default: 8082
// (Requires restart)
+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));
}
}
+4
View File
@@ -4416,6 +4416,9 @@ fn init_session(
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 default_shell = config_options.default_shell.clone().map(|command| {
TerminalAction::RunCommand(RunCommand {
@@ -4581,6 +4584,7 @@ fn init_session(
web_server_port,
has_certificate,
enforce_https_for_localhost,
dangerously_allow_web_serving_without_a_certificate,
);
move || {
background_jobs_main(
+12 -1
View File
@@ -418,12 +418,23 @@ context_menu {
// (127.0.0.0/8)
// (Requires restart)
//
// Note: https is ALWAYS enforced when bound to non-local interfaces
// Note: https is enforced when bound to non-local interfaces, unless
// dangerously_allow_web_serving_without_a_certificate is set
//
// Default: false
//
// enforce_https_for_localhost true
// DANGEROUS: allow the web server to serve plain, unencrypted HTTP when bound to a
// non-local interface without web_server_cert and web_server_key.
// Anyone on the network can read and tamper with the traffic, including login tokens.
// Only use behind a trusted TLS-terminating proxy or on a fully trusted network.
// Does not override enforce_https_for_localhost.
// Default: false
// (Requires restart)
//
// dangerously_allow_web_serving_without_a_certificate true
// The port the web server should listen on when it starts
// Default: 8082
// (Requires restart)
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -2174,6 +2174,8 @@ pub struct Options {
pub keybinds_unlock: ::core::option::Option<::prost::alloc::string::String>,
#[prost(bool, optional, tag="75")]
pub context_menu_enabled: ::core::option::Option<bool>,
#[prost(bool, optional, tag="76")]
pub dangerously_allow_web_serving_without_a_certificate: ::core::option::Option<bool>,
}
/// Pane-targeting action messages
#[allow(clippy::derive_partial_eq_without_eq)]
@@ -1286,6 +1286,7 @@ message Options {
optional string keybinds_secondary = 73;
optional string keybinds_unlock = 74;
optional bool context_menu_enabled = 75;
optional bool dangerously_allow_web_serving_without_a_certificate = 76;
}
enum OnForceClose {
+1
View File
@@ -4672,6 +4672,7 @@ setting_keys! {
WebServerCert => (TopLevel, "web_server_cert", true, Text),
WebServerKey => (TopLevel, "web_server_key", true, Text),
EnforceHttpsForLocalhost => (TopLevel, "enforce_https_for_localhost", true, Flag),
DangerouslyAllowWebServingWithoutACertificate => (TopLevel, "dangerously_allow_web_serving_without_a_certificate", true, Flag),
PostCommandDiscoveryHook => (TopLevel, "post_command_discovery_hook", false, Text),
ClientAsyncWorkerTasks => (TopLevel, "client_async_worker_tasks", true, Number),
NestedSessionHandling => (TopLevel, "nested_session_handling", false, Text),
@@ -163,6 +163,7 @@ fn option_values(options: &Options, values: &mut BTreeMap<SettingKey, Option<Str
web_server_cert,
web_server_key,
enforce_https_for_localhost,
dangerously_allow_web_serving_without_a_certificate,
post_command_discovery_hook,
client_async_worker_tasks,
nested_session_handling,
@@ -303,6 +304,10 @@ fn option_values(options: &Options, values: &mut BTreeMap<SettingKey, Option<Str
SettingKey::EnforceHttpsForLocalhost,
display_text(enforce_https_for_localhost),
),
(
SettingKey::DangerouslyAllowWebServingWithoutACertificate,
display_text(dangerously_allow_web_serving_without_a_certificate),
),
(
SettingKey::PostCommandDiscoveryHook,
post_command_discovery_hook.clone(),
@@ -622,6 +627,10 @@ pub fn copy_setting(target: &mut Config, source: &Config, key: SettingKey) {
SettingKey::EnforceHttpsForLocalhost => {
options.enforce_https_for_localhost = from.enforce_https_for_localhost
},
SettingKey::DangerouslyAllowWebServingWithoutACertificate => {
options.dangerously_allow_web_serving_without_a_certificate =
from.dangerously_allow_web_serving_without_a_certificate
},
SettingKey::PostCommandDiscoveryHook => {
options.post_command_discovery_hook = from.post_command_discovery_hook.clone()
},
+9
View File
@@ -447,6 +447,7 @@ pub struct Options {
pub web_server_cert: Option<PathBuf>,
pub web_server_key: Option<PathBuf>,
pub enforce_https_for_localhost: Option<bool>,
pub dangerously_allow_web_serving_without_a_certificate: Option<bool>,
/// A command to run after the discovery of running commands when serializing, for the purpose
/// of manipulating the command (eg. with a regex) before it gets serialized
#[clap(long, value_parser)]
@@ -647,6 +648,9 @@ impl Options {
let enforce_https_for_localhost = other
.enforce_https_for_localhost
.or(self.enforce_https_for_localhost);
let dangerously_allow_web_serving_without_a_certificate = other
.dangerously_allow_web_serving_without_a_certificate
.or(self.dangerously_allow_web_serving_without_a_certificate);
let post_command_discovery_hook = other
.post_command_discovery_hook
.or(self.post_command_discovery_hook.clone());
@@ -722,6 +726,7 @@ impl Options {
web_server_cert,
web_server_key,
enforce_https_for_localhost,
dangerously_allow_web_serving_without_a_certificate,
post_command_discovery_hook,
client_async_worker_tasks,
nested_session_handling,
@@ -838,6 +843,9 @@ impl Options {
let enforce_https_for_localhost = other
.enforce_https_for_localhost
.or(self.enforce_https_for_localhost);
let dangerously_allow_web_serving_without_a_certificate = other
.dangerously_allow_web_serving_without_a_certificate
.or(self.dangerously_allow_web_serving_without_a_certificate);
let post_command_discovery_hook = other
.post_command_discovery_hook
.or_else(|| self.post_command_discovery_hook.clone());
@@ -913,6 +921,7 @@ impl Options {
web_server_cert,
web_server_key,
enforce_https_for_localhost,
dangerously_allow_web_serving_without_a_certificate,
post_command_discovery_hook,
client_async_worker_tasks,
nested_session_handling,
@@ -76,6 +76,7 @@ fn every_option_set() -> Options {
web_server_cert: Some(PathBuf::from("/tmp/cert.pem")),
web_server_key: Some(PathBuf::from("/tmp/key.pem")),
enforce_https_for_localhost: Some(true),
dangerously_allow_web_serving_without_a_certificate: Some(true),
post_command_discovery_hook: Some("echo hook".to_owned()),
client_async_worker_tasks: Some(8),
nested_session_handling: Some(NestedSessionHandling::Never),
@@ -996,6 +996,8 @@ impl From<crate::input::options::Options>
.web_server_key
.map(|p| p.to_string_lossy().to_string()),
enforce_https_for_localhost: options.enforce_https_for_localhost,
dangerously_allow_web_serving_without_a_certificate: options
.dangerously_allow_web_serving_without_a_certificate,
post_command_discovery_hook: options.post_command_discovery_hook,
client_async_worker_tasks: options.client_async_worker_tasks.map(|v| v as u64),
visual_bell: options.visual_bell,
@@ -1140,6 +1142,8 @@ impl TryFrom<crate::client_server_contract::client_server_contract::Options>
web_server_cert: options.web_server_cert.map(std::path::PathBuf::from),
web_server_key: options.web_server_key.map(std::path::PathBuf::from),
enforce_https_for_localhost: options.enforce_https_for_localhost,
dangerously_allow_web_serving_without_a_certificate: options
.dangerously_allow_web_serving_without_a_certificate,
post_command_discovery_hook: options.post_command_discovery_hook,
client_async_worker_tasks: options.client_async_worker_tasks.map(|v| v as usize),
visual_bell: options.visual_bell,
@@ -515,6 +515,7 @@ fn test_client_messages() {
web_server_cert: Some(PathBuf::from("web_server_cert")),
web_server_key: Some(PathBuf::from("web_server_key")),
enforce_https_for_localhost: Some(true),
dangerously_allow_web_serving_without_a_certificate: Some(true),
post_command_discovery_hook: Some("post_command_discovery_hook".to_owned()),
client_async_worker_tasks: Some(16),
mouse_hover_effects: Some(false),
+93 -2
View File
@@ -3128,6 +3128,12 @@ impl Options {
let enforce_https_for_localhost =
kdl_property_first_arg_as_bool_or_error!(kdl_options, "enforce_https_for_localhost")
.map(|(v, _)| v);
let dangerously_allow_web_serving_without_a_certificate =
kdl_property_first_arg_as_bool_or_error!(
kdl_options,
"dangerously_allow_web_serving_without_a_certificate"
)
.map(|(v, _)| v);
let post_command_discovery_hook =
kdl_property_first_arg_as_string_or_error!(kdl_options, "post_command_discovery_hook")
.map(|(hook, _entry)| hook.to_string());
@@ -3257,6 +3263,7 @@ impl Options {
web_server_cert,
web_server_key,
enforce_https_for_localhost,
dangerously_allow_web_serving_without_a_certificate,
post_command_discovery_hook,
client_async_worker_tasks,
nested_session_handling,
@@ -4400,11 +4407,12 @@ impl Options {
}
fn enforce_https_for_localhost_to_kdl(&self, add_comments: bool) -> Option<KdlNode> {
let comment_text = format!(
"{}\n{}\n{}\n{}\n{}\n{}\n{}",
"{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}",
"/// Whether to enforce https connections to the web server when it is bound to localhost",
"/// (127.0.0.0/8)",
"///",
"/// Note: https is ALWAYS enforced when bound to non-local interfaces",
"/// Note: https is enforced when bound to non-local interfaces, unless",
"/// dangerously_allow_web_serving_without_a_certificate is set",
"///",
"/// Default: false",
"// ",
@@ -4429,6 +4437,42 @@ impl Options {
None
}
}
fn dangerously_allow_web_serving_without_a_certificate_to_kdl(
&self,
add_comments: bool,
) -> Option<KdlNode> {
let comment_text = format!(
"{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}",
" ",
"// DANGEROUS: allow the web server to serve plain, unencrypted HTTP when bound to a",
"// non-local interface without web_server_cert and web_server_key.",
"// Anyone on the network can read and tamper with the traffic, including login tokens.",
"// Only use behind a trusted TLS-terminating proxy or on a fully trusted network.",
"// Does not override enforce_https_for_localhost.",
"// Default: false",
"// (Requires restart)",
"//",
);
let create_node = |node_value: bool| -> KdlNode {
let mut node = KdlNode::new("dangerously_allow_web_serving_without_a_certificate");
node.push(KdlValue::Bool(node_value));
node
};
if let Some(value) = self.dangerously_allow_web_serving_without_a_certificate {
let mut node = create_node(value);
if add_comments {
node.set_leading(format!("{}\n", comment_text));
}
Some(node)
} else if add_comments {
let mut node = create_node(false);
node.set_leading(format!("{}\n// ", comment_text));
Some(node)
} else {
None
}
}
fn stacked_resize_to_kdl(&self, add_comments: bool) -> Option<KdlNode> {
let comment_text = format!(
"{}\n{}\n{}\n{}",
@@ -5171,6 +5215,11 @@ impl Options {
{
nodes.push(enforce_https_for_localhost);
}
if let Some(dangerously_allow_web_serving_without_a_certificate) =
self.dangerously_allow_web_serving_without_a_certificate_to_kdl(add_comments)
{
nodes.push(dangerously_allow_web_serving_without_a_certificate);
}
if let Some(stacked_resize) = self.stacked_resize_to_kdl(add_comments) {
nodes.push(stacked_resize);
}
@@ -8888,6 +8937,48 @@ fn explicit_theme_hue_round_trips_through_kdl() {
);
}
#[test]
fn dangerously_allow_web_serving_without_a_certificate_is_parsed_and_round_trips() {
for (fake_config, expected) in [
(
"dangerously_allow_web_serving_without_a_certificate true",
Some(true),
),
(
"dangerously_allow_web_serving_without_a_certificate false",
Some(false),
),
("", None),
] {
let document: KdlDocument = fake_config.parse().unwrap();
let deserialized = Options::from_kdl(&document).unwrap();
assert_eq!(
deserialized.dangerously_allow_web_serving_without_a_certificate, expected,
"parsed from {:?}",
fake_config
);
let mut serialized = Options::to_kdl(&deserialized, false);
let mut fake_document = KdlDocument::new();
fake_document.nodes_mut().append(&mut serialized);
let deserialized_from_serialized =
Options::from_kdl(&fake_document.to_string().parse::<KdlDocument>().unwrap()).unwrap();
assert_eq!(
deserialized_from_serialized.dangerously_allow_web_serving_without_a_certificate,
expected,
"round trip of {:?}",
fake_config
);
}
}
#[test]
fn dangerously_allow_web_serving_without_a_certificate_rejects_non_bool() {
let document: KdlDocument = "dangerously_allow_web_serving_without_a_certificate \"yes\""
.parse()
.unwrap();
assert!(Options::from_kdl(&document).is_err());
}
#[test]
fn config_options_to_string() {
let fake_config = r##"
@@ -1,6 +1,5 @@
---
source: zellij-utils/src/kdl/mod.rs
assertion_line: 9130
expression: fake_config_stringified
---
@@ -300,12 +299,23 @@ web_client {
/// Whether to enforce https connections to the web server when it is bound to localhost
/// (127.0.0.0/8)
///
/// Note: https is ALWAYS enforced when bound to non-local interfaces
/// Note: https is enforced when bound to non-local interfaces, unless
/// dangerously_allow_web_serving_without_a_certificate is set
///
/// Default: false
//
// enforce_https_for_localhost false
// DANGEROUS: allow the web server to serve plain, unencrypted HTTP when bound to a
// non-local interface without web_server_cert and web_server_key.
// Anyone on the network can read and tamper with the traffic, including login tokens.
// Only use behind a trusted TLS-terminating proxy or on a fully trusted network.
// Does not override enforce_https_for_localhost.
// Default: false
// (Requires restart)
//
// dangerously_allow_web_serving_without_a_certificate false
// Whether to stack panes when resizing beyond a certain size
// Default: true
//
@@ -1,6 +1,5 @@
---
source: zellij-utils/src/kdl/mod.rs
assertion_line: 8798
expression: fake_document.to_string()
---
@@ -261,12 +260,23 @@ web_sharing "disabled"
/// Whether to enforce https connections to the web server when it is bound to localhost
/// (127.0.0.0/8)
///
/// Note: https is ALWAYS enforced when bound to non-local interfaces
/// Note: https is enforced when bound to non-local interfaces, unless
/// dangerously_allow_web_serving_without_a_certificate is set
///
/// Default: false
//
// enforce_https_for_localhost false
// DANGEROUS: allow the web server to serve plain, unencrypted HTTP when bound to a
// non-local interface without web_server_cert and web_server_key.
// Anyone on the network can read and tamper with the traffic, including login tokens.
// Only use behind a trusted TLS-terminating proxy or on a fully trusted network.
// Does not override enforce_https_for_localhost.
// Default: false
// (Requires restart)
//
// dangerously_allow_web_serving_without_a_certificate false
// Whether to stack panes when resizing beyond a certain size
// Default: true
//
+86 -5
View File
@@ -179,17 +179,20 @@ pub fn web_server_base_url(
web_server_port: u16,
has_certificate: bool,
enforce_https_for_localhost: bool,
dangerously_allow_web_serving_without_a_certificate: bool,
) -> String {
let is_loopback = match web_server_ip {
IpAddr::V4(ipv4) => ipv4.is_loopback(),
IpAddr::V6(ipv6) => ipv6.is_loopback(),
};
let url_prefix = if is_loopback && !enforce_https_for_localhost && !has_certificate {
"http"
} else {
"https"
};
let serves_plain_http = !has_certificate
&& if is_loopback {
!enforce_https_for_localhost
} else {
dangerously_allow_web_serving_without_a_certificate
};
let url_prefix = if serves_plain_http { "http" } else { "https" };
format!("{}://{}:{}", url_prefix, web_server_ip, web_server_port)
}
@@ -201,11 +204,15 @@ pub fn web_server_base_url_from_config(config_options: Options) -> String {
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);
web_server_base_url(
web_server_ip,
web_server_port,
has_certificate,
enforce_https_for_localhost,
dangerously_allow_web_serving_without_a_certificate,
)
}
@@ -226,3 +233,77 @@ pub fn parse_base_url(url: &str) -> Result<ServerAddress> {
Ok(ServerAddress { ip, port })
}
#[cfg(test)]
mod web_server_base_url_tests {
use super::*;
fn remote() -> IpAddr {
IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))
}
fn localhost() -> IpAddr {
IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))
}
#[test]
fn non_loopback_without_certificate_uses_https_by_default() {
assert_eq!(
web_server_base_url(remote(), 8082, false, false, false),
"https://0.0.0.0:8082"
);
}
#[test]
fn non_loopback_without_certificate_uses_http_when_dangerously_allowed() {
assert_eq!(
web_server_base_url(remote(), 8082, false, false, true),
"http://0.0.0.0:8082"
);
}
#[test]
fn non_loopback_with_certificate_uses_https_when_dangerously_allowed() {
assert_eq!(
web_server_base_url(remote(), 8082, true, false, true),
"https://0.0.0.0:8082"
);
}
#[test]
fn loopback_urls_are_unchanged_by_dangerous_allowance() {
assert_eq!(
web_server_base_url(localhost(), 8082, false, false, true),
"http://127.0.0.1:8082"
);
assert_eq!(
web_server_base_url(localhost(), 8082, false, true, true),
"https://127.0.0.1:8082"
);
assert_eq!(
web_server_base_url(localhost(), 8082, true, false, true),
"https://127.0.0.1:8082"
);
}
#[test]
fn base_url_from_config_reads_dangerous_allowance() {
let options = Options {
web_server_ip: Some(remote()),
dangerously_allow_web_serving_without_a_certificate: Some(true),
..Default::default()
};
assert_eq!(
web_server_base_url_from_config(options),
"http://0.0.0.0:8082"
);
let options = Options {
web_server_ip: Some(remote()),
..Default::default()
};
assert_eq!(
web_server_base_url_from_config(options),
"https://0.0.0.0:8082"
);
}
}
@@ -1,6 +1,5 @@
---
source: zellij-utils/src/setup.rs
assertion_line: 848
expression: "format!(\"{:#?}\", options)"
---
Options {
@@ -67,6 +66,7 @@ Options {
web_server_cert: None,
web_server_key: None,
enforce_https_for_localhost: None,
dangerously_allow_web_serving_without_a_certificate: None,
post_command_discovery_hook: None,
client_async_worker_tasks: None,
nested_session_handling: None,
@@ -1,6 +1,5 @@
---
source: zellij-utils/src/setup.rs
assertion_line: 882
expression: "format!(\"{:#?}\", options)"
---
Options {
@@ -67,6 +66,7 @@ Options {
web_server_cert: None,
web_server_key: None,
enforce_https_for_localhost: None,
dangerously_allow_web_serving_without_a_certificate: None,
post_command_discovery_hook: None,
client_async_worker_tasks: None,
nested_session_handling: None,
@@ -1,6 +1,5 @@
---
source: zellij-utils/src/setup.rs
assertion_line: 838
expression: "format!(\"{:#?}\", options)"
---
Options {
@@ -65,6 +64,7 @@ Options {
web_server_cert: None,
web_server_key: None,
enforce_https_for_localhost: None,
dangerously_allow_web_serving_without_a_certificate: None,
post_command_discovery_hook: None,
client_async_worker_tasks: None,
nested_session_handling: None,
@@ -1,6 +1,5 @@
---
source: zellij-utils/src/setup.rs
assertion_line: 836
expression: "format!(\"{:#?}\", config)"
---
Config {
@@ -6175,6 +6174,7 @@ Config {
web_server_cert: None,
web_server_key: None,
enforce_https_for_localhost: None,
dangerously_allow_web_serving_without_a_certificate: None,
post_command_discovery_hook: None,
client_async_worker_tasks: None,
nested_session_handling: None,
@@ -1,6 +1,5 @@
---
source: zellij-utils/src/setup.rs
assertion_line: 906
expression: "format!(\"{:#?}\", config)"
---
Config {
@@ -6175,6 +6174,7 @@ Config {
web_server_cert: None,
web_server_key: None,
enforce_https_for_localhost: None,
dangerously_allow_web_serving_without_a_certificate: None,
post_command_discovery_hook: None,
client_async_worker_tasks: None,
nested_session_handling: None,
@@ -1,6 +1,5 @@
---
source: zellij-utils/src/setup.rs
assertion_line: 948
expression: "format!(\"{:#?}\", config)"
---
Config {
@@ -152,6 +151,7 @@ Config {
web_server_cert: None,
web_server_key: None,
enforce_https_for_localhost: None,
dangerously_allow_web_serving_without_a_certificate: None,
post_command_discovery_hook: None,
client_async_worker_tasks: None,
nested_session_handling: None,
@@ -1,6 +1,5 @@
---
source: zellij-utils/src/setup.rs
assertion_line: 858
expression: "format!(\"{:#?}\", options)"
---
Options {
@@ -67,6 +66,7 @@ Options {
web_server_cert: None,
web_server_key: None,
enforce_https_for_localhost: None,
dangerously_allow_web_serving_without_a_certificate: None,
post_command_discovery_hook: None,
client_async_worker_tasks: None,
nested_session_handling: None,
@@ -1,6 +1,5 @@
---
source: zellij-utils/src/setup.rs
assertion_line: 934
expression: "format!(\"{:#?}\", config)"
---
Config {
@@ -6175,6 +6174,7 @@ Config {
web_server_cert: None,
web_server_key: None,
enforce_https_for_localhost: None,
dangerously_allow_web_serving_without_a_certificate: None,
post_command_discovery_hook: None,
client_async_worker_tasks: None,
nested_session_handling: None,
@@ -1,6 +1,5 @@
---
source: zellij-utils/src/setup.rs
assertion_line: 920
expression: "format!(\"{:#?}\", config)"
---
Config {
@@ -6175,6 +6174,7 @@ Config {
web_server_cert: None,
web_server_key: None,
enforce_https_for_localhost: None,
dangerously_allow_web_serving_without_a_certificate: None,
post_command_discovery_hook: None,
client_async_worker_tasks: None,
nested_session_handling: None,